From c4fff393136556b36def22f2c270397e9247534c Mon Sep 17 00:00:00 2001
From: David Rowley <dgrowley@gmail.com>
Date: Wed, 16 Mar 2016 23:17:59 +1300
Subject: [PATCH 2/5] Allow INTERNAL state aggregates to participate in partial
 aggregation

 This adds infrastructure to allow internal states of aggregate functions
 to be serialized so that they can be transferred from a worker process
 into the master back-end process. The master back-end process then
 performs a de-serialization of the state before performing the final
 aggregate stage.

 This commit does not add any serialization or de-serialization functions.
 These functions will arrive in a follow-on commit.
---
 doc/src/sgml/ref/create_aggregate.sgml  |  36 +++-
 src/backend/catalog/pg_aggregate.c      |  82 ++++++++-
 src/backend/commands/aggregatecmds.c    |  57 ++++++
 src/backend/executor/nodeAgg.c          | 219 ++++++++++++++++++++--
 src/backend/nodes/copyfuncs.c           |   1 +
 src/backend/nodes/outfuncs.c            |   1 +
 src/backend/nodes/readfuncs.c           |   1 +
 src/backend/optimizer/plan/createplan.c |   7 +-
 src/backend/optimizer/plan/planner.c    |  25 ++-
 src/backend/optimizer/prep/prepunion.c  |   3 +-
 src/backend/optimizer/util/clauses.c    |  18 +-
 src/backend/optimizer/util/pathnode.c   |   8 +-
 src/backend/optimizer/util/tlist.c      |  15 +-
 src/backend/parser/parse_agg.c          |  74 ++++++++
 src/bin/pg_dump/pg_dump.c               |  50 ++++-
 src/include/catalog/pg_aggregate.h      | 314 +++++++++++++++++---------------
 src/include/nodes/execnodes.h           |   1 +
 src/include/nodes/plannodes.h           |   1 +
 src/include/nodes/relation.h            |   1 +
 src/include/optimizer/pathnode.h        |   3 +-
 src/include/optimizer/planmain.h        |   2 +-
 src/include/optimizer/tlist.h           |   2 +-
 src/include/parser/parse_agg.h          |  12 ++
 23 files changed, 734 insertions(+), 199 deletions(-)

diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml
index 4bda23a..deb3956 100644
--- a/doc/src/sgml/ref/create_aggregate.sgml
+++ b/doc/src/sgml/ref/create_aggregate.sgml
@@ -28,6 +28,9 @@ CREATE AGGREGATE <replaceable class="parameter">name</replaceable> ( [ <replacea
     [ , FINALFUNC = <replaceable class="PARAMETER">ffunc</replaceable> ]
     [ , FINALFUNC_EXTRA ]
     [ , COMBINEFUNC = <replaceable class="PARAMETER">combinefunc</replaceable> ]
+    [ , SERIALFUNC = <replaceable class="PARAMETER">serialfunc</replaceable> ]
+    [ , DESERIALFUNC = <replaceable class="PARAMETER">deserialfunc</replaceable> ]
+    [ , SERIALTYPE = <replaceable class="PARAMETER">serialtype</replaceable> ]
     [ , INITCOND = <replaceable class="PARAMETER">initial_condition</replaceable> ]
     [ , MSFUNC = <replaceable class="PARAMETER">msfunc</replaceable> ]
     [ , MINVFUNC = <replaceable class="PARAMETER">minvfunc</replaceable> ]
@@ -47,6 +50,9 @@ CREATE AGGREGATE <replaceable class="parameter">name</replaceable> ( [ [ <replac
     [ , FINALFUNC = <replaceable class="PARAMETER">ffunc</replaceable> ]
     [ , FINALFUNC_EXTRA ]
     [ , COMBINEFUNC = <replaceable class="PARAMETER">combinefunc</replaceable> ]
+    [ , SERIALFUNC = <replaceable class="PARAMETER">serialfunc</replaceable> ]
+    [ , DESERIALFUNC = <replaceable class="PARAMETER">deserialfunc</replaceable> ]
+    [ , SERIALTYPE = <replaceable class="PARAMETER">serialtype</replaceable> ]
     [ , INITCOND = <replaceable class="PARAMETER">initial_condition</replaceable> ]
     [ , HYPOTHETICAL ]
 )
@@ -61,6 +67,9 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> (
     [ , FINALFUNC = <replaceable class="PARAMETER">ffunc</replaceable> ]
     [ , FINALFUNC_EXTRA ]
     [ , COMBINEFUNC = <replaceable class="PARAMETER">combinefunc</replaceable> ]
+    [ , SERIALFUNC = <replaceable class="PARAMETER">serialfunc</replaceable> ]
+    [ , DESERIALFUNC = <replaceable class="PARAMETER">deserialfunc</replaceable> ]
+    [ , SERIALTYPE = <replaceable class="PARAMETER">serialtype</replaceable> ]
     [ , INITCOND = <replaceable class="PARAMETER">initial_condition</replaceable> ]
     [ , MSFUNC = <replaceable class="PARAMETER">msfunc</replaceable> ]
     [ , MINVFUNC = <replaceable class="PARAMETER">minvfunc</replaceable> ]
@@ -110,13 +119,21 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> (
    <replaceable class="PARAMETER">sfunc</replaceable>,
    an optional final calculation function
    <replaceable class="PARAMETER">ffunc</replaceable>,
-   and an optional combine function
-   <replaceable class="PARAMETER">combinefunc</replaceable>.
+   an optional combine function
+   <replaceable class="PARAMETER">combinefunc</replaceable>,
+   an optional serialization function
+   <replaceable class="PARAMETER">serialfunc</replaceable>,
+   an optional de-serialization function
+   <replaceable class="PARAMETER">deserialfunc</replaceable>,
+   and an optional serialization type
+   <replaceable class="PARAMETER">serialtype</replaceable>.
    These are used as follows:
 <programlisting>
 <replaceable class="PARAMETER">sfunc</replaceable>( internal-state, next-data-values ) ---> next-internal-state
 <replaceable class="PARAMETER">ffunc</replaceable>( internal-state ) ---> aggregate-value
 <replaceable class="PARAMETER">combinefunc</replaceable>( internal-state, internal-state ) ---> next-internal-state
+<replaceable class="PARAMETER">serialfunc</replaceable>( internal-state ) ---> serialized-state
+<replaceable class="PARAMETER">deserialfunc</replaceable>( serialized-state ) ---> internal-state
 </programlisting>
   </para>
 
@@ -140,6 +157,21 @@ CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> (
   </para>
 
   <para>
+  A serialization and de-serialization function may also be supplied. These
+  functions are required in order to allow parallel aggregation for aggregates
+  with an <replaceable class="PARAMETER">stype</replaceable> of <literal>
+  INTERNAL</>. The <replaceable class="PARAMETER">serialfunc</replaceable>, if
+  present must transform the aggregate state into a value of
+  <replaceable class="PARAMETER">serialtype</replaceable>, whereas the 
+  <replaceable class="PARAMETER">deserialfunc</replaceable> performs the
+  opposite, transforming the aggregate state back into the
+  <replaceable class="PARAMETER">stype</replaceable>. This is required due to
+  the process model being unable to pass references to <literal>INTERNAL
+  </literal> types between different <productname>PostgreSQL</productname>
+  processes. These parameters are only valid when
+  <replaceable class="PARAMETER">stype</replaceable> is <literal>INTERNAL</>.
+
+  <para>
    An aggregate function can provide an initial condition,
    that is, an initial value for the internal state value.
    This is specified and stored in the database as a value of type
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index c612ab9..177689b 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -58,6 +58,8 @@ AggregateCreate(const char *aggName,
 				List *aggtransfnName,
 				List *aggfinalfnName,
 				List *aggcombinefnName,
+				List *aggserialfnName,
+				List *aggdeserialfnName,
 				List *aggmtransfnName,
 				List *aggminvtransfnName,
 				List *aggmfinalfnName,
@@ -65,6 +67,7 @@ AggregateCreate(const char *aggName,
 				bool mfinalfnExtraArgs,
 				List *aggsortopName,
 				Oid aggTransType,
+				Oid aggSerialType,
 				int32 aggTransSpace,
 				Oid aggmTransType,
 				int32 aggmTransSpace,
@@ -79,6 +82,8 @@ AggregateCreate(const char *aggName,
 	Oid			transfn;
 	Oid			finalfn = InvalidOid;	/* can be omitted */
 	Oid			combinefn = InvalidOid;	/* can be omitted */
+	Oid			serialfn = InvalidOid;	/* can be omitted */
+	Oid			deserialfn = InvalidOid;	/* can be omitted */
 	Oid			mtransfn = InvalidOid;	/* can be omitted */
 	Oid			minvtransfn = InvalidOid;		/* can be omitted */
 	Oid			mfinalfn = InvalidOid;	/* can be omitted */
@@ -423,6 +428,59 @@ AggregateCreate(const char *aggName,
 	}
 
 	/*
+	 * Validate the serial function, if present. We must ensure that the return
+	 * type of this function is the same as the specified serialType, and that
+	 * indeed a serialType was actually also specified.
+	 */
+	if (aggserialfnName)
+	{
+		/* check that we also got a serial type */
+		if (!OidIsValid(aggSerialType))
+			ereport(ERROR,
+				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+				 errmsg("must specify serial type when specifying serialfunc")));
+
+		fnArgs[0] = aggTransType;
+
+		serialfn = lookup_agg_function(aggserialfnName, 1,
+									   fnArgs, variadicArgType,
+									   &rettype);
+
+		if (rettype != aggSerialType)
+			ereport(ERROR,
+					(errcode(ERRCODE_DATATYPE_MISMATCH),
+					 errmsg("return type of serial function %s is not %s",
+							NameListToString(aggserialfnName),
+							format_type_be(aggSerialType))));
+	}
+
+	/*
+	 * Validate the de-serial function, if present. We must ensure that the
+	 * return type of this function is the same as the transType.
+	 */
+	if (aggdeserialfnName)
+	{
+		/* check that we also got a serial type */
+		if (!OidIsValid(aggSerialType))
+			ereport(ERROR,
+				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+				 errmsg("must specify serialtype when specifying deserialfunc")));
+
+		fnArgs[0] = aggSerialType;
+
+		deserialfn = lookup_agg_function(aggdeserialfnName, 1,
+										 fnArgs, variadicArgType,
+										 &rettype);
+
+		if (rettype != aggTransType)
+			ereport(ERROR,
+					(errcode(ERRCODE_DATATYPE_MISMATCH),
+					 errmsg("return type of de-serial function %s is not %s",
+							NameListToString(aggdeserialfnName),
+							format_type_be(aggTransType))));
+	}
+
+	/*
 	 * If finaltype (i.e. aggregate return type) is polymorphic, inputs must
 	 * be polymorphic also, else parser will fail to deduce result type.
 	 * (Note: given the previous test on transtype and inputs, this cannot
@@ -594,6 +652,8 @@ AggregateCreate(const char *aggName,
 	values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
 	values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
 	values[Anum_pg_aggregate_aggcombinefn - 1] = ObjectIdGetDatum(combinefn);
+	values[Anum_pg_aggregate_aggserialfn - 1] = ObjectIdGetDatum(serialfn);
+	values[Anum_pg_aggregate_aggdeserialfn - 1] = ObjectIdGetDatum(deserialfn);
 	values[Anum_pg_aggregate_aggmtransfn - 1] = ObjectIdGetDatum(mtransfn);
 	values[Anum_pg_aggregate_aggminvtransfn - 1] = ObjectIdGetDatum(minvtransfn);
 	values[Anum_pg_aggregate_aggmfinalfn - 1] = ObjectIdGetDatum(mfinalfn);
@@ -601,6 +661,7 @@ AggregateCreate(const char *aggName,
 	values[Anum_pg_aggregate_aggmfinalextra - 1] = BoolGetDatum(mfinalfnExtraArgs);
 	values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop);
 	values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
+	values[Anum_pg_aggregate_aggserialtype - 1] = ObjectIdGetDatum(aggSerialType);
 	values[Anum_pg_aggregate_aggtransspace - 1] = Int32GetDatum(aggTransSpace);
 	values[Anum_pg_aggregate_aggmtranstype - 1] = ObjectIdGetDatum(aggmTransType);
 	values[Anum_pg_aggregate_aggmtransspace - 1] = Int32GetDatum(aggmTransSpace);
@@ -627,7 +688,8 @@ AggregateCreate(const char *aggName,
 	 * Create dependencies for the aggregate (above and beyond those already
 	 * made by ProcedureCreate).  Note: we don't need an explicit dependency
 	 * on aggTransType since we depend on it indirectly through transfn.
-	 * Likewise for aggmTransType if any.
+	 * Likewise for aggmTransType using the mtransfunc, and also for
+	 * aggSerialType using the serialfn, if they exist.
 	 */
 
 	/* Depends on transition function */
@@ -654,6 +716,24 @@ AggregateCreate(const char *aggName,
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
 
+	/* Depends on serial function, if any */
+	if (OidIsValid(serialfn))
+	{
+		referenced.classId = ProcedureRelationId;
+		referenced.objectId = serialfn;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+	}
+
+	/* Depends on de-serial function, if any */
+	if (OidIsValid(deserialfn))
+	{
+		referenced.classId = ProcedureRelationId;
+		referenced.objectId = deserialfn;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+	}
+
 	/* Depends on forward transition function, if any */
 	if (OidIsValid(mtransfn))
 	{
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c
index 59bc6e6..dc25453 100644
--- a/src/backend/commands/aggregatecmds.c
+++ b/src/backend/commands/aggregatecmds.c
@@ -62,6 +62,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
 	List	   *transfuncName = NIL;
 	List	   *finalfuncName = NIL;
 	List	   *combinefuncName = NIL;
+	List	   *serialfuncName = NIL;
+	List	   *deserialfuncName = NIL;
 	List	   *mtransfuncName = NIL;
 	List	   *minvtransfuncName = NIL;
 	List	   *mfinalfuncName = NIL;
@@ -70,6 +72,7 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
 	List	   *sortoperatorName = NIL;
 	TypeName   *baseType = NULL;
 	TypeName   *transType = NULL;
+	TypeName   *serialType = NULL;
 	TypeName   *mtransType = NULL;
 	int32		transSpace = 0;
 	int32		mtransSpace = 0;
@@ -84,6 +87,7 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
 	List	   *parameterDefaults;
 	Oid			variadicArgType;
 	Oid			transTypeId;
+	Oid			serialTypeId = InvalidOid;
 	Oid			mtransTypeId = InvalidOid;
 	char		transTypeType;
 	char		mtransTypeType = 0;
@@ -127,6 +131,10 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
 			finalfuncName = defGetQualifiedName(defel);
 		else if (pg_strcasecmp(defel->defname, "combinefunc") == 0)
 			combinefuncName = defGetQualifiedName(defel);
+		else if (pg_strcasecmp(defel->defname, "serialfunc") == 0)
+			serialfuncName = defGetQualifiedName(defel);
+		else if (pg_strcasecmp(defel->defname, "deserialfunc") == 0)
+			deserialfuncName = defGetQualifiedName(defel);
 		else if (pg_strcasecmp(defel->defname, "msfunc") == 0)
 			mtransfuncName = defGetQualifiedName(defel);
 		else if (pg_strcasecmp(defel->defname, "minvfunc") == 0)
@@ -154,6 +162,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
 		}
 		else if (pg_strcasecmp(defel->defname, "stype") == 0)
 			transType = defGetTypeName(defel);
+		else if (pg_strcasecmp(defel->defname, "serialtype") == 0)
+			serialType = defGetTypeName(defel);
 		else if (pg_strcasecmp(defel->defname, "stype1") == 0)
 			transType = defGetTypeName(defel);
 		else if (pg_strcasecmp(defel->defname, "sspace") == 0)
@@ -319,6 +329,50 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
 							format_type_be(transTypeId))));
 	}
 
+	if (serialType)
+	{
+		/*
+		 * There's little point in having a serial/de-serial function on
+		 * aggregates that don't have an internal state, so let's just disallow
+		 * this as it may help clear up any confusion or needless authoring of
+		 * these functions.
+		 */
+		if (transTypeId != INTERNALOID)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+					 errmsg("a serialtype must only be specified when stype is \"%s\"",
+						 format_type_be(INTERNALOID))));
+
+		serialTypeId = typenameTypeId(NULL, serialType);
+
+		/*
+		 * We disallow INTERNAL serialType as the whole point of the
+		 * serialized types is to allow the aggregate state to be output,
+		 * and we cannot output INTERNAL. This check, combined with the one
+		 * above ensures that the trans type and serial type are not the same.
+		 */
+		if (serialTypeId == INTERNALOID)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+						errmsg("aggregate serial data type cannot be \"%s\"",
+							format_type_be(serialTypeId))));
+
+		/*
+		 * If serialType is specified then serialfuncName and deserialfuncName
+		 * must be present; if not, then none of the serialization options
+		 * should have been specified.
+		 */
+		if (serialfuncName == NIL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+					 errmsg("aggregate serialfunc must be specified when serialtype is specified")));
+
+		if (deserialfuncName == NIL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+					 errmsg("aggregate deserialfunc must be specified when serialtype is specified")));
+	}
+
 	/*
 	 * If a moving-aggregate transtype is specified, look that up.  Same
 	 * restrictions as for transtype.
@@ -387,6 +441,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
 						   transfuncName,		/* step function name */
 						   finalfuncName,		/* final function name */
 						   combinefuncName,		/* combine function name */
