public inbox for [email protected]  
help / color / mirror / Atom feed
[PATCH 14/21] another way to install uri-regress/uri_regress
35+ messages / 7 participants
[nested] [flat]

* [PATCH 13/19] another way to install uri-regress/uri_regress
@ 2022-05-27 03:24 Justin Pryzby <[email protected]>
  0 siblings, 0 replies; 35+ messages in thread

From: Justin Pryzby @ 2022-05-27 03:24 UTC (permalink / raw)

This reverts commit a17fd67d2f2861ae0ce00d1aeefdf2facc47cd5e.

TODO: partially revert 6b04abdfc5e0653542ac5d586e639185a8c61a39

See also:
a17fd67d2f2861ae0ce00d1aeefdf2facc47cd5e
https://www.postgresql.org/message-id/20220416144454.GX26620%40telsasoft.com

ci-os-only: windows
---
 src/interfaces/libpq/test/Makefile |  6 ++---
 src/tools/msvc/Mkvcbuild.pm        | 36 +++++++++++++-----------------
 2 files changed, 18 insertions(+), 24 deletions(-)

diff --git a/src/interfaces/libpq/test/Makefile b/src/interfaces/libpq/test/Makefile
index 1f75b73b8c1..d8e5ae824f2 100644
--- a/src/interfaces/libpq/test/Makefile
+++ b/src/interfaces/libpq/test/Makefile
@@ -11,9 +11,9 @@ endif
 override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS)
 LDFLAGS_INTERNAL += $(libpq_pgport)
 
-PROGS = libpq_testclient libpq_uri_regress
+PROGRAMS = libpq_testclient libpq_uri_regress
 
-all: $(PROGS)
+all: $(PROGRAMS)
 
 clean distclean maintainer-clean:
-	rm -f $(PROGS) *.o
+	rm -f $(PROGRAMS) *.o
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index e4feda10fd8..1de77075724 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -37,11 +37,14 @@ my @unlink_on_exit;
 # Set of variables for modules in contrib/ and src/test/modules/
 my $contrib_defines        = {};
 my @contrib_uselibpq       = ();
-my @contrib_uselibpgport   = ();
+my @contrib_uselibpgport   = ('libpq_uri_regress', 'libpq_testclient');
 my @contrib_uselibpgcommon = ();
 my $contrib_extralibs      = { 'libpq_pipeline' => ['ws2_32.lib'] };
 my $contrib_extraincludes  = {};
-my $contrib_extrasource    = {};
+my $contrib_extrasource    = {
+	'libpq_uri_regress' => ['src/interfaces/libpq/test/libpq_uri_regress.c'],
+	'libpq_testclient'  => ['src/interfaces/libpq/test/libpq_testclient.c'],
+};
 my @contrib_excludes       = (
 	'bool_plperl',     'commit_ts',
 	'hstore_plperl',   'hstore_plpython',
@@ -284,24 +287,6 @@ sub mkvcbuild
 	$libpqwalreceiver->AddIncludeDir('src/interfaces/libpq');
 	$libpqwalreceiver->AddReference($postgres, $libpq);
 
-	my $libpq_testclient =
-	  $solution->AddProject('libpq_testclient', 'exe', 'misc',
-		'src/interfaces/libpq/test');
-	$libpq_testclient->AddFile(
-		'src/interfaces/libpq/test/libpq_testclient.c');
-	$libpq_testclient->AddIncludeDir('src/interfaces/libpq');
-	$libpq_testclient->AddReference($libpgport, $libpq);
-	$libpq_testclient->AddLibrary('ws2_32.lib');
-
-	my $libpq_uri_regress =
-	  $solution->AddProject('libpq_uri_regress', 'exe', 'misc',
-		'src/interfaces/libpq/test');
-	$libpq_uri_regress->AddFile(
-		'src/interfaces/libpq/test/libpq_uri_regress.c');
-	$libpq_uri_regress->AddIncludeDir('src/interfaces/libpq');
-	$libpq_uri_regress->AddReference($libpgport, $libpq);
-	$libpq_uri_regress->AddLibrary('ws2_32.lib');
-
 	my $pgoutput = $solution->AddProject('pgoutput', 'dll', '',
 		'src/backend/replication/pgoutput');
 	$pgoutput->AddReference($postgres);
@@ -475,7 +460,7 @@ sub mkvcbuild
 		push @contrib_excludes, 'uuid-ossp';
 	}
 
-	foreach my $subdir ('contrib', 'src/test/modules')
+	foreach my $subdir ('contrib', 'src/test/modules', 'src/interfaces/libpq')
 	{
 		opendir($D, $subdir) || croak "Could not opendir on $subdir!\n";
 		while (my $d = readdir($D))
@@ -986,6 +971,15 @@ sub AddContrib
 		AdjustContribProj($proj);
 		push @projects, $proj;
 	}
+	elsif ($mf =~ /^PROGRAMS\s*=\s*(.*)$/mg)
+	{
+		foreach my $proj (split /\s+/, $1)
+		{
+			my $p = $solution->AddProject($proj, 'exe', 'contrib', "$subdir/$n");
+			AdjustContribProj($p);
+			push @projects, $p;
+		}
+	}
 	else
 	{
 		croak "Could not determine contrib module type for $n\n";
-- 
2.17.1


--Sw7tCqrGA+HQ0/zt
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0014-Move-libpq_pipeline-test-into-src-interfaces-libpq.patch"



^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* [PATCH 14/21] another way to install uri-regress/uri_regress
@ 2022-05-27 03:24 Justin Pryzby <[email protected]>
  0 siblings, 0 replies; 35+ messages in thread

From: Justin Pryzby @ 2022-05-27 03:24 UTC (permalink / raw)

This reverts commit a17fd67d2f2861ae0ce00d1aeefdf2facc47cd5e.

TODO: partially revert 6b04abdfc5e0653542ac5d586e639185a8c61a39

See also:
a17fd67d2f2861ae0ce00d1aeefdf2facc47cd5e
https://www.postgresql.org/message-id/20220416144454.GX26620%40telsasoft.com
https://www.postgresql.org/message-id/20220223211606.ikcwoolsz2nohsw6%40alap3.anarazel.de

TODO: doc/src/sgml/extend.sgml

ci-os-only: linux, windows
---
 src/interfaces/libpq/test/Makefile |  6 ++---
 src/tools/msvc/Mkvcbuild.pm        | 36 +++++++++++++-----------------
 2 files changed, 18 insertions(+), 24 deletions(-)

diff --git a/src/interfaces/libpq/test/Makefile b/src/interfaces/libpq/test/Makefile
index 1f75b73b8c1..d8e5ae824f2 100644
--- a/src/interfaces/libpq/test/Makefile
+++ b/src/interfaces/libpq/test/Makefile
@@ -11,9 +11,9 @@ endif
 override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS)
 LDFLAGS_INTERNAL += $(libpq_pgport)
 
-PROGS = libpq_testclient libpq_uri_regress
+PROGRAMS = libpq_testclient libpq_uri_regress
 
-all: $(PROGS)
+all: $(PROGRAMS)
 
 clean distclean maintainer-clean:
-	rm -f $(PROGS) *.o
+	rm -f $(PROGRAMS) *.o
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index e4feda10fd8..1de77075724 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -37,11 +37,14 @@ my @unlink_on_exit;
 # Set of variables for modules in contrib/ and src/test/modules/
 my $contrib_defines        = {};
 my @contrib_uselibpq       = ();
-my @contrib_uselibpgport   = ();
+my @contrib_uselibpgport   = ('libpq_uri_regress', 'libpq_testclient');
 my @contrib_uselibpgcommon = ();
 my $contrib_extralibs      = { 'libpq_pipeline' => ['ws2_32.lib'] };
 my $contrib_extraincludes  = {};
-my $contrib_extrasource    = {};
+my $contrib_extrasource    = {
+	'libpq_uri_regress' => ['src/interfaces/libpq/test/libpq_uri_regress.c'],
+	'libpq_testclient'  => ['src/interfaces/libpq/test/libpq_testclient.c'],
+};
 my @contrib_excludes       = (
 	'bool_plperl',     'commit_ts',
 	'hstore_plperl',   'hstore_plpython',
@@ -284,24 +287,6 @@ sub mkvcbuild
 	$libpqwalreceiver->AddIncludeDir('src/interfaces/libpq');
 	$libpqwalreceiver->AddReference($postgres, $libpq);
 
-	my $libpq_testclient =
-	  $solution->AddProject('libpq_testclient', 'exe', 'misc',
-		'src/interfaces/libpq/test');
-	$libpq_testclient->AddFile(
-		'src/interfaces/libpq/test/libpq_testclient.c');
-	$libpq_testclient->AddIncludeDir('src/interfaces/libpq');
-	$libpq_testclient->AddReference($libpgport, $libpq);
-	$libpq_testclient->AddLibrary('ws2_32.lib');
-
-	my $libpq_uri_regress =
-	  $solution->AddProject('libpq_uri_regress', 'exe', 'misc',
-		'src/interfaces/libpq/test');
-	$libpq_uri_regress->AddFile(
-		'src/interfaces/libpq/test/libpq_uri_regress.c');
-	$libpq_uri_regress->AddIncludeDir('src/interfaces/libpq');
-	$libpq_uri_regress->AddReference($libpgport, $libpq);
-	$libpq_uri_regress->AddLibrary('ws2_32.lib');
-
 	my $pgoutput = $solution->AddProject('pgoutput', 'dll', '',
 		'src/backend/replication/pgoutput');
 	$pgoutput->AddReference($postgres);
@@ -475,7 +460,7 @@ sub mkvcbuild
 		push @contrib_excludes, 'uuid-ossp';
 	}
 
-	foreach my $subdir ('contrib', 'src/test/modules')
+	foreach my $subdir ('contrib', 'src/test/modules', 'src/interfaces/libpq')
 	{
 		opendir($D, $subdir) || croak "Could not opendir on $subdir!\n";
 		while (my $d = readdir($D))
@@ -986,6 +971,15 @@ sub AddContrib
 		AdjustContribProj($proj);
 		push @projects, $proj;
 	}
+	elsif ($mf =~ /^PROGRAMS\s*=\s*(.*)$/mg)
+	{
+		foreach my $proj (split /\s+/, $1)
+		{
+			my $p = $solution->AddProject($proj, 'exe', 'contrib', "$subdir/$n");
+			AdjustContribProj($p);
+			push @projects, $p;
+		}
+	}
 	else
 	{
 		croak "Could not determine contrib module type for $n\n";
-- 
2.17.1


--STPqjqpCrtky8aYs
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0015-Move-libpq_pipeline-test-into-src-interfaces-libpq.patch"



^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* [PATCH 13/19] another way to install uri-regress/uri_regress
@ 2022-05-27 03:24 Justin Pryzby <[email protected]>
  0 siblings, 0 replies; 35+ messages in thread

From: Justin Pryzby @ 2022-05-27 03:24 UTC (permalink / raw)

This reverts commit a17fd67d2f2861ae0ce00d1aeefdf2facc47cd5e.

TODO: partially revert 6b04abdfc5e0653542ac5d586e639185a8c61a39

See also:
a17fd67d2f2861ae0ce00d1aeefdf2facc47cd5e
https://www.postgresql.org/message-id/20220416144454.GX26620%40telsasoft.com

ci-os-only: windows
---
 src/interfaces/libpq/test/Makefile |  6 ++---
 src/tools/msvc/Mkvcbuild.pm        | 36 +++++++++++++-----------------
 2 files changed, 18 insertions(+), 24 deletions(-)

diff --git a/src/interfaces/libpq/test/Makefile b/src/interfaces/libpq/test/Makefile
index 1f75b73b8c1..d8e5ae824f2 100644
--- a/src/interfaces/libpq/test/Makefile
+++ b/src/interfaces/libpq/test/Makefile
@@ -11,9 +11,9 @@ endif
 override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS)
 LDFLAGS_INTERNAL += $(libpq_pgport)
 
-PROGS = libpq_testclient libpq_uri_regress
+PROGRAMS = libpq_testclient libpq_uri_regress
 
-all: $(PROGS)
+all: $(PROGRAMS)
 
 clean distclean maintainer-clean:
-	rm -f $(PROGS) *.o
+	rm -f $(PROGRAMS) *.o
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index e4feda10fd8..1de77075724 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -37,11 +37,14 @@ my @unlink_on_exit;
 # Set of variables for modules in contrib/ and src/test/modules/
 my $contrib_defines        = {};
 my @contrib_uselibpq       = ();
-my @contrib_uselibpgport   = ();
+my @contrib_uselibpgport   = ('libpq_uri_regress', 'libpq_testclient');
 my @contrib_uselibpgcommon = ();
 my $contrib_extralibs      = { 'libpq_pipeline' => ['ws2_32.lib'] };
 my $contrib_extraincludes  = {};
-my $contrib_extrasource    = {};
+my $contrib_extrasource    = {
+	'libpq_uri_regress' => ['src/interfaces/libpq/test/libpq_uri_regress.c'],
+	'libpq_testclient'  => ['src/interfaces/libpq/test/libpq_testclient.c'],
+};
 my @contrib_excludes       = (
 	'bool_plperl',     'commit_ts',
 	'hstore_plperl',   'hstore_plpython',
@@ -284,24 +287,6 @@ sub mkvcbuild
 	$libpqwalreceiver->AddIncludeDir('src/interfaces/libpq');
 	$libpqwalreceiver->AddReference($postgres, $libpq);
 
-	my $libpq_testclient =
-	  $solution->AddProject('libpq_testclient', 'exe', 'misc',
-		'src/interfaces/libpq/test');
-	$libpq_testclient->AddFile(
-		'src/interfaces/libpq/test/libpq_testclient.c');
-	$libpq_testclient->AddIncludeDir('src/interfaces/libpq');
-	$libpq_testclient->AddReference($libpgport, $libpq);
-	$libpq_testclient->AddLibrary('ws2_32.lib');
-
-	my $libpq_uri_regress =
-	  $solution->AddProject('libpq_uri_regress', 'exe', 'misc',
-		'src/interfaces/libpq/test');
-	$libpq_uri_regress->AddFile(
-		'src/interfaces/libpq/test/libpq_uri_regress.c');
-	$libpq_uri_regress->AddIncludeDir('src/interfaces/libpq');
-	$libpq_uri_regress->AddReference($libpgport, $libpq);
-	$libpq_uri_regress->AddLibrary('ws2_32.lib');
-
 	my $pgoutput = $solution->AddProject('pgoutput', 'dll', '',
 		'src/backend/replication/pgoutput');
 	$pgoutput->AddReference($postgres);
@@ -475,7 +460,7 @@ sub mkvcbuild
 		push @contrib_excludes, 'uuid-ossp';
 	}
 
-	foreach my $subdir ('contrib', 'src/test/modules')
+	foreach my $subdir ('contrib', 'src/test/modules', 'src/interfaces/libpq')
 	{
 		opendir($D, $subdir) || croak "Could not opendir on $subdir!\n";
 		while (my $d = readdir($D))
@@ -986,6 +971,15 @@ sub AddContrib
 		AdjustContribProj($proj);
 		push @projects, $proj;
 	}
+	elsif ($mf =~ /^PROGRAMS\s*=\s*(.*)$/mg)
+	{
+		foreach my $proj (split /\s+/, $1)
+		{
+			my $p = $solution->AddProject($proj, 'exe', 'contrib', "$subdir/$n");
+			AdjustContribProj($p);
+			push @projects, $p;
+		}
+	}
 	else
 	{
 		croak "Could not determine contrib module type for $n\n";
-- 
2.17.1


--Sw7tCqrGA+HQ0/zt
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0014-Move-libpq_pipeline-test-into-src-interfaces-libpq.patch"



^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* [PATCH 14/21] another way to install uri-regress/uri_regress
@ 2022-05-27 03:24 Justin Pryzby <[email protected]>
  0 siblings, 0 replies; 35+ messages in thread

From: Justin Pryzby @ 2022-05-27 03:24 UTC (permalink / raw)

This reverts commit a17fd67d2f2861ae0ce00d1aeefdf2facc47cd5e.

TODO: partially revert 6b04abdfc5e0653542ac5d586e639185a8c61a39

See also:
a17fd67d2f2861ae0ce00d1aeefdf2facc47cd5e
https://www.postgresql.org/message-id/20220416144454.GX26620%40telsasoft.com
https://www.postgresql.org/message-id/20220223211606.ikcwoolsz2nohsw6%40alap3.anarazel.de

TODO: doc/src/sgml/extend.sgml

ci-os-only: linux, windows
---
 src/interfaces/libpq/test/Makefile |  8 +++----
 src/tools/msvc/Mkvcbuild.pm        | 36 +++++++++++++-----------------
 2 files changed, 19 insertions(+), 25 deletions(-)

diff --git a/src/interfaces/libpq/test/Makefile b/src/interfaces/libpq/test/Makefile
index 75ac08f943d..91c8d87d528 100644
--- a/src/interfaces/libpq/test/Makefile
+++ b/src/interfaces/libpq/test/Makefile
@@ -14,11 +14,11 @@ endif
 override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS)
 LDFLAGS_INTERNAL += $(libpq_pgport)
 
-PROGS = libpq_testclient libpq_uri_regress
+PROGRAMS = libpq_testclient libpq_uri_regress
 
-all: $(PROGS)
+all: $(PROGRAMS)
 
-$(PROGS): $(WIN32RES)
+$(PROGRAMS): $(WIN32RES)
 
 clean distclean maintainer-clean:
-	rm -f $(PROGS) *.o
+	rm -f $(PROGRAMS) *.o
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index e4feda10fd8..1de77075724 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -37,11 +37,14 @@ my @unlink_on_exit;
 # Set of variables for modules in contrib/ and src/test/modules/
 my $contrib_defines        = {};
 my @contrib_uselibpq       = ();
-my @contrib_uselibpgport   = ();
+my @contrib_uselibpgport   = ('libpq_uri_regress', 'libpq_testclient');
 my @contrib_uselibpgcommon = ();
 my $contrib_extralibs      = { 'libpq_pipeline' => ['ws2_32.lib'] };
 my $contrib_extraincludes  = {};
-my $contrib_extrasource    = {};
+my $contrib_extrasource    = {
+	'libpq_uri_regress' => ['src/interfaces/libpq/test/libpq_uri_regress.c'],
+	'libpq_testclient'  => ['src/interfaces/libpq/test/libpq_testclient.c'],
+};
 my @contrib_excludes       = (
 	'bool_plperl',     'commit_ts',
 	'hstore_plperl',   'hstore_plpython',
@@ -284,24 +287,6 @@ sub mkvcbuild
 	$libpqwalreceiver->AddIncludeDir('src/interfaces/libpq');
 	$libpqwalreceiver->AddReference($postgres, $libpq);
 
-	my $libpq_testclient =
-	  $solution->AddProject('libpq_testclient', 'exe', 'misc',
-		'src/interfaces/libpq/test');
-	$libpq_testclient->AddFile(
-		'src/interfaces/libpq/test/libpq_testclient.c');
-	$libpq_testclient->AddIncludeDir('src/interfaces/libpq');
-	$libpq_testclient->AddReference($libpgport, $libpq);
-	$libpq_testclient->AddLibrary('ws2_32.lib');
-
-	my $libpq_uri_regress =
-	  $solution->AddProject('libpq_uri_regress', 'exe', 'misc',
-		'src/interfaces/libpq/test');
-	$libpq_uri_regress->AddFile(
-		'src/interfaces/libpq/test/libpq_uri_regress.c');
-	$libpq_uri_regress->AddIncludeDir('src/interfaces/libpq');
-	$libpq_uri_regress->AddReference($libpgport, $libpq);
-	$libpq_uri_regress->AddLibrary('ws2_32.lib');
-
 	my $pgoutput = $solution->AddProject('pgoutput', 'dll', '',
 		'src/backend/replication/pgoutput');
 	$pgoutput->AddReference($postgres);
@@ -475,7 +460,7 @@ sub mkvcbuild
 		push @contrib_excludes, 'uuid-ossp';
 	}
 
-	foreach my $subdir ('contrib', 'src/test/modules')
+	foreach my $subdir ('contrib', 'src/test/modules', 'src/interfaces/libpq')
 	{
 		opendir($D, $subdir) || croak "Could not opendir on $subdir!\n";
 		while (my $d = readdir($D))
@@ -986,6 +971,15 @@ sub AddContrib
 		AdjustContribProj($proj);
 		push @projects, $proj;
 	}
+	elsif ($mf =~ /^PROGRAMS\s*=\s*(.*)$/mg)
+	{
+		foreach my $proj (split /\s+/, $1)
+		{
+			my $p = $solution->AddProject($proj, 'exe', 'contrib', "$subdir/$n");
+			AdjustContribProj($p);
+			push @projects, $p;
+		}
+	}
 	else
 	{
 		croak "Could not determine contrib module type for $n\n";
-- 
2.17.1


--mln0rGgUGuXEqmuI
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0015-Move-libpq_pipeline-test-into-src-interfaces-libpq.patch"



^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* [PATCH 13/19] another way to install uri-regress/uri_regress
@ 2022-05-27 03:24 Justin Pryzby <[email protected]>
  0 siblings, 0 replies; 35+ messages in thread

From: Justin Pryzby @ 2022-05-27 03:24 UTC (permalink / raw)

This reverts commit a17fd67d2f2861ae0ce00d1aeefdf2facc47cd5e.

TODO: partially revert 6b04abdfc5e0653542ac5d586e639185a8c61a39

See also:
a17fd67d2f2861ae0ce00d1aeefdf2facc47cd5e
https://www.postgresql.org/message-id/20220416144454.GX26620%40telsasoft.com

ci-os-only: windows
---
 src/interfaces/libpq/test/Makefile |  6 ++---
 src/tools/msvc/Mkvcbuild.pm        | 36 +++++++++++++-----------------
 2 files changed, 18 insertions(+), 24 deletions(-)

diff --git a/src/interfaces/libpq/test/Makefile b/src/interfaces/libpq/test/Makefile
index 1f75b73b8c1..d8e5ae824f2 100644
--- a/src/interfaces/libpq/test/Makefile
+++ b/src/interfaces/libpq/test/Makefile
@@ -11,9 +11,9 @@ endif
 override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS)
 LDFLAGS_INTERNAL += $(libpq_pgport)
 
-PROGS = libpq_testclient libpq_uri_regress
+PROGRAMS = libpq_testclient libpq_uri_regress
 
-all: $(PROGS)
+all: $(PROGRAMS)
 
 clean distclean maintainer-clean:
-	rm -f $(PROGS) *.o
+	rm -f $(PROGRAMS) *.o
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index e4feda10fd8..1de77075724 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -37,11 +37,14 @@ my @unlink_on_exit;
 # Set of variables for modules in contrib/ and src/test/modules/
 my $contrib_defines        = {};
 my @contrib_uselibpq       = ();
-my @contrib_uselibpgport   = ();
+my @contrib_uselibpgport   = ('libpq_uri_regress', 'libpq_testclient');
 my @contrib_uselibpgcommon = ();
 my $contrib_extralibs      = { 'libpq_pipeline' => ['ws2_32.lib'] };
 my $contrib_extraincludes  = {};
