From 5f548a5e8d5bc04ea62caf87b18645e8a8f0a669 Mon Sep 17 00:00:00 2001
From: Yuki Fujii <Fujii.Yuki@df.MitsubishiElectric.co.jp>
Date: Tue, 6 Jun 2023 08:00:32 -0400
Subject: [PATCH] Partial aggregates push down v19

---
 contrib/postgres_fdw/deparse.c                |  71 +-
 .../postgres_fdw/expected/postgres_fdw.out    | 608 +++++++++++++++-
 contrib/postgres_fdw/postgres_fdw.c           |  18 +-
 contrib/postgres_fdw/sql/postgres_fdw.sql     | 279 ++++++-
 doc/src/sgml/catalogs.sgml                    |  18 +
 doc/src/sgml/postgres-fdw.sgml                |  88 ++-
 doc/src/sgml/ref/create_aggregate.sgml        |  41 ++
 doc/src/sgml/xaggr.sgml                       |  12 +-
 src/backend/catalog/pg_aggregate.c            |  79 ++
 src/backend/commands/aggregatecmds.c          |   4 +
 src/bin/pg_dump/pg_dump.c                     |   9 +-
 src/include/catalog/pg_aggregate.dat          | 680 ++++++++++++++----
 src/include/catalog/pg_aggregate.h            |   4 +
 src/include/catalog/pg_proc.dat               | 196 +++++
 .../regress/expected/create_aggregate.out     | 161 +++++
 src/test/regress/expected/oidjoins.out        |   1 +
 src/test/regress/sql/create_aggregate.sql     | 163 +++++
 17 files changed, 2255 insertions(+), 177 deletions(-)

diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 09d6dd60dd..501579b88d 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -202,7 +202,7 @@ static bool is_subquery_var(Var *node, RelOptInfo *foreignrel,
 							int *relno, int *colno);
 static void get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
 										  int *relno, int *colno);
-
+static bool partial_agg_ok(Aggref* agg, PgFdwRelationInfo* fpinfo);
 
 /*
  * Examine each qual clause in input_conds, and classify them into two groups,
@@ -907,8 +907,9 @@ foreign_expr_walker(Node *node,
 				if (!IS_UPPER_REL(glob_cxt->foreignrel))
 					return false;
 
-				/* Only non-split aggregates are pushable. */
-				if (agg->aggsplit != AGGSPLIT_SIMPLE)
+				if ((agg->aggsplit != AGGSPLIT_SIMPLE) && (agg->aggsplit != AGGSPLIT_INITIAL_SERIAL))
+					return false;
+				if (agg->aggsplit == AGGSPLIT_INITIAL_SERIAL && !partial_agg_ok(agg, fpinfo))
 					return false;
 
 				/* As usual, it must be shippable. */
@@ -3517,14 +3518,33 @@ deparseAggref(Aggref *node, deparse_expr_cxt *context)
 	StringInfo	buf = context->buf;
 	bool		use_variadic;
 
-	/* Only basic, non-split aggregation accepted. */
-	Assert(node->aggsplit == AGGSPLIT_SIMPLE);
+
+	Assert((node->aggsplit == AGGSPLIT_SIMPLE) ||
+		(node->aggsplit == AGGSPLIT_INITIAL_SERIAL));
 
 	/* Check if need to print VARIADIC (cf. ruleutils.c) */
 	use_variadic = node->aggvariadic;
 
-	/* Find aggregate name from aggfnoid which is a pg_proc entry */
-	appendFunctionName(node->aggfnoid, context);
+	if (node->aggsplit == AGGSPLIT_SIMPLE)
+		/* Find aggregate name from aggfnoid which is a pg_proc entry */
+		appendFunctionName(node->aggfnoid, context);
+	else {
+		HeapTuple			aggtup;
+		Form_pg_aggregate	aggform;
+
+		/* Find aggregate name from aggfnoid and aggpartialfn */
+		aggtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(node->aggfnoid));
+		if (!HeapTupleIsValid(aggtup))
+			elog(ERROR, "cache lookup failed for aggregate %u", node->aggfnoid);
+		aggform = (Form_pg_aggregate)GETSTRUCT(aggtup);
+
+		if(aggform->aggpartialfn)
+			appendFunctionName(aggform->aggpartialfn, context);
+		else
+			elog(ERROR, "there is no aggpartialfn %u", node->aggfnoid);
+		ReleaseSysCache(aggtup);
+	}
+
 	appendStringInfoChar(buf, '(');
 
 	/* Add DISTINCT */
@@ -4047,3 +4067,40 @@ get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
 	/* Shouldn't get here */
 	elog(ERROR, "unexpected expression in subquery output");
 }
+
+/*
+ * Check that partial aggregate agg is safe to push down.
+ *
+ * It is pushdown-safe when all of the following conditions are true.
+ * condition1) agg is AGGKIND_NORMAL aggregate which contains no distinct
+ *   or order by clauses
+ * condition2) there is an aggregate function for partial aggregation
+ *   (call it aggpartialfunc) corresponding to agg
+ *   and aggpartialfunc is shippable
+ */
+static bool
+partial_agg_ok(Aggref* agg, PgFdwRelationInfo *fpinfo)
+{
+	HeapTuple	aggtup;
+	Form_pg_aggregate aggform;
+	bool        partial_agg_ok = true;
+
+	Assert(agg->aggsplit == AGGSPLIT_INITIAL_SERIAL);
+
+	/* We don't support complex partial aggregates */
+	if (agg->aggdistinct || agg->aggkind != AGGKIND_NORMAL || agg->aggorder != NIL)
+		return false;
+
+	aggtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(agg->aggfnoid));
+	if (!HeapTupleIsValid(aggtup))
+		elog(ERROR, "cache lookup failed for aggregate %u", agg->aggfnoid);
+	aggform = (Form_pg_aggregate)GETSTRUCT(aggtup);
+
+	if (!aggform->aggpartialfn)
+		partial_agg_ok = false;
+	else if (!is_shippable(aggform->aggpartialfn, ProcedureRelationId, fpinfo))
+		partial_agg_ok = false;
+
+	ReleaseSysCache(aggtup);
+	return partial_agg_ok;
+}
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index c8c4614b54..e8f656b953 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -9700,13 +9700,27 @@ RESET enable_partitionwise_join;
 -- ===================================================================
 -- test partitionwise aggregates
 -- ===================================================================
-CREATE TABLE pagg_tab (a int, b int, c text) PARTITION BY RANGE(a);
+ALTER SERVER loopback OPTIONS (ADD fdw_tuple_cost '0.5');
+CREATE TYPE mood AS enum ('sad', 'ok', 'happy');
+ALTER EXTENSION postgres_fdw ADD TYPE mood;
+CREATE TABLE pagg_tab (a int, b int, c text, c_serial int4,
+					c_int4array _int4, c_interval interval,
+					c_money money, c_1c text, c_1b bytea,
+					c_bit bit(2), c_1or3int2 int2,
+					c_1or3int4 int4, c_1or3int8 int8,
+					c_bool bool, c_enum mood, c_pg_lsn pg_lsn,
+					c_tid tid, c_int4range int4range,
+					c_int4multirange int4multirange,
+					c_time time, c_timetz timetz,
+					c_timestamp timestamp, c_timestamptz timestamptz,
+					c_xid8 xid8)
+	PARTITION BY RANGE(a);
 CREATE TABLE pagg_tab_p1 (LIKE pagg_tab);
 CREATE TABLE pagg_tab_p2 (LIKE pagg_tab);
 CREATE TABLE pagg_tab_p3 (LIKE pagg_tab);
-INSERT INTO pagg_tab_p1 SELECT i % 30, i % 50, to_char(i/30, 'FM0000') FROM generate_series(1, 3000) i WHERE (i % 30) < 10;
-INSERT INTO pagg_tab_p2 SELECT i % 30, i % 50, to_char(i/30, 'FM0000') FROM generate_series(1, 3000) i WHERE (i % 30) < 20 and (i % 30) >= 10;
-INSERT INTO pagg_tab_p3 SELECT i % 30, i % 50, to_char(i/30, 'FM0000') FROM generate_series(1, 3000) i WHERE (i % 30) < 30 and (i % 30) >= 20;
+INSERT INTO pagg_tab_p1 SELECT i % 30, i % 50, to_char(i/30, 'FM0000'), i, array[(i % 2), 0], ((i % 2) || ' seconds')::interval, (i % 2)::money, ((i - 1) % 10)::text, (((i - 1) % 10)::text)::bytea, case when (i % 2) = 0 then B'01' else B'11' end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then true else false end, (case when (i % 2) = 0 then 'sad' else 'happy' end)::mood, ('0/' || i)::pg_lsn, ('(0,' || i || ')')::tid, int4range(0, i), int4multirange(int4range(0, i), int4range(100, 100 + i)), '00:00:00'::time + (i || ' seconds')::interval, '00:00:00'::timetz + (i || ' seconds')::interval, '2000-01-01'::timestamp + (i || ' seconds')::interval, '2000-01-01'::timestamptz + (i || ' seconds')::interval, ((i % 10)::text)::xid8 FROM generate_series(1, 3000) i WHERE (i % 30) < 10;
+INSERT INTO pagg_tab_p2 SELECT i % 30, i % 50, to_char(i/30, 'FM0000'), i, array[(i % 2), 0], ((i % 2) || ' seconds')::interval, (i % 2)::money, ((i - 1) % 10)::text, (((i - 1) % 10)::text)::bytea, case when (i % 2) = 0 then B'01' else B'11' end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then true else false end, (case when (i % 2) = 0 then 'sad' else 'happy' end)::mood, ('0/' || i)::pg_lsn, ('(0,' || i || ')')::tid, int4range(0, i), int4multirange(int4range(0, i), int4range(100, 100 + i)), '00:00:00'::time + (i || ' seconds')::interval, '00:00:00'::timetz + (i || ' seconds')::interval, '2000-01-01'::timestamp + (i || ' seconds')::interval, '2000-01-01'::timestamptz + (i || ' seconds')::interval, ((i % 10)::text)::xid8 FROM generate_series(1, 3000) i WHERE (i % 30) < 20 and (i % 30) >= 10;
+INSERT INTO pagg_tab_p3 SELECT i % 30, i % 50, to_char(i/30, 'FM0000'), i, array[(i % 2), 0], ((i % 2) || ' seconds')::interval, (i % 2)::money, ((i - 1) % 10)::text, (((i - 1) % 10)::text)::bytea, case when (i % 2) = 0 then B'01' else B'11' end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then true else false end, (case when (i % 2) = 0 then 'sad' else 'happy' end)::mood, ('0/' || i)::pg_lsn, ('(0,' || i || ')')::tid, int4range(0, i), int4multirange(int4range(0, i), int4range(100, 100 + i)), '00:00:00'::time + (i || ' seconds')::interval, '00:00:00'::timetz + (i || ' seconds')::interval, '2000-01-01'::timestamp + (i || ' seconds')::interval, '2000-01-01'::timestamptz + (i || ' seconds')::interval, ((i % 10)::text)::xid8 FROM generate_series(1, 3000) i WHERE (i % 30) < 30 and (i % 30) >= 20;
 -- Create foreign partitions
 CREATE FOREIGN TABLE fpagg_tab_p1 PARTITION OF pagg_tab FOR VALUES FROM (0) TO (10) SERVER loopback OPTIONS (table_name 'pagg_tab_p1');
 CREATE FOREIGN TABLE fpagg_tab_p2 PARTITION OF pagg_tab FOR VALUES FROM (10) TO (20) SERVER loopback OPTIONS (table_name 'pagg_tab_p2');
@@ -9765,8 +9779,8 @@ SELECT a, sum(b), min(b), count(*) FROM pagg_tab GROUP BY a HAVING avg(b) < 22 O
 -- Should have all the columns in the target list for the given relation
 EXPLAIN (VERBOSE, COSTS OFF)
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
-                               QUERY PLAN                               
-------------------------------------------------------------------------
+                                                                                                                                           QUERY PLAN                                                                                                                                            
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  Sort
    Output: t1.a, (count(((t1.*)::pagg_tab)))
    Sort Key: t1.a
@@ -9777,21 +9791,21 @@ SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
                Filter: (avg(t1.b) < '22'::numeric)
                ->  Foreign Scan on public.fpagg_tab_p1 t1
                      Output: t1.a, t1.*, t1.b
-                     Remote SQL: SELECT a, b, c FROM public.pagg_tab_p1
+                     Remote SQL: SELECT a, b, c, c_serial, c_int4array, c_interval, c_money, c_1c, c_1b, c_bit, c_1or3int2, c_1or3int4, c_1or3int8, c_bool, c_enum, c_pg_lsn, c_tid, c_int4range, c_int4multirange, c_time, c_timetz, c_timestamp, c_timestamptz, c_xid8 FROM public.pagg_tab_p1
          ->  HashAggregate
                Output: t1_1.a, count(((t1_1.*)::pagg_tab))
                Group Key: t1_1.a
                Filter: (avg(t1_1.b) < '22'::numeric)
                ->  Foreign Scan on public.fpagg_tab_p2 t1_1
                      Output: t1_1.a, t1_1.*, t1_1.b
-                     Remote SQL: SELECT a, b, c FROM public.pagg_tab_p2
+                     Remote SQL: SELECT a, b, c, c_serial, c_int4array, c_interval, c_money, c_1c, c_1b, c_bit, c_1or3int2, c_1or3int4, c_1or3int8, c_bool, c_enum, c_pg_lsn, c_tid, c_int4range, c_int4multirange, c_time, c_timetz, c_timestamp, c_timestamptz, c_xid8 FROM public.pagg_tab_p2
          ->  HashAggregate
                Output: t1_2.a, count(((t1_2.*)::pagg_tab))
                Group Key: t1_2.a
                Filter: (avg(t1_2.b) < '22'::numeric)
                ->  Foreign Scan on public.fpagg_tab_p3 t1_2
                      Output: t1_2.a, t1_2.*, t1_2.b
-                     Remote SQL: SELECT a, b, c FROM public.pagg_tab_p3
+                     Remote SQL: SELECT a, b, c, c_serial, c_int4array, c_interval, c_money, c_1c, c_1b, c_bit, c_1or3int2, c_1or3int4, c_1or3int8, c_bool, c_enum, c_pg_lsn, c_tid, c_int4range, c_int4multirange, c_time, c_timetz, c_timestamp, c_timestamptz, c_xid8 FROM public.pagg_tab_p3
 (25 rows)
 
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
@@ -9810,11 +9824,11 @@ EXPLAIN (COSTS OFF)
 SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
                            QUERY PLAN                            
 -----------------------------------------------------------------
- Sort
-   Sort Key: pagg_tab.b
-   ->  Finalize HashAggregate
-         Group Key: pagg_tab.b
-         Filter: (sum(pagg_tab.a) < 700)
+ Finalize GroupAggregate
+   Group Key: pagg_tab.b
+   Filter: (sum(pagg_tab.a) < 700)
+   ->  Sort
+         Sort Key: pagg_tab.b
          ->  Append
                ->  Partial HashAggregate
                      Group Key: pagg_tab.b
@@ -9827,6 +9841,572 @@ SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700
                      ->  Foreign Scan on fpagg_tab_p3 pagg_tab_2
 (15 rows)
 