+						   serialfuncName,		/* serial function name */
+						   deserialfuncName,	/* de-serial function name */
 						   mtransfuncName,		/* fwd trans function name */
 						   minvtransfuncName,	/* inv trans function name */
 						   mfinalfuncName,		/* final function name */
@@ -394,6 +450,7 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
 						   mfinalfuncExtraArgs,
 						   sortoperatorName,	/* sort operator name */
 						   transTypeId, /* transition data type */
+						   serialTypeId, /* serial data type */
 						   transSpace,	/* transition space */
 						   mtransTypeId,		/* transition data type */
 						   mtransSpace, /* transition space */
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 03aa20f..d4890f8 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -13,13 +13,14 @@
  *	  If a finalfunc is not supplied or finalizeAggs is false, then the result
  *	  is just the ending value of transvalue.
  *
- *	  Other behavior is also supported and is controlled by the 'combineStates'
- *	  and 'finalizeAggs'. 'combineStates' controls whether the trans func or
- *	  the combine func is used during aggregation.  When 'combineStates' is
- *	  true we expect other (previously) aggregated states as input rather than
- *	  input tuples. This mode facilitates multiple aggregate stages which
- *	  allows us to support pushing aggregation down deeper into the plan rather
- *	  than leaving it for the final stage. For example with a query such as:
+ *	  Other behavior is also supported and is controlled by the 'combineStates',
+ *	  'finalizeAggs' and 'serialStates' parameters. 'combineStates' controls
+ *	  whether the trans func or the combine func is used during aggregation.
+ *	  When 'combineStates' is true we expect other (previously) aggregated
+ *	  states as input rather than input tuples. This mode facilitates multiple
+ *	  aggregate stages which allows us to support pushing aggregation down
+ *	  deeper into the plan rather than leaving it for the final stage. For
+ *	  example with a query such as:
  *
  *	  SELECT count(*) FROM (SELECT * FROM a UNION ALL SELECT * FROM b);
  *
@@ -44,6 +45,16 @@
  *	  incorrect. Instead a new state should be created in the correct aggregate
  *	  memory context and the 2nd state should be copied over.
  *
+ *	  The 'serialStates' option can be used to allow multi-stage aggregation
+ *	  for aggregates with an INTERNAL state type. When this mode is disabled
+ *	  only a pointer to the INTERNAL aggregate states are passed around the
+ *	  executor. This behaviour does not suit a parallel environment where the
+ *	  process is unable to dereference pointers for memory which belongs to a
+ *	  worker process. Enabling this mode causes the INTERNAL states to be
+ *	  serialized and de-serialized as and when required, which of course
+ *	  requires that the aggregate function also have a 'serialfunc' and
+ *	  'deserialfunc' function specified.
+ *
  *	  If a normal aggregate call specifies DISTINCT or ORDER BY, we sort the
  *	  input tuples and eliminate duplicates (if required) before performing
  *	  the above-depicted process.  (However, we don't do that for ordered-set
@@ -232,6 +243,12 @@ typedef struct AggStatePerTransData
 	/* Oid of the state transition or combine function */
 	Oid			transfn_oid;
 
+	/* Oid of the serial function or InvalidOid */
+	Oid			serialfn_oid;
+
+	/* Oid of the de-serial function or InvalidOid */
+	Oid			deserialfn_oid;
+
 	/* Oid of state value's datatype */
 	Oid			aggtranstype;
 
@@ -246,6 +263,12 @@ typedef struct AggStatePerTransData
 	 */
 	FmgrInfo	transfn;
 
+	/* fmgr lookup data for serial function */
+	FmgrInfo	serialfn;
+
+	/* fmgr lookup data for de-serial function */
+	FmgrInfo	deserialfn;
+
 	/* Input collation derived for aggregate */
 	Oid			aggCollation;
 
@@ -326,6 +349,11 @@ typedef struct AggStatePerTransData
 	 * worth the extra space consumption.
 	 */
 	FunctionCallInfoData transfn_fcinfo;
+
+	/* Likewise for serial and de-serial functions */
+	FunctionCallInfoData serialfn_fcinfo;
+
+	FunctionCallInfoData deserialfn_fcinfo;
 }	AggStatePerTransData;
 
 /*
@@ -487,12 +515,15 @@ static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
 static void build_pertrans_for_aggref(AggStatePerTrans pertrans,
 						  AggState *aggsate, EState *estate,
 						  Aggref *aggref, Oid aggtransfn, Oid aggtranstype,
-						  Datum initValue, bool initValueIsNull,
-						  Oid *inputTypes, int numArguments);
+						  Oid aggserialtype, Oid aggserialfn,
+						  Oid aggdeserialfn, Datum initValue,
+						  bool initValueIsNull, Oid *inputTypes,
+						  int numArguments);
 static int find_compatible_peragg(Aggref *newagg, AggState *aggstate,
 					   int lastaggno, List **same_input_transnos);
 static int find_compatible_pertrans(AggState *aggstate, Aggref *newagg,
 						 Oid aggtransfn, Oid aggtranstype,
+						 Oid aggserialfn, Oid aggdeserialfn,
 						 Datum initValue, bool initValueIsNull,
 						 List *transnos);
 
@@ -944,8 +975,30 @@ combine_aggregates(AggState *aggstate, AggStatePerGroup pergroup)
 		slot = ExecProject(pertrans->evalproj, NULL);
 		Assert(slot->tts_nvalid >= 1);
 
-		fcinfo->arg[1] = slot->tts_values[0];
-		fcinfo->argnull[1] = slot->tts_isnull[0];
+		/*
+		 * deserialfn_oid will be set if we must deserialize the input state
+		 * before calling the combine function
+		 */
+		if (OidIsValid(pertrans->deserialfn_oid))
+		{
+			/* don't call a strict de-serial function with NULL input */
+			if (pertrans->deserialfn.fn_strict && slot->tts_isnull[0])
+				continue;
+			else
+			{
+				FunctionCallInfo dsinfo = &pertrans->deserialfn_fcinfo;
+				dsinfo->arg[0] = slot->tts_values[0];
+				dsinfo->argnull[0] = slot->tts_isnull[0];
+
+				fcinfo->arg[1] = FunctionCallInvoke(dsinfo);
+				fcinfo->argnull[1] = dsinfo->isnull;
+			}
+		}
+		else
+		{
+			fcinfo->arg[1] = slot->tts_values[0];
+			fcinfo->argnull[1] = slot->tts_isnull[0];
+		}
 
 		advance_combine_function(aggstate, pertrans, pergroupstate);
 	}
@@ -1454,6 +1507,27 @@ finalize_aggregates(AggState *aggstate,
 		if (aggstate->finalizeAggs)
 			finalize_aggregate(aggstate, peragg, pergroupstate,
 							   &aggvalues[aggno], &aggnulls[aggno]);
+
+		/*
+		 * serialfn_oid will be set if we must serialize the input state
+		 * before calling the combine function on the state.
+		 */
+		else if (OidIsValid(pertrans->serialfn_oid))
+		{
+			/* don't call a strict serial function with NULL input */
+			if (pertrans->serialfn.fn_strict &&
+				pergroupstate->transValueIsNull)
+				continue;
+			else
+			{
+				FunctionCallInfo fcinfo = &pertrans->serialfn_fcinfo;
+				fcinfo->arg[0] = pergroupstate->transValue;
+				fcinfo->argnull[0] = pergroupstate->transValueIsNull;
+
+				aggvalues[aggno] = FunctionCallInvoke(fcinfo);
+				aggnulls[aggno] = fcinfo->isnull;
+			}
+		}
 		else
 		{
 			aggvalues[aggno] = pergroupstate->transValue;
@@ -2238,6 +2312,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->agg_done = false;
 	aggstate->combineStates = node->combineStates;
 	aggstate->finalizeAggs = node->finalizeAggs;
+	aggstate->serialStates = node->serialStates;
 	aggstate->input_done = false;
 	aggstate->pergroup = NULL;
 	aggstate->grp_firstTuple = NULL;
@@ -2546,6 +2621,9 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 		AclResult	aclresult;
 		Oid			transfn_oid,
 					finalfn_oid;
+		Oid			serialtype_oid,
+					serialfn_oid,
+					deserialfn_oid;
 		Expr	   *finalfnexpr;
 		Oid			aggtranstype;
 		Datum		textInitVal;
@@ -2610,6 +2688,47 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 		else
 			peragg->finalfn_oid = finalfn_oid = InvalidOid;
 
+		serialtype_oid = InvalidOid;
+		serialfn_oid = InvalidOid;
+		deserialfn_oid = InvalidOid;
+
+		/*
+		 * Determine if we require serialization or de-serialization of the
+		 * aggregate states. This is only required if the aggregate state is
+		 * internal.
+		 */
+		if (aggstate->serialStates && aggform->aggtranstype == INTERNALOID)
+		{
+			/*
+			 * The planner should only have generated an agg node with
+			 * serialStates if every aggregate with an INTERNAL state has a
+			 * serial type, serial function and de-serial function. Let's ensure
+			 * it didn't mess that up.
+			 */
+			if (!OidIsValid(aggform->aggserialtype))
+				elog(ERROR, "serial type not set during serialStates aggregation step");
+
+			if (!OidIsValid(aggform->aggserialfn))
+				elog(ERROR, "serial func not set during serialStates aggregation step");
+
+			if (!OidIsValid(aggform->aggdeserialfn))
+				elog(ERROR, "de-serial func not set during serialStates aggregation step");
+
+			/* serial func only required when not finalizing aggs */
+			if (!aggstate->finalizeAggs)
+			{
+				serialfn_oid = aggform->aggserialfn;
+				serialtype_oid = aggform->aggserialtype;
+			}
+
+			/* de-serial func only required when combining states */
+			if (aggstate->combineStates)
+			{
+				deserialfn_oid = aggform->aggdeserialfn;
+				serialtype_oid = aggform->aggserialtype;
+			}
+		}
+
 		/* Check that aggregate owner has permission to call component fns */
 		{
 			HeapTuple	procTuple;
@@ -2638,6 +2757,24 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 								   get_func_name(finalfn_oid));
 				InvokeFunctionExecuteHook(finalfn_oid);
 			}
+			if (OidIsValid(serialfn_oid))
+			{
+				aclresult = pg_proc_aclcheck(serialfn_oid, aggOwner,
+											 ACL_EXECUTE);
+				if (aclresult != ACLCHECK_OK)
+					aclcheck_error(aclresult, ACL_KIND_PROC,
+								   get_func_name(serialfn_oid));
+				InvokeFunctionExecuteHook(serialfn_oid);
+			}
+			if (OidIsValid(deserialfn_oid))
+			{
+				aclresult = pg_proc_aclcheck(deserialfn_oid, aggOwner,
+											 ACL_EXECUTE);
+				if (aclresult != ACLCHECK_OK)
+					aclcheck_error(aclresult, ACL_KIND_PROC,
+								   get_func_name(deserialfn_oid));
+				InvokeFunctionExecuteHook(deserialfn_oid);
+			}
 		}
 
 		/*
@@ -2707,7 +2844,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 		 */
 		existing_transno = find_compatible_pertrans(aggstate, aggref,
 													transfn_oid, aggtranstype,
-												  initValue, initValueIsNull,
+												  serialfn_oid, deserialfn_oid,
+													initValue, initValueIsNull,
 													same_input_transnos);
 		if (existing_transno != -1)
 		{
@@ -2723,8 +2861,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 			pertrans = &pertransstates[++transno];
 			build_pertrans_for_aggref(pertrans, aggstate, estate,
 									  aggref, transfn_oid, aggtranstype,
-									  initValue, initValueIsNull,
-									  inputTypes, numArguments);
+									  serialtype_oid, serialfn_oid,
+									  deserialfn_oid, initValue,
+									  initValueIsNull, inputTypes,
+									  numArguments);
 			peragg->transno = transno;
 		}
 		ReleaseSysCache(aggTuple);