-my $contrib_extrasource    = {};
+my $contrib_extrasource    = {
+	'libpq_uri_regress' => ['src/interfaces/libpq/test/libpq_uri_regress.c'],
+	'libpq_testclient'  => ['src/interfaces/libpq/test/libpq_testclient.c'],
+};
 my @contrib_excludes       = (
 	'bool_plperl',     'commit_ts',
 	'hstore_plperl',   'hstore_plpython',
@@ -284,24 +287,6 @@ sub mkvcbuild
 	$libpqwalreceiver->AddIncludeDir('src/interfaces/libpq');
 	$libpqwalreceiver->AddReference($postgres, $libpq);
 
-	my $libpq_testclient =
-	  $solution->AddProject('libpq_testclient', 'exe', 'misc',
-		'src/interfaces/libpq/test');
-	$libpq_testclient->AddFile(
-		'src/interfaces/libpq/test/libpq_testclient.c');
-	$libpq_testclient->AddIncludeDir('src/interfaces/libpq');
-	$libpq_testclient->AddReference($libpgport, $libpq);
-	$libpq_testclient->AddLibrary('ws2_32.lib');
-
-	my $libpq_uri_regress =
-	  $solution->AddProject('libpq_uri_regress', 'exe', 'misc',
-		'src/interfaces/libpq/test');
-	$libpq_uri_regress->AddFile(
-		'src/interfaces/libpq/test/libpq_uri_regress.c');
-	$libpq_uri_regress->AddIncludeDir('src/interfaces/libpq');
-	$libpq_uri_regress->AddReference($libpgport, $libpq);
-	$libpq_uri_regress->AddLibrary('ws2_32.lib');
-
 	my $pgoutput = $solution->AddProject('pgoutput', 'dll', '',
 		'src/backend/replication/pgoutput');
 	$pgoutput->AddReference($postgres);
@@ -475,7 +460,7 @@ sub mkvcbuild
 		push @contrib_excludes, 'uuid-ossp';
 	}
 
-	foreach my $subdir ('contrib', 'src/test/modules')
+	foreach my $subdir ('contrib', 'src/test/modules', 'src/interfaces/libpq')
 	{
 		opendir($D, $subdir) || croak "Could not opendir on $subdir!\n";
 		while (my $d = readdir($D))
@@ -986,6 +971,15 @@ sub AddContrib
 		AdjustContribProj($proj);
 		push @projects, $proj;
 	}
