Improving avg performance for numeric

Started by Hadi Moshayedialmost 13 years ago29 messages
#1Hadi Moshayedi
hadi@moshayedi.net
1 attachment(s)

Revisiting:
/messages/by-id/45661BE7.4050205@paradise.net.nz

I think the reasons which the numeric average was slow were:
(1) Using Numeric for count, which is slower than int8 to increment,
(2) Constructing/deconstructing arrays at each transition step.

This is also discussed at:
http://www.citusdata.com/blog/53-postgres-performance-to-avg-or-to-sum-divided-by-count

So, I think we can improve the speed of numeric average by keeping the
transition state as an struct in the aggregate context, and just passing
the pointer to that struct from/to the aggregate transition function.

The attached patch uses this method.

I tested it using the data generated using:
CREATE TABLE avg_test AS SELECT (random() * 999)::decimal(5,2) as d FROM
generate_series(1, 10000000) s;

After applying this patch, run time of "SELECT avg(d) FROM avg_test;"
improves from 10.701 seconds to 5.204 seconds, which seems to be a huge
improvement.

I think we may also be able to use a similar method to improve performance
of some other numeric aggregates (like stddev). But I want to know your
feedback first.

Is this worth working on?

Thanks,
-- Hadi

Attachments:

numeric-avg-optimize.patchapplication/octet-stream; name=numeric-avg-optimize.patchDownload
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 229b408..6e1a6ae 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);
 
 /* ----------------------------------------------------------------------
  *
@@ -2511,38 +2518,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;
+	Numeric 	 newsumX;
+	size_t		 newsumX_size;
 
-	/* 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];
+	/* Calculate the new value for sumX. */
+	newsumX = DatumGetNumeric(DirectFunctionCall2(numeric_add, 
+							  NumericGetDatum(state->sumX),
+							  NumericGetDatum(newval)));
 
-	N = DirectFunctionCall1(numeric_inc, N);
-	sumX = DirectFunctionCall2(numeric_add, sumX,
-							   NumericGetDatum(newval));
-
-	transdatums[0] = N;
-	transdatums[1] = sumX;
-
-	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 +2557,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);
+
+	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_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval));
+	PG_RETURN_POINTER(state);
 }
 
 /*
@@ -2617,42 +2624,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;
+	Datum 		 countd;
+	AvgAggState *state;
 
-	/* 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]);
-
-	/* 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 +6178,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..b34fedf 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -77,10 +77,10 @@ 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}" ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index c97056e..f30a7a3 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2385,7 +2385,7 @@ 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 = 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");
@@ -2393,9 +2393,9 @@ DATA(insert OID = 1835 (  int4_accum	   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0
 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 = 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");
#2Pavel Stehule
pavel.stehule@gmail.com
In reply to: Hadi Moshayedi (#1)
Re: Improving avg performance for numeric

Hello

2013/3/16 Hadi Moshayedi <hadi@moshayedi.net>:

Revisiting:
/messages/by-id/45661BE7.4050205@paradise.net.nz

I think the reasons which the numeric average was slow were:
(1) Using Numeric for count, which is slower than int8 to increment,
(2) Constructing/deconstructing arrays at each transition step.

This is also discussed at:
http://www.citusdata.com/blog/53-postgres-performance-to-avg-or-to-sum-divided-by-count

So, I think we can improve the speed of numeric average by keeping the
transition state as an struct in the aggregate context, and just passing the
pointer to that struct from/to the aggregate transition function.

The attached patch uses this method.

I tested it using the data generated using:
CREATE TABLE avg_test AS SELECT (random() * 999)::decimal(5,2) as d FROM
generate_series(1, 10000000) s;

After applying this patch, run time of "SELECT avg(d) FROM avg_test;"
improves from 10.701 seconds to 5.204 seconds, which seems to be a huge
improvement.

I think we may also be able to use a similar method to improve performance
of some other numeric aggregates (like stddev). But I want to know your
feedback first.

Is this worth working on?

nice

+1

Regards

Pavel

Thanks,
-- Hadi

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#3Pavel Stehule
pavel.stehule@gmail.com
In reply to: Hadi Moshayedi (#1)
Re: Improving avg performance for numeric

2013/3/16 Hadi Moshayedi <hadi@moshayedi.net>:

Revisiting:
/messages/by-id/45661BE7.4050205@paradise.net.nz

I think the reasons which the numeric average was slow were:
(1) Using Numeric for count, which is slower than int8 to increment,
(2) Constructing/deconstructing arrays at each transition step.

This is also discussed at:
http://www.citusdata.com/blog/53-postgres-performance-to-avg-or-to-sum-divided-by-count

So, I think we can improve the speed of numeric average by keeping the
transition state as an struct in the aggregate context, and just passing the
pointer to that struct from/to the aggregate transition function.

The attached patch uses this method.

I tested it using the data generated using:
CREATE TABLE avg_test AS SELECT (random() * 999)::decimal(5,2) as d FROM
generate_series(1, 10000000) s;

After applying this patch, run time of "SELECT avg(d) FROM avg_test;"
improves from 10.701 seconds to 5.204 seconds, which seems to be a huge
improvement.

I think we may also be able to use a similar method to improve performance
of some other numeric aggregates (like stddev). But I want to know your
feedback first.

Is this worth working on?

I checked this patch and it has a interesting speedup - and a price of
this methoud should not be limited to numeric type only

Pavel

Thanks,
-- Hadi

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Hadi Moshayedi
hadi@moshayedi.net
In reply to: Pavel Stehule (#3)
Re: Improving avg performance for numeric

Hi Pavel,

Thanks a lot for your feedback.

I'll work more on this patch this week, and will send a more complete patch
later this week.

I'll also try to see how much is the speed up of this method for other
types.

Thanks,
-- Hadi

On Mon, Mar 18, 2013 at 10:36 AM, Pavel Stehule <pavel.stehule@gmail.com>wrote:

Show quoted text

2013/3/16 Hadi Moshayedi <hadi@moshayedi.net>:

Revisiting:
/messages/by-id/45661BE7.4050205@paradise.net.nz

I think the reasons which the numeric average was slow were:
(1) Using Numeric for count, which is slower than int8 to increment,
(2) Constructing/deconstructing arrays at each transition step.

This is also discussed at:

http://www.citusdata.com/blog/53-postgres-performance-to-avg-or-to-sum-divided-by-count

So, I think we can improve the speed of numeric average by keeping the
transition state as an struct in the aggregate context, and just passing

the

pointer to that struct from/to the aggregate transition function.

The attached patch uses this method.

I tested it using the data generated using:
CREATE TABLE avg_test AS SELECT (random() * 999)::decimal(5,2) as d FROM
generate_series(1, 10000000) s;

After applying this patch, run time of "SELECT avg(d) FROM avg_test;"
improves from 10.701 seconds to 5.204 seconds, which seems to be a huge
improvement.

I think we may also be able to use a similar method to improve

performance

of some other numeric aggregates (like stddev). But I want to know your
feedback first.

Is this worth working on?

I checked this patch and it has a interesting speedup - and a price of
this methoud should not be limited to numeric type only

Pavel

Thanks,
-- Hadi

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Pavel Stehule
pavel.stehule@gmail.com
In reply to: Hadi Moshayedi (#4)
1 attachment(s)
Re: Improving avg performance for numeric

Hello

I played with sum(numeric) optimization

Now it is based on generic numeric_add function - this code is
relative old - now we can design aggregates with internal transition
buffers, and probably we can do this work more effective.

I just removed useles palloc/free operations and I got a 30% better
performance! My patch is ugly - because I used a generic add_var
function. Because Sum, Avg and almost all aggregates functions is
limited by speed of sum calculation I thing so we need a new numeric
routines optimized for calculation "sum", that use a only preallocated
buffers. A speed of numeric is more important now, because there are
more and more warehouses, where CPU is botleneck.

Regards

Pavel

2013/3/18 Hadi Moshayedi <hadi@moshayedi.net>:

Show quoted text

Hi Pavel,

Thanks a lot for your feedback.

I'll work more on this patch this week, and will send a more complete patch
later this week.

I'll also try to see how much is the speed up of this method for other
types.

Thanks,
-- Hadi

On Mon, Mar 18, 2013 at 10:36 AM, Pavel Stehule <pavel.stehule@gmail.com>
wrote:

2013/3/16 Hadi Moshayedi <hadi@moshayedi.net>:

Revisiting:
/messages/by-id/45661BE7.4050205@paradise.net.nz

I think the reasons which the numeric average was slow were:
(1) Using Numeric for count, which is slower than int8 to increment,
(2) Constructing/deconstructing arrays at each transition step.

This is also discussed at:

http://www.citusdata.com/blog/53-postgres-performance-to-avg-or-to-sum-divided-by-count

So, I think we can improve the speed of numeric average by keeping the
transition state as an struct in the aggregate context, and just passing
the
pointer to that struct from/to the aggregate transition function.

The attached patch uses this method.

I tested it using the data generated using:
CREATE TABLE avg_test AS SELECT (random() * 999)::decimal(5,2) as d FROM
generate_series(1, 10000000) s;

After applying this patch, run time of "SELECT avg(d) FROM avg_test;"
improves from 10.701 seconds to 5.204 seconds, which seems to be a huge
improvement.

I think we may also be able to use a similar method to improve
performance
of some other numeric aggregates (like stddev). But I want to know your
feedback first.

Is this worth working on?

I checked this patch and it has a interesting speedup - and a price of
this methoud should not be limited to numeric type only

Pavel

Thanks,
-- Hadi

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Attachments:

numeric-sum-optimize.patchapplication/octet-stream; name=numeric-sum-optimize.patchDownload
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);
#6Hadi Moshayedi
hadi@moshayedi.net
In reply to: Pavel Stehule (#5)
1 attachment(s)
Re: Improving avg performance for numeric

Hello,

I updated the patch by taking ideas from your patch, and unifying the
transition struct and update function for different aggregates. The speed
of avg improved even more. It now has 60% better performance than the
current committed version.

This patch optimizes numeric/int8 sum, avg, stddev_pop, stddev_samp,
var_pop, var_samp.

I also noticed that this patch makes matview test fail. It seems that it
just changes the ordering of rows for queries like "SELECT * FROM tv;".
Does this seem like a bug in my patch, or should we add "ORDER BY" clauses
to this test to make it more deterministic?

I also agree with you that adding sum functions to use preallocated buffers
will make even more optimization. I'll try to see if I can find a simple
way to do this.

Thanks,
-- Hadi

On Mon, Mar 18, 2013 at 5:25 PM, Pavel Stehule <pavel.stehule@gmail.com>wrote:

Show quoted text

Hello

I played with sum(numeric) optimization

Now it is based on generic numeric_add function - this code is
relative old - now we can design aggregates with internal transition
buffers, and probably we can do this work more effective.

I just removed useles palloc/free operations and I got a 30% better
performance! My patch is ugly - because I used a generic add_var
function. Because Sum, Avg and almost all aggregates functions is
limited by speed of sum calculation I thing so we need a new numeric
routines optimized for calculation "sum", that use a only preallocated
buffers. A speed of numeric is more important now, because there are
more and more warehouses, where CPU is botleneck.

Regards

Pavel

2013/3/18 Hadi Moshayedi <hadi@moshayedi.net>:

Hi Pavel,

Thanks a lot for your feedback.

I'll work more on this patch this week, and will send a more complete

patch

later this week.

I'll also try to see how much is the speed up of this method for other
types.

Thanks,
-- Hadi

On Mon, Mar 18, 2013 at 10:36 AM, Pavel Stehule <pavel.stehule@gmail.com

wrote:

2013/3/16 Hadi Moshayedi <hadi@moshayedi.net>:

Revisiting:
/messages/by-id/45661BE7.4050205@paradise.net.nz

I think the reasons which the numeric average was slow were:
(1) Using Numeric for count, which is slower than int8 to increment,
(2) Constructing/deconstructing arrays at each transition step.

This is also discussed at:

http://www.citusdata.com/blog/53-postgres-performance-to-avg-or-to-sum-divided-by-count

So, I think we can improve the speed of numeric average by keeping the
transition state as an struct in the aggregate context, and just

passing

the
pointer to that struct from/to the aggregate transition function.

The attached patch uses this method.

I tested it using the data generated using:
CREATE TABLE avg_test AS SELECT (random() * 999)::decimal(5,2) as d

FROM

generate_series(1, 10000000) s;

After applying this patch, run time of "SELECT avg(d) FROM avg_test;"
improves from 10.701 seconds to 5.204 seconds, which seems to be a

huge

improvement.

I think we may also be able to use a similar method to improve
performance
of some other numeric aggregates (like stddev). But I want to know

your

feedback first.

Is this worth working on?

I checked this patch and it has a interesting speedup - and a price of
this methoud should not be limited to numeric type only

Pavel

Thanks,
-- Hadi

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Attachments:

numeric-optimize-v2.patchapplication/octet-stream; name=numeric-optimize-v2.patchDownload
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 229b408..e8294b6 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -2464,94 +2464,122 @@ numeric_float4(PG_FUNCTION_ARGS)
  *
  * Aggregate functions
  *
- * The transition datatype for all these aggregates is a 3-element array
- * of Numeric, holding the values N, sum(X), sum(X*X) in that order.
- *
- * We represent N as a numeric mainly to avoid having to build a special
- * datatype; it's unlikely it'd overflow an int4, but ...
+ * The transition datatype for all these aggregates is a pointer to 
+ * a struct NumericAggState allocated in the aggregate context.
  *
  * ----------------------------------------------------------------------
  */
 
-static ArrayType *
-do_numeric_accum(ArrayType *transarray, Numeric newval)
+typedef struct NumericAggState
+{
+	bool 			first;
+	bool 			isNaN;
+	uint64			N;
+	NumericVar 		sumX;
+	NumericVar 		sumX2;
+	bool 			calcSumX2;
+	MemoryContext 	agg_context;
+} NumericAggState;
+
+static NumericAggState *
+makeNumericAggState(FunctionCallInfo fcinfo, bool calcSumX2)
 {
-	Datum	   *transdatums;
-	int			ndatums;
-	Datum		N,
-				sumX,
-				sumX2;
-	ArrayType  *result;
-
-	/* We assume the input is array of numeric */
-	deconstruct_array(transarray,
-					  NUMERICOID, -1, false, 'i',
-					  &transdatums, NULL, &ndatums);
-	if (ndatums != 3)
-		elog(ERROR, "expected 3-element numeric array");
-	N = transdatums[0];
-	sumX = transdatums[1];
-	sumX2 = transdatums[2];
-
-	N = DirectFunctionCall1(numeric_inc, N);
-	sumX = DirectFunctionCall2(numeric_add, sumX,
-							   NumericGetDatum(newval));
-	sumX2 = DirectFunctionCall2(numeric_add, sumX2,
-								DirectFunctionCall2(numeric_mul,
-													NumericGetDatum(newval),
-													NumericGetDatum(newval)));
-
-	transdatums[0] = N;
-	transdatums[1] = sumX;
-	transdatums[2] = sumX2;
-
-	result = construct_array(transdatums, 3,
-							 NUMERICOID, -1, false, 'i');
+	NumericAggState 	*state;
+	MemoryContext 		 agg_context;
+	MemoryContext 		 old_context;
 
-	return result;
+	if (!AggCheckCallContext(fcinfo, &agg_context))
+	{
+		elog(ERROR, "this is called in non-aggregate context");
+	}
+
+	old_context = MemoryContextSwitchTo(agg_context);
+
+	state = palloc0(sizeof(NumericAggState));
+	state->first = true;
+	state->calcSumX2 = calcSumX2;
+	state->agg_context = agg_context;
+
+	MemoryContextSwitchTo(old_context);
+
+	return state;
 }
 
-/*
- * Improve avg performance by not caclulating sum(X*X).
- */
-static ArrayType *
-do_numeric_avg_accum(ArrayType *transarray, Numeric newval)
+static void
+do_numeric_accum(NumericAggState *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));
-
-	transdatums[0] = N;
-	transdatums[1] = sumX;
-
-	result = construct_array(transdatums, 2,
-							 NUMERICOID, -1, false, 'i');
+	NumericVar 		X;
+	NumericVar 		X2;
+	MemoryContext 	old_context;
+	bool 			first;
+	
+	first = state->first;
+	state->first = false;
+	state->N++;
+
+	if (state->isNaN || NUMERIC_IS_NAN(newval))
+	{
+		state->isNaN = true;
+		return;
+	}
 
-	return result;
+	init_var_from_num(newval, &X);
+
+	if (state->calcSumX2)
+	{
+		init_var(&X2);
+		mul_var(&X, &X, &X2, X.dscale * 2);
+	}
+
+	old_context = MemoryContextSwitchTo(state->agg_context);
+
+	if (!first)
+	{
+		NumericVar preSumX;
+
+		memcpy(&preSumX, &(state->sumX), sizeof(NumericVar));
+		init_var(&(state->sumX));
+		add_var(&X, &preSumX, &(state->sumX));
+		free_var(&preSumX);
+
+		if (state->calcSumX2)
+		{
+			NumericVar preSumX2;
+
+			memcpy(&preSumX2, &(state->sumX2), sizeof(NumericVar));
+			init_var(&(state->sumX2));
+			add_var(&X2, &preSumX2, &(state->sumX2));
+			free_var(&preSumX2);
+		}
+	}
+	else
+	{
+		set_var_from_var(&X, &(state->sumX));
+
+		if (state->calcSumX2)
+			set_var_from_var(&X2, &(state->sumX2));
+	}
+
+	MemoryContextSwitchTo(old_context);
 }
 
 Datum
 numeric_accum(PG_FUNCTION_ARGS)
 {
-	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-	Numeric		newval = PG_GETARG_NUMERIC(1);
+	NumericAggState *state;
+
+	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+
+	if (!PG_ARGISNULL(1))
+	{
+		/* On the first time through, create the state variable. */
+		if (state == NULL)
+			state = makeNumericAggState(fcinfo, true);
+		
+		do_numeric_accum(state, PG_GETARG_NUMERIC(1));
+	}
 
-	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
+	PG_RETURN_POINTER(state);
 }
 
 /*
@@ -2560,10 +2588,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);
+	NumericAggState *state;
 
-	PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval));
+	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+
+	if (!PG_ARGISNULL(1))
+	{
+		/* On the first time through, create the state variable. */
+		if (state == NULL)
+			state = makeNumericAggState(fcinfo, false);
+		
+		do_numeric_accum(state, PG_GETARG_NUMERIC(1));
+	}
+
+	PG_RETURN_POINTER(state);
 }
 
 /*
@@ -2578,37 +2616,70 @@ numeric_avg_accum(PG_FUNCTION_ARGS)
 Datum
 int2_accum(PG_FUNCTION_ARGS)
 {
-	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-	Datum		newval2 = PG_GETARG_DATUM(1);
-	Numeric		newval;
+	NumericAggState *state;
 
-	newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric, newval2));
+	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+
+	if (!PG_ARGISNULL(1))
+	{
+		Datum		newval2 = PG_GETARG_DATUM(1);
+		Numeric		newval;
+
+		/* On the first time through, create the state variable. */
+		if (state == NULL)
+			state = makeNumericAggState(fcinfo, true);
+		
+		newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric, newval2));
+		do_numeric_accum(state, newval);
+	}
 
-	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
+	PG_RETURN_POINTER(state);
 }
 
 Datum
 int4_accum(PG_FUNCTION_ARGS)
 {
-	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-	Datum		newval4 = PG_GETARG_DATUM(1);
-	Numeric		newval;
+	NumericAggState *state;
+
+	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
 
-	newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric, newval4));
+	if (!PG_ARGISNULL(1))
+	{
+		Datum		newval4 = PG_GETARG_DATUM(1);
+		Numeric		newval;
+
+		/* On the first time through, create the state variable. */
+		if (state == NULL)
+			state = makeNumericAggState(fcinfo, true);
+		
+		newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric, newval4));
+		do_numeric_accum(state, newval);
+	}
 
-	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
+	PG_RETURN_POINTER(state);
 }
 
 Datum
 int8_accum(PG_FUNCTION_ARGS)
 {
-	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-	Datum		newval8 = PG_GETARG_DATUM(1);
-	Numeric		newval;
+	NumericAggState *state;
 
-	newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
+	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
 
-	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
+	if (!PG_ARGISNULL(1))
+	{
+		Datum		newval8 = PG_GETARG_DATUM(1);
+		Numeric		newval;
+
+		/* On the first time through, create the state variable. */
+		if (state == NULL)
+			state = makeNumericAggState(fcinfo, true);
+		
+		newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
+		do_numeric_accum(state, newval);
+	}
+
+	PG_RETURN_POINTER(state);
 }
 
 /*
@@ -2617,48 +2688,61 @@ 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;
+	NumericAggState *state;
 
-	newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
+	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
 
-	PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval));
-}
+	if (!PG_ARGISNULL(1))
+	{
+		Datum		newval8 = PG_GETARG_DATUM(1);
+		Numeric		newval;
+
+		/* On the first time through, create the state variable. */
+		if (state == NULL)
+			state = makeNumericAggState(fcinfo, false);
+		
+		newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
+		do_numeric_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 		 		N_datum;
+	Datum 				sumX_datum;
+	NumericAggState 	*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 = (NumericAggState *) PG_GETARG_POINTER(0);
+
+	N_datum = DirectFunctionCall1(int8_numeric, Int64GetDatum(state->N));
+	sumX_datum = NumericGetDatum(make_result(&state->sumX));
+
+	PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, sumX_datum, N_datum));
+}
+
+Datum
+numeric_sum(PG_FUNCTION_ARGS)
+{
+	NumericAggState 	*state;
+
+	if (PG_ARGISNULL(0))
 		PG_RETURN_NULL();
 
-	PG_RETURN_DATUM(DirectFunctionCall2(numeric_div,
-										NumericGetDatum(sumX),
-										NumericGetDatum(N)));
+	state = (NumericAggState *) PG_GETARG_POINTER(0);
+
+	PG_RETURN_NUMERIC(make_result(&(state->sumX)));
 }
 
 /*
  * Workhorse routine for the standard deviance and variance
- * aggregates. 'transarray' is the aggregate's transition
- * array. 'variance' specifies whether we should calculate the
+ * aggregates. 'state' is aggregate's transition state.
+ * 'variance' specifies whether we should calculate the
  * variance or the standard deviation. 'sample' indicates whether the
  * caller is interested in the sample or the population
  * variance/stddev.
@@ -2667,16 +2751,11 @@ numeric_avg(PG_FUNCTION_ARGS)
  * *is_null is set to true and NULL is returned.
  */
 static Numeric
-numeric_stddev_internal(ArrayType *transarray,
+numeric_stddev_internal(NumericAggState *state,
 						bool variance, bool sample,
 						bool *is_null)
 {
-	Datum	   *transdatums;
-	int			ndatums;
-	Numeric		N,
-				sumX,
-				sumX2,
-				res;
+	Numeric		res;
 	NumericVar	vN,
 				vsumX,
 				vsumX2,
@@ -2684,22 +2763,24 @@ numeric_stddev_internal(ArrayType *transarray,
 	NumericVar *comp;
 	int			rscale;
 
+	if (state == NULL)
+	{
+		*is_null = true;
+		return NULL;
+	}
+
 	*is_null = false;
 
-	/* We assume the input is array of numeric */
-	deconstruct_array(transarray,
-					  NUMERICOID, -1, false, 'i',
-					  &transdatums, NULL, &ndatums);
-	if (ndatums != 3)
-		elog(ERROR, "expected 3-element numeric array");
-	N = DatumGetNumeric(transdatums[0]);
-	sumX = DatumGetNumeric(transdatums[1]);
-	sumX2 = DatumGetNumeric(transdatums[2]);
-
-	if (NUMERIC_IS_NAN(N) || NUMERIC_IS_NAN(sumX) || NUMERIC_IS_NAN(sumX2))
+	if (state->isNaN)
 		return make_result(&const_nan);
 
-	init_var_from_num(N, &vN);
+	init_var(&vN);
+	init_var(&vsumX);
+	init_var(&vsumX2);
+
+	int8_to_numericvar(state->N, &vN);
+	set_var_from_var(&(state->sumX), &vsumX);
+	set_var_from_var(&(state->sumX2), &vsumX2);
 
 	/*
 	 * Sample stddev and variance are undefined when N <= 1; population stddev
@@ -2719,8 +2800,8 @@ numeric_stddev_internal(ArrayType *transarray,
 	init_var(&vNminus1);
 	sub_var(&vN, &const_one, &vNminus1);
 
-	init_var_from_num(sumX, &vsumX);
-	init_var_from_num(sumX2, &vsumX2);
+	set_var_from_var(&(state->sumX), &vsumX);
+	set_var_from_var(&(state->sumX2), &vsumX2);
 
 	/* compute rscale for mul_var calls */
 	rscale = vsumX.dscale * 2;
@@ -2761,7 +2842,7 @@ numeric_var_samp(PG_FUNCTION_ARGS)
 	Numeric		res;
 	bool		is_null;
 
-	res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
+	res = numeric_stddev_internal((NumericAggState *) PG_GETARG_POINTER(0),
 								  true, true, &is_null);
 
 	if (is_null)
@@ -2776,7 +2857,7 @@ numeric_stddev_samp(PG_FUNCTION_ARGS)
 	Numeric		res;
 	bool		is_null;
 
-	res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
+	res = numeric_stddev_internal((NumericAggState *) PG_GETARG_POINTER(0),
 								  false, true, &is_null);
 
 	if (is_null)
@@ -2791,7 +2872,7 @@ numeric_var_pop(PG_FUNCTION_ARGS)
 	Numeric		res;
 	bool		is_null;
 
-	res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
+	res = numeric_stddev_internal((NumericAggState *) PG_GETARG_POINTER(0),
 								  true, false, &is_null);
 
 	if (is_null)
@@ -2806,7 +2887,7 @@ numeric_stddev_pop(PG_FUNCTION_ARGS)
 	Numeric		res;
 	bool		is_null;
 
-	res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
+	res = numeric_stddev_internal((NumericAggState *) PG_GETARG_POINTER(0),
 								  false, false, &is_null);
 
 	if (is_null)
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 6fb10a9..6f4090e 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -77,23 +77,23 @@ 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}" ));
 
 /* sum */
-DATA(insert ( 2107	int8_sum		-				0	1700	_null_ ));
+DATA(insert ( 2107	int8_avg_accum	numeric_sum		0	2281	_null_ ));
 DATA(insert ( 2108	int4_sum		-				0	20		_null_ ));
 DATA(insert ( 2109	int2_sum		-				0	20		_null_ ));
 DATA(insert ( 2110	float4pl		-				0	700		_null_ ));
 DATA(insert ( 2111	float8pl		-				0	701		_null_ ));
 DATA(insert ( 2112	cash_pl			-				0	790		_null_ ));
 DATA(insert ( 2113	interval_pl		-				0	1186	_null_ ));
-DATA(insert ( 2114	numeric_add		-				0	1700	_null_ ));
+DATA(insert ( 2114	numeric_avg_accum	numeric_sum	0	2281	_null_ ));
 
 /* max */
 DATA(insert ( 2115	int8larger		-				413		20		_null_ ));
@@ -144,52 +144,52 @@ DATA(insert ( 2147	int8inc_any		-				0		20		"0" ));
 DATA(insert ( 2803	int8inc			-				0		20		"0" ));
 
 /* var_pop */
-DATA(insert ( 2718	int8_accum	numeric_var_pop 0	1231	"{0,0,0}" ));
-DATA(insert ( 2719	int4_accum	numeric_var_pop 0	1231	"{0,0,0}" ));
-DATA(insert ( 2720	int2_accum	numeric_var_pop 0	1231	"{0,0,0}" ));
+DATA(insert ( 2718	int8_accum	numeric_var_pop 0	2281	_null_ ));
+DATA(insert ( 2719	int4_accum	numeric_var_pop 0	2281	_null_ ));
+DATA(insert ( 2720	int2_accum	numeric_var_pop 0	2281	_null_ ));
 DATA(insert ( 2721	float4_accum	float8_var_pop 0	1022	"{0,0,0}" ));
 DATA(insert ( 2722	float8_accum	float8_var_pop 0	1022	"{0,0,0}" ));
-DATA(insert ( 2723	numeric_accum  numeric_var_pop 0	1231	"{0,0,0}" ));
+DATA(insert ( 2723	numeric_accum  numeric_var_pop 0	2281	_null_ ));
 
 /* var_samp */
-DATA(insert ( 2641	int8_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
-DATA(insert ( 2642	int4_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
-DATA(insert ( 2643	int2_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
+DATA(insert ( 2641	int8_accum	numeric_var_samp	0	2281	_null_ ));
+DATA(insert ( 2642	int4_accum	numeric_var_samp	0	2281	_null_ ));
+DATA(insert ( 2643	int2_accum	numeric_var_samp	0	2281	_null_ ));
 DATA(insert ( 2644	float4_accum	float8_var_samp 0	1022	"{0,0,0}" ));
 DATA(insert ( 2645	float8_accum	float8_var_samp 0	1022	"{0,0,0}" ));
-DATA(insert ( 2646	numeric_accum  numeric_var_samp 0	1231	"{0,0,0}" ));
+DATA(insert ( 2646	numeric_accum  numeric_var_samp 0	2281	_null_ ));
 
 /* variance: historical Postgres syntax for var_samp */
-DATA(insert ( 2148	int8_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
-DATA(insert ( 2149	int4_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
-DATA(insert ( 2150	int2_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
+DATA(insert ( 2148	int8_accum	numeric_var_samp	0	2281	_null_ ));
+DATA(insert ( 2149	int4_accum	numeric_var_samp	0	2281	_null_ ));
+DATA(insert ( 2150	int2_accum	numeric_var_samp	0	2281	_null_ ));
 DATA(insert ( 2151	float4_accum	float8_var_samp 0	1022	"{0,0,0}" ));
 DATA(insert ( 2152	float8_accum	float8_var_samp 0	1022	"{0,0,0}" ));
-DATA(insert ( 2153	numeric_accum  numeric_var_samp 0	1231	"{0,0,0}" ));
+DATA(insert ( 2153	numeric_accum  numeric_var_samp 0	2281	_null_ ));
 
 /* stddev_pop */
-DATA(insert ( 2724	int8_accum	numeric_stddev_pop		0	1231	"{0,0,0}" ));
-DATA(insert ( 2725	int4_accum	numeric_stddev_pop		0	1231	"{0,0,0}" ));
-DATA(insert ( 2726	int2_accum	numeric_stddev_pop		0	1231	"{0,0,0}" ));
+DATA(insert ( 2724	int8_accum	numeric_stddev_pop		0	2281	_null_ ));
+DATA(insert ( 2725	int4_accum	numeric_stddev_pop		0	2281	_null_ ));
+DATA(insert ( 2726	int2_accum	numeric_stddev_pop		0	2281	_null_ ));
 DATA(insert ( 2727	float4_accum	float8_stddev_pop	0	1022	"{0,0,0}" ));
 DATA(insert ( 2728	float8_accum	float8_stddev_pop	0	1022	"{0,0,0}" ));
-DATA(insert ( 2729	numeric_accum	numeric_stddev_pop	0	1231	"{0,0,0}" ));
+DATA(insert ( 2729	numeric_accum	numeric_stddev_pop	0	2281	_null_ ));
 
 /* stddev_samp */
-DATA(insert ( 2712	int8_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
-DATA(insert ( 2713	int4_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
-DATA(insert ( 2714	int2_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
+DATA(insert ( 2712	int8_accum	numeric_stddev_samp		0	2281	_null_ ));
+DATA(insert ( 2713	int4_accum	numeric_stddev_samp		0	2281	_null_ ));
+DATA(insert ( 2714	int2_accum	numeric_stddev_samp		0	2281	_null_ ));
 DATA(insert ( 2715	float4_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
 DATA(insert ( 2716	float8_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
-DATA(insert ( 2717	numeric_accum	numeric_stddev_samp 0	1231	"{0,0,0}" ));
+DATA(insert ( 2717	numeric_accum	numeric_stddev_samp 0	2281	_null_ ));
 
 /* stddev: historical Postgres syntax for stddev_samp */
-DATA(insert ( 2154	int8_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
-DATA(insert ( 2155	int4_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
-DATA(insert ( 2156	int2_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
+DATA(insert ( 2154	int8_accum	numeric_stddev_samp		0	2281	_null_ ));
+DATA(insert ( 2155	int4_accum	numeric_stddev_samp		0	2281	_null_ ));
+DATA(insert ( 2156	int2_accum	numeric_stddev_samp		0	2281	_null_ ));
 DATA(insert ( 2157	float4_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
 DATA(insert ( 2158	float8_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
-DATA(insert ( 2159	numeric_accum	numeric_stddev_samp 0	1231	"{0,0,0}" ));
+DATA(insert ( 2159	numeric_accum	numeric_stddev_samp 0	2281	_null_ ));
 
 /* SQL2003 binary regression aggregates */
 DATA(insert ( 2818	int8inc_float8_float8		-				0	20		"0" ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index c97056e..8541112 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2383,27 +2383,29 @@ DATA(insert OID = 2513 (  float8_stddev_pop PGNSP PGUID 12 1 0 0 0 f f f f t f i
 DESCR("aggregate final function");
 DATA(insert OID = 1832 (  float8_stddev_samp	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 701 "1022" _null_ _null_ _null_ _null_ float8_stddev_samp _null_ _null_ _null_ ));
 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_ ));
+DATA(insert OID = 1833 (  numeric_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_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 = 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_ ));
+DATA(insert OID = 1834 (  int2_accum	   PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 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_ ));
+DATA(insert OID = 1835 (  int4_accum	   PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 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_ ));
+DATA(insert OID = 1836 (  int8_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_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 = 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_ ));
+DATA(insert OID = 3179 (  numeric_sum	   PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_sum _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_ ));
+DATA(insert OID = 2514 (  numeric_var_pop  PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_var_pop _null_ _null_ _null_ ));
 DESCR("aggregate final function");
-DATA(insert OID = 2596 (  numeric_stddev_pop PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1700 "1231" _null_ _null_ _null_ _null_	numeric_stddev_pop _null_ _null_ _null_ ));
+DATA(insert OID = 1838 (  numeric_var_samp PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_var_samp _null_ _null_ _null_ ));
 DESCR("aggregate final function");
-DATA(insert OID = 1839 (  numeric_stddev_samp	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1700 "1231" _null_ _null_ _null_ _null_ numeric_stddev_samp _null_ _null_ _null_ ));
+DATA(insert OID = 2596 (  numeric_stddev_pop PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_	numeric_stddev_pop _null_ _null_ _null_ ));
+DESCR("aggregate final function");
+DATA(insert OID = 1839 (  numeric_stddev_samp	PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_stddev_samp _null_ _null_ _null_ ));
 DESCR("aggregate final function");
 DATA(insert OID = 1840 (  int2_sum		   PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 20 "20 21" _null_ _null_ _null_ _null_ int2_sum _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index c0debe4..fb8880f 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -980,6 +980,7 @@ extern Datum int4_accum(PG_FUNCTION_ARGS);
 extern Datum int8_accum(PG_FUNCTION_ARGS);
 extern Datum int8_avg_accum(PG_FUNCTION_ARGS);
 extern Datum numeric_avg(PG_FUNCTION_ARGS);
+extern Datum numeric_sum(PG_FUNCTION_ARGS);
 extern Datum numeric_var_pop(PG_FUNCTION_ARGS);
 extern Datum numeric_var_samp(PG_FUNCTION_ARGS);
 extern Datum numeric_stddev_pop(PG_FUNCTION_ARGS);
#7Kevin Grittner
kgrittn@ymail.com
In reply to: Hadi Moshayedi (#6)
Re: Improving avg performance for numeric

Hadi Moshayedi <hadi@moshayedi.net> wrote:

I updated the patch by taking ideas from your patch, and unifying
the transition struct and update function for different
aggregates. The speed of avg improved even more. It now has 60%
better performance than the current committed version.

Outstanding!

I also noticed that this patch makes matview test fail. It seems
that it just changes the ordering of rows for queries like
"SELECT * FROM tv;". Does this seem like a bug in my patch, or
should we add "ORDER BY" clauses to this test to make it more
deterministic?

I added some ORDER BY clauses.  That is probably a good thing
anyway for purposes of code coverage.  Does that fix it for you?

--
Kevin Grittner
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8Pavel Stehule
pavel.stehule@gmail.com
In reply to: Kevin Grittner (#7)
Re: Improving avg performance for numeric

2013/3/19 Kevin Grittner <kgrittn@ymail.com>:

Hadi Moshayedi <hadi@moshayedi.net> wrote:

I updated the patch by taking ideas from your patch, and unifying
the transition struct and update function for different
aggregates. The speed of avg improved even more. It now has 60%
better performance than the current committed version.

Outstanding!

I did some tests ala OLAP queries and I am thinking so ~ 40% speedup
for queries with AVG is realistic. Depends on other conditions.

But there are lot of situation when data are in shared buffers or file
system memory and then this patch can carry significant speedup - and
probably can be better if some better algorithm for sum two numeric
numbers in aggregate.

Regards

Pavel

I also noticed that this patch makes matview test fail. It seems
that it just changes the ordering of rows for queries like
"SELECT * FROM tv;". Does this seem like a bug in my patch, or
should we add "ORDER BY" clauses to this test to make it more
deterministic?

I added some ORDER BY clauses. That is probably a good thing
anyway for purposes of code coverage. Does that fix it for you?

--
Kevin Grittner
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#9Tom Lane
tgl@sss.pgh.pa.us
In reply to: Kevin Grittner (#7)
Re: Improving avg performance for numeric

Kevin Grittner <kgrittn@ymail.com> writes:

Hadi Moshayedi <hadi@moshayedi.net> wrote:

I also noticed that this patch makes matview test fail. It seems
that it just changes the ordering of rows for queries like
"SELECT * FROM tv;". Does this seem like a bug in my patch, or
should we add "ORDER BY" clauses to this test to make it more
deterministic?

I added some ORDER BY clauses.� That is probably a good thing
anyway for purposes of code coverage.� Does that fix it for you?

Uh, what? Fooling around with the implementation of avg() should surely
not change any planning decisions.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#10Hadi Moshayedi
hadi@moshayedi.net
In reply to: Tom Lane (#9)
Re: Improving avg performance for numeric

I am not sure how this works, but I also changed numeric sum(), and the
views in question had a numeric sum() column. Can that have any impact?

I am going to dig deeper to see why this happens.

On Tue, Mar 19, 2013 at 6:25 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Show quoted text

Kevin Grittner <kgrittn@ymail.com> writes:

Hadi Moshayedi <hadi@moshayedi.net> wrote:

I also noticed that this patch makes matview test fail. It seems
that it just changes the ordering of rows for queries like
"SELECT * FROM tv;". Does this seem like a bug in my patch, or
should we add "ORDER BY" clauses to this test to make it more
deterministic?

I added some ORDER BY clauses. That is probably a good thing
anyway for purposes of code coverage. Does that fix it for you?

Uh, what? Fooling around with the implementation of avg() should surely
not change any planning decisions.

regards, tom lane

#11Tom Lane
tgl@sss.pgh.pa.us
In reply to: Hadi Moshayedi (#10)
Re: Improving avg performance for numeric

[ please do not top-reply ]

Hadi Moshayedi <hadi@moshayedi.net> writes:

On Tue, Mar 19, 2013 at 6:25 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Uh, what? Fooling around with the implementation of avg() should surely
not change any planning decisions.

I am not sure how this works, but I also changed numeric sum(), and the
views in question had a numeric sum() column. Can that have any impact?

[ looks at patch... ] Oh, I see what's affecting the plan: you changed
the aggtranstypes to internal for a bunch of aggregates. That's not
very good, because right now the planner takes that to mean that the
aggregate could eat a lot of space. We don't want that to happen for
these aggregates, I think.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tom Lane (#11)
Re: Improving avg performance for numeric

I wrote:

[ looks at patch... ] Oh, I see what's affecting the plan: you changed
the aggtranstypes to internal for a bunch of aggregates. That's not
very good, because right now the planner takes that to mean that the
aggregate could eat a lot of space. We don't want that to happen for
these aggregates, I think.

After thinking about that for awhile: if we pursue this type of
optimization, what would probably be appropriate is to add an aggregate
property (stored in pg_aggregate) that allows direct specification of
the size that the planner should assume for the aggregate's transition
value. We were getting away with a hardwired assumption of 8K for
"internal" because the existing aggregates that used that transtype all
had similar properties, but it was always really a band-aid not a proper
solution. A per-aggregate override could be useful in other cases too.

This was looking like 9.4 material already, but adding such a property
would definitely put it over the top of what we could think about
squeezing into 9.3, IMO.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#13Pavel Stehule
pavel.stehule@gmail.com
In reply to: Tom Lane (#12)
Re: Improving avg performance for numeric

Hello

2013/3/19 Tom Lane <tgl@sss.pgh.pa.us>:

I wrote:

[ looks at patch... ] Oh, I see what's affecting the plan: you changed
the aggtranstypes to internal for a bunch of aggregates. That's not
very good, because right now the planner takes that to mean that the
aggregate could eat a lot of space. We don't want that to happen for
these aggregates, I think.

After thinking about that for awhile: if we pursue this type of
optimization, what would probably be appropriate is to add an aggregate
property (stored in pg_aggregate) that allows direct specification of
the size that the planner should assume for the aggregate's transition
value. We were getting away with a hardwired assumption of 8K for
"internal" because the existing aggregates that used that transtype all
had similar properties, but it was always really a band-aid not a proper
solution. A per-aggregate override could be useful in other cases too.

This was looking like 9.4 material already, but adding such a property
would definitely put it over the top of what we could think about
squeezing into 9.3, IMO.

Postgres is not a "in memory" OLAP database, but lot of companies use
it for OLAP queries due pg comfortable usage. This feature can be very
interesting for these users - and can introduce interesting speedup
with relative low price.

Regards

Pavel

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14Hadi Moshayedi
hadi@moshayedi.net
In reply to: Tom Lane (#12)
1 attachment(s)
Re: Improving avg performance for numeric

Hi Tom,

Tom Lane <tgl@sss.pgh.pa.us> wrote:

After thinking about that for awhile: if we pursue this type of
optimization, what would probably be appropriate is to add an aggregate
property (stored in pg_aggregate) that allows direct specification of
the size that the planner should assume for the aggregate's transition
value. We were getting away with a hardwired assumption of 8K for
"internal" because the existing aggregates that used that transtype all
had similar properties, but it was always really a band-aid not a proper
solution. A per-aggregate override could be useful in other cases too.

Cool.

I created a patch which adds an aggregate property to pg_aggregate, so
the transition space is can be overridden. This patch doesn't contain
the numeric optimizations. It uses "0" (meaning not-set) for all
existing aggregates.

I manual-tested it a bit, by changing this value for aggregates and
observing the changes in plan. I also updated some docs and pg_dump.

Does this look like something along the lines of what you meant?

Thanks,
-- Hadi

Attachments:

aggregate-transspace.patchapplication/octet-stream; name=aggregate-transspace.patchDownload
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 6c0ef5b..4b752d6 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -373,6 +373,12 @@
       <entry>Data type of the aggregate function's internal transition (state) data</entry>
      </row>
      <row>
+      <entry><structfield>aggtransspace</structfield></entry>
+      <entry><type>int4</type></entry>
+      <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
+      <entry>Approximation for the average size of the aggregate function's internal transition (state) data</entry>
+     </row>
+     <row>
       <entry><structfield>agginitval</structfield></entry>
       <entry><type>text</type></entry>
       <entry></entry>
diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml
index d5e4e27..594d095 100644
--- a/doc/src/sgml/ref/create_aggregate.sgml
+++ b/doc/src/sgml/ref/create_aggregate.sgml
@@ -24,6 +24,7 @@ PostgreSQL documentation
 CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( <replaceable class="PARAMETER">input_data_type</replaceable> [ , ... ] ) (
     SFUNC = <replaceable class="PARAMETER">sfunc</replaceable>,
     STYPE = <replaceable class="PARAMETER">state_data_type</replaceable>
+    [ , SSPACE = <replaceable class="PARAMETER">state_data_size</replaceable> ]
     [ , FINALFUNC = <replaceable class="PARAMETER">ffunc</replaceable> ]
     [ , INITCOND = <replaceable class="PARAMETER">initial_condition</replaceable> ]
     [ , SORTOP = <replaceable class="PARAMETER">sort_operator</replaceable> ]
@@ -35,6 +36,7 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> (
     BASETYPE = <replaceable class="PARAMETER">base_type</replaceable>,
     SFUNC = <replaceable class="PARAMETER">sfunc</replaceable>,
     STYPE = <replaceable class="PARAMETER">state_data_type</replaceable>
+    [ , SSPACE = <replaceable class="PARAMETER">state_data_size</replaceable> ]
     [ , FINALFUNC = <replaceable class="PARAMETER">ffunc</replaceable> ]
     [ , INITCOND = <replaceable class="PARAMETER">initial_condition</replaceable> ]
     [ , SORTOP = <replaceable class="PARAMETER">sort_operator</replaceable> ]
@@ -241,6 +243,18 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
    </varlistentry>
 
    <varlistentry>
+    <term><replaceable class="PARAMETER">state_data_size</replaceable></term>
+    <listitem>
+     <para>
+      Approximate average size (in bytes) of aggregate's state value. 
+      Planner uses this value to approximate the memory required for 
+      the aggregation. If this value is not provided, a default value is 
+      used based on state_data_type.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="PARAMETER">ffunc</replaceable></term>
     <listitem>
      <para>
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index e80b600..d08e14a 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -51,6 +51,7 @@ AggregateCreate(const char *aggName,
 				List *aggfinalfnName,
 				List *aggsortopName,
 				Oid aggTransType,
+				int32 aggTransSpace,
 				const char *agginitval)
 {
 	Relation	aggdesc;
@@ -269,6 +270,7 @@ AggregateCreate(const char *aggName,
 	values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
 	values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop);
 	values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
+	values[Anum_pg_aggregate_aggtransspace - 1] = Int32GetDatum(aggTransSpace);
 	if (agginitval)
 		values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval);
 	else
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c
index d34a102..6e5446a 100644
--- a/src/backend/commands/aggregatecmds.c
+++ b/src/backend/commands/aggregatecmds.c
@@ -62,6 +62,7 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
 	Oid		   *aggArgTypes;
 	int			numArgs;
 	Oid			transTypeId;
+	int32		transSpace = 0;
 	char		transTypeType;
 	ListCell   *pl;
 
@@ -96,6 +97,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
 			transType = defGetTypeName(defel);
 		else if (pg_strcasecmp(defel->defname, "stype1") == 0)
 			transType = defGetTypeName(defel);
+		else if (pg_strcasecmp(defel->defname, "sspace") == 0)
+			transSpace = defGetInt32(defel);
 		else if (pg_strcasecmp(defel->defname, "initcond") == 0)
 			initval = defGetString(defel);
 		else if (pg_strcasecmp(defel->defname, "initcond1") == 0)
@@ -225,5 +228,6 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
 						   finalfuncName,		/* final function name */
 						   sortoperatorName,	/* sort operator name */
 						   transTypeId,	/* transition data type */
+						   transSpace,			/* transition space */
 						   initval);	/* initial condition */
 }
diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c
index 9fa222f..75f77da 100644
--- a/src/backend/commands/define.c
+++ b/src/backend/commands/define.c
@@ -165,6 +165,30 @@ defGetBoolean(DefElem *def)
 }
 
 /*
+ * Extract an int32 value from a DefElem.
+ */
+int32
+defGetInt32(DefElem *def)
+{
+	if (def->arg == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("%s requires an integer value",
+						def->defname)));
+	switch (nodeTag(def->arg))
+	{
+		case T_Integer:
+			return (int32) intVal(def->arg);
+		default:
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("%s requires an integer value",
+							def->defname)));
+	}
+	return 0;					/* keep compiler quiet */
+}
+
+/*
  * Extract an int64 value from a DefElem.
  */
 int64
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 657a18b..b5e2c01 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -461,6 +461,7 @@ count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
 		Oid			aggtransfn;
 		Oid			aggfinalfn;
 		Oid			aggtranstype;
+		int32 		aggtransspace;
 		QualCost	argcosts;
 		Oid		   *inputTypes;
 		int			numArguments;
@@ -478,6 +479,7 @@ count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
 		aggtransfn = aggform->aggtransfn;
 		aggfinalfn = aggform->aggfinalfn;
 		aggtranstype = aggform->aggtranstype;
+		aggtransspace = aggform->aggtransspace;
 		ReleaseSysCache(aggTuple);
 
 		/* count it */
@@ -524,13 +526,21 @@ count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
 			pfree(declaredArgTypes);
 		}
 
+		/* 
+		 * If approximate average space used by aggregate transition value is
+		 * specified in pg_aggregate, then use it for transitionSpace.
+		 */
+		if (aggtransspace > 0)
+		{
+			costs->transitionSpace += aggtransspace;
+		}
 		/*
 		 * If the transition type is pass-by-value then it doesn't add
 		 * anything to the required size of the hashtable.	If it is
 		 * pass-by-reference then we have to add the estimated size of the
 		 * value itself, plus palloc overhead.
 		 */
-		if (!get_typbyval(aggtranstype))
+		else if (!get_typbyval(aggtranstype))
 		{
 			int32		aggtranstypmod;
 			int32		avgwidth;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 9458429..fd04c54 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -11268,12 +11268,14 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	int			i_aggfinalfn;
 	int			i_aggsortop;
 	int			i_aggtranstype;
+	int 		i_aggtransspace;
 	int			i_agginitval;
 	int			i_convertok;
 	const char *aggtransfn;
 	const char *aggfinalfn;
 	const char *aggsortop;
 	const char *aggtranstype;
+	const char *aggtransspace;
 	const char *agginitval;
 	bool		convertok;
 
@@ -11291,12 +11293,24 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	selectSourceSchema(fout, agginfo->aggfn.dobj.namespace->dobj.name);
 
 	/* Get aggregate-specific details */
-	if (fout->remoteVersion >= 80100)
+	if (fout->remoteVersion >= 90300)
+	{
+		appendPQExpBuffer(query, "SELECT aggtransfn, "
+						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
+						  "aggsortop::pg_catalog.regoperator, "
+						  "aggtransspace, agginitval, "
+						  "'t'::boolean AS convertok "
+					  "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
+						  "WHERE a.aggfnoid = p.oid "
+						  "AND p.oid = '%u'::pg_catalog.oid",
+						  agginfo->aggfn.dobj.catId.oid);
+	}
+	else if (fout->remoteVersion >= 80100)
 	{
 		appendPQExpBuffer(query, "SELECT aggtransfn, "
 						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
 						  "aggsortop::pg_catalog.regoperator, "
-						  "agginitval, "
+						  "0 AS aggtransspace, agginitval, "
 						  "'t'::boolean AS convertok "
 					  "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
 						  "WHERE a.aggfnoid = p.oid "
@@ -11308,7 +11322,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 		appendPQExpBuffer(query, "SELECT aggtransfn, "
 						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
 						  "0 AS aggsortop, "
-						  "agginitval, "
+						  "0 AS aggtransspace, agginitval, "
 						  "'t'::boolean AS convertok "
 					  "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
 						  "WHERE a.aggfnoid = p.oid "
@@ -11320,7 +11334,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 		appendPQExpBuffer(query, "SELECT aggtransfn, aggfinalfn, "
 						  "format_type(aggtranstype, NULL) AS aggtranstype, "
 						  "0 AS aggsortop, "
-						  "agginitval, "
+						  "0 AS aggtransspace, agginitval, "
 						  "'t'::boolean AS convertok "
 						  "FROM pg_aggregate "
 						  "WHERE oid = '%u'::oid",
@@ -11332,7 +11346,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 						  "aggfinalfn, "
 						  "(SELECT typname FROM pg_type WHERE oid = aggtranstype1) AS aggtranstype, "
 						  "0 AS aggsortop, "
-						  "agginitval1 AS agginitval, "
+						  "0 AS aggtransspace, agginitval1 AS agginitval, "
 						  "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) AS convertok "
 						  "FROM pg_aggregate "
 						  "WHERE oid = '%u'::oid",
@@ -11345,6 +11359,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	i_aggfinalfn = PQfnumber(res, "aggfinalfn");
 	i_aggsortop = PQfnumber(res, "aggsortop");
 	i_aggtranstype = PQfnumber(res, "aggtranstype");
+	i_aggtransspace = PQfnumber(res, "aggtransspace");
 	i_agginitval = PQfnumber(res, "agginitval");
 	i_convertok = PQfnumber(res, "convertok");
 
@@ -11352,6 +11367,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn);
 	aggsortop = PQgetvalue(res, 0, i_aggsortop);
 	aggtranstype = PQgetvalue(res, 0, i_aggtranstype);
+	aggtransspace = PQgetvalue(res, 0, i_aggtransspace);
 	agginitval = PQgetvalue(res, 0, i_agginitval);
 	convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't');
 
@@ -11388,6 +11404,12 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 						  fmtId(aggtranstype));
 	}
 
+	if (strcmp(aggtransspace, "0") != 0)
+	{
+		appendPQExpBuffer(details, ",\n    SSPACE = %s",
+						  aggtransspace);
+	}
+
 	if (!PQgetisnull(res, 0, i_agginitval))
 	{
 		appendPQExpBuffer(details, ",\n    INITCOND = ");
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 6fb10a9..65ab160 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -32,6 +32,7 @@
  *	aggfinalfn			final function (0 if none)
  *	aggsortop			associated sort operator (0 if none)
  *	aggtranstype		type of aggregate's transition (state) data
+ *	aggtransspace		approximate size of aggregate transition state
  *	agginitval			initial value for transition state (can be NULL)
  * ----------------------------------------------------------------
  */
@@ -44,6 +45,7 @@ CATALOG(pg_aggregate,2600) BKI_WITHOUT_OIDS
 	regproc		aggfinalfn;
 	Oid			aggsortop;
 	Oid			aggtranstype;
+	int32 		aggtransspace;
 
 #ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	text		agginitval;
@@ -62,13 +64,14 @@ typedef FormData_pg_aggregate *Form_pg_aggregate;
  * ----------------
  */
 
-#define Natts_pg_aggregate				6
+#define Natts_pg_aggregate				7
 #define Anum_pg_aggregate_aggfnoid		1
 #define Anum_pg_aggregate_aggtransfn	2
 #define Anum_pg_aggregate_aggfinalfn	3
 #define Anum_pg_aggregate_aggsortop		4
 #define Anum_pg_aggregate_aggtranstype	5
-#define Anum_pg_aggregate_agginitval	6
+#define Anum_pg_aggregate_aggtransspace	6
+#define Anum_pg_aggregate_agginitval	7
 
 
 /* ----------------
@@ -77,163 +80,163 @@ typedef FormData_pg_aggregate *Form_pg_aggregate;
  */
 
 /* avg */
-DATA(insert ( 2100	int8_avg_accum	numeric_avg		0	1231	"{0,0}" ));
-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 ( 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 ( 2100	int8_avg_accum	numeric_avg		0	1231	0	"{0,0}" ));
+DATA(insert ( 2101	int4_avg_accum	int8_avg		0	1016	0	"{0,0}" ));
+DATA(insert ( 2102	int2_avg_accum	int8_avg		0	1016	0	"{0,0}" ));
+DATA(insert ( 2103	numeric_avg_accum	numeric_avg		0	1231	0	"{0,0}" ));
+DATA(insert ( 2104	float4_accum	float8_avg		0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2105	float8_accum	float8_avg		0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2106	interval_accum	interval_avg	0	1187	0	"{0 second,0 second}" ));
 
 /* sum */
-DATA(insert ( 2107	int8_sum		-				0	1700	_null_ ));
-DATA(insert ( 2108	int4_sum		-				0	20		_null_ ));
-DATA(insert ( 2109	int2_sum		-				0	20		_null_ ));
-DATA(insert ( 2110	float4pl		-				0	700		_null_ ));
-DATA(insert ( 2111	float8pl		-				0	701		_null_ ));
-DATA(insert ( 2112	cash_pl			-				0	790		_null_ ));
-DATA(insert ( 2113	interval_pl		-				0	1186	_null_ ));
-DATA(insert ( 2114	numeric_add		-				0	1700	_null_ ));
+DATA(insert ( 2107	int8_sum		-				0	1700	0	_null_ ));
+DATA(insert ( 2108	int4_sum		-				0	20		0	_null_ ));
+DATA(insert ( 2109	int2_sum		-				0	20		0	_null_ ));
+DATA(insert ( 2110	float4pl		-				0	700		0	_null_ ));
+DATA(insert ( 2111	float8pl		-				0	701		0	_null_ ));
+DATA(insert ( 2112	cash_pl			-				0	790		0	_null_ ));
+DATA(insert ( 2113	interval_pl		-				0	1186	0	_null_ ));
+DATA(insert ( 2114	numeric_add		-				0	1700	0	_null_ ));
 
 /* max */
-DATA(insert ( 2115	int8larger		-				413		20		_null_ ));
-DATA(insert ( 2116	int4larger		-				521		23		_null_ ));
-DATA(insert ( 2117	int2larger		-				520		21		_null_ ));
-DATA(insert ( 2118	oidlarger		-				610		26		_null_ ));
-DATA(insert ( 2119	float4larger	-				623		700		_null_ ));
-DATA(insert ( 2120	float8larger	-				674		701		_null_ ));
-DATA(insert ( 2121	int4larger		-				563		702		_null_ ));
-DATA(insert ( 2122	date_larger		-				1097	1082	_null_ ));
-DATA(insert ( 2123	time_larger		-				1112	1083	_null_ ));
-DATA(insert ( 2124	timetz_larger	-				1554	1266	_null_ ));
-DATA(insert ( 2125	cashlarger		-				903		790		_null_ ));
-DATA(insert ( 2126	timestamp_larger	-			2064	1114	_null_ ));
-DATA(insert ( 2127	timestamptz_larger	-			1324	1184	_null_ ));
-DATA(insert ( 2128	interval_larger -				1334	1186	_null_ ));
-DATA(insert ( 2129	text_larger		-				666		25		_null_ ));
-DATA(insert ( 2130	numeric_larger	-				1756	1700	_null_ ));
-DATA(insert ( 2050	array_larger	-				1073	2277	_null_ ));
-DATA(insert ( 2244	bpchar_larger	-				1060	1042	_null_ ));
-DATA(insert ( 2797	tidlarger		-				2800	27		_null_ ));
-DATA(insert ( 3526	enum_larger		-				3519	3500	_null_ ));
+DATA(insert ( 2115	int8larger		-				413		20		0	_null_ ));
+DATA(insert ( 2116	int4larger		-				521		23		0	_null_ ));
+DATA(insert ( 2117	int2larger		-				520		21		0	_null_ ));
+DATA(insert ( 2118	oidlarger		-				610		26		0	_null_ ));
+DATA(insert ( 2119	float4larger	-				623		700		0	_null_ ));
+DATA(insert ( 2120	float8larger	-				674		701		0	_null_ ));
+DATA(insert ( 2121	int4larger		-				563		702		0	_null_ ));
+DATA(insert ( 2122	date_larger		-				1097	1082	0	_null_ ));
+DATA(insert ( 2123	time_larger		-				1112	1083	0	_null_ ));
+DATA(insert ( 2124	timetz_larger	-				1554	1266	0	_null_ ));
+DATA(insert ( 2125	cashlarger		-				903		790		0	_null_ ));
+DATA(insert ( 2126	timestamp_larger	-			2064	1114	0	_null_ ));
+DATA(insert ( 2127	timestamptz_larger	-			1324	1184	0	_null_ ));
+DATA(insert ( 2128	interval_larger -				1334	1186	0	_null_ ));
+DATA(insert ( 2129	text_larger		-				666		25		0	_null_ ));
+DATA(insert ( 2130	numeric_larger	-				1756	1700	0	_null_ ));
+DATA(insert ( 2050	array_larger	-				1073	2277	0	_null_ ));
+DATA(insert ( 2244	bpchar_larger	-				1060	1042	0	_null_ ));
+DATA(insert ( 2797	tidlarger		-				2800	27		0	_null_ ));
+DATA(insert ( 3526	enum_larger		-				3519	3500	0	_null_ ));
 
 /* min */
-DATA(insert ( 2131	int8smaller		-				412		20		_null_ ));
-DATA(insert ( 2132	int4smaller		-				97		23		_null_ ));
-DATA(insert ( 2133	int2smaller		-				95		21		_null_ ));
-DATA(insert ( 2134	oidsmaller		-				609		26		_null_ ));
-DATA(insert ( 2135	float4smaller	-				622		700		_null_ ));
-DATA(insert ( 2136	float8smaller	-				672		701		_null_ ));
-DATA(insert ( 2137	int4smaller		-				562		702		_null_ ));
-DATA(insert ( 2138	date_smaller	-				1095	1082	_null_ ));
-DATA(insert ( 2139	time_smaller	-				1110	1083	_null_ ));
-DATA(insert ( 2140	timetz_smaller	-				1552	1266	_null_ ));
-DATA(insert ( 2141	cashsmaller		-				902		790		_null_ ));
-DATA(insert ( 2142	timestamp_smaller	-			2062	1114	_null_ ));
-DATA(insert ( 2143	timestamptz_smaller -			1322	1184	_null_ ));
-DATA(insert ( 2144	interval_smaller	-			1332	1186	_null_ ));
-DATA(insert ( 2145	text_smaller	-				664		25		_null_ ));
-DATA(insert ( 2146	numeric_smaller -				1754	1700	_null_ ));
-DATA(insert ( 2051	array_smaller	-				1072	2277	_null_ ));
-DATA(insert ( 2245	bpchar_smaller	-				1058	1042	_null_ ));
-DATA(insert ( 2798	tidsmaller		-				2799	27		_null_ ));
-DATA(insert ( 3527	enum_smaller	-				3518	3500	_null_ ));
+DATA(insert ( 2131	int8smaller		-				412		20		0	_null_ ));
+DATA(insert ( 2132	int4smaller		-				97		23		0	_null_ ));
+DATA(insert ( 2133	int2smaller		-				95		21		0	_null_ ));
+DATA(insert ( 2134	oidsmaller		-				609		26		0	_null_ ));
+DATA(insert ( 2135	float4smaller	-				622		700		0	_null_ ));
+DATA(insert ( 2136	float8smaller	-				672		701		0	_null_ ));
+DATA(insert ( 2137	int4smaller		-				562		702		0	_null_ ));
+DATA(insert ( 2138	date_smaller	-				1095	1082	0	_null_ ));
+DATA(insert ( 2139	time_smaller	-				1110	1083	0	_null_ ));
+DATA(insert ( 2140	timetz_smaller	-				1552	1266	0	_null_ ));
+DATA(insert ( 2141	cashsmaller		-				902		790		0	_null_ ));
+DATA(insert ( 2142	timestamp_smaller	-			2062	1114	0	_null_ ));
+DATA(insert ( 2143	timestamptz_smaller -			1322	1184	0	_null_ ));
+DATA(insert ( 2144	interval_smaller	-			1332	1186	0	_null_ ));
+DATA(insert ( 2145	text_smaller	-				664		25		0	_null_ ));
+DATA(insert ( 2146	numeric_smaller -				1754	1700	0	_null_ ));
+DATA(insert ( 2051	array_smaller	-				1072	2277	0	_null_ ));
+DATA(insert ( 2245	bpchar_smaller	-				1058	1042	0	_null_ ));
+DATA(insert ( 2798	tidsmaller		-				2799	27		0	_null_ ));
+DATA(insert ( 3527	enum_smaller	-				3518	3500	0	_null_ ));
 
 /* count */
-DATA(insert ( 2147	int8inc_any		-				0		20		"0" ));
-DATA(insert ( 2803	int8inc			-				0		20		"0" ));
+DATA(insert ( 2147	int8inc_any		-				0		20		0	"0" ));
+DATA(insert ( 2803	int8inc			-				0		20		0	"0" ));
 
 /* var_pop */
-DATA(insert ( 2718	int8_accum	numeric_var_pop 0	1231	"{0,0,0}" ));
-DATA(insert ( 2719	int4_accum	numeric_var_pop 0	1231	"{0,0,0}" ));
-DATA(insert ( 2720	int2_accum	numeric_var_pop 0	1231	"{0,0,0}" ));
-DATA(insert ( 2721	float4_accum	float8_var_pop 0	1022	"{0,0,0}" ));
-DATA(insert ( 2722	float8_accum	float8_var_pop 0	1022	"{0,0,0}" ));
-DATA(insert ( 2723	numeric_accum  numeric_var_pop 0	1231	"{0,0,0}" ));
+DATA(insert ( 2718	int8_accum	numeric_var_pop 0	1231	0	"{0,0,0}" ));
+DATA(insert ( 2719	int4_accum	numeric_var_pop 0	1231	0	"{0,0,0}" ));
+DATA(insert ( 2720	int2_accum	numeric_var_pop 0	1231	0	"{0,0,0}" ));
+DATA(insert ( 2721	float4_accum	float8_var_pop 0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2722	float8_accum	float8_var_pop 0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2723	numeric_accum  numeric_var_pop 0	1231	0	"{0,0,0}" ));
 
 /* var_samp */
-DATA(insert ( 2641	int8_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
-DATA(insert ( 2642	int4_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
-DATA(insert ( 2643	int2_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
-DATA(insert ( 2644	float4_accum	float8_var_samp 0	1022	"{0,0,0}" ));
-DATA(insert ( 2645	float8_accum	float8_var_samp 0	1022	"{0,0,0}" ));
-DATA(insert ( 2646	numeric_accum  numeric_var_samp 0	1231	"{0,0,0}" ));
+DATA(insert ( 2641	int8_accum	numeric_var_samp	0	1231	0	"{0,0,0}" ));
+DATA(insert ( 2642	int4_accum	numeric_var_samp	0	1231	0	"{0,0,0}" ));
+DATA(insert ( 2643	int2_accum	numeric_var_samp	0	1231	0	"{0,0,0}" ));
+DATA(insert ( 2644	float4_accum	float8_var_samp 0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2645	float8_accum	float8_var_samp 0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2646	numeric_accum  numeric_var_samp 0	1231	0	"{0,0,0}" ));
 
 /* variance: historical Postgres syntax for var_samp */
-DATA(insert ( 2148	int8_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
-DATA(insert ( 2149	int4_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
-DATA(insert ( 2150	int2_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
-DATA(insert ( 2151	float4_accum	float8_var_samp 0	1022	"{0,0,0}" ));
-DATA(insert ( 2152	float8_accum	float8_var_samp 0	1022	"{0,0,0}" ));
-DATA(insert ( 2153	numeric_accum  numeric_var_samp 0	1231	"{0,0,0}" ));
+DATA(insert ( 2148	int8_accum	numeric_var_samp	0	1231	0	"{0,0,0}" ));
+DATA(insert ( 2149	int4_accum	numeric_var_samp	0	1231	0	"{0,0,0}" ));
+DATA(insert ( 2150	int2_accum	numeric_var_samp	0	1231	0	"{0,0,0}" ));
+DATA(insert ( 2151	float4_accum	float8_var_samp 0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2152	float8_accum	float8_var_samp 0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2153	numeric_accum  numeric_var_samp 0	1231	0	"{0,0,0}" ));
 
 /* stddev_pop */
-DATA(insert ( 2724	int8_accum	numeric_stddev_pop		0	1231	"{0,0,0}" ));
-DATA(insert ( 2725	int4_accum	numeric_stddev_pop		0	1231	"{0,0,0}" ));
-DATA(insert ( 2726	int2_accum	numeric_stddev_pop		0	1231	"{0,0,0}" ));
-DATA(insert ( 2727	float4_accum	float8_stddev_pop	0	1022	"{0,0,0}" ));
-DATA(insert ( 2728	float8_accum	float8_stddev_pop	0	1022	"{0,0,0}" ));
-DATA(insert ( 2729	numeric_accum	numeric_stddev_pop	0	1231	"{0,0,0}" ));
+DATA(insert ( 2724	int8_accum	numeric_stddev_pop		0	1231	0	"{0,0,0}" ));
+DATA(insert ( 2725	int4_accum	numeric_stddev_pop		0	1231	0	"{0,0,0}" ));
+DATA(insert ( 2726	int2_accum	numeric_stddev_pop		0	1231	0	"{0,0,0}" ));
+DATA(insert ( 2727	float4_accum	float8_stddev_pop	0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2728	float8_accum	float8_stddev_pop	0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2729	numeric_accum	numeric_stddev_pop	0	1231	0	"{0,0,0}" ));
 
 /* stddev_samp */
-DATA(insert ( 2712	int8_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
-DATA(insert ( 2713	int4_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
-DATA(insert ( 2714	int2_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
-DATA(insert ( 2715	float4_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
-DATA(insert ( 2716	float8_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
-DATA(insert ( 2717	numeric_accum	numeric_stddev_samp 0	1231	"{0,0,0}" ));
+DATA(insert ( 2712	int8_accum	numeric_stddev_samp		0	1231	0	"{0,0,0}" ));
+DATA(insert ( 2713	int4_accum	numeric_stddev_samp		0	1231	0	"{0,0,0}" ));
+DATA(insert ( 2714	int2_accum	numeric_stddev_samp		0	1231	0	"{0,0,0}" ));
+DATA(insert ( 2715	float4_accum	float8_stddev_samp	0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2716	float8_accum	float8_stddev_samp	0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2717	numeric_accum	numeric_stddev_samp 0	1231	0	"{0,0,0}" ));
 
 /* stddev: historical Postgres syntax for stddev_samp */
-DATA(insert ( 2154	int8_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
-DATA(insert ( 2155	int4_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
-DATA(insert ( 2156	int2_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
-DATA(insert ( 2157	float4_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
-DATA(insert ( 2158	float8_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
-DATA(insert ( 2159	numeric_accum	numeric_stddev_samp 0	1231	"{0,0,0}" ));
+DATA(insert ( 2154	int8_accum	numeric_stddev_samp		0	1231	0	"{0,0,0}" ));
+DATA(insert ( 2155	int4_accum	numeric_stddev_samp		0	1231	0	"{0,0,0}" ));
+DATA(insert ( 2156	int2_accum	numeric_stddev_samp		0	1231	0	"{0,0,0}" ));
+DATA(insert ( 2157	float4_accum	float8_stddev_samp	0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2158	float8_accum	float8_stddev_samp	0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2159	numeric_accum	numeric_stddev_samp 0	1231	0	"{0,0,0}" ));
 
 /* SQL2003 binary regression aggregates */
-DATA(insert ( 2818	int8inc_float8_float8		-				0	20		"0" ));
-DATA(insert ( 2819	float8_regr_accum	float8_regr_sxx			0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2820	float8_regr_accum	float8_regr_syy			0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2821	float8_regr_accum	float8_regr_sxy			0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2822	float8_regr_accum	float8_regr_avgx		0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2823	float8_regr_accum	float8_regr_avgy		0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2824	float8_regr_accum	float8_regr_r2			0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2825	float8_regr_accum	float8_regr_slope		0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2826	float8_regr_accum	float8_regr_intercept	0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2827	float8_regr_accum	float8_covar_pop		0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2828	float8_regr_accum	float8_covar_samp		0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2829	float8_regr_accum	float8_corr				0	1022	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2818	int8inc_float8_float8		-				0	20		0	"0" ));
+DATA(insert ( 2819	float8_regr_accum	float8_regr_sxx			0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2820	float8_regr_accum	float8_regr_syy			0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2821	float8_regr_accum	float8_regr_sxy			0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2822	float8_regr_accum	float8_regr_avgx		0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2823	float8_regr_accum	float8_regr_avgy		0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2824	float8_regr_accum	float8_regr_r2			0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2825	float8_regr_accum	float8_regr_slope		0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2826	float8_regr_accum	float8_regr_intercept	0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2827	float8_regr_accum	float8_covar_pop		0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2828	float8_regr_accum	float8_covar_samp		0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2829	float8_regr_accum	float8_corr				0	1022	0	"{0,0,0,0,0,0}" ));
 
 /* boolean-and and boolean-or */
-DATA(insert ( 2517	booland_statefunc	-			58	16		_null_ ));
-DATA(insert ( 2518	boolor_statefunc	-			59	16		_null_ ));
-DATA(insert ( 2519	booland_statefunc	-			58	16		_null_ ));
+DATA(insert ( 2517	booland_statefunc	-			58	16		0	_null_ ));
+DATA(insert ( 2518	boolor_statefunc	-			59	16		0	_null_ ));
+DATA(insert ( 2519	booland_statefunc	-			58	16		0	_null_ ));
 
 /* bitwise integer */
-DATA(insert ( 2236 int2and		  -					0	21		_null_ ));
-DATA(insert ( 2237 int2or		  -					0	21		_null_ ));
-DATA(insert ( 2238 int4and		  -					0	23		_null_ ));
-DATA(insert ( 2239 int4or		  -					0	23		_null_ ));
-DATA(insert ( 2240 int8and		  -					0	20		_null_ ));
-DATA(insert ( 2241 int8or		  -					0	20		_null_ ));
-DATA(insert ( 2242 bitand		  -					0	1560	_null_ ));
-DATA(insert ( 2243 bitor		  -					0	1560	_null_ ));
+DATA(insert ( 2236 int2and		  -					0	21		0	_null_ ));
+DATA(insert ( 2237 int2or		  -					0	21		0	_null_ ));
+DATA(insert ( 2238 int4and		  -					0	23		0	_null_ ));
+DATA(insert ( 2239 int4or		  -					0	23		0	_null_ ));
+DATA(insert ( 2240 int8and		  -					0	20		0	_null_ ));
+DATA(insert ( 2241 int8or		  -					0	20		0	_null_ ));
+DATA(insert ( 2242 bitand		  -					0	1560	0	_null_ ));
+DATA(insert ( 2243 bitor		  -					0	1560	0	_null_ ));
 
 /* xml */
-DATA(insert ( 2901 xmlconcat2	  -					0	142		_null_ ));
+DATA(insert ( 2901 xmlconcat2	  -					0	142		0	_null_ ));
 
 /* array */
-DATA(insert ( 2335	array_agg_transfn	array_agg_finalfn		0	2281	_null_ ));
+DATA(insert ( 2335	array_agg_transfn	array_agg_finalfn		0	2281	0	_null_ ));
 
 /* text */
-DATA(insert ( 3538	string_agg_transfn	string_agg_finalfn		0	2281	_null_ ));
+DATA(insert ( 3538	string_agg_transfn	string_agg_finalfn		0	2281	0	_null_ ));
 
 /* bytea */
-DATA(insert ( 3545	bytea_string_agg_transfn	bytea_string_agg_finalfn		0	2281	_null_ ));
+DATA(insert ( 3545	bytea_string_agg_transfn	bytea_string_agg_finalfn		0	2281	0	_null_ ));
 
 /* json */
-DATA(insert ( 3175	json_agg_transfn	json_agg_finalfn		0	2281	_null_ ));
+DATA(insert ( 3175	json_agg_transfn	json_agg_finalfn		0	2281	0	_null_ ));
 
 /*
  * prototypes for functions in pg_aggregate.c
@@ -246,6 +249,7 @@ extern Oid AggregateCreate(const char *aggName,
 				List *aggfinalfnName,
 				List *aggsortopName,
 				Oid aggTransType,
+				int32 aggTransSpace,
 				const char *agginitval);
 
 #endif   /* PG_AGGREGATE_H */
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 62515b2..3abc89b 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -122,6 +122,7 @@ extern Datum transformGenericOptions(Oid catalogId,
 extern char *defGetString(DefElem *def);
 extern double defGetNumeric(DefElem *def);
 extern bool defGetBoolean(DefElem *def);
+extern int32 defGetInt32(DefElem *def);
 extern int64 defGetInt64(DefElem *def);
 extern List *defGetQualifiedName(DefElem *def);
 extern TypeName *defGetTypeName(DefElem *def);
#15Pavel Stehule
pavel.stehule@gmail.com
In reply to: Hadi Moshayedi (#14)
Re: Improving avg performance for numeric

Hello

2013/3/20 Hadi Moshayedi <hadi@moshayedi.net>:

Hi Tom,

Tom Lane <tgl@sss.pgh.pa.us> wrote:

After thinking about that for awhile: if we pursue this type of
optimization, what would probably be appropriate is to add an aggregate
property (stored in pg_aggregate) that allows direct specification of
the size that the planner should assume for the aggregate's transition
value. We were getting away with a hardwired assumption of 8K for
"internal" because the existing aggregates that used that transtype all
had similar properties, but it was always really a band-aid not a proper
solution. A per-aggregate override could be useful in other cases too.

Cool.

I created a patch which adds an aggregate property to pg_aggregate, so
the transition space is can be overridden. This patch doesn't contain
the numeric optimizations. It uses "0" (meaning not-set) for all
existing aggregates.

I manual-tested it a bit, by changing this value for aggregates and
observing the changes in plan. I also updated some docs and pg_dump.

Does this look like something along the lines of what you meant?

please, can you subscribe your patch to next commitfest?

I tested this patch, and it increase performance about 20% what is
interesting. More - it allows more comfortable custom aggregates for
custom types with better hash agg support.

Regards

Pavel

Thanks,
-- Hadi

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#16Hadi Moshayedi
hadi@moshayedi.net
In reply to: Pavel Stehule (#15)
1 attachment(s)
Re: Improving avg performance for numeric

I am attaching the updated the patch, which also fixes a bug which
caused one of the regression tests failed.

I'll subscribe this patch to the commitfest in the next hour.

Can you please review the patch?

Thanks,
-- Hadi

Attachments:

numeric-optimize-v3.patchapplication/octet-stream; name=numeric-optimize-v3.patchDownload
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 6715782..1fae7db 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -373,6 +373,12 @@
       <entry>Data type of the aggregate function's internal transition (state) data</entry>
      </row>
      <row>
+      <entry><structfield>aggtransspace</structfield></entry>
+      <entry><type>int4</type></entry>
+      <entry></entry>
+      <entry>Approximation for the average size of the aggregate function's internal transition (state) data</entry>
+     </row>
+     <row>
       <entry><structfield>agginitval</structfield></entry>
       <entry><type>text</type></entry>
       <entry></entry>
diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml
index d5e4e27..594d095 100644
--- a/doc/src/sgml/ref/create_aggregate.sgml
+++ b/doc/src/sgml/ref/create_aggregate.sgml
@@ -24,6 +24,7 @@ PostgreSQL documentation
 CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( <replaceable class="PARAMETER">input_data_type</replaceable> [ , ... ] ) (
     SFUNC = <replaceable class="PARAMETER">sfunc</replaceable>,
     STYPE = <replaceable class="PARAMETER">state_data_type</replaceable>
+    [ , SSPACE = <replaceable class="PARAMETER">state_data_size</replaceable> ]
     [ , FINALFUNC = <replaceable class="PARAMETER">ffunc</replaceable> ]
     [ , INITCOND = <replaceable class="PARAMETER">initial_condition</replaceable> ]
     [ , SORTOP = <replaceable class="PARAMETER">sort_operator</replaceable> ]
@@ -35,6 +36,7 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> (
     BASETYPE = <replaceable class="PARAMETER">base_type</replaceable>,
     SFUNC = <replaceable class="PARAMETER">sfunc</replaceable>,
     STYPE = <replaceable class="PARAMETER">state_data_type</replaceable>
+    [ , SSPACE = <replaceable class="PARAMETER">state_data_size</replaceable> ]
     [ , FINALFUNC = <replaceable class="PARAMETER">ffunc</replaceable> ]
     [ , INITCOND = <replaceable class="PARAMETER">initial_condition</replaceable> ]
     [ , SORTOP = <replaceable class="PARAMETER">sort_operator</replaceable> ]
@@ -241,6 +243,18 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
    </varlistentry>
 
    <varlistentry>
+    <term><replaceable class="PARAMETER">state_data_size</replaceable></term>
+    <listitem>
+     <para>
+      Approximate average size (in bytes) of aggregate's state value. 
+      Planner uses this value to approximate the memory required for 
+      the aggregation. If this value is not provided, a default value is 
+      used based on state_data_type.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="PARAMETER">ffunc</replaceable></term>
     <listitem>
      <para>
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index e80b600..d08e14a 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -51,6 +51,7 @@ AggregateCreate(const char *aggName,
 				List *aggfinalfnName,
 				List *aggsortopName,
 				Oid aggTransType,
+				int32 aggTransSpace,
 				const char *agginitval)
 {
 	Relation	aggdesc;
@@ -269,6 +270,7 @@ AggregateCreate(const char *aggName,
 	values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
 	values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop);
 	values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
+	values[Anum_pg_aggregate_aggtransspace - 1] = Int32GetDatum(aggTransSpace);
 	if (agginitval)
 		values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval);
 	else
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c
index 4a03786..656bf7a 100644
--- a/src/backend/commands/aggregatecmds.c
+++ b/src/backend/commands/aggregatecmds.c
@@ -62,6 +62,7 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
 	Oid		   *aggArgTypes;
 	int			numArgs;
 	Oid			transTypeId;
+	int32		transSpace = 0;
 	char		transTypeType;
 	ListCell   *pl;
 
@@ -96,6 +97,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
 			transType = defGetTypeName(defel);
 		else if (pg_strcasecmp(defel->defname, "stype1") == 0)
 			transType = defGetTypeName(defel);
+		else if (pg_strcasecmp(defel->defname, "sspace") == 0)
+			transSpace = defGetInt32(defel);
 		else if (pg_strcasecmp(defel->defname, "initcond") == 0)
 			initval = defGetString(defel);
 		else if (pg_strcasecmp(defel->defname, "initcond1") == 0)
@@ -225,5 +228,6 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
 						   finalfuncName,		/* final function name */
 						   sortoperatorName,	/* sort operator name */
 						   transTypeId, /* transition data type */
+						   transSpace,			/* transition space */
 						   initval);	/* initial condition */
 }
diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c
index 9fa222f..75f77da 100644
--- a/src/backend/commands/define.c
+++ b/src/backend/commands/define.c
@@ -165,6 +165,30 @@ defGetBoolean(DefElem *def)
 }
 
 /*
+ * Extract an int32 value from a DefElem.
+ */
+int32
+defGetInt32(DefElem *def)
+{
+	if (def->arg == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("%s requires an integer value",
+						def->defname)));
+	switch (nodeTag(def->arg))
+	{
+		case T_Integer:
+			return (int32) intVal(def->arg);
+		default:
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("%s requires an integer value",
+							def->defname)));
+	}
+	return 0;					/* keep compiler quiet */
+}
+
+/*
  * Extract an int64 value from a DefElem.
  */
 int64
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 6d5b204..5d8f56d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -461,6 +461,7 @@ count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
 		Oid			aggtransfn;
 		Oid			aggfinalfn;
 		Oid			aggtranstype;
+		int32 		aggtransspace;
 		QualCost	argcosts;
 		Oid		   *inputTypes;
 		int			numArguments;
@@ -478,6 +479,7 @@ count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
 		aggtransfn = aggform->aggtransfn;
 		aggfinalfn = aggform->aggfinalfn;
 		aggtranstype = aggform->aggtranstype;
+		aggtransspace = aggform->aggtransspace;
 		ReleaseSysCache(aggTuple);
 
 		/* count it */
@@ -524,13 +526,21 @@ count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
 			pfree(declaredArgTypes);
 		}
 
+		/* 
+		 * If approximate average space used by aggregate transition value is
+		 * specified in pg_aggregate, then use it for transitionSpace.
+		 */
+		if (aggtransspace > 0)
+		{
+			costs->transitionSpace += aggtransspace;
+		}
 		/*
 		 * If the transition type is pass-by-value then it doesn't add
 		 * anything to the required size of the hashtable.	If it is
 		 * pass-by-reference then we have to add the estimated size of the
 		 * value itself, plus palloc overhead.
 		 */
-		if (!get_typbyval(aggtranstype))
+		else if (!get_typbyval(aggtranstype))
 		{
 			int32		aggtranstypmod;
 			int32		avgwidth;
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index b4d6394..9a4706b 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -2464,108 +2464,157 @@ numeric_float4(PG_FUNCTION_ARGS)
  *
  * Aggregate functions
  *
- * The transition datatype for all these aggregates is a 3-element array
- * of Numeric, holding the values N, sum(X), sum(X*X) in that order.
- *
- * We represent N as a numeric mainly to avoid having to build a special
- * datatype; it's unlikely it'd overflow an int4, but ...
+ * The transition datatype for all these aggregates is a pointer to 
+ * a struct NumericAggState allocated in the aggregate context.
  *
  * ----------------------------------------------------------------------
  */
 
-static ArrayType *
-do_numeric_accum(ArrayType *transarray, Numeric newval)
+typedef struct NumericAggState
+{
+	bool 			first;
+	bool 			isNaN;
+	uint64			N;
+	NumericVar 		sumX;
+	NumericVar 		sumX2;
+	bool 			calcSumX2;
+	MemoryContext 	agg_context;
+} NumericAggState;
+
+
+static NumericAggState *
+makeNumericAggState(FunctionCallInfo fcinfo, bool calcSumX2)
 {
-	Datum	   *transdatums;
-	int			ndatums;
-	Datum		N,
-				sumX,
-				sumX2;
-	ArrayType  *result;
-
-	/* We assume the input is array of numeric */
-	deconstruct_array(transarray,
-					  NUMERICOID, -1, false, 'i',
-					  &transdatums, NULL, &ndatums);
-	if (ndatums != 3)
-		elog(ERROR, "expected 3-element numeric array");
-	N = transdatums[0];
-	sumX = transdatums[1];
-	sumX2 = transdatums[2];
-
-	N = DirectFunctionCall1(numeric_inc, N);
-	sumX = DirectFunctionCall2(numeric_add, sumX,
-							   NumericGetDatum(newval));
-	sumX2 = DirectFunctionCall2(numeric_add, sumX2,
-								DirectFunctionCall2(numeric_mul,
-													NumericGetDatum(newval),
-													NumericGetDatum(newval)));
-
-	transdatums[0] = N;
-	transdatums[1] = sumX;
-	transdatums[2] = sumX2;
-
-	result = construct_array(transdatums, 3,
-							 NUMERICOID, -1, false, 'i');
+	NumericAggState 	*state;
+	MemoryContext 		 agg_context;
+	MemoryContext 		 old_context;
 
-	return result;
+	if (!AggCheckCallContext(fcinfo, &agg_context))
+	{
+		elog(ERROR, "this is called in non-aggregate context");
+	}
+
+	old_context = MemoryContextSwitchTo(agg_context);
+
+	state = palloc0(sizeof(NumericAggState));
+	state->first = true;
+	state->calcSumX2 = calcSumX2;
+	state->agg_context = agg_context;
+
+	MemoryContextSwitchTo(old_context);
+
+	return state;
 }
 
-/*
- * Improve avg performance by not caclulating sum(X*X).
- */
-static ArrayType *
-do_numeric_avg_accum(ArrayType *transarray, Numeric newval)
+
+static void
+do_numeric_accum(NumericAggState *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));
-
-	transdatums[0] = N;
-	transdatums[1] = sumX;
-
-	result = construct_array(transdatums, 2,
-							 NUMERICOID, -1, false, 'i');
+	NumericVar 		X;
+	NumericVar 		X2;
+	MemoryContext 	old_context;
+	bool 			first;
+	
+	first = state->first;
+	state->first = false;
+	state->N++;
+
+	if (state->isNaN || NUMERIC_IS_NAN(newval))
+	{
+		state->isNaN = true;
+		return;
+	}
 
-	return result;
+	init_var_from_num(newval, &X);
+
+	if (state->calcSumX2)
+	{
+		init_var(&X2);
+		mul_var(&X, &X, &X2, X.dscale * 2);
+	}
+
+	old_context = MemoryContextSwitchTo(state->agg_context);
+
+	if (!first)
+	{
+		NumericVar preSumX;
+
+		memcpy(&preSumX, &(state->sumX), sizeof(NumericVar));
+		init_var(&(state->sumX));
+		add_var(&X, &preSumX, &(state->sumX));
+		free_var(&preSumX);
+
+		if (state->calcSumX2)
+		{
+			NumericVar preSumX2;
+
+			memcpy(&preSumX2, &(state->sumX2), sizeof(NumericVar));
+			init_var(&(state->sumX2));
+			add_var(&X2, &preSumX2, &(state->sumX2));
+			free_var(&preSumX2);
+		}
+	}
+	else
+	{
+		set_var_from_var(&X, &(state->sumX));
+
+		if (state->calcSumX2)
+			set_var_from_var(&X2, &(state->sumX2));
+	}
+
+	MemoryContextSwitchTo(old_context);
 }
 
+
 Datum
 numeric_accum(PG_FUNCTION_ARGS)
 {
-	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-	Numeric		newval = PG_GETARG_NUMERIC(1);
+	NumericAggState *state;
+
+	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
 
-	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
+	if (!PG_ARGISNULL(1))
+	{
+		/* On the first time through, create the state variable. */
+		if (state == NULL)
+			state = makeNumericAggState(fcinfo, true);
+		
+		do_numeric_accum(state, PG_GETARG_NUMERIC(1));
+	}
+
+	if (state == NULL)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_POINTER(state);
 }
 
+
 /*
  * Optimized case for average of numeric.
  */
 Datum
 numeric_avg_accum(PG_FUNCTION_ARGS)
 {
-	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-	Numeric		newval = PG_GETARG_NUMERIC(1);
+	NumericAggState *state;
 
-	PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval));
+	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+
+	if (!PG_ARGISNULL(1))
+	{
+		/* On the first time through, create the state variable. */
+		if (state == NULL)
+			state = makeNumericAggState(fcinfo, false);
+		
+		do_numeric_accum(state, PG_GETARG_NUMERIC(1));
+	}
+
+	if (state == NULL)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_POINTER(state);
 }
 
+
 /*
  * Integer data types all use Numeric accumulators to share code and
  * avoid risk of overflow.	For int2 and int4 inputs, Numeric accumulation
@@ -2578,87 +2627,150 @@ numeric_avg_accum(PG_FUNCTION_ARGS)
 Datum
 int2_accum(PG_FUNCTION_ARGS)
 {
-	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-	Datum		newval2 = PG_GETARG_DATUM(1);
-	Numeric		newval;
+	NumericAggState *state;
 
-	newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric, newval2));
+	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
 
-	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
+	if (!PG_ARGISNULL(1))
+	{
+		Datum		newval2 = PG_GETARG_DATUM(1);
+		Numeric		newval;
+
+		/* On the first time through, create the state variable. */
+		if (state == NULL)
+			state = makeNumericAggState(fcinfo, true);
+		
+		newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric, newval2));
+		do_numeric_accum(state, newval);
+	}
+
+	if (state == NULL)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_POINTER(state);
 }
 
+
 Datum
 int4_accum(PG_FUNCTION_ARGS)
 {
-	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-	Datum		newval4 = PG_GETARG_DATUM(1);
-	Numeric		newval;
+	NumericAggState *state;
+
+	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
 
-	newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric, newval4));
+	if (!PG_ARGISNULL(1))
+	{
+		Datum		newval4 = PG_GETARG_DATUM(1);
+		Numeric		newval;
+
+		/* On the first time through, create the state variable. */
+		if (state == NULL)
+			state = makeNumericAggState(fcinfo, true);
+		
+		newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric, newval4));
+		do_numeric_accum(state, newval);
+	}
 
-	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
+	if (state == NULL)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_POINTER(state);
 }
 
+
 Datum
 int8_accum(PG_FUNCTION_ARGS)
 {
-	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-	Datum		newval8 = PG_GETARG_DATUM(1);
-	Numeric		newval;
+	NumericAggState *state;
 
-	newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
+	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
+
+	if (!PG_ARGISNULL(1))
+	{
+		Datum		newval8 = PG_GETARG_DATUM(1);
+		Numeric		newval;
+
+		/* On the first time through, create the state variable. */
+		if (state == NULL)
+			state = makeNumericAggState(fcinfo, true);
+		
+		newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
+		do_numeric_accum(state, newval);
+	}
 
-	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
+	if (state == NULL)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_POINTER(state);
 }
 
+
 /*
  * Optimized case for average of int8.
  */
 Datum
 int8_avg_accum(PG_FUNCTION_ARGS)
 {
-	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
-	Datum		newval8 = PG_GETARG_DATUM(1);
-	Numeric		newval;
-
-	newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
+	NumericAggState *state;	
+	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
 
-	PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval));
+	if (!PG_ARGISNULL(1))
+	{
+		Datum		newval8 = PG_GETARG_DATUM(1);
+		Numeric		newval;
+
+		/* On the first time through, create the state variable. */
+		if (state == NULL)
+			state = makeNumericAggState(fcinfo, false);
+		
+		newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
+		do_numeric_accum(state, newval);
+	}
+ 
+	if (state == NULL)
+		PG_RETURN_NULL();
+	else
+		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 		 		N_datum;
+	Datum 				sumX_datum;
+	NumericAggState 	*state;
 
-	/* SQL 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();
 
-	PG_RETURN_DATUM(DirectFunctionCall2(numeric_div,
-										NumericGetDatum(sumX),
-										NumericGetDatum(N)));
+	state = (NumericAggState *) PG_GETARG_POINTER(0);
+
+	N_datum = DirectFunctionCall1(int8_numeric, Int64GetDatum(state->N));
+	sumX_datum = NumericGetDatum(make_result(&state->sumX));
+
+	PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, sumX_datum, N_datum));
 }
 
+
+Datum
+numeric_sum(PG_FUNCTION_ARGS)
+{
+	NumericAggState 	*state;
+
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
+ 
+	state = (NumericAggState *) PG_GETARG_POINTER(0);
+
+	PG_RETURN_NUMERIC(make_result(&(state->sumX)));
+}
+
+
 /*
  * Workhorse routine for the standard deviance and variance
- * aggregates. 'transarray' is the aggregate's transition
- * array. 'variance' specifies whether we should calculate the
+ * aggregates. 'state' is aggregate's transition state.
+ * 'variance' specifies whether we should calculate the
  * variance or the standard deviation. 'sample' indicates whether the
  * caller is interested in the sample or the population
  * variance/stddev.
@@ -2667,16 +2779,11 @@ numeric_avg(PG_FUNCTION_ARGS)
  * *is_null is set to true and NULL is returned.
  */
 static Numeric
-numeric_stddev_internal(ArrayType *transarray,
+numeric_stddev_internal(NumericAggState *state,
 						bool variance, bool sample,
 						bool *is_null)
 {
-	Datum	   *transdatums;
-	int			ndatums;
-	Numeric		N,
-				sumX,
-				sumX2,
-				res;
+	Numeric		res;
 	NumericVar	vN,
 				vsumX,
 				vsumX2,
@@ -2684,22 +2791,24 @@ numeric_stddev_internal(ArrayType *transarray,
 	NumericVar *comp;
 	int			rscale;
 
+	if (state == NULL)
+	{
+		*is_null = true;
+		return NULL;
+	}
+
 	*is_null = false;
 
-	/* We assume the input is array of numeric */
-	deconstruct_array(transarray,
-					  NUMERICOID, -1, false, 'i',
-					  &transdatums, NULL, &ndatums);
-	if (ndatums != 3)
-		elog(ERROR, "expected 3-element numeric array");
-	N = DatumGetNumeric(transdatums[0]);
-	sumX = DatumGetNumeric(transdatums[1]);
-	sumX2 = DatumGetNumeric(transdatums[2]);
-
-	if (NUMERIC_IS_NAN(N) || NUMERIC_IS_NAN(sumX) || NUMERIC_IS_NAN(sumX2))
+	if (state->isNaN)
 		return make_result(&const_nan);
 
-	init_var_from_num(N, &vN);
+	init_var(&vN);
+	init_var(&vsumX);
+	init_var(&vsumX2);
+
+	int8_to_numericvar(state->N, &vN);
+	set_var_from_var(&(state->sumX), &vsumX);
+	set_var_from_var(&(state->sumX2), &vsumX2);
 
 	/*
 	 * Sample stddev and variance are undefined when N <= 1; population stddev
@@ -2719,8 +2828,8 @@ numeric_stddev_internal(ArrayType *transarray,
 	init_var(&vNminus1);
 	sub_var(&vN, &const_one, &vNminus1);
 
-	init_var_from_num(sumX, &vsumX);
-	init_var_from_num(sumX2, &vsumX2);
+	set_var_from_var(&(state->sumX), &vsumX);
+	set_var_from_var(&(state->sumX2), &vsumX2);
 
 	/* compute rscale for mul_var calls */
 	rscale = vsumX.dscale * 2;
@@ -2761,7 +2870,7 @@ numeric_var_samp(PG_FUNCTION_ARGS)
 	Numeric		res;
 	bool		is_null;
 
-	res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
+	res = numeric_stddev_internal((NumericAggState *) PG_GETARG_POINTER(0),
 								  true, true, &is_null);
 
 	if (is_null)
@@ -2776,7 +2885,7 @@ numeric_stddev_samp(PG_FUNCTION_ARGS)
 	Numeric		res;
 	bool		is_null;
 
-	res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
+	res = numeric_stddev_internal((NumericAggState *) PG_GETARG_POINTER(0),
 								  false, true, &is_null);
 
 	if (is_null)
@@ -2791,7 +2900,7 @@ numeric_var_pop(PG_FUNCTION_ARGS)
 	Numeric		res;
 	bool		is_null;
 
-	res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
+	res = numeric_stddev_internal((NumericAggState *) PG_GETARG_POINTER(0),
 								  true, false, &is_null);
 
 	if (is_null)
@@ -2806,7 +2915,7 @@ numeric_stddev_pop(PG_FUNCTION_ARGS)
 	Numeric		res;
 	bool		is_null;
 
-	res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
+	res = numeric_stddev_internal((NumericAggState *) PG_GETARG_POINTER(0),
 								  false, false, &is_null);
 
 	if (is_null)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index f40961f..ff71eca 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -11402,12 +11402,14 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	int			i_aggfinalfn;
 	int			i_aggsortop;
 	int			i_aggtranstype;
+	int 		i_aggtransspace;
 	int			i_agginitval;
 	int			i_convertok;
 	const char *aggtransfn;
 	const char *aggfinalfn;
 	const char *aggsortop;
 	const char *aggtranstype;
+	const char *aggtransspace;
 	const char *agginitval;
 	bool		convertok;
 
@@ -11425,12 +11427,24 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	selectSourceSchema(fout, agginfo->aggfn.dobj.namespace->dobj.name);
 
 	/* Get aggregate-specific details */
-	if (fout->remoteVersion >= 80100)
+	if (fout->remoteVersion >= 90300)
+	{
+		appendPQExpBuffer(query, "SELECT aggtransfn, "
+						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
+						  "aggsortop::pg_catalog.regoperator, "
+						  "aggtransspace, agginitval, "
+						  "'t'::boolean AS convertok "
+					  "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
+						  "WHERE a.aggfnoid = p.oid "
+						  "AND p.oid = '%u'::pg_catalog.oid",
+						  agginfo->aggfn.dobj.catId.oid);
+	}
+	else if (fout->remoteVersion >= 80100)
 	{
 		appendPQExpBuffer(query, "SELECT aggtransfn, "
 						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
 						  "aggsortop::pg_catalog.regoperator, "
-						  "agginitval, "
+						  "0 AS aggtransspace, agginitval, "
 						  "'t'::boolean AS convertok "
 					  "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
 						  "WHERE a.aggfnoid = p.oid "
@@ -11442,7 +11456,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 		appendPQExpBuffer(query, "SELECT aggtransfn, "
 						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
 						  "0 AS aggsortop, "
-						  "agginitval, "
+						  "0 AS aggtransspace, agginitval, "
 						  "'t'::boolean AS convertok "
 					  "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
 						  "WHERE a.aggfnoid = p.oid "
@@ -11454,7 +11468,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 		appendPQExpBuffer(query, "SELECT aggtransfn, aggfinalfn, "
 						  "format_type(aggtranstype, NULL) AS aggtranstype, "
 						  "0 AS aggsortop, "
-						  "agginitval, "
+						  "0 AS aggtransspace, agginitval, "
 						  "'t'::boolean AS convertok "
 						  "FROM pg_aggregate "
 						  "WHERE oid = '%u'::oid",
@@ -11466,7 +11480,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 						  "aggfinalfn, "
 						  "(SELECT typname FROM pg_type WHERE oid = aggtranstype1) AS aggtranstype, "
 						  "0 AS aggsortop, "
-						  "agginitval1 AS agginitval, "
+						  "0 AS aggtransspace, agginitval1 AS agginitval, "
 						  "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) AS convertok "
 						  "FROM pg_aggregate "
 						  "WHERE oid = '%u'::oid",
@@ -11479,6 +11493,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	i_aggfinalfn = PQfnumber(res, "aggfinalfn");
 	i_aggsortop = PQfnumber(res, "aggsortop");
 	i_aggtranstype = PQfnumber(res, "aggtranstype");
+	i_aggtransspace = PQfnumber(res, "aggtransspace");
 	i_agginitval = PQfnumber(res, "agginitval");
 	i_convertok = PQfnumber(res, "convertok");
 
@@ -11486,6 +11501,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn);
 	aggsortop = PQgetvalue(res, 0, i_aggsortop);
 	aggtranstype = PQgetvalue(res, 0, i_aggtranstype);
+	aggtransspace = PQgetvalue(res, 0, i_aggtransspace);
 	agginitval = PQgetvalue(res, 0, i_agginitval);
 	convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't');
 
@@ -11522,6 +11538,12 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 						  fmtId(aggtranstype));
 	}
 
+	if (strcmp(aggtransspace, "0") != 0)
+	{
+		appendPQExpBuffer(details, ",\n    SSPACE = %s",
+						  aggtransspace);
+	}
+
 	if (!PQgetisnull(res, 0, i_agginitval))
 	{
 		appendPQExpBuffer(details, ",\n    INITCOND = ");
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 6fb10a9..385c906 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -44,6 +44,7 @@ CATALOG(pg_aggregate,2600) BKI_WITHOUT_OIDS
 	regproc		aggfinalfn;
 	Oid			aggsortop;
 	Oid			aggtranstype;
+	int32		aggtransspace;
 
 #ifdef CATALOG_VARLEN			/* variable-length fields start here */
 	text		agginitval;
@@ -62,13 +63,14 @@ typedef FormData_pg_aggregate *Form_pg_aggregate;
  * ----------------
  */
 
-#define Natts_pg_aggregate				6
+#define Natts_pg_aggregate				7
 #define Anum_pg_aggregate_aggfnoid		1
 #define Anum_pg_aggregate_aggtransfn	2
 #define Anum_pg_aggregate_aggfinalfn	3
 #define Anum_pg_aggregate_aggsortop		4
 #define Anum_pg_aggregate_aggtranstype	5
-#define Anum_pg_aggregate_agginitval	6
+#define Anum_pg_aggregate_aggtransspace	6
+#define Anum_pg_aggregate_agginitval	7
 
 
 /* ----------------
@@ -77,163 +79,163 @@ typedef FormData_pg_aggregate *Form_pg_aggregate;
  */
 
 /* avg */
-DATA(insert ( 2100	int8_avg_accum	numeric_avg		0	1231	"{0,0}" ));
-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 ( 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 ( 2100	int8_avg_accum		numeric_avg		0	2281	128	_null_ ));
+DATA(insert ( 2101	int4_avg_accum		int8_avg		0	1016	0	"{0,0}" ));
+DATA(insert ( 2102	int2_avg_accum		int8_avg		0	1016	0	"{0,0}" ));
+DATA(insert ( 2103	numeric_accum		numeric_avg		0	2281	128	_null_ ));
+DATA(insert ( 2104	float4_accum		float8_avg		0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2105	float8_accum		float8_avg		0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2106	interval_accum		interval_avg	0	1187	0	"{0 second,0 second}" ));
 
 /* sum */
-DATA(insert ( 2107	int8_sum		-				0	1700	_null_ ));
-DATA(insert ( 2108	int4_sum		-				0	20		_null_ ));
-DATA(insert ( 2109	int2_sum		-				0	20		_null_ ));
-DATA(insert ( 2110	float4pl		-				0	700		_null_ ));
-DATA(insert ( 2111	float8pl		-				0	701		_null_ ));
-DATA(insert ( 2112	cash_pl			-				0	790		_null_ ));
-DATA(insert ( 2113	interval_pl		-				0	1186	_null_ ));
-DATA(insert ( 2114	numeric_add		-				0	1700	_null_ ));
+DATA(insert ( 2107	int8_avg_accum	numeric_sum		0	2281	128	_null_ ));
+DATA(insert ( 2108	int4_sum		-				0	20		0	_null_ ));
+DATA(insert ( 2109	int2_sum		-				0	20		0	_null_ ));
+DATA(insert ( 2110	float4pl		-				0	700		0	_null_ ));
+DATA(insert ( 2111	float8pl		-				0	701		0	_null_ ));
+DATA(insert ( 2112	cash_pl			-				0	790		0	_null_ ));
+DATA(insert ( 2113	interval_pl		-				0	1186	0	_null_ ));
+DATA(insert ( 2114	numeric_avg_accum	numeric_sum	0	2281	128	_null_ ));
 
 /* max */
-DATA(insert ( 2115	int8larger		-				413		20		_null_ ));
-DATA(insert ( 2116	int4larger		-				521		23		_null_ ));
-DATA(insert ( 2117	int2larger		-				520		21		_null_ ));
-DATA(insert ( 2118	oidlarger		-				610		26		_null_ ));
-DATA(insert ( 2119	float4larger	-				623		700		_null_ ));
-DATA(insert ( 2120	float8larger	-				674		701		_null_ ));
-DATA(insert ( 2121	int4larger		-				563		702		_null_ ));
-DATA(insert ( 2122	date_larger		-				1097	1082	_null_ ));
-DATA(insert ( 2123	time_larger		-				1112	1083	_null_ ));
-DATA(insert ( 2124	timetz_larger	-				1554	1266	_null_ ));
-DATA(insert ( 2125	cashlarger		-				903		790		_null_ ));
-DATA(insert ( 2126	timestamp_larger	-			2064	1114	_null_ ));
-DATA(insert ( 2127	timestamptz_larger	-			1324	1184	_null_ ));
-DATA(insert ( 2128	interval_larger -				1334	1186	_null_ ));
-DATA(insert ( 2129	text_larger		-				666		25		_null_ ));
-DATA(insert ( 2130	numeric_larger	-				1756	1700	_null_ ));
-DATA(insert ( 2050	array_larger	-				1073	2277	_null_ ));
-DATA(insert ( 2244	bpchar_larger	-				1060	1042	_null_ ));
-DATA(insert ( 2797	tidlarger		-				2800	27		_null_ ));
-DATA(insert ( 3526	enum_larger		-				3519	3500	_null_ ));
+DATA(insert ( 2115	int8larger		-				413		20		0	_null_ ));
+DATA(insert ( 2116	int4larger		-				521		23		0	_null_ ));
+DATA(insert ( 2117	int2larger		-				520		21		0	_null_ ));
+DATA(insert ( 2118	oidlarger		-				610		26		0	_null_ ));
+DATA(insert ( 2119	float4larger	-				623		700		0	_null_ ));
+DATA(insert ( 2120	float8larger	-				674		701		0	_null_ ));
+DATA(insert ( 2121	int4larger		-				563		702		0	_null_ ));
+DATA(insert ( 2122	date_larger		-				1097	1082	0	_null_ ));
+DATA(insert ( 2123	time_larger		-				1112	1083	0	_null_ ));
+DATA(insert ( 2124	timetz_larger	-				1554	1266	0	_null_ ));
+DATA(insert ( 2125	cashlarger		-				903		790		0	_null_ ));
+DATA(insert ( 2126	timestamp_larger	-			2064	1114	0	_null_ ));
+DATA(insert ( 2127	timestamptz_larger	-			1324	1184	0	_null_ ));
+DATA(insert ( 2128	interval_larger -				1334	1186	0	_null_ ));
+DATA(insert ( 2129	text_larger		-				666		25		0	_null_ ));
+DATA(insert ( 2130	numeric_larger	-				1756	1700	0	_null_ ));
+DATA(insert ( 2050	array_larger	-				1073	2277	0	_null_ ));
+DATA(insert ( 2244	bpchar_larger	-				1060	1042	0	_null_ ));
+DATA(insert ( 2797	tidlarger		-				2800	27		0	_null_ ));
+DATA(insert ( 3526	enum_larger		-				3519	3500	0	_null_ ));
 
 /* min */
-DATA(insert ( 2131	int8smaller		-				412		20		_null_ ));
-DATA(insert ( 2132	int4smaller		-				97		23		_null_ ));
-DATA(insert ( 2133	int2smaller		-				95		21		_null_ ));
-DATA(insert ( 2134	oidsmaller		-				609		26		_null_ ));
-DATA(insert ( 2135	float4smaller	-				622		700		_null_ ));
-DATA(insert ( 2136	float8smaller	-				672		701		_null_ ));
-DATA(insert ( 2137	int4smaller		-				562		702		_null_ ));
-DATA(insert ( 2138	date_smaller	-				1095	1082	_null_ ));
-DATA(insert ( 2139	time_smaller	-				1110	1083	_null_ ));
-DATA(insert ( 2140	timetz_smaller	-				1552	1266	_null_ ));
-DATA(insert ( 2141	cashsmaller		-				902		790		_null_ ));
-DATA(insert ( 2142	timestamp_smaller	-			2062	1114	_null_ ));
-DATA(insert ( 2143	timestamptz_smaller -			1322	1184	_null_ ));
-DATA(insert ( 2144	interval_smaller	-			1332	1186	_null_ ));
-DATA(insert ( 2145	text_smaller	-				664		25		_null_ ));
-DATA(insert ( 2146	numeric_smaller -				1754	1700	_null_ ));
-DATA(insert ( 2051	array_smaller	-				1072	2277	_null_ ));
-DATA(insert ( 2245	bpchar_smaller	-				1058	1042	_null_ ));
-DATA(insert ( 2798	tidsmaller		-				2799	27		_null_ ));
-DATA(insert ( 3527	enum_smaller	-				3518	3500	_null_ ));
+DATA(insert ( 2131	int8smaller		-				412		20		0	_null_ ));
+DATA(insert ( 2132	int4smaller		-				97		23		0	_null_ ));
+DATA(insert ( 2133	int2smaller		-				95		21		0	_null_ ));
+DATA(insert ( 2134	oidsmaller		-				609		26		0	_null_ ));
+DATA(insert ( 2135	float4smaller	-				622		700		0	_null_ ));
+DATA(insert ( 2136	float8smaller	-				672		701		0	_null_ ));
+DATA(insert ( 2137	int4smaller		-				562		702		0	_null_ ));
+DATA(insert ( 2138	date_smaller	-				1095	1082	0	_null_ ));
+DATA(insert ( 2139	time_smaller	-				1110	1083	0	_null_ ));
+DATA(insert ( 2140	timetz_smaller	-				1552	1266	0	_null_ ));
+DATA(insert ( 2141	cashsmaller		-				902		790		0	_null_ ));
+DATA(insert ( 2142	timestamp_smaller	-			2062	1114	0	_null_ ));
+DATA(insert ( 2143	timestamptz_smaller -			1322	1184	0	_null_ ));
+DATA(insert ( 2144	interval_smaller	-			1332	1186	0	_null_ ));
+DATA(insert ( 2145	text_smaller	-				664		25		0	_null_ ));
+DATA(insert ( 2146	numeric_smaller -				1754	1700	0	_null_ ));
+DATA(insert ( 2051	array_smaller	-				1072	2277	0	_null_ ));
+DATA(insert ( 2245	bpchar_smaller	-				1058	1042	0	_null_ ));
+DATA(insert ( 2798	tidsmaller		-				2799	27		0	_null_ ));
+DATA(insert ( 3527	enum_smaller	-				3518	3500	0	_null_ ));
 
 /* count */
-DATA(insert ( 2147	int8inc_any		-				0		20		"0" ));
-DATA(insert ( 2803	int8inc			-				0		20		"0" ));
+DATA(insert ( 2147	int8inc_any		-				0		20		0	"0" ));
+DATA(insert ( 2803	int8inc			-				0		20		0	"0" ));
 
 /* var_pop */
-DATA(insert ( 2718	int8_accum	numeric_var_pop 0	1231	"{0,0,0}" ));
-DATA(insert ( 2719	int4_accum	numeric_var_pop 0	1231	"{0,0,0}" ));
-DATA(insert ( 2720	int2_accum	numeric_var_pop 0	1231	"{0,0,0}" ));
-DATA(insert ( 2721	float4_accum	float8_var_pop 0	1022	"{0,0,0}" ));
-DATA(insert ( 2722	float8_accum	float8_var_pop 0	1022	"{0,0,0}" ));
-DATA(insert ( 2723	numeric_accum  numeric_var_pop 0	1231	"{0,0,0}" ));
+DATA(insert ( 2718	int8_accum	numeric_var_pop 0	2281	128	_null_ ));
+DATA(insert ( 2719	int4_accum	numeric_var_pop 0	2281	128	_null_ ));
+DATA(insert ( 2720	int2_accum	numeric_var_pop 0	2281	128	_null_ ));
+DATA(insert ( 2721	float4_accum	float8_var_pop 0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2722	float8_accum	float8_var_pop 0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2723	numeric_accum  numeric_var_pop 0	2281	128	_null_ ));
 
 /* var_samp */
-DATA(insert ( 2641	int8_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
-DATA(insert ( 2642	int4_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
-DATA(insert ( 2643	int2_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
-DATA(insert ( 2644	float4_accum	float8_var_samp 0	1022	"{0,0,0}" ));
-DATA(insert ( 2645	float8_accum	float8_var_samp 0	1022	"{0,0,0}" ));
-DATA(insert ( 2646	numeric_accum  numeric_var_samp 0	1231	"{0,0,0}" ));
+DATA(insert ( 2641	int8_accum	numeric_var_samp	0	2281	128	_null_ ));
+DATA(insert ( 2642	int4_accum	numeric_var_samp	0	2281	128	_null_ ));
+DATA(insert ( 2643	int2_accum	numeric_var_samp	0	2281	128	_null_ ));
+DATA(insert ( 2644	float4_accum	float8_var_samp 0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2645	float8_accum	float8_var_samp 0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2646	numeric_accum  numeric_var_samp 0	2281	128	_null_ ));
 
 /* variance: historical Postgres syntax for var_samp */
-DATA(insert ( 2148	int8_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
-DATA(insert ( 2149	int4_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
-DATA(insert ( 2150	int2_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
-DATA(insert ( 2151	float4_accum	float8_var_samp 0	1022	"{0,0,0}" ));
-DATA(insert ( 2152	float8_accum	float8_var_samp 0	1022	"{0,0,0}" ));
-DATA(insert ( 2153	numeric_accum  numeric_var_samp 0	1231	"{0,0,0}" ));
+DATA(insert ( 2148	int8_accum	numeric_var_samp	0	2281	128	_null_ ));
+DATA(insert ( 2149	int4_accum	numeric_var_samp	0	2281	128	_null_ ));
+DATA(insert ( 2150	int2_accum	numeric_var_samp	0	2281	128	_null_ ));
+DATA(insert ( 2151	float4_accum	float8_var_samp 0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2152	float8_accum	float8_var_samp 0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2153	numeric_accum  numeric_var_samp 0	2281	128	_null_ ));
 
 /* stddev_pop */
-DATA(insert ( 2724	int8_accum	numeric_stddev_pop		0	1231	"{0,0,0}" ));
-DATA(insert ( 2725	int4_accum	numeric_stddev_pop		0	1231	"{0,0,0}" ));
-DATA(insert ( 2726	int2_accum	numeric_stddev_pop		0	1231	"{0,0,0}" ));
-DATA(insert ( 2727	float4_accum	float8_stddev_pop	0	1022	"{0,0,0}" ));
-DATA(insert ( 2728	float8_accum	float8_stddev_pop	0	1022	"{0,0,0}" ));
-DATA(insert ( 2729	numeric_accum	numeric_stddev_pop	0	1231	"{0,0,0}" ));
+DATA(insert ( 2724	int8_accum	numeric_stddev_pop		0	2281	128	_null_ ));
+DATA(insert ( 2725	int4_accum	numeric_stddev_pop		0	2281	128	_null_ ));
+DATA(insert ( 2726	int2_accum	numeric_stddev_pop		0	2281	128	_null_ ));
+DATA(insert ( 2727	float4_accum	float8_stddev_pop	0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2728	float8_accum	float8_stddev_pop	0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2729	numeric_accum	numeric_stddev_pop	0	2281	128	_null_ ));
 
 /* stddev_samp */
-DATA(insert ( 2712	int8_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
-DATA(insert ( 2713	int4_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
-DATA(insert ( 2714	int2_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
-DATA(insert ( 2715	float4_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
-DATA(insert ( 2716	float8_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
-DATA(insert ( 2717	numeric_accum	numeric_stddev_samp 0	1231	"{0,0,0}" ));
+DATA(insert ( 2712	int8_accum	numeric_stddev_samp		0	2281	128	_null_ ));
+DATA(insert ( 2713	int4_accum	numeric_stddev_samp		0	2281	128	_null_ ));
+DATA(insert ( 2714	int2_accum	numeric_stddev_samp		0	2281	128	_null_ ));
+DATA(insert ( 2715	float4_accum	float8_stddev_samp	0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2716	float8_accum	float8_stddev_samp	0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2717	numeric_accum	numeric_stddev_samp 0	2281	128	_null_ ));
 
 /* stddev: historical Postgres syntax for stddev_samp */
-DATA(insert ( 2154	int8_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
-DATA(insert ( 2155	int4_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
-DATA(insert ( 2156	int2_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
-DATA(insert ( 2157	float4_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
-DATA(insert ( 2158	float8_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
-DATA(insert ( 2159	numeric_accum	numeric_stddev_samp 0	1231	"{0,0,0}" ));
+DATA(insert ( 2154	int8_accum	numeric_stddev_samp		0	2281	128	_null_ ));
+DATA(insert ( 2155	int4_accum	numeric_stddev_samp		0	2281	128	_null_ ));
+DATA(insert ( 2156	int2_accum	numeric_stddev_samp		0	2281	128	_null_ ));
+DATA(insert ( 2157	float4_accum	float8_stddev_samp	0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2158	float8_accum	float8_stddev_samp	0	1022	0	"{0,0,0}" ));
+DATA(insert ( 2159	numeric_accum	numeric_stddev_samp 0	2281	128	_null_ ));
 
 /* SQL2003 binary regression aggregates */
-DATA(insert ( 2818	int8inc_float8_float8		-				0	20		"0" ));
-DATA(insert ( 2819	float8_regr_accum	float8_regr_sxx			0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2820	float8_regr_accum	float8_regr_syy			0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2821	float8_regr_accum	float8_regr_sxy			0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2822	float8_regr_accum	float8_regr_avgx		0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2823	float8_regr_accum	float8_regr_avgy		0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2824	float8_regr_accum	float8_regr_r2			0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2825	float8_regr_accum	float8_regr_slope		0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2826	float8_regr_accum	float8_regr_intercept	0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2827	float8_regr_accum	float8_covar_pop		0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2828	float8_regr_accum	float8_covar_samp		0	1022	"{0,0,0,0,0,0}" ));
-DATA(insert ( 2829	float8_regr_accum	float8_corr				0	1022	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2818	int8inc_float8_float8		-				0	20		0	"0" ));
+DATA(insert ( 2819	float8_regr_accum	float8_regr_sxx			0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2820	float8_regr_accum	float8_regr_syy			0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2821	float8_regr_accum	float8_regr_sxy			0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2822	float8_regr_accum	float8_regr_avgx		0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2823	float8_regr_accum	float8_regr_avgy		0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2824	float8_regr_accum	float8_regr_r2			0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2825	float8_regr_accum	float8_regr_slope		0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2826	float8_regr_accum	float8_regr_intercept	0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2827	float8_regr_accum	float8_covar_pop		0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2828	float8_regr_accum	float8_covar_samp		0	1022	0	"{0,0,0,0,0,0}" ));
+DATA(insert ( 2829	float8_regr_accum	float8_corr				0	1022	0	"{0,0,0,0,0,0}" ));
 
 /* boolean-and and boolean-or */
-DATA(insert ( 2517	booland_statefunc	-			58	16		_null_ ));
-DATA(insert ( 2518	boolor_statefunc	-			59	16		_null_ ));
-DATA(insert ( 2519	booland_statefunc	-			58	16		_null_ ));
+DATA(insert ( 2517	booland_statefunc	-			58	16		0	_null_ ));
+DATA(insert ( 2518	boolor_statefunc	-			59	16		0	_null_ ));
+DATA(insert ( 2519	booland_statefunc	-			58	16		0	_null_ ));
 
 /* bitwise integer */
-DATA(insert ( 2236 int2and		  -					0	21		_null_ ));
-DATA(insert ( 2237 int2or		  -					0	21		_null_ ));
-DATA(insert ( 2238 int4and		  -					0	23		_null_ ));
-DATA(insert ( 2239 int4or		  -					0	23		_null_ ));
-DATA(insert ( 2240 int8and		  -					0	20		_null_ ));
-DATA(insert ( 2241 int8or		  -					0	20		_null_ ));
-DATA(insert ( 2242 bitand		  -					0	1560	_null_ ));
-DATA(insert ( 2243 bitor		  -					0	1560	_null_ ));
+DATA(insert ( 2236 int2and		  -					0	21		0	_null_ ));
+DATA(insert ( 2237 int2or		  -					0	21		0	_null_ ));
+DATA(insert ( 2238 int4and		  -					0	23		0	_null_ ));
+DATA(insert ( 2239 int4or		  -					0	23		0	_null_ ));
+DATA(insert ( 2240 int8and		  -					0	20		0	_null_ ));
+DATA(insert ( 2241 int8or		  -					0	20		0	_null_ ));
+DATA(insert ( 2242 bitand		  -					0	1560	0	_null_ ));
+DATA(insert ( 2243 bitor		  -					0	1560	0	_null_ ));
 
 /* xml */
-DATA(insert ( 2901 xmlconcat2	  -					0	142		_null_ ));
+DATA(insert ( 2901 xmlconcat2	  -					0	142		0	_null_ ));
 
 /* array */
-DATA(insert ( 2335	array_agg_transfn	array_agg_finalfn		0	2281	_null_ ));
+DATA(insert ( 2335	array_agg_transfn	array_agg_finalfn		0	2281	0	_null_ ));
 
 /* text */
-DATA(insert ( 3538	string_agg_transfn	string_agg_finalfn		0	2281	_null_ ));
+DATA(insert ( 3538	string_agg_transfn	string_agg_finalfn		0	2281	0	_null_ ));
 
 /* bytea */
-DATA(insert ( 3545	bytea_string_agg_transfn	bytea_string_agg_finalfn		0	2281	_null_ ));
+DATA(insert ( 3545	bytea_string_agg_transfn	bytea_string_agg_finalfn		0	2281	0	_null_ ));
 
 /* json */
-DATA(insert ( 3175	json_agg_transfn	json_agg_finalfn		0	2281	_null_ ));
+DATA(insert ( 3175	json_agg_transfn	json_agg_finalfn		0	2281	0	_null_ ));
 
 /*
  * prototypes for functions in pg_aggregate.c
@@ -246,6 +248,7 @@ extern Oid AggregateCreate(const char *aggName,
 				List *aggfinalfnName,
 				List *aggsortopName,
 				Oid aggTransType,
+				int32 aggTransSpace,
 				const char *agginitval);
 
 #endif   /* PG_AGGREGATE_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 90aff3d..b413e60 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2381,27 +2381,29 @@ DATA(insert OID = 2513 (  float8_stddev_pop PGNSP PGUID 12 1 0 0 0 f f f f t f i
 DESCR("aggregate final function");
 DATA(insert OID = 1832 (  float8_stddev_samp	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 701 "1022" _null_ _null_ _null_ _null_ float8_stddev_samp _null_ _null_ _null_ ));
 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_ ));
+DATA(insert OID = 1833 (  numeric_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_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 = 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_ ));
+DATA(insert OID = 1834 (  int2_accum	   PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 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_ ));
+DATA(insert OID = 1835 (  int4_accum	   PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 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_ ));
+DATA(insert OID = 1836 (  int8_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_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 = 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_ ));
+DATA(insert OID = 3179 (  numeric_sum	   PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_sum _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_ ));
+DATA(insert OID = 2514 (  numeric_var_pop  PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_var_pop _null_ _null_ _null_ ));
 DESCR("aggregate final function");
-DATA(insert OID = 2596 (  numeric_stddev_pop PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1700 "1231" _null_ _null_ _null_ _null_	numeric_stddev_pop _null_ _null_ _null_ ));
+DATA(insert OID = 1838 (  numeric_var_samp PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_var_samp _null_ _null_ _null_ ));
 DESCR("aggregate final function");
-DATA(insert OID = 1839 (  numeric_stddev_samp	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1700 "1231" _null_ _null_ _null_ _null_ numeric_stddev_samp _null_ _null_ _null_ ));
+DATA(insert OID = 2596 (  numeric_stddev_pop PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_	numeric_stddev_pop _null_ _null_ _null_ ));
+DESCR("aggregate final function");
+DATA(insert OID = 1839 (  numeric_stddev_samp	PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_stddev_samp _null_ _null_ _null_ ));
 DESCR("aggregate final function");
 DATA(insert OID = 1840 (  int2_sum		   PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 20 "20 21" _null_ _null_ _null_ _null_ int2_sum _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index fa9f41f..7ccb656 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -122,6 +122,7 @@ extern Datum transformGenericOptions(Oid catalogId,
 extern char *defGetString(DefElem *def);
 extern double defGetNumeric(DefElem *def);
 extern bool defGetBoolean(DefElem *def);
+extern int32 defGetInt32(DefElem *def);
 extern int64 defGetInt64(DefElem *def);
 extern List *defGetQualifiedName(DefElem *def);
 extern TypeName *defGetTypeName(DefElem *def);
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 667c58b..9e3ef1c 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -981,6 +981,7 @@ extern Datum int4_accum(PG_FUNCTION_ARGS);
 extern Datum int8_accum(PG_FUNCTION_ARGS);
 extern Datum int8_avg_accum(PG_FUNCTION_ARGS);
 extern Datum numeric_avg(PG_FUNCTION_ARGS);
+extern Datum numeric_sum(PG_FUNCTION_ARGS);
 extern Datum numeric_var_pop(PG_FUNCTION_ARGS);
 extern Datum numeric_var_samp(PG_FUNCTION_ARGS);
 extern Datum numeric_stddev_pop(PG_FUNCTION_ARGS);
#17Pavel Stehule
pavel.stehule@gmail.com
In reply to: Hadi Moshayedi (#16)
Re: Improving avg performance for numeric

2013/7/8 Hadi Moshayedi <hadi@moshayedi.net>:

I am attaching the updated the patch, which also fixes a bug which
caused one of the regression tests failed.

I'll subscribe this patch to the commitfest in the next hour.

Can you please review the patch?

sure, :)

Pavel

Thanks,
-- Hadi

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#18Pavel Stehule
pavel.stehule@gmail.com
In reply to: Hadi Moshayedi (#16)
1 attachment(s)
Re: Improving avg performance for numeric

Hello

I am testing your code, and It increase speed of sum about 24% faster
then original implementation.

But I am surprise of AVG speed - it should have same speed like sum in
new implementation, but it is 2x slower, than sum - what signalize
some strange and there is used wrong transition function

I am sending fixed version

postgres=# create table bubu(a int, b float, c numeric);
CREATE TABLE
postgres=# insert into bubu select i, i+1, i+1.122 from
generate_series(1,1000000) g(i);
INSERT 0 1000000

After fixing a speed of sum and avg for numeric is similar

postgres=# select avg(c) from bubu;
avg
---------------------
500001.622000000000
(1 row)

Time: 228.483 ms
postgres=# select sum(c) from bubu;
sum
------------------
500001622000.000
(1 row)

Time: 222.791 ms

Regards

Pavel

2013/7/8 Hadi Moshayedi <hadi@moshayedi.net>:

Show quoted text

I am attaching the updated the patch, which also fixes a bug which
caused one of the regression tests failed.

I'll subscribe this patch to the commitfest in the next hour.

Can you please review the patch?

Thanks,
-- Hadi

Attachments:

numeric-optimize-v4.patchapplication/octet-stream; name=numeric-optimize-v4.patchDownload
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 373,378 ****
--- 373,384 ----
        <entry>Data type of the aggregate function's internal transition (state) data</entry>
       </row>
       <row>
+       <entry><structfield>aggtransspace</structfield></entry>
+       <entry><type>int4</type></entry>
+       <entry></entry>
+       <entry>Approximation for the average size of the aggregate function's internal transition (state) data</entry>
+      </row>
+      <row>
        <entry><structfield>agginitval</structfield></entry>
        <entry><type>text</type></entry>
        <entry></entry>
*** a/doc/src/sgml/ref/create_aggregate.sgml
--- b/doc/src/sgml/ref/create_aggregate.sgml
***************
*** 24,29 **** PostgreSQL documentation
--- 24,30 ----
  CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> ( <replaceable class="PARAMETER">input_data_type</replaceable> [ , ... ] ) (
      SFUNC = <replaceable class="PARAMETER">sfunc</replaceable>,
      STYPE = <replaceable class="PARAMETER">state_data_type</replaceable>
+     [ , SSPACE = <replaceable class="PARAMETER">state_data_size</replaceable> ]
      [ , FINALFUNC = <replaceable class="PARAMETER">ffunc</replaceable> ]
      [ , INITCOND = <replaceable class="PARAMETER">initial_condition</replaceable> ]
      [ , SORTOP = <replaceable class="PARAMETER">sort_operator</replaceable> ]
***************
*** 35,40 **** CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> (
--- 36,42 ----
      BASETYPE = <replaceable class="PARAMETER">base_type</replaceable>,
      SFUNC = <replaceable class="PARAMETER">sfunc</replaceable>,
      STYPE = <replaceable class="PARAMETER">state_data_type</replaceable>
+     [ , SSPACE = <replaceable class="PARAMETER">state_data_size</replaceable> ]
      [ , FINALFUNC = <replaceable class="PARAMETER">ffunc</replaceable> ]
      [ , INITCOND = <replaceable class="PARAMETER">initial_condition</replaceable> ]
      [ , SORTOP = <replaceable class="PARAMETER">sort_operator</replaceable> ]
***************
*** 241,246 **** SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
--- 243,260 ----
     </varlistentry>
  
     <varlistentry>
+     <term><replaceable class="PARAMETER">state_data_size</replaceable></term>
+     <listitem>
+      <para>
+       Approximate average size (in bytes) of aggregate's state value. 
+       Planner uses this value to approximate the memory required for 
+       the aggregation. If this value is not provided, a default value is 
+       used based on state_data_type.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
      <term><replaceable class="PARAMETER">ffunc</replaceable></term>
      <listitem>
       <para>
*** a/src/backend/catalog/pg_aggregate.c
--- b/src/backend/catalog/pg_aggregate.c
***************
*** 51,56 **** AggregateCreate(const char *aggName,
--- 51,57 ----
  				List *aggfinalfnName,
  				List *aggsortopName,
  				Oid aggTransType,
+ 				int32 aggTransSpace,
  				const char *agginitval)
  {
  	Relation	aggdesc;
***************
*** 269,274 **** AggregateCreate(const char *aggName,
--- 270,276 ----
  	values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
  	values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop);
  	values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
+ 	values[Anum_pg_aggregate_aggtransspace - 1] = Int32GetDatum(aggTransSpace);
  	if (agginitval)
  		values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval);
  	else
*** a/src/backend/commands/aggregatecmds.c
--- b/src/backend/commands/aggregatecmds.c
***************
*** 62,67 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
--- 62,68 ----
  	Oid		   *aggArgTypes;
  	int			numArgs;
  	Oid			transTypeId;
+ 	int32		transSpace = 0;
  	char		transTypeType;
  	ListCell   *pl;
  
***************
*** 96,101 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
--- 97,104 ----
  			transType = defGetTypeName(defel);
  		else if (pg_strcasecmp(defel->defname, "stype1") == 0)
  			transType = defGetTypeName(defel);
+ 		else if (pg_strcasecmp(defel->defname, "sspace") == 0)
+ 			transSpace = defGetInt32(defel);
  		else if (pg_strcasecmp(defel->defname, "initcond") == 0)
  			initval = defGetString(defel);
  		else if (pg_strcasecmp(defel->defname, "initcond1") == 0)
***************
*** 225,229 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
--- 228,233 ----
  						   finalfuncName,		/* final function name */
  						   sortoperatorName,	/* sort operator name */
  						   transTypeId, /* transition data type */
+ 						   transSpace,			/* transition space */
  						   initval);	/* initial condition */
  }
*** a/src/backend/commands/define.c
--- b/src/backend/commands/define.c
***************
*** 165,170 **** defGetBoolean(DefElem *def)
--- 165,194 ----
  }
  
  /*
+  * Extract an int32 value from a DefElem.
+  */
+ int32
+ defGetInt32(DefElem *def)
+ {
+ 	if (def->arg == NULL)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_SYNTAX_ERROR),
+ 				 errmsg("%s requires an integer value",
+ 						def->defname)));
+ 	switch (nodeTag(def->arg))
+ 	{
+ 		case T_Integer:
+ 			return (int32) intVal(def->arg);
+ 		default:
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 					 errmsg("%s requires an integer value",
+ 							def->defname)));
+ 	}
+ 	return 0;					/* keep compiler quiet */
+ }
+ 
+ /*
   * Extract an int64 value from a DefElem.
   */
  int64
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
***************
*** 461,466 **** count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
--- 461,467 ----
  		Oid			aggtransfn;
  		Oid			aggfinalfn;
  		Oid			aggtranstype;
+ 		int32 		aggtransspace;
  		QualCost	argcosts;
  		Oid		   *inputTypes;
  		int			numArguments;
***************
*** 478,483 **** count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
--- 479,485 ----
  		aggtransfn = aggform->aggtransfn;
  		aggfinalfn = aggform->aggfinalfn;
  		aggtranstype = aggform->aggtranstype;
+ 		aggtransspace = aggform->aggtransspace;
  		ReleaseSysCache(aggTuple);
  
  		/* count it */
***************
*** 524,536 **** count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
  			pfree(declaredArgTypes);
  		}
  
  		/*
  		 * If the transition type is pass-by-value then it doesn't add
  		 * anything to the required size of the hashtable.	If it is
  		 * pass-by-reference then we have to add the estimated size of the
  		 * value itself, plus palloc overhead.
  		 */
! 		if (!get_typbyval(aggtranstype))
  		{
  			int32		aggtranstypmod;
  			int32		avgwidth;
--- 526,546 ----
  			pfree(declaredArgTypes);
  		}
  
+ 		/* 
+ 		 * If approximate average space used by aggregate transition value is
+ 		 * specified in pg_aggregate, then use it for transitionSpace.
+ 		 */
+ 		if (aggtransspace > 0)
+ 		{
+ 			costs->transitionSpace += aggtransspace;
+ 		}
  		/*
  		 * If the transition type is pass-by-value then it doesn't add
  		 * anything to the required size of the hashtable.	If it is
  		 * pass-by-reference then we have to add the estimated size of the
  		 * value itself, plus palloc overhead.
  		 */
! 		else if (!get_typbyval(aggtranstype))
  		{
  			int32		aggtranstypmod;
  			int32		avgwidth;
*** a/src/backend/utils/adt/numeric.c
--- b/src/backend/utils/adt/numeric.c
***************
*** 2464,2571 **** numeric_float4(PG_FUNCTION_ARGS)
   *
   * Aggregate functions
   *
!  * The transition datatype for all these aggregates is a 3-element array
!  * of Numeric, holding the values N, sum(X), sum(X*X) in that order.
!  *
!  * We represent N as a numeric mainly to avoid having to build a special
!  * datatype; it's unlikely it'd overflow an int4, but ...
   *
   * ----------------------------------------------------------------------
   */
  
! static ArrayType *
! do_numeric_accum(ArrayType *transarray, Numeric newval)
  {
! 	Datum	   *transdatums;
! 	int			ndatums;
! 	Datum		N,
! 				sumX,
! 				sumX2;
! 	ArrayType  *result;
! 
! 	/* We assume the input is array of numeric */
! 	deconstruct_array(transarray,
! 					  NUMERICOID, -1, false, 'i',
! 					  &transdatums, NULL, &ndatums);
! 	if (ndatums != 3)
! 		elog(ERROR, "expected 3-element numeric array");
! 	N = transdatums[0];
! 	sumX = transdatums[1];
! 	sumX2 = transdatums[2];
! 
! 	N = DirectFunctionCall1(numeric_inc, N);
! 	sumX = DirectFunctionCall2(numeric_add, sumX,
! 							   NumericGetDatum(newval));
! 	sumX2 = DirectFunctionCall2(numeric_add, sumX2,
! 								DirectFunctionCall2(numeric_mul,
! 													NumericGetDatum(newval),
! 													NumericGetDatum(newval)));
! 
! 	transdatums[0] = N;
! 	transdatums[1] = sumX;
! 	transdatums[2] = sumX2;
! 
! 	result = construct_array(transdatums, 3,
! 							 NUMERICOID, -1, false, 'i');
  
! 	return result;
  }
  
! /*
!  * Improve avg performance by not caclulating sum(X*X).
!  */
! static ArrayType *
! do_numeric_avg_accum(ArrayType *transarray, 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));
! 
! 	transdatums[0] = N;
! 	transdatums[1] = sumX;
! 
! 	result = construct_array(transdatums, 2,
! 							 NUMERICOID, -1, false, 'i');
  
! 	return result;
  }
  
  Datum
  numeric_accum(PG_FUNCTION_ARGS)
  {
! 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
! 	Numeric		newval = PG_GETARG_NUMERIC(1);
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
  }
  
  /*
   * Optimized case for average of numeric.
   */
  Datum
  numeric_avg_accum(PG_FUNCTION_ARGS)
  {
! 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
! 	Numeric		newval = PG_GETARG_NUMERIC(1);
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval));
  }
  
  /*
   * Integer data types all use Numeric accumulators to share code and
   * avoid risk of overflow.	For int2 and int4 inputs, Numeric accumulation
--- 2464,2620 ----
   *
   * Aggregate functions
   *
!  * The transition datatype for all these aggregates is a pointer to 
!  * a struct NumericAggState allocated in the aggregate context.
   *
   * ----------------------------------------------------------------------
   */
  
! typedef struct NumericAggState
! {
! 	bool 			first;
! 	bool 			isNaN;
! 	uint64			N;
! 	NumericVar 		sumX;
! 	NumericVar 		sumX2;
! 	bool 			calcSumX2;
! 	MemoryContext 	agg_context;
! } NumericAggState;
! 
! 
! static NumericAggState *
! makeNumericAggState(FunctionCallInfo fcinfo, bool calcSumX2)
  {
! 	NumericAggState 	*state;
! 	MemoryContext 		 agg_context;
! 	MemoryContext 		 old_context;
  
! 	if (!AggCheckCallContext(fcinfo, &agg_context))
! 	{
! 		elog(ERROR, "this is called in non-aggregate context");
! 	}
! 
! 	old_context = MemoryContextSwitchTo(agg_context);
! 
! 	state = palloc0(sizeof(NumericAggState));
! 	state->first = true;
! 	state->calcSumX2 = calcSumX2;
! 	state->agg_context = agg_context;
! 
! 	MemoryContextSwitchTo(old_context);
! 
! 	return state;
  }
  
! 
! static void
! do_numeric_accum(NumericAggState *state, Numeric newval)
  {
! 	NumericVar 		X;
! 	NumericVar 		X2;
! 	MemoryContext 	old_context;
! 	bool 			first;
! 	
! 	first = state->first;
! 	state->first = false;
! 	state->N++;
! 
! 	if (state->isNaN || NUMERIC_IS_NAN(newval))
! 	{
! 		state->isNaN = true;
! 		return;
! 	}
  
! 	init_var_from_num(newval, &X);
! 
! 	if (state->calcSumX2)
! 	{
! 		init_var(&X2);
! 		mul_var(&X, &X, &X2, X.dscale * 2);
! 	}
! 
! 	old_context = MemoryContextSwitchTo(state->agg_context);
! 
! 	if (!first)
! 	{
! 		NumericVar preSumX;
! 
! 		memcpy(&preSumX, &(state->sumX), sizeof(NumericVar));
! 		init_var(&(state->sumX));
! 		add_var(&X, &preSumX, &(state->sumX));
! 		free_var(&preSumX);
! 
! 		if (state->calcSumX2)
! 		{
! 			NumericVar preSumX2;
! 
! 			memcpy(&preSumX2, &(state->sumX2), sizeof(NumericVar));
! 			init_var(&(state->sumX2));
! 			add_var(&X2, &preSumX2, &(state->sumX2));
! 			free_var(&preSumX2);
! 		}
! 	}
! 	else
! 	{
! 		set_var_from_var(&X, &(state->sumX));
! 
! 		if (state->calcSumX2)
! 			set_var_from_var(&X2, &(state->sumX2));
! 	}
! 
! 	MemoryContextSwitchTo(old_context);
  }
  
+ 
  Datum
  numeric_accum(PG_FUNCTION_ARGS)
  {
! 	NumericAggState *state;
! 
! 	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
  
! 	if (!PG_ARGISNULL(1))
! 	{
! 		/* On the first time through, create the state variable. */
! 		if (state == NULL)
! 			state = makeNumericAggState(fcinfo, true);
! 		
! 		do_numeric_accum(state, PG_GETARG_NUMERIC(1));
! 	}
! 
! 	if (state == NULL)
! 		PG_RETURN_NULL();
! 	else
! 		PG_RETURN_POINTER(state);
  }
  
+ 
  /*
   * Optimized case for average of numeric.
   */
  Datum
  numeric_avg_accum(PG_FUNCTION_ARGS)
  {
! 	NumericAggState *state;
  
! 	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
! 
! 	if (!PG_ARGISNULL(1))
! 	{
! 		/* On the first time through, create the state variable. */
! 		if (state == NULL)
! 			state = makeNumericAggState(fcinfo, false);
! 		
! 		do_numeric_accum(state, PG_GETARG_NUMERIC(1));
! 	}
! 
! 	if (state == NULL)
! 		PG_RETURN_NULL();
! 	else
! 		PG_RETURN_POINTER(state);
  }
  
+ 
  /*
   * Integer data types all use Numeric accumulators to share code and
   * avoid risk of overflow.	For int2 and int4 inputs, Numeric accumulation
***************
*** 2578,2664 **** numeric_avg_accum(PG_FUNCTION_ARGS)
  Datum
  int2_accum(PG_FUNCTION_ARGS)
  {
! 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
! 	Datum		newval2 = PG_GETARG_DATUM(1);
! 	Numeric		newval;
  
! 	newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric, newval2));
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
  }
  
  Datum
  int4_accum(PG_FUNCTION_ARGS)
  {
! 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
! 	Datum		newval4 = PG_GETARG_DATUM(1);
! 	Numeric		newval;
  
! 	newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric, newval4));
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
  }
  
  Datum
  int8_accum(PG_FUNCTION_ARGS)
  {
! 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
! 	Datum		newval8 = PG_GETARG_DATUM(1);
! 	Numeric		newval;
  
! 	newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
  }
  
  /*
   * Optimized case for average of int8.
   */
  Datum
  int8_avg_accum(PG_FUNCTION_ARGS)
  {
! 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
! 	Datum		newval8 = PG_GETARG_DATUM(1);
! 	Numeric		newval;
! 
! 	newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval));
  }
  
  
  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]);
  
! 	/* SQL defines AVG of no values to be NULL */
! 	/* N is zero iff no digits (cf. numeric_uminus) */
! 	if (NUMERIC_NDIGITS(N) == 0)
  		PG_RETURN_NULL();
  
! 	PG_RETURN_DATUM(DirectFunctionCall2(numeric_div,
! 										NumericGetDatum(sumX),
! 										NumericGetDatum(N)));
  }
  
  /*
   * Workhorse routine for the standard deviance and variance
!  * aggregates. 'transarray' is the aggregate's transition
!  * array. 'variance' specifies whether we should calculate the
   * variance or the standard deviation. 'sample' indicates whether the
   * caller is interested in the sample or the population
   * variance/stddev.
--- 2627,2776 ----
  Datum
  int2_accum(PG_FUNCTION_ARGS)
  {
! 	NumericAggState *state;
  
! 	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
  
! 	if (!PG_ARGISNULL(1))
! 	{
! 		Datum		newval2 = PG_GETARG_DATUM(1);
! 		Numeric		newval;
! 
! 		/* On the first time through, create the state variable. */
! 		if (state == NULL)
! 			state = makeNumericAggState(fcinfo, true);
! 		
! 		newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric, newval2));
! 		do_numeric_accum(state, newval);
! 	}
! 
! 	if (state == NULL)
! 		PG_RETURN_NULL();
! 	else
! 		PG_RETURN_POINTER(state);
  }
  
+ 
  Datum
  int4_accum(PG_FUNCTION_ARGS)
  {
! 	NumericAggState *state;
! 
! 	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
  
! 	if (!PG_ARGISNULL(1))
! 	{
! 		Datum		newval4 = PG_GETARG_DATUM(1);
! 		Numeric		newval;
! 
! 		/* On the first time through, create the state variable. */
! 		if (state == NULL)
! 			state = makeNumericAggState(fcinfo, true);
! 		
! 		newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric, newval4));
! 		do_numeric_accum(state, newval);
! 	}
  
! 	if (state == NULL)
! 		PG_RETURN_NULL();
! 	else
! 		PG_RETURN_POINTER(state);
  }
  
+ 
  Datum
  int8_accum(PG_FUNCTION_ARGS)
  {
! 	NumericAggState *state;
  
! 	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
! 
! 	if (!PG_ARGISNULL(1))
! 	{
! 		Datum		newval8 = PG_GETARG_DATUM(1);
! 		Numeric		newval;
! 
! 		/* On the first time through, create the state variable. */
! 		if (state == NULL)
! 			state = makeNumericAggState(fcinfo, true);
! 		
! 		newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
! 		do_numeric_accum(state, newval);
! 	}
  
! 	if (state == NULL)
! 		PG_RETURN_NULL();
! 	else
! 		PG_RETURN_POINTER(state);
  }
  
+ 
  /*
   * Optimized case for average of int8.
   */
  Datum
  int8_avg_accum(PG_FUNCTION_ARGS)
  {
! 	NumericAggState *state;	
! 	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
  
! 	if (!PG_ARGISNULL(1))
! 	{
! 		Datum		newval8 = PG_GETARG_DATUM(1);
! 		Numeric		newval;
! 
! 		/* On the first time through, create the state variable. */
! 		if (state == NULL)
! 			state = makeNumericAggState(fcinfo, false);
! 		
! 		newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
! 		do_numeric_accum(state, newval);
! 	}
!  
! 	if (state == NULL)
! 		PG_RETURN_NULL();
! 	else
! 		PG_RETURN_POINTER(state);
  }
  
  
  Datum
  numeric_avg(PG_FUNCTION_ARGS)
  {
! 	Datum 		 		N_datum;
! 	Datum 				sumX_datum;
! 	NumericAggState 	*state;
  
! 	if (PG_ARGISNULL(0))
  		PG_RETURN_NULL();
  
! 	state = (NumericAggState *) PG_GETARG_POINTER(0);
! 
! 	N_datum = DirectFunctionCall1(int8_numeric, Int64GetDatum(state->N));
! 	sumX_datum = NumericGetDatum(make_result(&state->sumX));
! 
! 	PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, sumX_datum, N_datum));
  }
  
+ 
+ Datum
+ numeric_sum(PG_FUNCTION_ARGS)
+ {
+ 	NumericAggState 	*state;
+ 
+ 	if (PG_ARGISNULL(0))
+ 		PG_RETURN_NULL();
+  
+ 	state = (NumericAggState *) PG_GETARG_POINTER(0);
+ 
+ 	PG_RETURN_NUMERIC(make_result(&(state->sumX)));
+ }
+ 
+ 
  /*
   * Workhorse routine for the standard deviance and variance
!  * aggregates. 'state' is aggregate's transition state.
!  * 'variance' specifies whether we should calculate the
   * variance or the standard deviation. 'sample' indicates whether the
   * caller is interested in the sample or the population
   * variance/stddev.
***************
*** 2667,2682 **** numeric_avg(PG_FUNCTION_ARGS)
   * *is_null is set to true and NULL is returned.
   */
  static Numeric
! numeric_stddev_internal(ArrayType *transarray,
  						bool variance, bool sample,
  						bool *is_null)
  {
! 	Datum	   *transdatums;
! 	int			ndatums;
! 	Numeric		N,
! 				sumX,
! 				sumX2,
! 				res;
  	NumericVar	vN,
  				vsumX,
  				vsumX2,
--- 2779,2789 ----
   * *is_null is set to true and NULL is returned.
   */
  static Numeric
! numeric_stddev_internal(NumericAggState *state,
  						bool variance, bool sample,
  						bool *is_null)
  {
! 	Numeric		res;
  	NumericVar	vN,
  				vsumX,
  				vsumX2,
***************
*** 2684,2705 **** numeric_stddev_internal(ArrayType *transarray,
  	NumericVar *comp;
  	int			rscale;
  
  	*is_null = false;
  
! 	/* We assume the input is array of numeric */
! 	deconstruct_array(transarray,
! 					  NUMERICOID, -1, false, 'i',
! 					  &transdatums, NULL, &ndatums);
! 	if (ndatums != 3)
! 		elog(ERROR, "expected 3-element numeric array");
! 	N = DatumGetNumeric(transdatums[0]);
! 	sumX = DatumGetNumeric(transdatums[1]);
! 	sumX2 = DatumGetNumeric(transdatums[2]);
! 
! 	if (NUMERIC_IS_NAN(N) || NUMERIC_IS_NAN(sumX) || NUMERIC_IS_NAN(sumX2))
  		return make_result(&const_nan);
  
! 	init_var_from_num(N, &vN);
  
  	/*
  	 * Sample stddev and variance are undefined when N <= 1; population stddev
--- 2791,2814 ----
  	NumericVar *comp;
  	int			rscale;
  
+ 	if (state == NULL)
+ 	{
+ 		*is_null = true;
+ 		return NULL;
+ 	}
+ 
  	*is_null = false;
  
! 	if (state->isNaN)
  		return make_result(&const_nan);
  
! 	init_var(&vN);
! 	init_var(&vsumX);
! 	init_var(&vsumX2);
! 
! 	int8_to_numericvar(state->N, &vN);
! 	set_var_from_var(&(state->sumX), &vsumX);
! 	set_var_from_var(&(state->sumX2), &vsumX2);
  
  	/*
  	 * Sample stddev and variance are undefined when N <= 1; population stddev
***************
*** 2719,2726 **** numeric_stddev_internal(ArrayType *transarray,
  	init_var(&vNminus1);
  	sub_var(&vN, &const_one, &vNminus1);
  
! 	init_var_from_num(sumX, &vsumX);
! 	init_var_from_num(sumX2, &vsumX2);
  
  	/* compute rscale for mul_var calls */
  	rscale = vsumX.dscale * 2;
--- 2828,2835 ----
  	init_var(&vNminus1);
  	sub_var(&vN, &const_one, &vNminus1);
  
! 	set_var_from_var(&(state->sumX), &vsumX);
! 	set_var_from_var(&(state->sumX2), &vsumX2);
  
  	/* compute rscale for mul_var calls */
  	rscale = vsumX.dscale * 2;
***************
*** 2761,2767 **** numeric_var_samp(PG_FUNCTION_ARGS)
  	Numeric		res;
  	bool		is_null;
  
! 	res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
  								  true, true, &is_null);
  
  	if (is_null)
--- 2870,2876 ----
  	Numeric		res;
  	bool		is_null;
  
! 	res = numeric_stddev_internal((NumericAggState *) PG_GETARG_POINTER(0),
  								  true, true, &is_null);
  
  	if (is_null)
***************
*** 2776,2782 **** numeric_stddev_samp(PG_FUNCTION_ARGS)
  	Numeric		res;
  	bool		is_null;
  
! 	res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
  								  false, true, &is_null);
  
  	if (is_null)
--- 2885,2891 ----
  	Numeric		res;
  	bool		is_null;
  
! 	res = numeric_stddev_internal((NumericAggState *) PG_GETARG_POINTER(0),
  								  false, true, &is_null);
  
  	if (is_null)
***************
*** 2791,2797 **** numeric_var_pop(PG_FUNCTION_ARGS)
  	Numeric		res;
  	bool		is_null;
  
! 	res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
  								  true, false, &is_null);
  
  	if (is_null)
--- 2900,2906 ----
  	Numeric		res;
  	bool		is_null;
  
! 	res = numeric_stddev_internal((NumericAggState *) PG_GETARG_POINTER(0),
  								  true, false, &is_null);
  
  	if (is_null)
***************
*** 2806,2812 **** numeric_stddev_pop(PG_FUNCTION_ARGS)
  	Numeric		res;
  	bool		is_null;
  
! 	res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
  								  false, false, &is_null);
  
  	if (is_null)
--- 2915,2921 ----
  	Numeric		res;
  	bool		is_null;
  
! 	res = numeric_stddev_internal((NumericAggState *) PG_GETARG_POINTER(0),
  								  false, false, &is_null);
  
  	if (is_null)
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 11402,11413 **** dumpAgg(Archive *fout, AggInfo *agginfo)
--- 11402,11415 ----
  	int			i_aggfinalfn;
  	int			i_aggsortop;
  	int			i_aggtranstype;
+ 	int 		i_aggtransspace;
  	int			i_agginitval;
  	int			i_convertok;
  	const char *aggtransfn;
  	const char *aggfinalfn;
  	const char *aggsortop;
  	const char *aggtranstype;
+ 	const char *aggtransspace;
  	const char *agginitval;
  	bool		convertok;
  
***************
*** 11425,11436 **** dumpAgg(Archive *fout, AggInfo *agginfo)
  	selectSourceSchema(fout, agginfo->aggfn.dobj.namespace->dobj.name);
  
  	/* Get aggregate-specific details */
! 	if (fout->remoteVersion >= 80100)
  	{
  		appendPQExpBuffer(query, "SELECT aggtransfn, "
  						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
  						  "aggsortop::pg_catalog.regoperator, "
! 						  "agginitval, "
  						  "'t'::boolean AS convertok "
  					  "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
  						  "WHERE a.aggfnoid = p.oid "
--- 11427,11450 ----
  	selectSourceSchema(fout, agginfo->aggfn.dobj.namespace->dobj.name);
  
  	/* Get aggregate-specific details */
! 	if (fout->remoteVersion >= 90300)
! 	{
! 		appendPQExpBuffer(query, "SELECT aggtransfn, "
! 						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
! 						  "aggsortop::pg_catalog.regoperator, "
! 						  "aggtransspace, agginitval, "
! 						  "'t'::boolean AS convertok "
! 					  "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
! 						  "WHERE a.aggfnoid = p.oid "
! 						  "AND p.oid = '%u'::pg_catalog.oid",
! 						  agginfo->aggfn.dobj.catId.oid);
! 	}
! 	else if (fout->remoteVersion >= 80100)
  	{
  		appendPQExpBuffer(query, "SELECT aggtransfn, "
  						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
  						  "aggsortop::pg_catalog.regoperator, "
! 						  "0 AS aggtransspace, agginitval, "
  						  "'t'::boolean AS convertok "
  					  "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
  						  "WHERE a.aggfnoid = p.oid "
***************
*** 11442,11448 **** dumpAgg(Archive *fout, AggInfo *agginfo)
  		appendPQExpBuffer(query, "SELECT aggtransfn, "
  						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
  						  "0 AS aggsortop, "
! 						  "agginitval, "
  						  "'t'::boolean AS convertok "
  					  "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
  						  "WHERE a.aggfnoid = p.oid "
--- 11456,11462 ----
  		appendPQExpBuffer(query, "SELECT aggtransfn, "
  						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
  						  "0 AS aggsortop, "
! 						  "0 AS aggtransspace, agginitval, "
  						  "'t'::boolean AS convertok "
  					  "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
  						  "WHERE a.aggfnoid = p.oid "
***************
*** 11454,11460 **** dumpAgg(Archive *fout, AggInfo *agginfo)
  		appendPQExpBuffer(query, "SELECT aggtransfn, aggfinalfn, "
  						  "format_type(aggtranstype, NULL) AS aggtranstype, "
  						  "0 AS aggsortop, "
! 						  "agginitval, "
  						  "'t'::boolean AS convertok "
  						  "FROM pg_aggregate "
  						  "WHERE oid = '%u'::oid",
--- 11468,11474 ----
  		appendPQExpBuffer(query, "SELECT aggtransfn, aggfinalfn, "
  						  "format_type(aggtranstype, NULL) AS aggtranstype, "
  						  "0 AS aggsortop, "
! 						  "0 AS aggtransspace, agginitval, "
  						  "'t'::boolean AS convertok "
  						  "FROM pg_aggregate "
  						  "WHERE oid = '%u'::oid",
***************
*** 11466,11472 **** dumpAgg(Archive *fout, AggInfo *agginfo)
  						  "aggfinalfn, "
  						  "(SELECT typname FROM pg_type WHERE oid = aggtranstype1) AS aggtranstype, "
  						  "0 AS aggsortop, "
! 						  "agginitval1 AS agginitval, "
  						  "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) AS convertok "
  						  "FROM pg_aggregate "
  						  "WHERE oid = '%u'::oid",
--- 11480,11486 ----
  						  "aggfinalfn, "
  						  "(SELECT typname FROM pg_type WHERE oid = aggtranstype1) AS aggtranstype, "
  						  "0 AS aggsortop, "
! 						  "0 AS aggtransspace, agginitval1 AS agginitval, "
  						  "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) AS convertok "
  						  "FROM pg_aggregate "
  						  "WHERE oid = '%u'::oid",
***************
*** 11479,11484 **** dumpAgg(Archive *fout, AggInfo *agginfo)
--- 11493,11499 ----
  	i_aggfinalfn = PQfnumber(res, "aggfinalfn");
  	i_aggsortop = PQfnumber(res, "aggsortop");
  	i_aggtranstype = PQfnumber(res, "aggtranstype");
+ 	i_aggtransspace = PQfnumber(res, "aggtransspace");
  	i_agginitval = PQfnumber(res, "agginitval");
  	i_convertok = PQfnumber(res, "convertok");
  
***************
*** 11486,11491 **** dumpAgg(Archive *fout, AggInfo *agginfo)
--- 11501,11507 ----
  	aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn);
  	aggsortop = PQgetvalue(res, 0, i_aggsortop);
  	aggtranstype = PQgetvalue(res, 0, i_aggtranstype);
+ 	aggtransspace = PQgetvalue(res, 0, i_aggtransspace);
  	agginitval = PQgetvalue(res, 0, i_agginitval);
  	convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't');
  
***************
*** 11522,11527 **** dumpAgg(Archive *fout, AggInfo *agginfo)
--- 11538,11549 ----
  						  fmtId(aggtranstype));
  	}
  
+ 	if (strcmp(aggtransspace, "0") != 0)
+ 	{
+ 		appendPQExpBuffer(details, ",\n    SSPACE = %s",
+ 						  aggtransspace);
+ 	}
+ 
  	if (!PQgetisnull(res, 0, i_agginitval))
  	{
  		appendPQExpBuffer(details, ",\n    INITCOND = ");
*** a/src/include/catalog/pg_aggregate.h
--- b/src/include/catalog/pg_aggregate.h
***************
*** 44,49 **** CATALOG(pg_aggregate,2600) BKI_WITHOUT_OIDS
--- 44,50 ----
  	regproc		aggfinalfn;
  	Oid			aggsortop;
  	Oid			aggtranstype;
+ 	int32		aggtransspace;
  
  #ifdef CATALOG_VARLEN			/* variable-length fields start here */
  	text		agginitval;
***************
*** 62,74 **** typedef FormData_pg_aggregate *Form_pg_aggregate;
   * ----------------
   */
  
! #define Natts_pg_aggregate				6
  #define Anum_pg_aggregate_aggfnoid		1
  #define Anum_pg_aggregate_aggtransfn	2
  #define Anum_pg_aggregate_aggfinalfn	3
  #define Anum_pg_aggregate_aggsortop		4
  #define Anum_pg_aggregate_aggtranstype	5
! #define Anum_pg_aggregate_agginitval	6
  
  
  /* ----------------
--- 63,76 ----
   * ----------------
   */
  
! #define Natts_pg_aggregate				7
  #define Anum_pg_aggregate_aggfnoid		1
  #define Anum_pg_aggregate_aggtransfn	2
  #define Anum_pg_aggregate_aggfinalfn	3
  #define Anum_pg_aggregate_aggsortop		4
  #define Anum_pg_aggregate_aggtranstype	5
! #define Anum_pg_aggregate_aggtransspace	6
! #define Anum_pg_aggregate_agginitval	7
  
  
  /* ----------------
***************
*** 77,239 **** typedef FormData_pg_aggregate *Form_pg_aggregate;
   */
  
  /* avg */
! DATA(insert ( 2100	int8_avg_accum	numeric_avg		0	1231	"{0,0}" ));
! 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 ( 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}" ));
  
  /* sum */
! DATA(insert ( 2107	int8_sum		-				0	1700	_null_ ));
! DATA(insert ( 2108	int4_sum		-				0	20		_null_ ));
! DATA(insert ( 2109	int2_sum		-				0	20		_null_ ));
! DATA(insert ( 2110	float4pl		-				0	700		_null_ ));
! DATA(insert ( 2111	float8pl		-				0	701		_null_ ));
! DATA(insert ( 2112	cash_pl			-				0	790		_null_ ));
! DATA(insert ( 2113	interval_pl		-				0	1186	_null_ ));
! DATA(insert ( 2114	numeric_add		-				0	1700	_null_ ));
  
  /* max */
! DATA(insert ( 2115	int8larger		-				413		20		_null_ ));
! DATA(insert ( 2116	int4larger		-				521		23		_null_ ));
! DATA(insert ( 2117	int2larger		-				520		21		_null_ ));
! DATA(insert ( 2118	oidlarger		-				610		26		_null_ ));
! DATA(insert ( 2119	float4larger	-				623		700		_null_ ));
! DATA(insert ( 2120	float8larger	-				674		701		_null_ ));
! DATA(insert ( 2121	int4larger		-				563		702		_null_ ));
! DATA(insert ( 2122	date_larger		-				1097	1082	_null_ ));
! DATA(insert ( 2123	time_larger		-				1112	1083	_null_ ));
! DATA(insert ( 2124	timetz_larger	-				1554	1266	_null_ ));
! DATA(insert ( 2125	cashlarger		-				903		790		_null_ ));
! DATA(insert ( 2126	timestamp_larger	-			2064	1114	_null_ ));
! DATA(insert ( 2127	timestamptz_larger	-			1324	1184	_null_ ));
! DATA(insert ( 2128	interval_larger -				1334	1186	_null_ ));
! DATA(insert ( 2129	text_larger		-				666		25		_null_ ));
! DATA(insert ( 2130	numeric_larger	-				1756	1700	_null_ ));
! DATA(insert ( 2050	array_larger	-				1073	2277	_null_ ));
! DATA(insert ( 2244	bpchar_larger	-				1060	1042	_null_ ));
! DATA(insert ( 2797	tidlarger		-				2800	27		_null_ ));
! DATA(insert ( 3526	enum_larger		-				3519	3500	_null_ ));
  
  /* min */
! DATA(insert ( 2131	int8smaller		-				412		20		_null_ ));
! DATA(insert ( 2132	int4smaller		-				97		23		_null_ ));
! DATA(insert ( 2133	int2smaller		-				95		21		_null_ ));
! DATA(insert ( 2134	oidsmaller		-				609		26		_null_ ));
! DATA(insert ( 2135	float4smaller	-				622		700		_null_ ));
! DATA(insert ( 2136	float8smaller	-				672		701		_null_ ));
! DATA(insert ( 2137	int4smaller		-				562		702		_null_ ));
! DATA(insert ( 2138	date_smaller	-				1095	1082	_null_ ));
! DATA(insert ( 2139	time_smaller	-				1110	1083	_null_ ));
! DATA(insert ( 2140	timetz_smaller	-				1552	1266	_null_ ));
! DATA(insert ( 2141	cashsmaller		-				902		790		_null_ ));
! DATA(insert ( 2142	timestamp_smaller	-			2062	1114	_null_ ));
! DATA(insert ( 2143	timestamptz_smaller -			1322	1184	_null_ ));
! DATA(insert ( 2144	interval_smaller	-			1332	1186	_null_ ));
! DATA(insert ( 2145	text_smaller	-				664		25		_null_ ));
! DATA(insert ( 2146	numeric_smaller -				1754	1700	_null_ ));
! DATA(insert ( 2051	array_smaller	-				1072	2277	_null_ ));
! DATA(insert ( 2245	bpchar_smaller	-				1058	1042	_null_ ));
! DATA(insert ( 2798	tidsmaller		-				2799	27		_null_ ));
! DATA(insert ( 3527	enum_smaller	-				3518	3500	_null_ ));
  
  /* count */
! DATA(insert ( 2147	int8inc_any		-				0		20		"0" ));
! DATA(insert ( 2803	int8inc			-				0		20		"0" ));
  
  /* var_pop */
! DATA(insert ( 2718	int8_accum	numeric_var_pop 0	1231	"{0,0,0}" ));
! DATA(insert ( 2719	int4_accum	numeric_var_pop 0	1231	"{0,0,0}" ));
! DATA(insert ( 2720	int2_accum	numeric_var_pop 0	1231	"{0,0,0}" ));
! DATA(insert ( 2721	float4_accum	float8_var_pop 0	1022	"{0,0,0}" ));
! DATA(insert ( 2722	float8_accum	float8_var_pop 0	1022	"{0,0,0}" ));
! DATA(insert ( 2723	numeric_accum  numeric_var_pop 0	1231	"{0,0,0}" ));
  
  /* var_samp */
! DATA(insert ( 2641	int8_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
! DATA(insert ( 2642	int4_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
! DATA(insert ( 2643	int2_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
! DATA(insert ( 2644	float4_accum	float8_var_samp 0	1022	"{0,0,0}" ));
! DATA(insert ( 2645	float8_accum	float8_var_samp 0	1022	"{0,0,0}" ));
! DATA(insert ( 2646	numeric_accum  numeric_var_samp 0	1231	"{0,0,0}" ));
  
  /* variance: historical Postgres syntax for var_samp */
! DATA(insert ( 2148	int8_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
! DATA(insert ( 2149	int4_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
! DATA(insert ( 2150	int2_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
! DATA(insert ( 2151	float4_accum	float8_var_samp 0	1022	"{0,0,0}" ));
! DATA(insert ( 2152	float8_accum	float8_var_samp 0	1022	"{0,0,0}" ));
! DATA(insert ( 2153	numeric_accum  numeric_var_samp 0	1231	"{0,0,0}" ));
  
  /* stddev_pop */
! DATA(insert ( 2724	int8_accum	numeric_stddev_pop		0	1231	"{0,0,0}" ));
! DATA(insert ( 2725	int4_accum	numeric_stddev_pop		0	1231	"{0,0,0}" ));
! DATA(insert ( 2726	int2_accum	numeric_stddev_pop		0	1231	"{0,0,0}" ));
! DATA(insert ( 2727	float4_accum	float8_stddev_pop	0	1022	"{0,0,0}" ));
! DATA(insert ( 2728	float8_accum	float8_stddev_pop	0	1022	"{0,0,0}" ));
! DATA(insert ( 2729	numeric_accum	numeric_stddev_pop	0	1231	"{0,0,0}" ));
  
  /* stddev_samp */
! DATA(insert ( 2712	int8_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
! DATA(insert ( 2713	int4_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
! DATA(insert ( 2714	int2_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
! DATA(insert ( 2715	float4_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
! DATA(insert ( 2716	float8_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
! DATA(insert ( 2717	numeric_accum	numeric_stddev_samp 0	1231	"{0,0,0}" ));
  
  /* stddev: historical Postgres syntax for stddev_samp */
! DATA(insert ( 2154	int8_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
! DATA(insert ( 2155	int4_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
! DATA(insert ( 2156	int2_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
! DATA(insert ( 2157	float4_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
! DATA(insert ( 2158	float8_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
! DATA(insert ( 2159	numeric_accum	numeric_stddev_samp 0	1231	"{0,0,0}" ));
  
  /* SQL2003 binary regression aggregates */
! DATA(insert ( 2818	int8inc_float8_float8		-				0	20		"0" ));
! DATA(insert ( 2819	float8_regr_accum	float8_regr_sxx			0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2820	float8_regr_accum	float8_regr_syy			0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2821	float8_regr_accum	float8_regr_sxy			0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2822	float8_regr_accum	float8_regr_avgx		0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2823	float8_regr_accum	float8_regr_avgy		0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2824	float8_regr_accum	float8_regr_r2			0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2825	float8_regr_accum	float8_regr_slope		0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2826	float8_regr_accum	float8_regr_intercept	0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2827	float8_regr_accum	float8_covar_pop		0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2828	float8_regr_accum	float8_covar_samp		0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2829	float8_regr_accum	float8_corr				0	1022	"{0,0,0,0,0,0}" ));
  
  /* boolean-and and boolean-or */
! DATA(insert ( 2517	booland_statefunc	-			58	16		_null_ ));
! DATA(insert ( 2518	boolor_statefunc	-			59	16		_null_ ));
! DATA(insert ( 2519	booland_statefunc	-			58	16		_null_ ));
  
  /* bitwise integer */
! DATA(insert ( 2236 int2and		  -					0	21		_null_ ));
! DATA(insert ( 2237 int2or		  -					0	21		_null_ ));
! DATA(insert ( 2238 int4and		  -					0	23		_null_ ));
! DATA(insert ( 2239 int4or		  -					0	23		_null_ ));
! DATA(insert ( 2240 int8and		  -					0	20		_null_ ));
! DATA(insert ( 2241 int8or		  -					0	20		_null_ ));
! DATA(insert ( 2242 bitand		  -					0	1560	_null_ ));
! DATA(insert ( 2243 bitor		  -					0	1560	_null_ ));
  
  /* xml */
! DATA(insert ( 2901 xmlconcat2	  -					0	142		_null_ ));
  
  /* array */
! DATA(insert ( 2335	array_agg_transfn	array_agg_finalfn		0	2281	_null_ ));
  
  /* text */
! DATA(insert ( 3538	string_agg_transfn	string_agg_finalfn		0	2281	_null_ ));
  
  /* bytea */
! DATA(insert ( 3545	bytea_string_agg_transfn	bytea_string_agg_finalfn		0	2281	_null_ ));
  
  /* json */
! DATA(insert ( 3175	json_agg_transfn	json_agg_finalfn		0	2281	_null_ ));
  
  /*
   * prototypes for functions in pg_aggregate.c
--- 79,241 ----
   */
  
  /* avg */
! DATA(insert ( 2100	int8_avg_accum		numeric_avg		0	2281	128	_null_ ));
! DATA(insert ( 2101	int4_avg_accum		int8_avg		0	1016	0	"{0,0}" ));
! DATA(insert ( 2102	int2_avg_accum		int8_avg		0	1016	0	"{0,0}" ));
! DATA(insert ( 2103	numeric_avg_accum		numeric_avg		0	2281	128	_null_ ));
! DATA(insert ( 2104	float4_accum		float8_avg		0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2105	float8_accum		float8_avg		0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2106	interval_accum		interval_avg	0	1187	0	"{0 second,0 second}" ));
  
  /* sum */
! DATA(insert ( 2107	int8_avg_accum	numeric_sum		0	2281	128	_null_ ));
! DATA(insert ( 2108	int4_sum		-				0	20		0	_null_ ));
! DATA(insert ( 2109	int2_sum		-				0	20		0	_null_ ));
! DATA(insert ( 2110	float4pl		-				0	700		0	_null_ ));
! DATA(insert ( 2111	float8pl		-				0	701		0	_null_ ));
! DATA(insert ( 2112	cash_pl			-				0	790		0	_null_ ));
! DATA(insert ( 2113	interval_pl		-				0	1186	0	_null_ ));
! DATA(insert ( 2114	numeric_avg_accum	numeric_sum	0	2281	128	_null_ ));
  
  /* max */
! DATA(insert ( 2115	int8larger		-				413		20		0	_null_ ));
! DATA(insert ( 2116	int4larger		-				521		23		0	_null_ ));
! DATA(insert ( 2117	int2larger		-				520		21		0	_null_ ));
! DATA(insert ( 2118	oidlarger		-				610		26		0	_null_ ));
! DATA(insert ( 2119	float4larger	-				623		700		0	_null_ ));
! DATA(insert ( 2120	float8larger	-				674		701		0	_null_ ));
! DATA(insert ( 2121	int4larger		-				563		702		0	_null_ ));
! DATA(insert ( 2122	date_larger		-				1097	1082	0	_null_ ));
! DATA(insert ( 2123	time_larger		-				1112	1083	0	_null_ ));
! DATA(insert ( 2124	timetz_larger	-				1554	1266	0	_null_ ));
! DATA(insert ( 2125	cashlarger		-				903		790		0	_null_ ));
! DATA(insert ( 2126	timestamp_larger	-			2064	1114	0	_null_ ));
! DATA(insert ( 2127	timestamptz_larger	-			1324	1184	0	_null_ ));
! DATA(insert ( 2128	interval_larger -				1334	1186	0	_null_ ));
! DATA(insert ( 2129	text_larger		-				666		25		0	_null_ ));
! DATA(insert ( 2130	numeric_larger	-				1756	1700	0	_null_ ));
! DATA(insert ( 2050	array_larger	-				1073	2277	0	_null_ ));
! DATA(insert ( 2244	bpchar_larger	-				1060	1042	0	_null_ ));
! DATA(insert ( 2797	tidlarger		-				2800	27		0	_null_ ));
! DATA(insert ( 3526	enum_larger		-				3519	3500	0	_null_ ));
  
  /* min */
! DATA(insert ( 2131	int8smaller		-				412		20		0	_null_ ));
! DATA(insert ( 2132	int4smaller		-				97		23		0	_null_ ));
! DATA(insert ( 2133	int2smaller		-				95		21		0	_null_ ));
! DATA(insert ( 2134	oidsmaller		-				609		26		0	_null_ ));
! DATA(insert ( 2135	float4smaller	-				622		700		0	_null_ ));
! DATA(insert ( 2136	float8smaller	-				672		701		0	_null_ ));
! DATA(insert ( 2137	int4smaller		-				562		702		0	_null_ ));
! DATA(insert ( 2138	date_smaller	-				1095	1082	0	_null_ ));
! DATA(insert ( 2139	time_smaller	-				1110	1083	0	_null_ ));
! DATA(insert ( 2140	timetz_smaller	-				1552	1266	0	_null_ ));
! DATA(insert ( 2141	cashsmaller		-				902		790		0	_null_ ));
! DATA(insert ( 2142	timestamp_smaller	-			2062	1114	0	_null_ ));
! DATA(insert ( 2143	timestamptz_smaller -			1322	1184	0	_null_ ));
! DATA(insert ( 2144	interval_smaller	-			1332	1186	0	_null_ ));
! DATA(insert ( 2145	text_smaller	-				664		25		0	_null_ ));
! DATA(insert ( 2146	numeric_smaller -				1754	1700	0	_null_ ));
! DATA(insert ( 2051	array_smaller	-				1072	2277	0	_null_ ));
! DATA(insert ( 2245	bpchar_smaller	-				1058	1042	0	_null_ ));
! DATA(insert ( 2798	tidsmaller		-				2799	27		0	_null_ ));
! DATA(insert ( 3527	enum_smaller	-				3518	3500	0	_null_ ));
  
  /* count */
! DATA(insert ( 2147	int8inc_any		-				0		20		0	"0" ));
! DATA(insert ( 2803	int8inc			-				0		20		0	"0" ));
  
  /* var_pop */
! DATA(insert ( 2718	int8_accum	numeric_var_pop 0	2281	128	_null_ ));
! DATA(insert ( 2719	int4_accum	numeric_var_pop 0	2281	128	_null_ ));
! DATA(insert ( 2720	int2_accum	numeric_var_pop 0	2281	128	_null_ ));
! DATA(insert ( 2721	float4_accum	float8_var_pop 0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2722	float8_accum	float8_var_pop 0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2723	numeric_accum  numeric_var_pop 0	2281	128	_null_ ));
  
  /* var_samp */
! DATA(insert ( 2641	int8_accum	numeric_var_samp	0	2281	128	_null_ ));
! DATA(insert ( 2642	int4_accum	numeric_var_samp	0	2281	128	_null_ ));
! DATA(insert ( 2643	int2_accum	numeric_var_samp	0	2281	128	_null_ ));
! DATA(insert ( 2644	float4_accum	float8_var_samp 0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2645	float8_accum	float8_var_samp 0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2646	numeric_accum  numeric_var_samp 0	2281	128	_null_ ));
  
  /* variance: historical Postgres syntax for var_samp */
! DATA(insert ( 2148	int8_accum	numeric_var_samp	0	2281	128	_null_ ));
! DATA(insert ( 2149	int4_accum	numeric_var_samp	0	2281	128	_null_ ));
! DATA(insert ( 2150	int2_accum	numeric_var_samp	0	2281	128	_null_ ));
! DATA(insert ( 2151	float4_accum	float8_var_samp 0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2152	float8_accum	float8_var_samp 0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2153	numeric_accum  numeric_var_samp 0	2281	128	_null_ ));
  
  /* stddev_pop */
! DATA(insert ( 2724	int8_accum	numeric_stddev_pop		0	2281	128	_null_ ));
! DATA(insert ( 2725	int4_accum	numeric_stddev_pop		0	2281	128	_null_ ));
! DATA(insert ( 2726	int2_accum	numeric_stddev_pop		0	2281	128	_null_ ));
! DATA(insert ( 2727	float4_accum	float8_stddev_pop	0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2728	float8_accum	float8_stddev_pop	0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2729	numeric_accum	numeric_stddev_pop	0	2281	128	_null_ ));
  
  /* stddev_samp */
! DATA(insert ( 2712	int8_accum	numeric_stddev_samp		0	2281	128	_null_ ));
! DATA(insert ( 2713	int4_accum	numeric_stddev_samp		0	2281	128	_null_ ));
! DATA(insert ( 2714	int2_accum	numeric_stddev_samp		0	2281	128	_null_ ));
! DATA(insert ( 2715	float4_accum	float8_stddev_samp	0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2716	float8_accum	float8_stddev_samp	0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2717	numeric_accum	numeric_stddev_samp 0	2281	128	_null_ ));
  
  /* stddev: historical Postgres syntax for stddev_samp */
! DATA(insert ( 2154	int8_accum	numeric_stddev_samp		0	2281	128	_null_ ));
! DATA(insert ( 2155	int4_accum	numeric_stddev_samp		0	2281	128	_null_ ));
! DATA(insert ( 2156	int2_accum	numeric_stddev_samp		0	2281	128	_null_ ));
! DATA(insert ( 2157	float4_accum	float8_stddev_samp	0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2158	float8_accum	float8_stddev_samp	0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2159	numeric_accum	numeric_stddev_samp 0	2281	128	_null_ ));
  
  /* SQL2003 binary regression aggregates */
! DATA(insert ( 2818	int8inc_float8_float8		-				0	20		0	"0" ));
! DATA(insert ( 2819	float8_regr_accum	float8_regr_sxx			0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2820	float8_regr_accum	float8_regr_syy			0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2821	float8_regr_accum	float8_regr_sxy			0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2822	float8_regr_accum	float8_regr_avgx		0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2823	float8_regr_accum	float8_regr_avgy		0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2824	float8_regr_accum	float8_regr_r2			0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2825	float8_regr_accum	float8_regr_slope		0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2826	float8_regr_accum	float8_regr_intercept	0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2827	float8_regr_accum	float8_covar_pop		0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2828	float8_regr_accum	float8_covar_samp		0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2829	float8_regr_accum	float8_corr				0	1022	0	"{0,0,0,0,0,0}" ));
  
  /* boolean-and and boolean-or */
! DATA(insert ( 2517	booland_statefunc	-			58	16		0	_null_ ));
! DATA(insert ( 2518	boolor_statefunc	-			59	16		0	_null_ ));
! DATA(insert ( 2519	booland_statefunc	-			58	16		0	_null_ ));
  
  /* bitwise integer */
! DATA(insert ( 2236 int2and		  -					0	21		0	_null_ ));
! DATA(insert ( 2237 int2or		  -					0	21		0	_null_ ));
! DATA(insert ( 2238 int4and		  -					0	23		0	_null_ ));
! DATA(insert ( 2239 int4or		  -					0	23		0	_null_ ));
! DATA(insert ( 2240 int8and		  -					0	20		0	_null_ ));
! DATA(insert ( 2241 int8or		  -					0	20		0	_null_ ));
! DATA(insert ( 2242 bitand		  -					0	1560	0	_null_ ));
! DATA(insert ( 2243 bitor		  -					0	1560	0	_null_ ));
  
  /* xml */
! DATA(insert ( 2901 xmlconcat2	  -					0	142		0	_null_ ));
  
  /* array */
! DATA(insert ( 2335	array_agg_transfn	array_agg_finalfn		0	2281	0	_null_ ));
  
  /* text */
! DATA(insert ( 3538	string_agg_transfn	string_agg_finalfn		0	2281	0	_null_ ));
  
  /* bytea */
! DATA(insert ( 3545	bytea_string_agg_transfn	bytea_string_agg_finalfn		0	2281	0	_null_ ));
  
  /* json */
! DATA(insert ( 3175	json_agg_transfn	json_agg_finalfn		0	2281	0	_null_ ));
  
  /*
   * prototypes for functions in pg_aggregate.c
***************
*** 246,251 **** extern Oid AggregateCreate(const char *aggName,
--- 248,254 ----
  				List *aggfinalfnName,
  				List *aggsortopName,
  				Oid aggTransType,
+ 				int32 aggTransSpace,
  				const char *agginitval);
  
  #endif   /* PG_AGGREGATE_H */
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 2381,2407 **** DATA(insert OID = 2513 (  float8_stddev_pop PGNSP PGUID 12 1 0 0 0 f f f f t f i
  DESCR("aggregate final function");
  DATA(insert OID = 1832 (  float8_stddev_samp	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 701 "1022" _null_ _null_ _null_ _null_ float8_stddev_samp _null_ _null_ _null_ ));
  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_ ));
  DESCR("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_ ));
  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_ ));
  DESCR("aggregate 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_ ));
  DESCR("aggregate final function");
! DATA(insert OID = 2596 (  numeric_stddev_pop PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1700 "1231" _null_ _null_ _null_ _null_	numeric_stddev_pop _null_ _null_ _null_ ));
  DESCR("aggregate final function");
! DATA(insert OID = 1839 (  numeric_stddev_samp	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1700 "1231" _null_ _null_ _null_ _null_ numeric_stddev_samp _null_ _null_ _null_ ));
  DESCR("aggregate final function");
  DATA(insert OID = 1840 (  int2_sum		   PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 20 "20 21" _null_ _null_ _null_ _null_ int2_sum _null_ _null_ _null_ ));
  DESCR("aggregate transition function");
--- 2381,2409 ----
  DESCR("aggregate final function");
  DATA(insert OID = 1832 (  float8_stddev_samp	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 701 "1022" _null_ _null_ _null_ _null_ float8_stddev_samp _null_ _null_ _null_ ));
  DESCR("aggregate final function");
! DATA(insert OID = 1833 (  numeric_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_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 2 0 2281 "2281 1700" _null_ _null_ _null_ _null_ numeric_avg_accum _null_ _null_ _null_ ));
  DESCR("aggregate transition function");
! DATA(insert OID = 1834 (  int2_accum	   PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 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 f f i 2 0 2281 "2281 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 f f i 2 0 2281 "2281 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 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 f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_avg _null_ _null_ _null_ ));
  DESCR("aggregate final function");
! DATA(insert OID = 3179 (  numeric_sum	   PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_sum _null_ _null_ _null_ ));
  DESCR("aggregate final function");
! DATA(insert OID = 2514 (  numeric_var_pop  PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _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 f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_var_samp _null_ _null_ _null_ ));
  DESCR("aggregate final function");
! DATA(insert OID = 2596 (  numeric_stddev_pop PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_	numeric_stddev_pop _null_ _null_ _null_ ));
! DESCR("aggregate final function");
! DATA(insert OID = 1839 (  numeric_stddev_samp	PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_stddev_samp _null_ _null_ _null_ ));
  DESCR("aggregate final function");
  DATA(insert OID = 1840 (  int2_sum		   PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 20 "20 21" _null_ _null_ _null_ _null_ int2_sum _null_ _null_ _null_ ));
  DESCR("aggregate transition function");
*** a/src/include/commands/defrem.h
--- b/src/include/commands/defrem.h
***************
*** 122,127 **** extern Datum transformGenericOptions(Oid catalogId,
--- 122,128 ----
  extern char *defGetString(DefElem *def);
  extern double defGetNumeric(DefElem *def);
  extern bool defGetBoolean(DefElem *def);
+ extern int32 defGetInt32(DefElem *def);
  extern int64 defGetInt64(DefElem *def);
  extern List *defGetQualifiedName(DefElem *def);
  extern TypeName *defGetTypeName(DefElem *def);
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 981,986 **** extern Datum int4_accum(PG_FUNCTION_ARGS);
--- 981,987 ----
  extern Datum int8_accum(PG_FUNCTION_ARGS);
  extern Datum int8_avg_accum(PG_FUNCTION_ARGS);
  extern Datum numeric_avg(PG_FUNCTION_ARGS);
+ extern Datum numeric_sum(PG_FUNCTION_ARGS);
  extern Datum numeric_var_pop(PG_FUNCTION_ARGS);
  extern Datum numeric_var_samp(PG_FUNCTION_ARGS);
  extern Datum numeric_stddev_pop(PG_FUNCTION_ARGS);
#19Josh Berkus
josh@agliodbs.com
In reply to: Hadi Moshayedi (#1)
Re: Improving avg performance for numeric

On 07/07/2013 09:14 PM, Hadi Moshayedi wrote:

I am attaching the updated the patch, which also fixes a bug which
caused one of the regression tests failed.

I'll subscribe this patch to the commitfest in the next hour.

Can you please review the patch?

I'm afraid that, since this patch wasn't included anywhere near the
first week of the CommitFest, I can't possibly include it in the June
commitfest now. Accordingly, I have moved it to the September
commitfest. Hopefully someone can look at it before then.

Sorry for missing this in my "patch sweep" at the beginning of the CF.
Searching email for patches is, at best, inexact.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#20Pavel Stehule
pavel.stehule@gmail.com
In reply to: Josh Berkus (#19)
Re: Improving avg performance for numeric

Hello

2013/7/8 Josh Berkus <josh@agliodbs.com>:

On 07/07/2013 09:14 PM, Hadi Moshayedi wrote:

I am attaching the updated the patch, which also fixes a bug which
caused one of the regression tests failed.

I'll subscribe this patch to the commitfest in the next hour.

Can you please review the patch?

I'm afraid that, since this patch wasn't included anywhere near the
first week of the CommitFest, I can't possibly include it in the June
commitfest now. Accordingly, I have moved it to the September
commitfest. Hopefully someone can look at it before then.

Sorry for missing this in my "patch sweep" at the beginning of the CF.
Searching email for patches is, at best, inexact.

sure, it should be in September CF. It is relative simple patch
without global impacts. But I like it, it increase speed for
sum(numeric) about 25% and avg(numeric) about 50%

Regards

Pavel

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#21Josh Berkus
josh@agliodbs.com
In reply to: Hadi Moshayedi (#1)
Re: Improving avg performance for numeric

Pavel,

sure, it should be in September CF. It is relative simple patch
without global impacts. But I like it, it increase speed for
sum(numeric) about 25% and avg(numeric) about 50%

Do you think you could give this a review after CF1 ends, but before
September? I hate to make Hadi wait just because I didn't see his patch.

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#22Pavel Stehule
pavel.stehule@gmail.com
In reply to: Josh Berkus (#21)
Re: Improving avg performance for numeric

2013/7/8 Josh Berkus <josh@agliodbs.com>:

Pavel,

sure, it should be in September CF. It is relative simple patch
without global impacts. But I like it, it increase speed for
sum(numeric) about 25% and avg(numeric) about 50%

Do you think you could give this a review after CF1 ends, but before
September? I hate to make Hadi wait just because I didn't see his patch.

yes, I can.

Regards

Pavel

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#23Pavel Stehule
pavel.stehule@gmail.com
In reply to: Hadi Moshayedi (#1)
1 attachment(s)
Re: Improving avg performance for numeric

Hello

here is a rebased patch. Hadi, please, can verify this version?

Regards

Pavel

p.s. Performance tests

postgres=# create table foo(a int, b float, c double precision, d numeric,
gr int);
CREATE TABLE
postgres=#
postgres=# insert into foo select 1, 2.0, 3.0, 3.14, random()*10000 from
generate_series(1,10000000);

postgres=# \d foo
Table "public.foo"
Column | Type | Modifiers
--------+------------------+-----------
a | integer |
b | double precision |
c | double precision |
d | numeric |
gr | integer |

set work_mem to '2MB';

postgres=# show debug_assertions;
debug_assertions
------------------
off
(1 row)

postgres=# explain (analyze, timing off) select sum(a) from foo;
QUERY
PLAN
---------------------------------------------------------------------------------------------------
Aggregate (cost=208332.23..208332.24 rows=1 width=4) (actual rows=1
loops=1)
-> Seq Scan on foo (cost=0.00..183332.58 rows=9999858 width=4) (actual
rows=10000000 loops=1)
Total runtime: 1210.321 ms (1195.117 ms) -- patched (original)
(3 rows)

Time: 1210.709 ms
postgres=# explain (analyze, timing off) select sum(a) from foo group by gr;
QUERY
PLAN
---------------------------------------------------------------------------------------------------
HashAggregate (cost=233331.87..233431.71 rows=9984 width=8) (actual
rows=10001 loops=1)
-> Seq Scan on foo (cost=0.00..183332.58 rows=9999858 width=8) (actual
rows=10000000 loops=1)
Total runtime: 2923.987 ms (2952.292 ms)
(3 rows)

Time: 2924.384 ms

postgres=# explain (analyze, timing off) select avg(a) from foo;
QUERY
PLAN
---------------------------------------------------------------------------------------------------
Aggregate (cost=208332.23..208332.24 rows=1 width=4) (actual rows=1
loops=1)
-> Seq Scan on foo (cost=0.00..183332.58 rows=9999858 width=4) (actual
rows=10000000 loops=1)
Total runtime: 1331.627 ms (1312.140 ms)
(3 rows)

postgres=# explain (analyze, timing off) select avg(a) from foo group by gr;
QUERY
PLAN
---------------------------------------------------------------------------------------------------
HashAggregate (cost=233331.87..233456.67 rows=9984 width=8) (actual
rows=10001 loops=1)
-> Seq Scan on foo (cost=0.00..183332.58 rows=9999858 width=8) (actual
rows=10000000 loops=1)
Total runtime: 3139.296 ms (3079.479 ms)
(3 rows)

postgres=# explain (analyze, timing off) select sum(b) from foo;
QUERY
PLAN
---------------------------------------------------------------------------------------------------
Aggregate (cost=208332.23..208332.24 rows=1 width=8) (actual rows=1
loops=1)
-> Seq Scan on foo (cost=0.00..183332.58 rows=9999858 width=8) (actual
rows=10000000 loops=1)
Total runtime: 1327.841 ms (1339.214 ms)
(3 rows)

postgres=# explain (analyze, timing off) select sum(b) from foo group by gr;
QUERY
PLAN
----------------------------------------------------------------------------------------------------
HashAggregate (cost=233331.87..233431.71 rows=9984 width=12) (actual
rows=10001 loops=1)
-> Seq Scan on foo (cost=0.00..183332.58 rows=9999858 width=12)
(actual rows=10000000 loops=1)
Total runtime: 3047.893 ms (3095.591 ms)
(3 rows)

postgres=# explain (analyze, timing off) select avg(b) from foo;
QUERY
PLAN
---------------------------------------------------------------------------------------------------
Aggregate (cost=208332.23..208332.24 rows=1 width=8) (actual rows=1
loops=1)
-> Seq Scan on foo (cost=0.00..183332.58 rows=9999858 width=8) (actual
rows=10000000 loops=1)
Total runtime: 1454.665 ms (1471.413 ms)
(3 rows)

postgres=# explain (analyze, timing off) select avg(b) from foo group by gr;
QUERY
PLAN
----------------------------------------------------------------------------------------------------
HashAggregate (cost=233331.87..233456.67 rows=9984 width=12) (actual
rows=10001 loops=1)
-> Seq Scan on foo (cost=0.00..183332.58 rows=9999858 width=12)
(actual rows=10000000 loops=1)
Total runtime: 3282.838 ms (3187.157 ms)
(3 rows)

postgres=# explain (analyze, timing off) select sum(c) from foo;
QUERY
PLAN
---------------------------------------------------------------------------------------------------
Aggregate (cost=208332.23..208332.24 rows=1 width=8) (actual rows=1
loops=1)
-> Seq Scan on foo (cost=0.00..183332.58 rows=9999858 width=8) (actual
rows=10000000 loops=1)
Total runtime: 1348.555 ms (1364.585 ms)
(3 rows)

postgres=# explain (analyze, timing off) select sum(c) from foo group by gr;
QUERY
PLAN
----------------------------------------------------------------------------------------------------
HashAggregate (cost=233331.87..233431.71 rows=9984 width=12) (actual
rows=10001 loops=1)
-> Seq Scan on foo (cost=0.00..183332.58 rows=9999858 width=12)
(actual rows=10000000 loops=1)
Total runtime: 3028.663 ms (3069.710 ms)
(3 rows)

postgres=# explain (analyze, timing off) select avg(c) from foo;
QUERY
PLAN
---------------------------------------------------------------------------------------------------
Aggregate (cost=208332.23..208332.24 rows=1 width=8) (actual rows=1
loops=1)
-> Seq Scan on foo (cost=0.00..183332.58 rows=9999858 width=8) (actual
rows=10000000 loops=1)
Total runtime: 1488.980 ms (1463.813 ms)
(3 rows)

postgres=# explain (analyze, timing off) select avg(c) from foo group by gr;
QUERY
PLAN
----------------------------------------------------------------------------------------------------
HashAggregate (cost=233331.87..233456.67 rows=9984 width=12) (actual
rows=10001 loops=1)
-> Seq Scan on foo (cost=0.00..183332.58 rows=9999858 width=12)
(actual rows=10000000 loops=1)
Total runtime: 3252.972 ms (3149.986 ms)
(3 rows)

postgres=# explain (analyze, timing off) select sum(d) from foo;
QUERY
PLAN
---------------------------------------------------------------------------------------------------
Aggregate (cost=208332.23..208332.24 rows=1 width=7) (actual rows=1
loops=1)
-> Seq Scan on foo (cost=0.00..183332.58 rows=9999858 width=7) (actual
rows=10000000 loops=1)
Total runtime: 2301.769 ms (2784.430 ms)
(3 rows)

postgres=# explain (analyze, timing off) select sum(d) from foo group by gr;
QUERY
PLAN
----------------------------------------------------------------------------------------------------
HashAggregate (cost=233331.87..233456.67 rows=9984 width=11) (actual
rows=10001 loops=1)
-> Seq Scan on foo (cost=0.00..183332.58 rows=9999858 width=11)
(actual rows=10000000 loops=1)
Total runtime: 4189.272 ms (4440.335 ms)
(3 rows)

postgres=# explain (analyze, timing off) select avg(d) from foo;
QUERY
PLAN
---------------------------------------------------------------------------------------------------
Aggregate (cost=208332.23..208332.24 rows=1 width=7) (actual rows=1
loops=1)
-> Seq Scan on foo (cost=0.00..183332.58 rows=9999858 width=7) (actual
rows=10000000 loops=1)
Total runtime: 2308.493 ms (5195.970 ms)
(3 rows)

postgres=# explain (analyze, timing off) select avg(d) from foo group by gr;
QUERY
PLAN
----------------------------------------------------------------------------------------------------
HashAggregate (cost=233331.87..233456.67 rows=9984 width=11) (actual
rows=10001 loops=1)
-> Seq Scan on foo (cost=0.00..183332.58 rows=9999858 width=11)
(actual rows=10000000 loops=1)
Total runtime: 4179.978 ms (6828.398 ms)
(3 rows)

int, float, double 26829 ms (26675 ms) -- 0.5% slower .. statistic error ..
cleaner code
numeric sum 6490 ms (7224 ms) -- 10% faster
numeric avg 6487 ms (12023 ms) -- 46% faster

2013/8/22 Hadi Moshayedi <hadi@moshayedi.net>

Show quoted text

Hello Pavel,

Do you think you could give this a review after CF1 ends, but before
September? I hate to make Hadi wait just because I didn't see his

patch.

yes, I can.

When do you think you will have time to review this patch?

Thanks,
-- Hadi

Attachments:

numeric-optimize-v5.patch.gzapplication/x-gzip; name=numeric-optimize-v5.patch.gzDownload
#24Hadi Moshayedi
hadi@moshayedi.net
In reply to: Pavel Stehule (#23)
Re: Improving avg performance for numeric

Hello,

int, float, double 26829 ms (26675 ms) -- 0.5% slower .. statistic error ..
cleaner code
numeric sum 6490 ms (7224 ms) -- 10% faster
numeric avg 6487 ms (12023 ms) -- 46% faster

I also got very similar results.

On the other hand, initially I was receiving sigsegv's whenever I
wanted to try to run an aggregate function. gdb was telling that this
was happening somewhere in nodeAgg.c at ExecInitAgg. While trying to
find the reason, I had to reboot my computer at some point, after the
reboot the sigsegv's went away. I want to look into this and find the
reason, I think I have missed something here. Any thoughts about why
this would happen?

--Hadi

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#25Pavel Stehule
pavel.stehule@gmail.com
In reply to: Hadi Moshayedi (#24)
Re: Improving avg performance for numeric

2013/8/29 Hadi Moshayedi <hadi@moshayedi.net>

Hello,

int, float, double 26829 ms (26675 ms) -- 0.5% slower .. statistic error

..

cleaner code
numeric sum 6490 ms (7224 ms) -- 10% faster
numeric avg 6487 ms (12023 ms) -- 46% faster

I also got very similar results.

On the other hand, initially I was receiving sigsegv's whenever I
wanted to try to run an aggregate function. gdb was telling that this
was happening somewhere in nodeAgg.c at ExecInitAgg. While trying to
find the reason, I had to reboot my computer at some point, after the
reboot the sigsegv's went away. I want to look into this and find the
reason, I think I have missed something here. Any thoughts about why
this would happen?

I found a few bugs, that I fixed. There was a issue with empty sets. Other
issues I didn't find.

Regards

Pavel

Show quoted text

--Hadi

#26Peter Eisentraut
peter_e@gmx.net
In reply to: Pavel Stehule (#18)
Re: Improving avg performance for numeric

On 7/8/13 10:05 AM, Pavel Stehule wrote:

I am testing your code, and It increase speed of sum about 24% faster
then original implementation.

This patch needs to be rebased (and/or the later version registered in
the commit fest).

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#27Pavel Stehule
pavel.stehule@gmail.com
In reply to: Peter Eisentraut (#26)
Re: Improving avg performance for numeric

2013/9/4 Peter Eisentraut <peter_e@gmx.net>

On 7/8/13 10:05 AM, Pavel Stehule wrote:

I am testing your code, and It increase speed of sum about 24% faster
then original implementation.

This patch needs to be rebased (and/or the later version registered in
the commit fest).

I updated a commit fest info

Regards

Pavel

#28Peter Eisentraut
peter_e@gmx.net
In reply to: Pavel Stehule (#27)
Re: Improving avg performance for numeric

On 9/4/13 2:26 PM, Pavel Stehule wrote:

2013/9/4 Peter Eisentraut <peter_e@gmx.net <mailto:peter_e@gmx.net>>

On 7/8/13 10:05 AM, Pavel Stehule wrote:

I am testing your code, and It increase speed of sum about 24% faster
then original implementation.

This patch needs to be rebased (and/or the later version registered in
the commit fest).

I updated a commit fest info

The new patch also needs to be rebased.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#29Pavel Stehule
pavel.stehule@gmail.com
In reply to: Peter Eisentraut (#28)
1 attachment(s)
Re: Improving avg performance for numeric

2013/9/4 Peter Eisentraut <peter_e@gmx.net>

Show quoted text

On 9/4/13 2:26 PM, Pavel Stehule wrote:

2013/9/4 Peter Eisentraut <peter_e@gmx.net <mailto:peter_e@gmx.net>>

On 7/8/13 10:05 AM, Pavel Stehule wrote:

I am testing your code, and It increase speed of sum about 24%

faster

then original implementation.

This patch needs to be rebased (and/or the later version registered

in

the commit fest).

I updated a commit fest info

The new patch also needs to be rebased.

rebased

Attachments:

numeric-optimize-v6.patchapplication/octet-stream; name=numeric-optimize-v6.patchDownload
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 373,378 ****
--- 373,384 ----
        <entry>Data type of the aggregate function's internal transition (state) data</entry>
       </row>
       <row>
+       <entry><structfield>aggtransspace</structfield></entry>
+       <entry><type>int4</type></entry>
+       <entry></entry>
+       <entry>Approximation for the average size of the aggregate function's internal transition (state) data</entry>
+      </row>
+      <row>
        <entry><structfield>agginitval</structfield></entry>
        <entry><type>text</type></entry>
        <entry></entry>
*** a/doc/src/sgml/ref/create_aggregate.sgml
--- b/doc/src/sgml/ref/create_aggregate.sgml
***************
*** 24,29 **** PostgreSQL documentation
--- 24,30 ----
  CREATE AGGREGATE <replaceable class="parameter">name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">arg_name</replaceable> ] <replaceable class="parameter">arg_data_type</replaceable> [ , ... ] ) (
      SFUNC = <replaceable class="PARAMETER">sfunc</replaceable>,
      STYPE = <replaceable class="PARAMETER">state_data_type</replaceable>
+     [ , SSPACE = <replaceable class="PARAMETER">state_data_size</replaceable> ]
      [ , FINALFUNC = <replaceable class="PARAMETER">ffunc</replaceable> ]
      [ , INITCOND = <replaceable class="PARAMETER">initial_condition</replaceable> ]
      [ , SORTOP = <replaceable class="PARAMETER">sort_operator</replaceable> ]
***************
*** 35,40 **** CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> (
--- 36,42 ----
      BASETYPE = <replaceable class="PARAMETER">base_type</replaceable>,
      SFUNC = <replaceable class="PARAMETER">sfunc</replaceable>,
      STYPE = <replaceable class="PARAMETER">state_data_type</replaceable>
+     [ , SSPACE = <replaceable class="PARAMETER">state_data_size</replaceable> ]
      [ , FINALFUNC = <replaceable class="PARAMETER">ffunc</replaceable> ]
      [ , INITCOND = <replaceable class="PARAMETER">initial_condition</replaceable> ]
      [ , SORTOP = <replaceable class="PARAMETER">sort_operator</replaceable> ]
***************
*** 265,270 **** SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
--- 267,284 ----
     </varlistentry>
  
     <varlistentry>
+     <term><replaceable class="PARAMETER">state_data_size</replaceable></term>
+     <listitem>
+      <para>
+       Approximate average size (in bytes) of aggregate's state value. 
+       Planner uses this value to approximate the memory required for 
+       the aggregation. If this value is not provided, a default value is 
+       used based on state_data_type.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
      <term><replaceable class="PARAMETER">ffunc</replaceable></term>
      <listitem>
       <para>
*** a/src/backend/catalog/pg_aggregate.c
--- b/src/backend/catalog/pg_aggregate.c
***************
*** 55,60 **** AggregateCreate(const char *aggName,
--- 55,61 ----
  				List *aggfinalfnName,
  				List *aggsortopName,
  				Oid aggTransType,
+ 				int32 aggTransSpace,
  				const char *agginitval)
  {
  	Relation	aggdesc;
***************
*** 273,278 **** AggregateCreate(const char *aggName,
--- 274,280 ----
  	values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
  	values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop);
  	values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
+ 	values[Anum_pg_aggregate_aggtransspace - 1] = Int32GetDatum(aggTransSpace);
  	if (agginitval)
  		values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval);
  	else
*** a/src/backend/commands/aggregatecmds.c
--- b/src/backend/commands/aggregatecmds.c
***************
*** 68,73 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
--- 68,74 ----
  	ArrayType  *parameterNames;
  	List	   *parameterDefaults;
  	Oid			transTypeId;
+ 	int32		transSpace = 0;
  	char		transTypeType;
  	ListCell   *pl;
  
***************
*** 102,107 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
--- 103,110 ----
  			transType = defGetTypeName(defel);
  		else if (pg_strcasecmp(defel->defname, "stype1") == 0)
  			transType = defGetTypeName(defel);
+ 		else if (pg_strcasecmp(defel->defname, "sspace") == 0)
+ 			transSpace = defGetInt32(defel);
  		else if (pg_strcasecmp(defel->defname, "initcond") == 0)
  			initval = defGetString(defel);
  		else if (pg_strcasecmp(defel->defname, "initcond1") == 0)
***************
*** 248,252 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
--- 251,256 ----
  						   finalfuncName,		/* final function name */
  						   sortoperatorName,	/* sort operator name */
  						   transTypeId, /* transition data type */
+ 						   transSpace,			/* transition space */
  						   initval);	/* initial condition */
  }
*** a/src/backend/commands/define.c
--- b/src/backend/commands/define.c
***************
*** 165,170 **** defGetBoolean(DefElem *def)
--- 165,194 ----
  }
  
  /*
+  * Extract an int32 value from a DefElem.
+  */
+ int32
+ defGetInt32(DefElem *def)
+ {
+ 	if (def->arg == NULL)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_SYNTAX_ERROR),
+ 				 errmsg("%s requires an integer value",
+ 						def->defname)));
+ 	switch (nodeTag(def->arg))
+ 	{
+ 		case T_Integer:
+ 			return (int32) intVal(def->arg);
+ 		default:
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 					 errmsg("%s requires an integer value",
+ 							def->defname)));
+ 	}
+ 	return 0;					/* keep compiler quiet */
+ }
+ 
+ /*
   * Extract an int64 value from a DefElem.
   */
  int64
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
***************
*** 461,466 **** count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
--- 461,467 ----
  		Oid			aggtransfn;
  		Oid			aggfinalfn;
  		Oid			aggtranstype;
+ 		int32 		aggtransspace;
  		QualCost	argcosts;
  		Oid		   *inputTypes;
  		int			numArguments;
***************
*** 478,483 **** count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
--- 479,485 ----
  		aggtransfn = aggform->aggtransfn;
  		aggfinalfn = aggform->aggfinalfn;
  		aggtranstype = aggform->aggtranstype;
+ 		aggtransspace = aggform->aggtransspace;
  		ReleaseSysCache(aggTuple);
  
  		/* count it */
***************
*** 533,545 **** count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
  			pfree(declaredArgTypes);
  		}
  
  		/*
  		 * If the transition type is pass-by-value then it doesn't add
  		 * anything to the required size of the hashtable.	If it is
  		 * pass-by-reference then we have to add the estimated size of the
  		 * value itself, plus palloc overhead.
  		 */
! 		if (!get_typbyval(aggtranstype))
  		{
  			int32		aggtranstypmod;
  			int32		avgwidth;
--- 535,555 ----
  			pfree(declaredArgTypes);
  		}
  
+ 		/* 
+ 		 * If approximate average space used by aggregate transition value is
+ 		 * specified in pg_aggregate, then use it for transitionSpace.
+ 		 */
+ 		if (aggtransspace > 0)
+ 		{
+ 			costs->transitionSpace += aggtransspace;
+ 		}
  		/*
  		 * If the transition type is pass-by-value then it doesn't add
  		 * anything to the required size of the hashtable.	If it is
  		 * pass-by-reference then we have to add the estimated size of the
  		 * value itself, plus palloc overhead.
  		 */
! 		else if (!get_typbyval(aggtranstype))
  		{
  			int32		aggtranstypmod;
  			int32		avgwidth;
*** a/src/backend/utils/adt/numeric.c
--- b/src/backend/utils/adt/numeric.c
***************
*** 2464,2571 **** numeric_float4(PG_FUNCTION_ARGS)
   *
   * Aggregate functions
   *
!  * The transition datatype for all these aggregates is a 3-element array
!  * of Numeric, holding the values N, sum(X), sum(X*X) in that order.
!  *
!  * We represent N as a numeric mainly to avoid having to build a special
!  * datatype; it's unlikely it'd overflow an int4, but ...
   *
   * ----------------------------------------------------------------------
   */
  
! static ArrayType *
! do_numeric_accum(ArrayType *transarray, Numeric newval)
  {
! 	Datum	   *transdatums;
! 	int			ndatums;
! 	Datum		N,
! 				sumX,
! 				sumX2;
! 	ArrayType  *result;
! 
! 	/* We assume the input is array of numeric */
! 	deconstruct_array(transarray,
! 					  NUMERICOID, -1, false, 'i',
! 					  &transdatums, NULL, &ndatums);
! 	if (ndatums != 3)
! 		elog(ERROR, "expected 3-element numeric array");
! 	N = transdatums[0];
! 	sumX = transdatums[1];
! 	sumX2 = transdatums[2];
! 
! 	N = DirectFunctionCall1(numeric_inc, N);
! 	sumX = DirectFunctionCall2(numeric_add, sumX,
! 							   NumericGetDatum(newval));
! 	sumX2 = DirectFunctionCall2(numeric_add, sumX2,
! 								DirectFunctionCall2(numeric_mul,
! 													NumericGetDatum(newval),
! 													NumericGetDatum(newval)));
! 
! 	transdatums[0] = N;
! 	transdatums[1] = sumX;
! 	transdatums[2] = sumX2;
! 
! 	result = construct_array(transdatums, 3,
! 							 NUMERICOID, -1, false, 'i');
  
! 	return result;
  }
  
! /*
!  * Improve avg performance by not caclulating sum(X*X).
!  */
! static ArrayType *
! do_numeric_avg_accum(ArrayType *transarray, 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));
! 
! 	transdatums[0] = N;
! 	transdatums[1] = sumX;
! 
! 	result = construct_array(transdatums, 2,
! 							 NUMERICOID, -1, false, 'i');
  
! 	return result;
  }
  
  Datum
  numeric_accum(PG_FUNCTION_ARGS)
  {
! 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
! 	Numeric		newval = PG_GETARG_NUMERIC(1);
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
  }
  
  /*
   * Optimized case for average of numeric.
   */
  Datum
  numeric_avg_accum(PG_FUNCTION_ARGS)
  {
! 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
! 	Numeric		newval = PG_GETARG_NUMERIC(1);
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval));
  }
  
  /*
   * Integer data types all use Numeric accumulators to share code and
   * avoid risk of overflow.	For int2 and int4 inputs, Numeric accumulation
--- 2464,2620 ----
   *
   * Aggregate functions
   *
!  * The transition datatype for all these aggregates is a pointer to 
!  * a struct NumericAggState allocated in the aggregate context.
   *
   * ----------------------------------------------------------------------
   */
  
! typedef struct NumericAggState
! {
! 	bool 			first;
! 	bool 			isNaN;
! 	uint64			N;
! 	NumericVar 		sumX;
! 	NumericVar 		sumX2;
! 	bool 			calcSumX2;
! 	MemoryContext 	agg_context;
! } NumericAggState;
! 
! 
! static NumericAggState *
! makeNumericAggState(FunctionCallInfo fcinfo, bool calcSumX2)
  {
! 	NumericAggState 	*state;
! 	MemoryContext 		 agg_context;
! 	MemoryContext 		 old_context;
  
! 	if (!AggCheckCallContext(fcinfo, &agg_context))
! 	{
! 		elog(ERROR, "this is called in non-aggregate context");
! 	}
! 
! 	old_context = MemoryContextSwitchTo(agg_context);
! 
! 	state = palloc0(sizeof(NumericAggState));
! 	state->first = true;
! 	state->calcSumX2 = calcSumX2;
! 	state->agg_context = agg_context;
! 
! 	MemoryContextSwitchTo(old_context);
! 
! 	return state;
  }
  
! 
! static void
! do_numeric_accum(NumericAggState *state, Numeric newval)
  {
! 	NumericVar 		X;
! 	NumericVar 		X2;
! 	MemoryContext 	old_context;
! 	bool 			first;
! 	
! 	first = state->first;
! 	state->first = false;
! 	state->N++;
! 
! 	if (state->isNaN || NUMERIC_IS_NAN(newval))
! 	{
! 		state->isNaN = true;
! 		return;
! 	}
  
! 	init_var_from_num(newval, &X);
! 
! 	if (state->calcSumX2)
! 	{
! 		init_var(&X2);
! 		mul_var(&X, &X, &X2, X.dscale * 2);
! 	}
! 
! 	old_context = MemoryContextSwitchTo(state->agg_context);
! 
! 	if (!first)
! 	{
! 		NumericVar preSumX;
! 
! 		memcpy(&preSumX, &(state->sumX), sizeof(NumericVar));
! 		init_var(&(state->sumX));
! 		add_var(&X, &preSumX, &(state->sumX));
! 		free_var(&preSumX);
! 
! 		if (state->calcSumX2)
! 		{
! 			NumericVar preSumX2;
! 
! 			memcpy(&preSumX2, &(state->sumX2), sizeof(NumericVar));
! 			init_var(&(state->sumX2));
! 			add_var(&X2, &preSumX2, &(state->sumX2));
! 			free_var(&preSumX2);
! 		}
! 	}
! 	else
! 	{
! 		set_var_from_var(&X, &(state->sumX));
! 
! 		if (state->calcSumX2)
! 			set_var_from_var(&X2, &(state->sumX2));
! 	}
! 
! 	MemoryContextSwitchTo(old_context);
  }
  
+ 
  Datum
  numeric_accum(PG_FUNCTION_ARGS)
  {
! 	NumericAggState *state;
! 
! 	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
  
! 	if (!PG_ARGISNULL(1))
! 	{
! 		/* On the first time through, create the state variable. */
! 		if (state == NULL)
! 			state = makeNumericAggState(fcinfo, true);
! 		
! 		do_numeric_accum(state, PG_GETARG_NUMERIC(1));
! 	}
! 
! 	if (state == NULL)
! 		PG_RETURN_NULL();
! 	else
! 		PG_RETURN_POINTER(state);
  }
  
+ 
  /*
   * Optimized case for average of numeric.
   */
  Datum
  numeric_avg_accum(PG_FUNCTION_ARGS)
  {
! 	NumericAggState *state;
  
! 	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
! 
! 	if (!PG_ARGISNULL(1))
! 	{
! 		/* On the first time through, create the state variable. */
! 		if (state == NULL)
! 			state = makeNumericAggState(fcinfo, false);
! 		
! 		do_numeric_accum(state, PG_GETARG_NUMERIC(1));
! 	}
! 
! 	if (state == NULL)
! 		PG_RETURN_NULL();
! 	else
! 		PG_RETURN_POINTER(state);
  }
  
+ 
  /*
   * Integer data types all use Numeric accumulators to share code and
   * avoid risk of overflow.	For int2 and int4 inputs, Numeric accumulation
***************
*** 2578,2664 **** numeric_avg_accum(PG_FUNCTION_ARGS)
  Datum
  int2_accum(PG_FUNCTION_ARGS)
  {
! 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
! 	Datum		newval2 = PG_GETARG_DATUM(1);
! 	Numeric		newval;
  
! 	newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric, newval2));
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
  }
  
  Datum
  int4_accum(PG_FUNCTION_ARGS)
  {
! 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
! 	Datum		newval4 = PG_GETARG_DATUM(1);
! 	Numeric		newval;
  
! 	newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric, newval4));
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
  }
  
  Datum
  int8_accum(PG_FUNCTION_ARGS)
  {
! 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
! 	Datum		newval8 = PG_GETARG_DATUM(1);
! 	Numeric		newval;
  
! 	newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_accum(transarray, newval));
  }
  
  /*
   * Optimized case for average of int8.
   */
  Datum
  int8_avg_accum(PG_FUNCTION_ARGS)
  {
! 	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
! 	Datum		newval8 = PG_GETARG_DATUM(1);
! 	Numeric		newval;
! 
! 	newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
  
! 	PG_RETURN_ARRAYTYPE_P(do_numeric_avg_accum(transarray, newval));
  }
  
  
  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]);
  
! 	/* SQL defines AVG of no values to be NULL */
! 	/* N is zero iff no digits (cf. numeric_uminus) */
! 	if (NUMERIC_NDIGITS(N) == 0)
  		PG_RETURN_NULL();
  
! 	PG_RETURN_DATUM(DirectFunctionCall2(numeric_div,
! 										NumericGetDatum(sumX),
! 										NumericGetDatum(N)));
  }
  
  /*
   * Workhorse routine for the standard deviance and variance
!  * aggregates. 'transarray' is the aggregate's transition
!  * array. 'variance' specifies whether we should calculate the
   * variance or the standard deviation. 'sample' indicates whether the
   * caller is interested in the sample or the population
   * variance/stddev.
--- 2627,2776 ----
  Datum
  int2_accum(PG_FUNCTION_ARGS)
  {
! 	NumericAggState *state;
  
! 	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
  
! 	if (!PG_ARGISNULL(1))
! 	{
! 		Datum		newval2 = PG_GETARG_DATUM(1);
! 		Numeric		newval;
! 
! 		/* On the first time through, create the state variable. */
! 		if (state == NULL)
! 			state = makeNumericAggState(fcinfo, true);
! 		
! 		newval = DatumGetNumeric(DirectFunctionCall1(int2_numeric, newval2));
! 		do_numeric_accum(state, newval);
! 	}
! 
! 	if (state == NULL)
! 		PG_RETURN_NULL();
! 	else
! 		PG_RETURN_POINTER(state);
  }
  
+ 
  Datum
  int4_accum(PG_FUNCTION_ARGS)
  {
! 	NumericAggState *state;
! 
! 	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
  
! 	if (!PG_ARGISNULL(1))
! 	{
! 		Datum		newval4 = PG_GETARG_DATUM(1);
! 		Numeric		newval;
! 
! 		/* On the first time through, create the state variable. */
! 		if (state == NULL)
! 			state = makeNumericAggState(fcinfo, true);
! 		
! 		newval = DatumGetNumeric(DirectFunctionCall1(int4_numeric, newval4));
! 		do_numeric_accum(state, newval);
! 	}
  
! 	if (state == NULL)
! 		PG_RETURN_NULL();
! 	else
! 		PG_RETURN_POINTER(state);
  }
  
+ 
  Datum
  int8_accum(PG_FUNCTION_ARGS)
  {
! 	NumericAggState *state;
  
! 	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
! 
! 	if (!PG_ARGISNULL(1))
! 	{
! 		Datum		newval8 = PG_GETARG_DATUM(1);
! 		Numeric		newval;
! 
! 		/* On the first time through, create the state variable. */
! 		if (state == NULL)
! 			state = makeNumericAggState(fcinfo, true);
! 		
! 		newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
! 		do_numeric_accum(state, newval);
! 	}
  
! 	if (state == NULL)
! 		PG_RETURN_NULL();
! 	else
! 		PG_RETURN_POINTER(state);
  }
  
+ 
  /*
   * Optimized case for average of int8.
   */
  Datum
  int8_avg_accum(PG_FUNCTION_ARGS)
  {
! 	NumericAggState *state;	
! 	state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
  
! 	if (!PG_ARGISNULL(1))
! 	{
! 		Datum		newval8 = PG_GETARG_DATUM(1);
! 		Numeric		newval;
! 
! 		/* On the first time through, create the state variable. */
! 		if (state == NULL)
! 			state = makeNumericAggState(fcinfo, false);
! 		
! 		newval = DatumGetNumeric(DirectFunctionCall1(int8_numeric, newval8));
! 		do_numeric_accum(state, newval);
! 	}
!  
! 	if (state == NULL)
! 		PG_RETURN_NULL();
! 	else
! 		PG_RETURN_POINTER(state);
  }
  
  
  Datum
  numeric_avg(PG_FUNCTION_ARGS)
  {
! 	Datum 		 		N_datum;
! 	Datum 				sumX_datum;
! 	NumericAggState 	*state;
  
! 	if (PG_ARGISNULL(0))
  		PG_RETURN_NULL();
  
! 	state = (NumericAggState *) PG_GETARG_POINTER(0);
! 
! 	N_datum = DirectFunctionCall1(int8_numeric, Int64GetDatum(state->N));
! 	sumX_datum = NumericGetDatum(make_result(&state->sumX));
! 
! 	PG_RETURN_DATUM(DirectFunctionCall2(numeric_div, sumX_datum, N_datum));
  }
  
+ 
+ Datum
+ numeric_sum(PG_FUNCTION_ARGS)
+ {
+ 	NumericAggState 	*state;
+ 
+ 	if (PG_ARGISNULL(0))
+ 		PG_RETURN_NULL();
+  
+ 	state = (NumericAggState *) PG_GETARG_POINTER(0);
+ 
+ 	PG_RETURN_NUMERIC(make_result(&(state->sumX)));
+ }
+ 
+ 
  /*
   * Workhorse routine for the standard deviance and variance
!  * aggregates. 'state' is aggregate's transition state.
!  * 'variance' specifies whether we should calculate the
   * variance or the standard deviation. 'sample' indicates whether the
   * caller is interested in the sample or the population
   * variance/stddev.
***************
*** 2667,2682 **** numeric_avg(PG_FUNCTION_ARGS)
   * *is_null is set to true and NULL is returned.
   */
  static Numeric
! numeric_stddev_internal(ArrayType *transarray,
  						bool variance, bool sample,
  						bool *is_null)
  {
! 	Datum	   *transdatums;
! 	int			ndatums;
! 	Numeric		N,
! 				sumX,
! 				sumX2,
! 				res;
  	NumericVar	vN,
  				vsumX,
  				vsumX2,
--- 2779,2789 ----
   * *is_null is set to true and NULL is returned.
   */
  static Numeric
! numeric_stddev_internal(NumericAggState *state,
  						bool variance, bool sample,
  						bool *is_null)
  {
! 	Numeric		res;
  	NumericVar	vN,
  				vsumX,
  				vsumX2,
***************
*** 2684,2705 **** numeric_stddev_internal(ArrayType *transarray,
  	NumericVar *comp;
  	int			rscale;
  
  	*is_null = false;
  
! 	/* We assume the input is array of numeric */
! 	deconstruct_array(transarray,
! 					  NUMERICOID, -1, false, 'i',
! 					  &transdatums, NULL, &ndatums);
! 	if (ndatums != 3)
! 		elog(ERROR, "expected 3-element numeric array");
! 	N = DatumGetNumeric(transdatums[0]);
! 	sumX = DatumGetNumeric(transdatums[1]);
! 	sumX2 = DatumGetNumeric(transdatums[2]);
! 
! 	if (NUMERIC_IS_NAN(N) || NUMERIC_IS_NAN(sumX) || NUMERIC_IS_NAN(sumX2))
  		return make_result(&const_nan);
  
! 	init_var_from_num(N, &vN);
  
  	/*
  	 * Sample stddev and variance are undefined when N <= 1; population stddev
--- 2791,2814 ----
  	NumericVar *comp;
  	int			rscale;
  
+ 	if (state == NULL)
+ 	{
+ 		*is_null = true;
+ 		return NULL;
+ 	}
+ 
  	*is_null = false;
  
! 	if (state->isNaN)
  		return make_result(&const_nan);
  
! 	init_var(&vN);
! 	init_var(&vsumX);
! 	init_var(&vsumX2);
! 
! 	int8_to_numericvar(state->N, &vN);
! 	set_var_from_var(&(state->sumX), &vsumX);
! 	set_var_from_var(&(state->sumX2), &vsumX2);
  
  	/*
  	 * Sample stddev and variance are undefined when N <= 1; population stddev
***************
*** 2719,2726 **** numeric_stddev_internal(ArrayType *transarray,
  	init_var(&vNminus1);
  	sub_var(&vN, &const_one, &vNminus1);
  
! 	init_var_from_num(sumX, &vsumX);
! 	init_var_from_num(sumX2, &vsumX2);
  
  	/* compute rscale for mul_var calls */
  	rscale = vsumX.dscale * 2;
--- 2828,2835 ----
  	init_var(&vNminus1);
  	sub_var(&vN, &const_one, &vNminus1);
  
! 	set_var_from_var(&(state->sumX), &vsumX);
! 	set_var_from_var(&(state->sumX2), &vsumX2);
  
  	/* compute rscale for mul_var calls */
  	rscale = vsumX.dscale * 2;
***************
*** 2761,2767 **** numeric_var_samp(PG_FUNCTION_ARGS)
  	Numeric		res;
  	bool		is_null;
  
! 	res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
  								  true, true, &is_null);
  
  	if (is_null)
--- 2870,2876 ----
  	Numeric		res;
  	bool		is_null;
  
! 	res = numeric_stddev_internal((NumericAggState *) PG_GETARG_POINTER(0),
  								  true, true, &is_null);
  
  	if (is_null)
***************
*** 2776,2782 **** numeric_stddev_samp(PG_FUNCTION_ARGS)
  	Numeric		res;
  	bool		is_null;
  
! 	res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
  								  false, true, &is_null);
  
  	if (is_null)
--- 2885,2891 ----
  	Numeric		res;
  	bool		is_null;
  
! 	res = numeric_stddev_internal((NumericAggState *) PG_GETARG_POINTER(0),
  								  false, true, &is_null);
  
  	if (is_null)
***************
*** 2791,2797 **** numeric_var_pop(PG_FUNCTION_ARGS)
  	Numeric		res;
  	bool		is_null;
  
! 	res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
  								  true, false, &is_null);
  
  	if (is_null)
--- 2900,2906 ----
  	Numeric		res;
  	bool		is_null;
  
! 	res = numeric_stddev_internal((NumericAggState *) PG_GETARG_POINTER(0),
  								  true, false, &is_null);
  
  	if (is_null)
***************
*** 2806,2812 **** numeric_stddev_pop(PG_FUNCTION_ARGS)
  	Numeric		res;
  	bool		is_null;
  
! 	res = numeric_stddev_internal(PG_GETARG_ARRAYTYPE_P(0),
  								  false, false, &is_null);
  
  	if (is_null)
--- 2915,2921 ----
  	Numeric		res;
  	bool		is_null;
  
! 	res = numeric_stddev_internal((NumericAggState *) PG_GETARG_POINTER(0),
  								  false, false, &is_null);
  
  	if (is_null)
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 11419,11430 **** dumpAgg(Archive *fout, AggInfo *agginfo)
--- 11419,11432 ----
  	int			i_aggfinalfn;
  	int			i_aggsortop;
  	int			i_aggtranstype;
+ 	int			i_aggtransspace;
  	int			i_agginitval;
  	int			i_convertok;
  	const char *aggtransfn;
  	const char *aggfinalfn;
  	const char *aggsortop;
  	const char *aggtranstype;
+ 	const char *aggtransspace;
  	const char *agginitval;
  	bool		convertok;
  
***************
*** 11442,11453 **** dumpAgg(Archive *fout, AggInfo *agginfo)
  	selectSourceSchema(fout, agginfo->aggfn.dobj.namespace->dobj.name);
  
  	/* Get aggregate-specific details */
! 	if (fout->remoteVersion >= 80400)
  	{
  		appendPQExpBuffer(query, "SELECT aggtransfn, "
  						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
  						  "aggsortop::pg_catalog.regoperator, "
! 						  "agginitval, "
  						  "'t'::boolean AS convertok, "
  						  "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, "
  						  "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs "
--- 11444,11469 ----
  	selectSourceSchema(fout, agginfo->aggfn.dobj.namespace->dobj.name);
  
  	/* Get aggregate-specific details */
! 	if (fout->remoteVersion >= 90300)
! 	{
! 		appendPQExpBuffer(query, "SELECT aggtransfn, "
! 						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
! 						  "aggsortop::pg_catalog.regoperator, "
! 						  "aggtransspace, agginitval, "
! 						  "'t'::boolean AS convertok, "
! 						  "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, "
! 						  "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs "
! 					  "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
! 						  "WHERE a.aggfnoid = p.oid "
! 						  "AND p.oid = '%u'::pg_catalog.oid",
! 						  agginfo->aggfn.dobj.catId.oid);
! 	}
! 	else if (fout->remoteVersion >= 80400)
  	{
  		appendPQExpBuffer(query, "SELECT aggtransfn, "
  						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
  						  "aggsortop::pg_catalog.regoperator, "
! 						  "0 AS aggtransspace, agginitval, "
  						  "'t'::boolean AS convertok, "
  						  "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, "
  						  "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs "
***************
*** 11461,11467 **** dumpAgg(Archive *fout, AggInfo *agginfo)
  		appendPQExpBuffer(query, "SELECT aggtransfn, "
  						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
  						  "aggsortop::pg_catalog.regoperator, "
! 						  "agginitval, "
  						  "'t'::boolean AS convertok "
  						  "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
  						  "WHERE a.aggfnoid = p.oid "
--- 11477,11483 ----
  		appendPQExpBuffer(query, "SELECT aggtransfn, "
  						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
  						  "aggsortop::pg_catalog.regoperator, "
! 						  "0 AS aggtransspace, agginitval, "
  						  "'t'::boolean AS convertok "
  						  "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
  						  "WHERE a.aggfnoid = p.oid "
***************
*** 11473,11479 **** dumpAgg(Archive *fout, AggInfo *agginfo)
  		appendPQExpBuffer(query, "SELECT aggtransfn, "
  						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
  						  "0 AS aggsortop, "
! 						  "agginitval, "
  						  "'t'::boolean AS convertok "
  					  "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
  						  "WHERE a.aggfnoid = p.oid "
--- 11489,11495 ----
  		appendPQExpBuffer(query, "SELECT aggtransfn, "
  						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
  						  "0 AS aggsortop, "
! 						  "0 AS aggtransspace, agginitval, "
  						  "'t'::boolean AS convertok "
  					  "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
  						  "WHERE a.aggfnoid = p.oid "
***************
*** 11485,11491 **** dumpAgg(Archive *fout, AggInfo *agginfo)
  		appendPQExpBuffer(query, "SELECT aggtransfn, aggfinalfn, "
  						  "format_type(aggtranstype, NULL) AS aggtranstype, "
  						  "0 AS aggsortop, "
! 						  "agginitval, "
  						  "'t'::boolean AS convertok "
  						  "FROM pg_aggregate "
  						  "WHERE oid = '%u'::oid",
--- 11501,11507 ----
  		appendPQExpBuffer(query, "SELECT aggtransfn, aggfinalfn, "
  						  "format_type(aggtranstype, NULL) AS aggtranstype, "
  						  "0 AS aggsortop, "
! 						  "0 AS aggtransspace, agginitval, "
  						  "'t'::boolean AS convertok "
  						  "FROM pg_aggregate "
  						  "WHERE oid = '%u'::oid",
***************
*** 11497,11503 **** dumpAgg(Archive *fout, AggInfo *agginfo)
  						  "aggfinalfn, "
  						  "(SELECT typname FROM pg_type WHERE oid = aggtranstype1) AS aggtranstype, "
  						  "0 AS aggsortop, "
! 						  "agginitval1 AS agginitval, "
  						  "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) AS convertok "
  						  "FROM pg_aggregate "
  						  "WHERE oid = '%u'::oid",
--- 11513,11519 ----
  						  "aggfinalfn, "
  						  "(SELECT typname FROM pg_type WHERE oid = aggtranstype1) AS aggtranstype, "
  						  "0 AS aggsortop, "
! 						  "0 AS aggtransspace, agginitval1 AS agginitval, "
  						  "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) AS convertok "
  						  "FROM pg_aggregate "
  						  "WHERE oid = '%u'::oid",
***************
*** 11510,11515 **** dumpAgg(Archive *fout, AggInfo *agginfo)
--- 11526,11532 ----
  	i_aggfinalfn = PQfnumber(res, "aggfinalfn");
  	i_aggsortop = PQfnumber(res, "aggsortop");
  	i_aggtranstype = PQfnumber(res, "aggtranstype");
+ 	i_aggtransspace = PQfnumber(res, "aggtransspace");
  	i_agginitval = PQfnumber(res, "agginitval");
  	i_convertok = PQfnumber(res, "convertok");
  
***************
*** 11517,11522 **** dumpAgg(Archive *fout, AggInfo *agginfo)
--- 11534,11540 ----
  	aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn);
  	aggsortop = PQgetvalue(res, 0, i_aggsortop);
  	aggtranstype = PQgetvalue(res, 0, i_aggtranstype);
+ 	aggtransspace = PQgetvalue(res, 0, i_aggtransspace);
  	agginitval = PQgetvalue(res, 0, i_agginitval);
  	convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't');
  
***************
*** 11570,11575 **** dumpAgg(Archive *fout, AggInfo *agginfo)
--- 11588,11599 ----
  						  fmtId(aggtranstype));
  	}
  
+ 	if (strcmp(aggfinalfn, "0") != 0)
+ 	{
+ 		appendPQExpBuffer(details, ",\n    SSPACE = %s",
+ 						  aggtransspace);
+ 	}
+ 
  	if (!PQgetisnull(res, 0, i_agginitval))
  	{
  		appendPQExpBuffer(details, ",\n    INITCOND = ");
*** a/src/include/catalog/pg_aggregate.h
--- b/src/include/catalog/pg_aggregate.h
***************
*** 44,49 **** CATALOG(pg_aggregate,2600) BKI_WITHOUT_OIDS
--- 44,50 ----
  	regproc		aggfinalfn;
  	Oid			aggsortop;
  	Oid			aggtranstype;
+ 	int32		aggtransspace;
  
  #ifdef CATALOG_VARLEN			/* variable-length fields start here */
  	text		agginitval;
***************
*** 62,74 **** typedef FormData_pg_aggregate *Form_pg_aggregate;
   * ----------------
   */
  
! #define Natts_pg_aggregate				6
  #define Anum_pg_aggregate_aggfnoid		1
  #define Anum_pg_aggregate_aggtransfn	2
  #define Anum_pg_aggregate_aggfinalfn	3
  #define Anum_pg_aggregate_aggsortop		4
  #define Anum_pg_aggregate_aggtranstype	5
! #define Anum_pg_aggregate_agginitval	6
  
  
  /* ----------------
--- 63,76 ----
   * ----------------
   */
  
! #define Natts_pg_aggregate				7
  #define Anum_pg_aggregate_aggfnoid		1
  #define Anum_pg_aggregate_aggtransfn	2
  #define Anum_pg_aggregate_aggfinalfn	3
  #define Anum_pg_aggregate_aggsortop		4
  #define Anum_pg_aggregate_aggtranstype	5
! #define Anum_pg_aggregate_aggtransspace	6
! #define Anum_pg_aggregate_agginitval	7
  
  
  /* ----------------
***************
*** 77,239 **** typedef FormData_pg_aggregate *Form_pg_aggregate;
   */
  
  /* avg */
! DATA(insert ( 2100	int8_avg_accum	numeric_avg		0	1231	"{0,0}" ));
! 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 ( 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}" ));
  
  /* sum */
! DATA(insert ( 2107	int8_sum		-				0	1700	_null_ ));
! DATA(insert ( 2108	int4_sum		-				0	20		_null_ ));
! DATA(insert ( 2109	int2_sum		-				0	20		_null_ ));
! DATA(insert ( 2110	float4pl		-				0	700		_null_ ));
! DATA(insert ( 2111	float8pl		-				0	701		_null_ ));
! DATA(insert ( 2112	cash_pl			-				0	790		_null_ ));
! DATA(insert ( 2113	interval_pl		-				0	1186	_null_ ));
! DATA(insert ( 2114	numeric_add		-				0	1700	_null_ ));
  
  /* max */
! DATA(insert ( 2115	int8larger		-				413		20		_null_ ));
! DATA(insert ( 2116	int4larger		-				521		23		_null_ ));
! DATA(insert ( 2117	int2larger		-				520		21		_null_ ));
! DATA(insert ( 2118	oidlarger		-				610		26		_null_ ));
! DATA(insert ( 2119	float4larger	-				623		700		_null_ ));
! DATA(insert ( 2120	float8larger	-				674		701		_null_ ));
! DATA(insert ( 2121	int4larger		-				563		702		_null_ ));
! DATA(insert ( 2122	date_larger		-				1097	1082	_null_ ));
! DATA(insert ( 2123	time_larger		-				1112	1083	_null_ ));
! DATA(insert ( 2124	timetz_larger	-				1554	1266	_null_ ));
! DATA(insert ( 2125	cashlarger		-				903		790		_null_ ));
! DATA(insert ( 2126	timestamp_larger	-			2064	1114	_null_ ));
! DATA(insert ( 2127	timestamptz_larger	-			1324	1184	_null_ ));
! DATA(insert ( 2128	interval_larger -				1334	1186	_null_ ));
! DATA(insert ( 2129	text_larger		-				666		25		_null_ ));
! DATA(insert ( 2130	numeric_larger	-				1756	1700	_null_ ));
! DATA(insert ( 2050	array_larger	-				1073	2277	_null_ ));
! DATA(insert ( 2244	bpchar_larger	-				1060	1042	_null_ ));
! DATA(insert ( 2797	tidlarger		-				2800	27		_null_ ));
! DATA(insert ( 3526	enum_larger		-				3519	3500	_null_ ));
  
  /* min */
! DATA(insert ( 2131	int8smaller		-				412		20		_null_ ));
! DATA(insert ( 2132	int4smaller		-				97		23		_null_ ));
! DATA(insert ( 2133	int2smaller		-				95		21		_null_ ));
! DATA(insert ( 2134	oidsmaller		-				609		26		_null_ ));
! DATA(insert ( 2135	float4smaller	-				622		700		_null_ ));
! DATA(insert ( 2136	float8smaller	-				672		701		_null_ ));
! DATA(insert ( 2137	int4smaller		-				562		702		_null_ ));
! DATA(insert ( 2138	date_smaller	-				1095	1082	_null_ ));
! DATA(insert ( 2139	time_smaller	-				1110	1083	_null_ ));
! DATA(insert ( 2140	timetz_smaller	-				1552	1266	_null_ ));
! DATA(insert ( 2141	cashsmaller		-				902		790		_null_ ));
! DATA(insert ( 2142	timestamp_smaller	-			2062	1114	_null_ ));
! DATA(insert ( 2143	timestamptz_smaller -			1322	1184	_null_ ));
! DATA(insert ( 2144	interval_smaller	-			1332	1186	_null_ ));
! DATA(insert ( 2145	text_smaller	-				664		25		_null_ ));
! DATA(insert ( 2146	numeric_smaller -				1754	1700	_null_ ));
! DATA(insert ( 2051	array_smaller	-				1072	2277	_null_ ));
! DATA(insert ( 2245	bpchar_smaller	-				1058	1042	_null_ ));
! DATA(insert ( 2798	tidsmaller		-				2799	27		_null_ ));
! DATA(insert ( 3527	enum_smaller	-				3518	3500	_null_ ));
  
  /* count */
! DATA(insert ( 2147	int8inc_any		-				0		20		"0" ));
! DATA(insert ( 2803	int8inc			-				0		20		"0" ));
  
  /* var_pop */
! DATA(insert ( 2718	int8_accum	numeric_var_pop 0	1231	"{0,0,0}" ));
! DATA(insert ( 2719	int4_accum	numeric_var_pop 0	1231	"{0,0,0}" ));
! DATA(insert ( 2720	int2_accum	numeric_var_pop 0	1231	"{0,0,0}" ));
! DATA(insert ( 2721	float4_accum	float8_var_pop 0	1022	"{0,0,0}" ));
! DATA(insert ( 2722	float8_accum	float8_var_pop 0	1022	"{0,0,0}" ));
! DATA(insert ( 2723	numeric_accum  numeric_var_pop 0	1231	"{0,0,0}" ));
  
  /* var_samp */
! DATA(insert ( 2641	int8_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
! DATA(insert ( 2642	int4_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
! DATA(insert ( 2643	int2_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
! DATA(insert ( 2644	float4_accum	float8_var_samp 0	1022	"{0,0,0}" ));
! DATA(insert ( 2645	float8_accum	float8_var_samp 0	1022	"{0,0,0}" ));
! DATA(insert ( 2646	numeric_accum  numeric_var_samp 0	1231	"{0,0,0}" ));
  
  /* variance: historical Postgres syntax for var_samp */
! DATA(insert ( 2148	int8_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
! DATA(insert ( 2149	int4_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
! DATA(insert ( 2150	int2_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
! DATA(insert ( 2151	float4_accum	float8_var_samp 0	1022	"{0,0,0}" ));
! DATA(insert ( 2152	float8_accum	float8_var_samp 0	1022	"{0,0,0}" ));
! DATA(insert ( 2153	numeric_accum  numeric_var_samp 0	1231	"{0,0,0}" ));
  
  /* stddev_pop */
! DATA(insert ( 2724	int8_accum	numeric_stddev_pop		0	1231	"{0,0,0}" ));
! DATA(insert ( 2725	int4_accum	numeric_stddev_pop		0	1231	"{0,0,0}" ));
! DATA(insert ( 2726	int2_accum	numeric_stddev_pop		0	1231	"{0,0,0}" ));
! DATA(insert ( 2727	float4_accum	float8_stddev_pop	0	1022	"{0,0,0}" ));
! DATA(insert ( 2728	float8_accum	float8_stddev_pop	0	1022	"{0,0,0}" ));
! DATA(insert ( 2729	numeric_accum	numeric_stddev_pop	0	1231	"{0,0,0}" ));
  
  /* stddev_samp */
! DATA(insert ( 2712	int8_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
! DATA(insert ( 2713	int4_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
! DATA(insert ( 2714	int2_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
! DATA(insert ( 2715	float4_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
! DATA(insert ( 2716	float8_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
! DATA(insert ( 2717	numeric_accum	numeric_stddev_samp 0	1231	"{0,0,0}" ));
  
  /* stddev: historical Postgres syntax for stddev_samp */
! DATA(insert ( 2154	int8_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
! DATA(insert ( 2155	int4_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
! DATA(insert ( 2156	int2_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
! DATA(insert ( 2157	float4_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
! DATA(insert ( 2158	float8_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
! DATA(insert ( 2159	numeric_accum	numeric_stddev_samp 0	1231	"{0,0,0}" ));
  
  /* SQL2003 binary regression aggregates */
! DATA(insert ( 2818	int8inc_float8_float8		-				0	20		"0" ));
! DATA(insert ( 2819	float8_regr_accum	float8_regr_sxx			0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2820	float8_regr_accum	float8_regr_syy			0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2821	float8_regr_accum	float8_regr_sxy			0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2822	float8_regr_accum	float8_regr_avgx		0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2823	float8_regr_accum	float8_regr_avgy		0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2824	float8_regr_accum	float8_regr_r2			0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2825	float8_regr_accum	float8_regr_slope		0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2826	float8_regr_accum	float8_regr_intercept	0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2827	float8_regr_accum	float8_covar_pop		0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2828	float8_regr_accum	float8_covar_samp		0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2829	float8_regr_accum	float8_corr				0	1022	"{0,0,0,0,0,0}" ));
  
  /* boolean-and and boolean-or */
! DATA(insert ( 2517	booland_statefunc	-			58	16		_null_ ));
! DATA(insert ( 2518	boolor_statefunc	-			59	16		_null_ ));
! DATA(insert ( 2519	booland_statefunc	-			58	16		_null_ ));
  
  /* bitwise integer */
! DATA(insert ( 2236 int2and		  -					0	21		_null_ ));
! DATA(insert ( 2237 int2or		  -					0	21		_null_ ));
! DATA(insert ( 2238 int4and		  -					0	23		_null_ ));
! DATA(insert ( 2239 int4or		  -					0	23		_null_ ));
! DATA(insert ( 2240 int8and		  -					0	20		_null_ ));
! DATA(insert ( 2241 int8or		  -					0	20		_null_ ));
! DATA(insert ( 2242 bitand		  -					0	1560	_null_ ));
! DATA(insert ( 2243 bitor		  -					0	1560	_null_ ));
  
  /* xml */
! DATA(insert ( 2901 xmlconcat2	  -					0	142		_null_ ));
  
  /* array */
! DATA(insert ( 2335	array_agg_transfn	array_agg_finalfn		0	2281	_null_ ));
  
  /* text */
! DATA(insert ( 3538	string_agg_transfn	string_agg_finalfn		0	2281	_null_ ));
  
  /* bytea */
! DATA(insert ( 3545	bytea_string_agg_transfn	bytea_string_agg_finalfn		0	2281	_null_ ));
  
  /* json */
! DATA(insert ( 3175	json_agg_transfn	json_agg_finalfn		0	2281	_null_ ));
  
  /*
   * prototypes for functions in pg_aggregate.c
--- 79,241 ----
   */
  
  /* avg */
! DATA(insert ( 2100	int8_avg_accum		numeric_avg		0	2281	128	_null_ ));
! DATA(insert ( 2101	int4_avg_accum		int8_avg		0	1016	0	"{0,0}" ));
! DATA(insert ( 2102	int2_avg_accum		int8_avg		0	1016	0	"{0,0}" ));
! DATA(insert ( 2103	numeric_avg_accum		numeric_avg		0	2281	128	_null_ ));
! DATA(insert ( 2104	float4_accum		float8_avg		0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2105	float8_accum		float8_avg		0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2106	interval_accum		interval_avg	0	1187	0	"{0 second,0 second}" ));
  
  /* sum */
! DATA(insert ( 2107	int8_avg_accum	numeric_sum		0	2281	128	_null_ ));
! DATA(insert ( 2108	int4_sum		-				0	20		0	_null_ ));
! DATA(insert ( 2109	int2_sum		-				0	20		0	_null_ ));
! DATA(insert ( 2110	float4pl		-				0	700		0	_null_ ));
! DATA(insert ( 2111	float8pl		-				0	701		0	_null_ ));
! DATA(insert ( 2112	cash_pl			-				0	790		0	_null_ ));
! DATA(insert ( 2113	interval_pl		-				0	1186	0	_null_ ));
! DATA(insert ( 2114	numeric_avg_accum	numeric_sum	0	2281	128	_null_ ));
  
  /* max */
! DATA(insert ( 2115	int8larger		-				413		20		0	_null_ ));
! DATA(insert ( 2116	int4larger		-				521		23		0	_null_ ));
! DATA(insert ( 2117	int2larger		-				520		21		0	_null_ ));
! DATA(insert ( 2118	oidlarger		-				610		26		0	_null_ ));
! DATA(insert ( 2119	float4larger	-				623		700		0	_null_ ));
! DATA(insert ( 2120	float8larger	-				674		701		0	_null_ ));
! DATA(insert ( 2121	int4larger		-				563		702		0	_null_ ));
! DATA(insert ( 2122	date_larger		-				1097	1082	0	_null_ ));
! DATA(insert ( 2123	time_larger		-				1112	1083	0	_null_ ));
! DATA(insert ( 2124	timetz_larger	-				1554	1266	0	_null_ ));
! DATA(insert ( 2125	cashlarger		-				903		790		0	_null_ ));
! DATA(insert ( 2126	timestamp_larger	-			2064	1114	0	_null_ ));
! DATA(insert ( 2127	timestamptz_larger	-			1324	1184	0	_null_ ));
! DATA(insert ( 2128	interval_larger -				1334	1186	0	_null_ ));
! DATA(insert ( 2129	text_larger		-				666		25		0	_null_ ));
! DATA(insert ( 2130	numeric_larger	-				1756	1700	0	_null_ ));
! DATA(insert ( 2050	array_larger	-				1073	2277	0	_null_ ));
! DATA(insert ( 2244	bpchar_larger	-				1060	1042	0	_null_ ));
! DATA(insert ( 2797	tidlarger		-				2800	27		0	_null_ ));
! DATA(insert ( 3526	enum_larger		-				3519	3500	0	_null_ ));
  
  /* min */
! DATA(insert ( 2131	int8smaller		-				412		20		0	_null_ ));
! DATA(insert ( 2132	int4smaller		-				97		23		0	_null_ ));
! DATA(insert ( 2133	int2smaller		-				95		21		0	_null_ ));
! DATA(insert ( 2134	oidsmaller		-				609		26		0	_null_ ));
! DATA(insert ( 2135	float4smaller	-				622		700		0	_null_ ));
! DATA(insert ( 2136	float8smaller	-				672		701		0	_null_ ));
! DATA(insert ( 2137	int4smaller		-				562		702		0	_null_ ));
! DATA(insert ( 2138	date_smaller	-				1095	1082	0	_null_ ));
! DATA(insert ( 2139	time_smaller	-				1110	1083	0	_null_ ));
! DATA(insert ( 2140	timetz_smaller	-				1552	1266	0	_null_ ));
! DATA(insert ( 2141	cashsmaller		-				902		790		0	_null_ ));
! DATA(insert ( 2142	timestamp_smaller	-			2062	1114	0	_null_ ));
! DATA(insert ( 2143	timestamptz_smaller -			1322	1184	0	_null_ ));
! DATA(insert ( 2144	interval_smaller	-			1332	1186	0	_null_ ));
! DATA(insert ( 2145	text_smaller	-				664		25		0	_null_ ));
! DATA(insert ( 2146	numeric_smaller -				1754	1700	0	_null_ ));
! DATA(insert ( 2051	array_smaller	-				1072	2277	0	_null_ ));
! DATA(insert ( 2245	bpchar_smaller	-				1058	1042	0	_null_ ));
! DATA(insert ( 2798	tidsmaller		-				2799	27		0	_null_ ));
! DATA(insert ( 3527	enum_smaller	-				3518	3500	0	_null_ ));
  
  /* count */
! DATA(insert ( 2147	int8inc_any		-				0		20		0	"0" ));
! DATA(insert ( 2803	int8inc			-				0		20		0	"0" ));
  
  /* var_pop */
! DATA(insert ( 2718	int8_accum	numeric_var_pop 0	2281	128	_null_ ));
! DATA(insert ( 2719	int4_accum	numeric_var_pop 0	2281	128	_null_ ));
! DATA(insert ( 2720	int2_accum	numeric_var_pop 0	2281	128	_null_ ));
! DATA(insert ( 2721	float4_accum	float8_var_pop 0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2722	float8_accum	float8_var_pop 0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2723	numeric_accum  numeric_var_pop 0	2281	128	_null_ ));
  
  /* var_samp */
! DATA(insert ( 2641	int8_accum	numeric_var_samp	0	2281	128	_null_ ));
! DATA(insert ( 2642	int4_accum	numeric_var_samp	0	2281	128	_null_ ));
! DATA(insert ( 2643	int2_accum	numeric_var_samp	0	2281	128	_null_ ));
! DATA(insert ( 2644	float4_accum	float8_var_samp 0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2645	float8_accum	float8_var_samp 0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2646	numeric_accum  numeric_var_samp 0	2281	128	_null_ ));
  
  /* variance: historical Postgres syntax for var_samp */
! DATA(insert ( 2148	int8_accum	numeric_var_samp	0	2281	128	_null_ ));
! DATA(insert ( 2149	int4_accum	numeric_var_samp	0	2281	128	_null_ ));
! DATA(insert ( 2150	int2_accum	numeric_var_samp	0	2281	128	_null_ ));
! DATA(insert ( 2151	float4_accum	float8_var_samp 0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2152	float8_accum	float8_var_samp 0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2153	numeric_accum  numeric_var_samp 0	2281	128	_null_ ));
  
  /* stddev_pop */
! DATA(insert ( 2724	int8_accum	numeric_stddev_pop		0	2281	128	_null_ ));
! DATA(insert ( 2725	int4_accum	numeric_stddev_pop		0	2281	128	_null_ ));
! DATA(insert ( 2726	int2_accum	numeric_stddev_pop		0	2281	128	_null_ ));
! DATA(insert ( 2727	float4_accum	float8_stddev_pop	0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2728	float8_accum	float8_stddev_pop	0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2729	numeric_accum	numeric_stddev_pop	0	2281	128	_null_ ));
  
  /* stddev_samp */
! DATA(insert ( 2712	int8_accum	numeric_stddev_samp		0	2281	128	_null_ ));
! DATA(insert ( 2713	int4_accum	numeric_stddev_samp		0	2281	128	_null_ ));
! DATA(insert ( 2714	int2_accum	numeric_stddev_samp		0	2281	128	_null_ ));
! DATA(insert ( 2715	float4_accum	float8_stddev_samp	0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2716	float8_accum	float8_stddev_samp	0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2717	numeric_accum	numeric_stddev_samp 0	2281	128	_null_ ));
  
  /* stddev: historical Postgres syntax for stddev_samp */
! DATA(insert ( 2154	int8_accum	numeric_stddev_samp		0	2281	128	_null_ ));
! DATA(insert ( 2155	int4_accum	numeric_stddev_samp		0	2281	128	_null_ ));
! DATA(insert ( 2156	int2_accum	numeric_stddev_samp		0	2281	128	_null_ ));
! DATA(insert ( 2157	float4_accum	float8_stddev_samp	0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2158	float8_accum	float8_stddev_samp	0	1022	0	"{0,0,0}" ));
! DATA(insert ( 2159	numeric_accum	numeric_stddev_samp 0	2281	128	_null_ ));
  
  /* SQL2003 binary regression aggregates */
! DATA(insert ( 2818	int8inc_float8_float8		-				0	20		0	"0" ));
! DATA(insert ( 2819	float8_regr_accum	float8_regr_sxx			0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2820	float8_regr_accum	float8_regr_syy			0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2821	float8_regr_accum	float8_regr_sxy			0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2822	float8_regr_accum	float8_regr_avgx		0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2823	float8_regr_accum	float8_regr_avgy		0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2824	float8_regr_accum	float8_regr_r2			0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2825	float8_regr_accum	float8_regr_slope		0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2826	float8_regr_accum	float8_regr_intercept	0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2827	float8_regr_accum	float8_covar_pop		0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2828	float8_regr_accum	float8_covar_samp		0	1022	0	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2829	float8_regr_accum	float8_corr				0	1022	0	"{0,0,0,0,0,0}" ));
  
  /* boolean-and and boolean-or */
! DATA(insert ( 2517	booland_statefunc	-			58	16		0	_null_ ));
! DATA(insert ( 2518	boolor_statefunc	-			59	16		0	_null_ ));
! DATA(insert ( 2519	booland_statefunc	-			58	16		0	_null_ ));
  
  /* bitwise integer */
! DATA(insert ( 2236 int2and		  -					0	21		0	_null_ ));
! DATA(insert ( 2237 int2or		  -					0	21		0	_null_ ));
! DATA(insert ( 2238 int4and		  -					0	23		0	_null_ ));
! DATA(insert ( 2239 int4or		  -					0	23		0	_null_ ));
! DATA(insert ( 2240 int8and		  -					0	20		0	_null_ ));
! DATA(insert ( 2241 int8or		  -					0	20		0	_null_ ));
! DATA(insert ( 2242 bitand		  -					0	1560	0	_null_ ));
! DATA(insert ( 2243 bitor		  -					0	1560	0	_null_ ));
  
  /* xml */
! DATA(insert ( 2901 xmlconcat2	  -					0	142		0	_null_ ));
  
  /* array */
! DATA(insert ( 2335	array_agg_transfn	array_agg_finalfn		0	2281	0	_null_ ));
  
  /* text */
! DATA(insert ( 3538	string_agg_transfn	string_agg_finalfn		0	2281	0	_null_ ));
  
  /* bytea */
! DATA(insert ( 3545	bytea_string_agg_transfn	bytea_string_agg_finalfn		0	2281	0	_null_ ));
  
  /* json */
! DATA(insert ( 3175	json_agg_transfn	json_agg_finalfn		0	2281	0	_null_ ));
  
  /*
   * prototypes for functions in pg_aggregate.c
***************
*** 250,255 **** extern Oid AggregateCreate(const char *aggName,
--- 252,258 ----
  				List *aggfinalfnName,
  				List *aggsortopName,
  				Oid aggTransType,
+ 				int32 aggTransSpace,
  				const char *agginitval);
  
  #endif   /* PG_AGGREGATE_H */
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 2381,2407 **** DATA(insert OID = 2513 (  float8_stddev_pop PGNSP PGUID 12 1 0 0 0 f f f f t f i
  DESCR("aggregate final function");
  DATA(insert OID = 1832 (  float8_stddev_samp	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 701 "1022" _null_ _null_ _null_ _null_ float8_stddev_samp _null_ _null_ _null_ ));
  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_ ));
  DESCR("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_ ));
  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_ ));
  DESCR("aggregate 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_ ));
  DESCR("aggregate final function");
! DATA(insert OID = 2596 (  numeric_stddev_pop PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1700 "1231" _null_ _null_ _null_ _null_	numeric_stddev_pop _null_ _null_ _null_ ));
  DESCR("aggregate final function");
! DATA(insert OID = 1839 (  numeric_stddev_samp	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1700 "1231" _null_ _null_ _null_ _null_ numeric_stddev_samp _null_ _null_ _null_ ));
  DESCR("aggregate final function");
  DATA(insert OID = 1840 (  int2_sum		   PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 20 "20 21" _null_ _null_ _null_ _null_ int2_sum _null_ _null_ _null_ ));
  DESCR("aggregate transition function");
--- 2381,2409 ----
  DESCR("aggregate final function");
  DATA(insert OID = 1832 (  float8_stddev_samp	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 701 "1022" _null_ _null_ _null_ _null_ float8_stddev_samp _null_ _null_ _null_ ));
  DESCR("aggregate final function");
! DATA(insert OID = 1833 (  numeric_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_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 2 0 2281 "2281 1700" _null_ _null_ _null_ _null_ numeric_avg_accum _null_ _null_ _null_ ));
  DESCR("aggregate transition function");
! DATA(insert OID = 1834 (  int2_accum	   PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 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 f f i 2 0 2281 "2281 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 f f i 2 0 2281 "2281 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 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 f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_avg _null_ _null_ _null_ ));
  DESCR("aggregate final function");
! DATA(insert OID = 3179 (  numeric_sum	   PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_sum _null_ _null_ _null_ ));
  DESCR("aggregate final function");
! DATA(insert OID = 2514 (  numeric_var_pop  PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _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 f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_var_samp _null_ _null_ _null_ ));
  DESCR("aggregate final function");
! DATA(insert OID = 2596 (  numeric_stddev_pop PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_	numeric_stddev_pop _null_ _null_ _null_ ));
! DESCR("aggregate final function");
! DATA(insert OID = 1839 (  numeric_stddev_samp	PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 1700 "2281" _null_ _null_ _null_ _null_ numeric_stddev_samp _null_ _null_ _null_ ));
  DESCR("aggregate final function");
  DATA(insert OID = 1840 (  int2_sum		   PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 20 "20 21" _null_ _null_ _null_ _null_ int2_sum _null_ _null_ _null_ ));
  DESCR("aggregate transition function");
*** a/src/include/commands/defrem.h
--- b/src/include/commands/defrem.h
***************
*** 133,138 **** extern Datum transformGenericOptions(Oid catalogId,
--- 133,139 ----
  extern char *defGetString(DefElem *def);
  extern double defGetNumeric(DefElem *def);
  extern bool defGetBoolean(DefElem *def);
+ extern int32 defGetInt32(DefElem *def);
  extern int64 defGetInt64(DefElem *def);
  extern List *defGetQualifiedName(DefElem *def);
  extern TypeName *defGetTypeName(DefElem *def);
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 982,987 **** extern Datum int4_accum(PG_FUNCTION_ARGS);
--- 982,988 ----
  extern Datum int8_accum(PG_FUNCTION_ARGS);
  extern Datum int8_avg_accum(PG_FUNCTION_ARGS);
  extern Datum numeric_avg(PG_FUNCTION_ARGS);
+ extern Datum numeric_sum(PG_FUNCTION_ARGS);
  extern Datum numeric_var_pop(PG_FUNCTION_ARGS);
  extern Datum numeric_var_samp(PG_FUNCTION_ARGS);
  extern Datum numeric_stddev_pop(PG_FUNCTION_ARGS);
*** a/src/test/regress/expected/aggregates.out
--- b/src/test/regress/expected/aggregates.out
***************
*** 1262,1264 **** select least_agg(variadic array[q1,q2]) from int8_tbl;
--- 1262,1313 ----
   -4567890123456789
  (1 row)
  
+ -- verify correct calculations for null set
+ select sum(null::int4) from generate_series(1,1);
+  sum 
+ -----
+     
+ (1 row)
+ 
+ select sum(null::int8) from generate_series(1,1);
+  sum 
+ -----
+     
+ (1 row)
+ 
+ select sum(null::numeric) from generate_series(1,1);
+  sum 
+ -----
+     
+ (1 row)
+ 
+ select sum(null::float8) from generate_series(1,1);
+  sum 
+ -----
+     
+ (1 row)
+ 
+ select avg(null::int4) from generate_series(1,1);
+  avg 
+ -----
+     
+ (1 row)
+ 
+ select avg(null::int8) from generate_series(1,1);
+  avg 
+ -----
+     
+ (1 row)
+ 
+ select avg(null::numeric) from generate_series(1,1);
+  avg 
+ -----
+     
+ (1 row)
+ 
+ select avg(null::float8) from generate_series(1,1);
+  avg 
+ -----
+     
+ (1 row)
+ 
*** a/src/test/regress/sql/aggregates.sql
--- b/src/test/regress/sql/aggregates.sql
***************
*** 484,486 **** select aggfns(distinct a,b,c order by a,c using ~<~,b) filter (where a > 1)
--- 484,497 ----
  -- variadic aggregates
  select least_agg(q1,q2) from int8_tbl;
  select least_agg(variadic array[q1,q2]) from int8_tbl;
+ 
+ -- verify correct calculations for null set
+ select sum(null::int4) from generate_series(1,1);
+ select sum(null::int8) from generate_series(1,1);
+ select sum(null::numeric) from generate_series(1,1);
+ select sum(null::float8) from generate_series(1,1);
+ 
+ select avg(null::int4) from generate_series(1,1);
+ select avg(null::int8) from generate_series(1,1);
+ select avg(null::numeric) from generate_series(1,1);
+ select avg(null::float8) from generate_series(1,1);