+-- It's unsafe to push down having clause when there are partial aggregates
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
+                                                                 QUERY PLAN                                                                  
+---------------------------------------------------------------------------------------------------------------------------------------------
+ Finalize GroupAggregate
+   Output: pagg_tab.b, avg(pagg_tab.a), max(pagg_tab.a), count(*)
+   Group Key: pagg_tab.b
+   Filter: (sum(pagg_tab.a) < 700)
+   ->  Sort
+         Output: pagg_tab.b, (PARTIAL avg(pagg_tab.a)), (PARTIAL max(pagg_tab.a)), (PARTIAL count(*)), (PARTIAL sum(pagg_tab.a))
+         Sort Key: pagg_tab.b
+         ->  Append
+               ->  Partial HashAggregate
+                     Output: pagg_tab.b, PARTIAL avg(pagg_tab.a), PARTIAL max(pagg_tab.a), PARTIAL count(*), PARTIAL sum(pagg_tab.a)
+                     Group Key: pagg_tab.b
+                     ->  Foreign Scan on public.fpagg_tab_p1 pagg_tab
+                           Output: pagg_tab.b, pagg_tab.a
+                           Remote SQL: SELECT a, b FROM public.pagg_tab_p1
+               ->  Partial HashAggregate
+                     Output: pagg_tab_1.b, PARTIAL avg(pagg_tab_1.a), PARTIAL max(pagg_tab_1.a), PARTIAL count(*), PARTIAL sum(pagg_tab_1.a)
+                     Group Key: pagg_tab_1.b
+                     ->  Foreign Scan on public.fpagg_tab_p2 pagg_tab_1
+                           Output: pagg_tab_1.b, pagg_tab_1.a
+                           Remote SQL: SELECT a, b FROM public.pagg_tab_p2
+               ->  Partial HashAggregate
+                     Output: pagg_tab_2.b, PARTIAL avg(pagg_tab_2.a), PARTIAL max(pagg_tab_2.a), PARTIAL count(*), PARTIAL sum(pagg_tab_2.a)
+                     Group Key: pagg_tab_2.b
+                     ->  Foreign Scan on public.fpagg_tab_p3 pagg_tab_2
+                           Output: pagg_tab_2.b, pagg_tab_2.a
+                           Remote SQL: SELECT a, b FROM public.pagg_tab_p3
+(26 rows)
+
+-- Partial aggregates are safe to push down without having clause
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b ORDER BY 1;
+                                                       QUERY PLAN                                                       
+------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: pagg_tab.b, (avg(pagg_tab.a)), (max(pagg_tab.a)), (count(*))
+   Sort Key: pagg_tab.b
+   ->  Finalize HashAggregate
+         Output: pagg_tab.b, avg(pagg_tab.a), max(pagg_tab.a), count(*)
+         Group Key: pagg_tab.b
+         ->  Append
+               ->  Foreign Scan
+                     Output: pagg_tab.b, (PARTIAL avg(pagg_tab.a)), (PARTIAL max(pagg_tab.a)), (PARTIAL count(*))
+                     Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab)
+                     Remote SQL: SELECT b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p1 GROUP BY 1
+               ->  Foreign Scan
+                     Output: pagg_tab_1.b, (PARTIAL avg(pagg_tab_1.a)), (PARTIAL max(pagg_tab_1.a)), (PARTIAL count(*))
+                     Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1)
+                     Remote SQL: SELECT b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p2 GROUP BY 1
+               ->  Foreign Scan
+                     Output: pagg_tab_2.b, (PARTIAL avg(pagg_tab_2.a)), (PARTIAL max(pagg_tab_2.a)), (PARTIAL count(*))
+                     Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2)
+                     Remote SQL: SELECT b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p3 GROUP BY 1
+(19 rows)
+
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b ORDER BY 1;
+ b  |         avg         | max | count 
+----+---------------------+-----+-------
+  0 | 10.0000000000000000 |  20 |    60
+  1 | 11.0000000000000000 |  21 |    60
+  2 | 12.0000000000000000 |  22 |    60
+  3 | 13.0000000000000000 |  23 |    60
+  4 | 14.0000000000000000 |  24 |    60
+  5 | 15.0000000000000000 |  25 |    60
+  6 | 16.0000000000000000 |  26 |    60
+  7 | 17.0000000000000000 |  27 |    60
+  8 | 18.0000000000000000 |  28 |    60
+  9 | 19.0000000000000000 |  29 |    60
+ 10 | 10.0000000000000000 |  20 |    60
+ 11 | 11.0000000000000000 |  21 |    60
+ 12 | 12.0000000000000000 |  22 |    60
+ 13 | 13.0000000000000000 |  23 |    60
+ 14 | 14.0000000000000000 |  24 |    60
+ 15 | 15.0000000000000000 |  25 |    60
+ 16 | 16.0000000000000000 |  26 |    60
+ 17 | 17.0000000000000000 |  27 |    60
+ 18 | 18.0000000000000000 |  28 |    60
+ 19 | 19.0000000000000000 |  29 |    60
+ 20 | 10.0000000000000000 |  20 |    60
+ 21 | 11.0000000000000000 |  21 |    60
+ 22 | 12.0000000000000000 |  22 |    60
+ 23 | 13.0000000000000000 |  23 |    60
+ 24 | 14.0000000000000000 |  24 |    60
+ 25 | 15.0000000000000000 |  25 |    60
+ 26 | 16.0000000000000000 |  26 |    60
+ 27 | 17.0000000000000000 |  27 |    60
+ 28 | 18.0000000000000000 |  28 |    60
+ 29 | 19.0000000000000000 |  29 |    60
+ 30 | 10.0000000000000000 |  20 |    60
+ 31 | 11.0000000000000000 |  21 |    60
+ 32 | 12.0000000000000000 |  22 |    60
+ 33 | 13.0000000000000000 |  23 |    60
+ 34 | 14.0000000000000000 |  24 |    60
+ 35 | 15.0000000000000000 |  25 |    60
+ 36 | 16.0000000000000000 |  26 |    60
+ 37 | 17.0000000000000000 |  27 |    60
+ 38 | 18.0000000000000000 |  28 |    60
+ 39 | 19.0000000000000000 |  29 |    60
+ 40 | 10.0000000000000000 |  20 |    60
+ 41 | 11.0000000000000000 |  21 |    60
+ 42 | 12.0000000000000000 |  22 |    60
+ 43 | 13.0000000000000000 |  23 |    60
+ 44 | 14.0000000000000000 |  24 |    60
+ 45 | 15.0000000000000000 |  25 |    60
+ 46 | 16.0000000000000000 |  26 |    60
+ 47 | 17.0000000000000000 |  27 |    60
+ 48 | 18.0000000000000000 |  28 |    60
+ 49 | 19.0000000000000000 |  29 |    60
+(50 rows)
+
+-- Partial aggregates are safe to push down for all built-in aggregates
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT /* aggregate function <> aggpartialfunc */
+    array_agg(c_int4array), array_agg(b),
+    avg(b::int2), avg(b::int4), avg(b::int8), avg(c_interval), avg(b::float4), avg(b::float8), avg(b::numeric),
+    corr(b::float8, (b * b)::float8),
+    covar_pop(b::float8, (b * b)::float8),
+    covar_samp(b::float8, (b * b)::float8),
+    regr_avgx((2 * b)::float8, b::float8),
+    regr_avgy((2 * b)::float8, b::float8),
+    regr_intercept((2 * b + 3)::float8, b::float8),
+    regr_r2((b * b)::float8, b::float8),
+    regr_slope((2 * b + 3)::float8, b::float8),
+    regr_sxx((2 * b + 3)::float8, b::float8),
+    regr_sxy((b * b)::float8, b::float8),
+    regr_syy((2 * b + 3)::float8, b::float8),
+    stddev(b::float4), stddev(b::float8), stddev(b::int2), stddev(b::int4), stddev(b::int8), stddev(b::numeric),
+    stddev_pop(b::float4), stddev_pop(b::float8), stddev_pop(b::int2), stddev_pop(b::int4), stddev_pop(b::int8), stddev_pop(b::numeric),
+    stddev_samp(b::float4), stddev_samp(b::float8), stddev_samp(b::int2), stddev_samp(b::int4), stddev_samp(b::int8), stddev_samp(b::numeric),
+    string_agg(c_1c, ','), string_agg(c_1b, ','),
+    sum(b::int8), sum(b::numeric),
+    variance(b::float4), variance(b::float8), variance(b::int2), variance(b::int4), variance(b::int8), variance(b::numeric),
+    var_pop(b::float4), var_pop(b::float8), var_pop(b::int2), var_pop(b::int4), var_pop(b::int8), var_pop(b::numeric),
+    var_samp(b::float4), var_samp(b::float8), var_samp(b::int2), var_samp(b::int4), var_samp(b::int8), var_samp(b::numeric),
+    /* aggregate function = aggpartialfunc */
+    any_value(b * 0),
+    bit_and(c_bit), bit_and(c_1or3int2), bit_and(c_1or3int4), bit_and(c_1or3int8),
+    bit_or(c_bit), bit_or(c_1or3int2), bit_or(c_1or3int4), bit_or(c_1or3int8),
+    bit_xor(c_bit), bit_xor(c_1or3int2), bit_xor(c_1or3int4), bit_xor(c_1or3int8),
+    bool_and(c_bool),
+    bool_or(c_bool),
+    count(b), count(*),
+    every(c_bool),
+    max(c_int4array), max(c_enum), max(c_1c::char(1)), max('2000-01-01'::date + b), max('0.0.0.0'::inet + b), max(b::float4), max(b::float8), max(b::int2), max(b::int4), max(b::int8), max(c_interval), max(c_money), max(b::numeric), max(b::oid), max(c_pg_lsn), max(c_tid), max(c_1c), max(c_time), max(c_timetz), max(c_timestamp), max(c_timestamptz), max(c_xid8),
+    min(c_int4array), min(c_enum), min(c_1c::char(1)), min('2000-01-01'::date + b), min('0.0.0.0'::inet + b), min(b::float4), min(b::float8), min(b::int2), min(b::int4), min(b::int8), min(c_interval), min(c_money), min(b::numeric), min(b::oid), min(c_pg_lsn), min(c_tid), min(c_1c), min(c_time), min(c_timetz), min(c_timestamp), min(c_timestamptz), min(c_xid8),
+    range_intersect_agg(c_int4range), range_intersect_agg(c_int4multirange),
+    regr_count((2 * b + 3)::float8, b::float8),
+    sum(b::float4), sum(b::float8), sum(b::int2), sum(b::int4),	sum(c_interval), sum(c_money)
+  FROM pagg_tab WHERE c_serial between 1 and 30;
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        QUERY PLAN                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: array_agg(pagg_tab.c_int4array), array_agg(pagg_tab.b), avg((pagg_tab.b)::smallint), avg(pagg_tab.b), avg((pagg_tab.b)::bigint), avg(pagg_tab.c_interval), avg((pagg_tab.b)::real), avg((pagg_tab.b)::double precision), avg((pagg_tab.b)::numeric), corr((pagg_tab.b)::double precision, ((pagg_tab.b * pagg_tab.b))::double precision), covar_pop((pagg_tab.b)::double precision, ((pagg_tab.b * pagg_tab.b))::double precision), covar_samp((pagg_tab.b)::double precision, ((pagg_tab.b * pagg_tab.b))::double precision), regr_avgx(((2 * pagg_tab.b))::double precision, (pagg_tab.b)::double precision), regr_avgy(((2 * pagg_tab.b))::double precision, (pagg_tab.b)::double precision), regr_intercept((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision), regr_r2(((pagg_tab.b * pagg_tab.b))::double precision, (pagg_tab.b)::double precision), regr_slope((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision), regr_sxx((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision), regr_sxy(((pagg_tab.b * pagg_tab.b))::double precision, (pagg_tab.b)::double precision), regr_syy((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision), stddev((pagg_tab.b)::real), stddev((pagg_tab.b)::double precision), stddev((pagg_tab.b)::smallint), stddev(pagg_tab.b), stddev((pagg_tab.b)::bigint), stddev((pagg_tab.b)::numeric), stddev_pop((pagg_tab.b)::real), stddev_pop((pagg_tab.b)::double precision), stddev_pop((pagg_tab.b)::smallint), stddev_pop(pagg_tab.b), stddev_pop((pagg_tab.b)::bigint), stddev_pop((pagg_tab.b)::numeric), stddev_samp((pagg_tab.b)::real), stddev_samp((pagg_tab.b)::double precision), stddev_samp((pagg_tab.b)::smallint), stddev_samp(pagg_tab.b), stddev_samp((pagg_tab.b)::bigint), stddev_samp((pagg_tab.b)::numeric), string_agg(pagg_tab.c_1c, ','::text), string_agg(pagg_tab.c_1b, '\x2c'::bytea), sum((pagg_tab.b)::bigint), sum((pagg_tab.b)::numeric), variance((pagg_tab.b)::real), variance((pagg_tab.b)::double precision), variance((pagg_tab.b)::smallint), variance(pagg_tab.b), variance((pagg_tab.b)::bigint), variance((pagg_tab.b)::numeric), var_pop((pagg_tab.b)::real), var_pop((pagg_tab.b)::double precision), var_pop((pagg_tab.b)::smallint), var_pop(pagg_tab.b), var_pop((pagg_tab.b)::bigint), var_pop((pagg_tab.b)::numeric), var_samp((pagg_tab.b)::real), var_samp((pagg_tab.b)::double precision), var_samp((pagg_tab.b)::smallint), var_samp(pagg_tab.b), var_samp((pagg_tab.b)::bigint), var_samp((pagg_tab.b)::numeric), any_value((pagg_tab.b * 0)), bit_and(pagg_tab.c_bit), bit_and(pagg_tab.c_1or3int2), bit_and(pagg_tab.c_1or3int4), bit_and(pagg_tab.c_1or3int8), bit_or(pagg_tab.c_bit), bit_or(pagg_tab.c_1or3int2), bit_or(pagg_tab.c_1or3int4), bit_or(pagg_tab.c_1or3int8), bit_xor(pagg_tab.c_bit), bit_xor(pagg_tab.c_1or3int2), bit_xor(pagg_tab.c_1or3int4), bit_xor(pagg_tab.c_1or3int8), bool_and(pagg_tab.c_bool), bool_or(pagg_tab.c_bool), count(pagg_tab.b), count(*), every(pagg_tab.c_bool), max(pagg_tab.c_int4array), max(pagg_tab.c_enum), max((pagg_tab.c_1c)::character(1)), max(('01-01-2000'::date + pagg_tab.b)), max(('0.0.0.0'::inet + (pagg_tab.b)::bigint)), max((pagg_tab.b)::real), max((pagg_tab.b)::double precision), max((pagg_tab.b)::smallint), max(pagg_tab.b), max((pagg_tab.b)::bigint), max(pagg_tab.c_interval), max(pagg_tab.c_money), max((pagg_tab.b)::numeric), max((pagg_tab.b)::oid), max(pagg_tab.c_pg_lsn), max(pagg_tab.c_tid), max(pagg_tab.c_1c), max(pagg_tab.c_time), max(pagg_tab.c_timetz), max(pagg_tab.c_timestamp), max(pagg_tab.c_timestamptz), max(pagg_tab.c_xid8), min(pagg_tab.c_int4array), min(pagg_tab.c_enum), min((pagg_tab.c_1c)::character(1)), min(('01-01-2000'::date + pagg_tab.b)), min(('0.0.0.0'::inet + (pagg_tab.b)::bigint)), min((pagg_tab.b)::real), min((pagg_tab.b)::double precision), min((pagg_tab.b)::smallint), min(pagg_tab.b), min((pagg_tab.b)::bigint), min(pagg_tab.c_interval), min(pagg_tab.c_money), min((pagg_tab.b)::numeric), min((pagg_tab.b)::oid), min(pagg_tab.c_pg_lsn), min(pagg_tab.c_tid), min(pagg_tab.c_1c), min(pagg_tab.c_time), min(pagg_tab.c_timetz), min(pagg_tab.c_timestamp), min(pagg_tab.c_timestamptz), min(pagg_tab.c_xid8), range_intersect_agg(pagg_tab.c_int4range), range_intersect_agg(pagg_tab.c_int4multirange), regr_count((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision), sum((pagg_tab.b)::real), sum((pagg_tab.b)::double precision), sum((pagg_tab.b)::smallint), sum(pagg_tab.b), sum(pagg_tab.c_interval), sum(pagg_tab.c_money)
+   ->  Append
+         ->  Foreign Scan
+               Output: (PARTIAL array_agg(pagg_tab.c_int4array)), (PARTIAL array_agg(pagg_tab.b)), (PARTIAL avg((pagg_tab.b)::smallint)), (PARTIAL avg(pagg_tab.b)), (PARTIAL avg((pagg_tab.b)::bigint)), (PARTIAL avg(pagg_tab.c_interval)), (PARTIAL avg((pagg_tab.b)::real)), (PARTIAL avg((pagg_tab.b)::double precision)), (PARTIAL avg((pagg_tab.b)::numeric)), (PARTIAL corr((pagg_tab.b)::double precision, ((pagg_tab.b * pagg_tab.b))::double precision)), (PARTIAL covar_pop((pagg_tab.b)::double precision, ((pagg_tab.b * pagg_tab.b))::double precision)), (PARTIAL covar_samp((pagg_tab.b)::double precision, ((pagg_tab.b * pagg_tab.b))::double precision)), (PARTIAL regr_avgx(((2 * pagg_tab.b))::double precision, (pagg_tab.b)::double precision)), (PARTIAL regr_avgy(((2 * pagg_tab.b))::double precision, (pagg_tab.b)::double precision)), (PARTIAL regr_intercept((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision)), (PARTIAL regr_r2(((pagg_tab.b * pagg_tab.b))::double precision, (pagg_tab.b)::double precision)), (PARTIAL regr_slope((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision)), (PARTIAL regr_sxx((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision)), (PARTIAL regr_sxy(((pagg_tab.b * pagg_tab.b))::double precision, (pagg_tab.b)::double precision)), (PARTIAL regr_syy((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision)), (PARTIAL stddev((pagg_tab.b)::real)), (PARTIAL stddev((pagg_tab.b)::double precision)), (PARTIAL stddev((pagg_tab.b)::smallint)), (PARTIAL stddev(pagg_tab.b)), (PARTIAL stddev((pagg_tab.b)::bigint)), (PARTIAL stddev((pagg_tab.b)::numeric)), (PARTIAL stddev_pop((pagg_tab.b)::real)), (PARTIAL stddev_pop((pagg_tab.b)::double precision)), (PARTIAL stddev_pop((pagg_tab.b)::smallint)), (PARTIAL stddev_pop(pagg_tab.b)), (PARTIAL stddev_pop((pagg_tab.b)::bigint)), (PARTIAL stddev_pop((pagg_tab.b)::numeric)), (PARTIAL stddev_samp((pagg_tab.b)::real)), (PARTIAL stddev_samp((pagg_tab.b)::double precision)), (PARTIAL stddev_samp((pagg_tab.b)::smallint)), (PARTIAL stddev_samp(pagg_tab.b)), (PARTIAL stddev_samp((pagg_tab.b)::bigint)), (PARTIAL stddev_samp((pagg_tab.b)::numeric)), (PARTIAL string_agg(pagg_tab.c_1c, ','::text)), (PARTIAL string_agg(pagg_tab.c_1b, '\x2c'::bytea)), (PARTIAL sum((pagg_tab.b)::bigint)), (PARTIAL sum((pagg_tab.b)::numeric)), (PARTIAL variance((pagg_tab.b)::real)), (PARTIAL variance((pagg_tab.b)::double precision)), (PARTIAL variance((pagg_tab.b)::smallint)), (PARTIAL variance(pagg_tab.b)), (PARTIAL variance((pagg_tab.b)::bigint)), (PARTIAL variance((pagg_tab.b)::numeric)), (PARTIAL var_pop((pagg_tab.b)::real)), (PARTIAL var_pop((pagg_tab.b)::double precision)), (PARTIAL var_pop((pagg_tab.b)::smallint)), (PARTIAL var_pop(pagg_tab.b)), (PARTIAL var_pop((pagg_tab.b)::bigint)), (PARTIAL var_pop((pagg_tab.b)::numeric)), (PARTIAL var_samp((pagg_tab.b)::real)), (PARTIAL var_samp((pagg_tab.b)::double precision)), (PARTIAL var_samp((pagg_tab.b)::smallint)), (PARTIAL var_samp(pagg_tab.b)), (PARTIAL var_samp((pagg_tab.b)::bigint)), (PARTIAL var_samp((pagg_tab.b)::numeric)), (PARTIAL any_value((pagg_tab.b * 0))), (PARTIAL bit_and(pagg_tab.c_bit)), (PARTIAL bit_and(pagg_tab.c_1or3int2)), (PARTIAL bit_and(pagg_tab.c_1or3int4)), (PARTIAL bit_and(pagg_tab.c_1or3int8)), (PARTIAL bit_or(pagg_tab.c_bit)), (PARTIAL bit_or(pagg_tab.c_1or3int2)), (PARTIAL bit_or(pagg_tab.c_1or3int4)), (PARTIAL bit_or(pagg_tab.c_1or3int8)), (PARTIAL bit_xor(pagg_tab.c_bit)), (PARTIAL bit_xor(pagg_tab.c_1or3int2)), (PARTIAL bit_xor(pagg_tab.c_1or3int4)), (PARTIAL bit_xor(pagg_tab.c_1or3int8)), (PARTIAL bool_and(pagg_tab.c_bool)), (PARTIAL bool_or(pagg_tab.c_bool)), (PARTIAL count(pagg_tab.b)), (PARTIAL count(*)), (PARTIAL every(pagg_tab.c_bool)), (PARTIAL max(pagg_tab.c_int4array)), (PARTIAL max(pagg_tab.c_enum)), (PARTIAL max((pagg_tab.c_1c)::character(1))), (PARTIAL max(('01-01-2000'::date + pagg_tab.b))), (PARTIAL max(('0.0.0.0'::inet + (pagg_tab.b)::bigint))), (PARTIAL max((pagg_tab.b)::real)), (PARTIAL max((pagg_tab.b)::double precision)), (PARTIAL max((pagg_tab.b)::smallint)), (PARTIAL max(pagg_tab.b)), (PARTIAL max((pagg_tab.b)::bigint)), (PARTIAL max(pagg_tab.c_interval)), (PARTIAL max(pagg_tab.c_money)), (PARTIAL max((pagg_tab.b)::numeric)), (PARTIAL max((pagg_tab.b)::oid)), (PARTIAL max(pagg_tab.c_pg_lsn)), (PARTIAL max(pagg_tab.c_tid)), (PARTIAL max(pagg_tab.c_1c)), (PARTIAL max(pagg_tab.c_time)), (PARTIAL max(pagg_tab.c_timetz)), (PARTIAL max(pagg_tab.c_timestamp)), (PARTIAL max(pagg_tab.c_timestamptz)), (PARTIAL max(pagg_tab.c_xid8)), (PARTIAL min(pagg_tab.c_int4array)), (PARTIAL min(pagg_tab.c_enum)), (PARTIAL min((pagg_tab.c_1c)::character(1))), (PARTIAL min(('01-01-2000'::date + pagg_tab.b))), (PARTIAL min(('0.0.0.0'::inet + (pagg_tab.b)::bigint))), (PARTIAL min((pagg_tab.b)::real)), (PARTIAL min((pagg_tab.b)::double precision)), (PARTIAL min((pagg_tab.b)::smallint)), (PARTIAL min(pagg_tab.b)), (PARTIAL min((pagg_tab.b)::bigint)), (PARTIAL min(pagg_tab.c_interval)), (PARTIAL min(pagg_tab.c_money)), (PARTIAL min((pagg_tab.b)::numeric)), (PARTIAL min((pagg_tab.b)::oid)), (PARTIAL min(pagg_tab.c_pg_lsn)), (PARTIAL min(pagg_tab.c_tid)), (PARTIAL min(pagg_tab.c_1c)), (PARTIAL min(pagg_tab.c_time)), (PARTIAL min(pagg_tab.c_timetz)), (PARTIAL min(pagg_tab.c_timestamp)), (PARTIAL min(pagg_tab.c_timestamptz)), (PARTIAL min(pagg_tab.c_xid8)), (PARTIAL range_intersect_agg(pagg_tab.c_int4range)), (PARTIAL range_intersect_agg(pagg_tab.c_int4multirange)), (PARTIAL regr_count((((2 * pagg_tab.b) + 3))::double precision, (pagg_tab.b)::double precision)), (PARTIAL sum((pagg_tab.b)::real)), (PARTIAL sum((pagg_tab.b)::double precision)), (PARTIAL sum((pagg_tab.b)::smallint)), (PARTIAL sum(pagg_tab.b)), (PARTIAL sum(pagg_tab.c_interval)), (PARTIAL sum(pagg_tab.c_money))
+               Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab)
+               Remote SQL: SELECT array_agg_p_anyarray(c_int4array), array_agg_p_anynonarray(b), avg_p_int2(b::smallint), avg_p_int4(b), avg_p_int8(b::bigint), avg_p_interval(c_interval), avg_p_float4(b::real), avg_p_float8(b::double precision), avg_p_numeric(b::numeric), corr_p(b::double precision, (b * b)::double precision), covar_pop_p(b::double precision, (b * b)::double precision), covar_samp_p(b::double precision, (b * b)::double precision), regr_avgx_p((2 * b)::double precision, b::double precision), regr_avgy_p((2 * b)::double precision, b::double precision), regr_intercept_p(((2 * b) + 3)::double precision, b::double precision), regr_r2_p((b * b)::double precision, b::double precision), regr_slope_p(((2 * b) + 3)::double precision, b::double precision), regr_sxx_p(((2 * b) + 3)::double precision, b::double precision), regr_sxy_p((b * b)::double precision, b::double precision), regr_syy_p(((2 * b) + 3)::double precision, b::double precision), stddev_p_float4(b::real), stddev_p_float8(b::double precision), stddev_p_int2(b::smallint), stddev_p_int4(b), stddev_p_int8(b::bigint), stddev_p_numeric(b::numeric), stddev_pop_p_float4(b::real), stddev_pop_p_float8(b::double precision), stddev_pop_p_int2(b::smallint), stddev_pop_p_int4(b), stddev_pop_p_int8(b::bigint), stddev_pop_p_numeric(b::numeric), stddev_samp_p_float4(b::real), stddev_samp_p_float8(b::double precision), stddev_samp_p_int2(b::smallint), stddev_samp_p_int4(b), stddev_samp_p_int8(b::bigint), stddev_samp_p_numeric(b::numeric), string_agg_p_text_text(c_1c, ','::text), string_agg_p_bytea_bytea(c_1b, E'\\x2c'::bytea), sum_p_int8(b::bigint), sum_p_numeric(b::numeric), variance_p_float4(b::real), variance_p_float8(b::double precision), variance_p_int2(b::smallint), variance_p_int4(b), variance_p_int8(b::bigint), variance_p_numeric(b::numeric), var_pop_p_float4(b::real), var_pop_p_float8(b::double precision), var_pop_p_int2(b::smallint), var_pop_p_int4(b), var_pop_p_int8(b::bigint), var_pop_p_numeric(b::numeric), var_samp_p_float4(b::real), var_samp_p_float8(b::double precision), var_samp_p_int2(b::smallint), var_samp_p_int4(b), var_samp_p_int8(b::bigint), var_samp_p_numeric(b::numeric), any_value((b * 0)), bit_and(c_bit), bit_and(c_1or3int2), bit_and(c_1or3int4), bit_and(c_1or3int8), bit_or(c_bit), bit_or(c_1or3int2), bit_or(c_1or3int4), bit_or(c_1or3int8), bit_xor(c_bit), bit_xor(c_1or3int2), bit_xor(c_1or3int4), bit_xor(c_1or3int8), bool_and(c_bool), bool_or(c_bool), count(b), count(*), every(c_bool), max(c_int4array), max(c_enum), max(c_1c::character(1)), max(('01-01-2000'::date + b)), max(('0.0.0.0'::inet + b)), max(b::real), max(b::double precision), max(b::smallint), max(b), max(b::bigint), max(c_interval), max(c_money), max(b::numeric), max(b::oid), max(c_pg_lsn), max(c_tid), max(c_1c), max(c_time), max(c_timetz), max(c_timestamp), max(c_timestamptz), max(c_xid8), min(c_int4array), min(c_enum), min(c_1c::character(1)), min(('01-01-2000'::date + b)), min(('0.0.0.0'::inet + b)), min(b::real), min(b::double precision), min(b::smallint), min(b), min(b::bigint), min(c_interval), min(c_money), min(b::numeric), min(b::oid), min(c_pg_lsn), min(c_tid), min(c_1c), min(c_time), min(c_timetz), min(c_timestamp), min(c_timestamptz), min(c_xid8), range_intersect_agg(c_int4range), range_intersect_agg(c_int4multirange), regr_count(((2 * b) + 3)::double precision, b::double precision), sum(b::real), sum(b::double precision), sum(b::smallint), sum(b), sum(c_interval), sum(c_money) FROM public.pagg_tab_p1 WHERE ((c_serial >= 1)) AND ((c_serial <= 30))
+         ->  Foreign Scan
+               Output: (PARTIAL array_agg(pagg_tab_1.c_int4array)), (PARTIAL array_agg(pagg_tab_1.b)), (PARTIAL avg((pagg_tab_1.b)::smallint)), (PARTIAL avg(pagg_tab_1.b)), (PARTIAL avg((pagg_tab_1.b)::bigint)), (PARTIAL avg(pagg_tab_1.c_interval)), (PARTIAL avg((pagg_tab_1.b)::real)), (PARTIAL avg((pagg_tab_1.b)::double precision)), (PARTIAL avg((pagg_tab_1.b)::numeric)), (PARTIAL corr((pagg_tab_1.b)::double precision, ((pagg_tab_1.b * pagg_tab_1.b))::double precision)), (PARTIAL covar_pop((pagg_tab_1.b)::double precision, ((pagg_tab_1.b * pagg_tab_1.b))::double precision)), (PARTIAL covar_samp((pagg_tab_1.b)::double precision, ((pagg_tab_1.b * pagg_tab_1.b))::double precision)), (PARTIAL regr_avgx(((2 * pagg_tab_1.b))::double precision, (pagg_tab_1.b)::double precision)), (PARTIAL regr_avgy(((2 * pagg_tab_1.b))::double precision, (pagg_tab_1.b)::double precision)), (PARTIAL regr_intercept((((2 * pagg_tab_1.b) + 3))::double precision, (pagg_tab_1.b)::double precision)), (PARTIAL regr_r2(((pagg_tab_1.b * pagg_tab_1.b))::double precision, (pagg_tab_1.b)::double precision)), (PARTIAL regr_slope((((2 * pagg_tab_1.b) + 3))::double precision, (pagg_tab_1.b)::double precision)), (PARTIAL regr_sxx((((2 * pagg_tab_1.b) + 3))::double precision, (pagg_tab_1.b)::double precision)), (PARTIAL regr_sxy(((pagg_tab_1.b * pagg_tab_1.b))::double precision, (pagg_tab_1.b)::double precision)), (PARTIAL regr_syy((((2 * pagg_tab_1.b) + 3))::double precision, (pagg_tab_1.b)::double precision)), (PARTIAL stddev((pagg_tab_1.b)::real)), (PARTIAL stddev((pagg_tab_1.b)::double precision)), (PARTIAL stddev((pagg_tab_1.b)::smallint)), (PARTIAL stddev(pagg_tab_1.b)), (PARTIAL stddev((pagg_tab_1.b)::bigint)), (PARTIAL stddev((pagg_tab_1.b)::numeric)), (PARTIAL stddev_pop((pagg_tab_1.b)::real)), (PARTIAL stddev_pop((pagg_tab_1.b)::double precision)), (PARTIAL stddev_pop((pagg_tab_1.b)::smallint)), (PARTIAL stddev_pop(pagg_tab_1.b)), (PARTIAL stddev_pop((pagg_tab_1.b)::bigint)), (PARTIAL stddev_pop((pagg_tab_1.b)::numeric)), (PARTIAL stddev_samp((pagg_tab_1.b)::real)), (PARTIAL stddev_samp((pagg_tab_1.b)::double precision)), (PARTIAL stddev_samp((pagg_tab_1.b)::smallint)), (PARTIAL stddev_samp(pagg_tab_1.b)), (PARTIAL stddev_samp((pagg_tab_1.b)::bigint)), (PARTIAL stddev_samp((pagg_tab_1.b)::numeric)), (PARTIAL string_agg(pagg_tab_1.c_1c, ','::text)), (PARTIAL string_agg(pagg_tab_1.c_1b, '\x2c'::bytea)), (PARTIAL sum((pagg_tab_1.b)::bigint)), (PARTIAL sum((pagg_tab_1.b)::numeric)), (PARTIAL variance((pagg_tab_1.b)::real)), (PARTIAL variance((pagg_tab_1.b)::double precision)), (PARTIAL variance((pagg_tab_1.b)::smallint)), (PARTIAL variance(pagg_tab_1.b)), (PARTIAL variance((pagg_tab_1.b)::bigint)), (PARTIAL variance((pagg_tab_1.b)::numeric)), (PARTIAL var_pop((pagg_tab_1.b)::real)), (PARTIAL var_pop((pagg_tab_1.b)::double precision)), (PARTIAL var_pop((pagg_tab_1.b)::smallint)), (PARTIAL var_pop(pagg_tab_1.b)), (PARTIAL var_pop((pagg_tab_1.b)::bigint)), (PARTIAL var_pop((pagg_tab_1.b)::numeric)), (PARTIAL var_samp((pagg_tab_1.b)::real)), (PARTIAL var_samp((pagg_tab_1.b)::double precision)), (PARTIAL var_samp((pagg_tab_1.b)::smallint)), (PARTIAL var_samp(pagg_tab_1.b)), (PARTIAL var_samp((pagg_tab_1.b)::bigint)), (PARTIAL var_samp((pagg_tab_1.b)::numeric)), (PARTIAL any_value((pagg_tab_1.b * 0))), (PARTIAL bit_and(pagg_tab_1.c_bit)), (PARTIAL bit_and(pagg_tab_1.c_1or3int2)), (PARTIAL bit_and(pagg_tab_1.c_1or3int4)), (PARTIAL bit_and(pagg_tab_1.c_1or3int8)), (PARTIAL bit_or(pagg_tab_1.c_bit)), (PARTIAL bit_or(pagg_tab_1.c_1or3int2)), (PARTIAL bit_or(pagg_tab_1.c_1or3int4)), (PARTIAL bit_or(pagg_tab_1.c_1or3int8)), (PARTIAL bit_xor(pagg_tab_1.c_bit)), (PARTIAL bit_xor(pagg_tab_1.c_1or3int2)), (PARTIAL bit_xor(pagg_tab_1.c_1or3int4)), (PARTIAL bit_xor(pagg_tab_1.c_1or3int8)), (PARTIAL bool_and(pagg_tab_1.c_bool)), (PARTIAL bool_or(pagg_tab_1.c_bool)), (PARTIAL count(pagg_tab_1.b)), (PARTIAL count(*)), (PARTIAL every(pagg_tab_1.c_bool)), (PARTIAL max(pagg_tab_1.c_int4array)), (PARTIAL max(pagg_tab_1.c_enum)), (PARTIAL max((pagg_tab_1.c_1c)::character(1))), (PARTIAL max(('01-01-2000'::date + pagg_tab_1.b))), (PARTIAL max(('0.0.0.0'::inet + (pagg_tab_1.b)::bigint))), (PARTIAL max((pagg_tab_1.b)::real)), (PARTIAL max((pagg_tab_1.b)::double precision)), (PARTIAL max((pagg_tab_1.b)::smallint)), (PARTIAL max(pagg_tab_1.b)), (PARTIAL max((pagg_tab_1.b)::bigint)), (PARTIAL max(pagg_tab_1.c_interval)), (PARTIAL max(pagg_tab_1.c_money)), (PARTIAL max((pagg_tab_1.b)::numeric)), (PARTIAL max((pagg_tab_1.b)::oid)), (PARTIAL max(pagg_tab_1.c_pg_lsn)), (PARTIAL max(pagg_tab_1.c_tid)), (PARTIAL max(pagg_tab_1.c_1c)), (PARTIAL max(pagg_tab_1.c_time)), (PARTIAL max(pagg_tab_1.c_timetz)), (PARTIAL max(pagg_tab_1.c_timestamp)), (PARTIAL max(pagg_tab_1.c_timestamptz)), (PARTIAL max(pagg_tab_1.c_xid8)), (PARTIAL min(pagg_tab_1.c_int4array)), (PARTIAL min(pagg_tab_1.c_enum)), (PARTIAL min((pagg_tab_1.c_1c)::character(1))), (PARTIAL min(('01-01-2000'::date + pagg_tab_1.b))), (PARTIAL min(('0.0.0.0'::inet + (pagg_tab_1.b)::bigint))), (PARTIAL min((pagg_tab_1.b)::real)), (PARTIAL min((pagg_tab_1.b)::double precision)), (PARTIAL min((pagg_tab_1.b)::smallint)), (PARTIAL min(pagg_tab_1.b)), (PARTIAL min((pagg_tab_1.b)::bigint)), (PARTIAL min(pagg_tab_1.c_interval)), (PARTIAL min(pagg_tab_1.c_money)), (PARTIAL min((pagg_tab_1.b)::numeric)), (PARTIAL min((pagg_tab_1.b)::oid)), (PARTIAL min(pagg_tab_1.c_pg_lsn)), (PARTIAL min(pagg_tab_1.c_tid)), (PARTIAL min(pagg_tab_1.c_1c)), (PARTIAL min(pagg_tab_1.c_time)), (PARTIAL min(pagg_tab_1.c_timetz)), (PARTIAL min(pagg_tab_1.c_timestamp)), (PARTIAL min(pagg_tab_1.c_timestamptz)), (PARTIAL min(pagg_tab_1.c_xid8)), (PARTIAL range_intersect_agg(pagg_tab_1.c_int4range)), (PARTIAL range_intersect_agg(pagg_tab_1.c_int4multirange)), (PARTIAL regr_count((((2 * pagg_tab_1.b) + 3))::double precision, (pagg_tab_1.b)::double precision)), (PARTIAL sum((pagg_tab_1.b)::real)), (PARTIAL sum((pagg_tab_1.b)::double precision)), (PARTIAL sum((pagg_tab_1.b)::smallint)), (PARTIAL sum(pagg_tab_1.b)), (PARTIAL sum(pagg_tab_1.c_interval)), (PARTIAL sum(pagg_tab_1.c_money))
+               Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1)
+               Remote SQL: SELECT array_agg_p_anyarray(c_int4array), array_agg_p_anynonarray(b), avg_p_int2(b::smallint), avg_p_int4(b), avg_p_int8(b::bigint), avg_p_interval(c_interval), avg_p_float4(b::real), avg_p_float8(b::double precision), avg_p_numeric(b::numeric), corr_p(b::double precision, (b * b)::double precision), covar_pop_p(b::double precision, (b * b)::double precision), covar_samp_p(b::double precision, (b * b)::double precision), regr_avgx_p((2 * b)::double precision, b::double precision), regr_avgy_p((2 * b)::double precision, b::double precision), regr_intercept_p(((2 * b) + 3)::double precision, b::double precision), regr_r2_p((b * b)::double precision, b::double precision), regr_slope_p(((2 * b) + 3)::double precision, b::double precision), regr_sxx_p(((2 * b) + 3)::double precision, b::double precision), regr_sxy_p((b * b)::double precision, b::double precision), regr_syy_p(((2 * b) + 3)::double precision, b::double precision), stddev_p_float4(b::real), stddev_p_float8(b::double precision), stddev_p_int2(b::smallint), stddev_p_int4(b), stddev_p_int8(b::bigint), stddev_p_numeric(b::numeric), stddev_pop_p_float4(b::real), stddev_pop_p_float8(b::double precision), stddev_pop_p_int2(b::smallint), stddev_pop_p_int4(b), stddev_pop_p_int8(b::bigint), stddev_pop_p_numeric(b::numeric), stddev_samp_p_float4(b::real), stddev_samp_p_float8(b::double precision), stddev_samp_p_int2(b::smallint), stddev_samp_p_int4(b), stddev_samp_p_int8(b::bigint), stddev_samp_p_numeric(b::numeric), string_agg_p_text_text(c_1c, ','::text), string_agg_p_bytea_bytea(c_1b, E'\\x2c'::bytea), sum_p_int8(b::bigint), sum_p_numeric(b::numeric), variance_p_float4(b::real), variance_p_float8(b::double precision), variance_p_int2(b::smallint), variance_p_int4(b), variance_p_int8(b::bigint), variance_p_numeric(b::numeric), var_pop_p_float4(b::real), var_pop_p_float8(b::double precision), var_pop_p_int2(b::smallint), var_pop_p_int4(b), var_pop_p_int8(b::bigint), var_pop_p_numeric(b::numeric), var_samp_p_float4(b::real), var_samp_p_float8(b::double precision), var_samp_p_int2(b::smallint), var_samp_p_int4(b), var_samp_p_int8(b::bigint), var_samp_p_numeric(b::numeric), any_value((b * 0)), bit_and(c_bit), bit_and(c_1or3int2), bit_and(c_1or3int4), bit_and(c_1or3int8), bit_or(c_bit), bit_or(c_1or3int2), bit_or(c_1or3int4), bit_or(c_1or3int8), bit_xor(c_bit), bit_xor(c_1or3int2), bit_xor(c_1or3int4), bit_xor(c_1or3int8), bool_and(c_bool), bool_or(c_bool), count(b), count(*), every(c_bool), max(c_int4array), max(c_enum), max(c_1c::character(1)), max(('01-01-2000'::date + b)), max(('0.0.0.0'::inet + b)), max(b::real), max(b::double precision), max(b::smallint), max(b), max(b::bigint), max(c_interval), max(c_money), max(b::numeric), max(b::oid), max(c_pg_lsn), max(c_tid), max(c_1c), max(c_time), max(c_timetz), max(c_timestamp), max(c_timestamptz), max(c_xid8), min(c_int4array), min(c_enum), min(c_1c::character(1)), min(('01-01-2000'::date + b)), min(('0.0.0.0'::inet + b)), min(b::real), min(b::double precision), min(b::smallint), min(b), min(b::bigint), min(c_interval), min(c_money), min(b::numeric), min(b::oid), min(c_pg_lsn), min(c_tid), min(c_1c), min(c_time), min(c_timetz), min(c_timestamp), min(c_timestamptz), min(c_xid8), range_intersect_agg(c_int4range), range_intersect_agg(c_int4multirange), regr_count(((2 * b) + 3)::double precision, b::double precision), sum(b::real), sum(b::double precision), sum(b::smallint), sum(b), sum(c_interval), sum(c_money) FROM public.pagg_tab_p2 WHERE ((c_serial >= 1)) AND ((c_serial <= 30))
+         ->  Foreign Scan
+               Output: (PARTIAL array_agg(pagg_tab_2.c_int4array)), (PARTIAL array_agg(pagg_tab_2.b)), (PARTIAL avg((pagg_tab_2.b)::smallint)), (PARTIAL avg(pagg_tab_2.b)), (PARTIAL avg((pagg_tab_2.b)::bigint)), (PARTIAL avg(pagg_tab_2.c_interval)), (PARTIAL avg((pagg_tab_2.b)::real)), (PARTIAL avg((pagg_tab_2.b)::double precision)), (PARTIAL avg((pagg_tab_2.b)::numeric)), (PARTIAL corr((pagg_tab_2.b)::double precision, ((pagg_tab_2.b * pagg_tab_2.b))::double precision)), (PARTIAL covar_pop((pagg_tab_2.b)::double precision, ((pagg_tab_2.b * pagg_tab_2.b))::double precision)), (PARTIAL covar_samp((pagg_tab_2.b)::double precision, ((pagg_tab_2.b * pagg_tab_2.b))::double precision)), (PARTIAL regr_avgx(((2 * pagg_tab_2.b))::double precision, (pagg_tab_2.b)::double precision)), (PARTIAL regr_avgy(((2 * pagg_tab_2.b))::double precision, (pagg_tab_2.b)::double precision)), (PARTIAL regr_intercept((((2 * pagg_tab_2.b) + 3))::double precision, (pagg_tab_2.b)::double precision)), (PARTIAL regr_r2(((pagg_tab_2.b * pagg_tab_2.b))::double precision, (pagg_tab_2.b)::double precision)), (PARTIAL regr_slope((((2 * pagg_tab_2.b) + 3))::double precision, (pagg_tab_2.b)::double precision)), (PARTIAL regr_sxx((((2 * pagg_tab_2.b) + 3))::double precision, (pagg_tab_2.b)::double precision)), (PARTIAL regr_sxy(((pagg_tab_2.b * pagg_tab_2.b))::double precision, (pagg_tab_2.b)::double precision)), (PARTIAL regr_syy((((2 * pagg_tab_2.b) + 3))::double precision, (pagg_tab_2.b)::double precision)), (PARTIAL stddev((pagg_tab_2.b)::real)), (PARTIAL stddev((pagg_tab_2.b)::double precision)), (PARTIAL stddev((pagg_tab_2.b)::smallint)), (PARTIAL stddev(pagg_tab_2.b)), (PARTIAL stddev((pagg_tab_2.b)::bigint)), (PARTIAL stddev((pagg_tab_2.b)::numeric)), (PARTIAL stddev_pop((pagg_tab_2.b)::real)), (PARTIAL stddev_pop((pagg_tab_2.b)::double precision)), (PARTIAL stddev_pop((pagg_tab_2.b)::smallint)), (PARTIAL stddev_pop(pagg_tab_2.b)), (PARTIAL stddev_pop((pagg_tab_2.b)::bigint)), (PARTIAL stddev_pop((pagg_tab_2.b)::numeric)), (PARTIAL stddev_samp((pagg_tab_2.b)::real)), (PARTIAL stddev_samp((pagg_tab_2.b)::double precision)), (PARTIAL stddev_samp((pagg_tab_2.b)::smallint)), (PARTIAL stddev_samp(pagg_tab_2.b)), (PARTIAL stddev_samp((pagg_tab_2.b)::bigint)), (PARTIAL stddev_samp((pagg_tab_2.b)::numeric)), (PARTIAL string_agg(pagg_tab_2.c_1c, ','::text)), (PARTIAL string_agg(pagg_tab_2.c_1b, '\x2c'::bytea)), (PARTIAL sum((pagg_tab_2.b)::bigint)), (PARTIAL sum((pagg_tab_2.b)::numeric)), (PARTIAL variance((pagg_tab_2.b)::real)), (PARTIAL variance((pagg_tab_2.b)::double precision)), (PARTIAL variance((pagg_tab_2.b)::smallint)), (PARTIAL variance(pagg_tab_2.b)), (PARTIAL variance((pagg_tab_2.b)::bigint)), (PARTIAL variance((pagg_tab_2.b)::numeric)), (PARTIAL var_pop((pagg_tab_2.b)::real)), (PARTIAL var_pop((pagg_tab_2.b)::double precision)), (PARTIAL var_pop((pagg_tab_2.b)::smallint)), (PARTIAL var_pop(pagg_tab_2.b)), (PARTIAL var_pop((pagg_tab_2.b)::bigint)), (PARTIAL var_pop((pagg_tab_2.b)::numeric)), (PARTIAL var_samp((pagg_tab_2.b)::real)), (PARTIAL var_samp((pagg_tab_2.b)::double precision)), (PARTIAL var_samp((pagg_tab_2.b)::smallint)), (PARTIAL var_samp(pagg_tab_2.b)), (PARTIAL var_samp((pagg_tab_2.b)::bigint)), (PARTIAL var_samp((pagg_tab_2.b)::numeric)), (PARTIAL any_value((pagg_tab_2.b * 0))), (PARTIAL bit_and(pagg_tab_2.c_bit)), (PARTIAL bit_and(pagg_tab_2.c_1or3int2)), (PARTIAL bit_and(pagg_tab_2.c_1or3int4)), (PARTIAL bit_and(pagg_tab_2.c_1or3int8)), (PARTIAL bit_or(pagg_tab_2.c_bit)), (PARTIAL bit_or(pagg_tab_2.c_1or3int2)), (PARTIAL bit_or(pagg_tab_2.c_1or3int4)), (PARTIAL bit_or(pagg_tab_2.c_1or3int8)), (PARTIAL bit_xor(pagg_tab_2.c_bit)), (PARTIAL bit_xor(pagg_tab_2.c_1or3int2)), (PARTIAL bit_xor(pagg_tab_2.c_1or3int4)), (PARTIAL bit_xor(pagg_tab_2.c_1or3int8)), (PARTIAL bool_and(pagg_tab_2.c_bool)), (PARTIAL bool_or(pagg_tab_2.c_bool)), (PARTIAL count(pagg_tab_2.b)), (PARTIAL count(*)), (PARTIAL every(pagg_tab_2.c_bool)), (PARTIAL max(pagg_tab_2.c_int4array)), (PARTIAL max(pagg_tab_2.c_enum)), (PARTIAL max((pagg_tab_2.c_1c)::character(1))), (PARTIAL max(('01-01-2000'::date + pagg_tab_2.b))), (PARTIAL max(('0.0.0.0'::inet + (pagg_tab_2.b)::bigint))), (PARTIAL max((pagg_tab_2.b)::real)), (PARTIAL max((pagg_tab_2.b)::double precision)), (PARTIAL max((pagg_tab_2.b)::smallint)), (PARTIAL max(pagg_tab_2.b)), (PARTIAL max((pagg_tab_2.b)::bigint)), (PARTIAL max(pagg_tab_2.c_interval)), (PARTIAL max(pagg_tab_2.c_money)), (PARTIAL max((pagg_tab_2.b)::numeric)), (PARTIAL max((pagg_tab_2.b)::oid)), (PARTIAL max(pagg_tab_2.c_pg_lsn)), (PARTIAL max(pagg_tab_2.c_tid)), (PARTIAL max(pagg_tab_2.c_1c)), (PARTIAL max(pagg_tab_2.c_time)), (PARTIAL max(pagg_tab_2.c_timetz)), (PARTIAL max(pagg_tab_2.c_timestamp)), (PARTIAL max(pagg_tab_2.c_timestamptz)), (PARTIAL max(pagg_tab_2.c_xid8)), (PARTIAL min(pagg_tab_2.c_int4array)), (PARTIAL min(pagg_tab_2.c_enum)), (PARTIAL min((pagg_tab_2.c_1c)::character(1))), (PARTIAL min(('01-01-2000'::date + pagg_tab_2.b))), (PARTIAL min(('0.0.0.0'::inet + (pagg_tab_2.b)::bigint))), (PARTIAL min((pagg_tab_2.b)::real)), (PARTIAL min((pagg_tab_2.b)::double precision)), (PARTIAL min((pagg_tab_2.b)::smallint)), (PARTIAL min(pagg_tab_2.b)), (PARTIAL min((pagg_tab_2.b)::bigint)), (PARTIAL min(pagg_tab_2.c_interval)), (PARTIAL min(pagg_tab_2.c_money)), (PARTIAL min((pagg_tab_2.b)::numeric)), (PARTIAL min((pagg_tab_2.b)::oid)), (PARTIAL min(pagg_tab_2.c_pg_lsn)), (PARTIAL min(pagg_tab_2.c_tid)), (PARTIAL min(pagg_tab_2.c_1c)), (PARTIAL min(pagg_tab_2.c_time)), (PARTIAL min(pagg_tab_2.c_timetz)), (PARTIAL min(pagg_tab_2.c_timestamp)), (PARTIAL min(pagg_tab_2.c_timestamptz)), (PARTIAL min(pagg_tab_2.c_xid8)), (PARTIAL range_intersect_agg(pagg_tab_2.c_int4range)), (PARTIAL range_intersect_agg(pagg_tab_2.c_int4multirange)), (PARTIAL regr_count((((2 * pagg_tab_2.b) + 3))::double precision, (pagg_tab_2.b)::double precision)), (PARTIAL sum((pagg_tab_2.b)::real)), (PARTIAL sum((pagg_tab_2.b)::double precision)), (PARTIAL sum((pagg_tab_2.b)::smallint)), (PARTIAL sum(pagg_tab_2.b)), (PARTIAL sum(pagg_tab_2.c_interval)), (PARTIAL sum(pagg_tab_2.c_money))
+               Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2)
+               Remote SQL: SELECT array_agg_p_anyarray(c_int4array), array_agg_p_anynonarray(b), avg_p_int2(b::smallint), avg_p_int4(b), avg_p_int8(b::bigint), avg_p_interval(c_interval), avg_p_float4(b::real), avg_p_float8(b::double precision), avg_p_numeric(b::numeric), corr_p(b::double precision, (b * b)::double precision), covar_pop_p(b::double precision, (b * b)::double precision), covar_samp_p(b::double precision, (b * b)::double precision), regr_avgx_p((2 * b)::double precision, b::double precision), regr_avgy_p((2 * b)::double precision, b::double precision), regr_intercept_p(((2 * b) + 3)::double precision, b::double precision), regr_r2_p((b * b)::double precision, b::double precision), regr_slope_p(((2 * b) + 3)::double precision, b::double precision), regr_sxx_p(((2 * b) + 3)::double precision, b::double precision), regr_sxy_p((b * b)::double precision, b::double precision), regr_syy_p(((2 * b) + 3)::double precision, b::double precision), stddev_p_float4(b::real), stddev_p_float8(b::double precision), stddev_p_int2(b::smallint), stddev_p_int4(b), stddev_p_int8(b::bigint), stddev_p_numeric(b::numeric), stddev_pop_p_float4(b::real), stddev_pop_p_float8(b::double precision), stddev_pop_p_int2(b::smallint), stddev_pop_p_int4(b), stddev_pop_p_int8(b::bigint), stddev_pop_p_numeric(b::numeric), stddev_samp_p_float4(b::real), stddev_samp_p_float8(b::double precision), stddev_samp_p_int2(b::smallint), stddev_samp_p_int4(b), stddev_samp_p_int8(b::bigint), stddev_samp_p_numeric(b::numeric), string_agg_p_text_text(c_1c, ','::text), string_agg_p_bytea_bytea(c_1b, E'\\x2c'::bytea), sum_p_int8(b::bigint), sum_p_numeric(b::numeric), variance_p_float4(b::real), variance_p_float8(b::double precision), variance_p_int2(b::smallint), variance_p_int4(b), variance_p_int8(b::bigint), variance_p_numeric(b::numeric), var_pop_p_float4(b::real), var_pop_p_float8(b::double precision), var_pop_p_int2(b::smallint), var_pop_p_int4(b), var_pop_p_int8(b::bigint), var_pop_p_numeric(b::numeric), var_samp_p_float4(b::real), var_samp_p_float8(b::double precision), var_samp_p_int2(b::smallint), var_samp_p_int4(b), var_samp_p_int8(b::bigint), var_samp_p_numeric(b::numeric), any_value((b * 0)), bit_and(c_bit), bit_and(c_1or3int2), bit_and(c_1or3int4), bit_and(c_1or3int8), bit_or(c_bit), bit_or(c_1or3int2), bit_or(c_1or3int4), bit_or(c_1or3int8), bit_xor(c_bit), bit_xor(c_1or3int2), bit_xor(c_1or3int4), bit_xor(c_1or3int8), bool_and(c_bool), bool_or(c_bool), count(b), count(*), every(c_bool), max(c_int4array), max(c_enum), max(c_1c::character(1)), max(('01-01-2000'::date + b)), max(('0.0.0.0'::inet + b)), max(b::real), max(b::double precision), max(b::smallint), max(b), max(b::bigint), max(c_interval), max(c_money), max(b::numeric), max(b::oid), max(c_pg_lsn), max(c_tid), max(c_1c), max(c_time), max(c_timetz), max(c_timestamp), max(c_timestamptz), max(c_xid8), min(c_int4array), min(c_enum), min(c_1c::character(1)), min(('01-01-2000'::date + b)), min(('0.0.0.0'::inet + b)), min(b::real), min(b::double precision), min(b::smallint), min(b), min(b::bigint), min(c_interval), min(c_money), min(b::numeric), min(b::oid), min(c_pg_lsn), min(c_tid), min(c_1c), min(c_time), min(c_timetz), min(c_timestamp), min(c_timestamptz), min(c_xid8), range_intersect_agg(c_int4range), range_intersect_agg(c_int4multirange), regr_count(((2 * b) + 3)::double precision, b::double precision), sum(b::real), sum(b::double precision), sum(b::smallint), sum(b), sum(c_interval), sum(c_money) FROM public.pagg_tab_p3 WHERE ((c_serial >= 1)) AND ((c_serial <= 30))
+(15 rows)
+
+SELECT /* aggregate function <> aggpartialfunc */
+    array_agg(c_int4array), array_agg(b),
+    avg(b::int2), avg(b::int4), avg(b::int8), avg(c_interval),
+    avg(b::float4), avg(b::float8),
+    corr(b::float8, (b * b)::float8),
+    covar_pop(b::float8, (b * b)::float8),
+    covar_samp(b::float8, (b * b)::float8),
+    regr_avgx((2 * b)::float8, b::float8),
+    regr_avgy((2 * b)::float8, b::float8),
+    regr_intercept((2 * b + 3)::float8, b::float8),
+    regr_r2((b * b)::float8, b::float8),
+    regr_slope((2 * b + 3)::float8, b::float8),
+    regr_sxx((2 * b + 3)::float8, b::float8),
+    regr_sxy((b * b)::float8, b::float8),
+    regr_syy((2 * b + 3)::float8, b::float8),
+    stddev(b::float4), stddev(b::float8), stddev(b::int2), stddev(b::int4), stddev(b::int8), stddev(b::numeric),
+    stddev_pop(b::float4), stddev_pop(b::float8), stddev_pop(b::int2), stddev_pop(b::int4), stddev_pop(b::int8), stddev_pop(b::numeric),
+    stddev_samp(b::float4), stddev_samp(b::float8), stddev_samp(b::int2), stddev_samp(b::int4), stddev_samp(b::int8), stddev_samp(b::numeric),
+    string_agg(c_1c, ','), string_agg(c_1b, ','),
+    sum(b::int8), sum(b::numeric),
+    variance(b::float4), variance(b::float8), variance(b::int2), variance(b::int4), variance(b::int8), variance(b::numeric),
+    var_pop(b::float4), var_pop(b::float8), var_pop(b::int2), var_pop(b::int4), var_pop(b::int8), var_pop(b::numeric),
+    var_samp(b::float4), var_samp(b::float8), var_samp(b::int2), var_samp(b::int4), var_samp(b::int8), var_samp(b::numeric),
+    /* aggregate function = aggpartialfunc */
+    any_value(b * 0),
+    bit_and(c_bit), bit_and(c_1or3int2), bit_and(c_1or3int4), bit_and(c_1or3int8),
+    bit_or(c_bit), bit_or(c_1or3int2), bit_or(c_1or3int4), bit_or(c_1or3int8),
+    bit_xor(c_bit), bit_xor(c_1or3int2), bit_xor(c_1or3int4), bit_xor(c_1or3int8),
+    bool_and(c_bool),
+    bool_or(c_bool),
+    count(b), count(*),
+    every(c_bool),
+    max(c_int4array), max(c_enum), max(c_1c::char(1)), max('2000-01-01'::date + b), max('0.0.0.0'::inet + b), max(b::float4), max(b::float8), max(b::int2), max(b::int4), max(b::int8), max(c_interval), max(c_money), max(b::numeric), max(b::oid), max(c_pg_lsn), max(c_tid), max(c_1c), max(c_time), max(c_timetz), max(c_timestamp), max(c_timestamptz), max(c_xid8),
+    min(c_int4array), min(c_enum), min(c_1c::char(1)), min('2000-01-01'::date + b), min('0.0.0.0'::inet + b), min(b::float4), min(b::float8), min(b::int2), min(b::int4), min(b::int8), min(c_interval), min(c_money), min(b::numeric), min(b::oid), min(c_pg_lsn), min(c_tid), min(c_1c), min(c_time), min(c_timetz), min(c_timestamp), min(c_timestamptz), min(c_xid8),
+    range_intersect_agg(c_int4range), range_intersect_agg(c_int4multirange),
+    regr_count((2 * b + 3)::float8, b::float8),
+    sum(b::float4), sum(b::float8), sum(b::int2), sum(b::int4),	sum(c_interval), sum(c_money)
+  FROM pagg_tab WHERE c_serial between 1 and 30;
+                                                                                       array_agg                                                                                       |                                     array_agg                                      |         avg         |         avg         |         avg         |    avg     | avg  | avg  |        corr        |     covar_pop      | covar_samp | regr_avgx | regr_avgy | regr_intercept |      regr_r2       | regr_slope | regr_sxx | regr_sxy | regr_syy |      stddev       |      stddev       |       stddev       |       stddev       |       stddev       |       stddev       |    stddev_pop    |    stddev_pop    |     stddev_pop     |     stddev_pop     |     stddev_pop     |     stddev_pop     |    stddev_samp    |    stddev_samp    |    stddev_samp     |    stddev_samp     |    stddev_samp     |    stddev_samp     |                         string_agg                          |                                                        string_agg                                                        | sum | sum | variance | variance |      variance       |      variance       |      variance       |      variance       |      var_pop      |      var_pop      |       var_pop       |       var_pop       |       var_pop       |       var_pop       | var_samp | var_samp |      var_samp       |      var_samp       |      var_samp       |      var_samp       | any_value | bit_and | bit_and | bit_and | bit_and | bit_or | bit_or | bit_or | bit_or | bit_xor | bit_xor | bit_xor | bit_xor | bool_and | bool_or | count | count | every |  max  |  max  | max |    max     |   max    | max | max | max | max | max |   max   |  max  | max | max | max  |  max   | max |   max    |     max     |           max            |             max              | max |  min  | min | min |    min     |   min   | min | min | min | min | min | min |  min  | min | min | min |  min  | min |   min    |     min     |           min            |             min              | min | range_intersect_agg | range_intersect_agg | regr_count | sum | sum | sum | sum |    sum    |  sum   
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------+---------------------+---------------------+---------------------+------------+------+------+--------------------+--------------------+------------+-----------+-----------+----------------+--------------------+------------+----------+----------+----------+-------------------+-------------------+--------------------+--------------------+--------------------+--------------------+------------------+------------------+--------------------+--------------------+--------------------+--------------------+-------------------+-------------------+--------------------+--------------------+--------------------+--------------------+-------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------+-----+-----+----------+----------+---------------------+---------------------+---------------------+---------------------+-------------------+-------------------+---------------------+---------------------+---------------------+---------------------+----------+----------+---------------------+---------------------+---------------------+---------------------+-----------+---------+---------+---------+---------+--------+--------+--------+--------+---------+---------+---------+---------+----------+---------+-------+-------+-------+-------+-------+-----+------------+----------+-----+-----+-----+-----+-----+---------+-------+-----+-----+------+--------+-----+----------+-------------+--------------------------+------------------------------+-----+-------+-----+-----+------------+---------+-----+-----+-----+-----+-----+-----+-------+-----+-----+-----+-------+-----+----------+-------------+--------------------------+------------------------------+-----+---------------------+---------------------+------------+-----+-----+-----+-----+-----------+--------
+ {{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0},{0,0},{1,0}} | {1,2,3,4,5,6,7,8,9,30,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29} | 15.5000000000000000 | 15.5000000000000000 | 15.5000000000000000 | @ 0.5 secs | 15.5 | 15.5 | 0.9702989135892578 | 2322.4166666666665 |     2402.5 |      15.5 |        31 |              3 | 0.9414799817124941 |          2 |   2247.5 |  69672.5 |     8990 | 8.803408430829505 | 8.803408430829505 | 8.8034084308295046 | 8.8034084308295046 | 8.8034084308295046 | 8.8034084308295046 | 8.65544144839919 | 8.65544144839919 | 8.6554414483991899 | 8.6554414483991899 | 8.6554414483991899 | 8.6554414483991899 | 8.803408430829505 | 8.803408430829505 | 8.8034084308295046 | 8.8034084308295046 | 8.8034084308295046 | 8.8034084308295046 | 0,1,2,3,4,5,6,7,8,9,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8 | \x302c312c322c332c342c352c362c372c382c392c392c302c312c322c332c342c352c362c372c382c392c302c312c322c332c342c352c362c372c38 | 465 | 465 |     77.5 |     77.5 | 77.5000000000000000 | 77.5000000000000000 | 77.5000000000000000 | 77.5000000000000000 | 74.91666666666667 | 74.91666666666667 | 74.9166666666666667 | 74.9166666666666667 | 74.9166666666666667 | 74.9166666666666667 |     77.5 |     77.5 | 77.5000000000000000 | 77.5000000000000000 | 77.5000000000000000 | 77.5000000000000000 |         0 | 01      |       1 |       1 |       1 | 11     |      3 |      3 |      3 | 10      |       2 |       2 |       2 | f        | t       |    30 |    30 | f     | {1,0} | happy | 9   | 01-31-2000 | 0.0.0.30 |  30 |  30 |  30 |  30 |  30 | @ 1 sec | $1.00 |  30 |  30 | 0/30 | (0,30) | 9   | 00:00:30 | 00:00:30-07 | Sat Jan 01 00:00:30 2000 | Sat Jan 01 00:00:30 2000 PST |   9 | {0,0} | sad | 0   | 01-02-2000 | 0.0.0.1 |   1 |   1 |   1 |   1 |   1 | @ 0 | $0.00 |   1 |   1 | 0/1 | (0,1) | 0   | 00:00:01 | 00:00:01-07 | Sat Jan 01 00:00:01 2000 | Sat Jan 01 00:00:01 2000 PST |   0 | [0,1)               | {[0,1),[100,101)}   |         30 | 465 | 465 | 465 | 465 | @ 15 secs | $15.00
+(1 row)
+
+-- List of aggregate functions such that the aggregate function supports partial aggregate
+-- and aggpartialfn field of the aggregate function is invalid
+SELECT aggfnoid::text
+  FROM pg_aggregate a JOIN pg_type b ON a.aggtranstype = b.oid
+    WHERE (aggcombinefn::text <> '-') AND (aggpartialfn::text = '-')
+  ORDER BY 1;
+ aggfnoid 
+----------
+(0 rows)
+
+-- List of aggregate functions which has incorrect aggpartialfunc
+SELECT a.aggfnoid
+  FROM pg_aggregate a JOIN pg_type b ON a.aggtranstype = b.oid
+       JOIN pg_aggregate c on a.aggpartialfn = c.aggfnoid
+    WHERE (a.aggcombinefn::text <> '-')
+          AND (a.aggpartialfn::text <> '-')
+          AND (a.aggtransfn <> c.aggtransfn
+               OR (c.aggcombinefn::text <> '-'
+                   AND a.aggcombinefn <> c.aggcombinefn)
+               OR (a.agginitval IS NOT NULL
+                   AND c.agginitval IS NOT NULL
+                   AND a.agginitval <> c.agginitval)
+               OR (b.typname = 'internal'
+                   AND c.aggserialfn::text <> '-'
+                   AND a.aggserialfn <> c.aggserialfn)
+               OR (b.typname = 'internal'
+                   AND c.aggdeserialfn::text <> '-'
+                   AND a.aggdeserialfn <> c.aggdeserialfn)
+               OR (b.typname = 'internal'
+                   AND a.aggserialfn <> c.aggfinalfn)
+               OR (b.typname <> 'internal'
+                   AND c.aggfinalfn::text <> '-'))
+  ORDER BY 1;
+ aggfnoid 
+----------
+(0 rows)
+
+-- It's unsafe to push down partial aggregates which contain distinct clause
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT max(a), count(distinct b) FROM pagg_tab;
+                                       QUERY PLAN                                        
+-----------------------------------------------------------------------------------------
+ Aggregate
+   Output: max(pagg_tab.a), count(DISTINCT pagg_tab.b)
+   ->  Merge Append
+         Sort Key: pagg_tab.b
+         ->  Foreign Scan on public.fpagg_tab_p1 pagg_tab_1
+               Output: pagg_tab_1.a, pagg_tab_1.b
+               Remote SQL: SELECT a, b FROM public.pagg_tab_p1 ORDER BY b ASC NULLS LAST
+         ->  Foreign Scan on public.fpagg_tab_p2 pagg_tab_2
+               Output: pagg_tab_2.a, pagg_tab_2.b
+               Remote SQL: SELECT a, b FROM public.pagg_tab_p2 ORDER BY b ASC NULLS LAST
+         ->  Foreign Scan on public.fpagg_tab_p3 pagg_tab_3
+               Output: pagg_tab_3.a, pagg_tab_3.b
+               Remote SQL: SELECT a, b FROM public.pagg_tab_p3 ORDER BY b ASC NULLS LAST
+(13 rows)
+
+SELECT max(a), count(distinct b) FROM pagg_tab;
+ max | count 
+-----+-------
+  29 |    50
+(1 row)
+
+-- It's unsafe to push down partial aggregates which contain order by clause
+EXPLAIN (VERBOSE, COSTS OFF) SELECT array_agg(b order by b) FROM pagg_tab WHERE c_serial between 1 and 30;
+                                                   QUERY PLAN                                                    
+-----------------------------------------------------------------------------------------------------------------
+ Aggregate
+   Output: array_agg(pagg_tab.b ORDER BY pagg_tab.b)
+   ->  Sort
+         Output: pagg_tab.b
+         Sort Key: pagg_tab.b
+         ->  Append
+               ->  Foreign Scan on public.fpagg_tab_p1 pagg_tab_1
+                     Output: pagg_tab_1.b
+                     Remote SQL: SELECT b FROM public.pagg_tab_p1 WHERE ((c_serial >= 1)) AND ((c_serial <= 30))
+               ->  Foreign Scan on public.fpagg_tab_p2 pagg_tab_2
+                     Output: pagg_tab_2.b
+                     Remote SQL: SELECT b FROM public.pagg_tab_p2 WHERE ((c_serial >= 1)) AND ((c_serial <= 30))
+               ->  Foreign Scan on public.fpagg_tab_p3 pagg_tab_3
+                     Output: pagg_tab_3.b
+                     Remote SQL: SELECT b FROM public.pagg_tab_p3 WHERE ((c_serial >= 1)) AND ((c_serial <= 30))
+(15 rows)
+
+SELECT array_agg(b order by b) FROM pagg_tab WHERE c_serial between 1 and 30;
+                                     array_agg                                      
+------------------------------------------------------------------------------------
+ {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30}
+(1 row)
+
+-- Create user-defined aggregates whose stype is internal
+CREATE AGGREGATE postgres_fdw_sum_p_int8(int8) (
+	SFUNC = int8_avg_accum,
+	STYPE = internal,
+	COMBINEFUNC = int8_avg_combine,
+	FINALFUNC = int8_avg_serialize,
+	SERIALFUNC = int8_avg_serialize,
+	DESERIALFUNC = int8_avg_deserialize,
+	AGGPARTIALFUNC = postgres_fdw_sum_p_int8
+);
+CREATE AGGREGATE postgres_fdw_sum(int8) (
+	SFUNC = int8_avg_accum,
+	STYPE = internal,
+	COMBINEFUNC = int8_avg_combine,
+	FINALFUNC = numeric_poly_sum,
+	SERIALFUNC = int8_avg_serialize,
+	DESERIALFUNC = int8_avg_deserialize,
+	AGGPARTIALFUNC = postgres_fdw_sum_p_int8
+);
+-- Create user-defined aggregates whose stype is not internal
+CREATE AGGREGATE postgres_fdw_avg_p_int4(int4) (
+	SFUNC = int4_avg_accum,
+	STYPE = _int8,
+	COMBINEFUNC = int4_avg_combine,
+	INITCOND = '{0,0}',
+	AGGPARTIALFUNC = postgres_fdw_avg_p_int4
+);
+CREATE AGGREGATE postgres_fdw_avg(int4) (
+	SFUNC = int4_avg_accum,
+	STYPE = _int8,
+	COMBINEFUNC = int4_avg_combine,
+	FINALFUNC = int8_avg,
+	INITCOND = '{0,0}',
+	AGGPARTIALFUNC = postgres_fdw_avg_p_int4
+);
+CREATE AGGREGATE postgres_fdw_sum_noaggpartialfunc(int8) (
+	SFUNC = int8_avg_accum,
+	STYPE = internal,
+	COMBINEFUNC = int8_avg_combine,
+	FINALFUNC = numeric_poly_sum,
+	SERIALFUNC = int8_avg_serialize,
+	DESERIALFUNC = int8_avg_deserialize
+);
+-- It's safe to push down partial aggregate for user-defined aggregate
+-- when it belongs to an extension that's listed in extensions set
+-- and when its aggpartialfunc belongs to an extension that's listed
+-- in extensions set.
+ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_sum(int8);
+ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_sum_p_int8(int8);
+ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_avg(int4);
+ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_avg_p_int4(int4);
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT postgres_fdw_sum(b::int8) FROM pagg_tab;
+                                             QUERY PLAN                                             
+----------------------------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: postgres_fdw_sum((pagg_tab.b)::bigint)
+   ->  Append
+         ->  Foreign Scan
+               Output: (PARTIAL postgres_fdw_sum((pagg_tab.b)::bigint))
+               Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab)
+               Remote SQL: SELECT public.postgres_fdw_sum_p_int8(b::bigint) FROM public.pagg_tab_p1
+         ->  Foreign Scan
+               Output: (PARTIAL postgres_fdw_sum((pagg_tab_1.b)::bigint))
+               Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1)
+               Remote SQL: SELECT public.postgres_fdw_sum_p_int8(b::bigint) FROM public.pagg_tab_p2
+         ->  Foreign Scan
+               Output: (PARTIAL postgres_fdw_sum((pagg_tab_2.b)::bigint))
+               Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2)
+               Remote SQL: SELECT public.postgres_fdw_sum_p_int8(b::bigint) FROM public.pagg_tab_p3
+(15 rows)
+
+SELECT postgres_fdw_sum(b::int8) FROM pagg_tab;
+ postgres_fdw_sum 
+------------------
+            73500
+(1 row)
+
+-- It's unsafe to push down partial aggregate for user-defined aggregate
+-- when it has no aggpartialfunc.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT postgres_fdw_sum_noaggpartialfunc(b::int8) FROM pagg_tab;
+                                       QUERY PLAN                                        
+-----------------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: postgres_fdw_sum_noaggpartialfunc((pagg_tab.b)::bigint)
+   ->  Append
+         ->  Partial Aggregate
+               Output: PARTIAL postgres_fdw_sum_noaggpartialfunc((pagg_tab.b)::bigint)
+               ->  Foreign Scan on public.fpagg_tab_p1 pagg_tab
+                     Output: pagg_tab.b
+                     Remote SQL: SELECT b FROM public.pagg_tab_p1
+         ->  Partial Aggregate
+               Output: PARTIAL postgres_fdw_sum_noaggpartialfunc((pagg_tab_1.b)::bigint)
+               ->  Foreign Scan on public.fpagg_tab_p2 pagg_tab_1
+                     Output: pagg_tab_1.b
+                     Remote SQL: SELECT b FROM public.pagg_tab_p2
+         ->  Partial Aggregate
+               Output: PARTIAL postgres_fdw_sum_noaggpartialfunc((pagg_tab_2.b)::bigint)
+               ->  Foreign Scan on public.fpagg_tab_p3 pagg_tab_2
+                     Output: pagg_tab_2.b
+                     Remote SQL: SELECT b FROM public.pagg_tab_p3
+(18 rows)
+
+SELECT postgres_fdw_sum_noaggpartialfunc(b::int8) FROM pagg_tab;
+ postgres_fdw_sum_noaggpartialfunc 
+-----------------------------------
+                             73500
+(1 row)
+
+ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_sum_p_int8(int8);
+-- Reconnect for flushing the shippability cache
+\c -
+SET enable_partitionwise_aggregate TO true;
+-- It's safe to push down partial aggregate for user defined aggregate
+-- when itself is aggpartialfunc and when it belongs to an extension
+-- that's listed in extensions set and when stype is internal.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT postgres_fdw_sum_p_int8(b::int8) FROM pagg_tab;
+                                  QUERY PLAN                                   
+-------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: postgres_fdw_sum_p_int8((pagg_tab.b)::bigint)
+   ->  Append
+         ->  Partial Aggregate
+               Output: PARTIAL postgres_fdw_sum_p_int8((pagg_tab.b)::bigint)
+               ->  Foreign Scan on public.fpagg_tab_p1 pagg_tab
+                     Output: pagg_tab.b
+                     Remote SQL: SELECT b FROM public.pagg_tab_p1
+         ->  Partial Aggregate
+               Output: PARTIAL postgres_fdw_sum_p_int8((pagg_tab_1.b)::bigint)
+               ->  Foreign Scan on public.fpagg_tab_p2 pagg_tab_1
+                     Output: pagg_tab_1.b
+                     Remote SQL: SELECT b FROM public.pagg_tab_p2
+         ->  Partial Aggregate
+               Output: PARTIAL postgres_fdw_sum_p_int8((pagg_tab_2.b)::bigint)
+               ->  Foreign Scan on public.fpagg_tab_p3 pagg_tab_2
+                     Output: pagg_tab_2.b
+                     Remote SQL: SELECT b FROM public.pagg_tab_p3
+(18 rows)
+
+SELECT postgres_fdw_sum_p_int8(b::int8) FROM pagg_tab;
+                  postgres_fdw_sum_p_int8                   
+------------------------------------------------------------
+ \x0000000000000bb80000000200000001000000000000000000070dac
+(1 row)
+
+ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_sum(int8);
+ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_sum_p_int8(int8);
+-- Reconnect for flushing the shippability cache
+\c -
+SET enable_partitionwise_aggregate TO true;
+-- It's unsafe to push down partial aggregate for user-defined aggregate
+-- when it doesn't belong to an extension that's listed in extensions set
+-- and when its aggpartialfunc belongs to an extension that's listed
+-- in extensions set.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT postgres_fdw_sum(b::int8) FROM pagg_tab;
+                               QUERY PLAN                               
+------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: postgres_fdw_sum((pagg_tab.b)::bigint)
+   ->  Append
+         ->  Partial Aggregate
+               Output: PARTIAL postgres_fdw_sum((pagg_tab.b)::bigint)
+               ->  Foreign Scan on public.fpagg_tab_p1 pagg_tab
+                     Output: pagg_tab.b
+                     Remote SQL: SELECT b FROM public.pagg_tab_p1
+         ->  Partial Aggregate
+               Output: PARTIAL postgres_fdw_sum((pagg_tab_1.b)::bigint)
+               ->  Foreign Scan on public.fpagg_tab_p2 pagg_tab_1
+                     Output: pagg_tab_1.b
+                     Remote SQL: SELECT b FROM public.pagg_tab_p2
+         ->  Partial Aggregate
+               Output: PARTIAL postgres_fdw_sum((pagg_tab_2.b)::bigint)
+               ->  Foreign Scan on public.fpagg_tab_p3 pagg_tab_2
+                     Output: pagg_tab_2.b
+                     Remote SQL: SELECT b FROM public.pagg_tab_p3
+(18 rows)
+
+SELECT postgres_fdw_sum(b::int8) FROM pagg_tab;
+ postgres_fdw_sum 
+------------------
+            73500
+(1 row)
+
+-- It's safe to push down partial aggregate for user-defined aggregate
+-- whose stype is not internal
+-- when it belongs to an extension that's listed in extensions set
+-- and when its aggpartialfunc belongs to an extension that's listed
+-- in extensions set.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT postgres_fdw_avg(b) FROM pagg_tab;
+                                         QUERY PLAN                                         
+--------------------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: postgres_fdw_avg(pagg_tab.b)
+   ->  Append
+         ->  Foreign Scan
+               Output: (PARTIAL postgres_fdw_avg(pagg_tab.b))
+               Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab)
+               Remote SQL: SELECT public.postgres_fdw_avg_p_int4(b) FROM public.pagg_tab_p1
+         ->  Foreign Scan
+               Output: (PARTIAL postgres_fdw_avg(pagg_tab_1.b))
+               Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1)
+               Remote SQL: SELECT public.postgres_fdw_avg_p_int4(b) FROM public.pagg_tab_p2
+         ->  Foreign Scan
+               Output: (PARTIAL postgres_fdw_avg(pagg_tab_2.b))
+               Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2)
+               Remote SQL: SELECT public.postgres_fdw_avg_p_int4(b) FROM public.pagg_tab_p3
+(15 rows)
+
+SELECT postgres_fdw_avg(b) FROM pagg_tab;
+  postgres_fdw_avg   
+---------------------
+ 24.5000000000000000
+(1 row)
+
+-- It's safe to push down partial aggregate for user-defined aggregate
+-- when itself is aggpartialfunc and when it belongs to an extension
+-- that's listed in extensions set and when stype is not internal.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT postgres_fdw_avg_p_int4(b) FROM pagg_tab;
+                             QUERY PLAN                              
+---------------------------------------------------------------------
+ Finalize Aggregate
+   Output: postgres_fdw_avg_p_int4(pagg_tab.b)
+   ->  Append
+         ->  Partial Aggregate
+               Output: PARTIAL postgres_fdw_avg_p_int4(pagg_tab.b)
+               ->  Foreign Scan on public.fpagg_tab_p1 pagg_tab
+                     Output: pagg_tab.b
+                     Remote SQL: SELECT b FROM public.pagg_tab_p1
+         ->  Partial Aggregate
+               Output: PARTIAL postgres_fdw_avg_p_int4(pagg_tab_1.b)
+               ->  Foreign Scan on public.fpagg_tab_p2 pagg_tab_1
+                     Output: pagg_tab_1.b
+                     Remote SQL: SELECT b FROM public.pagg_tab_p2
+         ->  Partial Aggregate
+               Output: PARTIAL postgres_fdw_avg_p_int4(pagg_tab_2.b)
+               ->  Foreign Scan on public.fpagg_tab_p3 pagg_tab_2
+                     Output: pagg_tab_2.b
+                     Remote SQL: SELECT b FROM public.pagg_tab_p3
+(18 rows)
+
+SELECT postgres_fdw_avg_p_int4(b) FROM pagg_tab;
+ postgres_fdw_avg_p_int4 
+-------------------------
+ {3000,73500}
+(1 row)
+
+ALTER EXTENSION postgres_fdw DROP TYPE mood;
+ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_sum_p_int8(int8);
+ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_avg_p_int4(int4);
+ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_avg(int4);
+DROP AGGREGATE postgres_fdw_sum(int8);
+DROP AGGREGATE postgres_fdw_sum_p_int8(int8);
+DROP AGGREGATE postgres_fdw_avg(int4);
+DROP AGGREGATE postgres_fdw_avg_p_int4(int4);
+DROP AGGREGATE postgres_fdw_sum_noaggpartialfunc(int8);
+ALTER SERVER loopback OPTIONS (DROP fdw_tuple_cost);
 -- ===================================================================
 -- access rights and superuser
 -- ===================================================================
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c5cada55fb..c0c61c51a2 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -519,7 +519,7 @@ static bool foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel,
 							JoinType jointype, RelOptInfo *outerrel, RelOptInfo *innerrel,
 							JoinPathExtraData *extra);
 static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