+	elsif ($mf =~ /^PROGRAMS\s*=\s*(.*)$/mg)
+	{
+		foreach my $proj (split /\s+/, $1)
+		{
+			my $p = $solution->AddProject($proj, 'exe', 'contrib', "$subdir/$n");
+			AdjustContribProj($p);
+			push @projects, $p;
+		}
+	}
 	else
 	{
 		croak "Could not determine contrib module type for $n\n";
-- 
2.17.1


--Sw7tCqrGA+HQ0/zt
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0014-Move-libpq_pipeline-test-into-src-interfaces-libpq.patch"



^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* RE: Partial aggregates pushdown
@ 2023-03-31 05:49 [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  0 siblings, 1 reply; 35+ messages in thread

From: [email protected] @ 2023-03-31 05:49 UTC (permalink / raw)
  To: Bruce Momjian <[email protected]>; +Cc: Andres Freund <[email protected]>; Alexander Pyhalov <[email protected]>; Tomas Vondra <[email protected]>; pgsql-hackers; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>; [email protected] <[email protected]>

Hi Mr.Momjian

> First, am I correct?
Yes, you are correct. This patch uses new special aggregate functions for partial aggregate
(then we call this partialaggfunc).

> Second, how far away is this from being committable
> and/or what work needs to be done to get it committable, either for PG 16 or 17?
I believe there are three: 1. and 2. are not clear if they are necessary or not; 3. are clearly necessary.
I would like to hear the opinions of the development community on whether or not 1. and 2. need to be addressed.

1. Making partialaggfunc user-defined function
In v17, I make partialaggfuncs as built-in functions.
Because of this approach, v17 changes specification of BKI file and pg_aggregate.
For now, partialaggfuncs are needed by only postgres_fdw which is just an extension of PostgreSQL.
In the future, when revising the specifications for BKI files and pg_aggregate when modifying existing PostgreSQL functions,
It is necessary to align them with this patch's changes.
I am concerned that this may be undesirable.
So I am thinking that v17 should be modified to making partialaggfunc as user defined function.

2. Automation of creating definition of partialaggfuncs
In development of v17, I manually create definition of partialaggfuncs for avg, min, max, sum, count.
I am concerned that this may be undesirable.
So I am thinking that v17 should be modified to automate creating definition of partialaggfuncs
for all built-in aggregate functions.

3. Documentation
I need add explanation of partialaggfunc to documents on postgres_fdw and other places.

Sincerely yours,
Yuuki Fujii

--
Yuuki Fujii
Information Technology R&D Center Mitsubishi Electric Corporation






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
@ 2023-04-07 01:59 ` Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  0 siblings, 1 reply; 35+ messages in thread

From: Bruce Momjian @ 2023-04-07 01:59 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: Andres Freund <[email protected]>; Alexander Pyhalov <[email protected]>; Tomas Vondra <[email protected]>; pgsql-hackers; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

On Fri, Mar 31, 2023 at 05:49:21AM +0000, [email protected] wrote:
> Hi Mr.Momjian
> 
> > First, am I correct?
> Yes, you are correct. This patch uses new special aggregate functions for partial aggregate
> (then we call this partialaggfunc).

First, my apologies for not addressing this sooner.  I was so focused on
my own tasks that I didn't realize this very important patch was not
getting attention.  I will try my best to get it into PG 17.

What amazes me is that you didn't need to create _any_ actual aggregate
functions.  Rather, you just needed to hook existing functions into the
aggregate tables for partial FDW execution.

> > Second, how far away is this from being committable
> > and/or what work needs to be done to get it committable, either for PG 16 or 17?
> I believe there are three: 1. and 2. are not clear if they are necessary or not; 3. are clearly necessary.
> I would like to hear the opinions of the development community on whether or not 1. and 2. need to be addressed.
> 
> 1. Making partialaggfunc user-defined function
> In v17, I make partialaggfuncs as built-in functions.
> Because of this approach, v17 changes specification of BKI file and pg_aggregate.
> For now, partialaggfuncs are needed by only postgres_fdw which is just an extension of PostgreSQL.
> In the future, when revising the specifications for BKI files and pg_aggregate when modifying existing PostgreSQL functions,
> It is necessary to align them with this patch's changes.
> I am concerned that this may be undesirable.
> So I am thinking that v17 should be modified to making partialaggfunc as user defined function.

I think we have three possible cases for aggregates pushdown to FDWs:

1)  Postgres built-in aggregate functions
2)  Postgres user-defined & extension aggregate functions
3)  aggregate functions calls to non-PG FDWs

Your patch handles #1 by checking that the FDW Postgres version is the
same as the calling Postgres version.  However, it doesn't check for
extension versions, and frankly, I don't see how we could implement that
cleanly without significant complexity.

I suggest we remove the version check requirement --- instead just
document that the FDW Postgres version should be the same or newer than
the calling Postgres server --- that way, we can assume that whatever is
in the system catalogs of the caller is in the receiving side.  We
should add a GUC to turn off this optimization for cases where the FDW
Postgres version is older than the caller.  This handles case 1-2.

For case 3, I don't even know how much pushdown those do of _any_
aggregates to non-PG servers, let along parallel FDW ones.  Does anyone
know the details?

> 2. Automation of creating definition of partialaggfuncs
> In development of v17, I manually create definition of partialaggfuncs for avg, min, max, sum, count.
> I am concerned that this may be undesirable.
> So I am thinking that v17 should be modified to automate creating definition of partialaggfuncs
> for all built-in aggregate functions.

Are there any other builtin functions that need this?  I think we can
just provide documention for extensions on how to do this.

> 3. Documentation
> I need add explanation of partialaggfunc to documents on postgres_fdw and other places.

I can help with that once we decide on the above.

I think 'partialaggfn' should be named 'aggpartialfn' to match other
columns in pg_aggregate.

I am confused by these changes to pg_aggegate:

+{ 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' },

...

+{ 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' },

Why are these marked as 'sum' but use 'avg' functions?

It would be good to explain exactly how this is diffent from background
worker parallelism.

-- 
  Bruce Momjian  <[email protected]>        https://momjian.us
  EDB                                      https://enterprisedb.com

  Embrace your flaws.  They make you human, rather than perfect,
  which you will never be.


Attachments:

  [text/x-diff] aggpush.diff (60.7K, ../../[email protected]/2-aggpush.diff)
  download | inline diff:
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 09d6dd60dd..fe219b8d2f 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,37 @@ 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 or partialaggfn which is a pg_proc entry */
+		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->aggtranstype != INTERNALOID) && (aggform->aggfinalfn == InvalidOid)) {
+			appendFunctionName(node->aggfnoid, context);
+		} else if(aggform->partialaggfn) {
+			appendFunctionName(aggform->partialaggfn, context);
+		} else {
+			elog(ERROR, "there is no partialaggfn %u", node->aggfnoid);
+		}
+		ReleaseSysCache(aggtup);
+	}
+
 	appendStringInfoChar(buf, '(');
 
 	/* Add DISTINCT */
@@ -4047,3 +4071,62 @@ get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
 	/* Shouldn't get here */
 	elog(ERROR, "unexpected expression in subquery output");
 }
+
+/*
+ * Check that partial aggregate function, described by aggform,
+ * exists on remote server, described by fpinfo.
+ */
+static bool
+partial_agg_compatible(Form_pg_aggregate aggform, PgFdwRelationInfo *fpinfo)
+{
+	int32  partialagg_minversion = PG_VERSION_NUM;
+	if (aggform->partialagg_minversion != PARTIALAGG_MINVERSION_DEFAULT) {
+		partialagg_minversion = aggform->partialagg_minversion;
+	}
+	return (fpinfo->server_version >= partialagg_minversion);
+}
+
+/*
+ * Check that partial aggregate agg is fine to push down.
+ *
+ * It is fine 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
+ *   (then we call this partialaggfunc) corresponding to agg.
+ *   condition2 is true when either of the following cases is true.
+ *   case2-1) return type of agg is not internal and agg has no finalfunc.
+ *     In this case, partialaggfunc is agg itself.
+ *   case2-2) agg has valid partialaggfn and partialagg_minversion <= server_version.
+ *     In this case, partialaggfunc is a aggregate function whose oid is partialaggfn.
+ */
+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->aggtranstype == INTERNALOID) || (aggform->aggfinalfn != InvalidOid)) {
+		/* Only aggregates which has partialaggfn, are allowed */
+		if (!aggform->partialaggfn) {
+			partial_agg_ok = false;
+		} else if (!partial_agg_compatible(aggform, 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 8f6a04f71b..ebdb7cff3b 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -9677,13 +9677,34 @@ RESET enable_partitionwise_join;
 -- ===================================================================
 -- test partitionwise aggregates
 -- ===================================================================
-CREATE TABLE pagg_tab (a int, b int, c text) PARTITION BY RANGE(a);
+CREATE OR REPLACE FUNCTION f_server_version_lag(version_offset int4) RETURNS text AS $$
+    SELECT (setting::int4 + version_offset)::text FROM pg_settings WHERE name = 'server_version_num';
+$$ LANGUAGE sql;
+CREATE OR REPLACE FUNCTION f_alter_server_version(server_name text, operation text, version_offset int4) RETURNS void AS $$
+    DECLARE
+        version_num text;
+    BEGIN
+        EXECUTE 'SELECT f_server_version_lag($1) '
+            INTO version_num
+            USING version_offset;
+        EXECUTE format('ALTER SERVER %I OPTIONS (%I server_version %L)',
+            server_name, operation, version_num);
+        RETURN;
+    END;
+$$ LANGUAGE plpgsql;
+SELECT f_alter_server_version('loopback', 'add', 0);
+ f_alter_server_version 
+------------------------
+ 
+(1 row)
+
+CREATE TABLE pagg_tab (a int, b int, c text, d int4) 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 % 40 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 % 40 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 % 40 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');
@@ -9742,8 +9763,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
@@ -9754,21 +9775,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, d 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, d 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, d 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;
@@ -9804,6 +9825,263 @@ 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, max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
+                                                    QUERY PLAN                                                    
+------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: pagg_tab.b, (max(pagg_tab.a)), (count(*))
+   Sort Key: pagg_tab.b
+   ->  Finalize HashAggregate
+         Output: pagg_tab.b, max(pagg_tab.a), count(*)
+         Group Key: pagg_tab.b
+         Filter: (sum(pagg_tab.a) < 700)
+         ->  Append
+               ->  Partial HashAggregate
+                     Output: pagg_tab.b, 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 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 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 fine to push down without having clause
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT b, min(a), max(a), count(*), sum(d), sum(d::int8), avg(d), avg(d::int8) FROM pagg_tab GROUP BY b ORDER BY 1;
+                                                                                                                           QUERY PLAN                                                                                                                           
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: pagg_tab.b, (min(pagg_tab.a)), (max(pagg_tab.a)), (count(*)), (sum(pagg_tab.d)), (sum((pagg_tab.d)::bigint)), (avg(pagg_tab.d)), (avg((pagg_tab.d)::bigint))
+   Sort Key: pagg_tab.b
+   ->  Finalize HashAggregate
+         Output: pagg_tab.b, min(pagg_tab.a), max(pagg_tab.a), count(*), sum(pagg_tab.d), sum((pagg_tab.d)::bigint), avg(pagg_tab.d), avg((pagg_tab.d)::bigint)
+         Group Key: pagg_tab.b
+         ->  Append
+               ->  Foreign Scan
+                     Output: pagg_tab.b, (PARTIAL min(pagg_tab.a)), (PARTIAL max(pagg_tab.a)), (PARTIAL count(*)), (PARTIAL sum(pagg_tab.d)), (PARTIAL sum((pagg_tab.d)::bigint)), (PARTIAL avg(pagg_tab.d)), (PARTIAL avg((pagg_tab.d)::bigint))
+                     Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab)
+                     Remote SQL: SELECT b, min(a), max(a), count(*), sum(d), sum_p_int8(d::bigint), avg_p_int4(d), avg_p_int8(d::bigint) FROM public.pagg_tab_p1 GROUP BY 1
+               ->  Foreign Scan
+                     Output: pagg_tab_1.b, (PARTIAL min(pagg_tab_1.a)), (PARTIAL max(pagg_tab_1.a)), (PARTIAL count(*)), (PARTIAL sum(pagg_tab_1.d)), (PARTIAL sum((pagg_tab_1.d)::bigint)), (PARTIAL avg(pagg_tab_1.d)), (PARTIAL avg((pagg_tab_1.d)::bigint))
+                     Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1)
+                     Remote SQL: SELECT b, min(a), max(a), count(*), sum(d), sum_p_int8(d::bigint), avg_p_int4(d), avg_p_int8(d::bigint) FROM public.pagg_tab_p2 GROUP BY 1
+               ->  Foreign Scan
+                     Output: pagg_tab_2.b, (PARTIAL min(pagg_tab_2.a)), (PARTIAL max(pagg_tab_2.a)), (PARTIAL count(*)), (PARTIAL sum(pagg_tab_2.d)), (PARTIAL sum((pagg_tab_2.d)::bigint)), (PARTIAL avg(pagg_tab_2.d)), (PARTIAL avg((pagg_tab_2.d)::bigint))
+                     Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2)
+                     Remote SQL: SELECT b, min(a), max(a), count(*), sum(d), sum_p_int8(d::bigint), avg_p_int4(d), avg_p_int8(d::bigint) FROM public.pagg_tab_p3 GROUP BY 1
+(19 rows)
+
+SELECT b, min(a), max(a), count(*), sum(d), sum(d::int8), avg(d), avg(d::int8) FROM pagg_tab GROUP BY b ORDER BY 1;
+ b  | min | max | count | sum  | sum  |         avg         |         avg         
+----+-----+-----+-------+------+------+---------------------+---------------------
+  0 |   0 |  20 |    60 |  900 |  900 | 15.0000000000000000 | 15.0000000000000000
+  1 |   1 |  21 |    60 |  960 |  960 | 16.0000000000000000 | 16.0000000000000000
+  2 |   2 |  22 |    60 | 1020 | 1020 | 17.0000000000000000 | 17.0000000000000000
+  3 |   3 |  23 |    60 | 1080 | 1080 | 18.0000000000000000 | 18.0000000000000000
+  4 |   4 |  24 |    60 | 1140 | 1140 | 19.0000000000000000 | 19.0000000000000000
+  5 |   5 |  25 |    60 | 1200 | 1200 | 20.0000000000000000 | 20.0000000000000000
+  6 |   6 |  26 |    60 | 1260 | 1260 | 21.0000000000000000 | 21.0000000000000000
+  7 |   7 |  27 |    60 | 1320 | 1320 | 22.0000000000000000 | 22.0000000000000000
+  8 |   8 |  28 |    60 | 1380 | 1380 | 23.0000000000000000 | 23.0000000000000000
+  9 |   9 |  29 |    60 | 1440 | 1440 | 24.0000000000000000 | 24.0000000000000000
+ 10 |   0 |  20 |    60 |  900 |  900 | 15.0000000000000000 | 15.0000000000000000
+ 11 |   1 |  21 |    60 |  960 |  960 | 16.0000000000000000 | 16.0000000000000000
+ 12 |   2 |  22 |    60 | 1020 | 1020 | 17.0000000000000000 | 17.0000000000000000
+ 13 |   3 |  23 |    60 | 1080 | 1080 | 18.0000000000000000 | 18.0000000000000000
+ 14 |   4 |  24 |    60 | 1140 | 1140 | 19.0000000000000000 | 19.0000000000000000
+ 15 |   5 |  25 |    60 | 1200 | 1200 | 20.0000000000000000 | 20.0000000000000000
+ 16 |   6 |  26 |    60 | 1260 | 1260 | 21.0000000000000000 | 21.0000000000000000
+ 17 |   7 |  27 |    60 | 1320 | 1320 | 22.0000000000000000 | 22.0000000000000000
+ 18 |   8 |  28 |    60 | 1380 | 1380 | 23.0000000000000000 | 23.0000000000000000
+ 19 |   9 |  29 |    60 | 1440 | 1440 | 24.0000000000000000 | 24.0000000000000000
+ 20 |   0 |  20 |    60 |  900 |  900 | 15.0000000000000000 | 15.0000000000000000
+ 21 |   1 |  21 |    60 |  960 |  960 | 16.0000000000000000 | 16.0000000000000000
+ 22 |   2 |  22 |    60 | 1020 | 1020 | 17.0000000000000000 | 17.0000000000000000
+ 23 |   3 |  23 |    60 | 1080 | 1080 | 18.0000000000000000 | 18.0000000000000000
+ 24 |   4 |  24 |    60 | 1140 | 1140 | 19.0000000000000000 | 19.0000000000000000
+ 25 |   5 |  25 |    60 | 1200 | 1200 | 20.0000000000000000 | 20.0000000000000000
+ 26 |   6 |  26 |    60 | 1260 | 1260 | 21.0000000000000000 | 21.0000000000000000
+ 27 |   7 |  27 |    60 | 1320 | 1320 | 22.0000000000000000 | 22.0000000000000000
+ 28 |   8 |  28 |    60 | 1380 | 1380 | 23.0000000000000000 | 23.0000000000000000
+ 29 |   9 |  29 |    60 | 1440 | 1440 | 24.0000000000000000 | 24.0000000000000000
+ 30 |   0 |  20 |    60 |  900 |  900 | 15.0000000000000000 | 15.0000000000000000
+ 31 |   1 |  21 |    60 |  960 |  960 | 16.0000000000000000 | 16.0000000000000000
+ 32 |   2 |  22 |    60 | 1020 | 1020 | 17.0000000000000000 | 17.0000000000000000
+ 33 |   3 |  23 |    60 | 1080 | 1080 | 18.0000000000000000 | 18.0000000000000000
+ 34 |   4 |  24 |    60 | 1140 | 1140 | 19.0000000000000000 | 19.0000000000000000
+ 35 |   5 |  25 |    60 | 1200 | 1200 | 20.0000000000000000 | 20.0000000000000000
+ 36 |   6 |  26 |    60 | 1260 | 1260 | 21.0000000000000000 | 21.0000000000000000
+ 37 |   7 |  27 |    60 | 1320 | 1320 | 22.0000000000000000 | 22.0000000000000000
+ 38 |   8 |  28 |    60 | 1380 | 1380 | 23.0000000000000000 | 23.0000000000000000
+ 39 |   9 |  29 |    60 | 1440 | 1440 | 24.0000000000000000 | 24.0000000000000000
+ 40 |   0 |  20 |    60 |  900 |  900 | 15.0000000000000000 | 15.0000000000000000
+ 41 |   1 |  21 |    60 |  960 |  960 | 16.0000000000000000 | 16.0000000000000000
+ 42 |   2 |  22 |    60 | 1020 | 1020 | 17.0000000000000000 | 17.0000000000000000
+ 43 |   3 |  23 |    60 | 1080 | 1080 | 18.0000000000000000 | 18.0000000000000000
+ 44 |   4 |  24 |    60 | 1140 | 1140 | 19.0000000000000000 | 19.0000000000000000
+ 45 |   5 |  25 |    60 | 1200 | 1200 | 20.0000000000000000 | 20.0000000000000000
+ 46 |   6 |  26 |    60 | 1260 | 1260 | 21.0000000000000000 | 21.0000000000000000
+ 47 |   7 |  27 |    60 | 1320 | 1320 | 22.0000000000000000 | 22.0000000000000000
+ 48 |   8 |  28 |    60 | 1380 | 1380 | 23.0000000000000000 | 23.0000000000000000
+ 49 |   9 |  29 |    60 | 1440 | 1440 | 24.0000000000000000 | 24.0000000000000000
+(50 rows)
+
+-- Partial aggregates are fine to push down
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT min(a), max(a), count(*), sum(d), sum(d::int8), avg(d), avg(d::int8) FROM pagg_tab;
+                                                                                                                 QUERY PLAN                                                                                                                 
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: min(pagg_tab.a), max(pagg_tab.a), count(*), sum(pagg_tab.d), sum((pagg_tab.d)::bigint), avg(pagg_tab.d), avg((pagg_tab.d)::bigint)
+   ->  Append
+         ->  Foreign Scan
+               Output: (PARTIAL min(pagg_tab.a)), (PARTIAL max(pagg_tab.a)), (PARTIAL count(*)), (PARTIAL sum(pagg_tab.d)), (PARTIAL sum((pagg_tab.d)::bigint)), (PARTIAL avg(pagg_tab.d)), (PARTIAL avg((pagg_tab.d)::bigint))
+               Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab)
+               Remote SQL: SELECT min(a), max(a), count(*), sum(d), sum_p_int8(d::bigint), avg_p_int4(d), avg_p_int8(d::bigint) FROM public.pagg_tab_p1
+         ->  Foreign Scan
+               Output: (PARTIAL min(pagg_tab_1.a)), (PARTIAL max(pagg_tab_1.a)), (PARTIAL count(*)), (PARTIAL sum(pagg_tab_1.d)), (PARTIAL sum((pagg_tab_1.d)::bigint)), (PARTIAL avg(pagg_tab_1.d)), (PARTIAL avg((pagg_tab_1.d)::bigint))
+               Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1)
+               Remote SQL: SELECT min(a), max(a), count(*), sum(d), sum_p_int8(d::bigint), avg_p_int4(d), avg_p_int8(d::bigint) FROM public.pagg_tab_p2
+         ->  Foreign Scan
+               Output: (PARTIAL min(pagg_tab_2.a)), (PARTIAL max(pagg_tab_2.a)), (PARTIAL count(*)), (PARTIAL sum(pagg_tab_2.d)), (PARTIAL sum((pagg_tab_2.d)::bigint)), (PARTIAL avg(pagg_tab_2.d)), (PARTIAL avg((pagg_tab_2.d)::bigint))
+               Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2)
+               Remote SQL: SELECT min(a), max(a), count(*), sum(d), sum_p_int8(d::bigint), avg_p_int4(d), avg_p_int8(d::bigint) FROM public.pagg_tab_p3
+(15 rows)
+
+SELECT min(a), max(a), count(*), sum(d), sum(d::int8), avg(d), avg(d::int8) FROM pagg_tab;
+ min | max | count |  sum  |  sum  |         avg         |         avg         
+-----+-----+-------+-------+-------+---------------------+---------------------
+   0 |  29 |  3000 | 58500 | 58500 | 19.5000000000000000 | 19.5000000000000000
+(1 row)
+
+-- It's unsafe to push down partial aggregates which contains 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 when
+-- server_version is older than partialagg_minversion, described by pg_aggregate
+SELECT f_alter_server_version('loopback', 'set', -1);
+ f_alter_server_version 
+------------------------
+ 
+(1 row)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(d) FROM pagg_tab;
+                            QUERY PLAN                            
+------------------------------------------------------------------
+ Finalize Aggregate
+   Output: avg(pagg_tab.d)
+   ->  Append
+         ->  Partial Aggregate
+               Output: PARTIAL avg(pagg_tab.d)
+               ->  Foreign Scan on public.fpagg_tab_p1 pagg_tab
+                     Output: pagg_tab.d
+                     Remote SQL: SELECT d FROM public.pagg_tab_p1
+         ->  Partial Aggregate
+               Output: PARTIAL avg(pagg_tab_1.d)
+               ->  Foreign Scan on public.fpagg_tab_p2 pagg_tab_1
+                     Output: pagg_tab_1.d
+                     Remote SQL: SELECT d FROM public.pagg_tab_p2
+         ->  Partial Aggregate
+               Output: PARTIAL avg(pagg_tab_2.d)
+               ->  Foreign Scan on public.fpagg_tab_p3 pagg_tab_2
+                     Output: pagg_tab_2.d
+                     Remote SQL: SELECT d FROM public.pagg_tab_p3
+(18 rows)
+
+SELECT avg(d) FROM pagg_tab;
+         avg         
+---------------------
+ 19.5000000000000000
+(1 row)
+
+-- It's safe to push down partial aggregate for user defined aggregate
+-- which has valid options.
+CREATE AGGREGATE postgres_fdw_sum_p_int8(int8) (
+	SFUNC = int8_avg_accum,
+	STYPE = internal,
+	FINALFUNC = int8_avg_serialize
+);
+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,
+	PARTIALAGGFUNC = postgres_fdw_sum_p_int8,
+	PARTIALAGG_MINVERSION = 150000
+);
+ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_sum(int8);
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT postgres_fdw_sum(d::int8) FROM pagg_tab;
+                                             QUERY PLAN                                             
+----------------------------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: postgres_fdw_sum((pagg_tab.d)::bigint)
+   ->  Append
+         ->  Foreign Scan
+               Output: (PARTIAL postgres_fdw_sum((pagg_tab.d)::bigint))
+               Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab)
+               Remote SQL: SELECT public.postgres_fdw_sum_p_int8(d::bigint) FROM public.pagg_tab_p1
+         ->  Foreign Scan
+               Output: (PARTIAL postgres_fdw_sum((pagg_tab_1.d)::bigint))
+               Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1)
+               Remote SQL: SELECT public.postgres_fdw_sum_p_int8(d::bigint) FROM public.pagg_tab_p2
+         ->  Foreign Scan
+               Output: (PARTIAL postgres_fdw_sum((pagg_tab_2.d)::bigint))
+               Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2)
+               Remote SQL: SELECT public.postgres_fdw_sum_p_int8(d::bigint) FROM public.pagg_tab_p3
+(15 rows)
+
+SELECT postgres_fdw_sum(d::int8) FROM pagg_tab;
+ postgres_fdw_sum 
+------------------
+            58500
+(1 row)
+
+ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_sum(int8);
+DROP AGGREGATE postgres_fdw_sum(int8);
+DROP AGGREGATE postgres_fdw_sum_p_int8(int8);
+DROP FUNCTION f_server_version_lag(int4);
+DROP FUNCTION f_alter_server_version(text, text, int4);
+ALTER SERVER loopback OPTIONS (DROP server_version);
 -- ===================================================================
 -- access rights and superuser
 -- ===================================================================
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 4229d2048c..75283fa2d8 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -211,6 +211,27 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
 						 errmsg("sslcert and sslkey are superuser-only"),
 						 errhint("User mappings with the sslcert or sslkey options set may only be created or modified by the superuser.")));
 		}
+		else if (strcmp(def->defname, "server_version") == 0)
+		{
+			char* value;
+			int			int_val;
+			bool		is_parsed;
+
+			value = defGetString(def);
+			is_parsed = parse_int(value, &int_val, 0, NULL);
+
+			if (!is_parsed)
+				ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg("invalid value for integer option \"%s\": %s",
+							def->defname, value)));
+
+			if (int_val <= 0)
+				ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg("\"%s\" must be an integer value greater than zero",
+							def->defname)));
+		}
 		else if (strcmp(def->defname, "analyze_sampling") == 0)
 		{
 			char	   *value;
@@ -288,6 +309,9 @@ InitPgFdwOptions(void)
 		{"sslcert", UserMappingRelationId, true},
 		{"sslkey", UserMappingRelationId, true},
 
+		/* options for partial aggregation pushdown */
+		{"server_version", ForeignServerRelationId, false},
+
 		{NULL, InvalidOid, false}
 	};
 
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index d728bd70b3..7b51acadf3 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);
@@ -651,6 +651,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	fpinfo->shippable_extensions = NIL;
 	fpinfo->fetch_size = 100;
 	fpinfo->async_capable = false;
+	fpinfo->server_version = 0;
 
 	apply_server_options(fpinfo);
 	apply_table_options(fpinfo);
@@ -6130,6 +6131,8 @@ apply_server_options(PgFdwRelationInfo *fpinfo)
 			(void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
 		else if (strcmp(def->defname, "async_capable") == 0)
 			fpinfo->async_capable = defGetBoolean(def);
+		else if (strcmp(def->defname, "server_version") == 0)
+			(void)parse_int(defGetString(def), &fpinfo->server_version, 0, NULL);
 	}
 }
 
@@ -6188,6 +6191,7 @@ merge_fdw_options(PgFdwRelationInfo *fpinfo,
 	fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
 	fpinfo->fetch_size = fpinfo_o->fetch_size;
 	fpinfo->async_capable = fpinfo_o->async_capable;
+	fpinfo->server_version = fpinfo_o->server_version;
 
 	/* Merge the table level options from either side of the join. */
 	if (fpinfo_i)
@@ -6367,7 +6371,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;
@@ -6381,6 +6385,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;
 
@@ -6619,6 +6627,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)
@@ -6635,6 +6644,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;
@@ -6675,7 +6688,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;
@@ -6695,7 +6709,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/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index 02c1152319..616f559080 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -88,6 +88,9 @@ typedef struct PgFdwRelationInfo
 
 	int			fetch_size;		/* fetch size for this remote table */
 
+	/* Options for checking compatibility of partial aggregation */
+	int			server_version;
+
 	/*
 	 * Name of the relation, for use while EXPLAINing ForeignScan.  It is used
 	 * for join and upper relations but is set for all relations.  For a base
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 5bd69339df..dcbf60d919 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -2944,16 +2944,34 @@ RESET enable_partitionwise_join;
 -- ===================================================================
 -- test partitionwise aggregates
 -- ===================================================================
+CREATE OR REPLACE FUNCTION f_server_version_lag(version_offset int4) RETURNS text AS $$
+    SELECT (setting::int4 + version_offset)::text FROM pg_settings WHERE name = 'server_version_num';
+$$ LANGUAGE sql;
 
-CREATE TABLE pagg_tab (a int, b int, c text) PARTITION BY RANGE(a);
+CREATE OR REPLACE FUNCTION f_alter_server_version(server_name text, operation text, version_offset int4) RETURNS void AS $$
+    DECLARE
+        version_num text;
+    BEGIN
+        EXECUTE 'SELECT f_server_version_lag($1) '
+            INTO version_num
+            USING version_offset;
+        EXECUTE format('ALTER SERVER %I OPTIONS (%I server_version %L)',
+            server_name, operation, version_num);
+        RETURN;
+    END;
+$$ LANGUAGE plpgsql;
+
+SELECT f_alter_server_version('loopback', 'add', 0);
+
+CREATE TABLE pagg_tab (a int, b int, c text, d int4) 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 % 40 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 % 40 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 % 40 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');
@@ -2987,6 +3005,65 @@ 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, max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
+
+-- Partial aggregates are fine to push down without having clause
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT b, min(a), max(a), count(*), sum(d), sum(d::int8), avg(d), avg(d::int8) FROM pagg_tab GROUP BY b ORDER BY 1;
+SELECT b, min(a), max(a), count(*), sum(d), sum(d::int8), avg(d), avg(d::int8) FROM pagg_tab GROUP BY b ORDER BY 1;
+
+-- Partial aggregates are fine to push down
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT min(a), max(a), count(*), sum(d), sum(d::int8), avg(d), avg(d::int8) FROM pagg_tab;
+SELECT min(a), max(a), count(*), sum(d), sum(d::int8), avg(d), avg(d::int8) FROM pagg_tab;
+
+-- It's unsafe to push down partial aggregates which contains 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 when
+-- server_version is older than partialagg_minversion, described by pg_aggregate
+SELECT f_alter_server_version('loopback', 'set', -1);
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(d) FROM pagg_tab;
+SELECT avg(d) FROM pagg_tab;
+
+-- It's safe to push down partial aggregate for user defined aggregate
+-- which has valid options.
+CREATE AGGREGATE postgres_fdw_sum_p_int8(int8) (
+	SFUNC = int8_avg_accum,
+	STYPE = internal,
+	FINALFUNC = int8_avg_serialize
+);
+
+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,
+	PARTIALAGGFUNC = postgres_fdw_sum_p_int8,
+	PARTIALAGG_MINVERSION = 150000
+);
+
+ALTER EXTENSION postgres_fdw ADD FUNCTION postgres_fdw_sum(int8);
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT postgres_fdw_sum(d::int8) FROM pagg_tab;
+SELECT postgres_fdw_sum(d::int8) FROM pagg_tab;
+
+ALTER EXTENSION postgres_fdw DROP FUNCTION postgres_fdw_sum(int8);
+DROP AGGREGATE postgres_fdw_sum(int8);
+DROP AGGREGATE postgres_fdw_sum_p_int8(int8);
+
+DROP FUNCTION f_server_version_lag(int4);
+DROP FUNCTION f_alter_server_version(text, text, int4);
+ALTER SERVER loopback OPTIONS (DROP server_version);
+
 -- ===================================================================
 -- access rights and superuser
 -- ===================================================================
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index ebc4454743..037eb06f4d 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 *partialaggfnName,
 				bool finalfnExtraArgs,
 				bool mfinalfnExtraArgs,
 				char finalfnModify,
@@ -72,6 +73,7 @@ AggregateCreate(const char *aggName,
 				int32 aggTransSpace,
 				Oid aggmTransType,
 				int32 aggmTransSpace,
+				int32 partialaggMinversion,
 				const char *agginitval,
 				const char *aggminitval,
 				char proparallel)
@@ -91,6 +93,7 @@ AggregateCreate(const char *aggName,
 	Oid			mtransfn = InvalidOid;	/* can be omitted */
 	Oid			minvtransfn = InvalidOid;	/* can be omitted */
 	Oid			mfinalfn = InvalidOid;	/* can be omitted */
+	Oid			partialaggfn = InvalidOid;	/* can be omitted */
 	Oid			sortop = InvalidOid;	/* can be omitted */
 	Oid		   *aggArgTypes = parameterTypes->values;
 	bool		mtransIsStrict = false;
@@ -569,6 +572,33 @@ AggregateCreate(const char *aggName,
 							format_type_be(finaltype))));
 	}
 
+	/*
+	 * Validate the partial aggregate function, if present.
+	 */
+	if (partialaggfnName)
+	{
+		HeapTuple aggtup;
+		partialaggfn = LookupFuncName(partialaggfnName, numArgs, aggArgTypes, false);
+
+		/* Check aggregate creator has permission to call the function */
+		aclresult = object_aclcheck(ProcedureRelationId, partialaggfn, GetUserId(), ACL_EXECUTE);
+		if (aclresult != ACLCHECK_OK)
+			aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(partialaggfn));
+
+		rettype = get_func_rettype(partialaggfn);
+
+		if (((aggTransType == INTERNALOID) && (rettype != BYTEAOID))
+			|| ((aggTransType != INTERNALOID) && (rettype != aggTransType)))
+			ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+					errmsg("return type of partialaggfunc of %s is not stype",
+						NameListToString(partialaggfnName))));
+		aggtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(partialaggfn));
+		if (!HeapTupleIsValid(aggtup))
+			elog(ERROR, "cache lookup failed for partialaggfunc %u", partialaggfn);
+		ReleaseSysCache(aggtup);
+	}
+
 	/* handle sortop, if supplied */
 	if (aggsortopName)
 	{
@@ -684,6 +714,8 @@ AggregateCreate(const char *aggName,
 		values[Anum_pg_aggregate_aggminitval - 1] = CStringGetTextDatum(aggminitval);
 	else
 		nulls[Anum_pg_aggregate_aggminitval - 1] = true;
+	values[Anum_pg_aggregate_partialaggfn - 1] = ObjectIdGetDatum(partialaggfn);
+	values[Anum_pg_aggregate_partialagg_minversion - 1] = Int32GetDatum(partialaggMinversion);
 
 	if (replace)
 		oldtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(procOid));
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c
index fda9d1aa77..cb49a79cfa 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	   *partialaggfuncName = NIL;
 	List	   *mtransfuncName = NIL;
 	List	   *minvtransfuncName = NIL;
 	List	   *mfinalfuncName = NIL;
@@ -83,6 +84,7 @@ DefineAggregate(ParseState *pstate,
 	TypeName   *mtransType = NULL;
 	int32		transSpace = 0;
 	int32		mtransSpace = 0;
+	int32		partialaggMinversion = 0;
 	char	   *initval = NULL;
 	char	   *minitval = NULL;
 	char	   *parallel = NULL;
@@ -143,6 +145,8 @@ DefineAggregate(ParseState *pstate,
 			serialfuncName = defGetQualifiedName(defel);
 		else if (strcmp(defel->defname, "deserialfunc") == 0)
 			deserialfuncName = defGetQualifiedName(defel);
+		else if (strcmp(defel->defname, "partialaggfunc") == 0)
+			partialaggfuncName = defGetQualifiedName(defel);
 		else if (strcmp(defel->defname, "msfunc") == 0)
 			mtransfuncName = defGetQualifiedName(defel);
 		else if (strcmp(defel->defname, "minvfunc") == 0)
@@ -182,6 +186,8 @@ DefineAggregate(ParseState *pstate,
 			mtransType = defGetTypeName(defel);
 		else if (strcmp(defel->defname, "msspace") == 0)
 			mtransSpace = defGetInt32(defel);
+		else if (strcmp(defel->defname, "partialagg_minversion") == 0)
+			partialaggMinversion = defGetInt32(defel);
 		else if (strcmp(defel->defname, "initcond") == 0)
 			initval = defGetString(defel);
 		else if (strcmp(defel->defname, "initcond1") == 0)
@@ -461,6 +467,7 @@ DefineAggregate(ParseState *pstate,
 						   mtransfuncName,	/* fwd trans function name */
 						   minvtransfuncName,	/* inv trans function name */
 						   mfinalfuncName,	/* final function name */
+						   partialaggfuncName,
 						   finalfuncExtraArgs,
 						   mfinalfuncExtraArgs,
 						   finalfuncModify,
@@ -470,6 +477,7 @@ DefineAggregate(ParseState *pstate,
 						   transSpace,	/* transition space */
 						   mtransTypeId,	/* transition data type */
 						   mtransSpace, /* transition space */
+						   partialaggMinversion,
 						   initval, /* initial condition */
 						   minitval,	/* initial condition */
 						   proparallel);	/* parallel safe? */
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 7a504dfe25..80165ada03 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -13693,6 +13693,8 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 	const char *aggmtransfn;
 	const char *aggminvtransfn;
 	const char *aggmfinalfn;
+	const char *partialaggfn;
+	const char *partialagg_minversion;
 	bool		aggfinalextra;
 	bool		aggmfinalextra;
 	char		aggfinalmodify;
@@ -13775,11 +13777,19 @@ 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");
+		if (fout->remoteVersion >= 160000)
+			appendPQExpBufferStr(query,
+								"partialaggfn,\n"
+								"partialagg_minversion\n");
+		else
+			appendPQExpBufferStr(query,
+								"'-' AS partialaggfn,\n"
+								"0 AS partialagg_minversion\n");
 
 		appendPQExpBufferStr(query,
 							 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
@@ -13805,6 +13815,8 @@ 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"));
+	partialaggfn = PQgetvalue(res, 0, PQfnumber(res, "partialaggfn"));
+	partialagg_minversion = PQgetvalue(res, 0, PQfnumber(res, "partialagg_minversion"));
 	aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
 	aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
 	aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
@@ -13894,6 +13906,15 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 	if (strcmp(aggdeserialfn, "-") != 0)
 		appendPQExpBuffer(details, ",\n    DESERIALFUNC = %s", aggdeserialfn);
 
+	if (strcmp(partialaggfn, "-") != 0)
+		appendPQExpBuffer(details, ",\n    PARTIALAGGFUNC = %s", partialaggfn);
+
+	if (strcmp(partialagg_minversion, "0") != 0)
+	{
+		appendPQExpBuffer(details, ",\n    PARTIALAGG_MINVERSION = %s",
+						  partialagg_minversion);
+	}
+
 	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 283f494bf5..689ed3546a 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' },
 { 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',
+  partialaggfn => 'avg_p_int8', partialagg_minversion => '160000' },
+{ aggfnoid => 'avg_p_int4', aggtransfn => 'int4_avg_accum',
+  aggcombinefn => 'int4_avg_combine',
+  aggtranstype => '_int8',
+  agginitval => '{0,0}' },
 { 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}',
+  partialaggfn => 'avg_p_int4', partialagg_minversion => '160000' },
+{ aggfnoid => 'avg_p_int2', aggtransfn => 'int2_avg_accum',
+  aggcombinefn => 'int4_avg_combine',
+  aggtranstype => '_int8',
+  agginitval => '{0,0}'  },
 { 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}',
+  partialaggfn => 'avg_p_int2', partialagg_minversion => '160000'  },
+{ 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',
+  partialaggfn => 'avg_p_numeric', partialagg_minversion => '160000' },
 { aggfnoid => 'avg(numeric)', aggtransfn => 'numeric_avg_accum',
   aggfinalfn => 'numeric_avg', aggcombinefn => 'numeric_avg_combine',
   aggserialfn => 'numeric_avg_serialize',
@@ -36,27 +58,45 @@
   aggmtransfn => 'numeric_avg_accum', aggminvtransfn => 'numeric_accum_inv',
   aggmfinalfn => 'numeric_avg', aggtranstype => 'internal',
   aggtransspace => '128', aggmtranstype => 'internal',
-  aggmtransspace => '128' },
+  aggmtransspace => '128',
+  partialaggfn => 'avg_p_numeric', partialagg_minversion => '160000' },
+{ aggfnoid => 'avg_p_float4', aggtransfn => 'float4_accum',
+  aggcombinefn => 'float8_combine',
+  aggtranstype => '_float8', agginitval => '{0,0,0}' },
 { aggfnoid => 'avg(float4)', aggtransfn => 'float4_accum',
   aggfinalfn => 'float8_avg', aggcombinefn => 'float8_combine',
+  aggtranstype => '_float8', agginitval => '{0,0,0}',
+  partialaggfn => 'avg_p_float4', partialagg_minversion => '160000' },
+{ aggfnoid => 'avg_p_float8', aggtransfn => 'float8_accum',
+  aggcombinefn => 'float8_combine',
   aggtranstype => '_float8', agginitval => '{0,0,0}' },
 { aggfnoid => 'avg(float8)', aggtransfn => 'float8_accum',
   aggfinalfn => 'float8_avg', aggcombinefn => 'float8_combine',
-  aggtranstype => '_float8', agginitval => '{0,0,0}' },
+  aggtranstype => '_float8', agginitval => '{0,0,0}',
+  partialaggfn => 'avg_p_float8', partialagg_minversion => '160000'  },
+{ aggfnoid => 'avg_p_interval', aggtransfn => 'interval_accum',
+  aggcombinefn => 'interval_combine',
+  aggtranstype => '_interval', agginitval => '{0 second,0 second}'  },
 { 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}',
+  partialaggfn => 'avg_p_interval', partialagg_minversion => '160000'  },
 
 # 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' },
 { 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',
+  partialaggfn => 'sum_p_int8', partialagg_minversion => '160000' },
 { aggfnoid => 'sum(int4)', aggtransfn => 'int4_sum', aggcombinefn => 'int8pl',
   aggmtransfn => 'int4_avg_accum', aggminvtransfn => 'int4_avg_accum_inv',
   aggmfinalfn => 'int2int4_sum', aggtranstype => 'int8',
@@ -76,6 +116,11 @@
   aggcombinefn => 'interval_pl', aggmtransfn => 'interval_pl',
   aggminvtransfn => 'interval_mi', aggtranstype => 'interval',
   aggmtranstype => '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' },
 { aggfnoid => 'sum(numeric)', aggtransfn => 'numeric_avg_accum',
   aggfinalfn => 'numeric_sum', aggcombinefn => 'numeric_avg_combine',
   aggserialfn => 'numeric_avg_serialize',
@@ -83,7 +128,8 @@
   aggmtransfn => 'numeric_avg_accum', aggminvtransfn => 'numeric_accum_inv',
   aggmfinalfn => 'numeric_sum', aggtranstype => 'internal',
   aggtransspace => '128', aggmtranstype => 'internal',
-  aggmtransspace => '128' },
+  aggmtransspace => '128',
+  partialaggfn => 'sum_p_numeric', partialagg_minversion => '160000' },
 
 # max
 { aggfnoid => 'max(int8)', aggtransfn => 'int8larger',
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 3112881193..1cefa1bfba 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -55,6 +55,12 @@ 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		partialaggfn BKI_DEFAULT(-) BKI_LOOKUP_OPT(pg_proc);
+
+	/* minimum PostgreSQL's version which has compatible partialaggfn */
+	int32		partialagg_minversion BKI_DEFAULT(0);
+
 	/* forward function for moving-aggregate mode (0 if none) */
 	regproc		aggmtransfn BKI_DEFAULT(-) BKI_LOOKUP_OPT(pg_proc);
 
@@ -141,6 +147,9 @@ DECLARE_UNIQUE_INDEX_PKEY(pg_aggregate_fnoid_index, 2650, AggregateFnoidIndexId,
 #define AGGMODIFY_SHAREABLE			's'
 #define AGGMODIFY_READ_WRITE		'w'
 
+/* Symbolic value for default partialagg_minversion */
+#define	PARTIALAGG_MINVERSION_DEFAULT	0
+
 #endif							/* EXPOSE_TO_CLIENT_CODE */
 
 
@@ -164,6 +173,7 @@ extern ObjectAddress AggregateCreate(const char *aggName,
 									 List *aggmtransfnName,
 									 List *aggminvtransfnName,
 									 List *aggmfinalfnName,
+									 List *aggpartialfnName,
 									 bool finalfnExtraArgs,
 									 bool mfinalfnExtraArgs,
 									 char finalfnModify,
@@ -173,6 +183,7 @@ extern ObjectAddress AggregateCreate(const char *aggName,
 									 int32 aggTransSpace,
 									 Oid aggmTransType,
 									 int32 aggmTransSpace,
+									 int32 partialaggMinversion,
 									 const char *agginitval,
 									 const char *aggminitval,
 									 char proparallel);
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index f9f2642201..2f27b54133 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6594,35 +6594,67 @@
   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 => '13500',
+  descr => 'the partial average (arithmetic mean) as numeric of all bigint values',
+  proname => 'avg_p_int8', prokind => 'a', proisstrict => 'f', prorettype => 'bytea',
+  proargtypes => 'int8', 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 => '13501',
+  descr => 'the partial average (arithmetic mean) as numeric of all integer values',
+  proname => 'avg_p_int4', prokind => 'a', proisstrict => 'f', prorettype => '_int8',
+  proargtypes => 'int4', 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 => '13502',
+  descr => 'the partial average (arithmetic mean) as numeric of all smallint values',
+  proname => 'avg_p_int2', prokind => 'a', proisstrict => 'f', prorettype => '_int8',
+  proargtypes => 'int2', 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 => '13503',
+  descr => 'the partial average (arithmetic mean) as numeric of all numeric values',
+  proname => 'avg_p_numeric', prokind => 'a', proisstrict => 'f', prorettype => 'bytea',
+  proargtypes => 'numeric', 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 => '13504',
+  descr => 'the partial average (arithmetic mean) as float8 of all float4 values',
+  proname => 'avg_p_float4', prokind => 'a', proisstrict => 'f', prorettype => '_float8',
+  proargtypes => 'float4', 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 => '13505',
+  descr => 'the partial average (arithmetic mean) as float8 of all float8 values',
+  proname => 'avg_p_float8', prokind => 'a', proisstrict => 'f', prorettype => '_float8',
+  proargtypes => 'float8', 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 => '13506',
+  descr => 'the partial average (arithmetic mean) as interval of all interval values',
+  proname => 'avg_p_interval', prokind => 'a', proisstrict => 'f',
+  prorettype => '_interval', proargtypes => 'interval',
+  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' },
+{ oid => '13507', descr => 'partial sum as numeric across all bigint input values',
+  proname => 'sum_p_int8', prokind => 'a', proisstrict => 'f', prorettype => 'bytea',
+  proargtypes => 'int8', prosrc => 'aggregate_dummy' },
 { oid => '2108', descr => 'sum as bigint across all integer input values',
   proname => 'sum', prokind => 'a', proisstrict => 'f', prorettype => 'int8',
   proargtypes => 'int4', prosrc => 'aggregate_dummy' },
@@ -6645,6 +6677,9 @@
 { oid => '2114', descr => 'sum as numeric across all numeric input values',
   proname => 'sum', prokind => 'a', proisstrict => 'f', prorettype => 'numeric',
   proargtypes => 'numeric', prosrc => 'aggregate_dummy' },
+{ oid => '13508', descr => 'partial sum as numeric across all numeric input values',
+  proname => 'sum_p_numeric', prokind => 'a', proisstrict => 'f', prorettype => 'bytea',
+  proargtypes => 'numeric', prosrc => 'aggregate_dummy' },
 
 { oid => '2115', descr => 'maximum value of all bigint input values',
   proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'int8',
diff --git a/src/test/regress/expected/create_aggregate.out b/src/test/regress/expected/create_aggregate.out
index dcf6909423..3197c8abba 100644
--- a/src/test/regress/expected/create_aggregate.out
+++ b/src/test/regress/expected/create_aggregate.out
@@ -322,3 +322,27 @@ 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: partialaggfunc which doesn't exist
+CREATE AGGREGATE haspartialagg_agg(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	combinefunc = int8_avg_combine,
+	finalfunc = numeric_poly_sum,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = int8_avg_deserialize,
+	partialaggfunc = partialagg_foo,
+	partialagg_minversion = 160000
+);
+ERROR:  function partialagg_foo(bigint) does not exist
+-- invalid: partialagg_minversion is not integer
+CREATE AGGREGATE haspartialagg_agg(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	combinefunc = int8_avg_combine,
+	finalfunc = numeric_poly_sum,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = int8_avg_deserialize,
+	partialaggfunc = partialagg_foo,
+	partialagg_minversion = aaa
+);
+ERROR:  partialagg_minversion requires an integer value
diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out
index 215eb899be..49f2c9bc35 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 {partialaggfn} => 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..499c120ca5 100644
--- a/src/test/regress/sql/create_aggregate.sql
+++ b/src/test/regress/sql/create_aggregate.sql
@@ -328,3 +328,27 @@ CREATE AGGREGATE case_agg(float8)
 	"Finalfunc_modify" = read_write,
 	"Parallel" = safe
 );
+
+-- invalid: partialaggfunc which doesn't exist
+CREATE AGGREGATE haspartialagg_agg(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	combinefunc = int8_avg_combine,
+	finalfunc = numeric_poly_sum,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = int8_avg_deserialize,
+	partialaggfunc = partialagg_foo,
+	partialagg_minversion = 160000
+);
+
+-- invalid: partialagg_minversion is not integer
+CREATE AGGREGATE haspartialagg_agg(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	combinefunc = int8_avg_combine,
+	finalfunc = numeric_poly_sum,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = int8_avg_deserialize,
+	partialaggfunc = partialagg_foo,
+	partialagg_minversion = aaa
+);
\ No newline at end of file


^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* RE: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
@ 2023-04-07 09:25   ` [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  0 siblings, 1 reply; 35+ messages in thread

From: [email protected] @ 2023-04-07 09:25 UTC (permalink / raw)
  To: Bruce Momjian <[email protected]>; +Cc: Andres Freund <[email protected]>; Alexander Pyhalov <[email protected]>; Tomas Vondra <[email protected]>; pgsql-hackers; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>; [email protected] <[email protected]>

Hi Mr.Momjian

> First, my apologies for not addressing this sooner.  I was so focused on my
> own tasks that I didn't realize this very important patch was not getting
> attention.  I will try my best to get it into PG 17.
Thank you very much for your comments. 
I will improve this patch for PG17.
I believe that this patch will help us use PostgreSQL's built-in sharding for OLAP.

> What amazes me is that you didn't need to create _any_ actual aggregate
> functions.  Rather, you just needed to hook existing functions into the
> aggregate tables for partial FDW execution.
Yes. This patch enables partial aggregate pushdown using 
only existing functions which belong to existing aggregate function
and are needed by parallel query(such as state transition function and serialization function).
This patch does not need new types of function belonging to aggregate functions
and does not need new functions belonging to existing aggregate functions.

> I suggest we remove the version check requirement --- instead just document
> that the FDW Postgres version should be the same or newer than the calling
> Postgres server --- that way, we can assume that whatever is in the system
> catalogs of the caller is in the receiving side.  
Thanks for the comment. I will modify this patch according to your comment.

> We should add a GUC to turn off
> this optimization for cases where the FDW Postgres version is older than the
> caller.  This handles case 1-2.
Thanks for the advice here too.
I thought it would be more appropriate to add a foregin server option of 
postgres_fdw rather than adding GUC. 
Would you mind if I ask you what you think about it?

> > 2. Automation of creating definition of partialaggfuncs In development
> > of v17, I manually create definition of partialaggfuncs for avg, min, max, sum,
> count.
> > I am concerned that this may be undesirable.
> > So I am thinking that v17 should be modified to automate creating
> > definition of partialaggfuncs for all built-in aggregate functions.
> 
> Are there any other builtin functions that need this?  I think we can just
> provide documention for extensions on how to do this.
For practical purposes, it is sufficient 
if partial aggregate for the above functions can be pushed down.
I think you are right, it would be sufficient to document how to achieve
 partial aggregate pushdown for other built-in functions.

> > 3. Documentation
> > I need add explanation of partialaggfunc to documents on postgres_fdw and
> other places.
> 
> I can help with that once we decide on the above.
Thank you. In the next verion of this patch, I will add documents on postgres_fdw
and other places. 

> I think 'partialaggfn' should be named 'aggpartialfn' to match other columns in
> pg_aggregate.
Thanks for the comment. I will modify this patch according to your comment.

> For case 3, I don't even know how much pushdown those do of _any_
> aggregates to non-PG servers, let along parallel FDW ones.  Does anyone
> know the details?
To allow partial aggregate pushdown for non-PG FDWs,
I think we need to add pushdown logic to their FDWs for each function.
For example, we need to add logic avg() -> sum()/count() to their FDWs for avg.
To allow parallel partial aggregate by non-PG FDWs,
I think we need to add FDW Routines for Asynchronous Execution to their FDWs[1].

> I am confused by these changes to pg_aggegate:
> 
> +{ 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' },
> 
> ...
> 
> +{ 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' },
> 
> Why are these marked as 'sum' but use 'avg' functions?
This reason is that sum(int8)/sum(numeric) shares some functions with avg(int8)/avg(numeric),
and sum_p_int8 is aggpartialfn of sum(int8) and sum_p_numeric is aggpartialfn of sum(numeric).

--Part of avg(int8) in BKI file in PostgreSQL15.0[2].
{ 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' },
--

--Part of sum(int8) in BKI file in PostgreSQL15.0[2].
{ 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' },
--

[1] https://www.postgresql.org/docs/15/fdw-callbacks.html#FDW-CALLBACKS-ASYNC
[2] https://github.com/postgres/postgres/blob/REL_15_0/src/include/catalog/pg_aggregate.dat

Sincerely yours,
Yuuki Fujii

--
Yuuki Fujii
Information Technology R&D Center Mitsubishi Electric Corporation






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
@ 2023-04-08 01:50     ` Bruce Momjian <[email protected]>
  2023-04-08 01:55       ` Re: Partial aggregates pushdown Tom Lane <[email protected]>
  2023-04-10 01:18       ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  0 siblings, 2 replies; 35+ messages in thread

From: Bruce Momjian @ 2023-04-08 01:50 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: Andres Freund <[email protected]>; Alexander Pyhalov <[email protected]>; Tomas Vondra <[email protected]>; pgsql-hackers; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

On Fri, Apr  7, 2023 at 09:25:52AM +0000, [email protected] wrote:
> Hi Mr.Momjian
> 
> > First, my apologies for not addressing this sooner.  I was so focused on my
> > own tasks that I didn't realize this very important patch was not getting
> > attention.  I will try my best to get it into PG 17.
> Thank you very much for your comments. 
> I will improve this patch for PG17.
> I believe that this patch will help us use PostgreSQL's built-in sharding for OLAP.

Agreed!  Again, my apologies for not helping with this _much_ sooner. 
You have done amazing work here.

> > What amazes me is that you didn't need to create _any_ actual aggregate
> > functions.  Rather, you just needed to hook existing functions into the
> > aggregate tables for partial FDW execution.
> Yes. This patch enables partial aggregate pushdown using 
> only existing functions which belong to existing aggregate function
> and are needed by parallel query(such as state transition function and serialization function).
> This patch does not need new types of function belonging to aggregate functions
> and does not need new functions belonging to existing aggregate functions.

Very nice.

> > I suggest we remove the version check requirement --- instead just document
> > that the FDW Postgres version should be the same or newer than the calling
> > Postgres server --- that way, we can assume that whatever is in the system
> > catalogs of the caller is in the receiving side.  
> Thanks for the comment. I will modify this patch according to your comment.
> 
> > We should add a GUC to turn off
> > this optimization for cases where the FDW Postgres version is older than the
> > caller.  This handles case 1-2.
> Thanks for the advice here too.
> I thought it would be more appropriate to add a foregin server option of 
> postgres_fdw rather than adding GUC. 
> Would you mind if I ask you what you think about it?

I like the GUC idea because it gives administrators a single place to
check if the feature is enabled.  However, I can imagine cases where you
might have multiple remote FDW servers and some might be older than the
sending server.

What I don't want is an error-prone setup where administrators have to
remember what the per-server settings are.  Based on your suggestion,
let's allow CREATE SERVER to have an option 'enable_async_aggregate' (is
that the right name?), which defaults to 'true' for _all_ servers, even
those that don't support async aggregates.  With that, all FDW servers
are enabled by default, and if the FDW extension supports async
aggregates, they will automatically be pushed down and will report an
error only if the remote FDW is too old to support it.

> > > 2. Automation of creating definition of partialaggfuncs In development
> > > of v17, I manually create definition of partialaggfuncs for avg, min, max, sum,
> > count.
> > > I am concerned that this may be undesirable.
> > > So I am thinking that v17 should be modified to automate creating
> > > definition of partialaggfuncs for all built-in aggregate functions.
> > 
> > Are there any other builtin functions that need this?  I think we can just
> > provide documention for extensions on how to do this.
> For practical purposes, it is sufficient 
> if partial aggregate for the above functions can be pushed down.
> I think you are right, it would be sufficient to document how to achieve
>  partial aggregate pushdown for other built-in functions.

Uh, we actually want the patch to implement partial aggregate pushdown
for all builtin data types that can support it.  Is that done?  I think
it is only extension aggregates, which we do not control, that need this
documentation.

> > > 3. Documentation
> > > I need add explanation of partialaggfunc to documents on postgres_fdw and
> > other places.
> > 
> > I can help with that once we decide on the above.
> Thank you. In the next verion of this patch, I will add documents on postgres_fdw
> and other places. 

Good.

> > I think 'partialaggfn' should be named 'aggpartialfn' to match other columns in
> > pg_aggregate.
> Thanks for the comment. I will modify this patch according to your comment.
> 
> > For case 3, I don't even know how much pushdown those do of _any_
> > aggregates to non-PG servers, let along parallel FDW ones.  Does anyone
> > know the details?
> To allow partial aggregate pushdown for non-PG FDWs,
> I think we need to add pushdown logic to their FDWs for each function.
> For example, we need to add logic avg() -> sum()/count() to their FDWs for avg.
> To allow parallel partial aggregate by non-PG FDWs,
> I think we need to add FDW Routines for Asynchronous Execution to their FDWs[1].

Okay, I think we can just implement this for 1-2 and let extensions
worry about 3.

> > I am confused by these changes to pg_aggegate:
> > 
> > +{ 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' },
> > 
> > ...
> > 
> > +{ 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' },
> > 
> > Why are these marked as 'sum' but use 'avg' functions?
> This reason is that sum(int8)/sum(numeric) shares some functions with avg(int8)/avg(numeric),
> and sum_p_int8 is aggpartialfn of sum(int8) and sum_p_numeric is aggpartialfn of sum(numeric).

Ah, I see this now, thanks.

-- 
  Bruce Momjian  <[email protected]>        https://momjian.us
  EDB                                      https://enterprisedb.com

  Embrace your flaws.  They make you human, rather than perfect,
  which you will never be.






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
@ 2023-04-08 01:55       ` Tom Lane <[email protected]>
  2023-04-08 02:34         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  1 sibling, 1 reply; 35+ messages in thread

From: Tom Lane @ 2023-04-08 01:55 UTC (permalink / raw)
  To: Bruce Momjian <[email protected]>; +Cc: [email protected] <[email protected]>; Andres Freund <[email protected]>; Alexander Pyhalov <[email protected]>; Tomas Vondra <[email protected]>; pgsql-hackers; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

Bruce Momjian <[email protected]> writes:
> What I don't want is an error-prone setup where administrators have to
> remember what the per-server settings are.  Based on your suggestion,
> let's allow CREATE SERVER to have an option 'enable_async_aggregate' (is
> that the right name?), which defaults to 'true' for _all_ servers, even
> those that don't support async aggregates.

Uh, what?  Why would we not be able to tell from the remote server's
version number whether it has this ability?

			regards, tom lane






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-08 01:55       ` Re: Partial aggregates pushdown Tom Lane <[email protected]>
@ 2023-04-08 02:34         ` Bruce Momjian <[email protected]>
  2023-04-08 02:44           ` Re: Partial aggregates pushdown Tom Lane <[email protected]>
  0 siblings, 1 reply; 35+ messages in thread

From: Bruce Momjian @ 2023-04-08 02:34 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: [email protected] <[email protected]>; Andres Freund <[email protected]>; Alexander Pyhalov <[email protected]>; Tomas Vondra <[email protected]>; pgsql-hackers; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

On Fri, Apr  7, 2023 at 09:55:00PM -0400, Tom Lane wrote:
> Bruce Momjian <[email protected]> writes:
> > What I don't want is an error-prone setup where administrators have to
> > remember what the per-server settings are.  Based on your suggestion,
> > let's allow CREATE SERVER to have an option 'enable_async_aggregate' (is
> > that the right name?), which defaults to 'true' for _all_ servers, even
> > those that don't support async aggregates.
> 
> Uh, what?  Why would we not be able to tell from the remote server's
> version number whether it has this ability?

That was covered here:

	https://www.postgresql.org/message-id/ZC95C0%2BPVhVP3iax%40momjian.us

	I think we have three possible cases for aggregate pushdown to FDWs:

	1)  Postgres built-in aggregate functions
	2)  Postgres user-defined & extension aggregate functions
	3)  aggregate functions calls to non-PG FDWs

	Your patch handles #1 by checking that the FDW Postgres version is the
-->	same as the calling Postgres version.  However, it doesn't check for
-->	extension versions, and frankly, I don't see how we could implement that
-->	cleanly without significant complexity.

The issue is not a mismatch of postgres_fdw versions but the extension
versions and whether the partial aggregate functions exist on the remote
side, e.g., something like a PostGIS upgrade.

-- 
  Bruce Momjian  <[email protected]>        https://momjian.us
  EDB                                      https://enterprisedb.com

  Embrace your flaws.  They make you human, rather than perfect,
  which you will never be.






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-08 01:55       ` Re: Partial aggregates pushdown Tom Lane <[email protected]>
  2023-04-08 02:34         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
@ 2023-04-08 02:44           ` Tom Lane <[email protected]>
  2023-04-08 02:53             ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  0 siblings, 1 reply; 35+ messages in thread

From: Tom Lane @ 2023-04-08 02:44 UTC (permalink / raw)
  To: Bruce Momjian <[email protected]>; +Cc: [email protected] <[email protected]>; Andres Freund <[email protected]>; Alexander Pyhalov <[email protected]>; Tomas Vondra <[email protected]>; pgsql-hackers; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

Bruce Momjian <[email protected]> writes:
> On Fri, Apr  7, 2023 at 09:55:00PM -0400, Tom Lane wrote:
>> Uh, what?  Why would we not be able to tell from the remote server's
>> version number whether it has this ability?

> The issue is not a mismatch of postgres_fdw versions but the extension
> versions and whether the partial aggregate functions exist on the remote
> side, e.g., something like a PostGIS upgrade.

postgres_fdw has no business pushing down calls to non-builtin functions
unless the user has explicitly authorized that via the existing
whitelisting mechanism.  I think you're reinventing the wheel,
and not very well.

			regards, tom lane






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-08 01:55       ` Re: Partial aggregates pushdown Tom Lane <[email protected]>
  2023-04-08 02:34         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-08 02:44           ` Re: Partial aggregates pushdown Tom Lane <[email protected]>
@ 2023-04-08 02:53             ` Bruce Momjian <[email protected]>
  2023-04-08 03:04               ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-08 04:16               ` Re: Partial aggregates pushdown Andres Freund <[email protected]>
  0 siblings, 2 replies; 35+ messages in thread

From: Bruce Momjian @ 2023-04-08 02:53 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: [email protected] <[email protected]>; Andres Freund <[email protected]>; Alexander Pyhalov <[email protected]>; Tomas Vondra <[email protected]>; pgsql-hackers; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

On Fri, Apr  7, 2023 at 10:44:09PM -0400, Tom Lane wrote:
> Bruce Momjian <[email protected]> writes:
> > On Fri, Apr  7, 2023 at 09:55:00PM -0400, Tom Lane wrote:
> >> Uh, what?  Why would we not be able to tell from the remote server's
> >> version number whether it has this ability?
> 
> > The issue is not a mismatch of postgres_fdw versions but the extension
> > versions and whether the partial aggregate functions exist on the remote
> > side, e.g., something like a PostGIS upgrade.
> 
> postgres_fdw has no business pushing down calls to non-builtin functions
> unless the user has explicitly authorized that via the existing
> whitelisting mechanism.  I think you're reinventing the wheel,
> and not very well.

The patch has you assign an option at CREATE AGGREGATE time if it can do
push down, and postgres_fdw checks that.  What whitelisting mechanism
are you talking about?  async_capable?

-- 
  Bruce Momjian  <[email protected]>        https://momjian.us
  EDB                                      https://enterprisedb.com

  Embrace your flaws.  They make you human, rather than perfect,
  which you will never be.






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-08 01:55       ` Re: Partial aggregates pushdown Tom Lane <[email protected]>
  2023-04-08 02:34         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-08 02:44           ` Re: Partial aggregates pushdown Tom Lane <[email protected]>
  2023-04-08 02:53             ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
@ 2023-04-08 03:04               ` Bruce Momjian <[email protected]>
  1 sibling, 0 replies; 35+ messages in thread

From: Bruce Momjian @ 2023-04-08 03:04 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: [email protected] <[email protected]>; Andres Freund <[email protected]>; Alexander Pyhalov <[email protected]>; Tomas Vondra <[email protected]>; pgsql-hackers; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

On Fri, Apr  7, 2023 at 10:53:53PM -0400, Bruce Momjian wrote:
> On Fri, Apr  7, 2023 at 10:44:09PM -0400, Tom Lane wrote:
> > Bruce Momjian <[email protected]> writes:
> > > On Fri, Apr  7, 2023 at 09:55:00PM -0400, Tom Lane wrote:
> > >> Uh, what?  Why would we not be able to tell from the remote server's
> > >> version number whether it has this ability?
> > 
> > > The issue is not a mismatch of postgres_fdw versions but the extension
> > > versions and whether the partial aggregate functions exist on the remote
> > > side, e.g., something like a PostGIS upgrade.
> > 
> > postgres_fdw has no business pushing down calls to non-builtin functions
> > unless the user has explicitly authorized that via the existing
> > whitelisting mechanism.  I think you're reinventing the wheel,
> > and not very well.
> 
> The patch has you assign an option at CREATE AGGREGATE time if it can do
> push down, and postgres_fdw checks that.  What whitelisting mechanism
> are you talking about?  async_capable?

FYI, in the patch the CREATE AGGREGATE option is called PARTIALAGGFUNC.

-- 
  Bruce Momjian  <[email protected]>        https://momjian.us
  EDB                                      https://enterprisedb.com

  Embrace your flaws.  They make you human, rather than perfect,
  which you will never be.






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-08 01:55       ` Re: Partial aggregates pushdown Tom Lane <[email protected]>
  2023-04-08 02:34         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-08 02:44           ` Re: Partial aggregates pushdown Tom Lane <[email protected]>
  2023-04-08 02:53             ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
@ 2023-04-08 04:16               ` Andres Freund <[email protected]>
  2023-04-08 14:15                 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  1 sibling, 1 reply; 35+ messages in thread

From: Andres Freund @ 2023-04-08 04:16 UTC (permalink / raw)
  To: Bruce Momjian <[email protected]>; +Cc: Tom Lane <[email protected]>; [email protected] <[email protected]>; Alexander Pyhalov <[email protected]>; Tomas Vondra <[email protected]>; pgsql-hackers; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

On 2023-04-07 22:53:53 -0400, Bruce Momjian wrote:
> On Fri, Apr  7, 2023 at 10:44:09PM -0400, Tom Lane wrote:
> > Bruce Momjian <[email protected]> writes:
> > > On Fri, Apr  7, 2023 at 09:55:00PM -0400, Tom Lane wrote:
> > >> Uh, what?  Why would we not be able to tell from the remote server's
> > >> version number whether it has this ability?
> > 
> > > The issue is not a mismatch of postgres_fdw versions but the extension
> > > versions and whether the partial aggregate functions exist on the remote
> > > side, e.g., something like a PostGIS upgrade.
> > 
> > postgres_fdw has no business pushing down calls to non-builtin functions
> > unless the user has explicitly authorized that via the existing
> > whitelisting mechanism.  I think you're reinventing the wheel,
> > and not very well.
> 
> The patch has you assign an option at CREATE AGGREGATE time if it can do
> push down, and postgres_fdw checks that.  What whitelisting mechanism
> are you talking about?  async_capable?

extensions (string)

    This option is a comma-separated list of names of PostgreSQL extensions that are installed, in compatible versions, on both the local and remote servers. Functions and operators that are immutable and belong to a listed extension will be considered shippable to the remote server. This option can only be specified for foreign servers, not per-table.

    When using the extensions option, it is the user's responsibility that the listed extensions exist and behave identically on both the local and remote servers. Otherwise, remote queries may fail or behave unexpectedly.






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-08 01:55       ` Re: Partial aggregates pushdown Tom Lane <[email protected]>
  2023-04-08 02:34         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-08 02:44           ` Re: Partial aggregates pushdown Tom Lane <[email protected]>
  2023-04-08 02:53             ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-08 04:16               ` Re: Partial aggregates pushdown Andres Freund <[email protected]>
@ 2023-04-08 14:15                 ` Bruce Momjian <[email protected]>
  2023-04-08 16:18                   ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  0 siblings, 1 reply; 35+ messages in thread

From: Bruce Momjian @ 2023-04-08 14:15 UTC (permalink / raw)
  To: Andres Freund <[email protected]>; +Cc: Tom Lane <[email protected]>; [email protected] <[email protected]>; Alexander Pyhalov <[email protected]>; Tomas Vondra <[email protected]>; pgsql-hackers; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

On Fri, Apr  7, 2023 at 09:16:14PM -0700, Andres Freund wrote:
> On 2023-04-07 22:53:53 -0400, Bruce Momjian wrote:
> > > postgres_fdw has no business pushing down calls to non-builtin functions
> > > unless the user has explicitly authorized that via the existing
> > > whitelisting mechanism.  I think you're reinventing the wheel,
> > > and not very well.
> > 
> > The patch has you assign an option at CREATE AGGREGATE time if it can do
> > push down, and postgres_fdw checks that.  What whitelisting mechanism
> > are you talking about?  async_capable?
> 
> extensions (string)
> 
>     This option is a comma-separated list of names of PostgreSQL extensions that are installed, in compatible versions, on both the local and remote servers. Functions and operators that are immutable and belong to a listed extension will be considered shippable to the remote server. This option can only be specified for foreign servers, not per-table.
> 
>     When using the extensions option, it is the user's responsibility that the listed extensions exist and behave identically on both the local and remote servers. Otherwise, remote queries may fail or behave unexpectedly.

Okay, this is very helpful --- it is exactly the issue we are dealing
with --- how can we know if partial aggregate functions exists on the
remote server.  (I knew I was going to need API help on this.)

So, let's remove the PARTIALAGG_MINVERSION option from the patch and
just make it automatic --- we push down builtin partial aggregates if
the remote server is the same or newer _major_ version than the sending
server.  For extensions, if people have older extensions on the same or
newer foreign servers, they can adjust 'extensions' above.

-- 
  Bruce Momjian  <[email protected]>        https://momjian.us
  EDB                                      https://enterprisedb.com

  Embrace your flaws.  They make you human, rather than perfect,
  which you will never be.






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-08 01:55       ` Re: Partial aggregates pushdown Tom Lane <[email protected]>
  2023-04-08 02:34         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-08 02:44           ` Re: Partial aggregates pushdown Tom Lane <[email protected]>
  2023-04-08 02:53             ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-08 04:16               ` Re: Partial aggregates pushdown Andres Freund <[email protected]>
  2023-04-08 14:15                 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
@ 2023-04-08 16:18                   ` Bruce Momjian <[email protected]>
  0 siblings, 0 replies; 35+ messages in thread

From: Bruce Momjian @ 2023-04-08 16:18 UTC (permalink / raw)
  To: Andres Freund <[email protected]>; +Cc: Tom Lane <[email protected]>; [email protected] <[email protected]>; Alexander Pyhalov <[email protected]>; Tomas Vondra <[email protected]>; pgsql-hackers; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

On Sat, Apr  8, 2023 at 10:15:40AM -0400, Bruce Momjian wrote:
> On Fri, Apr  7, 2023 at 09:16:14PM -0700, Andres Freund wrote:
> > extensions (string)
> > 
> >     This option is a comma-separated list of names of PostgreSQL extensions that are installed, in compatible versions, on both the local and remote servers. Functions and operators that are immutable and belong to a listed extension will be considered shippable to the remote server. This option can only be specified for foreign servers, not per-table.
> > 
> >     When using the extensions option, it is the user's responsibility that the listed extensions exist and behave identically on both the local and remote servers. Otherwise, remote queries may fail or behave unexpectedly.
> 
> Okay, this is very helpful --- it is exactly the issue we are dealing
> with --- how can we know if partial aggregate functions exists on the
> remote server.  (I knew I was going to need API help on this.)
> 
> So, let's remove the PARTIALAGG_MINVERSION option from the patch and
> just make it automatic --- we push down builtin partial aggregates if
> the remote server is the same or newer _major_ version than the sending
> server.  For extensions, if people have older extensions on the same or
> newer foreign servers, they can adjust 'extensions' above.

Looking further, I don't see any cases where we check if a builtin
function added in a major release also exists on the foreign server, so
maybe we don't need any checks but just need a mention in the release
notes.

-- 
  Bruce Momjian  <[email protected]>        https://momjian.us
  EDB                                      https://enterprisedb.com

  Embrace your flaws.  They make you human, rather than perfect,
  which you will never be.






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* RE: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
@ 2023-04-10 01:18       ` [email protected] <[email protected]>
  2023-04-13 06:12         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  1 sibling, 1 reply; 35+ messages in thread

From: [email protected] @ 2023-04-10 01:18 UTC (permalink / raw)
  To: Bruce Momjian <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; +Cc: Alexander Pyhalov <[email protected]>; Tomas Vondra <[email protected]>; pgsql-hackers; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>; [email protected] <[email protected]>

Hi Mr.Momjian, Mr.Lane, Mr.Freund.

Thank you for advices.

> From: Bruce Momjian <[email protected]>
> > > > 2. Automation of creating definition of partialaggfuncs In
> > > > development of v17, I manually create definition of
> > > > partialaggfuncs for avg, min, max, sum,
> > > count.
> > > > I am concerned that this may be undesirable.
> > > > So I am thinking that v17 should be modified to automate creating
> > > > definition of partialaggfuncs for all built-in aggregate functions.
> > >
> > > Are there any other builtin functions that need this?  I think we
> > > can just provide documention for extensions on how to do this.
> > For practical purposes, it is sufficient if partial aggregate for the
> > above functions can be pushed down.
> > I think you are right, it would be sufficient to document how to
> > achieve  partial aggregate pushdown for other built-in functions.
> 
> Uh, we actually want the patch to implement partial aggregate pushdown for all
> builtin data types that can support it.  Is that done?  I think it is only extension
> aggregates, which we do not control, that need this documentation.
The last version of this patch can't pushdown partial aggregate for all builtin aggregate functions that can support it.
I will improve this patch to pushdown partial aggregate for all builtin aggregate functions
that can support it.

There is one more thing I would like your opinion on.
As the major version of PostgreSQL increase, it is possible that
the new builtin aggregate functions are added to the newer PostgreSQL.
This patch assume that aggpartialfns definitions exist in BKI files.
Due to this assumption, someone should add aggpartialfns definitions of new builtin aggregate functions to BKI files.
There are two possible ways to address this issue. Would the way1 be sufficient?
Or would way2 be preferable?
  way1) Adding documentaion for how to add these definitions to BKI files
  way2) Improving this patch to automatically add these definitions to BKI files by some tool such as initdb.

> From: Bruce Momjian <[email protected]>
> On Fri, Apr  7, 2023 at 09:16:14PM -0700, Andres Freund wrote:
> > On 2023-04-07 22:53:53 -0400, Bruce Momjian wrote:
> > > > postgres_fdw has no business pushing down calls to non-builtin
> > > > functions unless the user has explicitly authorized that via the
> > > > existing whitelisting mechanism.  I think you're reinventing the
> > > > wheel, and not very well.
> > >
> > > The patch has you assign an option at CREATE AGGREGATE time if it
> > > can do push down, and postgres_fdw checks that.  What whitelisting
> > > mechanism are you talking about?  async_capable?
> >
> > extensions (string)
> >
> >     This option is a comma-separated list of names of PostgreSQL
> extensions that are installed, in compatible versions, on both the local and
> remote servers. Functions and operators that are immutable and belong to a
> listed extension will be considered shippable to the remote server. This option
> can only be specified for foreign servers, not per-table.
> >
> >     When using the extensions option, it is the user's responsibility that the
> listed extensions exist and behave identically on both the local and remote
> servers. Otherwise, remote queries may fail or behave unexpectedly.
> 
> Okay, this is very helpful --- it is exactly the issue we are dealing with --- how
> can we know if partial aggregate functions exists on the remote server.  (I
> knew I was going to need API help on this.)
> 
> So, let's remove the PARTIALAGG_MINVERSION option from the patch and just
> make it automatic --- we push down builtin partial aggregates if the remote
> server is the same or newer _major_ version than the sending server.  For
> extensions, if people have older extensions on the same or newer foreign
> servers, they can adjust 'extensions' above.
Okay, I understand. I will remove PARTIALAGG_MINVERSION option from the patch
and I will add check whether aggpartialfn depends on some extension which
is containd in extensions list of the postgres_fdw's foreign server.
In the next version of this patch,
we can pushdown partial aggregate for an user-defined aggregate function only 
when the function pass through this check.

Sincerely yours,
Yuuki Fujii

--
Yuuki Fujii
Information Technology R&D Center Mitsubishi Electric Corporation






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-10 01:18       ` RE: Partial aggregates pushdown [email protected] <[email protected]>
@ 2023-04-13 06:12         ` Bruce Momjian <[email protected]>
  2023-04-13 06:50           ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  0 siblings, 1 reply; 35+ messages in thread

From: Bruce Momjian @ 2023-04-13 06:12 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: Andres Freund <[email protected]>; Tom Lane <[email protected]>; Alexander Pyhalov <[email protected]>; Tomas Vondra <[email protected]>; pgsql-hackers; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

On Mon, Apr 10, 2023 at 01:18:37AM +0000, [email protected] wrote:
> > Uh, we actually want the patch to implement partial aggregate pushdown for all
> > builtin data types that can support it.  Is that done?  I think it is only extension
> > aggregates, which we do not control, that need this documentation.
> The last version of this patch can't pushdown partial aggregate for all builtin aggregate functions that can support it.
> I will improve this patch to pushdown partial aggregate for all builtin aggregate functions
> that can support it.
> 
> There is one more thing I would like your opinion on.
> As the major version of PostgreSQL increase, it is possible that
> the new builtin aggregate functions are added to the newer PostgreSQL.
> This patch assume that aggpartialfns definitions exist in BKI files.
> Due to this assumption, someone should add aggpartialfns definitions of new builtin aggregate functions to BKI files.
> There are two possible ways to address this issue. Would the way1 be sufficient?
> Or would way2 be preferable?
>   way1) Adding documentaion for how to add these definitions to BKI files
>   way2) Improving this patch to automatically add these definitions to BKI files by some tool such as initdb.

I think documentation is sufficient.  You already showed that someone
can do this with CREATE AGGREGATE for non-builtin aggregates.

> > So, let's remove the PARTIALAGG_MINVERSION option from the patch and just
> > make it automatic --- we push down builtin partial aggregates if the remote
> > server is the same or newer _major_ version than the sending server.  For
> > extensions, if people have older extensions on the same or newer foreign
> > servers, they can adjust 'extensions' above.
> Okay, I understand. I will remove PARTIALAGG_MINVERSION option from the patch
> and I will add check whether aggpartialfn depends on some extension which
> is containd in extensions list of the postgres_fdw's foreign server.

Yes, good.  Did we never push down aggregates before?  I thought we
pushed down partitionwise aggregates already, and such a check should
already be done.  If the check isn't there, it should be.

> In the next version of this patch,
> we can pushdown partial aggregate for an user-defined aggregate function only 
> when the function pass through this check.

Understood.

-- 
  Bruce Momjian  <[email protected]>        https://momjian.us
  EDB                                      https://enterprisedb.com

  Embrace your flaws.  They make you human, rather than perfect,
  which you will never be.






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-10 01:18       ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-13 06:12         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
@ 2023-04-13 06:50           ` Bruce Momjian <[email protected]>
  2023-04-13 10:56             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  0 siblings, 1 reply; 35+ messages in thread

From: Bruce Momjian @ 2023-04-13 06:50 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: Andres Freund <[email protected]>; Tom Lane <[email protected]>; Alexander Pyhalov <[email protected]>; Tomas Vondra <[email protected]>; pgsql-hackers; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

On Thu, Apr 13, 2023 at 02:12:44AM -0400, Bruce Momjian wrote:
> > In the next version of this patch,
> > we can pushdown partial aggregate for an user-defined aggregate function only 
> > when the function pass through this check.
> 
> Understood.

In summary, we don't do any version check for built-in function
pushdown, so we don't need it for aggregates either.  We check extension
functions against the extension pushdown list, so we should be checking
this for partial aggregate pushdown, and for partition-wise aggregate
pushdown.  If we don't do that last check already, it is a bug.

-- 
  Bruce Momjian  <[email protected]>        https://momjian.us
  EDB                                      https://enterprisedb.com

  Embrace your flaws.  They make you human, rather than perfect,
  which you will never be.






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* RE: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-10 01:18       ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-13 06:12         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 06:50           ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
@ 2023-04-13 10:56             ` [email protected] <[email protected]>
  2023-04-14 06:54               ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  0 siblings, 1 reply; 35+ messages in thread

From: [email protected] @ 2023-04-13 10:56 UTC (permalink / raw)
  To: Bruce Momjian <[email protected]>; +Cc: Andres Freund <[email protected]>; Tom Lane <[email protected]>; Alexander Pyhalov <[email protected]>; Tomas Vondra <[email protected]>; pgsql-hackers; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>; [email protected] <[email protected]>

Hi Mr.Momjian.

> > There is one more thing I would like your opinion on.
> > As the major version of PostgreSQL increase, it is possible that the
> > new builtin aggregate functions are added to the newer PostgreSQL.
> > This patch assume that aggpartialfns definitions exist in BKI files.
> > Due to this assumption, someone should add aggpartialfns definitions of
> new builtin aggregate functions to BKI files.
> > There are two possible ways to address this issue. Would the way1 be
> sufficient?
> > Or would way2 be preferable?
> >   way1) Adding documentaion for how to add these definitions to BKI files
> >   way2) Improving this patch to automatically add these definitions to BKI
> files by some tool such as initdb.
> 
> I think documentation is sufficient.  You already showed that someone can do
> this with CREATE AGGREGATE for non-builtin aggregates.
Thank you for your opinion. I will modify this patch according to the way1.

> > > So, let's remove the PARTIALAGG_MINVERSION option from the patch and
> > > just make it automatic --- we push down builtin partial aggregates
> > > if the remote server is the same or newer _major_ version than the
> > > sending server.  For extensions, if people have older extensions on
> > > the same or newer foreign servers, they can adjust 'extensions' above.
> > Okay, I understand. I will remove PARTIALAGG_MINVERSION option from
> > the patch and I will add check whether aggpartialfn depends on some
> > extension which is containd in extensions list of the postgres_fdw's foreign
> server.
> 
> Yes, good.  Did we never push down aggregates before?  I thought we
> pushed down partitionwise aggregates already, and such a check should
> already be done.  If the check isn't there, it should be.
Yes. The last version of this patch(and original postgres_fdw) checks if
user-defined aggregate depends some extension which is contained in 'extensions'.
But, in the last version of this patch, there is no such check for 
aggpartialfn of user-defined aggregate. So, I will add such check to this patch. 
I think that this modification is easy to do . 

> In summary, we don't do any version check for built-in function pushdown, so
> we don't need it for aggregates either.  We check extension functions against
> the extension pushdown list, so we should be checking this for partial
> aggregate pushdown, and for partition-wise aggregate pushdown.  If we don't
> do that last check already, it is a bug.
I understood.

Sincerely yours,
Yuuki Fujii

--
Yuuki Fujii
Information Technology R&D Center Mitsubishi Electric Corporation






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-10 01:18       ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-13 06:12         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 06:50           ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 10:56             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
@ 2023-04-14 06:54               ` Bruce Momjian <[email protected]>
  2023-06-02 03:54                 ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  0 siblings, 1 reply; 35+ messages in thread

From: Bruce Momjian @ 2023-04-14 06:54 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: Andres Freund <[email protected]>; Tom Lane <[email protected]>; Alexander Pyhalov <[email protected]>; Tomas Vondra <[email protected]>; pgsql-hackers; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

On Thu, Apr 13, 2023 at 10:56:26AM +0000, [email protected] wrote:
> > Yes, good.  Did we never push down aggregates before?  I thought we
> > pushed down partitionwise aggregates already, and such a check should
> > already be done.  If the check isn't there, it should be.
> Yes. The last version of this patch(and original postgres_fdw) checks if
> user-defined aggregate depends some extension which is contained in 'extensions'.
> But, in the last version of this patch, there is no such check for 
> aggpartialfn of user-defined aggregate. So, I will add such check to this patch. 
> I think that this modification is easy to do . 

Good, so our existing code is correct and the patch just needs
adjustment.

-- 
  Bruce Momjian  <[email protected]>        https://momjian.us
  EDB                                      https://enterprisedb.com

  Embrace your flaws.  They make you human, rather than perfect,
  which you will never be.






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* RE: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-10 01:18       ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-13 06:12         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 06:50           ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 10:56             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-14 06:54               ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
@ 2023-06-02 03:54                 ` [email protected] <[email protected]>
  2023-06-05 09:00                   ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  2023-06-06 00:10                   ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  0 siblings, 2 replies; 35+ messages in thread

From: [email protected] @ 2023-06-02 03:54 UTC (permalink / raw)
  To: Bruce Momjian <[email protected]>; pgsql-hackers; +Cc: Andres Freund <[email protected]>; Tom Lane <[email protected]>; Alexander Pyhalov <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>; [email protected] <[email protected]>

Hi Mr.Bruce, hackers.

I updated the patch.
The following is a list of comments received on the previous version of the patch
and my update to them in this version of the patch.

[comment1]
> From: Bruce Momjian <[email protected]>
> Sent: Saturday, April 8, 2023 10:50 AM
> Uh, we actually want the patch to implement partial aggregate pushdown for all
> builtin data types that can support it.

I improved the patch to push partial aggregate down for all builtin aggregates that 
support it.

[comment2]
> From: Bruce Momjian <[email protected]>
> Sent: Thursday, April 13, 2023 3:51 PM
> In summary, we don't do any version check for built-in function pushdown, so
> we don't need it for aggregates either.  We check extension functions against
> the extension pushdown list, so we should be checking this for partial
> aggregate pushdown, and for partition-wise aggregate pushdown.

I removed partialagg_minversion from pg_aggregate and removed the version 
check for partial aggregate pushdown by it. postgres_fdw assumes that every 
built-in aggregate function has its aggpartialfunc on remote server.
postgres_fdw assumes that a user-defined aggregate function has its 
aggpartialfunc on remote server only when the user-defined aggregate function 
and the aggpartialfunc of it belong to some extension that's listed in the 
foreign server's extensions option. 

[comment3]
> From: Bruce Momjian <[email protected]>
> Sent: Saturday, April 8, 2023 10:50 AM
> > > > 3. Documentation
> > > > I need add explanation of partialaggfunc to documents on
> > > > postgres_fdw and
> > > other places.
> > >
> > > I can help with that once we decide on the above.
> > Thank you. In the next verion of this patch, I will add documents on
> > postgres_fdw and other places.
> 
> Good.

I appended description for partial aggregate pushdown feature by postgres_fdw
to existing documents.
The following is a list of sgml files that have been appended and the
contents of the additions.
  postgres-fdw.sgml    : Description about this partial aggregate pushdown feature
                         and definition of aggpartialfunc.
      * Unlike existing aggregate pushdown feature, this partial aggregate
        pushdown feature is a one of the built-in sharding features in PostgreSQL.
        So I added a section about built-in sharding feature in PostgreSQL, and in that
        section I added a description of this partial aggregate pushdown feature.
        In this document, a description of the built-in sharding feature in PostgreSQL
        is based on [1].
  xaggr.sgml           :Partial aggregate pushdown feature for user-defined
                        aggregate functions.
  create_aggregate.sgml:Description about additional parameters for
                        partial aggregate pushdown feature.
  catalogs.sgml        :Description about aggpartialfn column.

[comment4]
> From: Bruce Momjian <[email protected]>
> Sent: Thursday, April 13, 2023 3:13 PM
> > There is one more thing I would like your opinion on.
> > As the major version of PostgreSQL increase, it is possible that the
> > new builtin aggregate functions are added to the newer PostgreSQL.
> > This patch assume that aggpartialfns definitions exist in BKI files.
> > Due to this assumption, someone should add aggpartialfns definitions of
> new builtin aggregate functions to BKI files.
> > There are two possible ways to address this issue. Would the way1 be
> sufficient?
> > Or would way2 be preferable?
> >   way1) Adding documentaion for how to add these definitions to BKI files
> >   way2) Improving this patch to automatically add these definitions to BKI
> files by some tool such as initdb.
> 
> I think documentation is sufficient.  You already showed that someone can do
> this with CREATE AGGREGATE for non-builtin aggregates.

The update addressesing to comment3 also addresses this comment.
If a new aggregate function is added in future, 
definition of aggpartialfunc in postgres-fdw.sgml
helps postgres_fdw developer add a new aggpartialfunc corresponding to the aggregate
function to existing BKI files.
For user-defined aggregate functions, description in xaggr.sgml and
create_aggregate.sgml help a user create an aggpartialfunc corresponding to
a user-defined aggregate function.

In addition, I added a validation mechanism to determine whether an
aggpartialfunc corresponding to an aggregate function is correctly created.
The aggpartialfunc information for the built-in aggregate functions is registered
in the system catalog pg_aggregate at the time the database cluster is created
from BKI files.
So I added this validation process to the regression test of postgres_fdw.

However, I am concerned that it might be more appropriate to add this validation
process to the build process or initdb, rather than to the regression test.
I would appreciate comments from the PostgreSQL community on this point.
For aggpartialfunc for user-defined functions,
I added this validation process to pg_aggregate.c.

[comment5]
> From: Bruce Momjian <[email protected]>
> Sent: Friday, April 7, 2023 11:00 AM
> I think 'partialaggfn' should be named 'aggpartialfn' to match other columns in
> pg_aggregate.

Fixed.

[1] PostgreSQL wiki, WIP PostgreSQL Sharding
https://wiki.postgresql.org/wiki/WIP_PostgreSQL_Sharding

Sincerely yours,
Yuuki Fujii

--
Yuuki Fujii
Information Technology R&D Center Mitsubishi Electric Corporation


Attachments:

  [application/octet-stream] 0001-Partial-aggregates-push-down-v18.patch (229.3K, ../../OS3PR01MB66607DC466F7F3B3909DA3CA954EA@OS3PR01MB6660.jpnprd01.prod.outlook.com/2-0001-Partial-aggregates-push-down-v18.patch)
  download | inline diff:
From e02da514a1538747c8f24e63a9ccb6a8bdb83487 Mon Sep 17 00:00:00 2001
From: Yuki Fujii <[email protected]>
Date: Wed, 31 May 2023 07:13:56 -0400
Subject: [PATCH] Partial aggregates push down v18

---
 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                |  58 +-
 doc/src/sgml/ref/create_aggregate.sgml        |  39 +
 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, 2227 insertions(+), 173 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..8722972dfd 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 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..199c5699f3 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 ggregate 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 ggregate 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,48 @@ 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.
+   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>
 
diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml
index 222e0aa5c9..303648a693 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,37 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">aggpartialfunc</replaceable></term>
+    <listitem>
+     <para>
+      An aggregate function which is the aggpartialfunc corresponding to this
+      aggregate function.
+      See
+      <xref linkend="postgres-fdw-remote-query-optimization"/> for the
+      definition of 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 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 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 3af97a6039..5ef3024072 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -13676,6 +13676,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;
@@ -13758,11 +13759,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 "
@@ -13788,6 +13789,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"));
@@ -13877,6 +13879,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



^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-10 01:18       ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-13 06:12         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 06:50           ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 10:56             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-14 06:54               ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-06-02 03:54                 ` RE: Partial aggregates pushdown [email protected] <[email protected]>
@ 2023-06-05 09:00                   ` Alexander Pyhalov <[email protected]>
  2023-06-06 03:08                     ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  1 sibling, 1 reply; 35+ messages in thread

From: Alexander Pyhalov @ 2023-06-05 09:00 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: Bruce Momjian <[email protected]>; pgsql-hackers; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

[email protected] писал 2023-06-02 06:54:
> Hi Mr.Bruce, hackers.
> 
> I updated the patch.
> The following is a list of comments received on the previous version
> of the patch
> and my update to them in this version of the patch.
> 

Hi.

I've looked through the last version of the patch.

Have found one issue -

src/backend/catalog/pg_aggregate.c

585                 if(strcmp(strVal(linitial(aggpartialfnName)), 
aggName) == 0){
586                         if(((aggTransType != INTERNALOID) && 
(finalfn != InvalidOid))
587                                         || ((aggTransType == 
INTERNALOID) && (finalfn != serialfn)))
588                                 elog(ERROR, "%s is not its own 
aggpartialfunc", aggName);
589                 } else {

Here string comparison of aggName and aggpartialfnName looks very 
suspicios - it seems you should compare oids, not names (in this case,
likely oids of transition function and partial aggregation function). 
The fact that aggregate name matches partial aggregation function name
is not a enough to make any decisions.


In documentation

doc/src/sgml/postgres-fdw.sgml:

  930    <filename>postgres_fdw</filename> attempts to optimize remote 
queries to reduce
  931    the amount of data transferred from foreign servers.  This is 
done by
  932    sending query <literal>WHERE</literal> clauses and ggregate 
expressions
  933    to the remote server for execution, and by not retrieving table 
columns that
  934    are not needed for the current query.
  935    To reduce the risk of misexecution of queries,
  936    <literal>WHERE</literal> clauses and ggregate expressions are 
not sent to
  937    the remote server unless they use only data types, operators, 
and functions
  938    that are built-in or belong to an extension that's listed in the 
foreign
  939    server's <literal>extensions</literal> option.
  940    Operators and functions in such clauses must
  941    be <literal>IMMUTABLE</literal> as well.

there are misprints in lines 932 and 936 - missing "a" in "aggregate" 
expressions.

Note that after these changes "select sum()" will fail for certain 
cases, when remote server version is not the latest. In other cases we 
tried
to preserve compatibility. Should we have a switch for a foreign server 
to turn this optimization off? Or do we just state that users
should use different workarounds if remote server version doesn't match 
local one?

-- 
Best regards,
Alexander Pyhalov,
Postgres Professional






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* RE: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-10 01:18       ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-13 06:12         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 06:50           ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 10:56             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-14 06:54               ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-06-02 03:54                 ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-05 09:00                   ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
@ 2023-06-06 03:08                     ` [email protected] <[email protected]>
  2023-06-06 04:19                       ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  2023-06-09 16:44                       ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  0 siblings, 2 replies; 35+ messages in thread

From: [email protected] @ 2023-06-06 03:08 UTC (permalink / raw)
  To: Alexander Pyhalov <[email protected]>; +Cc: Bruce Momjian <[email protected]>; pgsql-hackers; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>; [email protected] <[email protected]>

Hi Mr.Pyhalov.

Thank you for your always thoughtful review.

> From: Alexander Pyhalov <[email protected]>
> Sent: Monday, June 5, 2023 6:00 PM
> Have found one issue -
> 
> src/backend/catalog/pg_aggregate.c
> 
> 585                 if(strcmp(strVal(linitial(aggpartialfnName)),
> aggName) == 0){
> 586                         if(((aggTransType != INTERNALOID) &&
> (finalfn != InvalidOid))
> 587                                         || ((aggTransType ==
> INTERNALOID) && (finalfn != serialfn)))
> 588                                 elog(ERROR, "%s is not its own
> aggpartialfunc", aggName);
> 589                 } else {
> 
> Here string comparison of aggName and aggpartialfnName looks very
> suspicios - it seems you should compare oids, not names (in this case,
> likely oids of transition function and partial aggregation function).
> The fact that aggregate name matches partial aggregation function name
> is not a enough to make any decisions.

I see no problem with this string comparison. Here is the reason.
The intent of this code is, to determine whether the user specifies 
the new aggregate function whose aggpartialfunc is itself.
For two aggregate functions,
If the argument list and function name match, then the two aggregate functions must match.
By definition of aggpartialfunc,
every aggregate function and its aggpartialfn must have the same argument list.
Thus, if aggpartialfnName and aggName are equal as strings,
I think it is correct to determine that the user is specifying 
the new aggregate function whose aggpartialfunc is itself.

However, since the document does not state these intentions
I think your suspicions are valid.
Therefore, I have added a specification to the document reflecting the above intentions.

> From: Alexander Pyhalov <[email protected]>
> Sent: Monday, June 5, 2023 6:00 PM
> In documentation
> 
> doc/src/sgml/postgres-fdw.sgml:
> 
>   930    <filename>postgres_fdw</filename> attempts to optimize remote
> queries to reduce
>   931    the amount of data transferred from foreign servers.  This is
> done by
>   932    sending query <literal>WHERE</literal> clauses and ggregate
> expressions
>   933    to the remote server for execution, and by not retrieving table
> columns that
>   934    are not needed for the current query.
>   935    To reduce the risk of misexecution of queries,
>   936    <literal>WHERE</literal> clauses and ggregate expressions are
> not sent to
>   937    the remote server unless they use only data types, operators,
> and functions
>   938    that are built-in or belong to an extension that's listed in the
> foreign
>   939    server's <literal>extensions</literal> option.
>   940    Operators and functions in such clauses must
>   941    be <literal>IMMUTABLE</literal> as well.
> 
> there are misprints in lines 932 and 936 - missing "a" in "aggregate"
> expressions.

Fixed.

> From: Alexander Pyhalov <[email protected]>
> Sent: Monday, June 5, 2023 6:00 PM
> Note that after these changes "select sum()" will fail for certain
> cases, when remote server version is not the latest. In other cases we
> tried
> to preserve compatibility. Should we have a switch for a foreign server
> to turn this optimization off? Or do we just state that users
> should use different workarounds if remote server version doesn't match
> local one?

It is the latter.
I added description about the above limitation to F.38.6. Built-in sharding in PostgreSQL and
F.38.8 Cross-Version Compatibility of doc/src/sgml/postgres-fdw.sgml.

> From: Alexander Pyhalov <[email protected]>
> Sent: Tuesday, June 6, 2023 3:15 AM
> Bruce Momjian писал 2023-06-05 19:26:
> > On Mon, Jun  5, 2023 at 12:00:27PM +0300, Alexander Pyhalov wrote:
> >> Note that after these changes "select sum()" will fail for certain
> >> cases, when remote server version is not the latest. In other cases
> >> we tried to preserve compatibility. Should we have a switch for a
> >> foreign server to turn this optimization off? Or do we just state
> >> that users should use different workarounds if remote server version
> >> doesn't match local one?
> >
> > We covered this in April in this and previous emails:
> >
> >
> 	https://www.postgresql.org/message-id/ZDGTza4rovCa%2BN3d%40
> momjian.us
> >
> > We don't check the version number for _any_ builtin functions so why
> > would we need to check for aggregate pushdown?  Yes, these will be new
> > functions in PG 17, we have added functions regularly in major
> > releases and have never heard reports of problems about that.
> >
> Hi.
> 
> I've seen this message. But introduction of new built-in function will break
> requests to old servers only if this new function is used in the request (this
> means that query changes). However, this patch changes the behavior of old
> queries, which worked prior to update. This seems to be different to me.

You are right.
However, for now, partial aggregates pushdown is mainly used when using built-in sharding in PostgreSQL.
I believe when using built-in sharding in PostgreSQL, the version of the primary node server and
the version of the remote server will usually be the same.
So I think it would be sufficient to include in the documentation a note about such problem
and a workaround for them.

Sincerely yours,
Yuuki Fujii

--
Yuuki Fujii
Information Technology R&D Center Mitsubishi Electric Corporation


Attachments:

  [application/octet-stream] 0001-Partial-aggregates-push-down-v19.patch (232.2K, ../../OS3PR01MB66604E404B003A621B0C03A69552A@OS3PR01MB6660.jpnprd01.prod.outlook.com/2-0001-Partial-aggregates-push-down-v19.patch)
  download | inline diff:
From 5f548a5e8d5bc04ea62caf87b18645e8a8f0a669 Mon Sep 17 00:00:00 2001
From: Yuki Fujii <[email protected]>
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



^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-10 01:18       ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-13 06:12         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 06:50           ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 10:56             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-14 06:54               ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-06-02 03:54                 ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-05 09:00                   ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  2023-06-06 03:08                     ` RE: Partial aggregates pushdown [email protected] <[email protected]>
@ 2023-06-06 04:19                       ` Alexander Pyhalov <[email protected]>
  2023-06-06 12:31                         ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  1 sibling, 1 reply; 35+ messages in thread

From: Alexander Pyhalov @ 2023-06-06 04:19 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: Bruce Momjian <[email protected]>; pgsql-hackers; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

[email protected] писал 2023-06-06 06:08:
> Hi Mr.Pyhalov.
> 
> Thank you for your always thoughtful review.
> 
>> From: Alexander Pyhalov <[email protected]>
>> Sent: Monday, June 5, 2023 6:00 PM
>> Have found one issue -
>> 
>> src/backend/catalog/pg_aggregate.c
>> 
>> 585                 if(strcmp(strVal(linitial(aggpartialfnName)),
>> aggName) == 0){
>> 586                         if(((aggTransType != INTERNALOID) &&
>> (finalfn != InvalidOid))
>> 587                                         || ((aggTransType ==
>> INTERNALOID) && (finalfn != serialfn)))
>> 588                                 elog(ERROR, "%s is not its own
>> aggpartialfunc", aggName);
>> 589                 } else {
>> 
>> Here string comparison of aggName and aggpartialfnName looks very
>> suspicios - it seems you should compare oids, not names (in this case,
>> likely oids of transition function and partial aggregation function).
>> The fact that aggregate name matches partial aggregation function name
>> is not a enough to make any decisions.
> 
> I see no problem with this string comparison. Here is the reason.
> The intent of this code is, to determine whether the user specifies
> the new aggregate function whose aggpartialfunc is itself.
> For two aggregate functions,
> If the argument list and function name match, then the two aggregate
> functions must match.
> By definition of aggpartialfunc,
> every aggregate function and its aggpartialfn must have the same 
> argument list.
> Thus, if aggpartialfnName and aggName are equal as strings,
> I think it is correct to determine that the user is specifying
> the new aggregate function whose aggpartialfunc is itself.
> 
> However, since the document does not state these intentions
> I think your suspicions are valid.
> Therefore, I have added a specification to the document reflecting the
> above intentions.
> 

Hi. Let me explain.

Look at this example, taken from test.

CREATE AGGREGATE udf_avg_p_int4(int4) (
        sfunc = int4_avg_accum,
        stype = _int8,
        combinefunc = int4_avg_combine,
        initcond = '{0,0}'
);
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
);

Now, let's create another aggregate.

# create schema test ;
create aggregate test.udf_avg_p_int4(int4) (
        sfunc = int4_avg_accum,
        stype = _int8,
        finalfunc = int8_avg,
        combinefunc = int4_avg_combine,
        initcond = '{0,0}',
        aggpartialfunc = udf_avg_p_int4
);
ERROR:  udf_avg_p_int4 is not its own aggpartialfunc

What's the difference between test.udf_avg_p_int4(int4) aggregate and 
udf_sum(int4)? They are essentially the same, but second one can't be 
defined.

Also note difference:

# CREATE AGGREGATE udf_sum(int4) (
        sfunc = int4_avg_accum,
        stype = _int8,
        finalfunc = int8_avg,
        combinefunc = pg_catalog.int4_avg_combine,
        initcond = '{0,0}',
        aggpartialfunc = udf_avg_p_int4
);
CREATE AGGREGATE

# CREATE AGGREGATE udf_sum(int4) (
        sfunc = int4_avg_accum,
        stype = _int8,
        finalfunc = int8_avg,
        combinefunc = pg_catalog.int4_avg_combine,
        initcond = '{0,0}',
        aggpartialfunc = public.udf_avg_p_int4
);
ERROR:  aggpartialfnName is invalid

It seems that assumption about aggpartialfnName - that it's a 
non-qualified name is incorrect. And if we use qualified names, we can't 
compare just names, likely we should compare oids.

-- 
Best regards,
Alexander Pyhalov,
Postgres Professional






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* RE: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-10 01:18       ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-13 06:12         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 06:50           ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 10:56             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-14 06:54               ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-06-02 03:54                 ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-05 09:00                   ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  2023-06-06 03:08                     ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-06 04:19                       ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
@ 2023-06-06 12:31                         ` [email protected] <[email protected]>
  2023-06-07 09:47                           ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  0 siblings, 1 reply; 35+ messages in thread

From: [email protected] @ 2023-06-06 12:31 UTC (permalink / raw)
  To: Alexander Pyhalov <[email protected]>; +Cc: Bruce Momjian <[email protected]>; pgsql-hackers; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>; [email protected] <[email protected]>

Hi Mr.Pyhalov.

Thank you for comments.

> From: Alexander Pyhalov <[email protected]>
> Sent: Tuesday, June 6, 2023 1:19 PM
> >> Have found one issue -
> >>
> >> src/backend/catalog/pg_aggregate.c
> >>
> >> 585                 if(strcmp(strVal(linitial(aggpartialfnName)),
> >> aggName) == 0){
> >> 586                         if(((aggTransType != INTERNALOID) &&
> >> (finalfn != InvalidOid))
> >> 587                                         || ((aggTransType ==
> >> INTERNALOID) && (finalfn != serialfn)))
> >> 588                                 elog(ERROR, "%s is not its own
> >> aggpartialfunc", aggName);
> >> 589                 } else {
> >>
> >> Here string comparison of aggName and aggpartialfnName looks very
> >> suspicios - it seems you should compare oids, not names (in this
> >> case, likely oids of transition function and partial aggregation function).
> >> The fact that aggregate name matches partial aggregation function
> >> name is not a enough to make any decisions.
> >
> > I see no problem with this string comparison. Here is the reason.
> > The intent of this code is, to determine whether the user specifies
> > the new aggregate function whose aggpartialfunc is itself.
> > For two aggregate functions,
> > If the argument list and function name match, then the two aggregate
> > functions must match.
> > By definition of aggpartialfunc,
> > every aggregate function and its aggpartialfn must have the same
> > argument list.
> > Thus, if aggpartialfnName and aggName are equal as strings, I think it
> > is correct to determine that the user is specifying the new aggregate
> > function whose aggpartialfunc is itself.
> >
> > However, since the document does not state these intentions I think
> > your suspicions are valid.
> > Therefore, I have added a specification to the document reflecting the
> > above intentions.
> >
> 
> Hi. Let me explain.
> 
> Look at this example, taken from test.
> 
> CREATE AGGREGATE udf_avg_p_int4(int4) (
>         sfunc = int4_avg_accum,
>         stype = _int8,
>         combinefunc = int4_avg_combine,
>         initcond = '{0,0}'
> );
> 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
> );
> 
> Now, let's create another aggregate.
> 
> # create schema test ;
> create aggregate test.udf_avg_p_int4(int4) (
>         sfunc = int4_avg_accum,
>         stype = _int8,
>         finalfunc = int8_avg,
>         combinefunc = int4_avg_combine,
>         initcond = '{0,0}',
>         aggpartialfunc = udf_avg_p_int4
> );
> ERROR:  udf_avg_p_int4 is not its own aggpartialfunc
> 
> What's the difference between test.udf_avg_p_int4(int4) aggregate and
> udf_sum(int4)? They are essentially the same, but second one can't be
> defined.
> 
> Also note difference:
> 
> # CREATE AGGREGATE udf_sum(int4) (
>         sfunc = int4_avg_accum,
>         stype = _int8,
>         finalfunc = int8_avg,
>         combinefunc = pg_catalog.int4_avg_combine,
>         initcond = '{0,0}',
>         aggpartialfunc = udf_avg_p_int4
> );
> CREATE AGGREGATE
> 
> # CREATE AGGREGATE udf_sum(int4) (
>         sfunc = int4_avg_accum,
>         stype = _int8,
>         finalfunc = int8_avg,
>         combinefunc = pg_catalog.int4_avg_combine,
>         initcond = '{0,0}',
>         aggpartialfunc = public.udf_avg_p_int4 );
> ERROR:  aggpartialfnName is invalid
> 
> It seems that assumption about aggpartialfnName - that it's a non-qualified
> name is incorrect. And if we use qualified names, we can't compare just names,
> likely we should compare oids.

Thanks for the explanation.
I understand that the method of comparing two function name strings is incorrect.
Instead, I added the parameter isaggpartialfunc indicating whether the aggregate
function and its aggpartialfunc are the same or different.

Sincerely yours,
Yuuki Fujii

--
Yuuki Fujii
Information Technology R&D Center Mitsubishi Electric Corporation


Attachments:

  [application/octet-stream] 0001-Partial-aggregates-push-down-v20.patch (233.2K, ../../OS3PR01MB666009200E5A3659C09955249552A@OS3PR01MB6660.jpnprd01.prod.outlook.com/2-0001-Partial-aggregates-push-down-v20.patch)
  download | inline diff:
From 576a2a79966a6ce538b20b8d073b9c68ea2bca64 Mon Sep 17 00:00:00 2001
From: Yuki Fujii <[email protected]>
Date: Tue, 6 Jun 2023 17:23:36 -0400
Subject: [PATCH] Partial aggregates push down v20

---
 contrib/postgres_fdw/deparse.c                |  71 +-
 .../postgres_fdw/expected/postgres_fdw.out    | 605 +++++++++++++++-
 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        |  56 ++
 doc/src/sgml/xaggr.sgml                       |  12 +-
 src/backend/catalog/pg_aggregate.c            |  83 +++
 src/backend/commands/aggregatecmds.c          |   8 +
 src/bin/pg_dump/pg_dump.c                     |   9 +-
 src/include/catalog/pg_aggregate.dat          | 680 ++++++++++++++----
 src/include/catalog/pg_aggregate.h            |   5 +
 src/include/catalog/pg_proc.dat               | 196 +++++
 .../regress/expected/create_aggregate.out     | 163 +++++
 src/test/regress/expected/oidjoins.out        |   1 +
 src/test/regress/sql/create_aggregate.sql     | 165 +++++
 17 files changed, 2280 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..ce93f45fca 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,569 @@ 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 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;
+ 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,
+	ISAGGPARTIALFUNC = true
+);
+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 = public.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}',
+	ISAGGPARTIALFUNC = true
+);
+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 unsafe 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
+         ->  Foreign Scan
+               Output: (PARTIAL postgres_fdw_avg_p_int4(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_p_int4(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_p_int4(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_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..27e125b225 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,
+	ISAGGPARTIALFUNC = true
+);
+
+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 = public.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}',
+	ISAGGPARTIALFUNC = true
+);
+
+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 unsafe 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..bef871770f 100644
--- a/doc/src/sgml/ref/create_aggregate.sgml
+++ b/doc/src/sgml/ref/create_aggregate.sgml
@@ -42,6 +42,8 @@ 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 } ]
+    [ , ISAGGPARTIALFUNC = <replaceable class="parameter">isaggpartialfunc</replaceable> ]
+    [ , 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 +82,8 @@ 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> ]
+    [ , ISAGGPARTIALFUNC = <replaceable class="parameter">isaggpartialfunc</replaceable> ]
+    [ , AGGPARTIALFUNC = <replaceable class="parameter">aggpartialfunc</replaceable> ]
 )
 </synopsis>
  </refsynopsisdiv>
@@ -244,6 +248,18 @@ 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.
+   See
+   <xref linkend="built-in-sharding-in-postgresql"/> for the
+   definition of aggpartialfunc.
+   An aggregate function and its aggpartialfunc can be same or different.
+   <literal>ISAGGPARTIALFUNC</literal> is a parameter indicating
+   whether they are the same or not.
+   <literal>AGGPARTIALFUNC</literal> is a parameter of aggpartialfunc.
   </para>
 
   <para>
@@ -653,6 +669,46 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">isaggpartialfunc</replaceable></term>
+    <listitem>
+     <para>
+     The logical constant indicating whether the aggregate function and
+     its aggpartialfunc are the same or different.
+     The default value of this parameter is <literal>false</literal>.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">aggpartialfunc</replaceable></term>
+    <listitem>
+     <para>
+      The name of the aggpartialfunc corresponding to the aggregate function.
+      If <replaceable class="parameter">isaggpartialfunc</replaceable>
+      is <literal>true</literal>, then this parameter will be ignored.
+      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..c25e3b980c 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -63,6 +63,8 @@ AggregateCreate(const char *aggName,
 				List *aggmtransfnName,
 				List *aggminvtransfnName,
 				List *aggmfinalfnName,
+				bool isaggpartialfn,
+				List *aggpartialfnName,
 				bool finalfnExtraArgs,
 				bool mfinalfnExtraArgs,
 				char finalfnModify,
@@ -91,6 +93,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 +572,83 @@ AggregateCreate(const char *aggName,
 							format_type_be(finaltype))));
 	}
 
+	/*
+	 * Validate the aggpartialfunc, if present.
+	 */
+	if (aggpartialfnName || isaggpartialfn)
+	{
+		if(!aggcombinefnName)
+			elog(ERROR, "aggcombinefnName must be supplied if aggpartialfnName is supplied");
+
+		if(isaggpartialfn)
+		{
+			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 +764,9 @@ AggregateCreate(const char *aggName,
 		values[Anum_pg_aggregate_aggminitval - 1] = CStringGetTextDatum(aggminitval);
 	else
 		nulls[Anum_pg_aggregate_aggminitval - 1] = true;
+	if(isaggpartialfn)
+		aggpartialfn = procOid;
+	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..12c6b29ac4 100644
--- a/src/backend/commands/aggregatecmds.c
+++ b/src/backend/commands/aggregatecmds.c
@@ -70,9 +70,11 @@ DefineAggregate(ParseState *pstate,
 	List	   *combinefuncName = NIL;
 	List	   *serialfuncName = NIL;
 	List	   *deserialfuncName = NIL;
+	List	   *aggpartialfuncName = NIL;
 	List	   *mtransfuncName = NIL;
 	List	   *minvtransfuncName = NIL;
 	List	   *mfinalfuncName = NIL;
+	bool        isaggpartialfunc = false;
 	bool		finalfuncExtraArgs = false;
 	bool		mfinalfuncExtraArgs = false;
 	char		finalfuncModify = 0;
@@ -143,6 +145,10 @@ DefineAggregate(ParseState *pstate,
 			serialfuncName = defGetQualifiedName(defel);
 		else if (strcmp(defel->defname, "deserialfunc") == 0)
 			deserialfuncName = defGetQualifiedName(defel);
+		else if (strcmp(defel->defname, "isaggpartialfunc") == 0)
+			isaggpartialfunc = defGetBoolean(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 +467,8 @@ DefineAggregate(ParseState *pstate,
 						   mtransfuncName,	/* fwd trans function name */
 						   minvtransfuncName,	/* inv trans function name */
 						   mfinalfuncName,	/* final function name */
+						   isaggpartialfunc,
+						   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..aaae7fe285 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,8 @@ extern ObjectAddress AggregateCreate(const char *aggName,
 									 List *aggmtransfnName,
 									 List *aggminvtransfnName,
 									 List *aggmfinalfnName,
+									 bool isaggpartialfn,
+									 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..5946c4c127 100644
--- a/src/test/regress/expected/create_aggregate.out
+++ b/src/test/regress/expected/create_aggregate.out
@@ -322,3 +322,166 @@ 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}',
+	isaggpartialfunc = true
+);
+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,
+	isaggpartialfunc = true
+);
+ERROR:  udf_avg_int8_invalid is not its own aggpartialfunc
+-- invalid: aggpartialfunc which doesn't exist
+--   and false is specified for isaggpartialfunc
+CREATE AGGREGATE nonexistent_aggpartial_agg(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	finalfunc = numeric_poly_sum,
+	combinefunc = int8_avg_combine,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = int8_avg_deserialize,
+	isaggpartialfunc = false,
+	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_avg_noprivilege(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..0deda7c3a8 100644
--- a/src/test/regress/sql/create_aggregate.sql
+++ b/src/test/regress/sql/create_aggregate.sql
@@ -328,3 +328,168 @@ 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}',
+	isaggpartialfunc = true
+);
+
+-- 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,
+	isaggpartialfunc = true
+);
+
+-- invalid: aggpartialfunc which doesn't exist
+--   and false is specified for isaggpartialfunc
+CREATE AGGREGATE nonexistent_aggpartial_agg(int8) (
+	sfunc = int8_avg_accum,
+	stype = internal,
+	finalfunc = numeric_poly_sum,
+	combinefunc = int8_avg_combine,
+	serialfunc = int8_avg_serialize,
+	deserialfunc = int8_avg_deserialize,
+	isaggpartialfunc = false,
+	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_avg_noprivilege(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



^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-10 01:18       ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-13 06:12         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 06:50           ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 10:56             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-14 06:54               ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-06-02 03:54                 ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-05 09:00                   ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  2023-06-06 03:08                     ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-06 04:19                       ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  2023-06-06 12:31                         ` RE: Partial aggregates pushdown [email protected] <[email protected]>
@ 2023-06-07 09:47                           ` Alexander Pyhalov <[email protected]>
  2023-06-07 23:08                             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  0 siblings, 1 reply; 35+ messages in thread

From: Alexander Pyhalov @ 2023-06-07 09:47 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: Bruce Momjian <[email protected]>; pgsql-hackers; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

[email protected] писал 2023-06-06 15:31:
> Thanks for the explanation.
> I understand that the method of comparing two function name strings is
> incorrect.
> Instead, I added the parameter isaggpartialfunc indicating whether the 
> aggregate
> function and its aggpartialfunc are the same or different.

Hi.

This seems to be more robust, but the interface became more strange.
I'm not sure what to do with it. Some ideas I had to avoid introducing 
this parameter. Not sure I like any of them.

1) You can use QualifiedNameGetCreationNamespace() for aggpartialfnName 
and still compare namespace and function name  for it and  aggName, 
aggNamespace.
Seems to be not ideal, but avoids introducing new parameters.

2) You can lookup for partial aggregate function after ProcedureCreate() 
in AggregateCreate(), if it wasn't found at earlier stages. If it is the 
aggregate itself - check it. If it's still not found, error out. Also 
seems to be a bit ugly - you leave uncommitted garbage for vacuum in 
catalogue.


Another issue - the patch misses recording dependency between 
aggpartialfn and aggregate procedure.

-- 
Best regards,
Alexander Pyhalov,
Postgres Professional






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* RE: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-10 01:18       ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-13 06:12         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 06:50           ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 10:56             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-14 06:54               ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-06-02 03:54                 ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-05 09:00                   ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  2023-06-06 03:08                     ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-06 04:19                       ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  2023-06-06 12:31                         ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-07 09:47                           ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
@ 2023-06-07 23:08                             ` [email protected] <[email protected]>
  2023-06-08 07:39                               ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  0 siblings, 1 reply; 35+ messages in thread

From: [email protected] @ 2023-06-07 23:08 UTC (permalink / raw)
  To: Alexander Pyhalov <[email protected]>; +Cc: Bruce Momjian <[email protected]>; pgsql-hackers; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>; [email protected] <[email protected]>

Hi Mr.Pyhalov.

Thank you for comments.

> From: Alexander Pyhalov <[email protected]>
> Sent: Wednesday, June 7, 2023 6:47 PM
> This seems to be more robust, but the interface became more strange.
> I'm not sure what to do with it. Some ideas I had to avoid introducing this
> parameter. Not sure I like any of them.
> 
> 1) You can use QualifiedNameGetCreationNamespace() for aggpartialfnName
> and still compare namespace and function name  for it and  aggName,
> aggNamespace.
> Seems to be not ideal, but avoids introducing new parameters.
> 
> 2) You can lookup for partial aggregate function after ProcedureCreate() in
> AggregateCreate(), if it wasn't found at earlier stages. If it is the aggregate itself
> - check it. If it's still not found, error out. Also seems to be a bit ugly - you leave
> uncommitted garbage for vacuum in catalogue.
Thank you for suggesting alternatives.
The disadvantages of alternative 2) appear to be undesirable, 
I have modified it according to alternative 1)

> Another issue - the patch misses recording dependency between aggpartialfn
> and aggregate procedure.
I added code to record dependencys between aggpartialfn
and aggregate procedure, similar to the code for functions such as combinefunc.

Sincerely yours,
Yuuki Fujii

--
Yuuki Fujii
Information Technology R&D Center Mitsubishi Electric Corporation


Attachments:

  [application/octet-stream] 0001-Partial-aggregates-push-down-v21.patch (235.0K, ../../OS3PR01MB66607A988602D953D37482389553A@OS3PR01MB6660.jpnprd01.prod.outlook.com/2-0001-Partial-aggregates-push-down-v21.patch)
  download | inline diff:
From 94a62dc6bd60b9a80261c71480b1baffab400a8e Mon Sep 17 00:00:00 2001
From: Yuki Fujii <[email protected]>
Date: Thu, 8 Jun 2023 03:50:16 -0400
Subject: [PATCH] Partial aggregates push down v21

---
 contrib/postgres_fdw/deparse.c                |  71 +-
 .../postgres_fdw/expected/postgres_fdw.out    | 607 +++++++++++++++-
 contrib/postgres_fdw/postgres_fdw.c           |  18 +-
 contrib/postgres_fdw/sql/postgres_fdw.sql     | 281 +++++++-
 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            | 100 +++
 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     | 184 +++++
 src/test/regress/expected/oidjoins.out        |   1 +
 src/test/regress/sql/create_aggregate.sql     | 183 +++++
 17 files changed, 2320 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..feffbc4c9b 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,571 @@ 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 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;
+ 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 SCHEMA postgres_fdw_test;
+CREATE AGGREGATE postgres_fdw_test.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_test.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_test.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_test.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 postgres_fdw_test.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 postgres_fdw_test.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 postgres_fdw_test.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_test.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 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_test.postgres_fdw_sum_p_int8(b::int8) FROM pagg_tab;
+                                           QUERY PLAN                                            
+-------------------------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: postgres_fdw_test.postgres_fdw_sum_p_int8((pagg_tab.b)::bigint)
+   ->  Append
+         ->  Partial Aggregate
+               Output: PARTIAL postgres_fdw_test.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_test.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_test.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_test.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_test.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
+         ->  Foreign Scan
+               Output: (PARTIAL postgres_fdw_avg_p_int4(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_p_int4(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_p_int4(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_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_test.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_test.postgres_fdw_sum_p_int8(int8);
+DROP SCHEMA postgres_fdw_test;
+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..49a4b876ad 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,262 @@ 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 SCHEMA postgres_fdw_test;
+CREATE AGGREGATE postgres_fdw_test.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_test.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_test.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_test.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_test.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 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_test.postgres_fdw_sum_p_int8(b::int8) FROM pagg_tab;
+SELECT postgres_fdw_test.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_test.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_test.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_test.postgres_fdw_sum_p_int8(int8);
+DROP SCHEMA postgres_fdw_test;
+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..35a2c10359 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,15 @@ 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.
+   See
+   <xref linkend="built-in-sharding-in-postgresql"/> for the
+   definition of aggpartialfunc.
+   <literal>AGGPARTIALFUNC</literal> is a parameter of aggpartialfunc.
   </para>
 
   <para>
@@ -653,6 +664,36 @@ 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.
+      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..0c6e07988a 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,8 @@ AggregateCreate(const char *aggName,
 	Oid			mtransfn = InvalidOid;	/* can be omitted */
 	Oid			minvtransfn = InvalidOid;	/* can be omitted */
 	Oid			mfinalfn = InvalidOid;	/* can be omitted */
+	bool        isaggpartialfn = false;
+	Oid			aggpartialfn = InvalidOid;	/* can be omitted */
 	Oid			sortop = InvalidOid;	/* can be omitted */
 	Oid		   *aggArgTypes = parameterTypes->values;
 	bool		mtransIsStrict = false;
@@ -569,6 +572,93 @@ AggregateCreate(const char *aggName,
 							format_type_be(finaltype))));
 	}
 
+	/*
+	 * Validate the aggpartialfunc, if present.
+	 */
+	if (aggpartialfnName)
+	{
+		char	   *aggpartialName;
+		Oid			aggpartialNamespace;
+		if(!aggcombinefnName)
+			elog(ERROR, "aggcombinefnName must be supplied if aggpartialfnName is supplied");
+
+		/* Convert list of names to a name and namespace */
+		aggpartialNamespace = QualifiedNameGetCreationNamespace(aggpartialfnName,
+			&aggpartialName);
+
+		if((aggNamespace == aggpartialNamespace)
+			&& (strcmp(aggName, aggpartialName) == 0))
+		{
+			if(((aggTransType != INTERNALOID) && (finalfn != InvalidOid))
+					|| ((aggTransType == INTERNALOID) && (finalfn != serialfn)))
+				elog(ERROR, "%s is not its own aggpartialfunc", aggName);
+			isaggpartialfn = true;
+		}
+		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 +774,9 @@ AggregateCreate(const char *aggName,
 		values[Anum_pg_aggregate_aggminitval - 1] = CStringGetTextDatum(aggminitval);
 	else
 		nulls[Anum_pg_aggregate_aggminitval - 1] = true;
+	if(isaggpartialfn)
+		aggpartialfn = procOid;
+	values[Anum_pg_aggregate_aggpartialfn - 1] = ObjectIdGetDatum(aggpartialfn);
 
 	if (replace)
 		oldtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(procOid));
@@ -805,6 +898,13 @@ AggregateCreate(const char *aggName,
 		add_exact_object_address(&referenced, addrs);
 	}
 
+	/* Depends on aggpartialfunc, if any */
+	if (OidIsValid(aggpartialfn))
+	{
+		ObjectAddressSet(referenced, ProcedureRelationId, aggpartialfn);
+		add_exact_object_address(&referenced, addrs);
+	}
+
 	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
 	free_object_addresses(addrs);
 	return myself;
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..73e4cfde0f 100644
--- a/src/test/regress/expected/create_aggregate.out
+++ b/src/test/regress/expected/create_aggregate.out
@@ -322,3 +322,187 @@ 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 nonexistent_aggpartial_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_avg_noprivilege(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;
+-- An aggregate function that is aggpartialfunc
+-- of another aggregate cannot be deleted.
+CREATE AGGREGATE udf_avg_int4_p(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	combinefunc = int4_avg_combine,
+	initcond = '{0,0}',
+	aggpartialfunc = udf_avg_int4_p
+);
+CREATE AGGREGATE udf_avg_int4(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	finalfunc = int8_avg,
+	combinefunc = int4_avg_combine,
+	initcond = '{0,0}',
+	aggpartialfunc = udf_avg_int4_p
+);
+DROP AGGREGATE udf_avg_int4_p(int4);
+ERROR:  cannot drop function udf_avg_int4_p(integer) because other objects depend on it
+DETAIL:  function udf_avg_int4(integer) depends on function udf_avg_int4_p(integer)
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+DROP AGGREGATE udf_avg_int4_p(int4) CASCADE;
+NOTICE:  drop cascades to function udf_avg_int4(integer)
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..e90e0ad2d6 100644
--- a/src/test/regress/sql/create_aggregate.sql
+++ b/src/test/regress/sql/create_aggregate.sql
@@ -328,3 +328,186 @@ 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 nonexistent_aggpartial_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_avg_noprivilege(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;
+
+-- An aggregate function that is aggpartialfunc
+-- of another aggregate cannot be deleted.
+CREATE AGGREGATE udf_avg_int4_p(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	combinefunc = int4_avg_combine,
+	initcond = '{0,0}',
+	aggpartialfunc = udf_avg_int4_p
+);
+CREATE AGGREGATE udf_avg_int4(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	finalfunc = int8_avg,
+	combinefunc = int4_avg_combine,
+	initcond = '{0,0}',
+	aggpartialfunc = udf_avg_int4_p
+);
+DROP AGGREGATE udf_avg_int4_p(int4);
+DROP AGGREGATE udf_avg_int4_p(int4) CASCADE;
-- 
2.31.1



^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-10 01:18       ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-13 06:12         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 06:50           ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 10:56             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-14 06:54               ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-06-02 03:54                 ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-05 09:00                   ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  2023-06-06 03:08                     ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-06 04:19                       ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  2023-06-06 12:31                         ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-07 09:47                           ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  2023-06-07 23:08                             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
@ 2023-06-08 07:39                               ` Alexander Pyhalov <[email protected]>
  2023-06-09 11:38                                 ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  0 siblings, 1 reply; 35+ messages in thread

From: Alexander Pyhalov @ 2023-06-08 07:39 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: Bruce Momjian <[email protected]>; pgsql-hackers; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

[email protected] писал 2023-06-08 02:08:
>> From: Alexander Pyhalov <[email protected]>
>> Sent: Wednesday, June 7, 2023 6:47 PM
>> This seems to be more robust, but the interface became more strange.
>> I'm not sure what to do with it. Some ideas I had to avoid introducing 
>> this
>> parameter. Not sure I like any of them.
>> 
>> 1) You can use QualifiedNameGetCreationNamespace() for 
>> aggpartialfnName
>> and still compare namespace and function name  for it and  aggName,
>> aggNamespace.
>> Seems to be not ideal, but avoids introducing new parameters.
>> 
>> 2) You can lookup for partial aggregate function after 
>> ProcedureCreate() in
>> AggregateCreate(), if it wasn't found at earlier stages. If it is the 
>> aggregate itself
>> - check it. If it's still not found, error out. Also seems to be a bit 
>> ugly - you leave
>> uncommitted garbage for vacuum in catalogue.
> Thank you for suggesting alternatives.
> The disadvantages of alternative 2) appear to be undesirable,
> I have modified it according to alternative 1)
> 
>> Another issue - the patch misses recording dependency between 
>> aggpartialfn
>> and aggregate procedure.
> I added code to record dependencys between aggpartialfn
> and aggregate procedure, similar to the code for functions such as 
> combinefunc.
> 

Hi.

Looks better. The only question I have is should we record dependency 
between procOid and aggpartialfn if aggpartialfn == procOid.

Also it seems new code likely should be run through pgindent.

doc/src/sgml/postgres-fdw.sgml:

+   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

No space between "true" and "(" in "is true(this condition".

Some sentences in documentation, like one starting with
"For aggregate expressions, this sending is active if conditions in..."
seem to be too long, but I'm not the best man to read out documentation.

In "Built-in sharding in PostgreSQL" term "shard" doesn't have a 
definition.

By the way, I'm not sure that "sharding" documentation belongs to this 
patch,
at least it needs a review from native speaker.

-- 
Best regards,
Alexander Pyhalov,
Postgres Professional






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* RE: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-10 01:18       ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-13 06:12         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 06:50           ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 10:56             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-14 06:54               ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-06-02 03:54                 ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-05 09:00                   ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  2023-06-06 03:08                     ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-06 04:19                       ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  2023-06-06 12:31                         ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-07 09:47                           ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  2023-06-07 23:08                             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-08 07:39                               ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
@ 2023-06-09 11:38                                 ` [email protected] <[email protected]>
  2023-06-09 14:09                                   ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  0 siblings, 1 reply; 35+ messages in thread

From: [email protected] @ 2023-06-09 11:38 UTC (permalink / raw)
  To: Alexander Pyhalov <[email protected]>; +Cc: Bruce Momjian <[email protected]>; pgsql-hackers; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>; [email protected] <[email protected]>

Hi Mr.Pyhalov.

Thank you for comments.

> From: Alexander Pyhalov <[email protected]>
> Sent: Thursday, June 8, 2023 4:40 PM
> Looks better. The only question I have is should we record dependency
> between procOid and aggpartialfn if aggpartialfn == procOid.
Fixed.

> From: Alexander Pyhalov <[email protected]>
> Sent: Thursday, June 8, 2023 4:40 PM
> Also it seems new code likely should be run through pgindent.
Done.

> From: Alexander Pyhalov <[email protected]>
> Sent: Thursday, June 8, 2023 4:40 PM
> doc/src/sgml/postgres-fdw.sgml:
> 
> +   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
> 
> No space between "true" and "(" in "is true(this condition".
Fixed.

> From: Alexander Pyhalov <[email protected]>
> Sent: Thursday, June 8, 2023 4:40 PM
> Some sentences in documentation, like one starting with "For aggregate
> expressions, this sending is active if conditions in..."
> seem to be too long, but I'm not the best man to read out documentation.
Fixed.

> From: Alexander Pyhalov <[email protected]>
> Sent: Thursday, June 8, 2023 4:40 PM
> In "Built-in sharding in PostgreSQL" term "shard" doesn't have a definition.
I have removed the sentence you pointed out.

> From: Alexander Pyhalov <[email protected]>
> Sent: Thursday, June 8, 2023 4:40 PM
> By the way, I'm not sure that "sharding" documentation belongs to this patch, at
> least it needs a review from native speaker.
I removed general description of sharding.

Sincerely yours,
Yuuki Fujii

--
Yuuki Fujii
Information Technology R&D Center Mitsubishi Electric Corporation


Attachments:

  [application/octet-stream] 0001-Partial-aggregates-push-down-v22.patch (235.1K, ../../OS3PR01MB666044B5BDCE90EC5B50DEB69551A@OS3PR01MB6660.jpnprd01.prod.outlook.com/2-0001-Partial-aggregates-push-down-v22.patch)
  download | inline diff:
From fc104eef02781a654a6057718cef81dafdfa44ed Mon Sep 17 00:00:00 2001
From: Yuki Fujii <[email protected]>
Date: Fri, 9 Jun 2023 16:21:47 -0400
Subject: [PATCH] Partial aggregates push down v22

---
 contrib/postgres_fdw/deparse.c                |  72 +-
 .../postgres_fdw/expected/postgres_fdw.out    | 607 +++++++++++++++-
 contrib/postgres_fdw/postgres_fdw.c           |  18 +-
 contrib/postgres_fdw/sql/postgres_fdw.sql     | 281 +++++++-
 doc/src/sgml/catalogs.sgml                    |  13 +
 doc/src/sgml/postgres-fdw.sgml                | 107 ++-
 doc/src/sgml/ref/create_aggregate.sgml        |  40 ++
 doc/src/sgml/xaggr.sgml                       |  12 +-
 src/backend/catalog/pg_aggregate.c            | 101 +++
 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     | 184 +++++
 src/test/regress/expected/oidjoins.out        |   1 +
 src/test/regress/sql/create_aggregate.sql     | 183 +++++
 17 files changed, 2335 insertions(+), 177 deletions(-)

diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 09d6dd60dd..4efaf04f85 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,34 @@ 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 +4068,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..feffbc4c9b 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,571 @@ 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 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;
+ 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 SCHEMA postgres_fdw_test;
+CREATE AGGREGATE postgres_fdw_test.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_test.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_test.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_test.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 postgres_fdw_test.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 postgres_fdw_test.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 postgres_fdw_test.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_test.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 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_test.postgres_fdw_sum_p_int8(b::int8) FROM pagg_tab;
+                                           QUERY PLAN                                            
+-------------------------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: postgres_fdw_test.postgres_fdw_sum_p_int8((pagg_tab.b)::bigint)
+   ->  Append
+         ->  Partial Aggregate
+               Output: PARTIAL postgres_fdw_test.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_test.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_test.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_test.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_test.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
+         ->  Foreign Scan
+               Output: (PARTIAL postgres_fdw_avg_p_int4(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_p_int4(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_p_int4(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_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_test.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_test.postgres_fdw_sum_p_int8(int8);
+DROP SCHEMA postgres_fdw_test;
+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..339968adf1 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..49a4b876ad 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,262 @@ 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 SCHEMA postgres_fdw_test;
+CREATE AGGREGATE postgres_fdw_test.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_test.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_test.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_test.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_test.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 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_test.postgres_fdw_sum_p_int8(b::int8) FROM pagg_tab;
+SELECT postgres_fdw_test.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_test.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_test.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_test.postgres_fdw_sum_p_int8(int8);
+DROP SCHEMA postgres_fdw_test;
+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..565629b821 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -644,6 +644,19 @@
        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>
+       Partial aggregate function (zero if none).
+       See
+       <xref linkend="built-in-sharding-in-postgresql"/> for the
+       definition of partial aggregate function.
+      </para></entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml
index 5062d712e7..8a67c49476 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,74 @@ postgres=# SELECT postgres_fdw_disconnect_all();
   </para>
  </sect2>
 
+ <sect2 id="built-in-sharding-in-postgresql">
+  <title>Built-in sharding in PostgreSQL</title>
+  <para>
+   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 such as <literal>WHERE</literal> clauses,
+   <literal>JOIN</literal> clauses, aggregate expressions are sent to
+   a <filename>postgres_fdw</filename> node if multiple conditions are true.
+   For <literal>WHERE</literal> clauses and
+   <literal>JOIN</literal> clauses, this sending is active if
+   the conditions in <xref linkend="postgres-fdw-remote-query-optimization"/>
+   are true and <varname>enable_partitionwise_join</varname> is true (this condition
+   is required for <literal>JOIN</literal> clauses only).
+   For aggregate expressions, this sending is active when the following
+   conditions are true:
+   <itemizedlist spacing="compact">
+     <listitem>
+      <para>
+       An aggregate function, called the partial aggregate function for partial aggregate
+       that corresponding to the aggregate function, is defined on the primary node and
+       the <filename>postgres_fdw</filename> node.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       The conditions in <xref linkend="postgres-fdw-remote-query-optimization"/> are true.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       The query doesn't contain a <literal>HAVING</literal> clause.
+      </para>
+     </listitem>
+     <listitem>
+      <para>
+       Aggregate expressions in the query don't contain
+       a <literal>DISTINCT</literal> or <literal>ORDER BY</literal> clause.
+      </para>
+     </listitem>
+    </itemizedlist>
+   If the data type of the state value is not internal, the partial aggregate
+   function has no final function.
+   Otherwise, the partial aggregate function must have the same final function
+   as the aggregate function's serialization function.
+   The partial aggregate function and the aggregate function have
+   the same transition function and data type of the state value.
+   If the partial aggregate function has any of the following members:
+   initial state, combine function, serialization function, or deserialization
+   function, the members must match with the corresponding members in the
+   aggregate function.
+   For all built-in aggregate functions that support partial aggregation the partial
+   aggregate function that corresponds to this function is defined as a built-in
+   aggregate function.
+   <filename>postgres_fdw</filename> assumes that for each built-in aggregate
+   function, the partial aggregate function is defined on
+   the primary node and the <filename>postgres_fdw</filename> node as well.
+   <filename>postgres_fdw</filename> assumes that a user-defined aggregate
+   function has the partial aggregate function on the <filename>postgres_fdw</filename>
+   node only if both belong to an extension that is listed in the foreign server's
+   extensions option.
+   When this sending is active for aggregate expressions, any aggregate on
+   the <filename>postgres_fdw</filename> node is a partial aggregate in the primary
+   node and any aggregate function is replaced by the partial aggregate function
+   in the remote query.
+  </para>
+ </sect2>
+
  <sect2 id="postgres-fdw-remote-query-execution-environment">
   <title>Remote Query Execution Environment</title>
 
@@ -1031,14 +1101,29 @@ 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 over foreign tables that are partitions
+   of some other table.
+   When using built-in sharding feature in PostgreSQL is used,
+   the partial aggregate function of the aggregate function is sent to
+   the <filename>postgres_fdw</filename> nodes if the conditions in
+   <xref linkend="built-in-sharding-in-postgresql"/> are met.
+   A limitation is that <filename>postgres_fdw</filename> assumes that if
+   the partial aggregate function exists on the primary node, the partial aggregate
+   function also exists on the <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 also be worked around as well by rewriting the query as
+   described above.
   </para>
  </sect2>
 
diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml
index 222e0aa5c9..943a5c1da7 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,16 @@ 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.
+
+   If you use are using the built-in sharding feature in PostgreSQL,
+   <filename>postgres_fdw</filename> needs an aggregate function
+   called the <firstterm>partial aggregate function</firstterm> to
+   aggregate on the <filename>postgres_fdw</filename> node.
+   See
+   <xref linkend="built-in-sharding-in-postgresql"/> for the
+   definition of the partial aggregate function.
+   <literal>AGGPARTIALFUNC</literal> is a parameter of
+   the partial aggregate function.
   </para>
 
   <para>
@@ -653,6 +665,34 @@ 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 partial aggregate function.
+      If the <replaceable class="parameter">name</replaceable> and the
+      <replaceable class="parameter">aggpartialfunc</replaceable> are the same,
+      then the aggregate function must be the partial aggregate function.
+      If the <replaceable class="parameter">aggpartialfunc</replaceable>
+      is specified, the <replaceable class="parameter">combinefunc</replaceable>
+      must be specified.
+      If the <replaceable class="parameter">state_data_type</replaceable> is not
+      <type>internal</type>, then the partial aggregate function must not have
+      the <replaceable class="parameter">ffunc</replaceable>.
+      If the <replaceable class="parameter">state_data_type</replaceable> is
+      <type>internal</type>, the partial aggregate function must have the same
+      final function as the <replaceable class="parameter">serialfunc</replaceable>.
+      The partial aggregate function and the aggregate function have
+      the same <replaceable class="parameter">sfunc</replaceable> and
+      <replaceable class="parameter">state_data_type</replaceable>.
+      If the partial aggregate function has any of the following members:
+      initial state, combine function, serialization function, or deserialization
+      function, the members must match with the corresponding members in the
+      aggregate function.
+     </para>
+    </listitem>
+   </varlistentry>
   </variablelist>
 
   <para>
diff --git a/doc/src/sgml/xaggr.sgml b/doc/src/sgml/xaggr.sgml
index bdad8d3dc2..0ff2e0037b 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>
+   If you are using the built-in sharding feature in PostgreSQL,
+   <filename>postgres_fdw</filename> needs an aggregate function
+   called the <firstterm>partial aggregate function</firstterm> for
+   aggregate on <filename>postgres_fdw</filename> node. See
+   <xref linkend="built-in-sharding-in-postgresql"/> for the
+   definition of the partial aggregate function.
+  </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..aaf1e62762 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,8 @@ AggregateCreate(const char *aggName,
 	Oid			mtransfn = InvalidOid;	/* can be omitted */
 	Oid			minvtransfn = InvalidOid;	/* can be omitted */
 	Oid			mfinalfn = InvalidOid;	/* can be omitted */
+	bool		isaggpartialfn = false;
+	Oid			aggpartialfn = InvalidOid;	/* can be omitted */
 	Oid			sortop = InvalidOid;	/* can be omitted */
 	Oid		   *aggArgTypes = parameterTypes->values;
 	bool		mtransIsStrict = false;
@@ -569,6 +572,94 @@ AggregateCreate(const char *aggName,
 							format_type_be(finaltype))));
 	}
 
+	/*
+	 * Validate the aggpartialfunc, if present.
+	 */
+	if (aggpartialfnName)
+	{
+		char	   *aggpartialName;
+		Oid			aggpartialNamespace;
+
+		if (!aggcombinefnName)
+			elog(ERROR, "aggcombinefnName must be supplied if aggpartialfnName is supplied");
+
+		/* Convert list of names to a name and namespace */
+		aggpartialNamespace = QualifiedNameGetCreationNamespace(aggpartialfnName,
+																&aggpartialName);
+
+		if ((aggNamespace == aggpartialNamespace)
+			&& (strcmp(aggName, aggpartialName) == 0))
+		{
+			if (((aggTransType != INTERNALOID) && (finalfn != InvalidOid))
+				|| ((aggTransType == INTERNALOID) && (finalfn != serialfn)))
+				elog(ERROR, "%s is not its own aggpartialfunc", aggName);
+			isaggpartialfn = true;
+		}
+		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 +775,9 @@ AggregateCreate(const char *aggName,
 		values[Anum_pg_aggregate_aggminitval - 1] = CStringGetTextDatum(aggminitval);
 	else
 		nulls[Anum_pg_aggregate_aggminitval - 1] = true;
+	if (isaggpartialfn)
+		aggpartialfn = procOid;
+	values[Anum_pg_aggregate_aggpartialfn - 1] = ObjectIdGetDatum(aggpartialfn);
 
 	if (replace)
 		oldtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(procOid));
@@ -805,6 +899,13 @@ AggregateCreate(const char *aggName,
 		add_exact_object_address(&referenced, addrs);
 	}
 
+	/* Depends on aggpartialfunc, if any */
+	if (OidIsValid(aggpartialfn) && (aggpartialfn != procOid))
+	{
+		ObjectAddressSet(referenced, ProcedureRelationId, aggpartialfn);
+		add_exact_object_address(&referenced, addrs);
+	}
+
 	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
 	free_object_addresses(addrs);
 	return myself;
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..73e4cfde0f 100644
--- a/src/test/regress/expected/create_aggregate.out
+++ b/src/test/regress/expected/create_aggregate.out
@@ -322,3 +322,187 @@ 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 nonexistent_aggpartial_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_avg_noprivilege(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;
+-- An aggregate function that is aggpartialfunc
+-- of another aggregate cannot be deleted.
+CREATE AGGREGATE udf_avg_int4_p(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	combinefunc = int4_avg_combine,
+	initcond = '{0,0}',
+	aggpartialfunc = udf_avg_int4_p
+);
+CREATE AGGREGATE udf_avg_int4(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	finalfunc = int8_avg,
+	combinefunc = int4_avg_combine,
+	initcond = '{0,0}',
+	aggpartialfunc = udf_avg_int4_p
+);
+DROP AGGREGATE udf_avg_int4_p(int4);
+ERROR:  cannot drop function udf_avg_int4_p(integer) because other objects depend on it
+DETAIL:  function udf_avg_int4(integer) depends on function udf_avg_int4_p(integer)
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+DROP AGGREGATE udf_avg_int4_p(int4) CASCADE;
+NOTICE:  drop cascades to function udf_avg_int4(integer)
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..e90e0ad2d6 100644
--- a/src/test/regress/sql/create_aggregate.sql
+++ b/src/test/regress/sql/create_aggregate.sql
@@ -328,3 +328,186 @@ 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 nonexistent_aggpartial_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_avg_noprivilege(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;
+
+-- An aggregate function that is aggpartialfunc
+-- of another aggregate cannot be deleted.
+CREATE AGGREGATE udf_avg_int4_p(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	combinefunc = int4_avg_combine,
+	initcond = '{0,0}',
+	aggpartialfunc = udf_avg_int4_p
+);
+CREATE AGGREGATE udf_avg_int4(int4) (
+	sfunc = int4_avg_accum,
+	stype = _int8,
+	finalfunc = int8_avg,
+	combinefunc = int4_avg_combine,
+	initcond = '{0,0}',
+	aggpartialfunc = udf_avg_int4_p
+);
+DROP AGGREGATE udf_avg_int4_p(int4);
+DROP AGGREGATE udf_avg_int4_p(int4) CASCADE;
-- 
2.31.1



^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-10 01:18       ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-13 06:12         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 06:50           ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 10:56             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-14 06:54               ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-06-02 03:54                 ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-05 09:00                   ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  2023-06-06 03:08                     ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-06 04:19                       ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  2023-06-06 12:31                         ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-07 09:47                           ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  2023-06-07 23:08                             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-08 07:39                               ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  2023-06-09 11:38                                 ` RE: Partial aggregates pushdown [email protected] <[email protected]>
@ 2023-06-09 14:09                                   ` Alexander Pyhalov <[email protected]>
  0 siblings, 0 replies; 35+ messages in thread

From: Alexander Pyhalov @ 2023-06-09 14:09 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: Bruce Momjian <[email protected]>; pgsql-hackers; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

Hi.

+       An aggregate function, called the partial aggregate function for 
partial aggregate
+       that corresponding to the aggregate function, is defined on the 
primary node and
+       the <filename>postgres_fdw</filename> node.

Something is clearly wrong here.

+   When using built-in sharding feature in PostgreSQL is used,

And here.

Overall the code looks good to me, but I suppose that documentation 
needs further review from some native speaker.


-- 
Best regards,
Alexander Pyhalov,
Postgres Professional






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-10 01:18       ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-13 06:12         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 06:50           ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 10:56             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-14 06:54               ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-06-02 03:54                 ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-06-05 09:00                   ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
  2023-06-06 03:08                     ` RE: Partial aggregates pushdown [email protected] <[email protected]>
@ 2023-06-09 16:44                       ` Bruce Momjian <[email protected]>
  1 sibling, 0 replies; 35+ messages in thread

From: Bruce Momjian @ 2023-06-09 16:44 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: Alexander Pyhalov <[email protected]>; pgsql-hackers; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

On Tue, Jun  6, 2023 at 03:08:50AM +0000, [email protected] wrote:
> > I've seen this message. But introduction of new built-in function will break
> > requests to old servers only if this new function is used in the request (this
> > means that query changes). However, this patch changes the behavior of old
> > queries, which worked prior to update. This seems to be different to me.
> 
> You are right.
> However, for now, partial aggregates pushdown is mainly used when using built-in sharding in PostgreSQL.
> I believe when using built-in sharding in PostgreSQL, the version of the primary node server and
> the version of the remote server will usually be the same.
> So I think it would be sufficient to include in the documentation a note about such problem
> and a workaround for them.

I agree that this feature is designed for built-in sharding, but it is
possible people could be using aggregates on partitions backed by
foreign tables without sharding.  Adding a requirement for non-sharding
setups to need PG 17+ servers might be unreasonable.

Looking at previous release note incompatibilities, we don't normally
change non-administrative functions in a way that causes errors if run
on older servers.  Based on Alexander's observations, I wonder if we
need to re-add the postgres_fdw option to control partial aggregate
pushdown, and default it to enabled.

If we ever add more function breakage we might need more postgres_fdw
options.  Fortunately, such changes are rare.

Yuki, thank you for writing and updating this patch, and Alexander,
thank you for helping with this patch.

-- 
  Bruce Momjian  <[email protected]>        https://momjian.us
  EDB                                      https://enterprisedb.com

  Only you can decide what is important to you.






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* Re: Partial aggregates pushdown
  2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-10 01:18       ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-13 06:12         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 06:50           ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-04-13 10:56             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
  2023-04-14 06:54               ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
  2023-06-02 03:54                 ` RE: Partial aggregates pushdown [email protected] <[email protected]>
@ 2023-06-06 00:10                   ` Bruce Momjian <[email protected]>
  1 sibling, 0 replies; 35+ messages in thread

From: Bruce Momjian @ 2023-06-06 00:10 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: pgsql-hackers; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Alexander Pyhalov <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>

On Fri, Jun  2, 2023 at 03:54:06AM +0000, [email protected] wrote:
> Hi Mr.Bruce, hackers.
> 
> I updated the patch.
> The following is a list of comments received on the previous version of the patch
> and my update to them in this version of the patch.

This thread started in October 2021 so I would like to explain what this
feature adds.

Basically for partitions made up of postgres_fdw tables, there are four
possible optimizations:

1.  Pruning, 3 stages, see slide 30 here:

	https://momjian.us/main/writings/pgsql/partitioning.pdf#page=30

2.  Parallelism across partitions, see slide 38 here:

	https://momjian.us/main/writings/pgsql/beyond.pdf#page=38

3.  Pushdown of partition-wise joins and aggregates, see slide 43 here:

	https://momjian.us/main/writings/pgsql/partitioning.pdf#page=43

4.  Pushdown of aggregates that aren't partition-wise

As far as I know, over the years we have accomplished all of these
items, except for #4.  #3 involves aggregates where the GROUP BY or
JOINed tables match the partition keys.

Number 4 involves things like a SUM our COUNT that does not match the
partition key, or has no groupings at all.

#3 is easier than #4 since we just need to pass _rows_ back from the
foreign servers.  #4 is more complex because _partial_ count/sum, or
even average values must be passed from the foreign servers to the
requesting server.

The good news is that we already have partial aggregate support as part
of our parallel aggregate feature, see:

	https://momjian.us/main/writings/pgsql/beyond.pdf#page=38

What the patch does is to expand the existing partial aggregate code to
allow partial aggregate results to pass from the foreign servers to the
requesting server.  This feature will be very useful for data warehouse
queries that need to compute aggregate across partitions.

-- 
  Bruce Momjian  <[email protected]>        https://momjian.us
  EDB                                      https://enterprisedb.com

  Only you can decide what is important to you.






^ permalink  raw  reply  [nested|flat] 35+ messages in thread

* use correct variable in error message in _allocAH function (pg_backup_archiver.c)
@ 2025-04-14 22:21 Mahendra Singh Thalor <[email protected]>
  0 siblings, 0 replies; 35+ messages in thread

From: Mahendra Singh Thalor @ 2025-04-14 22:21 UTC (permalink / raw)
  To: PostgreSQL Hackers <[email protected]>

Hi hackers,

In _allocAH function(pg_backup_archiver.c), we were using the *fmt*
variable in switch case for *default case*, but correct variable is
AH->format.

Here, I am attaching a patch for the same.

-- 
Thanks and Regards
Mahendra Singh Thalor
EnterpriseDB: http://www.enterprisedb.com


Attachments:

  [application/octet-stream] v01-use-correct-variable-in-error-message-in-_allocAH-fun.patch (887B, ../../CAKYtNAqu+N-Ab2Fq6wzNSOm_-0N-BMneanYNV1+6kFDXjva1Eg@mail.gmail.com/3-v01-use-correct-variable-in-error-message-in-_allocAH-fun.patch)
  download | inline diff:
From b9ec096c2ed98e03aa95682a6ac243c2c1fc45e4 Mon Sep 17 00:00:00 2001
From: Mahendra Singh Thalor <[email protected]>
Date: Tue, 15 Apr 2025 03:43:01 +0530
Subject: [PATCH] use correct variable in error message in _allocAH function.

In _allocAH function, we were using fmt variable in switch case, but
correct is AH->format.
---
 src/bin/pg_dump/pg_backup_archiver.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index f961162f365..afa42337b11 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -2469,7 +2469,7 @@ _allocAH(const char *FileSpec, const ArchiveFormat fmt,
 			break;
 
 		default:
-			pg_fatal("unrecognized file format \"%d\"", fmt);
+			pg_fatal("unrecognized file format \"%d\"", AH->format);
 	}
 
 	return AH;
-- 
2.39.3



^ permalink  raw  reply  [nested|flat] 35+ messages in thread


end of thread, other threads:[~2025-04-14 22:21 UTC | newest]

Thread overview: 35+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2022-05-27 03:24 [PATCH 13/19] another way to install uri-regress/uri_regress Justin Pryzby <[email protected]>
2022-05-27 03:24 [PATCH 14/21] another way to install uri-regress/uri_regress Justin Pryzby <[email protected]>
2022-05-27 03:24 [PATCH 13/19] another way to install uri-regress/uri_regress Justin Pryzby <[email protected]>
2022-05-27 03:24 [PATCH 14/21] another way to install uri-regress/uri_regress Justin Pryzby <[email protected]>
2022-05-27 03:24 [PATCH 13/19] another way to install uri-regress/uri_regress Justin Pryzby <[email protected]>
2023-03-31 05:49 RE: Partial aggregates pushdown [email protected] <[email protected]>
2023-04-07 01:59 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
2023-04-07 09:25   ` RE: Partial aggregates pushdown [email protected] <[email protected]>
2023-04-08 01:50     ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
2023-04-08 01:55       ` Re: Partial aggregates pushdown Tom Lane <[email protected]>
2023-04-08 02:34         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
2023-04-08 02:44           ` Re: Partial aggregates pushdown Tom Lane <[email protected]>
2023-04-08 02:53             ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
2023-04-08 03:04               ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
2023-04-08 04:16               ` Re: Partial aggregates pushdown Andres Freund <[email protected]>
2023-04-08 14:15                 ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
2023-04-08 16:18                   ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
2023-04-10 01:18       ` RE: Partial aggregates pushdown [email protected] <[email protected]>
2023-04-13 06:12         ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
2023-04-13 06:50           ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
2023-04-13 10:56             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
2023-04-14 06:54               ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
2023-06-02 03:54                 ` RE: Partial aggregates pushdown [email protected] <[email protected]>
2023-06-05 09:00                   ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
2023-06-06 03:08                     ` RE: Partial aggregates pushdown [email protected] <[email protected]>
2023-06-06 04:19                       ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
2023-06-06 12:31                         ` RE: Partial aggregates pushdown [email protected] <[email protected]>
2023-06-07 09:47                           ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
2023-06-07 23:08                             ` RE: Partial aggregates pushdown [email protected] <[email protected]>
2023-06-08 07:39                               ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
2023-06-09 11:38                                 ` RE: Partial aggregates pushdown [email protected] <[email protected]>
2023-06-09 14:09                                   ` Re: Partial aggregates pushdown Alexander Pyhalov <[email protected]>
2023-06-09 16:44                       ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
2023-06-06 00:10                   ` Re: Partial aggregates pushdown Bruce Momjian <[email protected]>
2025-04-14 22:21 use correct variable in error message in _allocAH function (pg_backup_archiver.c) Mahendra Singh Thalor <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox