Re: Improving avg performance for numeric

Started by Tomas Vondraover 12 years ago7 messages
#1Tomas Vondra
tv@fuzzy.cz

Hi,

I've reviewed the v6 of the "numeric optimize" patch
(/messages/by-id/CAFj8pRDQhG7Pqmf8XqXY0PnHfakkPQLPHnoRLJ_=EKFSbOAWeA@mail.gmail.com),
as Pavel did some hacking on the patch and asked me to do the review.

The patch seems fine to me, the following comments are mostly nitpicking:

1) Applies cleanly to the HEAD (although only by "patch" and not "git
apply").

2) I think we should use "estimate" instead of "approximation" in the
docs, it seems more correct / natural to me (but maybe I'm wrong on this
one).

3) state_data_size does not make much sense to me - it should be
state_size. This probably comes from the state_data_type, but that's
('state' + 'data type') and by replacing the second part with 'size'
you'll get state_size.

4) currently the state size may be specified for all data types, no
matter if their size is fixed or variable. Wouldn't it be safer to allow
this option only for variable-length data types? Right now you can
specify stype=int and sspace=200 which does not make much sense to me.
We can always relax the restrictions if needed, the opposite is much
more difficult.

5) in numeric.c, there are no comments for
- fields of the NumericAggState (some are obvious, some are not)
- multiple functions are missing the initial comments (e.g.
numeric_accum, do_numeric_accum)

6) I think the "first" variable in do_numeric_accum is rather useless,
it's trivial to remove it - just use the state->first instead and move
the assignment to the proper branch (ok, this is nitpicking).

7) I think the error message in makeNumericAggState should be something
like "This must not be called in non-aggregate context!" or something
like that.

8) The records in pg_aggregate.c are using either 0 (for fixed-length)
or 128. This seems slightly excessive to me. What is the reasoning
behind this? Is that because of the two NumericVar fields?

regards
Tomas

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

#2Pavel Stehule
pavel.stehule@gmail.com
In reply to: Tomas Vondra (#1)
1 attachment(s)

Hello

2013/9/22 Tomas Vondra <tv@fuzzy.cz>

Hi,

I've reviewed the v6 of the "numeric optimize" patch
(http://www.postgresql.org/**message-id/**CAFj8pRDQhG7Pqmf8XqXY0PnHfakkP**
QLPHnoRLJ_=EKFSbOAWeA@mail.**gmail.com</messages/by-id/CAFj8pRDQhG7Pqmf8XqXY0PnHfakkPQLPHnoRLJ_=EKFSbOAWeA@mail.gmail.com&gt;
),
as Pavel did some hacking on the patch and asked me to do the review.

The patch seems fine to me, the following comments are mostly nitpicking:

1) Applies cleanly to the HEAD (although only by "patch" and not "git
apply").

2) I think we should use "estimate" instead of "approximation" in the
docs, it seems more correct / natural to me (but maybe I'm wrong on this
one).

3) state_data_size does not make much sense to me - it should be
state_size. This probably comes from the state_data_type, but that's
('state' + 'data type') and by replacing the second part with 'size'
you'll get state_size.

This name is consistent with previous field state_data_type - I expected so
this mean 'state data' + 'type'. I am not native speaker, so my position is
not strong, but in this moment I am thinking so state_data_size has a
sense. In this case both variant has sense - 'state data' + type or
'state' + 'data type'.

4) currently the state size may be specified for all data types, no
matter if their size is fixed or variable. Wouldn't it be safer to allow
this option only for variable-length data types? Right now you can
specify stype=int and sspace=200 which does not make much sense to me.
We can always relax the restrictions if needed, the opposite is much
more difficult.

good idea

5) in numeric.c, there are no comments for
- fields of the NumericAggState (some are obvious, some are not)
- multiple functions are missing the initial comments (e.g.
numeric_accum, do_numeric_accum)

ok

6) I think the "first" variable in do_numeric_accum is rather useless,
it's trivial to remove it - just use the state->first instead and move
the assignment to the proper branch (ok, this is nitpicking).

ok

7) I think the error message in makeNumericAggState should be something
like "This must not be called in non-aggregate context!" or something like
that.

I used a "a numeric aggregate function called in non-aggregate context" -
it is similar to other related messages

8) The records in pg_aggregate.c are using either 0 (for fixed-length) or
128. This seems slightly excessive to me. What is the reasoning behind
this? Is that because of the two NumericVar fields?