-								Node *havingQual);
+								Node *havingQual, PartitionwiseAggregateType patype);
 static List *get_useful_pathkeys_for_relation(PlannerInfo *root,
 											  RelOptInfo *rel);
 static List *get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel);
@@ -6366,7 +6366,7 @@ postgresGetForeignJoinPaths(PlannerInfo *root,
  */
 static bool
 foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
-					Node *havingQual)
+					Node *havingQual, PartitionwiseAggregateType patype)
 {
 	Query	   *query = root->parse;
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
@@ -6380,6 +6380,10 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	if (query->groupingSets)
 		return false;
 
+	/* It's unsafe to push having statements with partial aggregates */
+	if ((patype == PARTITIONWISE_AGGREGATE_PARTIAL) && havingQual)
+		return false;
+
 	/* Get the fpinfo of the underlying scan relation. */
 	ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
 
@@ -6621,6 +6625,7 @@ postgresGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage,
 
 	/* Ignore stages we don't support; and skip any duplicate calls. */
 	if ((stage != UPPERREL_GROUP_AGG &&
+		stage != UPPERREL_PARTIAL_GROUP_AGG &&
 		 stage != UPPERREL_ORDERED &&
 		 stage != UPPERREL_FINAL) ||
 		output_rel->fdw_private)
@@ -6637,6 +6642,10 @@ postgresGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage,
 			add_foreign_grouping_paths(root, input_rel, output_rel,
 									   (GroupPathExtraData *) extra);
 			break;
+		case UPPERREL_PARTIAL_GROUP_AGG:
+			add_foreign_grouping_paths(root, input_rel, output_rel,
+									   (GroupPathExtraData*)extra);
+			break;
 		case UPPERREL_ORDERED:
 			add_foreign_ordered_paths(root, input_rel, output_rel);
 			break;
@@ -6677,7 +6686,8 @@ add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
 		return;
 
 	Assert(extra->patype == PARTITIONWISE_AGGREGATE_NONE ||
-		   extra->patype == PARTITIONWISE_AGGREGATE_FULL);
+		   extra->patype == PARTITIONWISE_AGGREGATE_FULL ||
+		   extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL);
 
 	/* save the input_rel as outerrel in fpinfo */
 	fpinfo->outerrel = input_rel;