@@ -2752,11 +2892,14 @@ static void
 build_pertrans_for_aggref(AggStatePerTrans pertrans,
 						  AggState *aggstate, EState *estate,
 						  Aggref *aggref,
-						  Oid aggtransfn, Oid aggtranstype,
+						  Oid aggtransfn, Oid aggtranstype, Oid aggserialtype,
+						  Oid aggserialfn, Oid aggdeserialfn,
 						  Datum initValue, bool initValueIsNull,
 						  Oid *inputTypes, int numArguments)
 {
 	int			numGroupingSets = Max(aggstate->maxsets, 1);
+	Expr	   *serialfnexpr = NULL;
+	Expr	   *deserialfnexpr = NULL;
 	ListCell   *lc;
 	int			numInputs;
 	int			numDirectArgs;
@@ -2770,6 +2913,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 	pertrans->aggref = aggref;
 	pertrans->aggCollation = aggref->inputcollid;
 	pertrans->transfn_oid = aggtransfn;
+	pertrans->serialfn_oid = aggserialfn;
+	pertrans->deserialfn_oid = aggdeserialfn;
 	pertrans->initValue = initValue;
 	pertrans->initValueIsNull = initValueIsNull;
 
@@ -2861,6 +3006,41 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 					&pertrans->transtypeLen,
 					&pertrans->transtypeByVal);
 
+	if (OidIsValid(aggserialfn))
+	{
+		build_aggregate_serialfn_expr(aggtranstype,
+									  aggserialtype,
+									  aggref->inputcollid,
+									  aggserialfn,
+									  &serialfnexpr);
+		fmgr_info(aggserialfn, &pertrans->serialfn);
+		fmgr_info_set_expr((Node *) serialfnexpr, &pertrans->serialfn);
+
+		InitFunctionCallInfoData(pertrans->serialfn_fcinfo,
+								 &pertrans->serialfn,
+								 1,
+								 pertrans->aggCollation,
+								 (void *) aggstate, NULL);
+	}
+
+	if (OidIsValid(aggdeserialfn))
+	{
+		build_aggregate_serialfn_expr(aggtranstype,
+									  aggserialtype,
+									  aggref->inputcollid,
+									  aggdeserialfn,
+									  &deserialfnexpr);
+		fmgr_info(aggdeserialfn, &pertrans->deserialfn);
+		fmgr_info_set_expr((Node *) deserialfnexpr, &pertrans->deserialfn);
+
+		InitFunctionCallInfoData(pertrans->deserialfn_fcinfo,
+								 &pertrans->deserialfn,
+								 1,
+								 pertrans->aggCollation,
+								 (void *) aggstate, NULL);
+
+	}
+
 	/*
 	 * Get a tupledesc corresponding to the aggregated inputs (including sort
 	 * expressions) of the agg.
@@ -3107,6 +3287,7 @@ find_compatible_peragg(Aggref *newagg, AggState *aggstate,
 static int
 find_compatible_pertrans(AggState *aggstate, Aggref *newagg,
 						 Oid aggtransfn, Oid aggtranstype,
+						 Oid aggserialfn, Oid aggdeserialfn,
 						 Datum initValue, bool initValueIsNull,
 						 List *transnos)
 {
@@ -3125,6 +3306,14 @@ find_compatible_pertrans(AggState *aggstate, Aggref *newagg,
 			aggtranstype != pertrans->aggtranstype)
 			continue;
 
+		/*
+		 * serial and de-serial functions must match, if present. Remember that
+		 * these will be InvalidOid if they're not required for this agg node
+		 */
+		if (aggserialfn != pertrans->serialfn_oid ||
+			aggdeserialfn != pertrans->deserialfn_oid)
+			continue;
+
 		/* Check that the initial condition matches, too. */
 		if (initValueIsNull && pertrans->initValueIsNull)
 			return transno;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index d502aef..b8185d5 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -869,6 +869,7 @@ _copyAgg(const Agg *from)
 	COPY_SCALAR_FIELD(aggstrategy);
 	COPY_SCALAR_FIELD(combineStates);
 	COPY_SCALAR_FIELD(finalizeAggs);
+	COPY_SCALAR_FIELD(serialStates);
 	COPY_SCALAR_FIELD(numCols);
 	if (from->numCols > 0)
 	{
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6e2a6e4..dfbd1b6 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -708,6 +708,7 @@ _outAgg(StringInfo str, const Agg *node)
 	WRITE_ENUM_FIELD(aggstrategy, AggStrategy);
 	WRITE_BOOL_FIELD(combineStates);
 	WRITE_BOOL_FIELD(finalizeAggs);
+	WRITE_BOOL_FIELD(serialStates);
 	WRITE_INT_FIELD(numCols);
 
 	appendStringInfoString(str, " :grpColIdx");
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 61be6c5..8efa261 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -2001,6 +2001,7 @@ _readAgg(void)
 	READ_ENUM_FIELD(aggstrategy, AggStrategy);
 	READ_BOOL_FIELD(combineStates);
 	READ_BOOL_FIELD(finalizeAggs);
+	READ_BOOL_FIELD(serialStates);
 	READ_INT_FIELD(numCols);
 	READ_ATTRNUMBER_ARRAY(grpColIdx, local_node->numCols);
 	READ_OID_ARRAY(grpOperators, local_node->numCols);
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 6953a60..b597a5e 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -1278,6 +1278,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
 								 AGG_HASHED,
 								 false,
 								 true,
+								 false,
 								 numGroupCols,
 								 groupColIdx,
 								 groupOperators,
@@ -1574,6 +1575,7 @@ create_agg_plan(PlannerInfo *root, AggPath *best_path)
 					best_path->aggstrategy,
 					best_path->combineStates,
 					best_path->finalizeAggs,
+					best_path->serialStates,
 					list_length(best_path->groupClause),
 					extract_grouping_cols(best_path->groupClause,
 										  subplan->targetlist),
@@ -1728,6 +1730,7 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path)
 										 AGG_SORTED,
 										 false,
 										 true,
+										 false,
 									   list_length((List *) linitial(gsets)),
 										 new_grpColIdx,
 										 extract_grouping_ops(groupClause),
@@ -1764,6 +1767,7 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path)
 						(numGroupCols > 0) ? AGG_SORTED : AGG_PLAIN,
 						false,
 						true,
+						false,
 						numGroupCols,
 						top_grpColIdx,
 						extract_grouping_ops(groupClause),
@@ -5631,7 +5635,7 @@ materialize_finished_plan(Plan *subplan)
 Agg *
 make_agg(List *tlist, List *qual,
 		 AggStrategy aggstrategy,
-		 bool combineStates, bool finalizeAggs,
+		 bool combineStates, bool finalizeAggs, bool serialStates,
 		 int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators,
 		 List *groupingSets, List *chain,
 		 double dNumGroups, Plan *lefttree)
@@ -5646,6 +5650,7 @@ make_agg(List *tlist, List *qual,
 	node->aggstrategy = aggstrategy;
 	node->combineStates = combineStates;
 	node->finalizeAggs = finalizeAggs;
+	node->serialStates = serialStates;
 	node->numCols = numGroupCols;
 	node->grpColIdx = grpColIdx;
 	node->grpOperators = grpOperators;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 3a80a76..a846963 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -135,7 +135,8 @@ static RelOptInfo *create_ordered_paths(PlannerInfo *root,
 static PathTarget *make_group_input_target(PlannerInfo *root,
 						PathTarget *final_target);
 static PathTarget *make_partialgroup_input_target(PlannerInfo *root,
-												  PathTarget *final_target);
+												  PathTarget *final_target,
+												  bool serialStates);
 static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
 static List *select_active_windows(PlannerInfo *root, WindowFuncLists *wflists);
 static PathTarget *make_window_input_target(PlannerInfo *root,
@@ -3304,7 +3305,8 @@ create_grouping_paths(PlannerInfo *root,
 		{
 			can_parallel = true;
 			partial_group_target = make_partialgroup_input_target(root,
-																  target);
+																  target,
+																  true);
 		}
 	}
 
@@ -3374,7 +3376,8 @@ create_grouping_paths(PlannerInfo *root,
 											 &agg_costs,
 											 dNumGroups,
 											 false,
-											 true));
+											 true,
+											 false));
 				}
 				else if (parse->groupClause)
 				{
@@ -3500,7 +3503,8 @@ create_grouping_paths(PlannerInfo *root,
 								 &agg_costs,
 								 dNumGroups,
 								 false,
-								 true));
+								 true,
+								 false));
 
 		if (can_parallel)
 		{
@@ -3912,7 +3916,8 @@ create_distinct_paths(PlannerInfo *root,
 								 NULL,
 								 numDistinctRows,
 								 false,
-								 true));
+								 true,
+								 false));
 	}
 
 	/* Give a helpful error if we failed to find any implementation */
@@ -4101,11 +4106,13 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
  * PathTarget.
  *
  * Aggrefs are also setup into partial mode and the partial return types are
- * set to become the type of the aggregate transition state rather than the
- * aggregate function's return type.
+ * set to become the type of the aggregate transition state or serialtype,
+ * depending on 'serialStates', rather than the aggregate function's return
+ * type.
  */
 static PathTarget *
-make_partialgroup_input_target(PlannerInfo *root, PathTarget *final_target)
+make_partialgroup_input_target(PlannerInfo *root, PathTarget *final_target,
+							   bool serialStates)
 {
 	Query	   *parse = root->parse;
 	PathTarget *input_target;
@@ -4172,7 +4179,7 @@ make_partialgroup_input_target(PlannerInfo *root, PathTarget *final_target)
 	list_free(non_group_cols);
 
 	/* Adjust Aggrefs to put them in partial mode. */
-	apply_partialaggref_adjustment(input_target);
+	apply_partialaggref_adjustment(input_target, serialStates);
 
 	/* XXX this causes some redundant cost calculation ... */
 	input_target = set_pathtarget_cost_width(root, input_target);
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index fb139af..a1ab4da 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -861,7 +861,8 @@ make_union_unique(SetOperationStmt *op, Path *path, List *tlist,
 										NULL,
 										dNumGroups,
 										false,
-										true);
+										true,
+										false);
 	}
 	else
 	{
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index f315961..7a7b60a 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -410,7 +410,13 @@ make_ands_implicit(Expr *clause)
  *		Recursively search for Aggref clauses and determine the maximum
  *		'degree' of partial aggregation which can be supported. Partial
  *		aggregation requires that each aggregate does not have a DISTINCT or
- *		ORDER BY clause, and that it also has a combine function set.
+ *		ORDER BY clause, and that it also has a combine function set. For
+ *		aggregates with an INTERNAL trans type we only can support all types of
+ *		partial aggregation when the aggregate has a serial and de-serial
+ *		function set. If this is not present then we can only support, at most
+ *		partial aggregation in the context of a single backend process, as
+ *		internal state pointers cannot be dereferenced from another backend
+ *		process.
  */
 PartialAggType
 aggregates_allow_partial(Node *clause)
@@ -466,11 +472,13 @@ aggregates_allow_partial_walker(Node *node, partial_agg_context *context)
 		}
 
 		/*
-		 * If we find any aggs with an internal transtype then we must ensure
-		 * that pointers to aggregate states are not passed to other processes,
-		 * therefore we set the maximum degree to PAT_INTERNAL_ONLY.
+		 * Any aggs with an internal transtype must have a serial type, serial
+		 * func and de-serial func, otherwise we can only support internal mode.
 		 */
-		if (aggform->aggtranstype == INTERNALOID)
+		if (aggform->aggtranstype == INTERNALOID &&
+			(!OidIsValid(aggform->aggserialtype) ||
+			 !OidIsValid(aggform->aggserialfn) ||
+			 !OidIsValid(aggform->aggdeserialfn)))
 			context->allowedtype = PAT_INTERNAL_ONLY;
 
 		ReleaseSysCache(aggTuple);
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index bc86c04..56697f2 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -2401,7 +2401,8 @@ create_agg_path(PlannerInfo *root,
 				const AggClauseCosts *aggcosts,
 				double numGroups,
 				bool combineStates,
-				bool finalizeAggs)
+				bool finalizeAggs,
+				bool serialStates)
 {
 	AggPath	   *pathnode = makeNode(AggPath);
 
@@ -2426,6 +2427,7 @@ create_agg_path(PlannerInfo *root,
 	pathnode->qual = qual;
 	pathnode->finalizeAggs = finalizeAggs;
 	pathnode->combineStates = combineStates;
+	pathnode->serialStates = serialStates;
 
 	cost_agg(&pathnode->path, root,
 			 aggstrategy, aggcosts,
@@ -2493,7 +2495,8 @@ create_parallelagg_path(PlannerInfo *root,
 							   aggcosts,
 							   numGroups,
 							   false,
-							   false);
+							   false,
+							   true);
 
 	gatherpath->path.pathtype = T_Gather;
 	gatherpath->path.parent = rel;
@@ -2549,6 +2552,7 @@ create_parallelagg_path(PlannerInfo *root,
 							   aggcosts,
 							   numGroups,
 							   true,
+							   true,
 							   true);
 
 	return pathnode;
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
index 7509747..5e338b0 100644
--- a/src/backend/optimizer/util/tlist.c
+++ b/src/backend/optimizer/util/tlist.c
@@ -16,6 +16,7 @@
 
 #include "access/htup_details.h"
 #include "catalog/pg_aggregate.h"
+#include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/tlist.h"
@@ -763,7 +764,7 @@ apply_pathtarget_labeling_to_tlist(List *tlist, PathTarget *target)
  * within other expressions.
  */
 void
-apply_partialaggref_adjustment(PathTarget *target)
+apply_partialaggref_adjustment(PathTarget *target, bool serialStates)
 {
 	ListCell *lc;
 
@@ -785,7 +786,17 @@ apply_partialaggref_adjustment(PathTarget *target)
 			aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
 
 			newaggref = (Aggref *) copyObject(aggref);
-			newaggref->aggpartialtype = aggform->aggtranstype;
+
+			/*
+			 * When serialStates is enabled, and the aggregate function has a
+			 * serial type, then the return type of the target item should be
+			 * that of the serial type rather than the aggregate's state type.
+			 */
+			if (serialStates && OidIsValid(aggform->aggserialtype))
+				newaggref->aggpartialtype = aggform->aggserialtype;
+			else
+				newaggref->aggpartialtype = aggform->aggtranstype;
+
 			newaggref->aggpartial = true;
 
 			lfirst(lc) = newaggref;
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 583462a..f02061c 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -1966,6 +1966,80 @@ build_aggregate_combinefn_expr(Oid agg_state_type,
 
 /*
  * Like build_aggregate_transfn_expr, but creates an expression tree for the
+ * serial function of an aggregate, rather than the transition function.
+ */
+void
+build_aggregate_serialfn_expr(Oid agg_state_type,
+							  Oid agg_serial_type,
+							  Oid agg_input_collation,
+							  Oid serialfn_oid,
+							  Expr **serialfnexpr)
+{
+	Param	   *argp;
+	List	   *args;
+	FuncExpr   *fexpr;
+
+	/* Build arg list to use in the serialfn FuncExpr node. */
+	argp = makeNode(Param);
+	argp->paramkind = PARAM_EXEC;
+	argp->paramid = -1;
+	argp->paramtype = agg_state_type;
+	argp->paramtypmod = -1;
+	argp->paramcollid = agg_input_collation;
+	argp->location = -1;
+
+	/* takes a single arg of the transition state type */
+	args = list_make1(argp);
+
+	fexpr = makeFuncExpr(serialfn_oid,
+						 agg_serial_type,
+						 args,
+						 InvalidOid,
+						 agg_input_collation,
+						 COERCE_EXPLICIT_CALL);
+	fexpr->funcvariadic = false;
+	*serialfnexpr = (Expr *) fexpr;
+}
+
+/*
+ * Like build_aggregate_transfn_expr, but creates an expression tree for the
+ * de-serial function of an aggregate, rather than the transition function.
+ */
+void
+build_aggregate_deserialfn_expr(Oid agg_state_type,
+								Oid agg_serial_type,
+								Oid agg_input_collation,
+								Oid deserialfn_oid,
+								Expr **deserialfnexpr)
+{
+	Param	   *argp;
+	List	   *args;
+	FuncExpr   *fexpr;
+
+	/* Build arg list to use in the serialfn FuncExpr node. */
+	argp = makeNode(Param);
+	argp->paramkind = PARAM_EXEC;
+	argp->paramid = -1;
+	argp->paramtype = agg_serial_type;
+	argp->paramtypmod = -1;
+	argp->paramcollid = agg_input_collation;
+	argp->location = -1;
+
+	/* takes a single arg of the serial type */
+	args = list_make1(argp);
+
+	fexpr = makeFuncExpr(deserialfn_oid,
+						 agg_state_type,
+						 args,
+						 InvalidOid,
+						 agg_input_collation,
+						 COERCE_EXPLICIT_CALL);
+	fexpr->funcvariadic = false;
+	*deserialfnexpr = (Expr *) fexpr;
+}
+
+/*
+ * Like build_aggregate_transfn_expr, but creates an expression tree for the
  * final function of an aggregate, rather than the transition function.
  */
 void
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 64c2673..345d3ed 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -12387,6 +12387,8 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	int			i_aggtransfn;
 	int			i_aggfinalfn;
 	int			i_aggcombinefn;
+	int			i_aggserialfn;
+	int			i_aggdeserialfn;
 	int			i_aggmtransfn;
 	int			i_aggminvtransfn;
 	int			i_aggmfinalfn;
@@ -12395,6 +12397,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	int			i_aggsortop;
 	int			i_hypothetical;
 	int			i_aggtranstype;
+	int			i_aggserialtype;
 	int			i_aggtransspace;
 	int			i_aggmtranstype;
 	int			i_aggmtransspace;
@@ -12404,6 +12407,8 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	const char *aggtransfn;
 	const char *aggfinalfn;
 	const char *aggcombinefn;
+	const char *aggserialfn;
+	const char *aggdeserialfn;
 	const char *aggmtransfn;
 	const char *aggminvtransfn;
 	const char *aggmfinalfn;
@@ -12413,6 +12418,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	char	   *aggsortconvop;
 	bool		hypothetical;
 	const char *aggtranstype;
+	const char *aggserialtype;
 	const char *aggtransspace;
 	const char *aggmtranstype;
 	const char *aggmtransspace;
@@ -12438,10 +12444,11 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	{
 		appendPQExpBuffer(query, "SELECT aggtransfn, "
 			"aggfinalfn, aggtranstype::pg_catalog.regtype, "
-			"aggcombinefn, aggmtransfn, "
+			"aggcombinefn, aggserialfn, aggdeserialfn, aggmtransfn, "
 			"aggminvtransfn, aggmfinalfn, aggmtranstype::pg_catalog.regtype, "
 			"aggfinalextra, aggmfinalextra, "
 			"aggsortop::pg_catalog.regoperator, "
+			"aggserialtype::pg_catalog.regtype, "
 			"(aggkind = 'h') AS hypothetical, "
 			"aggtransspace, agginitval, "
 			"aggmtransspace, aggminitval, "
@@ -12457,10 +12464,12 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	{
 		appendPQExpBuffer(query, "SELECT aggtransfn, "
 						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
-						  "'-' AS aggcombinefn, aggmtransfn, aggminvtransfn, "
+						  "'-' AS aggcombinefn, '-' AS aggserialfn, "
+						  "'-' AS aggdeserialfn,aggmtransfn, aggminvtransfn, "
 						  "aggmfinalfn, aggmtranstype::pg_catalog.regtype, "
 						  "aggfinalextra, aggmfinalextra, "
 						  "aggsortop::pg_catalog.regoperator, "
+						  "0 AS aggserialtype, "
 						  "(aggkind = 'h') AS hypothetical, "
 						  "aggtransspace, agginitval, "
 						  "aggmtransspace, aggminitval, "
@@ -12476,11 +12485,13 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	{
 		appendPQExpBuffer(query, "SELECT aggtransfn, "
 						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
-						  "'-' AS aggcombinefn, '-' AS aggmtransfn, "
+						  "'-' AS aggcombinefn, '-' AS aggserialfn, "
+						  "'-' AS aggdeserialfn, '-' AS aggmtransfn, "
 						  "'-' AS aggminvtransfn, '-' AS aggmfinalfn, "
 						  "0 AS aggmtranstype, false AS aggfinalextra, "
 						  "false AS aggmfinalextra, "
 						  "aggsortop::pg_catalog.regoperator, "
+						  "0 AS aggserialtype, "
 						  "false AS hypothetical, "
 						  "0 AS aggtransspace, agginitval, "
 						  "0 AS aggmtransspace, NULL AS aggminitval, "
@@ -12496,11 +12507,13 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	{
 		appendPQExpBuffer(query, "SELECT aggtransfn, "
 						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
-						  "'-' AS aggcombinefn, '-' AS aggmtransfn, "
+						  "'-' AS aggcombinefn, '-' AS aggserialfn, "
+						  "'-' AS aggdeserialfn, '-' AS aggmtransfn, "
 						  "'-' AS aggminvtransfn, '-' AS aggmfinalfn, "
 						  "0 AS aggmtranstype, false AS aggfinalextra, "
 						  "false AS aggmfinalextra, "
 						  "aggsortop::pg_catalog.regoperator, "
+						  "0 AS aggserialtype, "
 						  "false AS hypothetical, "
 						  "0 AS aggtransspace, agginitval, "
 						  "0 AS aggmtransspace, NULL AS aggminitval, "
@@ -12514,10 +12527,12 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	{
 		appendPQExpBuffer(query, "SELECT aggtransfn, "
 						  "aggfinalfn, aggtranstype::pg_catalog.regtype, "
-						  "'-' AS aggcombinefn, '-' AS aggmtransfn, "
+						  "'-' AS aggcombinefn, '-' AS aggserialfn, "
+						  "'-' AS aggdeserialfn, '-' AS aggmtransfn, "
 						  "'-' AS aggminvtransfn, '-' AS aggmfinalfn, "
 						  "0 AS aggmtranstype, false AS aggfinalextra, "
 						  "false AS aggmfinalextra, 0 AS aggsortop, "
+						  "0 AS aggserialtype, "
 						  "false AS hypothetical, "
 						  "0 AS aggtransspace, agginitval, "
 						  "0 AS aggmtransspace, NULL AS aggminitval, "
@@ -12531,10 +12546,12 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	{
 		appendPQExpBuffer(query, "SELECT aggtransfn, aggfinalfn, "
 						  "format_type(aggtranstype, NULL) AS aggtranstype, "
-						  "'-' AS aggcombinefn, '-' AS aggmtransfn, "
+						  "'-' AS aggcombinefn, '-' AS aggserialfn, "
+						  "'-' AS aggdeserialfn, '-' AS aggmtransfn, "
 						  "'-' AS aggminvtransfn, '-' AS aggmfinalfn, "
 						  "0 AS aggmtranstype, false AS aggfinalextra, "
 						  "false AS aggmfinalextra, 0 AS aggsortop, "
+						  "0 AS aggserialtype, "
 						  "false AS hypothetical, "
 						  "0 AS aggtransspace, agginitval, "
 						  "0 AS aggmtransspace, NULL AS aggminitval, "
@@ -12548,10 +12565,12 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 		appendPQExpBuffer(query, "SELECT aggtransfn1 AS aggtransfn, "
 						  "aggfinalfn, "
 						  "(SELECT typname FROM pg_type WHERE oid = aggtranstype1) AS aggtranstype, "
-						  "'-' AS aggcombinefn, '-' AS aggmtransfn, "
+						  "'-' AS aggcombinefn, '-' AS aggserialfn, "
+						  "'-' AS aggdeserialfn, '-' AS aggmtransfn, "
 						  "'-' AS aggminvtransfn, '-' AS aggmfinalfn, "
 						  "0 AS aggmtranstype, false AS aggfinalextra, "
 						  "false AS aggmfinalextra, 0 AS aggsortop, "
+						  "0 AS aggserialtype, "
 						  "false AS hypothetical, "
 						  "0 AS aggtransspace, agginitval1 AS agginitval, "
 						  "0 AS aggmtransspace, NULL AS aggminitval, "
@@ -12566,12 +12585,15 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	i_aggtransfn = PQfnumber(res, "aggtransfn");
 	i_aggfinalfn = PQfnumber(res, "aggfinalfn");
 	i_aggcombinefn = PQfnumber(res, "aggcombinefn");
+	i_aggserialfn = PQfnumber(res, "aggserialfn");
+	i_aggdeserialfn = PQfnumber(res, "aggdeserialfn");
 	i_aggmtransfn = PQfnumber(res, "aggmtransfn");
 	i_aggminvtransfn = PQfnumber(res, "aggminvtransfn");
 	i_aggmfinalfn = PQfnumber(res, "aggmfinalfn");
 	i_aggfinalextra = PQfnumber(res, "aggfinalextra");
 	i_aggmfinalextra = PQfnumber(res, "aggmfinalextra");
 	i_aggsortop = PQfnumber(res, "aggsortop");
+	i_aggserialtype = PQfnumber(res, "aggserialtype");
 	i_hypothetical = PQfnumber(res, "hypothetical");
 	i_aggtranstype = PQfnumber(res, "aggtranstype");
 	i_aggtransspace = PQfnumber(res, "aggtransspace");
@@ -12584,6 +12606,8 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	aggtransfn = PQgetvalue(res, 0, i_aggtransfn);
 	aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn);
 	aggcombinefn = PQgetvalue(res, 0, i_aggcombinefn);
+	aggserialfn = PQgetvalue(res, 0, i_aggserialfn);
+	aggdeserialfn = PQgetvalue(res, 0, i_aggdeserialfn);
 	aggmtransfn = PQgetvalue(res, 0, i_aggmtransfn);
 	aggminvtransfn = PQgetvalue(res, 0, i_aggminvtransfn);
 	aggmfinalfn = PQgetvalue(res, 0, i_aggmfinalfn);
@@ -12592,6 +12616,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 	aggsortop = PQgetvalue(res, 0, i_aggsortop);
 	hypothetical = (PQgetvalue(res, 0, i_hypothetical)[0] == 't');
 	aggtranstype = PQgetvalue(res, 0, i_aggtranstype);
+	aggserialtype = PQgetvalue(res, 0, i_aggserialtype);
 	aggtransspace = PQgetvalue(res, 0, i_aggtransspace);
 	aggmtranstype = PQgetvalue(res, 0, i_aggmtranstype);
 	aggmtransspace = PQgetvalue(res, 0, i_aggmtransspace);
@@ -12677,6 +12702,17 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
 		appendPQExpBuffer(details, ",\n    COMBINEFUNC = %s",	aggcombinefn);
 	}
 
+	/*
+	 * CREATE AGGREGATE should ensure we either have all of these, or none of
+	 * them.
+	 */
+	if (strcmp(aggserialfn, "-") != 0)
+	{
+		appendPQExpBuffer(details, ",\n    SERIALFUNC = %s",	aggserialfn);
+		appendPQExpBuffer(details, ",\n    DESERIALFUNC = %s",	aggdeserialfn);
+		appendPQExpBuffer(details, ",\n    SERIALTYPE = %s",	aggserialtype);
+	}
+
 	if (strcmp(aggmtransfn, "-") != 0)
 	{
 		appendPQExpBuffer(details, ",\n    MSFUNC = %s,\n    MINVFUNC = %s,\n    MSTYPE = %s",
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 441db30..47d8c92 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -34,6 +34,8 @@
  *	aggtransfn			transition function
  *	aggfinalfn			final function (0 if none)
  *	aggcombinefn		combine function (0 if none)
+ *	aggserialfn			function to convert transtype into serialtype
+ *	aggdeserialfn		function to convert serialtype into transtype
  *	aggmtransfn			forward function for moving-aggregate mode (0 if none)
  *	aggminvtransfn		inverse function for moving-aggregate mode (0 if none)
  *	aggmfinalfn			final function for moving-aggregate mode (0 if none)
@@ -43,6 +45,7 @@
  *	aggtranstype		type of aggregate's transition (state) data
  *	aggtransspace		estimated size of state data (0 for default estimate)
  *	aggmtranstype		type of moving-aggregate state data (0 if none)
+ *	aggserialtype		datatype to serialize state to. (0 if none)
  *	aggmtransspace		estimated size of moving-agg state (0 for default est)
  *	agginitval			initial value for transition state (can be NULL)
  *	aggminitval			initial value for moving-agg state (can be NULL)
@@ -58,6 +61,8 @@ CATALOG(pg_aggregate,2600) BKI_WITHOUT_OIDS
 	regproc		aggtransfn;
 	regproc		aggfinalfn;
 	regproc		aggcombinefn;
+	regproc		aggserialfn;
+	regproc		aggdeserialfn;
 	regproc		aggmtransfn;
 	regproc		aggminvtransfn;
 	regproc		aggmfinalfn;
@@ -65,6 +70,7 @@ CATALOG(pg_aggregate,2600) BKI_WITHOUT_OIDS
 	bool		aggmfinalextra;
 	Oid			aggsortop;
 	Oid			aggtranstype;
+	Oid			aggserialtype;
 	int32		aggtransspace;
 	Oid			aggmtranstype;
 	int32		aggmtransspace;
@@ -87,25 +93,28 @@ typedef FormData_pg_aggregate *Form_pg_aggregate;
  * ----------------
  */
 
-#define Natts_pg_aggregate					18
+#define Natts_pg_aggregate					21
 #define Anum_pg_aggregate_aggfnoid			1
 #define Anum_pg_aggregate_aggkind			2
 #define Anum_pg_aggregate_aggnumdirectargs	3
 #define Anum_pg_aggregate_aggtransfn		4
 #define Anum_pg_aggregate_aggfinalfn		5
 #define Anum_pg_aggregate_aggcombinefn		6
-#define Anum_pg_aggregate_aggmtransfn		7
-#define Anum_pg_aggregate_aggminvtransfn	8
-#define Anum_pg_aggregate_aggmfinalfn		9
-#define Anum_pg_aggregate_aggfinalextra		10
-#define Anum_pg_aggregate_aggmfinalextra	11
-#define Anum_pg_aggregate_aggsortop			12
-#define Anum_pg_aggregate_aggtranstype		13
-#define Anum_pg_aggregate_aggtransspace		14
-#define Anum_pg_aggregate_aggmtranstype		15
-#define Anum_pg_aggregate_aggmtransspace	16
-#define Anum_pg_aggregate_agginitval		17
-#define Anum_pg_aggregate_aggminitval		18
+#define Anum_pg_aggregate_aggserialfn		7
+#define Anum_pg_aggregate_aggdeserialfn		8
+#define Anum_pg_aggregate_aggmtransfn		9
+#define Anum_pg_aggregate_aggminvtransfn	10
+#define Anum_pg_aggregate_aggmfinalfn		11
+#define Anum_pg_aggregate_aggfinalextra		12
+#define Anum_pg_aggregate_aggmfinalextra	13
+#define Anum_pg_aggregate_aggsortop			14
+#define Anum_pg_aggregate_aggtranstype		15
+#define Anum_pg_aggregate_aggserialtype		16
+#define Anum_pg_aggregate_aggtransspace		17
+#define Anum_pg_aggregate_aggmtranstype		18
+#define Anum_pg_aggregate_aggmtransspace	19
+#define Anum_pg_aggregate_agginitval		20
+#define Anum_pg_aggregate_aggminitval		21
 
 /*
  * Symbolic values for aggkind column.  We distinguish normal aggregates
@@ -129,184 +138,184 @@ typedef FormData_pg_aggregate *Form_pg_aggregate;
  */
 
 /* avg */
-DATA(insert ( 2100	n 0 int8_avg_accum	numeric_poly_avg	-	int8_avg_accum	int8_avg_accum_inv	numeric_poly_avg	f f 0	2281	48	2281	48	_null_ _null_ ));
-DATA(insert ( 2101	n 0 int4_avg_accum	int8_avg			-	int4_avg_accum	int4_avg_accum_inv	int8_avg			f f 0	1016	0	1016	0	"{0,0}" "{0,0}" ));
-DATA(insert ( 2102	n 0 int2_avg_accum	int8_avg			-	int2_avg_accum	int2_avg_accum_inv	int8_avg			f f 0	1016	0	1016	0	"{0,0}" "{0,0}" ));
-DATA(insert ( 2103	n 0 numeric_avg_accum numeric_avg		-	numeric_avg_accum numeric_accum_inv numeric_avg			f f 0	2281	128 2281	128 _null_ _null_ ));
-DATA(insert ( 2104	n 0 float4_accum	float8_avg			-	-				-				-						f f 0	1022	0	0		0	"{0,0,0}" _null_ ));
-DATA(insert ( 2105	n 0 float8_accum	float8_avg			-	-				-				-						f f 0	1022	0	0		0	"{0,0,0}" _null_ ));
-DATA(insert ( 2106	n 0 interval_accum	interval_avg		-	interval_accum	interval_accum_inv interval_avg			f f 0	1187	0	1187	0	"{0 second,0 second}" "{0 second,0 second}" ));
+DATA(insert ( 2100	n 0 int8_avg_accum	numeric_poly_avg	-					-	-	int8_avg_accum	int8_avg_accum_inv	numeric_poly_avg	f f 0	2281	0	48	2281	48	_null_ _null_ ));
+DATA(insert ( 2101	n 0 int4_avg_accum	int8_avg			-					-	-	int4_avg_accum	int4_avg_accum_inv	int8_avg			f f 0	1016	0	0	1016	0	"{0,0}" "{0,0}" ));
+DATA(insert ( 2102	n 0 int2_avg_accum	int8_avg			-					-	-	int2_avg_accum	int2_avg_accum_inv	int8_avg			f f 0	1016	0	0	1016	0	"{0,0}" "{0,0}" ));
+DATA(insert ( 2103	n 0 numeric_avg_accum numeric_avg		-					-	-	numeric_avg_accum numeric_accum_inv numeric_avg			f f 0	2281	17	128 2281	128 _null_ _null_ ));
+DATA(insert ( 2104	n 0 float4_accum	float8_avg			-					-	-	-				-				-						f f 0	1022	0	0	0		0	"{0,0,0}" _null_ ));
+DATA(insert ( 2105	n 0 float8_accum	float8_avg			-					-	-	-				-				-						f f 0	1022	0	0	0		0	"{0,0,0}" _null_ ));
+DATA(insert ( 2106	n 0 interval_accum	interval_avg		-					-	-	interval_accum	interval_accum_inv interval_avg			f f 0	1187	0	0	1187	0	"{0 second,0 second}" "{0 second,0 second}" ));
 
 /* sum */
-DATA(insert ( 2107	n 0 int8_avg_accum	numeric_poly_sum	-					int8_avg_accum	int8_avg_accum_inv	numeric_poly_sum	f f 0	2281	48	2281	48	_null_ _null_ ));
-DATA(insert ( 2108	n 0 int4_sum		-					int8pl				int4_avg_accum	int4_avg_accum_inv	int2int4_sum		f f 0	20		0	1016	0	_null_ "{0,0}" ));
-DATA(insert ( 2109	n 0 int2_sum		-					int8pl				int2_avg_accum	int2_avg_accum_inv	int2int4_sum		f f 0	20		0	1016	0	_null_ "{0,0}" ));
-DATA(insert ( 2110	n 0 float4pl		-					float4pl			-				-					-					f f 0	700		0	0		0	_null_ _null_ ));
-DATA(insert ( 2111	n 0 float8pl		-					float8pl			-				-					-					f f 0	701		0	0		0	_null_ _null_ ));
-DATA(insert ( 2112	n 0 cash_pl			-					cash_pl				cash_pl			cash_mi				-					f f 0	790		0	790		0	_null_ _null_ ));
-DATA(insert ( 2113	n 0 interval_pl		-					interval_pl			interval_pl		interval_mi			-					f f 0	1186	0	1186	0	_null_ _null_ ));
-DATA(insert ( 2114	n 0 numeric_avg_accum	numeric_sum		-					numeric_avg_accum	numeric_accum_inv	numeric_sum		f f 0	2281	128 2281	128 _null_ _null_ ));
+DATA(insert ( 2107	n 0 int8_avg_accum	numeric_poly_sum	-					-	-	int8_avg_accum	int8_avg_accum_inv	numeric_poly_sum	f f 0	2281	0	48	2281	48	_null_ _null_ ));
+DATA(insert ( 2108	n 0 int4_sum		-					int8pl				-	-	int4_avg_accum	int4_avg_accum_inv	int2int4_sum		f f 0	20		0	0	1016	0	_null_ "{0,0}" ));
+DATA(insert ( 2109	n 0 int2_sum		-					int8pl				-	-	int2_avg_accum	int2_avg_accum_inv	int2int4_sum		f f 0	20		0	0	1016	0	_null_ "{0,0}" ));
+DATA(insert ( 2110	n 0 float4pl		-					float4pl			-	-	-				-					-					f f 0	700		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2111	n 0 float8pl		-					float8pl			-	-	-				-					-					f f 0	701		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2112	n 0 cash_pl			-					cash_pl				-	-	cash_pl			cash_mi				-					f f 0	790		0	0	790		0	_null_ _null_ ));
+DATA(insert ( 2113	n 0 interval_pl		-					interval_pl			-	-	interval_pl		interval_mi			-					f f 0	1186	0	0	1186	0	_null_ _null_ ));
+DATA(insert ( 2114	n 0 numeric_avg_accum	numeric_sum		-					-	-	numeric_avg_accum	numeric_accum_inv	numeric_sum		f f 0	2281	0	128 2281	128 _null_ _null_ ));
 
 /* max */
-DATA(insert ( 2115	n 0 int8larger		-				int8larger			-				-				-				f f 413		20		0	0		0	_null_ _null_ ));
-DATA(insert ( 2116	n 0 int4larger		-				int4larger			-				-				-				f f 521		23		0	0		0	_null_ _null_ ));
-DATA(insert ( 2117	n 0 int2larger		-				int2larger			-				-				-				f f 520		21		0	0		0	_null_ _null_ ));
-DATA(insert ( 2118	n 0 oidlarger		-				oidlarger			-				-				-				f f 610		26		0	0		0	_null_ _null_ ));
-DATA(insert ( 2119	n 0 float4larger	-				float4larger		-				-				-				f f 623		700		0	0		0	_null_ _null_ ));
-DATA(insert ( 2120	n 0 float8larger	-				float8larger		-				-				-				f f 674		701		0	0		0	_null_ _null_ ));
-DATA(insert ( 2121	n 0 int4larger		-				int4larger			-				-				-				f f 563		702		0	0		0	_null_ _null_ ));
-DATA(insert ( 2122	n 0 date_larger		-				date_larger			-				-				-				f f 1097	1082	0	0		0	_null_ _null_ ));
-DATA(insert ( 2123	n 0 time_larger		-				time_larger			-				-				-				f f 1112	1083	0	0		0	_null_ _null_ ));
-DATA(insert ( 2124	n 0 timetz_larger	-				timetz_larger		-				-				-				f f 1554	1266	0	0		0	_null_ _null_ ));
-DATA(insert ( 2125	n 0 cashlarger		-				cashlarger			-				-				-				f f 903		790		0	0		0	_null_ _null_ ));
-DATA(insert ( 2126	n 0 timestamp_larger	-			timestamp_larger	-				-				-				f f 2064	1114	0	0		0	_null_ _null_ ));
-DATA(insert ( 2127	n 0 timestamptz_larger	-			timestamptz_larger	-				-				-				f f 1324	1184	0	0		0	_null_ _null_ ));
-DATA(insert ( 2128	n 0 interval_larger -				interval_larger		-				-				-				f f 1334	1186	0	0		0	_null_ _null_ ));
-DATA(insert ( 2129	n 0 text_larger		-				text_larger			-				-				-				f f 666		25		0	0		0	_null_ _null_ ));
-DATA(insert ( 2130	n 0 numeric_larger	-				numeric_larger		-				-				-				f f 1756	1700	0	0		0	_null_ _null_ ));
-DATA(insert ( 2050	n 0 array_larger	-				array_larger		-				-				-				f f 1073	2277	0	0		0	_null_ _null_ ));
-DATA(insert ( 2244	n 0 bpchar_larger	-				bpchar_larger		-				-				-				f f 1060	1042	0	0		0	_null_ _null_ ));
-DATA(insert ( 2797	n 0 tidlarger		-				tidlarger			-				-				-				f f 2800	27		0	0		0	_null_ _null_ ));
-DATA(insert ( 3526	n 0 enum_larger		-				enum_larger			-				-				-				f f 3519	3500	0	0		0	_null_ _null_ ));
-DATA(insert ( 3564	n 0 network_larger	-				network_larger		-				-				-				f f 1205	869		0	0		0	_null_ _null_ ));
+DATA(insert ( 2115	n 0 int8larger		-				int8larger			-	-	-				-				-				f f 413		20		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2116	n 0 int4larger		-				int4larger			-	-	-				-				-				f f 521		23		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2117	n 0 int2larger		-				int2larger			-	-	-				-				-				f f 520		21		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2118	n 0 oidlarger		-				oidlarger			-	-	-				-				-				f f 610		26		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2119	n 0 float4larger	-				float4larger		-	-	-				-				-				f f 623		700		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2120	n 0 float8larger	-				float8larger		-	-	-				-				-				f f 674		701		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2121	n 0 int4larger		-				int4larger			-	-	-				-				-				f f 563		702		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2122	n 0 date_larger		-				date_larger			-	-	-				-				-				f f 1097	1082	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2123	n 0 time_larger		-				time_larger			-	-	-				-				-				f f 1112	1083	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2124	n 0 timetz_larger	-				timetz_larger		-	-	-				-				-				f f 1554	1266	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2125	n 0 cashlarger		-				cashlarger			-	-	-				-				-				f f 903		790		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2126	n 0 timestamp_larger	-			timestamp_larger	-	-	-				-				-				f f 2064	1114	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2127	n 0 timestamptz_larger	-			timestamptz_larger	-	-	-				-				-				f f 1324	1184	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2128	n 0 interval_larger -				interval_larger		-	-	-				-				-				f f 1334	1186	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2129	n 0 text_larger		-				text_larger			-	-	-				-				-				f f 666		25		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2130	n 0 numeric_larger	-				numeric_larger		-	-	-				-				-				f f 1756	1700	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2050	n 0 array_larger	-				array_larger		-	-	-				-				-				f f 1073	2277	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2244	n 0 bpchar_larger	-				bpchar_larger		-	-	-				-				-				f f 1060	1042	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2797	n 0 tidlarger		-				tidlarger			-	-	-				-				-				f f 2800	27		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 3526	n 0 enum_larger		-				enum_larger			-	-	-				-				-				f f 3519	3500	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 3564	n 0 network_larger	-				network_larger		-	-	-				-				-				f f 1205	869		0	0	0		0	_null_ _null_ ));
 
 /* min */
-DATA(insert ( 2131	n 0 int8smaller		-				int8smaller			-				-				-				f f 412		20		0	0		0	_null_ _null_ ));
-DATA(insert ( 2132	n 0 int4smaller		-				int4smaller			-				-				-				f f 97		23		0	0		0	_null_ _null_ ));
-DATA(insert ( 2133	n 0 int2smaller		-				int2smaller			-				-				-				f f 95		21		0	0		0	_null_ _null_ ));
-DATA(insert ( 2134	n 0 oidsmaller		-				oidsmaller			-				-				-				f f 609		26		0	0		0	_null_ _null_ ));
-DATA(insert ( 2135	n 0 float4smaller	-				float4smaller		-				-				-				f f 622		700		0	0		0	_null_ _null_ ));
-DATA(insert ( 2136	n 0 float8smaller	-				float8smaller		-				-				-				f f 672		701		0	0		0	_null_ _null_ ));
-DATA(insert ( 2137	n 0 int4smaller		-				int4smaller			-				-				-				f f 562		702		0	0		0	_null_ _null_ ));
-DATA(insert ( 2138	n 0 date_smaller	-				date_smaller		-				-				-				f f 1095	1082	0	0		0	_null_ _null_ ));
-DATA(insert ( 2139	n 0 time_smaller	-				time_smaller		-				-				-				f f 1110	1083	0	0		0	_null_ _null_ ));
-DATA(insert ( 2140	n 0 timetz_smaller	-				timetz_smaller		-				-				-				f f 1552	1266	0	0		0	_null_ _null_ ));
-DATA(insert ( 2141	n 0 cashsmaller		-				cashsmaller			-				-				-				f f 902		790		0	0		0	_null_ _null_ ));
-DATA(insert ( 2142	n 0 timestamp_smaller	-			timestamp_smaller	-				-				-				f f 2062	1114	0	0		0	_null_ _null_ ));
-DATA(insert ( 2143	n 0 timestamptz_smaller -			timestamptz_smaller	-				-				-				f f 1322	1184	0	0		0	_null_ _null_ ));
-DATA(insert ( 2144	n 0 interval_smaller	-			interval_smaller	-				-				-				f f 1332	1186	0	0		0	_null_ _null_ ));
-DATA(insert ( 2145	n 0 text_smaller	-				text_smaller		-				-				-				f f 664		25		0	0		0	_null_ _null_ ));
-DATA(insert ( 2146	n 0 numeric_smaller -				numeric_smaller		-				-				-				f f 1754	1700	0	0		0	_null_ _null_ ));
-DATA(insert ( 2051	n 0 array_smaller	-				array_smaller		-				-				-				f f 1072	2277	0	0		0	_null_ _null_ ));
-DATA(insert ( 2245	n 0 bpchar_smaller	-				bpchar_smaller		-				-				-				f f 1058	1042	0	0		0	_null_ _null_ ));
-DATA(insert ( 2798	n 0 tidsmaller		-				tidsmaller			-				-				-				f f 2799	27		0	0		0	_null_ _null_ ));
-DATA(insert ( 3527	n 0 enum_smaller	-				enum_smaller		-				-				-				f f 3518	3500	0	0		0	_null_ _null_ ));
-DATA(insert ( 3565	n 0 network_smaller -				network_smaller		-				-				-				f f 1203	869		0	0		0	_null_ _null_ ));
+DATA(insert ( 2131	n 0 int8smaller		-				int8smaller			-	-	-				-				-				f f 412		20		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2132	n 0 int4smaller		-				int4smaller			-	-	-				-				-				f f 97		23		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2133	n 0 int2smaller		-				int2smaller			-	-	-				-				-				f f 95		21		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2134	n 0 oidsmaller		-				oidsmaller			-	-	-				-				-				f f 609		26		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2135	n 0 float4smaller	-				float4smaller		-	-	-				-				-				f f 622		700		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2136	n 0 float8smaller	-				float8smaller		-	-	-				-				-				f f 672		701		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2137	n 0 int4smaller		-				int4smaller			-	-	-				-				-				f f 562		702		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2138	n 0 date_smaller	-				date_smaller		-	-	-				-				-				f f 1095	1082	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2139	n 0 time_smaller	-				time_smaller		-	-	-				-				-				f f 1110	1083	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2140	n 0 timetz_smaller	-				timetz_smaller		-	-	-				-				-				f f 1552	1266	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2141	n 0 cashsmaller		-				cashsmaller			-	-	-				-				-				f f 902		790		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2142	n 0 timestamp_smaller	-			timestamp_smaller	-	-	-				-				-				f f 2062	1114	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2143	n 0 timestamptz_smaller -			timestamptz_smaller	-	-	-				-				-				f f 1322	1184	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2144	n 0 interval_smaller	-			interval_smaller	-	-	-				-				-				f f 1332	1186	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2145	n 0 text_smaller	-				text_smaller		-	-	-				-				-				f f 664		25		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2146	n 0 numeric_smaller -				numeric_smaller		-	-	-				-				-				f f 1754	1700	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2051	n 0 array_smaller	-				array_smaller		-	-	-				-				-				f f 1072	2277	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2245	n 0 bpchar_smaller	-				bpchar_smaller		-	-	-				-				-				f f 1058	1042	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2798	n 0 tidsmaller		-				tidsmaller			-	-	-				-				-				f f 2799	27		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 3527	n 0 enum_smaller	-				enum_smaller		-	-	-				-				-				f f 3518	3500	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 3565	n 0 network_smaller -				network_smaller		-	-	-				-				-				f f 1203	869		0	0	0		0	_null_ _null_ ));
 
 /* count */
-DATA(insert ( 2147	n 0 int8inc_any		-				int8pl	int8inc_any		int8dec_any		-				f f 0		20		0	20		0	"0" "0" ));
-DATA(insert ( 2803	n 0 int8inc			-				int8pl	int8inc			int8dec			-				f f 0		20		0	20		0	"0" "0" ));
+DATA(insert ( 2147	n 0 int8inc_any		-				int8pl	-	-	int8inc_any		int8dec_any		-				f f 0		20		0	0	20		0	"0" "0" ));
+DATA(insert ( 2803	n 0 int8inc			-				int8pl	-	-	int8inc			int8dec			-				f f 0		20		0	0	20		0	"0" "0" ));
 
 /* var_pop */
-DATA(insert ( 2718	n 0 int8_accum	numeric_var_pop			-	int8_accum		int8_accum_inv	numeric_var_pop			f f 0	2281	128 2281	128 _null_ _null_ ));
-DATA(insert ( 2719	n 0 int4_accum	numeric_poly_var_pop	-	int4_accum		int4_accum_inv	numeric_poly_var_pop	f f 0	2281	48	2281	48	_null_ _null_ ));
-DATA(insert ( 2720	n 0 int2_accum	numeric_poly_var_pop	-	int2_accum		int2_accum_inv	numeric_poly_var_pop	f f 0	2281	48	2281	48	_null_ _null_ ));
-DATA(insert ( 2721	n 0 float4_accum	float8_var_pop		-	-				-				-						f f 0	1022	0	0		0	"{0,0,0}" _null_ ));
-DATA(insert ( 2722	n 0 float8_accum	float8_var_pop		-	-				-				-						f f 0	1022	0	0		0	"{0,0,0}" _null_ ));
-DATA(insert ( 2723	n 0 numeric_accum	numeric_var_pop		-	numeric_accum numeric_accum_inv numeric_var_pop			f f 0	2281	128 2281	128 _null_ _null_ ));
+DATA(insert ( 2718	n 0 int8_accum	numeric_var_pop			-	-	-	int8_accum		int8_accum_inv	numeric_var_pop			f f 0	2281	0	128 2281	128 _null_ _null_ ));
+DATA(insert ( 2719	n 0 int4_accum	numeric_poly_var_pop	-	-	-	int4_accum		int4_accum_inv	numeric_poly_var_pop	f f 0	2281	0	48	2281	48	_null_ _null_ ));
+DATA(insert ( 2720	n 0 int2_accum	numeric_poly_var_pop	-	-	-	int2_accum		int2_accum_inv	numeric_poly_var_pop	f f 0	2281	0	48	2281	48	_null_ _null_ ));
+DATA(insert ( 2721	n 0 float4_accum	float8_var_pop		-	-	-	-				-				-						f f 0	1022	0	0	0		0	"{0,0,0}" _null_ ));
+DATA(insert ( 2722	n 0 float8_accum	float8_var_pop		-	-	-	-				-				-						f f 0	1022	0	0	0		0	"{0,0,0}" _null_ ));
+DATA(insert ( 2723	n 0 numeric_accum	numeric_var_pop		-	-	-	numeric_accum numeric_accum_inv numeric_var_pop			f f 0	2281	0	128 2281	128 _null_ _null_ ));
 
 /* var_samp */
-DATA(insert ( 2641	n 0 int8_accum	numeric_var_samp		-	int8_accum		int8_accum_inv	numeric_var_samp		f f 0	2281	128 2281	128 _null_ _null_ ));
-DATA(insert ( 2642	n 0 int4_accum	numeric_poly_var_samp	-	int4_accum		int4_accum_inv	numeric_poly_var_samp	f f 0	2281	48	2281	48	_null_ _null_ ));
-DATA(insert ( 2643	n 0 int2_accum	numeric_poly_var_samp	-	int2_accum		int2_accum_inv	numeric_poly_var_samp	f f 0	2281	48	2281	48	_null_ _null_ ));
-DATA(insert ( 2644	n 0 float4_accum	float8_var_samp		-	-				-				-						f f 0	1022	0	0		0	"{0,0,0}" _null_ ));
-DATA(insert ( 2645	n 0 float8_accum	float8_var_samp		-	-				-				-						f f 0	1022	0	0		0	"{0,0,0}" _null_ ));
-DATA(insert ( 2646	n 0 numeric_accum	numeric_var_samp	-	numeric_accum	numeric_accum_inv numeric_var_samp		f f 0	2281	128 2281	128 _null_ _null_ ));
+DATA(insert ( 2641	n 0 int8_accum	numeric_var_samp		-	-	-	int8_accum		int8_accum_inv	numeric_var_samp		f f 0	2281	0	128 2281	128 _null_ _null_ ));
+DATA(insert ( 2642	n 0 int4_accum	numeric_poly_var_samp	-	-	-	int4_accum		int4_accum_inv	numeric_poly_var_samp	f f 0	2281	0	48	2281	48	_null_ _null_ ));
+DATA(insert ( 2643	n 0 int2_accum	numeric_poly_var_samp	-	-	-	int2_accum		int2_accum_inv	numeric_poly_var_samp	f f 0	2281	0	48	2281	48	_null_ _null_ ));
+DATA(insert ( 2644	n 0 float4_accum	float8_var_samp		-	-	-	-				-				-						f f 0	1022	0	0	0		0	"{0,0,0}" _null_ ));
+DATA(insert ( 2645	n 0 float8_accum	float8_var_samp		-	-	-	-				-				-						f f 0	1022	0	0	0		0	"{0,0,0}" _null_ ));
+DATA(insert ( 2646	n 0 numeric_accum	numeric_var_samp	-	-	-	numeric_accum	numeric_accum_inv numeric_var_samp		f f 0	2281	0	128 2281	128 _null_ _null_ ));
 
 /* variance: historical Postgres syntax for var_samp */
-DATA(insert ( 2148	n 0 int8_accum	numeric_var_samp		-	int8_accum		int8_accum_inv	numeric_var_samp		f f 0	2281	128 2281	128 _null_ _null_ ));
-DATA(insert ( 2149	n 0 int4_accum	numeric_poly_var_samp	-	int4_accum		int4_accum_inv	numeric_poly_var_samp	f f 0	2281	48	2281	48	_null_ _null_ ));
-DATA(insert ( 2150	n 0 int2_accum	numeric_poly_var_samp	-	int2_accum		int2_accum_inv	numeric_poly_var_samp	f f 0	2281	48	2281	48	_null_ _null_ ));
-DATA(insert ( 2151	n 0 float4_accum	float8_var_samp		-	-				-				-						f f 0	1022	0	0		0	"{0,0,0}" _null_ ));
-DATA(insert ( 2152	n 0 float8_accum	float8_var_samp		-	-				-				-						f f 0	1022	0	0		0	"{0,0,0}" _null_ ));
-DATA(insert ( 2153	n 0 numeric_accum	numeric_var_samp	-	numeric_accum numeric_accum_inv numeric_var_samp		f f 0	2281	128 2281	128 _null_ _null_ ));
+DATA(insert ( 2148	n 0 int8_accum	numeric_var_samp		-	-	-	int8_accum		int8_accum_inv	numeric_var_samp		f f 0	2281	0	128 2281	128 _null_ _null_ ));
+DATA(insert ( 2149	n 0 int4_accum	numeric_poly_var_samp	-	-	-	int4_accum		int4_accum_inv	numeric_poly_var_samp	f f 0	2281	0	48	2281	48	_null_ _null_ ));
+DATA(insert ( 2150	n 0 int2_accum	numeric_poly_var_samp	-	-	-	int2_accum		int2_accum_inv	numeric_poly_var_samp	f f 0	2281	0	48	2281	48	_null_ _null_ ));
+DATA(insert ( 2151	n 0 float4_accum	float8_var_samp		-	-	-	-				-				-						f f 0	1022	0	0	0		0	"{0,0,0}" _null_ ));
+DATA(insert ( 2152	n 0 float8_accum	float8_var_samp		-	-	-	-				-				-						f f 0	1022	0	0	0		0	"{0,0,0}" _null_ ));
+DATA(insert ( 2153	n 0 numeric_accum	numeric_var_samp	-	-	-	numeric_accum numeric_accum_inv numeric_var_samp		f f 0	2281	0	128 2281	128 _null_ _null_ ));
 
 /* stddev_pop */
-DATA(insert ( 2724	n 0 int8_accum	numeric_stddev_pop		-	int8_accum	int8_accum_inv	numeric_stddev_pop		f f 0	2281	128	2281	128 _null_ _null_ ));
-DATA(insert ( 2725	n 0 int4_accum	numeric_poly_stddev_pop	-	int4_accum	int4_accum_inv	numeric_poly_stddev_pop	f f 0	2281	48	2281	48	_null_ _null_ ));
-DATA(insert ( 2726	n 0 int2_accum	numeric_poly_stddev_pop	-	int2_accum	int2_accum_inv	numeric_poly_stddev_pop	f f 0	2281	48	2281	48	_null_ _null_ ));
-DATA(insert ( 2727	n 0 float4_accum	float8_stddev_pop	-	-				-				-					f f 0	1022	0	0		0	"{0,0,0}" _null_ ));
-DATA(insert ( 2728	n 0 float8_accum	float8_stddev_pop	-	-				-				-					f f 0	1022	0	0		0	"{0,0,0}" _null_ ));
-DATA(insert ( 2729	n 0 numeric_accum	numeric_stddev_pop	-	numeric_accum numeric_accum_inv numeric_stddev_pop	f f 0	2281	128 2281	128 _null_ _null_ ));
+DATA(insert ( 2724	n 0 int8_accum	numeric_stddev_pop		-	-	-	int8_accum	int8_accum_inv	numeric_stddev_pop		f f 0	2281	0	128	2281	128 _null_ _null_ ));
+DATA(insert ( 2725	n 0 int4_accum	numeric_poly_stddev_pop	-	-	-	int4_accum	int4_accum_inv	numeric_poly_stddev_pop	f f 0	2281	0	48	2281	48	_null_ _null_ ));
+DATA(insert ( 2726	n 0 int2_accum	numeric_poly_stddev_pop	-	-	-	int2_accum	int2_accum_inv	numeric_poly_stddev_pop	f f 0	2281	0	48	2281	48	_null_ _null_ ));
+DATA(insert ( 2727	n 0 float4_accum	float8_stddev_pop	-	-	-	-				-				-					f f 0	1022	0	0	0		0	"{0,0,0}" _null_ ));
+DATA(insert ( 2728	n 0 float8_accum	float8_stddev_pop	-	-	-	-				-				-					f f 0	1022	0	0	0		0	"{0,0,0}" _null_ ));
+DATA(insert ( 2729	n 0 numeric_accum	numeric_stddev_pop	-	-	-	numeric_accum numeric_accum_inv numeric_stddev_pop	f f 0	2281	0	128 2281	128 _null_ _null_ ));
 
 /* stddev_samp */
-DATA(insert ( 2712	n 0 int8_accum	numeric_stddev_samp			-	int8_accum	int8_accum_inv	numeric_stddev_samp			f f 0	2281	128 2281	128 _null_ _null_ ));
-DATA(insert ( 2713	n 0 int4_accum	numeric_poly_stddev_samp	-	int4_accum	int4_accum_inv	numeric_poly_stddev_samp	f f 0	2281	48	2281	48	_null_ _null_ ));
-DATA(insert ( 2714	n 0 int2_accum	numeric_poly_stddev_samp	-	int2_accum	int2_accum_inv	numeric_poly_stddev_samp	f f 0	2281	48	2281	48	_null_ _null_ ));
-DATA(insert ( 2715	n 0 float4_accum	float8_stddev_samp		-	-				-				-						f f 0	1022	0	0		0	"{0,0,0}" _null_ ));
-DATA(insert ( 2716	n 0 float8_accum	float8_stddev_samp		-	-				-				-						f f 0	1022	0	0		0	"{0,0,0}" _null_ ));
-DATA(insert ( 2717	n 0 numeric_accum	numeric_stddev_samp		-	numeric_accum numeric_accum_inv numeric_stddev_samp		f f 0	2281	128 2281	128 _null_ _null_ ));
+DATA(insert ( 2712	n 0 int8_accum	numeric_stddev_samp			-	-	-	int8_accum	int8_accum_inv	numeric_stddev_samp			f f 0	2281	0	128 2281	128 _null_ _null_ ));
+DATA(insert ( 2713	n 0 int4_accum	numeric_poly_stddev_samp	-	-	-	int4_accum	int4_accum_inv	numeric_poly_stddev_samp	f f 0	2281	0	48	2281	48	_null_ _null_ ));
+DATA(insert ( 2714	n 0 int2_accum	numeric_poly_stddev_samp	-	-	-	int2_accum	int2_accum_inv	numeric_poly_stddev_samp	f f 0	2281	0	48	2281	48	_null_ _null_ ));
+DATA(insert ( 2715	n 0 float4_accum	float8_stddev_samp		-	-	-	-				-				-						f f 0	1022	0	0	0		0	"{0,0,0}" _null_ ));
+DATA(insert ( 2716	n 0 float8_accum	float8_stddev_samp		-	-	-	-				-				-						f f 0	1022	0	0	0		0	"{0,0,0}" _null_ ));
+DATA(insert ( 2717	n 0 numeric_accum	numeric_stddev_samp		-	-	-	numeric_accum numeric_accum_inv numeric_stddev_samp		f f 0	2281	0	128 2281	128 _null_ _null_ ));
 
 /* stddev: historical Postgres syntax for stddev_samp */
-DATA(insert ( 2154	n 0 int8_accum	numeric_stddev_samp			-	int8_accum		int8_accum_inv	numeric_stddev_samp			f f 0	2281	128 2281	128 _null_ _null_ ));
-DATA(insert ( 2155	n 0 int4_accum	numeric_poly_stddev_samp	-	int4_accum		int4_accum_inv	numeric_poly_stddev_samp	f f 0	2281	48	2281	48	_null_ _null_ ));
-DATA(insert ( 2156	n 0 int2_accum	numeric_poly_stddev_samp	-	int2_accum		int2_accum_inv	numeric_poly_stddev_samp	f f 0	2281	48	2281	48	_null_ _null_ ));
-DATA(insert ( 2157	n 0 float4_accum	float8_stddev_samp		-	-				-				-							f f 0	1022	0	0		0	"{0,0,0}" _null_ ));
-DATA(insert ( 2158	n 0 float8_accum	float8_stddev_samp		-	-				-				-							f f 0	1022	0	0		0	"{0,0,0}" _null_ ));
-DATA(insert ( 2159	n 0 numeric_accum	numeric_stddev_samp		-	numeric_accum	numeric_accum_inv numeric_stddev_samp		f f 0	2281	128 2281	128 _null_ _null_ ));
+DATA(insert ( 2154	n 0 int8_accum	numeric_stddev_samp			-	-	-	int8_accum		int8_accum_inv	numeric_stddev_samp			f f 0	2281	0	128 2281	128 _null_ _null_ ));
+DATA(insert ( 2155	n 0 int4_accum	numeric_poly_stddev_samp	-	-	-	int4_accum		int4_accum_inv	numeric_poly_stddev_samp	f f 0	2281	0	48	2281	48	_null_ _null_ ));
+DATA(insert ( 2156	n 0 int2_accum	numeric_poly_stddev_samp	-	-	-	int2_accum		int2_accum_inv	numeric_poly_stddev_samp	f f 0	2281	0	48	2281	48	_null_ _null_ ));
+DATA(insert ( 2157	n 0 float4_accum	float8_stddev_samp		-	-	-	-				-				-							f f 0	1022	0	0	0		0	"{0,0,0}" _null_ ));
+DATA(insert ( 2158	n 0 float8_accum	float8_stddev_samp		-	-	-	-				-				-							f f 0	1022	0	0	0		0	"{0,0,0}" _null_ ));
+DATA(insert ( 2159	n 0 numeric_accum	numeric_stddev_samp		-	-	-	numeric_accum	numeric_accum_inv numeric_stddev_samp		f f 0	2281	0	128 2281	128 _null_ _null_ ));
 
 /* SQL2003 binary regression aggregates */
-DATA(insert ( 2818	n 0 int8inc_float8_float8	-					-	-				-				-			f f 0	20		0	0		0	"0" _null_ ));
-DATA(insert ( 2819	n 0 float8_regr_accum	float8_regr_sxx			-	-				-				-			f f 0	1022	0	0		0	"{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2820	n 0 float8_regr_accum	float8_regr_syy			-	-				-				-			f f 0	1022	0	0		0	"{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2821	n 0 float8_regr_accum	float8_regr_sxy			-	-				-				-			f f 0	1022	0	0		0	"{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2822	n 0 float8_regr_accum	float8_regr_avgx		-	-				-				-			f f 0	1022	0	0		0	"{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2823	n 0 float8_regr_accum	float8_regr_avgy		-	-				-				-			f f 0	1022	0	0		0	"{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2824	n 0 float8_regr_accum	float8_regr_r2			-	-				-				-			f f 0	1022	0	0		0	"{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2825	n 0 float8_regr_accum	float8_regr_slope		-	-				-				-			f f 0	1022	0	0		0	"{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2826	n 0 float8_regr_accum	float8_regr_intercept	-	-				-				-			f f 0	1022	0	0		0	"{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2827	n 0 float8_regr_accum	float8_covar_pop		-	-				-				-			f f 0	1022	0	0		0	"{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2828	n 0 float8_regr_accum	float8_covar_samp		-	-				-				-			f f 0	1022	0	0		0	"{0,0,0,0,0,0}" _null_ ));
-DATA(insert ( 2829	n 0 float8_regr_accum	float8_corr				-	-				-				-			f f 0	1022	0	0		0	"{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2818	n 0 int8inc_float8_float8	-					-	-	-	-				-				-			f f 0	20		0	0	0		0	"0" _null_ ));
+DATA(insert ( 2819	n 0 float8_regr_accum	float8_regr_sxx			-	-	-	-				-				-			f f 0	1022	0	0	0		0	"{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2820	n 0 float8_regr_accum	float8_regr_syy			-	-	-	-				-				-			f f 0	1022	0	0	0		0	"{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2821	n 0 float8_regr_accum	float8_regr_sxy			-	-	-	-				-				-			f f 0	1022	0	0	0		0	"{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2822	n 0 float8_regr_accum	float8_regr_avgx		-	-	-	-				-				-			f f 0	1022	0	0	0		0	"{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2823	n 0 float8_regr_accum	float8_regr_avgy		-	-	-	-				-				-			f f 0	1022	0	0	0		0	"{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2824	n 0 float8_regr_accum	float8_regr_r2			-	-	-	-				-				-			f f 0	1022	0	0	0		0	"{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2825	n 0 float8_regr_accum	float8_regr_slope		-	-	-	-				-				-			f f 0	1022	0	0	0		0	"{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2826	n 0 float8_regr_accum	float8_regr_intercept	-	-	-	-				-				-			f f 0	1022	0	0	0		0	"{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2827	n 0 float8_regr_accum	float8_covar_pop		-	-	-	-				-				-			f f 0	1022	0	0	0		0	"{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2828	n 0 float8_regr_accum	float8_covar_samp		-	-	-	-				-				-			f f 0	1022	0	0	0		0	"{0,0,0,0,0,0}" _null_ ));
+DATA(insert ( 2829	n 0 float8_regr_accum	float8_corr				-	-	-	-				-				-			f f 0	1022	0	0	0		0	"{0,0,0,0,0,0}" _null_ ));
 
 /* boolean-and and boolean-or */
-DATA(insert ( 2517	n 0 booland_statefunc	-	-	bool_accum	bool_accum_inv	bool_alltrue	f f 58	16		0	2281	16	_null_ _null_ ));
-DATA(insert ( 2518	n 0 boolor_statefunc	-	-	bool_accum	bool_accum_inv	bool_anytrue	f f 59	16		0	2281	16	_null_ _null_ ));
-DATA(insert ( 2519	n 0 booland_statefunc	-	-	bool_accum	bool_accum_inv	bool_alltrue	f f 58	16		0	2281	16	_null_ _null_ ));
+DATA(insert ( 2517	n 0 booland_statefunc	-	-	-	-	bool_accum	bool_accum_inv	bool_alltrue	f f 58	16	0		0	2281	16	_null_ _null_ ));
+DATA(insert ( 2518	n 0 boolor_statefunc	-	-	-	-	bool_accum	bool_accum_inv	bool_anytrue	f f 59	16	0		0	2281	16	_null_ _null_ ));
+DATA(insert ( 2519	n 0 booland_statefunc	-	-	-	-	bool_accum	bool_accum_inv	bool_alltrue	f f 58	16	0		0	2281	16	_null_ _null_ ));
 
 /* bitwise integer */
-DATA(insert ( 2236	n 0 int2and		-				int2and	-				-				-				f f 0	21		0	0		0	_null_ _null_ ));
-DATA(insert ( 2237	n 0 int2or		-				int2or	-				-				-				f f 0	21		0	0		0	_null_ _null_ ));
-DATA(insert ( 2238	n 0 int4and		-				int4and	-				-				-				f f 0	23		0	0		0	_null_ _null_ ));
-DATA(insert ( 2239	n 0 int4or		-				int4or	-				-				-				f f 0	23		0	0		0	_null_ _null_ ));
-DATA(insert ( 2240	n 0 int8and		-				int8and	-				-				-				f f 0	20		0	0		0	_null_ _null_ ));
-DATA(insert ( 2241	n 0 int8or		-				int8or	-				-				-				f f 0	20		0	0		0	_null_ _null_ ));
-DATA(insert ( 2242	n 0 bitand		-				bitand	-				-				-				f f 0	1560	0	0		0	_null_ _null_ ));
-DATA(insert ( 2243	n 0 bitor		-				bitor	-				-				-				f f 0	1560	0	0		0	_null_ _null_ ));
+DATA(insert ( 2236	n 0 int2and		-				int2and	-	-	-				-				-				f f 0	21		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2237	n 0 int2or		-				int2or	-	-	-				-				-				f f 0	21		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2238	n 0 int4and		-				int4and	-	-	-				-				-				f f 0	23		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2239	n 0 int4or		-				int4or	-	-	-				-				-				f f 0	23		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2240	n 0 int8and		-				int8and	-	-	-				-				-				f f 0	20		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2241	n 0 int8or		-				int8or	-	-	-				-				-				f f 0	20		0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2242	n 0 bitand		-				bitand	-	-	-				-				-				f f 0	1560	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 2243	n 0 bitor		-				bitor	-	-	-				-				-				f f 0	1560	0	0	0		0	_null_ _null_ ));
 
 /* xml */
-DATA(insert ( 2901	n 0 xmlconcat2	-				-		-				-				-				f f 0	142		0	0		0	_null_ _null_ ));
+DATA(insert ( 2901	n 0 xmlconcat2	-				-		-	-	-				-				-				f f 0	142		0	0	0		0	_null_ _null_ ));
 
 /* array */
-DATA(insert ( 2335	n 0 array_agg_transfn		array_agg_finalfn		-	-		-				-				t f 0	2281	0	0		0	_null_ _null_ ));
-DATA(insert ( 4053	n 0 array_agg_array_transfn array_agg_array_finalfn	-	-		-				-				t f 0	2281	0	0		0	_null_ _null_ ));
+DATA(insert ( 2335	n 0 array_agg_transfn		array_agg_finalfn		-	-	-	-		-				-				t f 0	2281	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 4053	n 0 array_agg_array_transfn array_agg_array_finalfn	-	-	-	-		-				-				t f 0	2281	0	0	0		0	_null_ _null_ ));
 
 /* text */
-DATA(insert ( 3538	n 0 string_agg_transfn	string_agg_finalfn	-	-				-				-				f f 0	2281	0	0		0	_null_ _null_ ));
+DATA(insert ( 3538	n 0 string_agg_transfn	string_agg_finalfn	-	-	-	-				-				-				f f 0	2281	0	0	0		0	_null_ _null_ ));
 
 /* bytea */
-DATA(insert ( 3545	n 0 bytea_string_agg_transfn	bytea_string_agg_finalfn	-	-				-				-		f f 0	2281	0	0		0	_null_ _null_ ));
+DATA(insert ( 3545	n 0 bytea_string_agg_transfn	bytea_string_agg_finalfn	-	-	-	-				-				-		f f 0	2281	0	0	0		0	_null_ _null_ ));
 
 /* json */
-DATA(insert ( 3175	n 0 json_agg_transfn	json_agg_finalfn			-	-				-				-				f f 0	2281	0	0		0	_null_ _null_ ));
-DATA(insert ( 3197	n 0 json_object_agg_transfn json_object_agg_finalfn -	-				-				-				f f 0	2281	0	0		0	_null_ _null_ ));
+DATA(insert ( 3175	n 0 json_agg_transfn	json_agg_finalfn			-	-	-	-				-				-				f f 0	2281	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 3197	n 0 json_object_agg_transfn json_object_agg_finalfn -	-	-	-				-				-				f f 0	2281	0	0	0		0	_null_ _null_ ));
 
 /* jsonb */
-DATA(insert ( 3267	n 0 jsonb_agg_transfn	jsonb_agg_finalfn				-	-				-				-			f f 0	2281	0	0		0	_null_ _null_ ));
-DATA(insert ( 3270	n 0 jsonb_object_agg_transfn jsonb_object_agg_finalfn	-	-				-				-			f f 0	2281	0	0		0	_null_ _null_ ));
+DATA(insert ( 3267	n 0 jsonb_agg_transfn	jsonb_agg_finalfn				-	-	-	-				-				-			f f 0	2281	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 3270	n 0 jsonb_object_agg_transfn jsonb_object_agg_finalfn	-	-	-	-				-				-			f f 0	2281	0	0	0		0	_null_ _null_ ));
 
 /* ordered-set and hypothetical-set aggregates */
-DATA(insert ( 3972	o 1 ordered_set_transition			percentile_disc_final					-	-		-		-		t f 0	2281	0	0		0	_null_ _null_ ));
-DATA(insert ( 3974	o 1 ordered_set_transition			percentile_cont_float8_final			-	-		-		-		f f 0	2281	0	0		0	_null_ _null_ ));
-DATA(insert ( 3976	o 1 ordered_set_transition			percentile_cont_interval_final			-	-		-		-		f f 0	2281	0	0		0	_null_ _null_ ));
-DATA(insert ( 3978	o 1 ordered_set_transition			percentile_disc_multi_final				-	-		-		-		t f 0	2281	0	0		0	_null_ _null_ ));
-DATA(insert ( 3980	o 1 ordered_set_transition			percentile_cont_float8_multi_final		-	-		-		-		f f 0	2281	0	0		0	_null_ _null_ ));
-DATA(insert ( 3982	o 1 ordered_set_transition			percentile_cont_interval_multi_final	-	-		-		-		f f 0	2281	0	0		0	_null_ _null_ ));
-DATA(insert ( 3984	o 0 ordered_set_transition			mode_final								-	-		-		-		t f 0	2281	0	0		0	_null_ _null_ ));
-DATA(insert ( 3986	h 1 ordered_set_transition_multi	rank_final								-	-		-		-		t f 0	2281	0	0		0	_null_ _null_ ));
-DATA(insert ( 3988	h 1 ordered_set_transition_multi	percent_rank_final						-	-		-		-		t f 0	2281	0	0		0	_null_ _null_ ));
-DATA(insert ( 3990	h 1 ordered_set_transition_multi	cume_dist_final							-	-		-		-		t f 0	2281	0	0		0	_null_ _null_ ));
-DATA(insert ( 3992	h 1 ordered_set_transition_multi	dense_rank_final						-	-		-		-		t f 0	2281	0	0		0	_null_ _null_ ));
+DATA(insert ( 3972	o 1 ordered_set_transition			percentile_disc_final					-	-	-	-		-		-		t f 0	2281	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 3974	o 1 ordered_set_transition			percentile_cont_float8_final			-	-	-	-		-		-		f f 0	2281	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 3976	o 1 ordered_set_transition			percentile_cont_interval_final			-	-	-	-		-		-		f f 0	2281	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 3978	o 1 ordered_set_transition			percentile_disc_multi_final				-	-	-	-		-		-		t f 0	2281	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 3980	o 1 ordered_set_transition			percentile_cont_float8_multi_final		-	-	-	-		-		-		f f 0	2281	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 3982	o 1 ordered_set_transition			percentile_cont_interval_multi_final	-	-	-	-		-		-		f f 0	2281	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 3984	o 0 ordered_set_transition			mode_final								-	-	-	-		-		-		t f 0	2281	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 3986	h 1 ordered_set_transition_multi	rank_final								-	-	-	-		-		-		t f 0	2281	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 3988	h 1 ordered_set_transition_multi	percent_rank_final						-	-	-	-		-		-		t f 0	2281	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 3990	h 1 ordered_set_transition_multi	cume_dist_final							-	-	-	-		-		-		t f 0	2281	0	0	0		0	_null_ _null_ ));
+DATA(insert ( 3992	h 1 ordered_set_transition_multi	dense_rank_final						-	-	-	-		-		-		t f 0	2281	0	0	0		0	_null_ _null_ ));
 
 
 /*
@@ -326,6 +335,8 @@ extern ObjectAddress AggregateCreate(const char *aggName,
 				List *aggtransfnName,
 				List *aggfinalfnName,
 				List *aggcombinefnName,
+				List *aggserialfnName,
+				List *aggdeserialfnName,
 				List *aggmtransfnName,
 				List *aggminvtransfnName,
 				List *aggmfinalfnName,
@@ -333,6 +344,7 @@ extern ObjectAddress AggregateCreate(const char *aggName,
 				bool mfinalfnExtraArgs,
 				List *aggsortopName,
 				Oid aggTransType,
+				Oid aggSerialType,
 				int32 aggTransSpace,
 				Oid aggmTransType,
 				int32 aggmTransSpace,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index d35ec81..069e675 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1865,6 +1865,7 @@ typedef struct AggState
 	bool		agg_done;		/* indicates completion of Agg scan */
 	bool		combineStates;	/* input tuples contain transition states */
 	bool		finalizeAggs;	/* should we call the finalfn on agg states? */
+	bool		serialStates;	/* should partial states be serialized? */
 	int			projected_set;	/* The last projected grouping set */
 	int			current_set;	/* The current grouping set being evaluated */
 	Bitmapset  *grouped_cols;	/* grouped cols in current projection */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 5961f2c..ed1997b 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -720,6 +720,7 @@ typedef struct Agg
 	AggStrategy aggstrategy;	/* basic strategy, see nodes.h */
 	bool		combineStates;	/* input tuples contain transition states */
 	bool		finalizeAggs;	/* should we call the finalfn on agg states? */
+	bool		serialStates;	/* should partial states be serialized? */
 	int			numCols;		/* number of grouping columns */
 	AttrNumber *grpColIdx;		/* their indexes in the target list */
 	Oid		   *grpOperators;	/* equality operators to compare with */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index ee7007a..851c983 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -1311,6 +1311,7 @@ typedef struct AggPath
 	List	   *qual;			/* quals (HAVING quals), if any */
 	bool		combineStates;	/* input is partially aggregated agg states */
 	bool		finalizeAggs;	/* should the executor call the finalfn? */
+	bool		serialStates;	/* should partial states be serialized? */
 } AggPath;
 
 /*
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 7c21bff..efa2fa7 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -170,7 +170,8 @@ extern AggPath *create_agg_path(PlannerInfo *root,
 				const AggClauseCosts *aggcosts,
 				double numGroups,
 				bool combineStates,
-				bool finalizeAggs);
+				bool finalizeAggs,
+				bool serialStates);
 extern AggPath *create_parallelagg_path(PlannerInfo *root,
 										RelOptInfo *rel,
 										Path *subpath,
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 596ffb3..1f96e27 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -58,7 +58,7 @@ extern bool is_projection_capable_plan(Plan *plan);
 /* External use of these functions is deprecated: */
 extern Sort *make_sort_from_sortclauses(List *sortcls, Plan *lefttree);
 extern Agg *make_agg(List *tlist, List *qual, AggStrategy aggstrategy,
-		 bool combineStates, bool finalizeAggs,
+		 bool combineStates, bool finalizeAggs, bool serialStates,
 		 int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators,
 		 List *groupingSets, List *chain,
 		 double dNumGroups, Plan *lefttree);
diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index de58db1..69034a2 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.h
@@ -61,7 +61,7 @@ extern void add_column_to_pathtarget(PathTarget *target,
 extern void add_new_column_to_pathtarget(PathTarget *target, Expr *expr);
 extern void add_new_columns_to_pathtarget(PathTarget *target, List *exprs);
 extern void apply_pathtarget_labeling_to_tlist(List *tlist, PathTarget *target);
-extern void apply_partialaggref_adjustment(PathTarget *target);
+extern void apply_partialaggref_adjustment(PathTarget *target, bool serialStates);
 
 /* Convenience macro to get a PathTarget with valid cost/width fields */
 #define create_pathtarget(root, tlist) \
diff --git a/src/include/parser/parse_agg.h b/src/include/parser/parse_agg.h
index 699b61c..43be714 100644
--- a/src/include/parser/parse_agg.h
+++ b/src/include/parser/parse_agg.h
@@ -51,6 +51,18 @@ extern void build_aggregate_combinefn_expr(Oid agg_state_type,
 										   Oid combinefn_oid,
 										   Expr **combinefnexpr);
 
+extern void build_aggregate_serialfn_expr(Oid agg_state_type,
+										  Oid agg_serial_type,
+										  Oid agg_input_collation,
+										  Oid serialfn_oid,
+										  Expr **serialfnexpr);
+
+extern void build_aggregate_deserialfn_expr(Oid agg_state_type,
+											Oid agg_serial_type,
+											Oid agg_input_collation,
+											Oid deserialfn_oid,
+											Expr **deserialfnexpr);
+
 extern void build_aggregate_finalfn_expr(Oid *agg_input_types,
 						int num_finalfn_inputs,
 						Oid agg_state_type,
-- 
1.9.5.msysgit.1