NumericAggState has 96 bytes - but you have to add a space for digits of
included numeric values (inclued in NumericVar) -- so it is others 16 + 16
= 128. I am not able to specify how much digits will be used exactly - 16
bytes is just good enough estimation - it is not used for memory
allocation, it is used for some planner magic.

Show quoted text

regards
Tomas

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

Attachments:

numeric-optimize-v7.patchapplication/octet-stream; name=numeric-optimize-v7.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;
***************
*** 195,200 **** AggregateCreate(const char *aggName,
--- 196,211 ----
  				 errmsg("unsafe use of pseudo-type \"internal\""),
  				 errdetail("A function returning \"internal\" must have at least one \"internal\" argument.")));
  
+ 	/*
+ 	 * Allow non zerro aggTransSpace only for INTERNALOID aggTransType 
+ 	 * Other types has known size.
+ 	 */
+ 	if (aggTransType != INTERNALOID && aggTransSpace > 0)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ 				 errmsg("SSPACE can be used only for \"internal\" transition type"),
+ 				 errdetail("SSPACE can be defined only together with a \"internal\" transition type.")));
+ 
  	/* handle sortop, if supplied */
  	if (aggsortopName)
  	{
***************
*** 273,278 **** AggregateCreate(const char *aggName,
--- 284,290 ----
  	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,2632 ----
   *
   * 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 			isNaN;			/* true, when some processed number is NaN */
! 	uint64			N;			/* count of processed numbers */
! 	NumericVar 		sumX;			/* sum of processed numbers */
! 	NumericVar 		sumX2;			/* sum of square of processed numbers */
! 	bool 			calcSumX2;		/* when is true, then sumX2 is calculated */
! 	MemoryContext 	agg_context;
! } NumericAggState;
  
! 
! /*
!  * Prepare state data for Numeric aggregate function that needs to know a sum, count
!  * and sometimes sum of square (when calcSumX2 is true).
!  */
! 
! static NumericAggState *
! makeNumericAggState(FunctionCallInfo fcinfo, bool calcSumX2)
! {
! 	NumericAggState 	*state;
! 	MemoryContext 		 agg_context;
! 	MemoryContext 		 old_context;
! 
! 	if (!AggCheckCallContext(fcinfo, &agg_context))
! 		elog(ERROR, "a numeric aggregate function called in non-aggregate context");
! 
! 	old_context = MemoryContextSwitchTo(agg_context);
! 
! 	state = palloc0(sizeof(NumericAggState));
! 	state->calcSumX2 = calcSumX2;
! 	state->agg_context = agg_context;
! 
! 	MemoryContextSwitchTo(old_context);
! 
! 	return state;
  }
  
+ 
  /*
!  * a workhorse for numeric accum functions. A different behave (controlled by calcSumX2)
!  * is specified when NumericAggState is initiated. This accum function is used for numeric
!  * sum too due minimize overhead related to packing and unpacking Numeric value.
!  *
   */
! 
! static void
! do_numeric_accum(NumericAggState *state, Numeric newval)
  {
! 	NumericVar 		X;
! 	NumericVar 		X2;
! 	MemoryContext 	old_context;
  
! 	/* result is NaN, when some processed number is NaN */
! 	if (state->isNaN || NUMERIC_IS_NAN(newval))
! 	{
! 		state->isNaN = true;
! 		return;
! 	}
! 
! 	/* load processed number */
! 	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 (state->N++ > 0)
! 	{
! 		NumericVar preSumX;
! 
! 		/* increase counters */
! 		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
! 	{
! 		/* initialize counters */
! 		set_var_from_var(&X, &(state->sumX));
! 
! 		if (state->calcSumX2)
! 			set_var_from_var(&X2, &(state->sumX2));
! 	}
! 
! 	MemoryContextSwitchTo(old_context);
  }
  
+ /*
+  * Generic accum function for Numeric aggregates. It initialize NumericAggState
+  * with calcSumX2 is true - it ensure a sumX2 calcualtion.
+  */
+ 
  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 accum function for average and sum aggregate function. Doesn't calculate sumX2.
   */
+ 
  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.
--- 2639,2788 ----
  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,
--- 2791,2801 ----
   * *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
--- 2803,2826 ----
  	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;
--- 2840,2847 ----
  	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)
--- 2882,2888 ----
  	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)
--- 2897,2903 ----
  	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)
--- 2912,2918 ----
  	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)
--- 2927,2933 ----
  	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/expected/create_aggregate.out