@@ -6697,7 +6707,7 @@ add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
 	 * Use HAVING qual from extra. In case of child partition, it will have
 	 * translated Vars.
 	 */
-	if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual))
+	if (!foreign_grouping_ok(root, grouped_rel, extra->havingQual, extra->patype))
 		return;
 
 	/*
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index b54903ad8f..63ff7b659b 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -2963,16 +2963,31 @@ RESET enable_partitionwise_join;
 -- ===================================================================
 -- test partitionwise aggregates
 -- ===================================================================
-
-CREATE TABLE pagg_tab (a int, b int, c text) PARTITION BY RANGE(a);
+ALTER SERVER loopback OPTIONS (ADD fdw_tuple_cost '0.5');
+
+CREATE TYPE mood AS enum ('sad', 'ok', 'happy');
+ALTER EXTENSION postgres_fdw ADD TYPE mood;
+
+CREATE TABLE pagg_tab (a int, b int, c text, c_serial int4,
+					c_int4array _int4, c_interval interval,
+					c_money money, c_1c text, c_1b bytea,
+					c_bit bit(2), c_1or3int2 int2,
+					c_1or3int4 int4, c_1or3int8 int8,
+					c_bool bool, c_enum mood, c_pg_lsn pg_lsn,
+					c_tid tid, c_int4range int4range,
+					c_int4multirange int4multirange,
+					c_time time, c_timetz timetz,
+					c_timestamp timestamp, c_timestamptz timestamptz,
+					c_xid8 xid8)
+	PARTITION BY RANGE(a);
 
 CREATE TABLE pagg_tab_p1 (LIKE pagg_tab);
 CREATE TABLE pagg_tab_p2 (LIKE pagg_tab);
 CREATE TABLE pagg_tab_p3 (LIKE pagg_tab);
 
-INSERT INTO pagg_tab_p1 SELECT i % 30, i % 50, to_char(i/30, 'FM0000') FROM generate_series(1, 3000) i WHERE (i % 30) < 10;
-INSERT INTO pagg_tab_p2 SELECT i % 30, i % 50, to_char(i/30, 'FM0000') FROM generate_series(1, 3000) i WHERE (i % 30) < 20 and (i % 30) >= 10;
-INSERT INTO pagg_tab_p3 SELECT i % 30, i % 50, to_char(i/30, 'FM0000') FROM generate_series(1, 3000) i WHERE (i % 30) < 30 and (i % 30) >= 20;
+INSERT INTO pagg_tab_p1 SELECT i % 30, i % 50, to_char(i/30, 'FM0000'), i, array[(i % 2), 0], ((i % 2) || ' seconds')::interval, (i % 2)::money, ((i - 1) % 10)::text, (((i - 1) % 10)::text)::bytea, case when (i % 2) = 0 then B'01' else B'11' end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then true else false end, (case when (i % 2) = 0 then 'sad' else 'happy' end)::mood, ('0/' || i)::pg_lsn, ('(0,' || i || ')')::tid, int4range(0, i), int4multirange(int4range(0, i), int4range(100, 100 + i)), '00:00:00'::time + (i || ' seconds')::interval, '00:00:00'::timetz + (i || ' seconds')::interval, '2000-01-01'::timestamp + (i || ' seconds')::interval, '2000-01-01'::timestamptz + (i || ' seconds')::interval, ((i % 10)::text)::xid8 FROM generate_series(1, 3000) i WHERE (i % 30) < 10;
+INSERT INTO pagg_tab_p2 SELECT i % 30, i % 50, to_char(i/30, 'FM0000'), i, array[(i % 2), 0], ((i % 2) || ' seconds')::interval, (i % 2)::money, ((i - 1) % 10)::text, (((i - 1) % 10)::text)::bytea, case when (i % 2) = 0 then B'01' else B'11' end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then true else false end, (case when (i % 2) = 0 then 'sad' else 'happy' end)::mood, ('0/' || i)::pg_lsn, ('(0,' || i || ')')::tid, int4range(0, i), int4multirange(int4range(0, i), int4range(100, 100 + i)), '00:00:00'::time + (i || ' seconds')::interval, '00:00:00'::timetz + (i || ' seconds')::interval, '2000-01-01'::timestamp + (i || ' seconds')::interval, '2000-01-01'::timestamptz + (i || ' seconds')::interval, ((i % 10)::text)::xid8 FROM generate_series(1, 3000) i WHERE (i % 30) < 20 and (i % 30) >= 10;
+INSERT INTO pagg_tab_p3 SELECT i % 30, i % 50, to_char(i/30, 'FM0000'), i, array[(i % 2), 0], ((i % 2) || ' seconds')::interval, (i % 2)::money, ((i - 1) % 10)::text, (((i - 1) % 10)::text)::bytea, case when (i % 2) = 0 then B'01' else B'11' end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then 1 else 3 end, case when (i % 2) = 0 then true else false end, (case when (i % 2) = 0 then 'sad' else 'happy' end)::mood, ('0/' || i)::pg_lsn, ('(0,' || i || ')')::tid, int4range(0, i), int4multirange(int4range(0, i), int4range(100, 100 + i)), '00:00:00'::time + (i || ' seconds')::interval, '00:00:00'::timetz + (i || ' seconds')::interval, '2000-01-01'::timestamp + (i || ' seconds')::interval, '2000-01-01'::timestamptz + (i || ' seconds')::interval, ((i % 10)::text)::xid8 FROM generate_series(1, 3000) i WHERE (i % 30) < 30 and (i % 30) >= 20;
 
 -- Create foreign partitions
 CREATE FOREIGN TABLE fpagg_tab_p1 PARTITION OF pagg_tab FOR VALUES FROM (0) TO (10) SERVER loopback OPTIONS (table_name 'pagg_tab_p1');
@@ -3006,6 +3021,260 @@ SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 EXPLAIN (COSTS OFF)
 SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
 
+-- It's unsafe to push down having clause when there are partial aggregates
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
+
+-- Partial aggregates are safe to push down without having clause
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b ORDER BY 1;
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b ORDER BY 1;
+
+-- Partial aggregates are safe to push down for all built-in aggregates
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT /* aggregate function <> aggpartialfunc */
+    array_agg(c_int4array), array_agg(b),
+    avg(b::int2), avg(b::int4), avg(b::int8), avg(c_interval), avg(b::float4), avg(b::float8), avg(b::numeric),
+    corr(b::float8, (b * b)::float8),
+    covar_pop(b::float8, (b * b)::float8),
+    covar_samp(b::float8, (b * b)::float8),
+    regr_avgx((2 * b)::float8, b::float8),
+    regr_avgy((2 * b)::float8, b::float8),
+    regr_intercept((2 * b + 3)::float8, b::float8),
+    regr_r2((b * b)::float8, b::float8),
+    regr_slope((2 * b + 3)::float8, b::float8),
+    regr_sxx((2 * b + 3)::float8, b::float8),
+    regr_sxy((b * b)::float8, b::float8),
+    regr_syy((2 * b + 3)::float8, b::float8),
+    stddev(b::float4), stddev(b::float8), stddev(b::int2), stddev(b::int4), stddev(b::int8), stddev(b::numeric),
+    stddev_pop(b::float4), stddev_pop(b::float8), stddev_pop(b::int2), stddev_pop(b::int4), stddev_pop(b::int8), stddev_pop(b::numeric),
+    stddev_samp(b::float4), stddev_samp(b::float8), stddev_samp(b::int2), stddev_samp(b::int4), stddev_samp(b::int8), stddev_samp(b::numeric),
+    string_agg(c_1c, ','), string_agg(c_1b, ','),
+    sum(b::int8), sum(b::numeric),
+    variance(b::float4), variance(b::float8), variance(b::int2), variance(b::int4), variance(b::int8), variance(b::numeric),
+    var_pop(b::float4), var_pop(b::float8), var_pop(b::int2), var_pop(b::int4), var_pop(b::int8), var_pop(b::numeric),
+    var_samp(b::float4), var_samp(b::float8), var_samp(b::int2), var_samp(b::int4), var_samp(b::int8), var_samp(b::numeric),
+    /* aggregate function = aggpartialfunc */
+    any_value(b * 0),
+    bit_and(c_bit), bit_and(c_1or3int2), bit_and(c_1or3int4), bit_and(c_1or3int8),
+    bit_or(c_bit), bit_or(c_1or3int2), bit_or(c_1or3int4), bit_or(c_1or3int8),
+    bit_xor(c_bit), bit_xor(c_1or3int2), bit_xor(c_1or3int4), bit_xor(c_1or3int8),
+    bool_and(c_bool),
+    bool_or(c_bool),
+    count(b), count(*),
+    every(c_bool),
+    max(c_int4array), max(c_enum), max(c_1c::char(1)), max('2000-01-01'::date + b), max('0.0.0.0'::inet + b), max(b::float4), max(b::float8), max(b::int2), max(b::int4), max(b::int8), max(c_interval), max(c_money), max(b::numeric), max(b::oid), max(c_pg_lsn), max(c_tid), max(c_1c), max(c_time), max(c_timetz), max(c_timestamp), max(c_timestamptz), max(c_xid8),
+    min(c_int4array), min(c_enum), min(c_1c::char(1)), min('2000-01-01'::date + b), min('0.0.0.0'::inet + b), min(b::float4), min(b::float8), min(b::int2), min(b::int4), min(b::int8), min(c_interval), min(c_money), min(b::numeric), min(b::oid), min(c_pg_lsn), min(c_tid), min(c_1c), min(c_time), min(c_timetz), min(c_timestamp), min(c_timestamptz), min(c_xid8),
+    range_intersect_agg(c_int4range), range_intersect_agg(c_int4multirange),
+    regr_count((2 * b + 3)::float8, b::float8),
+    sum(b::float4), sum(b::float8), sum(b::int2), sum(b::int4),	sum(c_interval), sum(c_money)
+  FROM pagg_tab WHERE c_serial between 1 and 30;
+
+SELECT /* aggregate function <> aggpartialfunc */
+    array_agg(c_int4array), array_agg(b),
+    avg(b::int2), avg(b::int4), avg(b::int8), avg(c_interval),
+    avg(b::float4), avg(b::float8),
+    corr(b::float8, (b * b)::float8),
+    covar_pop(b::float8, (b * b)::float8),
+    covar_samp(b::float8, (b * b)::float8),
+    regr_avgx((2 * b)::float8, b::float8),
+    regr_avgy((2 * b)::float8, b::float8),
+    regr_intercept((2 * b + 3)::float8, b::float8),
+    regr_r2((b * b)::float8, b::float8),
+    regr_slope((2 * b + 3)::float8, b::float8),
+    regr_sxx((2 * b + 3)::float8, b::float8),
+    regr_sxy((b * b)::float8, b::float8),
+    regr_syy((2 * b + 3)::float8, b::float8),
+    stddev(b::float4), stddev(b::float8), stddev(b::int2), stddev(b::int4), stddev(b::int8), stddev(b::numeric),
+    stddev_pop(b::float4), stddev_pop(b::float8), stddev_pop(b::int2), stddev_pop(b::int4), stddev_pop(b::int8), stddev_pop(b::numeric),
+    stddev_samp(b::float4), stddev_samp(b::float8), stddev_samp(b::int2), stddev_samp(b::int4), stddev_samp(b::int8), stddev_samp(b::numeric),
+    string_agg(c_1c, ','), string_agg(c_1b, ','),
+    sum(b::int8), sum(b::numeric),
+    variance(b::float4), variance(b::float8), variance(b::int2), variance(b::int4), variance(b::int8), variance(b::numeric),
+    var_pop(b::float4), var_pop(b::float8), var_pop(b::int2), var_pop(b::int4), var_pop(b::int8), var_pop(b::numeric),
+    var_samp(b::float4), var_samp(b::float8), var_samp(b::int2), var_samp(b::int4), var_samp(b::int8), var_samp(b::numeric),
+    /* aggregate function = aggpartialfunc */
+    any_value(b * 0),
+    bit_and(c_bit), bit_and(c_1or3int2), bit_and(c_1or3int4), bit_and(c_1or3int8),
+    bit_or(c_bit), bit_or(c_1or3int2), bit_or(c_1or3int4), bit_or(c_1or3int8),
+    bit_xor(c_bit), bit_xor(c_1or3int2), bit_xor(c_1or3int4), bit_xor(c_1or3int8),
+    bool_and(c_bool),
+    bool_or(c_bool),
+    count(b), count(*),
+    every(c_bool),
+    max(c_int4array), max(c_enum), max(c_1c::char(1)), max('2000-01-01'::date + b), max('0.0.0.0'::inet + b), max(b::float4), max(b::float8), max(b::int2), max(b::int4), max(b::int8), max(c_interval), max(c_money), max(b::numeric), max(b::oid), max(c_pg_lsn), max(c_tid), max(c_1c), max(c_time), max(c_timetz), max(c_timestamp), max(c_timestamptz), max(c_xid8),
+    min(c_int4array), min(c_enum), min(c_1c::char(1)), min('2000-01-01'::date + b), min('0.0.0.0'::inet + b), min(b::float4), min(b::float8), min(b::int2), min(b::int4), min(b::int8), min(c_interval), min(c_money), min(b::numeric), min(b::oid), min(c_pg_lsn), min(c_tid), min(c_1c), min(c_time), min(c_timetz), min(c_timestamp), min(c_timestamptz), min(c_xid8),
+    range_intersect_agg(c_int4range), range_intersect_agg(c_int4multirange),
+    regr_count((2 * b + 3)::float8, b::float8),
+    sum(b::float4), sum(b::float8), sum(b::int2), sum(b::int4),	sum(c_interval), sum(c_money)
+  FROM pagg_tab WHERE c_serial between 1 and 30;
+
+-- List of aggregate functions such that the aggregate function supports partial aggregate
+-- and aggpartialfn field of the aggregate function is invalid
+SELECT aggfnoid::text
+  FROM pg_aggregate a JOIN pg_type b ON a.aggtranstype = b.oid
+    WHERE (aggcombinefn::text <> '-') AND (aggpartialfn::text = '-')
+  ORDER BY 1;
+
+-- List of aggregate functions which have incorrect aggpartialfunc
+SELECT a.aggfnoid
+  FROM pg_aggregate a JOIN pg_type b ON a.aggtranstype = b.oid
+       JOIN pg_aggregate c on a.aggpartialfn = c.aggfnoid
+    WHERE (a.aggcombinefn::text <> '-')
+          AND (a.aggpartialfn::text <> '-')
+          AND (a.aggtransfn <> c.aggtransfn
+               OR (c.aggcombinefn::text <> '-'
+                   AND a.aggcombinefn <> c.aggcombinefn)
+               OR (a.agginitval IS NOT NULL
+                   AND c.agginitval IS NOT NULL
+                   AND a.agginitval <> c.agginitval)
+               OR (b.typname = 'internal'
+                   AND c.aggserialfn::text <> '-'
+                   AND a.aggserialfn <> c.aggserialfn)
+               OR (b.typname = 'internal'
+                   AND c.aggdeserialfn::text <> '-'
+                   AND a.aggdeserialfn <> c.aggdeserialfn)
+               OR (b.typname = 'internal'
+                   AND a.aggserialfn <> c.aggfinalfn)
+               OR (b.typname <> 'internal'
+                   AND c.aggfinalfn::text <> '-'))
+  ORDER BY 1;
+
+-- It's unsafe to push down partial aggregates which contain distinct clause
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT max(a), count(distinct b) FROM pagg_tab;
+SELECT max(a), count(distinct b) FROM pagg_tab;
+
+-- It's unsafe to push down partial aggregates which contain order by clause
+EXPLAIN (VERBOSE, COSTS OFF) SELECT array_agg(b order by b) FROM pagg_tab WHERE c_serial between 1 and 30;
+SELECT array_agg(b order by b) FROM pagg_tab WHERE c_serial between 1 and 30;
+
+-- Create user-defined aggregates whose stype is internal
+CREATE AGGREGATE postgres_fdw_sum_p_int8(int8) (
+	SFUNC = int8_avg_accum,
+	STYPE = internal,
+	COMBINEFUNC = int8_avg_combine,
+	FINALFUNC = int8_avg_serialize,
+	SERIALFUNC = int8_avg_serialize,
+	DESERIALFUNC = int8_avg_deserialize,
+	AGGPARTIALFUNC = postgres_fdw_sum_p_int8
+);
+
+CREATE AGGREGATE postgres_fdw_sum(int8) (
+	SFUNC = int8_avg_accum,
+	STYPE = internal,
+	COMBINEFUNC = int8_avg_combine,
+	FINALFUNC = numeric_poly_sum,
+	SERIALFUNC = int8_avg_serialize,
+	DESERIALFUNC = int8_avg_deserialize,
+	AGGPARTIALFUNC = postgres_fdw_sum_p_int8
+);
+
+-- Create user-defined aggregates whose stype is not internal
+CREATE AGGREGATE postgres_fdw_avg_p_int4(int4) (
+	SFUNC = int4_avg_accum,
+	STYPE = _int8,
+	COMBINEFUNC = int4_avg_combine,
+	INITCOND = '{0,0}',
+	AGGPARTIALFUNC = postgres_fdw_avg_p_int4
+);
+
+CREATE AGGREGATE postgres_fdw_avg(int4) (
+	SFUNC = int4_avg_accum,
+	STYPE = _int8,
+	COMBINEFUNC = int4_avg_combine,
+	FINALFUNC = int8_avg,
+	INITCOND = '{0,0}',
+	AGGPARTIALFUNC = postgres_fdw_avg_p_int4
+);
+
+CREATE AGGREGATE postgres_fdw_sum_noaggpartialfunc(int8) (
+	SFUNC = int8_avg_accum,
+	STYPE = internal,
+	COMBINEFUNC = int8_avg_combine,
+	FINALFUNC = numeric_poly_sum,
+	SERIALFUNC = int8_avg_serialize,
+	DESERIALFUNC = int8_avg_deserialize
+);
+
+-- It's safe to push down partial aggregate for user-defined aggregate
+-- when it belongs to an extension that's listed in extensions set
+-- and when its aggpartialfunc belongs to an extension that's listed
+-- in extensions set.
+ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_sum(int8);
+ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_sum_p_int8(int8);
+
+ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_avg(int4);
+ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_avg_p_int4(int4);
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT postgres_fdw_sum(b::int8) FROM pagg_tab;
+SELECT postgres_fdw_sum(b::int8) FROM pagg_tab;
+
+-- It's unsafe to push down partial aggregate for user-defined aggregate
+-- when it has no aggpartialfunc.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT postgres_fdw_sum_noaggpartialfunc(b::int8) FROM pagg_tab;
+SELECT postgres_fdw_sum_noaggpartialfunc(b::int8) FROM pagg_tab;
+
+ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_sum_p_int8(int8);
+
+-- Reconnect for flushing the shippability cache
+\c -
+SET enable_partitionwise_aggregate TO true;
+
+-- It's safe to push down partial aggregate for user-defined aggregate
+-- when itself is aggpartialfunc and when it belongs to an extension
+-- that's listed in extensions set and when stype is internal.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT postgres_fdw_sum_p_int8(b::int8) FROM pagg_tab;
+SELECT postgres_fdw_sum_p_int8(b::int8) FROM pagg_tab;
+
+ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_sum(int8);
+ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_sum_p_int8(int8);
+
+-- Reconnect for flushing the shippability cache
+\c -
+SET enable_partitionwise_aggregate TO true;
+
+-- It's unsafe to push down partial aggregate for user-defined aggregate
+-- when it doesn't belong to an extension that's listed in extensions set
+-- and when its aggpartialfunc belongs to an extension that's listed
+-- in extensions set.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT postgres_fdw_sum(b::int8) FROM pagg_tab;
+SELECT postgres_fdw_sum(b::int8) FROM pagg_tab;
+
+-- It's safe to push down partial aggregate for user-defined aggregate
+-- whose stype is not internal
+-- when it belongs to an extension that's listed in extensions set
+-- and when its aggpartialfunc belongs to an extension that's listed
+-- in extensions set.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT postgres_fdw_avg(b) FROM pagg_tab;
+SELECT postgres_fdw_avg(b) FROM pagg_tab;
+
+-- It's safe to push down partial aggregate for user-defined aggregate
+-- when itself is aggpartialfunc and when it belongs to an extension
+-- that's listed in extensions set and when stype is not internal.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT postgres_fdw_avg_p_int4(b) FROM pagg_tab;
+SELECT postgres_fdw_avg_p_int4(b) FROM pagg_tab;
+
+ALTER EXTENSION postgres_fdw DROP TYPE mood;
+
+ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_sum_p_int8(int8);
+
+ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_avg_p_int4(int4);
+ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_avg(int4);
+
+DROP AGGREGATE postgres_fdw_sum(int8);
+DROP AGGREGATE postgres_fdw_sum_p_int8(int8);
+DROP AGGREGATE postgres_fdw_avg(int4);
+DROP AGGREGATE postgres_fdw_avg_p_int4(int4);
+DROP AGGREGATE postgres_fdw_sum_noaggpartialfunc(int8);
+ALTER SERVER loopback OPTIONS (DROP fdw_tuple_cost);
+
 -- ===================================================================
 -- access rights and superuser
 -- ===================================================================
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index ed32ca0349..d0a6e94e1f 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -644,6 +644,24 @@
        value starts out null.
       </para></entry>
      </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>aggpartialfn</structfield> <type>regproc</type>