--- b/src/test/regress/expected/create_aggregate.out
***************
*** 66,68 **** returns anyelement language sql as
--- 66,75 ----
  create aggregate least_agg(variadic items anyarray) (
    stype = anyelement, sfunc = least_accum
  );
+ -- should not work, sspace is allowed only for stype internal
+ create aggregate aggfns(integer,integer,text) (
+    sfunc = aggfns_trans, stype = aggtype[], sspace = 200,
+    initcond = '{}'
+ );
+ ERROR:  SSPACE can be used only for "internal" transition type
+ DETAIL:  SSPACE can be defined only together with a "internal" transition type.
*** 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);
*** a/src/test/regress/sql/create_aggregate.sql
--- b/src/test/regress/sql/create_aggregate.sql
***************
*** 80,82 **** returns anyelement language sql as
--- 80,88 ----
  create aggregate least_agg(variadic items anyarray) (
    stype = anyelement, sfunc = least_accum
  );
+ 
+ -- should not work, sspace is allowed only for stype internal
+ create aggregate aggfns(integer,integer,text) (
+    sfunc = aggfns_trans, stype = aggtype[], sspace = 200,
+    initcond = '{}'
+ );
#3Tomas Vondra
tv@fuzzy.cz
In reply to: Pavel Stehule (#2)

On 23 Září 2013, 18:18, Pavel Stehule wrote:

Hello

2013/9/22 Tomas Vondra <tv@fuzzy.cz>

Hi,

I've reviewed the v6 of the "numeric optimize" patch
(http://www.postgresql.org/**message-id/**CAFj8pRDQhG7Pqmf8XqXY0PnHfakkP**
QLPHnoRLJ_=EKFSbOAWeA@mail.**gmail.com</messages/by-id/CAFj8pRDQhG7Pqmf8XqXY0PnHfakkPQLPHnoRLJ_=EKFSbOAWeA@mail.gmail.com&gt;
),
as Pavel did some hacking on the patch and asked me to do the review.

The patch seems fine to me, the following comments are mostly
nitpicking:

1) Applies cleanly to the HEAD (although only by "patch" and not "git
apply").

2) I think we should use "estimate" instead of "approximation" in the
docs, it seems more correct / natural to me (but maybe I'm wrong on this
one).

3) state_data_size does not make much sense to me - it should be
state_size. This probably comes from the state_data_type, but that's
('state' + 'data type') and by replacing the second part with 'size'
you'll get state_size.

This name is consistent with previous field state_data_type - I expected
so
this mean 'state data' + 'type'. I am not native speaker, so my position
is
not strong, but in this moment I am thinking so state_data_size has a
sense. In this case both variant has sense - 'state data' + type or
'state' + 'data type'.

OK, let's leave this up to a native speaker.

8) The records in pg_aggregate.c are using either 0 (for fixed-length)
or
128. This seems slightly excessive to me. What is the reasoning behind
this? Is that because of the two NumericVar fields?

NumericAggState has 96 bytes - but you have to add a space for digits of
included numeric values (inclued in NumericVar) -- so it is others 16 + 16
= 128. I am not able to specify how much digits will be used exactly - 16
bytes is just good enough estimation - it is not used for memory
allocation, it is used for some planner magic.

OK, makes sense.

I've made some basic tests on a 40M table with random numerics, for
example this query:

select avg(val), sum(val), var_pop(val) from numeric_test ;

takes ~57 seconds on current master, but only ~26 seconds with the v7
patch. Granted, in practice the improvements won't be as good because of
I/O costs etc., but it's a nice gain.

Seems "ready for commiter" to me. I'll wait a few days for others to
comment, and then I'll update the commitfest page.

regards
Tomas

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

#4Robert Haas
robertmhaas@gmail.com
In reply to: Tomas Vondra (#3)

On Mon, Sep 23, 2013 at 4:15 PM, Tomas Vondra <tv@fuzzy.cz> wrote:

Seems "ready for commiter" to me. I'll wait a few days for others to
comment, and then I'll update the commitfest page.

Some thoughts:

The documentation doesn't reflect the restriction to type internal.
On a related note, why restrict this to type internal?

Formatting fixes are needed:

+               if (aggtransspace > 0)
+               {
+                       costs->transitionSpace += aggtransspace;
+               }

Project style is not to use curly-braces for single statements. Also,
the changes to numeric.c add blank lines before and after function
header comments, which is not the usual style.

! if (state == NULL)
! PG_RETURN_NULL();
! else
! PG_RETURN_POINTER(state);

I think this should just say PG_RETURN_POINTER(state). PG_RETURN_NULL
is for returning an SQL NULL, not (void *) 0. Is there some reason
why we need an SQL NULL here, rather than a NULL pointer?

On the whole this looks fairly solid on a first read-through.

--
Robert Haas
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

#5Pavel Stehule
pavel.stehule@gmail.com
In reply to: Robert Haas (#4)
1 attachment(s)

2013/9/24 Robert Haas <robertmhaas@gmail.com>

On Mon, Sep 23, 2013 at 4:15 PM, Tomas Vondra <tv@fuzzy.cz> wrote:

Seems "ready for commiter" to me. I'll wait a few days for others to
comment, and then I'll update the commitfest page.

Some thoughts:

The documentation doesn't reflect the restriction to type internal.
On a related note, why restrict this to type internal?

Now, for almost all types Postgres well estimate size of state value. Only
arrays with unknown size can be a different. When we enable this value for
all types, then users can specify some bad values for scalar buildin types.
Next argument is simply and bad - I don't see a good use case for
customization this value for other than types than internal type.

I have no strong position here - prefer joining with internal type due
little bit higher robustness.

Formatting fixes are needed:

+               if (aggtransspace > 0)
+               {
+                       costs->transitionSpace += aggtransspace;
+               }

Project style is not to use curly-braces for single statements. Also,
the changes to numeric.c add blank lines before and after function
header comments, which is not the usual style.

! if (state == NULL)
! PG_RETURN_NULL();
! else
! PG_RETURN_POINTER(state);

I think this should just say PG_RETURN_POINTER(state). PG_RETURN_NULL
is for returning an SQL NULL, not (void *) 0. Is there some reason
why we need an SQL NULL here, rather than a NULL pointer?

fixed

On the whole this looks fairly solid on a first read-through.

Thank you

Pavel

Show quoted text

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

numeric-optimize-v8.patchapplication/octet-stream; name=numeric-optimize-v8.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,288 ----
     </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. This parameter is enabled only only for
+       aggregate function where <replaceable
+       class="PARAMETER">state_data_type</replaceable> is internal, where size
+       is unknown and planner uses a 8KB as default. Default value is usually
+       too high (that effective disable HashAgg) and should be specified better.
+      </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;
***************
*** 195,200 **** AggregateCreate(const char *aggName,
--- 196,211 ----
  				 errmsg("unsafe use of pseudo-type \"internal\""),
  				 errdetail("A function returning \"internal\" must have at least one \"internal\" argument.")));
  
+ 	/*
+ 	 * Allow non zerro aggTransSpace only for INTERNALOID aggTransType 
+ 	 * Other types has known size.
+ 	 */
+ 	if (aggTransType != INTERNALOID && aggTransSpace > 0)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ 				 errmsg("SSPACE can be used only for \"internal\" transition type"),
+ 				 errdetail("SSPACE can be defined only together with a \"internal\" transition type.")));
+ 
  	/* handle sortop, if supplied */
  	if (aggsortopName)
  	{
***************
*** 273,278 **** AggregateCreate(const char *aggName,
--- 284,290 ----
  	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 */
***************
*** 539,545 **** count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
  		 * 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;
--- 541,547 ----
  		 * 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;
***************
*** 564,575 **** count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
  			/*
  			 * INTERNAL transition type is a special case: although INTERNAL
  			 * is pass-by-value, it's almost certainly being used as a pointer
! 			 * to some large data structure.  We assume usage of
! 			 * ALLOCSET_DEFAULT_INITSIZE, which is a good guess if the data is
! 			 * being kept in a private memory context, as is done by
! 			 * array_agg() for instance.
  			 */
! 			costs->transitionSpace += ALLOCSET_DEFAULT_INITSIZE;
  		}
  
  		/*
--- 566,581 ----
  			/*
  			 * INTERNAL transition type is a special case: although INTERNAL
  			 * is pass-by-value, it's almost certainly being used as a pointer
! 			 * to some large data structure. A size of this structure can be
! 			 * specified by aggtransspace value. When it is unspecified, then
! 			 * We assume usage of ALLOCSET_DEFAULT_INITSIZE, which is a good
! 			 * guess if the data is being kept in a private memory context,
! 			 * as is done by array_agg() for instance.
  			 */
! 			if (aggtransspace > 0)
! 				costs->transitionSpace += aggtransspace;
! 			else
! 				costs->transitionSpace += ALLOCSET_DEFAULT_INITSIZE;
  		}
  
  		/*
*** 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,2623 ----
   *
   * 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 			isNaN;			/* true, when some processed number is NaN */
! 	uint64			N;			/* count of processed numbers */
! 	NumericVar 		sumX;			/* sum of processed numbers */
! 	NumericVar 		sumX2;			/* sum of square of processed numbers */
! 	bool 			calcSumX2;		/* when is true, then sumX2 is calculated */
! 	MemoryContext 	agg_context;
! } NumericAggState;
  
! 
! /*
!  * Prepare state data for Numeric aggregate function that needs to know a sum, count
!  * and sometimes sum of square (when calcSumX2 is true).
!  */
! static NumericAggState *
! makeNumericAggState(FunctionCallInfo fcinfo, bool calcSumX2)
! {
! 	NumericAggState 	*state;
! 	MemoryContext 		 agg_context;
! 	MemoryContext 		 old_context;
! 
! 	if (!AggCheckCallContext(fcinfo, &agg_context))
! 		elog(ERROR, "a numeric aggregate function called in non-aggregate context");
! 
! 	old_context = MemoryContextSwitchTo(agg_context);
! 
! 	state = palloc0(sizeof(NumericAggState));
! 	state->calcSumX2 = calcSumX2;
! 	state->agg_context = agg_context;
! 
! 	MemoryContextSwitchTo(old_context);
! 
! 	return state;
  }
  
+ 
  /*
!  * a workhorse for numeric accum functions. A different behave (controlled by calcSumX2)
!  * is specified when NumericAggState is initiated. This accum function is used for numeric
!  * sum too due minimize overhead related to packing and unpacking Numeric value.
!  *
   */
! static void
! do_numeric_accum(NumericAggState *state, Numeric newval)
  {
! 	NumericVar 		X;
! 	NumericVar 		X2;
! 	MemoryContext 	old_context;
  
! 	/* result is NaN, when some processed number is NaN */
! 	if (state->isNaN || NUMERIC_IS_NAN(newval))
! 	{
! 		state->isNaN = true;
! 		return;
! 	}
! 
! 	/* load processed number */
! 	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 (state->N++ > 0)
! 	{
! 		NumericVar preSumX;
! 
! 		/* increase counters */
! 		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
! 	{
! 		/* initialize counters */
! 		set_var_from_var(&X, &(state->sumX));
! 
! 		if (state->calcSumX2)
! 			set_var_from_var(&X2, &(state->sumX2));
! 	}
! 
! 	MemoryContextSwitchTo(old_context);
  }
  
+ /*
+  * Generic accum function for Numeric aggregates. It initialize NumericAggState
+  * with calcSumX2 is true - it ensure a sumX2 calcualtion.
+  */
  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));
! 	}
! 
! 	PG_RETURN_POINTER(state);
  }
  
+ 
  /*
!  * Optimized accum function for average and sum aggregate function.
!  * Doesn't calculate sumX2.
   */
  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));
! 	}
! 
! 	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
***************
*** 2574,2664 **** numeric_avg_accum(PG_FUNCTION_ARGS)
   * for stddev/variance --- there are faster special-purpose accumulator
   * routines for SUM and AVG of these datatypes.
   */
- 
  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.
--- 2626,2764 ----
   * for stddev/variance --- there are faster special-purpose accumulator
   * routines for SUM and AVG of these datatypes.
   */
  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);
! 	}
! 
! 	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);
! 	}
  
! 	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);
! 	}
! 
! 	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);
! 	}
!  
! 	PG_RETURN_POINTER(state);
  }
  
  
  Datum
  numeric_avg(PG_FUNCTION_ARGS)
  {
! 	Datum 		 		N_datum;
! 	Datum 				sumX_datum;
! 	NumericAggState 	*state;
  
! 	state = (NumericAggState *) PG_GETARG_POINTER(0);
! 	if (state == NULL)
! 		PG_RETURN_NULL();
! 
! 	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;
! 
! 	state = (NumericAggState *) PG_GETARG_POINTER(0);
! 	if (state == NULL)
  		PG_RETURN_NULL();
  
! 	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,
--- 2767,2777 ----
   * *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
--- 2779,2802 ----
  	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;
--- 2816,2823 ----
  	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)
--- 2858,2864 ----
  	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)
--- 2873,2879 ----
  	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)
--- 2888,2894 ----
  	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)
--- 2903,2909 ----
  	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/expected/create_aggregate.out
--- b/src/test/regress/expected/create_aggregate.out
***************
*** 66,68 **** returns anyelement language sql as
--- 66,75 ----
  create aggregate least_agg(variadic items anyarray) (
    stype = anyelement, sfunc = least_accum
  );