+       (references <link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.<structfield>oid</structfield>)
+      </para>
+      <para>
+       This field refers to an aggregate function called
+       aggpartialfunc corresponding to the aggregation function.
+       aggpartialfunc is need for aggregate on
+       <filename>postgres_fdw</filename> node when you use the built-in
+       sharding feature in PostgreSQL.
+       See
+       <xref linkend="built-in-sharding-in-postgresql"/> for the
+       definition of aggpartialfunc.
+       This field is invalid if aggpartialfunc is not defined.
+      </para></entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index 5062d712e7..fa5395f01a 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -929,13 +929,15 @@ postgres=# SELECT postgres_fdw_disconnect_all();
   <para>
    <filename>postgres_fdw</filename> attempts to optimize remote queries to reduce
    the amount of data transferred from foreign servers.  This is done by
-   sending query <literal>WHERE</literal> clauses to the remote server for
-   execution, and by not retrieving table columns that are not needed for
-   the current query.  To reduce the risk of misexecution of queries,
-   <literal>WHERE</literal> clauses are not sent to the remote server unless they use
-   only data types, operators, and functions that are built-in or belong to an
-   extension that's listed in the foreign server's <literal>extensions</literal>
-   option.  Operators and functions in such clauses must
+   sending query <literal>WHERE</literal> clauses and aggregate expressions
+   to the remote server for execution, and by not retrieving table columns that
+   are not needed for the current query.
+   To reduce the risk of misexecution of queries,
+   <literal>WHERE</literal> clauses and aggregate expressions are not sent to
+   the remote server unless they use only data types, operators, and functions
+   that are built-in or belong to an extension that's listed in the foreign
+   server's <literal>extensions</literal> option.
+   Operators and functions in such clauses must
    be <literal>IMMUTABLE</literal> as well.
    For an <command>UPDATE</command> or <command>DELETE</command> query,
    <filename>postgres_fdw</filename> attempts to optimize the query execution by
@@ -966,6 +968,57 @@ postgres=# SELECT postgres_fdw_disconnect_all();
   </para>
  </sect2>
 
+ <sect2 id="built-in-sharding-in-postgresql">
+  <title>Built-in sharding in PostgreSQL</title>
+  <para>
+   Sharding in database is the ability to horizontally partition data across
+   one more database shards. The built-in sharding feature in PostgreSQL uses
+   the declarative partitioning feature and FDW.
+   It is the mechanism to partition a table across one or more foreign servers.
+   Various parts of the query e.g., <literal>WHERE</literal> clauses,
+   <literal>JOIN</literal> clauses, aggregate expressions are sent to
+   a <filename>postgres_fdw</filename> node if several conditions hold.
+   For <literal>WHERE</literal> clauses,
+   <literal>JOIN</literal> clauses, this sending is active if
+   conditions in <xref linkend="postgres-fdw-remote-query-optimization"/>
+   hold and <varname>enable_partitionwise_join</varname> is true(this condition
+   is need for only <literal>JOIN</literal> clauses).
+   For aggregate expressions, this sending is active if conditions in
+   <xref linkend="postgres-fdw-remote-query-optimization"/> hold and
+   <varname>enable_partitionwise_aggregate</varname> is true and their
+   aggregate expressions don't contain <literal>DISTINCT</literal> clause and
+   <literal>ORDER BY</literal> clause and an aggregate function called
+   aggpartialfunc for partial aggregate corresponding to the aggregate
+   function is defined on the primary node and
+   <filename>postgres_fdw</filename> node.
+   For transition function and state value data type, it must match in the
+   aggregate function and the aggpartialfunc.
+   If the state value data type is not internal, the aggpartialfunc doesn't
+   have final function.
+   If the state value data type is internal, the aggpartialfunc has the final
+   function which is same as the serialization function, and for serialization
+   function and deserialization function it must match in the aggregate function
+   and the aggpartialfunc.
+   For initial state and combine function and serialization function and
+   deserialization function, if the aggpartialfunc has it,
+   it must match in the aggregate function and the aggpartialfunc.
+   For any built-in aggregate function which supports partial aggregation,
+   aggpartialfunc is defined as a built-in aggregate function.
+   <filename>postgres_fdw</filename> assumes that for any built-in aggregate
+   function its aggpartialfunc is defined on <filename>postgres_fdw</filename> node
+   as well.
+   <filename>postgres_fdw</filename> assumes that an user-defined aggregate
+   function has its aggpartialfunc on <filename>postgres_fdw</filename> node
+   only when the user-defined aggregate function and its aggpartialfunc belong
+   to some extension that's listed in the foreign server's extensions option.
+   If this sending is active for aggregate expressions,
+   any aggregate on <filename>postgres_fdw</filename> node
+   is a partial aggregate in the primary node and
+   any aggregate function is replaced by the aggpartialfunc
+   in the remote query.
+  </para>
+ </sect2>
+
  <sect2 id="postgres-fdw-remote-query-execution-environment">
   <title>Remote Query Execution Environment</title>
 
@@ -1031,14 +1084,27 @@ postgres=# SELECT postgres_fdw_disconnect_all();
    back to 8.1.  A limitation however is that <filename>postgres_fdw</filename>
    generally assumes that immutable built-in functions and operators are
    safe to send to the remote server for execution, if they appear in a
-   <literal>WHERE</literal> clause for a foreign table.  Thus, a built-in
-   function that was added since the remote server's release might be sent
-   to it for execution, resulting in <quote>function does not exist</quote> or
-   a similar error.  This type of failure can be worked around by
+   <literal>WHERE</literal> clause or aggregate expressions for a foreign table.
+   Thus, a built-in function that was added since the remote server's release
+   might be sent to it for execution, resulting in <quote>function does not
+   exist</quote> or a similar error.  This type of failure can be worked around by
    rewriting the query, for example by embedding the foreign table
    reference in a sub-<literal>SELECT</literal> with <literal>OFFSET 0</literal> as an
    optimization fence, and placing the problematic function or operator
    outside the sub-<literal>SELECT</literal>.
+   A similar problem occurs when using the built-in sharding feature in PostgreSQL and
+   the query contains an aggregate across one more database shards.
+   When the built-in sharding feature in PostgreSQL is used, the aggpartialfunc of the
+   aggregate function is sent to <filename>postgres_fdw</filename> nodes
+   if the conditions shown in
+   <xref linkend="built-in-sharding-in-postgresql"/> are hold.
+   A similar limitation is that <filename>postgres_fdw</filename> assumes that if
+   the aggpartialfunc exists on the primary node, the aggpartialfunc also exists on
+   <filename>postgres_fdw</filename> nodes.
+   This limitation might cause <quote>function does not exist</quote>
+   or a similar error if the <filename>postgres_fdw</filename> node server version
+   is older than the primary node server version.
+   This problem can be worked around as well by the above rewrite of the query.
   </para>
  </sect2>
 
diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml
index 222e0aa5c9..244ca62e66 100644
--- a/doc/src/sgml/ref/create_aggregate.sgml
+++ b/doc/src/sgml/ref/create_aggregate.sgml
@@ -42,6 +42,7 @@ CREATE [ OR REPLACE ] AGGREGATE <replaceable class="parameter">name</replaceable
     [ , MINITCOND = <replaceable class="parameter">minitial_condition</replaceable> ]
     [ , SORTOP = <replaceable class="parameter">sort_operator</replaceable> ]
     [ , PARALLEL = { SAFE | RESTRICTED | UNSAFE } ]
+    [ , AGGPARTIALFUNC = <replaceable class="parameter">aggpartialfunc</replaceable> ]
 )
 
 CREATE [ OR REPLACE ] AGGREGATE <replaceable class="parameter">name</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">arg_data_type</replaceable> [ , ... ] ]
@@ -80,6 +81,7 @@ CREATE [ OR REPLACE ] AGGREGATE <replaceable class="parameter">name</replaceable
     [ , MFINALFUNC_MODIFY = { READ_ONLY | SHAREABLE | READ_WRITE } ]
     [ , MINITCOND = <replaceable class="parameter">minitial_condition</replaceable> ]
     [ , SORTOP = <replaceable class="parameter">sort_operator</replaceable> ]
+    [ , AGGPARTIALFUNC = <replaceable class="parameter">aggpartialfunc</replaceable> ]
 )
 </synopsis>
  </refsynopsisdiv>
@@ -244,6 +246,12 @@ CREATE [ OR REPLACE ] AGGREGATE <replaceable class="parameter">name</replaceable
    <literal>SERIALFUNC</literal> and <literal>DESERIALFUNC</literal> parameters so that
    parallel aggregation is possible.  Note that the aggregate must also be
    marked <literal>PARALLEL SAFE</literal> to enable parallel aggregation.
+
+   When you use the built-in sharding feature in PostgreSQL,
+   <filename>postgres_fdw</filename> needs an aggregate function
+   called <firstterm>aggpartialfunc</firstterm> for
+   aggregate on <filename>postgres_fdw</filename> node.
+   <literal>AGGPARTIALFUNC</literal> is a parameter of aggpartialfunc.
   </para>
 
   <para>
@@ -653,6 +661,39 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">aggpartialfunc</replaceable></term>
+    <listitem>
+     <para>
+      The name of the aggpartialfunc corresponding to the aggregate function.
+      See
+      <xref linkend="built-in-sharding-in-postgresql"/> for the
+      definition of aggpartialfunc.
+      If <replaceable class="parameter">name</replaceable> and
+      <replaceable class="parameter">aggpartialfunc</replaceable> match,
+      then the aggregate function to create must be its aggpartialfunc.
+      If aggpartialfunc provided, <replaceable class="parameter">combinefunc</replaceable>
+      must be provided.
+      For <replaceable class="parameter">sfunc</replaceable> and
+      <replaceable class="parameter">state_data_type</replaceable>, it must match in the aggregate
+      function and its aggpartialfunc.
+      For <replaceable class="parameter">combinefunc</replaceable> and
+      <replaceable class="parameter">initial_condition</replaceable>, if aggpartialfunc has it,
+      it must match in the aggregate function and its aggpartialfunc.
+      If <replaceable class="parameter">state_data_type</replaceable> is <type>internal</type>,
+      for <replaceable class="parameter">serialfunc</replaceable> and
+      <replaceable class="parameter">deserialfunc</replaceable>,
+      if aggpartialfunc has it,
+      it must match in the aggregate function and its aggpartialfunc.
+      If <replaceable class="parameter">state_data_type</replaceable> is <type>internal</type>,
+      serialfunc of the aggregate function must match
+      <replaceable class="parameter">ffunc</replaceable> of aggpartialfunc.
+      If state_data_type is not <type>internal</type>, aggpartialfunc must not have
+      <replaceable class="parameter">ffunc</replaceable>.
+     </para>
+    </listitem>
+   </varlistentry>
   </variablelist>
 
   <para>
diff --git a/doc/src/sgml/xaggr.sgml b/doc/src/sgml/xaggr.sgml
index bdad8d3dc2..c9779e0e3c 100644
--- a/doc/src/sgml/xaggr.sgml
+++ b/doc/src/sgml/xaggr.sgml
@@ -536,9 +536,6 @@ SELECT percentile_disc(0.5) WITHIN GROUP (ORDER BY income) FROM households;
    parallel aggregation by having different worker processes scan different
    portions of a table.  Each worker produces a partial state value, and at
    the end those state values are combined to produce a final state value.
-   (In the future this mode might also be used for purposes such as combining
-   aggregations over local and remote tables; but that is not implemented
-   yet.)
   </para>
 
   <para>
@@ -612,6 +609,15 @@ SELECT percentile_disc(0.5) WITHIN GROUP (ORDER BY income) FROM households;
    parallel-safety markings on its support functions are not consulted.
   </para>
 
+  <para>
+   When you use the built-in sharding feature in PostgreSQL,
+   <filename>postgres_fdw</filename> needs an aggregate function
+   called <firstterm>aggpartialfunc</firstterm> for
+   aggregate on <filename>postgres_fdw</filename> node. See
+   <xref linkend="built-in-sharding-in-postgresql"/> for the definition
+   of aggpartialfunc.
+  </para>
+
  </sect2>
 
  <sect2 id="xaggr-support-functions">
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index ebc4454743..79c5dc2b58 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -63,6 +63,7 @@ AggregateCreate(const char *aggName,
 				List *aggmtransfnName,
 				List *aggminvtransfnName,
 				List *aggmfinalfnName,
+				List *aggpartialfnName,
 				bool finalfnExtraArgs,
 				bool mfinalfnExtraArgs,
 				char finalfnModify,
@@ -91,6 +92,7 @@ AggregateCreate(const char *aggName,
 	Oid			mtransfn = InvalidOid;	/* can be omitted */
 	Oid			minvtransfn = InvalidOid;	/* can be omitted */
 	Oid			mfinalfn = InvalidOid;	/* can be omitted */
+	Oid			aggpartialfn = InvalidOid;	/* can be omitted */
 	Oid			sortop = InvalidOid;	/* can be omitted */
 	Oid		   *aggArgTypes = parameterTypes->values;
 	bool		mtransIsStrict = false;
@@ -569,6 +571,82 @@ AggregateCreate(const char *aggName,
 							format_type_be(finaltype))));
 	}
 
+	/*
+	 * Validate the aggpartialfunc, if present.
+	 */
+	if (aggpartialfnName)
+	{
+		if(!aggcombinefnName)
+			elog(ERROR, "aggcombinefnName must be supplied if aggpartialfnName is supplied");
+
+		if(list_length(aggpartialfnName) != 1)
+			elog(ERROR, "aggpartialfnName is invalid");
+
+		if(strcmp(strVal(linitial(aggpartialfnName)), aggName) == 0){
+			if(((aggTransType != INTERNALOID) && (finalfn != InvalidOid))
+					|| ((aggTransType == INTERNALOID) && (finalfn != serialfn)))
+				elog(ERROR, "%s is not its own aggpartialfunc", aggName);
+		} else {
+			HeapTuple aggtup;
+			Form_pg_aggregate aggpartialform;
+			Datum textInitVal;
+			char *strInitVal;
+			bool initValueIsNull;
+
+			aggpartialfn = LookupFuncName(aggpartialfnName, numArgs, aggArgTypes, false);
+
+			/* Check aggregate creator has permission to call the function */
+			aclresult = object_aclcheck(ProcedureRelationId, aggpartialfn,
+				GetUserId(), ACL_EXECUTE);
+			if (aclresult != ACLCHECK_OK)
+				aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(aggpartialfn));
+
+			rettype = get_func_rettype(aggpartialfn);
+
+			aggtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(aggpartialfn));
+			if (!HeapTupleIsValid(aggtup))
+				elog(ERROR, "cache lookup failed for aggpartialfunc %u", aggpartialfn);
+
+			aggpartialform = (Form_pg_aggregate) GETSTRUCT(aggtup);
+
+			if((aggpartialform->aggcombinefn != InvalidOid) &&
+				(aggpartialform->aggcombinefn != combinefn))
+				elog(ERROR, "%s in aggregate and %s in its aggpartialfunc must match",
+					"combinefunc", "combinefunc");
+
+			if(aggpartialform->aggtransfn != transfn)
+				elog(ERROR, "%s in aggregate and its aggpartialfunc must match", "sfunc");
+
+			textInitVal = SysCacheGetAttr(AGGFNOID, aggtup,
+							Anum_pg_aggregate_agginitval, &initValueIsNull);
+
+			if(!initValueIsNull && (agginitval != NULL)){
+				strInitVal = TextDatumGetCString(textInitVal);
+				if(strcmp(strInitVal, agginitval) != 0)
+					elog(ERROR, "%s in aggregate and %s in its aggpartialfunc must match",
+						"initcond", "initcond");
+			}
+
+			if (aggTransType == INTERNALOID){
+				if(aggpartialform->aggcombinefn != InvalidOid){
+					if(aggpartialform->aggserialfn != serialfn)
+						elog(ERROR, "%s in aggregate and %s in its aggpartialfunc must match",
+							"serialfunc", "serialfunc");
+
+					if(aggpartialform->aggdeserialfn != deserialfn)
+						elog(ERROR, "%s in aggregate and %s in its aggpartialfunc must match",
+							"deserialfunc", "deserialfunc");
+				}
+				if(aggpartialform->aggfinalfn != serialfn)
+					elog(ERROR, "finalfunc of aggpartialfunc must match serialfunc of aggregate when stype is internal");
+			}
+			else if(aggpartialform->aggfinalfn != InvalidOid)
+				elog(ERROR, "finalfunc of aggpartialfunc must not be supplied when stype isn't internal");
+
+			ReleaseSysCache(aggtup);
+		}
+	}
+
 	/* handle sortop, if supplied */
 	if (aggsortopName)
 	{
@@ -684,6 +762,7 @@ AggregateCreate(const char *aggName,
 		values[Anum_pg_aggregate_aggminitval - 1] = CStringGetTextDatum(aggminitval);
 	else
 		nulls[Anum_pg_aggregate_aggminitval - 1] = true;
+	values[Anum_pg_aggregate_aggpartialfn - 1] = ObjectIdGetDatum(aggpartialfn);
 
 	if (replace)
 		oldtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(procOid));
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c
index fda9d1aa77..8c8f1789e9 100644
--- a/src/backend/commands/aggregatecmds.c
+++ b/src/backend/commands/aggregatecmds.c
@@ -70,6 +70,7 @@ DefineAggregate(ParseState *pstate,
 	List	   *combinefuncName = NIL;
 	List	   *serialfuncName = NIL;
 	List	   *deserialfuncName = NIL;
+	List	   *aggpartialfuncName = NIL;
 	List	   *mtransfuncName = NIL;
 	List	   *minvtransfuncName = NIL;
 	List	   *mfinalfuncName = NIL;
@@ -143,6 +144,8 @@ DefineAggregate(ParseState *pstate,
 			serialfuncName = defGetQualifiedName(defel);
 		else if (strcmp(defel->defname, "deserialfunc") == 0)
 			deserialfuncName = defGetQualifiedName(defel);
+		else if (strcmp(defel->defname, "aggpartialfunc") == 0)
+			aggpartialfuncName = defGetQualifiedName(defel);
 		else if (strcmp(defel->defname, "msfunc") == 0)
 			mtransfuncName = defGetQualifiedName(defel);
 		else if (strcmp(defel->defname, "minvfunc") == 0)
@@ -461,6 +464,7 @@ DefineAggregate(ParseState *pstate,
 						   mtransfuncName,	/* fwd trans function name */
 						   minvtransfuncName,	/* inv trans function name */
 						   mfinalfuncName,	/* final function name */
+						   aggpartialfuncName,
 						   finalfuncExtraArgs,
 						   mfinalfuncExtraArgs,
 						   finalfuncModify,
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 5dab1ba9ea..b958145ec2 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -13679,6 +13679,7 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 	const char *aggmtransfn;
 	const char *aggminvtransfn;
 	const char *aggmfinalfn;
+	const char *aggpartialfn;
 	bool		aggfinalextra;
 	bool		aggmfinalextra;
 	char		aggfinalmodify;
@@ -13761,11 +13762,11 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 		if (fout->remoteVersion >= 110000)
 			appendPQExpBufferStr(query,
 								 "aggfinalmodify,\n"
-								 "aggmfinalmodify\n");
+								 "aggmfinalmodify,\n");
 		else
 			appendPQExpBufferStr(query,
 								 "'0' AS aggfinalmodify,\n"
-								 "'0' AS aggmfinalmodify\n");
+								 "'0' AS aggmfinalmodify,\n");
 
 		appendPQExpBufferStr(query,
 							 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
@@ -13791,6 +13792,7 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 	aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
 	aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
 	aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
+	aggpartialfn = PQgetvalue(res, 0, PQfnumber(res, "aggpartialfn"));
 	aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
 	aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
 	aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
@@ -13880,6 +13882,9 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 	if (strcmp(aggdeserialfn, "-") != 0)
 		appendPQExpBuffer(details, ",\n    DESERIALFUNC = %s", aggdeserialfn);
 
+	if (strcmp(aggpartialfn, "-") != 0)
+		appendPQExpBuffer(details, ",\n    AGGPARTIALFUNC = %s", aggpartialfn);
+
 	if (strcmp(aggmtransfn, "-") != 0)
 	{
 		appendPQExpBuffer(details, ",\n    MSFUNC = %s,\n    MINVFUNC = %s,\n    MSTYPE = %s",
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index 1bc1d97d74..dd97b4dc34 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -13,22 +13,44 @@
 [
 
 # avg
+{ aggfnoid => 'avg_p_int8', aggtransfn => 'int8_avg_accum',
+  aggfinalfn => 'int8_avg_serialize', aggcombinefn => 'int8_avg_combine',
+  aggserialfn => 'int8_avg_serialize', aggdeserialfn => 'int8_avg_deserialize',
+  aggtranstype => 'internal', aggtransspace => '48',
+  aggpartialfn => 'avg_p_int8' },
 { aggfnoid => 'avg(int8)', aggtransfn => 'int8_avg_accum',
   aggfinalfn => 'numeric_poly_avg', aggcombinefn => 'int8_avg_combine',
   aggserialfn => 'int8_avg_serialize', aggdeserialfn => 'int8_avg_deserialize',
   aggmtransfn => 'int8_avg_accum', aggminvtransfn => 'int8_avg_accum_inv',
   aggmfinalfn => 'numeric_poly_avg', aggtranstype => 'internal',
-  aggtransspace => '48', aggmtranstype => 'internal', aggmtransspace => '48' },
+  aggtransspace => '48', aggmtranstype => 'internal', aggmtransspace => '48',
+  aggpartialfn => 'avg_p_int8' },
+{ aggfnoid => 'avg_p_int4', aggtransfn => 'int4_avg_accum',
+  aggcombinefn => 'int4_avg_combine', aggtranstype => '_int8',
+  agginitval => '{0,0}',
+  aggpartialfn => 'avg_p_int4' },
 { aggfnoid => 'avg(int4)', aggtransfn => 'int4_avg_accum',
   aggfinalfn => 'int8_avg', aggcombinefn => 'int4_avg_combine',
   aggmtransfn => 'int4_avg_accum', aggminvtransfn => 'int4_avg_accum_inv',
   aggmfinalfn => 'int8_avg', aggtranstype => '_int8', aggmtranstype => '_int8',
-  agginitval => '{0,0}', aggminitval => '{0,0}' },
+  agginitval => '{0,0}', aggminitval => '{0,0}',
+  aggpartialfn => 'avg_p_int4' },
+{ aggfnoid => 'avg_p_int2', aggtransfn => 'int2_avg_accum',
+  aggcombinefn => 'int4_avg_combine', aggtranstype => '_int8',
+  agginitval => '{0,0}',
+  aggpartialfn => 'avg_p_int2' },
 { aggfnoid => 'avg(int2)', aggtransfn => 'int2_avg_accum',
   aggfinalfn => 'int8_avg', aggcombinefn => 'int4_avg_combine',
   aggmtransfn => 'int2_avg_accum', aggminvtransfn => 'int2_avg_accum_inv',
   aggmfinalfn => 'int8_avg', aggtranstype => '_int8', aggmtranstype => '_int8',
-  agginitval => '{0,0}', aggminitval => '{0,0}' },
+  agginitval => '{0,0}', aggminitval => '{0,0}',
+  aggpartialfn => 'avg_p_int2' },
+{ aggfnoid => 'avg_p_numeric', aggtransfn => 'numeric_avg_accum',
+  aggfinalfn => 'numeric_avg_serialize', aggcombinefn => 'numeric_avg_combine',
+  aggserialfn => 'numeric_avg_serialize',
+  aggdeserialfn => 'numeric_avg_deserialize', aggtranstype => 'internal',
+  aggtransspace => '128',
+  aggpartialfn => 'avg_p_numeric' },
 { aggfnoid => 'avg(numeric)', aggtransfn => 'numeric_avg_accum',
   aggfinalfn => 'numeric_avg', aggcombinefn => 'numeric_avg_combine',
   aggserialfn => 'numeric_avg_serialize',
@@ -36,46 +58,80 @@
   aggmtransfn => 'numeric_avg_accum', aggminvtransfn => 'numeric_accum_inv',
   aggmfinalfn => 'numeric_avg', aggtranstype => 'internal',
   aggtransspace => '128', aggmtranstype => 'internal',
-  aggmtransspace => '128' },
+  aggmtransspace => '128',
+  aggpartialfn => 'avg_p_numeric' },
+{ aggfnoid => 'avg_p_float4', aggtransfn => 'float4_accum',
+  aggcombinefn => 'float8_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0}',
+  aggpartialfn => 'avg_p_float4' },
 { aggfnoid => 'avg(float4)', aggtransfn => 'float4_accum',
   aggfinalfn => 'float8_avg', aggcombinefn => 'float8_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0}',
+  aggpartialfn => 'avg_p_float4' },
+{ aggfnoid => 'avg_p_float8', aggtransfn => 'float8_accum',
+  aggcombinefn => 'float8_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0}',
+  aggpartialfn => 'avg_p_float8' },
 { aggfnoid => 'avg(float8)', aggtransfn => 'float8_accum',
   aggfinalfn => 'float8_avg', aggcombinefn => 'float8_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0}',
+  aggpartialfn => 'avg_p_float8' },
+{ aggfnoid => 'avg_p_interval', aggtransfn => 'interval_accum',
+  aggcombinefn => 'interval_combine', aggtranstype => '_interval',
+  agginitval => '{0 second,0 second}',
+  aggpartialfn => 'avg_p_interval' },
 { aggfnoid => 'avg(interval)', aggtransfn => 'interval_accum',
   aggfinalfn => 'interval_avg', aggcombinefn => 'interval_combine',
   aggmtransfn => 'interval_accum', aggminvtransfn => 'interval_accum_inv',
   aggmfinalfn => 'interval_avg', aggtranstype => '_interval',
   aggmtranstype => '_interval', agginitval => '{0 second,0 second}',
-  aggminitval => '{0 second,0 second}' },
+  aggminitval => '{0 second,0 second}',
+  aggpartialfn => 'avg_p_interval' },
 
 # sum
+{ aggfnoid => 'sum_p_int8', aggtransfn => 'int8_avg_accum',
+  aggfinalfn => 'int8_avg_serialize', aggcombinefn => 'int8_avg_combine',
+  aggserialfn => 'int8_avg_serialize', aggdeserialfn => 'int8_avg_deserialize',
+  aggtranstype => 'internal', aggtransspace => '48',
+  aggpartialfn => 'sum_p_int8' },
 { aggfnoid => 'sum(int8)', aggtransfn => 'int8_avg_accum',
   aggfinalfn => 'numeric_poly_sum', aggcombinefn => 'int8_avg_combine',
   aggserialfn => 'int8_avg_serialize', aggdeserialfn => 'int8_avg_deserialize',
   aggmtransfn => 'int8_avg_accum', aggminvtransfn => 'int8_avg_accum_inv',
   aggmfinalfn => 'numeric_poly_sum', aggtranstype => 'internal',
-  aggtransspace => '48', aggmtranstype => 'internal', aggmtransspace => '48' },
+  aggtransspace => '48', aggmtranstype => 'internal', aggmtransspace => '48',
+  aggpartialfn => 'sum_p_int8' },
 { aggfnoid => 'sum(int4)', aggtransfn => 'int4_sum', aggcombinefn => 'int8pl',
   aggmtransfn => 'int4_avg_accum', aggminvtransfn => 'int4_avg_accum_inv',
   aggmfinalfn => 'int2int4_sum', aggtranstype => 'int8',
-  aggmtranstype => '_int8', aggminitval => '{0,0}' },
+  aggmtranstype => '_int8', aggminitval => '{0,0}',
+  aggpartialfn => 'sum(int4)' },
 { aggfnoid => 'sum(int2)', aggtransfn => 'int2_sum', aggcombinefn => 'int8pl',
   aggmtransfn => 'int2_avg_accum', aggminvtransfn => 'int2_avg_accum_inv',
   aggmfinalfn => 'int2int4_sum', aggtranstype => 'int8',
-  aggmtranstype => '_int8', aggminitval => '{0,0}' },
+  aggmtranstype => '_int8', aggminitval => '{0,0}',
+  aggpartialfn => 'sum(int2)' },
 { aggfnoid => 'sum(float4)', aggtransfn => 'float4pl',
-  aggcombinefn => 'float4pl', aggtranstype => 'float4' },
+  aggcombinefn => 'float4pl', aggtranstype => 'float4',
+  aggpartialfn => 'sum(float4)' },
 { aggfnoid => 'sum(float8)', aggtransfn => 'float8pl',
-  aggcombinefn => 'float8pl', aggtranstype => 'float8' },
+  aggcombinefn => 'float8pl', aggtranstype => 'float8',
+  aggpartialfn => 'sum(float8)' },
 { aggfnoid => 'sum(money)', aggtransfn => 'cash_pl', aggcombinefn => 'cash_pl',
   aggmtransfn => 'cash_pl', aggminvtransfn => 'cash_mi',
-  aggtranstype => 'money', aggmtranstype => 'money' },
+  aggtranstype => 'money', aggmtranstype => 'money',
+  aggpartialfn => 'sum(money)' },
 { aggfnoid => 'sum(interval)', aggtransfn => 'interval_pl',
   aggcombinefn => 'interval_pl', aggmtransfn => 'interval_pl',
   aggminvtransfn => 'interval_mi', aggtranstype => 'interval',
-  aggmtranstype => 'interval' },
+  aggmtranstype => 'interval',
+  aggpartialfn => 'sum(interval)' },
+{ aggfnoid => 'sum_p_numeric', aggtransfn => 'numeric_avg_accum',
+  aggfinalfn => 'numeric_avg_serialize', aggcombinefn => 'numeric_avg_combine',
+  aggserialfn => 'numeric_avg_serialize',
+  aggdeserialfn => 'numeric_avg_deserialize', aggtranstype => 'internal',
+  aggtransspace => '128',
+  aggpartialfn => 'sum_p_numeric' },
 { aggfnoid => 'sum(numeric)', aggtransfn => 'numeric_avg_accum',
   aggfinalfn => 'numeric_sum', aggcombinefn => 'numeric_avg_combine',
   aggserialfn => 'numeric_avg_serialize',
@@ -83,269 +139,436 @@
   aggmtransfn => 'numeric_avg_accum', aggminvtransfn => 'numeric_accum_inv',
   aggmfinalfn => 'numeric_sum', aggtranstype => 'internal',
   aggtransspace => '128', aggmtranstype => 'internal',
-  aggmtransspace => '128' },
+  aggmtransspace => '128',
+  aggpartialfn => 'sum_p_numeric' },
 
 # max
 { aggfnoid => 'max(int8)', aggtransfn => 'int8larger',
   aggcombinefn => 'int8larger', aggsortop => '>(int8,int8)',
-  aggtranstype => 'int8' },
+  aggtranstype => 'int8',
+  aggpartialfn => 'max(int8)' },
 { aggfnoid => 'max(int4)', aggtransfn => 'int4larger',
   aggcombinefn => 'int4larger', aggsortop => '>(int4,int4)',
-  aggtranstype => 'int4' },
+  aggtranstype => 'int4',
+  aggpartialfn => 'max(int4)' },
 { aggfnoid => 'max(int2)', aggtransfn => 'int2larger',
   aggcombinefn => 'int2larger', aggsortop => '>(int2,int2)',
-  aggtranstype => 'int2' },
+  aggtranstype => 'int2',
+  aggpartialfn => 'max(int2)' },
 { aggfnoid => 'max(oid)', aggtransfn => 'oidlarger',
   aggcombinefn => 'oidlarger', aggsortop => '>(oid,oid)',
-  aggtranstype => 'oid' },
+  aggtranstype => 'oid',
+  aggpartialfn => 'max(oid)' },
 { aggfnoid => 'max(float4)', aggtransfn => 'float4larger',
   aggcombinefn => 'float4larger', aggsortop => '>(float4,float4)',
-  aggtranstype => 'float4' },
+  aggtranstype => 'float4',
+  aggpartialfn => 'max(float4)' },
 { aggfnoid => 'max(float8)', aggtransfn => 'float8larger',
   aggcombinefn => 'float8larger', aggsortop => '>(float8,float8)',
-  aggtranstype => 'float8' },
+  aggtranstype => 'float8',
+  aggpartialfn => 'max(float8)' },
 { aggfnoid => 'max(date)', aggtransfn => 'date_larger',
   aggcombinefn => 'date_larger', aggsortop => '>(date,date)',
-  aggtranstype => 'date' },
+  aggtranstype => 'date',
+  aggpartialfn => 'max(date)' },
 { aggfnoid => 'max(time)', aggtransfn => 'time_larger',
   aggcombinefn => 'time_larger', aggsortop => '>(time,time)',
-  aggtranstype => 'time' },
+  aggtranstype => 'time',
+  aggpartialfn => 'max(time)' },
 { aggfnoid => 'max(timetz)', aggtransfn => 'timetz_larger',
   aggcombinefn => 'timetz_larger', aggsortop => '>(timetz,timetz)',
-  aggtranstype => 'timetz' },
+  aggtranstype => 'timetz',
+  aggpartialfn => 'max(timetz)' },
 { aggfnoid => 'max(money)', aggtransfn => 'cashlarger',
   aggcombinefn => 'cashlarger', aggsortop => '>(money,money)',
-  aggtranstype => 'money' },
+  aggtranstype => 'money',
+  aggpartialfn => 'max(money)' },
 { aggfnoid => 'max(timestamp)', aggtransfn => 'timestamp_larger',
   aggcombinefn => 'timestamp_larger', aggsortop => '>(timestamp,timestamp)',
-  aggtranstype => 'timestamp' },
+  aggtranstype => 'timestamp',
+  aggpartialfn => 'max(timestamp)' },
 { aggfnoid => 'max(timestamptz)', aggtransfn => 'timestamptz_larger',
   aggcombinefn => 'timestamptz_larger',
-  aggsortop => '>(timestamptz,timestamptz)', aggtranstype => 'timestamptz' },
+  aggsortop => '>(timestamptz,timestamptz)', aggtranstype => 'timestamptz',
+  aggpartialfn => 'max(timestamptz)' },
 { aggfnoid => 'max(interval)', aggtransfn => 'interval_larger',
   aggcombinefn => 'interval_larger', aggsortop => '>(interval,interval)',
-  aggtranstype => 'interval' },
+  aggtranstype => 'interval',
+  aggpartialfn => 'max(interval)' },
 { aggfnoid => 'max(text)', aggtransfn => 'text_larger',
   aggcombinefn => 'text_larger', aggsortop => '>(text,text)',
-  aggtranstype => 'text' },
+  aggtranstype => 'text',
+  aggpartialfn => 'max(text)' },
 { aggfnoid => 'max(numeric)', aggtransfn => 'numeric_larger',
   aggcombinefn => 'numeric_larger', aggsortop => '>(numeric,numeric)',
-  aggtranstype => 'numeric' },
+  aggtranstype => 'numeric',
+  aggpartialfn => 'max(numeric)' },
 { aggfnoid => 'max(anyarray)', aggtransfn => 'array_larger',
   aggcombinefn => 'array_larger', aggsortop => '>(anyarray,anyarray)',
-  aggtranstype => 'anyarray' },
+  aggtranstype => 'anyarray',
+  aggpartialfn => 'max(anyarray)' },
 { aggfnoid => 'max(bpchar)', aggtransfn => 'bpchar_larger',
   aggcombinefn => 'bpchar_larger', aggsortop => '>(bpchar,bpchar)',
-  aggtranstype => 'bpchar' },
+  aggtranstype => 'bpchar',
+  aggpartialfn => 'max(bpchar)' },
 { aggfnoid => 'max(tid)', aggtransfn => 'tidlarger',
   aggcombinefn => 'tidlarger', aggsortop => '>(tid,tid)',
-  aggtranstype => 'tid' },
+  aggtranstype => 'tid',
+  aggpartialfn => 'max(tid)' },
 { aggfnoid => 'max(anyenum)', aggtransfn => 'enum_larger',
   aggcombinefn => 'enum_larger', aggsortop => '>(anyenum,anyenum)',
-  aggtranstype => 'anyenum' },
+  aggtranstype => 'anyenum',
+  aggpartialfn => 'max(anyenum)' },
 { aggfnoid => 'max(inet)', aggtransfn => 'network_larger',
   aggcombinefn => 'network_larger', aggsortop => '>(inet,inet)',
-  aggtranstype => 'inet' },
+  aggtranstype => 'inet',
+  aggpartialfn => 'max(inet)' },
 { aggfnoid => 'max(pg_lsn)', aggtransfn => 'pg_lsn_larger',
   aggcombinefn => 'pg_lsn_larger', aggsortop => '>(pg_lsn,pg_lsn)',
-  aggtranstype => 'pg_lsn' },
+  aggtranstype => 'pg_lsn',
+  aggpartialfn => 'max(pg_lsn)' },
 { aggfnoid => 'max(xid8)', aggtransfn => 'xid8_larger',
   aggcombinefn => 'xid8_larger', aggsortop => '>(xid8,xid8)',
-  aggtranstype => 'xid8' },
+  aggtranstype => 'xid8',
+  aggpartialfn => 'max(xid8)' },
 
 # min
 { aggfnoid => 'min(int8)', aggtransfn => 'int8smaller',
   aggcombinefn => 'int8smaller', aggsortop => '<(int8,int8)',
-  aggtranstype => 'int8' },
+  aggtranstype => 'int8',
+  aggpartialfn => 'min(int8)' },
 { aggfnoid => 'min(int4)', aggtransfn => 'int4smaller',
   aggcombinefn => 'int4smaller', aggsortop => '<(int4,int4)',
-  aggtranstype => 'int4' },
+  aggtranstype => 'int4',
+  aggpartialfn => 'min(int4)' },
 { aggfnoid => 'min(int2)', aggtransfn => 'int2smaller',
   aggcombinefn => 'int2smaller', aggsortop => '<(int2,int2)',
-  aggtranstype => 'int2' },
+  aggtranstype => 'int2',
+  aggpartialfn => 'min(int2)' },
 { aggfnoid => 'min(oid)', aggtransfn => 'oidsmaller',
   aggcombinefn => 'oidsmaller', aggsortop => '<(oid,oid)',
-  aggtranstype => 'oid' },
+  aggtranstype => 'oid',
+  aggpartialfn => 'min(oid)' },
 { aggfnoid => 'min(float4)', aggtransfn => 'float4smaller',
   aggcombinefn => 'float4smaller', aggsortop => '<(float4,float4)',
-  aggtranstype => 'float4' },
+  aggtranstype => 'float4',
+  aggpartialfn => 'min(float4)' },
 { aggfnoid => 'min(float8)', aggtransfn => 'float8smaller',
   aggcombinefn => 'float8smaller', aggsortop => '<(float8,float8)',
-  aggtranstype => 'float8' },
+  aggtranstype => 'float8',
+  aggpartialfn => 'min(float8)' },
 { aggfnoid => 'min(date)', aggtransfn => 'date_smaller',
   aggcombinefn => 'date_smaller', aggsortop => '<(date,date)',
-  aggtranstype => 'date' },
+  aggtranstype => 'date',
+  aggpartialfn => 'min(date)' },
 { aggfnoid => 'min(time)', aggtransfn => 'time_smaller',
   aggcombinefn => 'time_smaller', aggsortop => '<(time,time)',
-  aggtranstype => 'time' },
+  aggtranstype => 'time',
+  aggpartialfn => 'min(time)' },
 { aggfnoid => 'min(timetz)', aggtransfn => 'timetz_smaller',
   aggcombinefn => 'timetz_smaller', aggsortop => '<(timetz,timetz)',
-  aggtranstype => 'timetz' },
+  aggtranstype => 'timetz',
+  aggpartialfn => 'min(timetz)' },
 { aggfnoid => 'min(money)', aggtransfn => 'cashsmaller',
   aggcombinefn => 'cashsmaller', aggsortop => '<(money,money)',
-  aggtranstype => 'money' },
+  aggtranstype => 'money',
+  aggpartialfn => 'min(money)' },
 { aggfnoid => 'min(timestamp)', aggtransfn => 'timestamp_smaller',
   aggcombinefn => 'timestamp_smaller', aggsortop => '<(timestamp,timestamp)',
-  aggtranstype => 'timestamp' },
+  aggtranstype => 'timestamp',
+  aggpartialfn => 'min(timestamp)' },
 { aggfnoid => 'min(timestamptz)', aggtransfn => 'timestamptz_smaller',
   aggcombinefn => 'timestamptz_smaller',
-  aggsortop => '<(timestamptz,timestamptz)', aggtranstype => 'timestamptz' },
+  aggsortop => '<(timestamptz,timestamptz)', aggtranstype => 'timestamptz',
+  aggpartialfn => 'min(timestamptz)' },
 { aggfnoid => 'min(interval)', aggtransfn => 'interval_smaller',
   aggcombinefn => 'interval_smaller', aggsortop => '<(interval,interval)',
-  aggtranstype => 'interval' },
+  aggtranstype => 'interval',
+  aggpartialfn => 'min(interval)' },
 { aggfnoid => 'min(text)', aggtransfn => 'text_smaller',
   aggcombinefn => 'text_smaller', aggsortop => '<(text,text)',
-  aggtranstype => 'text' },
+  aggtranstype => 'text',
+  aggpartialfn => 'min(text)' },
 { aggfnoid => 'min(numeric)', aggtransfn => 'numeric_smaller',
   aggcombinefn => 'numeric_smaller', aggsortop => '<(numeric,numeric)',
-  aggtranstype => 'numeric' },
+  aggtranstype => 'numeric',
+  aggpartialfn => 'min(numeric)' },
 { aggfnoid => 'min(anyarray)', aggtransfn => 'array_smaller',
   aggcombinefn => 'array_smaller', aggsortop => '<(anyarray,anyarray)',
-  aggtranstype => 'anyarray' },
+  aggtranstype => 'anyarray',
+  aggpartialfn => 'min(anyarray)' },
 { aggfnoid => 'min(bpchar)', aggtransfn => 'bpchar_smaller',
   aggcombinefn => 'bpchar_smaller', aggsortop => '<(bpchar,bpchar)',
-  aggtranstype => 'bpchar' },
+  aggtranstype => 'bpchar',
+  aggpartialfn => 'min(bpchar)' },
 { aggfnoid => 'min(tid)', aggtransfn => 'tidsmaller',
   aggcombinefn => 'tidsmaller', aggsortop => '<(tid,tid)',
-  aggtranstype => 'tid' },
+  aggtranstype => 'tid',
+  aggpartialfn => 'min(tid)' },
 { aggfnoid => 'min(anyenum)', aggtransfn => 'enum_smaller',
   aggcombinefn => 'enum_smaller', aggsortop => '<(anyenum,anyenum)',
-  aggtranstype => 'anyenum' },
+  aggtranstype => 'anyenum',
+  aggpartialfn => 'min(anyenum)' },
 { aggfnoid => 'min(inet)', aggtransfn => 'network_smaller',
   aggcombinefn => 'network_smaller', aggsortop => '<(inet,inet)',
-  aggtranstype => 'inet' },
+  aggtranstype => 'inet',
+  aggpartialfn => 'min(inet)' },
 { aggfnoid => 'min(pg_lsn)', aggtransfn => 'pg_lsn_smaller',
   aggcombinefn => 'pg_lsn_smaller', aggsortop => '<(pg_lsn,pg_lsn)',
-  aggtranstype => 'pg_lsn' },
+  aggtranstype => 'pg_lsn',
+  aggpartialfn => 'min(pg_lsn)' },
 { aggfnoid => 'min(xid8)', aggtransfn => 'xid8_smaller',
   aggcombinefn => 'xid8_smaller', aggsortop => '<(xid8,xid8)',
-  aggtranstype => 'xid8' },
+  aggtranstype => 'xid8',
+  aggpartialfn => 'min(xid8)' },
 
 # count
 { aggfnoid => 'count(any)', aggtransfn => 'int8inc_any',
   aggcombinefn => 'int8pl', aggmtransfn => 'int8inc_any',
   aggminvtransfn => 'int8dec_any', aggtranstype => 'int8',
-  aggmtranstype => 'int8', agginitval => '0', aggminitval => '0' },
+  aggmtranstype => 'int8', agginitval => '0', aggminitval => '0',
+  aggpartialfn => 'count(any)' },
 { aggfnoid => 'count()', aggtransfn => 'int8inc', aggcombinefn => 'int8pl',
   aggmtransfn => 'int8inc', aggminvtransfn => 'int8dec', aggtranstype => 'int8',
-  aggmtranstype => 'int8', agginitval => '0', aggminitval => '0' },
+  aggmtranstype => 'int8', agginitval => '0', aggminitval => '0',
+  aggpartialfn => 'count()' },
 
 # var_pop
+{ aggfnoid => 'var_pop_p_int8', aggtransfn => 'int8_accum',
+  aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine',
+  aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
+  aggtranstype => 'internal', aggtransspace => '128',
+  aggpartialfn => 'var_pop_p_int8' },
 { aggfnoid => 'var_pop(int8)', aggtransfn => 'int8_accum',
   aggfinalfn => 'numeric_var_pop', aggcombinefn => 'numeric_combine',
   aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
   aggmtransfn => 'int8_accum', aggminvtransfn => 'int8_accum_inv',
   aggmfinalfn => 'numeric_var_pop', aggtranstype => 'internal',
   aggtransspace => '128', aggmtranstype => 'internal',
-  aggmtransspace => '128' },
+  aggmtransspace => '128',
+  aggpartialfn => 'var_pop_p_int8' },
+{ aggfnoid => 'var_pop_p_int4', aggtransfn => 'int4_accum',
+  aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine',
+  aggserialfn => 'numeric_poly_serialize',
+  aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal',
+  aggtransspace => '48',
+  aggpartialfn => 'var_pop_p_int4' },
 { aggfnoid => 'var_pop(int4)', aggtransfn => 'int4_accum',
   aggfinalfn => 'numeric_poly_var_pop', aggcombinefn => 'numeric_poly_combine',
   aggserialfn => 'numeric_poly_serialize',
   aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int4_accum',
   aggminvtransfn => 'int4_accum_inv', aggmfinalfn => 'numeric_poly_var_pop',
   aggtranstype => 'internal', aggtransspace => '48',
-  aggmtranstype => 'internal', aggmtransspace => '48' },
+  aggmtranstype => 'internal', aggmtransspace => '48',
+  aggpartialfn => 'var_pop_p_int4' },
+{ aggfnoid => 'var_pop_p_int2', aggtransfn => 'int2_accum',
+  aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine',
+  aggserialfn => 'numeric_poly_serialize',
+  aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal',
+  aggtransspace => '48',
+  aggpartialfn => 'var_pop_p_int2' },
 { aggfnoid => 'var_pop(int2)', aggtransfn => 'int2_accum',
   aggfinalfn => 'numeric_poly_var_pop', aggcombinefn => 'numeric_poly_combine',
   aggserialfn => 'numeric_poly_serialize',
   aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int2_accum',
   aggminvtransfn => 'int2_accum_inv', aggmfinalfn => 'numeric_poly_var_pop',
   aggtranstype => 'internal', aggtransspace => '48',
-  aggmtranstype => 'internal', aggmtransspace => '48' },
+  aggmtranstype => 'internal', aggmtransspace => '48',
+  aggpartialfn => 'var_pop_p_int2' },
+{ aggfnoid => 'var_pop_p_float4', aggtransfn => 'float4_accum',
+  aggcombinefn => 'float8_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0}',
+  aggpartialfn => 'var_pop_p_float4' },
 { aggfnoid => 'var_pop(float4)', aggtransfn => 'float4_accum',
   aggfinalfn => 'float8_var_pop', aggcombinefn => 'float8_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0}',
+  aggpartialfn => 'var_pop_p_float4' },
+{ aggfnoid => 'var_pop_p_float8', aggtransfn => 'float8_accum',
+  aggcombinefn => 'float8_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0}',
+  aggpartialfn => 'var_pop_p_float8' },
 { aggfnoid => 'var_pop(float8)', aggtransfn => 'float8_accum',
   aggfinalfn => 'float8_var_pop', aggcombinefn => 'float8_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0}',
+  aggpartialfn => 'var_pop_p_float8' },
+{ aggfnoid => 'var_pop_p_numeric', aggtransfn => 'numeric_accum',
+  aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine',
+  aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
+  aggtranstype => 'internal', aggtransspace => '128',
+  aggpartialfn => 'var_pop_p_numeric' },
 { aggfnoid => 'var_pop(numeric)', aggtransfn => 'numeric_accum',
   aggfinalfn => 'numeric_var_pop', aggcombinefn => 'numeric_combine',
   aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
   aggmtransfn => 'numeric_accum', aggminvtransfn => 'numeric_accum_inv',
   aggmfinalfn => 'numeric_var_pop', aggtranstype => 'internal',
   aggtransspace => '128', aggmtranstype => 'internal',
-  aggmtransspace => '128' },
+  aggmtransspace => '128',
+  aggpartialfn => 'var_pop_p_numeric' },
 
 # var_samp
+{ aggfnoid => 'var_samp_p_int8', aggtransfn => 'int8_accum',
+  aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine',
+  aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
+  aggtranstype => 'internal', aggtransspace => '128',
+  aggpartialfn => 'var_samp_p_int8' },
 { aggfnoid => 'var_samp(int8)', aggtransfn => 'int8_accum',
   aggfinalfn => 'numeric_var_samp', aggcombinefn => 'numeric_combine',
   aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
   aggmtransfn => 'int8_accum', aggminvtransfn => 'int8_accum_inv',
   aggmfinalfn => 'numeric_var_samp', aggtranstype => 'internal',
   aggtransspace => '128', aggmtranstype => 'internal',
-  aggmtransspace => '128' },
+  aggmtransspace => '128',
+  aggpartialfn => 'var_samp_p_int8' },
+{ aggfnoid => 'var_samp_p_int4', aggtransfn => 'int4_accum',
+  aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine',
+  aggserialfn => 'numeric_poly_serialize',
+  aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal',
+  aggtransspace => '48',
+  aggpartialfn => 'var_samp_p_int4' },
 { aggfnoid => 'var_samp(int4)', aggtransfn => 'int4_accum',
   aggfinalfn => 'numeric_poly_var_samp', aggcombinefn => 'numeric_poly_combine',
   aggserialfn => 'numeric_poly_serialize',
   aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int4_accum',
   aggminvtransfn => 'int4_accum_inv', aggmfinalfn => 'numeric_poly_var_samp',
   aggtranstype => 'internal', aggtransspace => '48',
-  aggmtranstype => 'internal', aggmtransspace => '48' },
+  aggmtranstype => 'internal', aggmtransspace => '48',
+  aggpartialfn => 'var_samp_p_int4' },
+{ aggfnoid => 'var_samp_p_int2', aggtransfn => 'int2_accum',
+  aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine',
+  aggserialfn => 'numeric_poly_serialize',
+  aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal',
+  aggtransspace => '48',
+  aggpartialfn => 'var_samp_p_int2' },
 { aggfnoid => 'var_samp(int2)', aggtransfn => 'int2_accum',
   aggfinalfn => 'numeric_poly_var_samp', aggcombinefn => 'numeric_poly_combine',
   aggserialfn => 'numeric_poly_serialize',
   aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int2_accum',
   aggminvtransfn => 'int2_accum_inv', aggmfinalfn => 'numeric_poly_var_samp',
   aggtranstype => 'internal', aggtransspace => '48',
-  aggmtranstype => 'internal', aggmtransspace => '48' },
+  aggmtranstype => 'internal', aggmtransspace => '48',
+  aggpartialfn => 'var_samp_p_int2' },
+{ aggfnoid => 'var_samp_p_float4', aggtransfn => 'float4_accum',
+  aggcombinefn => 'float8_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0}',
+  aggpartialfn => 'var_samp_p_float4' },
 { aggfnoid => 'var_samp(float4)', aggtransfn => 'float4_accum',
   aggfinalfn => 'float8_var_samp', aggcombinefn => 'float8_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0}',
+  aggpartialfn => 'var_samp_p_float4' },
+{ aggfnoid => 'var_samp_p_float8', aggtransfn => 'float8_accum',
+  aggcombinefn => 'float8_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0}',
+  aggpartialfn => 'var_samp_p_float8' },
 { aggfnoid => 'var_samp(float8)', aggtransfn => 'float8_accum',
   aggfinalfn => 'float8_var_samp', aggcombinefn => 'float8_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0}',
+  aggpartialfn => 'var_samp_p_float8' },
+{ aggfnoid => 'var_samp_p_numeric', aggtransfn => 'numeric_accum',
+  aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine',
+  aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
+  aggtranstype => 'internal', aggtransspace => '128',
+  aggpartialfn => 'var_samp_p_numeric' },
 { aggfnoid => 'var_samp(numeric)', aggtransfn => 'numeric_accum',
   aggfinalfn => 'numeric_var_samp', aggcombinefn => 'numeric_combine',
   aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
   aggmtransfn => 'numeric_accum', aggminvtransfn => 'numeric_accum_inv',
   aggmfinalfn => 'numeric_var_samp', aggtranstype => 'internal',
   aggtransspace => '128', aggmtranstype => 'internal',
-  aggmtransspace => '128' },
+  aggmtransspace => '128',
+  aggpartialfn => 'var_samp_p_numeric' },
 
 # variance: historical Postgres syntax for var_samp
+{ aggfnoid => 'variance_p_int8', aggtransfn => 'int8_accum',
+  aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine',
+  aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
+  aggtranstype => 'internal', aggtransspace => '128',
+  aggpartialfn => 'variance_p_int8' },
 { aggfnoid => 'variance(int8)', aggtransfn => 'int8_accum',
   aggfinalfn => 'numeric_var_samp', aggcombinefn => 'numeric_combine',
   aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
   aggmtransfn => 'int8_accum', aggminvtransfn => 'int8_accum_inv',
   aggmfinalfn => 'numeric_var_samp', aggtranstype => 'internal',
   aggtransspace => '128', aggmtranstype => 'internal',
-  aggmtransspace => '128' },
+  aggmtransspace => '128',
+  aggpartialfn => 'variance_p_int8' },
+{ aggfnoid => 'variance_p_int4', aggtransfn => 'int4_accum',
+  aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine',
+  aggserialfn => 'numeric_poly_serialize',
+  aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal',
+  aggtransspace => '48',
+  aggpartialfn => 'variance_p_int4' },
 { aggfnoid => 'variance(int4)', aggtransfn => 'int4_accum',
   aggfinalfn => 'numeric_poly_var_samp', aggcombinefn => 'numeric_poly_combine',
   aggserialfn => 'numeric_poly_serialize',
   aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int4_accum',
   aggminvtransfn => 'int4_accum_inv', aggmfinalfn => 'numeric_poly_var_samp',
   aggtranstype => 'internal', aggtransspace => '48',
-  aggmtranstype => 'internal', aggmtransspace => '48' },
+  aggmtranstype => 'internal', aggmtransspace => '48',
+  aggpartialfn => 'variance_p_int4' },
+{ aggfnoid => 'variance_p_int2', aggtransfn => 'int2_accum',
+  aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine',
+  aggserialfn => 'numeric_poly_serialize',
+  aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal',
+  aggtransspace => '48',
+  aggpartialfn => 'variance_p_int2' },
 { aggfnoid => 'variance(int2)', aggtransfn => 'int2_accum',
   aggfinalfn => 'numeric_poly_var_samp', aggcombinefn => 'numeric_poly_combine',
   aggserialfn => 'numeric_poly_serialize',
   aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int2_accum',
   aggminvtransfn => 'int2_accum_inv', aggmfinalfn => 'numeric_poly_var_samp',
   aggtranstype => 'internal', aggtransspace => '48',
-  aggmtranstype => 'internal', aggmtransspace => '48' },
+  aggmtranstype => 'internal', aggmtransspace => '48',
+  aggpartialfn => 'variance_p_int2' },
+{ aggfnoid => 'variance_p_float4', aggtransfn => 'float4_accum',
+  aggcombinefn => 'float8_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0}',
+  aggpartialfn => 'variance_p_float4' },
 { aggfnoid => 'variance(float4)', aggtransfn => 'float4_accum',
   aggfinalfn => 'float8_var_samp', aggcombinefn => 'float8_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0}',
+  aggpartialfn => 'variance_p_float4' },
+{ aggfnoid => 'variance_p_float8', aggtransfn => 'float8_accum',
+  aggcombinefn => 'float8_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0}',
+  aggpartialfn => 'variance_p_float8' },
 { aggfnoid => 'variance(float8)', aggtransfn => 'float8_accum',
   aggfinalfn => 'float8_var_samp', aggcombinefn => 'float8_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0}',
+  aggpartialfn => 'variance_p_float8' },
+{ aggfnoid => 'variance_p_numeric', aggtransfn => 'numeric_accum',
+  aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine',
+  aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
+  aggtranstype => 'internal', aggtransspace => '128',
+  aggpartialfn => 'variance_p_numeric' },
 { aggfnoid => 'variance(numeric)', aggtransfn => 'numeric_accum',
   aggfinalfn => 'numeric_var_samp', aggcombinefn => 'numeric_combine',
   aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
   aggmtransfn => 'numeric_accum', aggminvtransfn => 'numeric_accum_inv',
   aggmfinalfn => 'numeric_var_samp', aggtranstype => 'internal',
   aggtransspace => '128', aggmtranstype => 'internal',
-  aggmtransspace => '128' },
+  aggmtransspace => '128',
+  aggpartialfn => 'variance_p_numeric' },
 
 # stddev_pop
+{ aggfnoid => 'stddev_pop_p_int8', aggtransfn => 'int8_accum',
+  aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine',
+  aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
+  aggtranstype => 'internal', aggtransspace => '128',
+  aggpartialfn => 'stddev_pop_p_int8' },
 { aggfnoid => 'stddev_pop(int8)', aggtransfn => 'int8_accum',
   aggfinalfn => 'numeric_stddev_pop', aggcombinefn => 'numeric_combine',
   aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
   aggmtransfn => 'int8_accum', aggminvtransfn => 'int8_accum_inv',
   aggmfinalfn => 'numeric_stddev_pop', aggtranstype => 'internal',
   aggtransspace => '128', aggmtranstype => 'internal',
-  aggmtransspace => '128' },
+  aggmtransspace => '128',
+  aggpartialfn => 'stddev_pop_p_int8' },
+{ aggfnoid => 'stddev_pop_p_int4', aggtransfn => 'int4_accum',
+  aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine',
+  aggserialfn => 'numeric_poly_serialize',
+  aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal',
+  aggtransspace => '48',
+  aggpartialfn => 'stddev_pop_p_int4' },
 { aggfnoid => 'stddev_pop(int4)', aggtransfn => 'int4_accum',
   aggfinalfn => 'numeric_poly_stddev_pop',
   aggcombinefn => 'numeric_poly_combine',
@@ -353,7 +576,14 @@
   aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int4_accum',
   aggminvtransfn => 'int4_accum_inv', aggmfinalfn => 'numeric_poly_stddev_pop',
   aggtranstype => 'internal', aggtransspace => '48',
-  aggmtranstype => 'internal', aggmtransspace => '48' },
+  aggmtranstype => 'internal', aggmtransspace => '48',
+  aggpartialfn => 'stddev_pop_p_int4' },
+{ aggfnoid => 'stddev_pop_p_int2', aggtransfn => 'int2_accum',
+  aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine',
+  aggserialfn => 'numeric_poly_serialize',
+  aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal',
+  aggtransspace => '48',
+  aggpartialfn => 'stddev_pop_p_int2' },
 { aggfnoid => 'stddev_pop(int2)', aggtransfn => 'int2_accum',
   aggfinalfn => 'numeric_poly_stddev_pop',
   aggcombinefn => 'numeric_poly_combine',
@@ -361,29 +591,58 @@
   aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int2_accum',
   aggminvtransfn => 'int2_accum_inv', aggmfinalfn => 'numeric_poly_stddev_pop',
   aggtranstype => 'internal', aggtransspace => '48',
-  aggmtranstype => 'internal', aggmtransspace => '48' },
+  aggmtranstype => 'internal', aggmtransspace => '48',
+  aggpartialfn => 'stddev_pop_p_int2' },
+{ aggfnoid => 'stddev_pop_p_float4', aggtransfn => 'float4_accum',
+  aggcombinefn => 'float8_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0}',
+  aggpartialfn => 'stddev_pop_p_float4' },
 { aggfnoid => 'stddev_pop(float4)', aggtransfn => 'float4_accum',
   aggfinalfn => 'float8_stddev_pop', aggcombinefn => 'float8_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0}',
+  aggpartialfn => 'stddev_pop_p_float4' },
+{ aggfnoid => 'stddev_pop_p_float8', aggtransfn => 'float8_accum',
+  aggcombinefn => 'float8_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0}',
+  aggpartialfn => 'stddev_pop_p_float8' },
 { aggfnoid => 'stddev_pop(float8)', aggtransfn => 'float8_accum',
   aggfinalfn => 'float8_stddev_pop', aggcombinefn => 'float8_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0}',
+  aggpartialfn => 'stddev_pop_p_float8' },
+{ aggfnoid => 'stddev_pop_p_numeric', aggtransfn => 'numeric_accum',
+  aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine',
+  aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
+  aggtranstype => 'internal', aggtransspace => '128',
+  aggpartialfn => 'stddev_pop_p_numeric' },
 { aggfnoid => 'stddev_pop(numeric)', aggtransfn => 'numeric_accum',
   aggfinalfn => 'numeric_stddev_pop', aggcombinefn => 'numeric_combine',
   aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
   aggmtransfn => 'numeric_accum', aggminvtransfn => 'numeric_accum_inv',
   aggmfinalfn => 'numeric_stddev_pop', aggtranstype => 'internal',
   aggtransspace => '128', aggmtranstype => 'internal',
-  aggmtransspace => '128' },
+  aggmtransspace => '128',
+  aggpartialfn => 'stddev_pop_p_numeric' },
 
 # stddev_samp