+ -- should not work, sspace is allowed only for stype internal
+ create aggregate aggfns(integer,integer,text) (
+    sfunc = aggfns_trans, stype = aggtype[], sspace = 200,
+    initcond = '{}'
+ );
+ ERROR:  SSPACE can be used only for "internal" transition type
+ DETAIL:  SSPACE can be defined only together with a "internal" transition type.
*** 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);
*** a/src/test/regress/sql/create_aggregate.sql
--- b/src/test/regress/sql/create_aggregate.sql
***************
*** 80,82 **** returns anyelement language sql as
--- 80,88 ----
  create aggregate least_agg(variadic items anyarray) (
    stype = anyelement, sfunc = least_accum
  );
+ 
+ -- should not work, sspace is allowed only for stype internal
+ create aggregate aggfns(integer,integer,text) (
+    sfunc = aggfns_trans, stype = aggtype[], sspace = 200,
+    initcond = '{}'
+ );
#6Tomas Vondra
tv@fuzzy.cz
In reply to: Pavel Stehule (#5)

On 24.9.2013 17:57, Pavel Stehule wrote:

2013/9/24 Robert Haas <robertmhaas@gmail.com
<mailto:robertmhaas@gmail.com>>

On Mon, Sep 23, 2013 at 4:15 PM, Tomas Vondra <tv@fuzzy.cz
<mailto:tv@fuzzy.cz>> wrote:

Seems "ready for commiter" to me. I'll wait a few days for others
to comment, and then I'll update the commitfest page.

Some thoughts:

The documentation doesn't reflect the restriction to type internal.
On a related note, why restrict this to type internal?

Now, for almost all types Postgres well estimate size of state
value. Only arrays with unknown size can be a different. When we
enable this value for all types, then users can specify some bad
values for scalar buildin types. Next argument is simply and bad - I
don't see a good use case for customization this value for other than
types than internal type.

I have no strong position here - prefer joining with internal type
due little bit higher robustness.

I share this oppinion. I was not able to come up with a single use case
benefiting from allowing this for types other than internal. Seems like
a footgun to me, with no potential benefit.

So +1 to keeping the patch 'type internal only' from me.

With the formatting issues now fixed, I believe the patch is ready for
committer (and someone already switched it to that state).

Tomas

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

#7Tom Lane
tgl@sss.pgh.pa.us
In reply to: Pavel Stehule (#5)

Pavel Stehule <pavel.stehule@gmail.com> writes:

[ numeric-optimize-v8.patch ]

I've committed this with some adjustments. Aside from cosmetic
changes and documentation/comment improvements:

* I don't agree at all with the claim that aggtransspace is only useful
for INTERNAL transition data. There are lots of cases with regular
SQL types where the planner doesn't have a good idea of how large the
transition value will be, and with the current code it tends to guess
on the small side (frequently 32 bytes :-(). It seems to me to be
useful to give users a knob to twiddle there. Consider for example
an aggregate that uses an integer array as transition state; the author
of the aggregate might know that there are usually hundreds of entries
in the array, and wish to dial up aggtransspace to prevent the planner
from optimistically choosing hash aggregation.

As committed, I just took out the restriction in CREATE AGGREGATE
altogether; it will let you set SSPACE for any transition datatype.
The planner will ignore it for pass-by-value types, where the behavior
is known, but otherwise honor it. It's possible that we should put in
a restriction to INTERNAL plus pass-by-reference types, or even INTERNAL
plus pass-by-reference variable-length types, but I can't get excited
about that. The error message would be too confusing I think.

* There was a stray "else" added to clauses.c that disabled space
accounting for polymorphic aggregates.

* I simplified the summing code in do_numeric_accum; the copying and
reallocating it was doing was not only unnecessary but contained
unpleasant assumptions about what you can do with a NumericVar. This
probably makes the committed patch a bit faster than what was submitted,
but I didn't try to benchmark the change.

* I made sure all the functions accessed their input state pointer with
state = PG_ARGISNULL(0) ? NULL : (NumericAggState *) PG_GETARG_POINTER(0);
There were places that omitted the ARGISNULL test, and thus only happened
to work if the uninitialized Datum value they got was zero. While that
might chance to be true in the current state of the code, it's not an
assumption you're supposed to make.

* numeric sum/avg failed to check for NaN inputs.

* I fixed incorrect tests in the code added to pg_dump.

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