+{ aggfnoid => 'stddev_samp_p_int8', aggtransfn => 'int8_accum',
+  aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine',
+  aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
+  aggtranstype => 'internal', aggtransspace => '128',
+  aggpartialfn => 'stddev_samp_p_int8' },
 { aggfnoid => 'stddev_samp(int8)', aggtransfn => 'int8_accum',
   aggfinalfn => 'numeric_stddev_samp', aggcombinefn => 'numeric_combine',
   aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
   aggmtransfn => 'int8_accum', aggminvtransfn => 'int8_accum_inv',
   aggmfinalfn => 'numeric_stddev_samp', aggtranstype => 'internal',
   aggtransspace => '128', aggmtranstype => 'internal',
-  aggmtransspace => '128' },
+  aggmtransspace => '128',
+  aggpartialfn => 'stddev_samp_p_int8' },
+{ aggfnoid => 'stddev_samp_p_int4', aggtransfn => 'int4_accum',
+  aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine',
+  aggserialfn => 'numeric_poly_serialize',
+  aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal',
+  aggtransspace => '48',
+  aggpartialfn => 'stddev_samp_p_int4' },
 { aggfnoid => 'stddev_samp(int4)', aggtransfn => 'int4_accum',
   aggfinalfn => 'numeric_poly_stddev_samp',
   aggcombinefn => 'numeric_poly_combine',
@@ -391,7 +650,14 @@
   aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int4_accum',
   aggminvtransfn => 'int4_accum_inv', aggmfinalfn => 'numeric_poly_stddev_samp',
   aggtranstype => 'internal', aggtransspace => '48',
-  aggmtranstype => 'internal', aggmtransspace => '48' },
+  aggmtranstype => 'internal', aggmtransspace => '48',
+  aggpartialfn => 'stddev_samp_p_int4' },
+{ aggfnoid => 'stddev_samp_p_int2', aggtransfn => 'int2_accum',
+  aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine',
+  aggserialfn => 'numeric_poly_serialize',
+  aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal',
+  aggtransspace => '48',
+  aggpartialfn => 'stddev_samp_p_int2' },
 { aggfnoid => 'stddev_samp(int2)', aggtransfn => 'int2_accum',
   aggfinalfn => 'numeric_poly_stddev_samp',
   aggcombinefn => 'numeric_poly_combine',
@@ -399,29 +665,58 @@
   aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int2_accum',
   aggminvtransfn => 'int2_accum_inv', aggmfinalfn => 'numeric_poly_stddev_samp',
   aggtranstype => 'internal', aggtransspace => '48',
-  aggmtranstype => 'internal', aggmtransspace => '48' },
+  aggmtranstype => 'internal', aggmtransspace => '48',
+  aggpartialfn => 'stddev_samp_p_int2' },
+{ aggfnoid => 'stddev_samp_p_float4', aggtransfn => 'float4_accum',
+  aggcombinefn => 'float8_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0}',
+  aggpartialfn => 'stddev_samp_p_float4' },
 { aggfnoid => 'stddev_samp(float4)', aggtransfn => 'float4_accum',
   aggfinalfn => 'float8_stddev_samp', aggcombinefn => 'float8_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0}',
+  aggpartialfn => 'stddev_samp_p_float4' },
+{ aggfnoid => 'stddev_samp_p_float8', aggtransfn => 'float8_accum',
+  aggcombinefn => 'float8_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0}',
+  aggpartialfn => 'stddev_samp_p_float8' },
 { aggfnoid => 'stddev_samp(float8)', aggtransfn => 'float8_accum',
   aggfinalfn => 'float8_stddev_samp', aggcombinefn => 'float8_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0}',
+  aggpartialfn => 'stddev_samp_p_float8' },
+{ aggfnoid => 'stddev_samp_p_numeric', aggtransfn => 'numeric_accum',
+  aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine',
+  aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
+  aggtranstype => 'internal', aggtransspace => '128',
+  aggpartialfn => 'stddev_samp_p_numeric' },
 { aggfnoid => 'stddev_samp(numeric)', aggtransfn => 'numeric_accum',
   aggfinalfn => 'numeric_stddev_samp', aggcombinefn => 'numeric_combine',
   aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
   aggmtransfn => 'numeric_accum', aggminvtransfn => 'numeric_accum_inv',
   aggmfinalfn => 'numeric_stddev_samp', aggtranstype => 'internal',
   aggtransspace => '128', aggmtranstype => 'internal',
-  aggmtransspace => '128' },
+  aggmtransspace => '128',
+  aggpartialfn => 'stddev_samp_p_numeric' },
 
 # stddev: historical Postgres syntax for stddev_samp
+{ aggfnoid => 'stddev_p_int8', aggtransfn => 'int8_accum',
+  aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine',
+  aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
+  aggtranstype => 'internal', aggtransspace => '128',
+  aggpartialfn => 'stddev_p_int8' },
 { aggfnoid => 'stddev(int8)', aggtransfn => 'int8_accum',
   aggfinalfn => 'numeric_stddev_samp', aggcombinefn => 'numeric_combine',
   aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
   aggmtransfn => 'int8_accum', aggminvtransfn => 'int8_accum_inv',
   aggmfinalfn => 'numeric_stddev_samp', aggtranstype => 'internal',
   aggtransspace => '128', aggmtranstype => 'internal',
-  aggmtransspace => '128' },
+  aggmtransspace => '128',
+  aggpartialfn => 'stddev_p_int8' },
+{ aggfnoid => 'stddev_p_int4', aggtransfn => 'int4_accum',
+  aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine',
+  aggserialfn => 'numeric_poly_serialize',
+  aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal',
+  aggtransspace => '48',
+  aggpartialfn => 'stddev_p_int4' },
 { aggfnoid => 'stddev(int4)', aggtransfn => 'int4_accum',
   aggfinalfn => 'numeric_poly_stddev_samp',
   aggcombinefn => 'numeric_poly_combine',
@@ -429,7 +724,14 @@
   aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int4_accum',
   aggminvtransfn => 'int4_accum_inv', aggmfinalfn => 'numeric_poly_stddev_samp',
   aggtranstype => 'internal', aggtransspace => '48',
-  aggmtranstype => 'internal', aggmtransspace => '48' },
+  aggmtranstype => 'internal', aggmtransspace => '48',
+  aggpartialfn => 'stddev_p_int4' },
+{ aggfnoid => 'stddev_p_int2', aggtransfn => 'int2_accum',
+  aggfinalfn => 'numeric_poly_serialize', aggcombinefn => 'numeric_poly_combine',
+  aggserialfn => 'numeric_poly_serialize',
+  aggdeserialfn => 'numeric_poly_deserialize', aggtranstype => 'internal',
+  aggtransspace => '48',
+  aggpartialfn => 'stddev_p_int2' },
 { aggfnoid => 'stddev(int2)', aggtransfn => 'int2_accum',
   aggfinalfn => 'numeric_poly_stddev_samp',
   aggcombinefn => 'numeric_poly_combine',
@@ -437,138 +739,253 @@
   aggdeserialfn => 'numeric_poly_deserialize', aggmtransfn => 'int2_accum',
   aggminvtransfn => 'int2_accum_inv', aggmfinalfn => 'numeric_poly_stddev_samp',
   aggtranstype => 'internal', aggtransspace => '48',
-  aggmtranstype => 'internal', aggmtransspace => '48' },
+  aggmtranstype => 'internal', aggmtransspace => '48',
+  aggpartialfn => 'stddev_p_int2' },
+{ aggfnoid => 'stddev_p_float4', aggtransfn => 'float4_accum',
+  aggcombinefn => 'float8_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0}',
+  aggpartialfn => 'stddev_p_float4' },
 { aggfnoid => 'stddev(float4)', aggtransfn => 'float4_accum',
   aggfinalfn => 'float8_stddev_samp', aggcombinefn => 'float8_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0}',
+  aggpartialfn => 'stddev_p_float4' },
+{ aggfnoid => 'stddev_p_float8', aggtransfn => 'float8_accum',
+  aggcombinefn => 'float8_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0}',
+  aggpartialfn => 'stddev_p_float8' },
 { aggfnoid => 'stddev(float8)', aggtransfn => 'float8_accum',
   aggfinalfn => 'float8_stddev_samp', aggcombinefn => 'float8_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0}',
+  aggpartialfn => 'stddev_p_float8' },
+{ aggfnoid => 'stddev_p_numeric', aggtransfn => 'numeric_accum',
+  aggfinalfn => 'numeric_serialize', aggcombinefn => 'numeric_combine',
+  aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
+  aggtranstype => 'internal', aggtransspace => '128',
+  aggpartialfn => 'stddev_p_numeric' },
 { aggfnoid => 'stddev(numeric)', aggtransfn => 'numeric_accum',
   aggfinalfn => 'numeric_stddev_samp', aggcombinefn => 'numeric_combine',
   aggserialfn => 'numeric_serialize', aggdeserialfn => 'numeric_deserialize',
   aggmtransfn => 'numeric_accum', aggminvtransfn => 'numeric_accum_inv',
   aggmfinalfn => 'numeric_stddev_samp', aggtranstype => 'internal',
   aggtransspace => '128', aggmtranstype => 'internal',
-  aggmtransspace => '128' },
+  aggmtransspace => '128',
+  aggpartialfn => 'stddev_p_numeric' },
 
 # SQL2003 binary regression aggregates
 { aggfnoid => 'regr_count', aggtransfn => 'int8inc_float8_float8',
-  aggcombinefn => 'int8pl', aggtranstype => 'int8', agginitval => '0' },
+  aggcombinefn => 'int8pl', aggtranstype => 'int8', agginitval => '0',
+  aggpartialfn => 'regr_count' },
+{ aggfnoid => 'regr_sxx_p', aggtransfn => 'float8_regr_accum',
+  aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'regr_sxx_p' },
 { aggfnoid => 'regr_sxx', aggtransfn => 'float8_regr_accum',
   aggfinalfn => 'float8_regr_sxx', aggcombinefn => 'float8_regr_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'regr_sxx_p' },
+{ aggfnoid => 'regr_syy_p', aggtransfn => 'float8_regr_accum',
+  aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'regr_syy_p' },
 { aggfnoid => 'regr_syy', aggtransfn => 'float8_regr_accum',
   aggfinalfn => 'float8_regr_syy', aggcombinefn => 'float8_regr_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'regr_syy_p' },
+{ aggfnoid => 'regr_sxy_p', aggtransfn => 'float8_regr_accum',
+  aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'regr_sxy_p' },
 { aggfnoid => 'regr_sxy', aggtransfn => 'float8_regr_accum',
   aggfinalfn => 'float8_regr_sxy', aggcombinefn => 'float8_regr_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'regr_sxy_p' },
+{ aggfnoid => 'regr_avgx_p', aggtransfn => 'float8_regr_accum',
+  aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'regr_avgx_p' },
 { aggfnoid => 'regr_avgx', aggtransfn => 'float8_regr_accum',
   aggfinalfn => 'float8_regr_avgx', aggcombinefn => 'float8_regr_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'regr_avgx_p' },
+{ aggfnoid => 'regr_avgy_p', aggtransfn => 'float8_regr_accum',
+  aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'regr_avgy_p' },
 { aggfnoid => 'regr_avgy', aggtransfn => 'float8_regr_accum',
   aggfinalfn => 'float8_regr_avgy', aggcombinefn => 'float8_regr_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'regr_avgy_p' },
+{ aggfnoid => 'regr_r2_p', aggtransfn => 'float8_regr_accum',
+  aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'regr_r2_p' },
 { aggfnoid => 'regr_r2', aggtransfn => 'float8_regr_accum',
   aggfinalfn => 'float8_regr_r2', aggcombinefn => 'float8_regr_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'regr_r2_p' },
+{ aggfnoid => 'regr_slope_p', aggtransfn => 'float8_regr_accum',
+  aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'regr_slope_p' },
 { aggfnoid => 'regr_slope', aggtransfn => 'float8_regr_accum',
   aggfinalfn => 'float8_regr_slope', aggcombinefn => 'float8_regr_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'regr_slope_p' },
+{ aggfnoid => 'regr_intercept_p', aggtransfn => 'float8_regr_accum',
+  aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'regr_intercept_p' },
 { aggfnoid => 'regr_intercept', aggtransfn => 'float8_regr_accum',
   aggfinalfn => 'float8_regr_intercept', aggcombinefn => 'float8_regr_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'regr_intercept_p' },
+{ aggfnoid => 'covar_pop_p', aggtransfn => 'float8_regr_accum',
+  aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'covar_pop_p' },
 { aggfnoid => 'covar_pop', aggtransfn => 'float8_regr_accum',
   aggfinalfn => 'float8_covar_pop', aggcombinefn => 'float8_regr_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'covar_pop_p' },
+{ aggfnoid => 'covar_samp_p', aggtransfn => 'float8_regr_accum',
+  aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'covar_samp_p' },
 { aggfnoid => 'covar_samp', aggtransfn => 'float8_regr_accum',
   aggfinalfn => 'float8_covar_samp', aggcombinefn => 'float8_regr_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'covar_samp_p' },
+{ aggfnoid => 'corr_p', aggtransfn => 'float8_regr_accum',
+  aggcombinefn => 'float8_regr_combine', aggtranstype => '_float8',
+  agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'corr_p' },
 { aggfnoid => 'corr', aggtransfn => 'float8_regr_accum',
   aggfinalfn => 'float8_corr', aggcombinefn => 'float8_regr_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0,0,0,0}',
+  aggpartialfn => 'corr_p' },
 
 # boolean-and and boolean-or
 { aggfnoid => 'bool_and', aggtransfn => 'booland_statefunc',
   aggcombinefn => 'booland_statefunc', aggmtransfn => 'bool_accum',
   aggminvtransfn => 'bool_accum_inv', aggmfinalfn => 'bool_alltrue',
   aggsortop => '<(bool,bool)', aggtranstype => 'bool',
-  aggmtranstype => 'internal', aggmtransspace => '16' },
+  aggmtranstype => 'internal', aggmtransspace => '16',
+  aggpartialfn => 'bool_and' },
 { aggfnoid => 'bool_or', aggtransfn => 'boolor_statefunc',
   aggcombinefn => 'boolor_statefunc', aggmtransfn => 'bool_accum',
   aggminvtransfn => 'bool_accum_inv', aggmfinalfn => 'bool_anytrue',
   aggsortop => '>(bool,bool)', aggtranstype => 'bool',
-  aggmtranstype => 'internal', aggmtransspace => '16' },
+  aggmtranstype => 'internal', aggmtransspace => '16',
+  aggpartialfn => 'bool_or' },
 { aggfnoid => 'every', aggtransfn => 'booland_statefunc',
   aggcombinefn => 'booland_statefunc', aggmtransfn => 'bool_accum',
   aggminvtransfn => 'bool_accum_inv', aggmfinalfn => 'bool_alltrue',
   aggsortop => '<(bool,bool)', aggtranstype => 'bool',
-  aggmtranstype => 'internal', aggmtransspace => '16' },
+  aggmtranstype => 'internal', aggmtransspace => '16',
+  aggpartialfn => 'every' },
 
 # bitwise integer
 { aggfnoid => 'bit_and(int2)', aggtransfn => 'int2and',
-  aggcombinefn => 'int2and', aggtranstype => 'int2' },
+  aggcombinefn => 'int2and', aggtranstype => 'int2',
+  aggpartialfn => 'bit_and(int2)' },
 { aggfnoid => 'bit_or(int2)', aggtransfn => 'int2or', aggcombinefn => 'int2or',
-  aggtranstype => 'int2' },
+  aggtranstype => 'int2',
+  aggpartialfn => 'bit_or(int2)' },
 { aggfnoid => 'bit_xor(int2)', aggtransfn => 'int2xor',
-  aggcombinefn => 'int2xor', aggtranstype => 'int2' },
+  aggcombinefn => 'int2xor', aggtranstype => 'int2',
+  aggpartialfn => 'bit_xor(int2)' },
 { aggfnoid => 'bit_and(int4)', aggtransfn => 'int4and',
-  aggcombinefn => 'int4and', aggtranstype => 'int4' },
+  aggcombinefn => 'int4and', aggtranstype => 'int4',
+  aggpartialfn => 'bit_and(int4)' },
 { aggfnoid => 'bit_or(int4)', aggtransfn => 'int4or', aggcombinefn => 'int4or',
-  aggtranstype => 'int4' },
+  aggtranstype => 'int4',
+  aggpartialfn => 'bit_or(int4)' },
 { aggfnoid => 'bit_xor(int4)', aggtransfn => 'int4xor',
-  aggcombinefn => 'int4xor', aggtranstype => 'int4' },
+  aggcombinefn => 'int4xor', aggtranstype => 'int4',
+  aggpartialfn => 'bit_xor(int4)' },
 { aggfnoid => 'bit_and(int8)', aggtransfn => 'int8and',
-  aggcombinefn => 'int8and', aggtranstype => 'int8' },
+  aggcombinefn => 'int8and', aggtranstype => 'int8',
+  aggpartialfn => 'bit_and(int8)' },
 { aggfnoid => 'bit_or(int8)', aggtransfn => 'int8or', aggcombinefn => 'int8or',
-  aggtranstype => 'int8' },
+  aggtranstype => 'int8',
+  aggpartialfn => 'bit_or(int8)' },
 { aggfnoid => 'bit_xor(int8)', aggtransfn => 'int8xor',
-  aggcombinefn => 'int8xor', aggtranstype => 'int8' },
+  aggcombinefn => 'int8xor', aggtranstype => 'int8',
+  aggpartialfn => 'bit_xor(int8)' },
 { aggfnoid => 'bit_and(bit)', aggtransfn => 'bitand', aggcombinefn => 'bitand',
-  aggtranstype => 'bit' },
+  aggtranstype => 'bit',
+  aggpartialfn => 'bit_and(bit)' },
 { aggfnoid => 'bit_or(bit)', aggtransfn => 'bitor', aggcombinefn => 'bitor',
-  aggtranstype => 'bit' },
+  aggtranstype => 'bit',
+  aggpartialfn => 'bit_or(bit)' },
 { aggfnoid => 'bit_xor(bit)', aggtransfn => 'bitxor', aggcombinefn => 'bitxor',
-  aggtranstype => 'bit' },
+  aggtranstype => 'bit',
+  aggpartialfn => 'bit_xor(bit)' },
 
 # xml
 { aggfnoid => 'xmlagg', aggtransfn => 'xmlconcat2', aggtranstype => 'xml' },
 
 # array
+{ aggfnoid => 'array_agg_p_anynonarray', aggtransfn => 'array_agg_transfn',
+  aggfinalfn => 'array_agg_serialize', aggcombinefn => 'array_agg_combine',
+  aggserialfn => 'array_agg_serialize', aggdeserialfn => 'array_agg_deserialize',
+  aggtranstype => 'internal',
+  aggpartialfn => 'array_agg_p_anynonarray' },
 { aggfnoid => 'array_agg(anynonarray)', aggtransfn => 'array_agg_transfn',
   aggfinalfn => 'array_agg_finalfn', aggcombinefn => 'array_agg_combine',
   aggserialfn => 'array_agg_serialize',
   aggdeserialfn => 'array_agg_deserialize', aggfinalextra => 't',
-  aggtranstype => 'internal' },
+  aggtranstype => 'internal',
+  aggpartialfn => 'array_agg_p_anynonarray' },
+{ aggfnoid => 'array_agg_p_anyarray', aggtransfn => 'array_agg_array_transfn',
+  aggfinalfn => 'array_agg_array_serialize',
+  aggcombinefn => 'array_agg_array_combine',
+  aggserialfn => 'array_agg_array_serialize',
+  aggdeserialfn => 'array_agg_array_deserialize', aggtranstype => 'internal',
+  aggpartialfn => 'array_agg_p_anyarray' },
 { aggfnoid => 'array_agg(anyarray)', aggtransfn => 'array_agg_array_transfn',
   aggfinalfn => 'array_agg_array_finalfn',
   aggcombinefn => 'array_agg_array_combine',
   aggserialfn => 'array_agg_array_serialize',
   aggdeserialfn => 'array_agg_array_deserialize', aggfinalextra => 't',
-  aggtranstype => 'internal' },
+  aggtranstype => 'internal',
+  aggpartialfn => 'array_agg_p_anyarray' },
 
 # text
+{ aggfnoid => 'string_agg_p_text_text', aggtransfn => 'string_agg_transfn',
+  aggfinalfn => 'string_agg_serialize', aggcombinefn => 'string_agg_combine',
+  aggserialfn => 'string_agg_serialize',
+  aggdeserialfn => 'string_agg_deserialize', aggtranstype => 'internal',
+  aggpartialfn => 'string_agg_p_text_text' },
 { aggfnoid => 'string_agg(text,text)', aggtransfn => 'string_agg_transfn',
   aggfinalfn => 'string_agg_finalfn', aggcombinefn => 'string_agg_combine',
   aggserialfn => 'string_agg_serialize',
-  aggdeserialfn => 'string_agg_deserialize', aggtranstype => 'internal' },
+  aggdeserialfn => 'string_agg_deserialize', aggtranstype => 'internal',
+  aggpartialfn => 'string_agg_p_text_text' },
 
 # bytea
+{ aggfnoid => 'string_agg_p_bytea_bytea',
+  aggtransfn => 'bytea_string_agg_transfn', aggfinalfn => 'string_agg_serialize',
+  aggcombinefn => 'string_agg_combine', aggserialfn => 'string_agg_serialize',
+  aggdeserialfn => 'string_agg_deserialize', aggtranstype => 'internal',
+  aggpartialfn => 'string_agg_p_bytea_bytea' },
 { aggfnoid => 'string_agg(bytea,bytea)',
   aggtransfn => 'bytea_string_agg_transfn',
   aggfinalfn => 'bytea_string_agg_finalfn',
   aggcombinefn => 'string_agg_combine', aggserialfn => 'string_agg_serialize',
-  aggdeserialfn => 'string_agg_deserialize', aggtranstype => 'internal' },
+  aggdeserialfn => 'string_agg_deserialize', aggtranstype => 'internal',
+  aggpartialfn => 'string_agg_p_bytea_bytea' },
 
 # range
 { aggfnoid => 'range_intersect_agg(anyrange)',
   aggtransfn => 'range_intersect_agg_transfn',
-  aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+  aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange',
+  aggpartialfn => 'range_intersect_agg(anyrange)' },
 { aggfnoid => 'range_intersect_agg(anymultirange)',
   aggtransfn => 'multirange_intersect_agg_transfn',
   aggcombinefn => 'multirange_intersect_agg_transfn',
-  aggtranstype => 'anymultirange' },
+  aggtranstype => 'anymultirange',
+  aggpartialfn => 'range_intersect_agg(anymultirange)' },
 { aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
   aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
   aggtranstype => 'internal' },
@@ -658,6 +1075,7 @@
 
 # any_value
 { aggfnoid => 'any_value(anyelement)', aggtransfn => 'any_value_transfn',
-  aggcombinefn => 'any_value_transfn', aggtranstype => 'anyelement' },
+  aggcombinefn => 'any_value_transfn', aggtranstype => 'anyelement',
+  aggpartialfn => 'any_value(anyelement)' },
 
 ]
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 3112881193..b8fb0372b3 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -55,6 +55,9 @@ CATALOG(pg_aggregate,2600,AggregateRelationId)
 	/* function to convert bytea to transtype (0 if none) */
 	regproc		aggdeserialfn BKI_DEFAULT(-) BKI_LOOKUP_OPT(pg_proc);
 
+	/* special aggregate function for partial aggregation (0 if none) */
+	regproc		aggpartialfn BKI_DEFAULT(-) BKI_LOOKUP_OPT(pg_proc);
+
 	/* forward function for moving-aggregate mode (0 if none) */
 	regproc		aggmtransfn BKI_DEFAULT(-) BKI_LOOKUP_OPT(pg_proc);
 
@@ -164,6 +167,7 @@ extern ObjectAddress AggregateCreate(const char *aggName,
 									 List *aggmtransfnName,
 									 List *aggminvtransfnName,
 									 List *aggmfinalfnName,
+									 List *aggpartialfnName,
 									 bool finalfnExtraArgs,
 									 bool mfinalfnExtraArgs,
 									 char finalfnModify,
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 6996073989..1c4087ad8c 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -1658,6 +1658,10 @@
 { oid => '2334', descr => 'aggregate final function',
   proname => 'array_agg_finalfn', proisstrict => 'f', prorettype => 'anyarray',
   proargtypes => 'internal anynonarray', prosrc => 'array_agg_finalfn' },
+{ oid => '6', descr => 'partial aggregation for array_agg(anynonarray)',
+  proname => 'array_agg_p_anynonarray', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'anynonarray',
+  prosrc => 'aggregate_dummy' },
 { oid => '2335', descr => 'concatenate aggregate input into an array',
   proname => 'array_agg', prokind => 'a', proisstrict => 'f',
   prorettype => 'anyarray', proargtypes => 'anynonarray',
@@ -1680,6 +1684,9 @@
   proname => 'array_agg_array_finalfn', proisstrict => 'f',
   prorettype => 'anyarray', proargtypes => 'internal anyarray',
   prosrc => 'array_agg_array_finalfn' },
+{ oid => '7', descr => 'partial aggregation for array_agg(anyarray)',
+  proname => 'array_agg_p_anyarray', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'anyarray', prosrc => 'aggregate_dummy' },
 { oid => '4053', descr => 'concatenate aggregate input into an array',
   proname => 'array_agg', prokind => 'a', proisstrict => 'f',
   prorettype => 'anyarray', proargtypes => 'anyarray',
@@ -4989,6 +4996,10 @@
 { oid => '3536', descr => 'aggregate final function',
   proname => 'string_agg_finalfn', proisstrict => 'f', prorettype => 'text',
   proargtypes => 'internal', prosrc => 'string_agg_finalfn' },
+{ oid => '8', descr => 'partial aggregation for string_agg(text, text)',
+  proname => 'string_agg_p_text_text', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'text text',
+  prosrc => 'aggregate_dummy' },
 { oid => '3538', descr => 'concatenate aggregate input into a string',
   proname => 'string_agg', prokind => 'a', proisstrict => 'f',
   prorettype => 'text', proargtypes => 'text text',
@@ -5001,6 +5012,10 @@
   proname => 'bytea_string_agg_finalfn', proisstrict => 'f',
   prorettype => 'bytea', proargtypes => 'internal',
   prosrc => 'bytea_string_agg_finalfn' },
+{ oid => '9', descr => 'partial aggregation for string_agg(bytea, bytea)',
+  proname => 'string_agg_p_bytea_bytea', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'bytea bytea',
+  prosrc => 'aggregate_dummy' },
 { oid => '3545', descr => 'concatenate aggregate input into a bytea',
   proname => 'string_agg', prokind => 'a', proisstrict => 'f',
   prorettype => 'bytea', proargtypes => 'bytea bytea',
@@ -6579,36 +6594,61 @@
 
 # Aggregates (moved here from pg_aggregate for 7.3)
 
+{ oid => '111', descr => 'partial aggregation for avg(int8)',
+  proname => 'avg_p_int8', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2100',
   descr => 'the average (arithmetic mean) as numeric of all bigint values',
   proname => 'avg', prokind => 'a', proisstrict => 'f', prorettype => 'numeric',
   proargtypes => 'int8', prosrc => 'aggregate_dummy' },
+{ oid => '226', descr => 'partial aggregation for avg(int4)',
+  proname => 'avg_p_int4', prokind => 'a', proisstrict => 'f',
+  prorettype => '_int8', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
 { oid => '2101',
   descr => 'the average (arithmetic mean) as numeric of all integer values',
   proname => 'avg', prokind => 'a', proisstrict => 'f', prorettype => 'numeric',
   proargtypes => 'int4', prosrc => 'aggregate_dummy' },
+{ oid => '388', descr => 'partial aggregation for avg(int2)',
+  proname => 'avg_p_int2', prokind => 'a', proisstrict => 'f',
+  prorettype => '_int8', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
 { oid => '2102',
   descr => 'the average (arithmetic mean) as numeric of all smallint values',
   proname => 'avg', prokind => 'a', proisstrict => 'f', prorettype => 'numeric',
   proargtypes => 'int2', prosrc => 'aggregate_dummy' },
+{ oid => '389', descr => 'partial aggregation for avg(numeric)',
+  proname => 'avg_p_numeric', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'numeric', prosrc => 'aggregate_dummy' },
 { oid => '2103',
   descr => 'the average (arithmetic mean) as numeric of all numeric values',
   proname => 'avg', prokind => 'a', proisstrict => 'f', prorettype => 'numeric',
   proargtypes => 'numeric', prosrc => 'aggregate_dummy' },
+{ oid => '560', descr => 'partial aggregation for avg(float4)',
+  proname => 'avg_p_float4', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' },
 { oid => '2104',
   descr => 'the average (arithmetic mean) as float8 of all float4 values',
   proname => 'avg', prokind => 'a', proisstrict => 'f', prorettype => 'float8',
   proargtypes => 'float4', prosrc => 'aggregate_dummy' },
+{ oid => '561', descr => 'partial aggregation for avg(float8)',
+  proname => 'avg_p_float8', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' },
 { oid => '2105',
   descr => 'the average (arithmetic mean) as float8 of all float8 values',
   proname => 'avg', prokind => 'a', proisstrict => 'f', prorettype => 'float8',
   proargtypes => 'float8', prosrc => 'aggregate_dummy' },
+{ oid => '562', descr => 'partial aggregation for avg(interval)',
+  proname => 'avg_p_interval', prokind => 'a', proisstrict => 'f',
+  prorettype => '_interval', proargtypes => 'interval',
+  prosrc => 'aggregate_dummy' },
 { oid => '2106',
   descr => 'the average (arithmetic mean) as interval of all interval values',
   proname => 'avg', prokind => 'a', proisstrict => 'f',
   prorettype => 'interval', proargtypes => 'interval',
   prosrc => 'aggregate_dummy' },
 
+{ oid => '563', descr => 'partial aggregation for sum(int8)',
+  proname => 'sum_p_int8', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2107', descr => 'sum as numeric across all bigint input values',
   proname => 'sum', prokind => 'a', proisstrict => 'f', prorettype => 'numeric',
   proargtypes => 'int8', prosrc => 'aggregate_dummy' },
@@ -6631,6 +6671,9 @@
   proname => 'sum', prokind => 'a', proisstrict => 'f',
   prorettype => 'interval', proargtypes => 'interval',
   prosrc => 'aggregate_dummy' },
+{ oid => '564', descr => 'partial aggregation for sum(numeric)',
+  proname => 'sum_p_numeric', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'numeric', prosrc => 'aggregate_dummy' },
 { oid => '2114', descr => 'sum as numeric across all numeric input values',
   proname => 'sum', prokind => 'a', proisstrict => 'f', prorettype => 'numeric',
   proargtypes => 'numeric', prosrc => 'aggregate_dummy' },
@@ -6789,153 +6832,261 @@
   proname => 'int8inc_support', prorettype => 'internal',
   proargtypes => 'internal', prosrc => 'int8inc_support' },
 
+{ oid => '565', descr => 'partial aggregation for var_pop(int8)',
+  proname => 'var_pop_p_int8', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2718',
   descr => 'population variance of bigint input values (square of the population standard deviation)',
   proname => 'var_pop', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
+{ oid => '566', descr => 'partial aggregation for var_pop(int4)',
+  proname => 'var_pop_p_int4', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
 { oid => '2719',
   descr => 'population variance of integer input values (square of the population standard deviation)',
   proname => 'var_pop', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
+{ oid => '567', descr => 'partial aggregation for var_pop(int2)',
+  proname => 'var_pop_p_int2', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
 { oid => '2720',
   descr => 'population variance of smallint input values (square of the population standard deviation)',
   proname => 'var_pop', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
+{ oid => '568', descr => 'partial aggregation for var_pop(float4)',
+  proname => 'var_pop_p_float4', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' },
 { oid => '2721',
   descr => 'population variance of float4 input values (square of the population standard deviation)',
   proname => 'var_pop', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float4',
   prosrc => 'aggregate_dummy' },
+{ oid => '569', descr => 'partial aggregation for var_pop(float8)',
+  proname => 'var_pop_p_float8', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' },
 { oid => '2722',
   descr => 'population variance of float8 input values (square of the population standard deviation)',
   proname => 'var_pop', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8',
   prosrc => 'aggregate_dummy' },
+{ oid => '570', descr => 'partial aggregation for var_pop(numeric)',
+  proname => 'var_pop_p_numeric', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'numeric', prosrc => 'aggregate_dummy' },
 { oid => '2723',
   descr => 'population variance of numeric input values (square of the population standard deviation)',
   proname => 'var_pop', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'numeric',
   prosrc => 'aggregate_dummy' },
 
+{ oid => '571', descr => 'partial aggregation for var_samp(int8)',
+  proname => 'var_samp_p_int8', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2641',
   descr => 'sample variance of bigint input values (square of the sample standard deviation)',
   proname => 'var_samp', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
+{ oid => '572', descr => 'partial aggregation for var_samp(int4)',
+  proname => 'var_samp_p_int4', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
 { oid => '2642',
   descr => 'sample variance of integer input values (square of the sample standard deviation)',
   proname => 'var_samp', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
+{ oid => '573', descr => 'partial aggregation for var_samp(int2)',
+  proname => 'var_samp_p_int2', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
 { oid => '2643',
   descr => 'sample variance of smallint input values (square of the sample standard deviation)',
   proname => 'var_samp', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
+{ oid => '574', descr => 'partial aggregation for var_samp(float4)',
+  proname => 'var_samp_p_float4', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' },
 { oid => '2644',
   descr => 'sample variance of float4 input values (square of the sample standard deviation)',
   proname => 'var_samp', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float4',
   prosrc => 'aggregate_dummy' },
 
+{ oid => '575', descr => 'partial aggregation for var_samp(float8)',
+  proname => 'var_samp_p_float8', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' },
 { oid => '2645',
   descr => 'sample variance of float8 input values (square of the sample standard deviation)',
   proname => 'var_samp', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8',
   prosrc => 'aggregate_dummy' },
+{ oid => '576', descr => 'partial aggregation for var_samp(numeric)',
+  proname => 'var_samp_p_numeric', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'numeric', prosrc => 'aggregate_dummy' },
 { oid => '2646',
   descr => 'sample variance of numeric input values (square of the sample standard deviation)',
   proname => 'var_samp', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'numeric',
   prosrc => 'aggregate_dummy' },
 
+{ oid => '577', descr => 'partial aggregation for variance(int8)',
+  proname => 'variance_p_int8', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2148', descr => 'historical alias for var_samp',
   proname => 'variance', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
+{ oid => '578', descr => 'partial aggregation for variance(int4)',
+  proname => 'variance_p_int4', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
 { oid => '2149', descr => 'historical alias for var_samp',
   proname => 'variance', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
+{ oid => '579', descr => 'partial aggregation for variance(int2)',
+  proname => 'variance_p_int2', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
 { oid => '2150', descr => 'historical alias for var_samp',
   proname => 'variance', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
+{ oid => '580', descr => 'partial aggregation for variance(float4)',
+  proname => 'variance_p_float4', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' },
 { oid => '2151', descr => 'historical alias for var_samp',
   proname => 'variance', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float4',
   prosrc => 'aggregate_dummy' },
+{ oid => '581', descr => 'partial aggregation for variance(float8)',
+  proname => 'variance_p_float8', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' },
 { oid => '2152', descr => 'historical alias for var_samp',
   proname => 'variance', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8',
   prosrc => 'aggregate_dummy' },
+{ oid => '582', descr => 'partial aggregation for variance(numeric)',
+  proname => 'variance_p_numeric', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'numeric', prosrc => 'aggregate_dummy' },
 { oid => '2153', descr => 'historical alias for var_samp',
   proname => 'variance', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'numeric',
   prosrc => 'aggregate_dummy' },
 
+{ oid => '583', descr => 'partial aggregation for stddev_pop(int8)',
+  proname => 'stddev_pop_p_int8', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2724',
   descr => 'population standard deviation of bigint input values',
   proname => 'stddev_pop', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
+{ oid => '703', descr => 'partial aggregation for stddev_pop(int4)',
+  proname => 'stddev_pop_p_int4', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
 { oid => '2725',
   descr => 'population standard deviation of integer input values',
   proname => 'stddev_pop', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
+{ oid => '726', descr => 'partial aggregation for stddev_pop(int2)',
+  proname => 'stddev_pop_p_int2', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
 { oid => '2726',
   descr => 'population standard deviation of smallint input values',
   proname => 'stddev_pop', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
+{ oid => '786', descr => 'partial aggregation for stddev_pop(float4)',
+  proname => 'stddev_pop_p_float4', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' },
 { oid => '2727',
   descr => 'population standard deviation of float4 input values',
   proname => 'stddev_pop', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float4',
   prosrc => 'aggregate_dummy' },
+{ oid => '787', descr => 'partial aggregation for stddev_pop(float8)',
+  proname => 'stddev_pop_p_float8', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' },
 { oid => '2728',
   descr => 'population standard deviation of float8 input values',
   proname => 'stddev_pop', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8',
   prosrc => 'aggregate_dummy' },
+{ oid => '788', descr => 'partial aggregation for stddev_pop(numeric)',
+  proname => 'stddev_pop_p_numeric', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'numeric', prosrc => 'aggregate_dummy' },
 { oid => '2729',
   descr => 'population standard deviation of numeric input values',
   proname => 'stddev_pop', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'numeric',
   prosrc => 'aggregate_dummy' },
 
+{ oid => '789', descr => 'partial aggregation for stddev_samp(int8)',
+  proname => 'stddev_samp_p_int8', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2712', descr => 'sample standard deviation of bigint input values',
   proname => 'stddev_samp', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
+{ oid => '811', descr => 'partial aggregation for stddev_samp(int4)',
+  proname => 'stddev_samp_p_int4', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
 { oid => '2713', descr => 'sample standard deviation of integer input values',
   proname => 'stddev_samp', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
+{ oid => '812', descr => 'partial aggregation for stddev_samp(int2)',
+  proname => 'stddev_samp_p_int2', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
 { oid => '2714',
   descr => 'sample standard deviation of smallint input values',
   proname => 'stddev_samp', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
+{ oid => '813', descr => 'partial aggregation for stddev_samp(float4)',
+  proname => 'stddev_samp_p_float4', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' },
 { oid => '2715', descr => 'sample standard deviation of float4 input values',
   proname => 'stddev_samp', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float4',
   prosrc => 'aggregate_dummy' },
+{ oid => '814', descr => 'partial aggregation for stddev_samp(float8)',
+  proname => 'stddev_samp_p_float8', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' },
 { oid => '2716', descr => 'sample standard deviation of float8 input values',
   proname => 'stddev_samp', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8',
   prosrc => 'aggregate_dummy' },
+{ oid => '815', descr => 'partial aggregation for stddev_samp(numeric)',
+  proname => 'stddev_samp_p_numeric', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'numeric', prosrc => 'aggregate_dummy' },
 { oid => '2717', descr => 'sample standard deviation of numeric input values',
   proname => 'stddev_samp', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'numeric',
   prosrc => 'aggregate_dummy' },
 
+{ oid => '816', descr => 'partial aggregation for stddev(int8)',
+  proname => 'stddev_p_int8', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2154', descr => 'historical alias for stddev_samp',
   proname => 'stddev', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int8', prosrc => 'aggregate_dummy' },
+{ oid => '970', descr => 'partial aggregation for stddev(int4)',
+  proname => 'stddev_p_int4', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
 { oid => '2155', descr => 'historical alias for stddev_samp',
   proname => 'stddev', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int4', prosrc => 'aggregate_dummy' },
+{ oid => '1382', descr => 'partial aggregation for stddev(int2)',
+  proname => 'stddev_p_int2', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
 { oid => '2156', descr => 'historical alias for stddev_samp',
   proname => 'stddev', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'int2', prosrc => 'aggregate_dummy' },
+{ oid => '1524', descr => 'partial aggregation for stddev(float4)',
+  proname => 'stddev_p_float4', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float4', prosrc => 'aggregate_dummy' },
 { oid => '2157', descr => 'historical alias for stddev_samp',
   proname => 'stddev', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float4',
   prosrc => 'aggregate_dummy' },
+{ oid => '1533', descr => 'partial aggregation for stddev(float8)',
+  proname => 'stddev_p_float8', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float8', prosrc => 'aggregate_dummy' },
 { oid => '2158', descr => 'historical alias for stddev_samp',
   proname => 'stddev', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8',
   prosrc => 'aggregate_dummy' },
+{ oid => '1566', descr => 'partial aggregation for stddev(numeric)',
+  proname => 'stddev_p_numeric', prokind => 'a', proisstrict => 'f',
+  prorettype => 'bytea', proargtypes => 'numeric', prosrc => 'aggregate_dummy' },
 { oid => '2159', descr => 'historical alias for stddev_samp',
   proname => 'stddev', prokind => 'a', proisstrict => 'f',
   prorettype => 'numeric', proargtypes => 'numeric',
@@ -6946,52 +7097,97 @@
   proname => 'regr_count', prokind => 'a', proisstrict => 'f',
   prorettype => 'int8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
+{ oid => '1568', descr => 'partial aggregation for regr_sxx(float8, float8)',
+  proname => 'regr_sxx_p', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float8 float8',
+  prosrc => 'aggregate_dummy' },
 { oid => '2819',
   descr => 'sum of squares of the independent variable (sum(X^2) - sum(X)^2/N)',
   proname => 'regr_sxx', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
+{ oid => '2173', descr => 'partial aggregation for regr_syy(float8, float8)',
+  proname => 'regr_syy_p', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float8 float8',
+  prosrc => 'aggregate_dummy' },
 { oid => '2820',
   descr => 'sum of squares of the dependent variable (sum(Y^2) - sum(Y)^2/N)',
   proname => 'regr_syy', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
+{ oid => '3813', descr => 'partial aggregation for regr_sxy(float8, float8)',
+  proname => 'regr_sxy_p', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float8 float8',
+  prosrc => 'aggregate_dummy' },
 { oid => '2821',
   descr => 'sum of products of independent times dependent variable (sum(X*Y) - sum(X) * sum(Y)/N)',
   proname => 'regr_sxy', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
+{ oid => '3814', descr => 'partial aggregation for regr_avgx(float8, float8)',
+  proname => 'regr_avgx_p', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float8 float8',
+  prosrc => 'aggregate_dummy' },
 { oid => '2822', descr => 'average of the independent variable (sum(X)/N)',
   proname => 'regr_avgx', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
+{ oid => '4551', descr => 'partial aggregation for regr_avgy(float8, float8)',
+  proname => 'regr_avgy_p', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float8 float8',
+  prosrc => 'aggregate_dummy' },
 { oid => '2823', descr => 'average of the dependent variable (sum(Y)/N)',
   proname => 'regr_avgy', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
+{ oid => '4552', descr => 'partial aggregation for regr_r2(float8, float8)',
+  proname => 'regr_r2_p', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float8 float8',
+  prosrc => 'aggregate_dummy' },
 { oid => '2824', descr => 'square of the correlation coefficient',
   proname => 'regr_r2', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
+{ oid => '4553', descr => 'partial aggregation for regr_slope(float8, float8)',
+  proname => 'regr_slope_p', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float8 float8',
+  prosrc => 'aggregate_dummy' },
 { oid => '2825',
   descr => 'slope of the least-squares-fit linear equation determined by the (X, Y) pairs',
   proname => 'regr_slope', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
+{ oid => '4554',
+  descr => 'partial aggregation for regr_intercept(float8, float8)',
+  proname => 'regr_intercept_p', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float8 float8',
+  prosrc => 'aggregate_dummy' },
 { oid => '2826',
   descr => 'y-intercept of the least-squares-fit linear equation determined by the (X, Y) pairs',
   proname => 'regr_intercept', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
 
+{ oid => '4555', descr => 'partial aggregation for covar_pop(float8, float8)',
+  proname => 'covar_pop_p', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float8 float8',
+  prosrc => 'aggregate_dummy' },
 { oid => '2827', descr => 'population covariance',
   proname => 'covar_pop', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
+{ oid => '4556', descr => 'partial aggregation for covar_samp(float8, float8)',
+  proname => 'covar_samp_p', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float8 float8',
+  prosrc => 'aggregate_dummy' },
 { oid => '2828', descr => 'sample covariance',
   proname => 'covar_samp', prokind => 'a', proisstrict => 'f',
   prorettype => 'float8', proargtypes => 'float8 float8',
   prosrc => 'aggregate_dummy' },
+{ oid => '4557', descr => 'partial aggregation for corr(float8, float8)',
+  proname => 'corr_p', prokind => 'a', proisstrict => 'f',
+  prorettype => '_float8', proargtypes => 'float8 float8',
+  prosrc => 'aggregate_dummy' },
 { oid => '2829', descr => 'correlation coefficient',
   proname => 'corr', prokind => 'a', proisstrict => 'f', prorettype => 'float8',
   proargtypes => 'float8 float8', prosrc => 'aggregate_dummy' },
diff --git a/src/test/regress/expected/create_aggregate.out b/src/test/regress/expected/create_aggregate.out
index dcf6909423..fa86d232d6 100644
--- a/src/test/regress/expected/create_aggregate.out
+++ b/src/test/regress/expected/create_aggregate.out
@@ -322,3 +322,164 @@ WARNING:  aggregate attribute "Finalfunc_extra" not recognized
 WARNING:  aggregate attribute "Finalfunc_modify" not recognized
 WARNING:  aggregate attribute "Parallel" not recognized
 ERROR:  aggregate stype must be specified
+-- invalid: finalfunc is specified and stype is not internal
+--   even though aggpartialfunc is equal to the aggregate name
+CREATE AGGREGATE udf_avg_int4_invalid(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	finalfunc = int8_avg,
+	combinefunc = int4_avg_combine,
+	initcond = '{0,0}',
+	aggpartialfunc = udf_avg_int4_invalid
+);
+ERROR:  udf_avg_int4_invalid is not its own aggpartialfunc
+-- invalid: finalfunc is not equal to serialfunc and stype is internal
+--   even though aggpartialfunc is equal to the aggregate name
+CREATE AGGREGATE udf_avg_int8_invalid(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	finalfunc = numeric_poly_avg,
+	combinefunc = int8_avg_combine,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = int8_avg_deserialize,
+	aggpartialfunc = udf_avg_int8_invalid
+);
+ERROR:  udf_avg_int8_invalid is not its own aggpartialfunc
+-- invalid: aggpartialfunc which doesn't exist
+CREATE AGGREGATE hasaggpartial_agg(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	finalfunc = numeric_poly_sum,
+	combinefunc = int8_avg_combine,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = int8_avg_deserialize,
+	aggpartialfunc = aggpartial_foo
+);
+ERROR:  function aggpartial_foo(bigint) does not exist
+-- invalid: aggpartialfunc is specified and combinefunc isn't specified
+CREATE AGGREGATE nocombinefunc_agg(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	finalfunc = numeric_poly_avg,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = int8_avg_deserialize,
+	aggpartialfunc = avg_p_int8
+);
+ERROR:  aggcombinefnName must be supplied if aggpartialfnName is supplied
+-- invalid: combinefunc in agg and combinefunc in aggpartialfunc don't match
+CREATE AGGREGATE udf_avg_invalid_combinefunc(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	finalfunc = numeric_poly_avg,
+	combinefunc = numeric_avg_combine,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = int8_avg_deserialize,
+	aggpartialfunc = avg_p_int8
+);
+ERROR:  combinefunc in aggregate and combinefunc in its aggpartialfunc must match
+CREATE FUNCTION udf_float8_accum(_float8, float8) RETURNS _float8 AS
+$$ SELECT float8_accum($1, $2) $$
+LANGUAGE SQL;
+-- invalid: sfunc in agg and sfunc in aggpartialfunc don't match
+CREATE AGGREGATE udf_avg_invalid_sfunc(float8) (
+	sfunc = udf_float8_accum,
+	stype = _float8,
+	finalfunc = float8_avg,
+	combinefunc = float8_combine,
+	initcond = '{0,0,0}',
+	aggpartialfunc = avg_p_float8
+);
+ERROR:  sfunc in aggregate and its aggpartialfunc must match
+DROP FUNCTION udf_float8_accum(_float8, float8);
+-- invalid: initcond in agg and initcond in aggpartialfunc don't match
+CREATE AGGREGATE udf_avg_invalid_initcond(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	initcond = '{1,0}',
+	finalfunc = int8_avg,
+	combinefunc = int4_avg_combine,
+	aggpartialfunc = avg_p_int4
+);
+ERROR:  initcond in aggregate and initcond in its aggpartialfunc must match
+-- invalid: aggserialfn in agg and aggserialfn in aggpartialfunc don't match
+CREATE AGGREGATE udf_avg_invalid_aggserialfn(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	finalfunc = numeric_poly_avg,
+	combinefunc = int8_avg_combine,
+	serialfunc = numeric_avg_serialize,
+	deserialfunc = int8_avg_deserialize,
+	aggpartialfunc = avg_p_int8
+);
+ERROR:  serialfunc in aggregate and serialfunc in its aggpartialfunc must match
+-- invalid: aggdeserialfn in agg and aggdeserialfn in aggpartialfunc don't match
+CREATE AGGREGATE udf_avg_invalid_aggdeserialfn(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	finalfunc = numeric_poly_avg,
+	combinefunc = int8_avg_combine,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = numeric_avg_deserialize,
+	aggpartialfunc = avg_p_int8
+);
+ERROR:  deserialfunc in aggregate and deserialfunc in its aggpartialfunc must match
+-- invalid: stype is internal and aggpartialfunc's finalfunc
+-- isn't serialfunc of agg
+CREATE AGGREGATE udf_avg_p_int8(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	finalfunc = numeric_avg_serialize,
+	combinefunc = int8_avg_combine,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = int8_avg_deserialize
+);
+CREATE AGGREGATE udf_avg(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	finalfunc = numeric_poly_avg,
+	combinefunc = int8_avg_combine,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = int8_avg_deserialize,
+	aggpartialfunc = udf_avg_p_int8
+);
+ERROR:  finalfunc of aggpartialfunc must match serialfunc of aggregate when stype is internal
+-- invalid: stype is not internal and aggpartialfunc has finalfunc
+CREATE AGGREGATE udf_avg_p_int4_hasfinal(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	finalfunc = int8_avg,
+	combinefunc = int4_avg_combine,
+	initcond = '{0,0}'
+);
+CREATE AGGREGATE udf_avg(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	finalfunc = int8_avg,
+	combinefunc = int4_avg_combine,
+	initcond = '{0,0}',
+	aggpartialfunc = udf_avg_p_int4_hasfinal
+);
+ERROR:  finalfunc of aggpartialfunc must not be supplied when stype isn't internal
+-- invalid: current user doesn't have execute privilege on aggpartialfunc
+CREATE AGGREGATE udf_avg_p_int4(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	combinefunc = int4_avg_combine,
+	initcond = '{0,0}'
+);
+CREATE USER regress_priv_user1;
+GRANT ALL ON SCHEMA public TO regress_priv_user1;
+REVOKE EXECUTE ON FUNCTION udf_avg_p_int4 FROM public;
+SET SESSION AUTHORIZATION regress_priv_user1;
+CREATE AGGREGATE udf_sum(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	finalfunc = int8_avg,
+	combinefunc = int4_avg_combine,
+	initcond = '{0,0}',
+	aggpartialfunc = udf_avg_p_int4
+);
+ERROR:  permission denied for function udf_avg_p_int4
+SET SESSION AUTHORIZATION DEFAULT;
+REVOKE ALL ON SCHEMA public FROM regress_priv_user1;
+DROP USER regress_priv_user1;
diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out
index 215eb899be..6c80efa7eb 100644
--- a/src/test/regress/expected/oidjoins.out
+++ b/src/test/regress/expected/oidjoins.out
@@ -145,6 +145,7 @@ NOTICE:  checking pg_aggregate {aggfinalfn} => pg_proc {oid}
 NOTICE:  checking pg_aggregate {aggcombinefn} => pg_proc {oid}
 NOTICE:  checking pg_aggregate {aggserialfn} => pg_proc {oid}
 NOTICE:  checking pg_aggregate {aggdeserialfn} => pg_proc {oid}
+NOTICE:  checking pg_aggregate {aggpartialfn} => pg_proc {oid}
 NOTICE:  checking pg_aggregate {aggmtransfn} => pg_proc {oid}
 NOTICE:  checking pg_aggregate {aggminvtransfn} => pg_proc {oid}
 NOTICE:  checking pg_aggregate {aggmfinalfn} => pg_proc {oid}
diff --git a/src/test/regress/sql/create_aggregate.sql b/src/test/regress/sql/create_aggregate.sql
index d4b4036fd7..6e9835bb49 100644
--- a/src/test/regress/sql/create_aggregate.sql
+++ b/src/test/regress/sql/create_aggregate.sql
@@ -328,3 +328,166 @@ CREATE AGGREGATE case_agg(float8)
 	"Finalfunc_modify" = read_write,
 	"Parallel" = safe
 );
+
+-- invalid: finalfunc is specified and stype is not internal
+--   even though aggpartialfunc is equal to the aggregate name
+CREATE AGGREGATE udf_avg_int4_invalid(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	finalfunc = int8_avg,
+	combinefunc = int4_avg_combine,
+	initcond = '{0,0}',
+	aggpartialfunc = udf_avg_int4_invalid
+);
+
+-- invalid: finalfunc is not equal to serialfunc and stype is internal
+--   even though aggpartialfunc is equal to the aggregate name
+CREATE AGGREGATE udf_avg_int8_invalid(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	finalfunc = numeric_poly_avg,
+	combinefunc = int8_avg_combine,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = int8_avg_deserialize,
+	aggpartialfunc = udf_avg_int8_invalid
+);
+
+-- invalid: aggpartialfunc which doesn't exist
+CREATE AGGREGATE hasaggpartial_agg(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	finalfunc = numeric_poly_sum,
+	combinefunc = int8_avg_combine,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = int8_avg_deserialize,
+	aggpartialfunc = aggpartial_foo
+);
+
+-- invalid: aggpartialfunc is specified and combinefunc isn't specified
+CREATE AGGREGATE nocombinefunc_agg(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	finalfunc = numeric_poly_avg,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = int8_avg_deserialize,
+	aggpartialfunc = avg_p_int8
+);
+
+-- invalid: combinefunc in agg and combinefunc in aggpartialfunc don't match
+CREATE AGGREGATE udf_avg_invalid_combinefunc(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	finalfunc = numeric_poly_avg,
+	combinefunc = numeric_avg_combine,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = int8_avg_deserialize,
+	aggpartialfunc = avg_p_int8
+);
+
+CREATE FUNCTION udf_float8_accum(_float8, float8) RETURNS _float8 AS
+$$ SELECT float8_accum($1, $2) $$
+LANGUAGE SQL;
+
+-- invalid: sfunc in agg and sfunc in aggpartialfunc don't match
+CREATE AGGREGATE udf_avg_invalid_sfunc(float8) (
+	sfunc = udf_float8_accum,
+	stype = _float8,
+	finalfunc = float8_avg,
+	combinefunc = float8_combine,
+	initcond = '{0,0,0}',
+	aggpartialfunc = avg_p_float8
+);
+DROP FUNCTION udf_float8_accum(_float8, float8);
+
+-- invalid: initcond in agg and initcond in aggpartialfunc don't match
+CREATE AGGREGATE udf_avg_invalid_initcond(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	initcond = '{1,0}',
+	finalfunc = int8_avg,
+	combinefunc = int4_avg_combine,
+	aggpartialfunc = avg_p_int4
+);
+
+-- invalid: aggserialfn in agg and aggserialfn in aggpartialfunc don't match
+CREATE AGGREGATE udf_avg_invalid_aggserialfn(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	finalfunc = numeric_poly_avg,
+	combinefunc = int8_avg_combine,
+	serialfunc = numeric_avg_serialize,
+	deserialfunc = int8_avg_deserialize,
+	aggpartialfunc = avg_p_int8
+);
+
+-- invalid: aggdeserialfn in agg and aggdeserialfn in aggpartialfunc don't match
+CREATE AGGREGATE udf_avg_invalid_aggdeserialfn(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	finalfunc = numeric_poly_avg,
+	combinefunc = int8_avg_combine,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = numeric_avg_deserialize,
+	aggpartialfunc = avg_p_int8
+);
+
+-- invalid: stype is internal and aggpartialfunc's finalfunc
+-- isn't serialfunc of agg
+CREATE AGGREGATE udf_avg_p_int8(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	finalfunc = numeric_avg_serialize,
+	combinefunc = int8_avg_combine,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = int8_avg_deserialize
+);
+CREATE AGGREGATE udf_avg(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	finalfunc = numeric_poly_avg,
+	combinefunc = int8_avg_combine,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = int8_avg_deserialize,
+	aggpartialfunc = udf_avg_p_int8
+);
+
+-- invalid: stype is not internal and aggpartialfunc has finalfunc
+CREATE AGGREGATE udf_avg_p_int4_hasfinal(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	finalfunc = int8_avg,
+	combinefunc = int4_avg_combine,
+	initcond = '{0,0}'
+);
+CREATE AGGREGATE udf_avg(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	finalfunc = int8_avg,
+	combinefunc = int4_avg_combine,
+	initcond = '{0,0}',
+	aggpartialfunc = udf_avg_p_int4_hasfinal
+);
+
+-- invalid: current user doesn't have execute privilege on aggpartialfunc
+CREATE AGGREGATE udf_avg_p_int4(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	combinefunc = int4_avg_combine,
+	initcond = '{0,0}'
+);
+CREATE USER regress_priv_user1;
+GRANT ALL ON SCHEMA public TO regress_priv_user1;
+REVOKE EXECUTE ON FUNCTION udf_avg_p_int4 FROM public;
+SET SESSION AUTHORIZATION regress_priv_user1;
+
+CREATE AGGREGATE udf_sum(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	finalfunc = int8_avg,
+	combinefunc = int4_avg_combine,
+	initcond = '{0,0}',
+	aggpartialfunc = udf_avg_p_int4
+);
+SET SESSION AUTHORIZATION DEFAULT;
+REVOKE ALL ON SCHEMA public FROM regress_priv_user1;
+DROP USER regress_priv_user1;
-- 
2.31.1

