public inbox for [email protected]  
help / color / mirror / Atom feed
RE: Partial aggregates pushdown
38+ messages / 6 participants
[nested] [flat]

* RE: Partial aggregates pushdown
@ 2023-06-12 08:51  [email protected] <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: [email protected] @ 2023-06-12 08:51 UTC (permalink / raw)
  To: Bruce Momjian <[email protected]>; Alexander Pyhalov <[email protected]>; pgsql-hackers; +Cc: 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.Bruce, Mr.Pyhalov, hackers.

Thank you for comments. I will try to respond to both of your comments as follows.
I plan to start revising the patch next week. If you have any comments on the following
respondences, I would appreciate it if you could give them to me this week.

> From: Bruce Momjian <[email protected]>
> Sent: Saturday, June 10, 2023 1:44 AM
> 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.
Indeed, it is possible to use partial aggregate pushdown feature for purposes other than sharding.
The description of the section "F.38.6. Built-in sharding in PostgreSQL" assumes the use of
Built-in sharding and will be modified to eliminate this assumption.
The title of this section should be changed to something like "Aggregate on partitioned table".

> From: Bruce Momjian <[email protected]>
> Sent: Saturday, June 10, 2023 1:44 AM
> 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.

I understand what the problem is. I will put a mechanism maintaining compatibility into the patch.
I believe there are three approaches.
Approach 1-1 is preferable because it does not require additional options for postgres_fdw.
I will revise the patch according to Approach 1-1, unless otherwise commented.

Approach1:
I ensure that postgres_fdw retrieves the version of each remote server
and does not partial aggregate pushd down if the server version is less than 17.
There are two approaches to obtaining remote server versions.
Approach1-1: postgres_fdw connects a remote server and use PQserverVersion().
Approach1-2: Adding a postgres_fdw option about a remote server version (like "server_version").

Approach2:
Adding a postgres_fdw option for partial aggregate pushdown is enable or not
(like enable_partial_aggregate_pushdown).

Sincerely yours,
Yuuki Fujii

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






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

* Re: Partial aggregates pushdown
@ 2023-06-12 13:37  Bruce Momjian <[email protected]>
  parent: [email protected] <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: Bruce Momjian @ 2023-06-12 13:37 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 Mon, Jun 12, 2023 at 08:51:30AM +0000, [email protected] wrote:
> Hi Mr.Bruce, Mr.Pyhalov, hackers.
> 
> Thank you for comments. I will try to respond to both of your comments as follows.
> I plan to start revising the patch next week. If you have any comments on the following
> respondences, I would appreciate it if you could give them to me this week.
> 
> > From: Bruce Momjian <[email protected]>
> > Sent: Saturday, June 10, 2023 1:44 AM
> > 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.
> Indeed, it is possible to use partial aggregate pushdown feature for purposes other than sharding.
> The description of the section "F.38.6. Built-in sharding in PostgreSQL" assumes the use of
> Built-in sharding and will be modified to eliminate this assumption.
> The title of this section should be changed to something like "Aggregate on partitioned table".

Sounds good.

> > From: Bruce Momjian <[email protected]>
> > Sent: Saturday, June 10, 2023 1:44 AM
> > 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.
> 
> I understand what the problem is. I will put a mechanism maintaining compatibility into the patch.
> I believe there are three approaches.
> Approach 1-1 is preferable because it does not require additional options for postgres_fdw.
> I will revise the patch according to Approach 1-1, unless otherwise commented.
> 
> Approach1:
> I ensure that postgres_fdw retrieves the version of each remote server
> and does not partial aggregate pushd down if the server version is less than 17.
> There are two approaches to obtaining remote server versions.
> Approach1-1: postgres_fdw connects a remote server and use PQserverVersion().
> Approach1-2: Adding a postgres_fdw option about a remote server version (like "server_version").
> 
> Approach2:
> Adding a postgres_fdw option for partial aggregate pushdown is enable or not
> (like enable_partial_aggregate_pushdown).

These are good questions.  Adding a postgres_fdw option called
enable_partial_aggregate_pushdown helps make the purpose of the option
clear, but remote_version can be used for future breakage as well.

I think remote_version is the best idea, and in the documention for the
option, let's explcitly say it is useful to disable partial aggreates
pushdown on pre-PG 17 servers.  If we need to use the option for other
cases, we can just update the documentation.  When the option is blank,
the default, everything is pushed down.

I see remote_version a logical addition to match our "extensions" option
that controls what extension functions can be pushed down.

-- 
  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] 38+ messages in thread

* RE: Partial aggregates pushdown
@ 2023-06-13 02:18  [email protected] <[email protected]>
  parent: Bruce Momjian <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: [email protected] @ 2023-06-13 02:18 UTC (permalink / raw)
  To: Bruce Momjian <[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]>; [email protected] <[email protected]>

Hi Mr.Momjian.

Thank you for advises.

> From: Bruce Momjian <[email protected]>
> Sent: Monday, June 12, 2023 10:38 PM
> > I understand what the problem is. I will put a mechanism maintaining compatibility into the patch.
> > I believe there are three approaches.
> > Approach 1-1 is preferable because it does not require additional options for postgres_fdw.
> > I will revise the patch according to Approach 1-1, unless otherwise commented.
> >
> > Approach1:
> > I ensure that postgres_fdw retrieves the version of each remote server
> > and does not partial aggregate pushd down if the server version is less than 17.
> > There are two approaches to obtaining remote server versions.
> > Approach1-1: postgres_fdw connects a remote server and use PQserverVersion().
> > Approach1-2: Adding a postgres_fdw option about a remote server version (like "server_version").
> >
> > Approach2:
> > Adding a postgres_fdw option for partial aggregate pushdown is enable
> > or not (like enable_partial_aggregate_pushdown).
> 
> These are good questions.  Adding a postgres_fdw option called enable_partial_aggregate_pushdown helps make the
> purpose of the option clear, but remote_version can be used for future breakage as well.
> 
> I think remote_version is the best idea, and in the documention for the option, let's explcitly say it is useful to disable
> partial aggreates pushdown on pre-PG 17 servers.  If we need to use the option for other cases, we can just update the
> documentation.  When the option is blank, the default, everything is pushed down.
> 
> I see remote_version a logical addition to match our "extensions" option that controls what extension functions can be
> pushed down.

Thank you for your perspective.
So, of the approaches I have presented, you think that approach 1-2 is
preferable and that the option name remote_server is preferable?
Indeed, the option of a remote version may have other uses.
However, this information can be obtained by connecting to a remote server, 
I'm concerned that some people may find this option redundant.

Is the problem with approach 1-1 because the user cannot decide whether to include the compatibility check in the decision to do partial aggregate pushdown or not?
# If Approach 1-1 is taken, the problem is that this feature cannot be used for all buit-in aggregate functions
# when the remote server is older than PG17.
If so, Approache1-3 below seem more desirable.
Would it be possible for us to hear your thoughts?

Approache1-3:We add a postgres_fdw option about a compatibility check for partial aggregate pushdown
(like "enable_aggpartialfunc_compatibility_check"). This option is false, the default.
When this option is true, postgres_fdw obtains the remote server version by connecting the remote server and using PQserverVersion(). 
And if the remote server version is older than PG17, then the partial aggregate pushdown feature is enable for all buit-in aggregate functions.
Otherwise the partial aggregate pushdown feature is disable for all buit-in aggregate functions.

Sincerely yours,
Yuuki Fujii

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






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

* Re: Partial aggregates pushdown
@ 2023-06-20 00:42  Bruce Momjian <[email protected]>
  parent: [email protected] <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: Bruce Momjian @ 2023-06-20 00:42 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 13, 2023 at 02:18:15AM +0000, [email protected] wrote:
> Hi Mr.Momjian.
> 
> Thank you for advises.
> 
> > From: Bruce Momjian <[email protected]>
> > Sent: Monday, June 12, 2023 10:38 PM
> > > I understand what the problem is. I will put a mechanism maintaining compatibility into the patch.
> > > I believe there are three approaches.
> > > Approach 1-1 is preferable because it does not require additional options for postgres_fdw.
> > > I will revise the patch according to Approach 1-1, unless otherwise commented.
> > >
> > > Approach1:
> > > I ensure that postgres_fdw retrieves the version of each remote server
> > > and does not partial aggregate pushd down if the server version is less than 17.
> > > There are two approaches to obtaining remote server versions.
> > > Approach1-1: postgres_fdw connects a remote server and use PQserverVersion().
> > > Approach1-2: Adding a postgres_fdw option about a remote server version (like "server_version").
> > >
> > > Approach2:
> > > Adding a postgres_fdw option for partial aggregate pushdown is enable
> > > or not (like enable_partial_aggregate_pushdown).
> > 
> > These are good questions.  Adding a postgres_fdw option called enable_partial_aggregate_pushdown helps make the
> > purpose of the option clear, but remote_version can be used for future breakage as well.
> > 
> > I think remote_version is the best idea, and in the documentation for the option, let's explicitly say it is useful to disable
> > partial aggregates pushdown on pre-PG 17 servers.  If we need to use the option for other cases, we can just update the
> > documentation.  When the option is blank, the default, everything is pushed down.
> > 
> > I see remote_version a logical addition to match our "extensions" option that controls what extension functions can be
> > pushed down.
> 
> Thank you for your perspective.
> So, of the approaches I have presented, you think that approach 1-2 is
> preferable and that the option name remote_server is preferable?
> Indeed, the option of a remote version may have other uses.
> However, this information can be obtained by connecting to a remote server, 
> I'm concerned that some people may find this option redundant.
> 
> Is the problem with approach 1-1 because the user cannot decide whether to include the compatibility check in the decision to do partial aggregate pushdown or not?
> # If Approach 1-1 is taken, the problem is that this feature cannot be used for all bait-in aggregate functions
> # when the remote server is older than PG17.
> If so, Approache1-3 below seem more desirable.
> Would it be possible for us to hear your thoughts?
> 
> Approache1-3:We add a postgres_fdw option about a compatibility check for partial aggregate pushdown
> (like "enable_aggpartialfunc_compatibility_check"). This option is false, the default.
> When this option is true, postgres_fdw obtains the remote server version by connecting the remote server and using PQserverVersion(). 
> And if the remote server version is older than PG17, then the partial aggregate pushdown feature is enable for all buit-in aggregate functions.
> Otherwise the partial aggregate pushdown feature is disable for all buit-in aggregate functions.

Apologies for the delay in my reply to this email.  I looked into the
existing code and I found three things:

1)  PQserverVersion() just pulls the conn->sversion value from the
existing connection because pqSaveParameterStatus() pulls the
server_version sent by the backend;  no need to issue SELECT version().

2)  postgres_fdw already has nine calls to GetConnection(), and only
opens a connection if it already doesn't have one.  Here is an example:

	/* Get the remote estimate */
	conn = GetConnection(fpinfo->user, false, NULL);
	get_remote_estimate(sql.data, conn, &rows, &width,
			    &startup_cost, &total_cost);
	ReleaseConnection(conn);

Therefore, it seems like it would be near-zero cost to just call conn =
GetConnection() and then PQserverVersion(conn), and ReleaseConnection().
You can then use the return value of PQserverVersion() to determine if
you can push down partial aggregates.

3)  Looking at postgresAcquireSampleRowsFunc(), I see this exact method
used:

    conn = GetConnection(user, false, NULL);

    /* We'll need server version, so fetch it now. */
    server_version_num = PQserverVersion(conn);

    ...

    if ((server_version_num < 95000) &&
	(method == ANALYZE_SAMPLE_SYSTEM ||
	 method == ANALYZE_SAMPLE_BERNOULLI))
	ereport(ERROR,
		(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
		 errmsg("remote server does not support TABLESAMPLE feature")));

I am sorry if you already knew all this, but I didn't.

-- 
  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] 38+ messages in thread

* Re: Partial aggregates pushdown
@ 2023-06-20 06:59  Alexander Pyhalov <[email protected]>
  parent: Bruce Momjian <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: Alexander Pyhalov @ 2023-06-20 06:59 UTC (permalink / raw)
  To: Bruce Momjian <[email protected]>; +Cc: [email protected] <[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]>

Bruce Momjian писал 2023-06-20 03:42:
> Apologies for the delay in my reply to this email.  I looked into the
> existing code and I found three things:
> 
> 1)  PQserverVersion() just pulls the conn->sversion value from the
> existing connection because pqSaveParameterStatus() pulls the
> server_version sent by the backend;  no need to issue SELECT version().
> 
> 2)  postgres_fdw already has nine calls to GetConnection(), and only
> opens a connection if it already doesn't have one.  Here is an example:
> 
> 	/* Get the remote estimate */
> 	conn = GetConnection(fpinfo->user, false, NULL);
> 	get_remote_estimate(sql.data, conn, &rows, &width,
> 			    &startup_cost, &total_cost);
> 	ReleaseConnection(conn);
> 
> Therefore, it seems like it would be near-zero cost to just call conn =
> GetConnection() and then PQserverVersion(conn), and 
> ReleaseConnection().
> You can then use the return value of PQserverVersion() to determine if
> you can push down partial aggregates.
> 

Hi.
Currently we don't get remote connection while planning if 
use_remote_estimate is not set.
Such change would require to get remote connection in planner, not in 
executor.
This can lead to change of behavior (like errors in explain when user 
mapping is wrong - e.g. bad password is specified).
Also this potentially can lead to establishing connections even when 
plan node is not actually used
(like extreme example - select sum(score) from t limit 0).
I'm not saying we shouldn't do it - just hint at possible consequences.

-- 
Best regards,
Alexander Pyhalov,
Postgres Professional






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

* Re: Partial aggregates pushdown
@ 2023-06-21 15:43  Bruce Momjian <[email protected]>
  parent: Alexander Pyhalov <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: Bruce Momjian @ 2023-06-21 15:43 UTC (permalink / raw)
  To: Alexander Pyhalov <[email protected]>; +Cc: [email protected] <[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 20, 2023 at 09:59:11AM +0300, Alexander Pyhalov wrote:
> > Therefore, it seems like it would be near-zero cost to just call conn =
> > GetConnection() and then PQserverVersion(conn), and ReleaseConnection().
> > You can then use the return value of PQserverVersion() to determine if
> > you can push down partial aggregates.
> 
> Hi.
> Currently we don't get remote connection while planning if
> use_remote_estimate is not set.
> Such change would require to get remote connection in planner, not in
> executor.
> This can lead to change of behavior (like errors in explain when user
> mapping is wrong - e.g. bad password is specified).
> Also this potentially can lead to establishing connections even when plan
> node is not actually used
> (like extreme example - select sum(score) from t limit 0).
> I'm not saying we shouldn't do it - just hint at possible consequences.

Agreed.  I noticed it was doing FDW connections during optimization, but
didn't see the postgres_fdw option that would turn it off. 
Interestingly, it is disabled by default.

After considering the options, I think we should have a postgres_fdw
option called "planner_version_check", and default that false.  When
false, a remote server version check will not be performed during
planning and partial aggregates will be always be considered.  When
true, a version check will be performed during planning and partial
aggregate pushdown disabled for pre-PG 17 foreign servers during the
query.

If we want to be more specific, we can call it
"check_partial_aggregate_support".

-- 
  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] 38+ messages in thread

* RE: Partial aggregates pushdown
@ 2023-06-22 05:23  [email protected] <[email protected]>
  parent: Bruce Momjian <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: [email protected] @ 2023-06-22 05:23 UTC (permalink / raw)
  To: Bruce Momjian <[email protected]>; Alexander Pyhalov <[email protected]>; +Cc: 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.Momjian, Mr.Pyhalov, hackers.

> From: Bruce Momjian <[email protected]>
> Sent: Thursday, June 22, 2023 12:44 AM
> On Tue, Jun 20, 2023 at 09:59:11AM +0300, Alexander Pyhalov wrote:
> > > Therefore, it seems like it would be near-zero cost to just call
> > > conn =
> > > GetConnection() and then PQserverVersion(conn), and ReleaseConnection().
> > > You can then use the return value of PQserverVersion() to determine
> > > if you can push down partial aggregates.
> >
> > Hi.
> > Currently we don't get remote connection while planning if
> > use_remote_estimate is not set.
> > Such change would require to get remote connection in planner, not in
> > executor.
> > This can lead to change of behavior (like errors in explain when user
> > mapping is wrong - e.g. bad password is specified).
> > Also this potentially can lead to establishing connections even when
> > plan node is not actually used (like extreme example - select
> > sum(score) from t limit 0).
> > I'm not saying we shouldn't do it - just hint at possible consequences.
> 
> Agreed.  I noticed it was doing FDW connections during optimization, but didn't see the postgres_fdw option that would
> turn it off.
> Interestingly, it is disabled by default.
> 
> After considering the options, I think we should have a postgres_fdw option called "planner_version_check", and default
> that false.  When false, a remote server version check will not be performed during planning and partial aggregates will be
> always be considered.  When true, a version check will be performed during planning and partial aggregate pushdown
> disabled for pre-PG 17 foreign servers during the query.
> 
> If we want to be more specific, we can call it "check_partial_aggregate_support".
Thank you both for your advice.
We will address the compatibility issues as follows.

Approach1-3:
I will add a postgres_fdw option "check_partial_aggregate_support".
This option is false, default.
Only if this option is true, postgres_fdw connect to the remote server and get the version of the remote server.
And if the version of the remote server is less than PG17, then partial aggregate push down to the remote server is disable.

Sincerely yours,
Yuuki Fujii

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

> -----Original Message-----
> From: Bruce Momjian <[email protected]>
> Sent: Thursday, June 22, 2023 12:44 AM
> To: Alexander Pyhalov <[email protected]>
> Cc: Fujii Yuki/藤井 雄規(MELCO/情報総研 DM最適G) <[email protected]>;
> PostgreSQL-development <[email protected]>; Andres Freund <[email protected]>; Tom Lane
> <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>;
> Daniel Gustafsson <[email protected]>; Ilya Gladyshev <[email protected]>
> Subject: Re: Partial aggregates pushdown
> 
> On Tue, Jun 20, 2023 at 09:59:11AM +0300, Alexander Pyhalov wrote:
> > > Therefore, it seems like it would be near-zero cost to just call
> > > conn =
> > > GetConnection() and then PQserverVersion(conn), and ReleaseConnection().
> > > You can then use the return value of PQserverVersion() to determine
> > > if you can push down partial aggregates.
> >
> > Hi.
> > Currently we don't get remote connection while planning if
> > use_remote_estimate is not set.
> > Such change would require to get remote connection in planner, not in
> > executor.
> > This can lead to change of behavior (like errors in explain when user
> > mapping is wrong - e.g. bad password is specified).
> > Also this potentially can lead to establishing connections even when
> > plan node is not actually used (like extreme example - select
> > sum(score) from t limit 0).
> > I'm not saying we shouldn't do it - just hint at possible consequences.
> 
> Agreed.  I noticed it was doing FDW connections during optimization, but didn't see the postgres_fdw option that would
> turn it off.
> Interestingly, it is disabled by default.
> 
> After considering the options, I think we should have a postgres_fdw option called "planner_version_check", and default
> that false.  When false, a remote server version check will not be performed during planning and partial aggregates will be
> always be considered.  When true, a version check will be performed during planning and partial aggregate pushdown
> disabled for pre-PG 17 foreign servers during the query.
> 
> If we want to be more specific, we can call it "check_partial_aggregate_support".
> 
> --
>   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] 38+ messages in thread

* Re: Partial aggregates pushdown
@ 2023-06-22 11:39  Bruce Momjian <[email protected]>
  parent: [email protected] <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: Bruce Momjian @ 2023-06-22 11:39 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 Thu, Jun 22, 2023 at 05:23:33AM +0000, [email protected] wrote:
> Approach1-3:
> I will add a postgres_fdw option "check_partial_aggregate_support".
> This option is false, default.
> Only if this option is true, postgres_fdw connect to the remote server and get the version of the remote server.
> And if the version of the remote server is less than PG17, then partial aggregate push down to the remote server is disable.

Great!

-- 
  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] 38+ messages in thread

* RE: Partial aggregates pushdown
@ 2023-07-10 07:35  [email protected] <[email protected]>
  parent: Bruce Momjian <[email protected]>
  0 siblings, 2 replies; 38+ messages in thread

From: [email protected] @ 2023-07-10 07:35 UTC (permalink / raw)
  To: Bruce Momjian <[email protected]>; Alexander Pyhalov <[email protected]>; pgsql-hackers; +Cc: 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.Bruce, Mr.Pyhalov, hackers.

> From: Bruce Momjian <[email protected]>
> Sent: Monday, June 12, 2023 10:38 PM
> 
> On Mon, Jun 12, 2023 at 08:51:30AM +0000, [email protected] wrote:
> > Hi Mr.Bruce, Mr.Pyhalov, hackers.
> >
> > Thank you for comments. I will try to respond to both of your comments as follows.
> > I plan to start revising the patch next week. If you have any comments
> > on the following respondences, I would appreciate it if you could give them to me this week.
> >
> > > From: Bruce Momjian <[email protected]>
> > > Sent: Saturday, June 10, 2023 1:44 AM 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.
> > Indeed, it is possible to use partial aggregate pushdown feature for purposes other than sharding.
> > The description of the section "F.38.6. Built-in sharding in
> > PostgreSQL" assumes the use of Built-in sharding and will be modified to eliminate this assumption.
> > The title of this section should be changed to something like "Aggregate on partitioned table".
> 
> Sounds good.
I have modified documents according to the above policy.

> From: Bruce Momjian <[email protected]>
> Sent: Thursday, June 22, 2023 8:39 PM
> On Thu, Jun 22, 2023 at 05:23:33AM +0000, [email protected] wrote:
> > Approach1-3:
> > I will add a postgres_fdw option "check_partial_aggregate_support".
> > This option is false, default.
> > Only if this option is true, postgres_fdw connect to the remote server and get the version of the remote server.
> > And if the version of the remote server is less than PG17, then partial aggregate push down to the remote server is
> disable.
> 
> Great!
I have modified the program except for the point "if the version of the remote server is less than PG17".
Instead, we have addressed the following.
"If check_partial_aggregate_support is true and the remote server version is older than the local server
version, postgres_fdw does not assume that the partial aggregate function is on the remote server unless
the partial aggregate function and the aggregate function match."
The reason for this is to maintain compatibility with any aggregate function that does not support partial
aggregate in one version of V1 (V1 is PG17 or higher), even if the next version supports partial aggregate.
For example, string_agg does not support partial aggregation in PG15, but it will support partial aggregation
in PG16.

We have not been able to add a test for the case where the remote server version is older than the
local server version to the regression test. Is there any way to add such tests to the existing regression
tests?

Sincerely yours,
Yuuki Fujii

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


Attachments:

  [application/octet-stream] 0001-Partial-aggregates-push-down-v23.patch (249.1K, ../../OS3PR01MB6660DF84634EDAFC55D7B0009530A@OS3PR01MB6660.jpnprd01.prod.outlook.com/2-0001-Partial-aggregates-push-down-v23.patch)
  download | inline diff:
From: Yuki Fujii <[email protected]>
Date: Mon, 10 Jul 2023 11:34:09 -0400
Subject: [PATCH] Partial aggregates push down v23

---
 contrib/postgres_fdw/deparse.c                | 100 ++-
 .../postgres_fdw/expected/postgres_fdw.out    | 771 +++++++++++++++++-
 contrib/postgres_fdw/option.c                 |   4 +-
 contrib/postgres_fdw/postgres_fdw.c           |  26 +-
 contrib/postgres_fdw/postgres_fdw.h           |   9 +
 contrib/postgres_fdw/sql/postgres_fdw.sql     | 294 ++++++-
 doc/src/sgml/catalogs.sgml                    |  13 +
 doc/src/sgml/postgres-fdw.sgml                | 130 ++-
 doc/src/sgml/ref/create_aggregate.sgml        |  42 +
 doc/src/sgml/xaggr.sgml                       |  15 +-
 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 +++++
 19 files changed, 2587 insertions(+), 179 deletions(-)

diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 09d6dd60dd..a6cfc4a789 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,68 @@ get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
 	/* Shouldn't get here */
 	elog(ERROR, "unexpected expression in subquery output");
 }
+
+/*
+ * Check that a buit-in aggpartialfunc exists on the remote server. If
+ * check_partial_aggregate_support is false, we assume the partial aggregate
+ * function exsits on the remote server. Otherwise we assume the partial
+ * aggregate function exsits on the remote server only if the remote server
+ * version is not less than the local server version.
+ */
+static bool
+is_builtin_aggpartialfunc_shippable(Oid aggpartialfn, PgFdwRelationInfo *fpinfo)
+{
+	bool		shippable = true;
+
+	if (fpinfo->check_partial_aggregate_support)
+	{
+		if (fpinfo->remoteversion == 0)
+		{
+			PGconn	   *conn = GetConnection(fpinfo->user, false, NULL);
+
+			fpinfo->remoteversion = PQserverVersion(conn);
+		}
+		if (fpinfo->remoteversion < PG_VERSION_NUM)
+			shippable = false;
+	}
+	return shippable;
+}
+
+/*
+ * 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 exists on the remote server
+ */
+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_builtin(aggform->aggpartialfn))
+		partial_agg_ok = is_builtin_aggpartialfunc_shippable(
+															 aggform->aggpartialfn, fpinfo);
+	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..8d80ba0a6b 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,735 @@ 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)
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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)
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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)
+
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+-- 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/option.c b/contrib/postgres_fdw/option.c
index 8c822f4ef9..67e15c8bb9 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -126,7 +126,8 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
 			strcmp(def->defname, "async_capable") == 0 ||
 			strcmp(def->defname, "parallel_commit") == 0 ||
 			strcmp(def->defname, "parallel_abort") == 0 ||
-			strcmp(def->defname, "keep_connections") == 0)
+			strcmp(def->defname, "keep_connections") == 0 ||
+			strcmp(def->defname, "check_partial_aggregate_support") == 0)
 		{
 			/* these accept only boolean values */
 			(void) defGetBoolean(def);
@@ -268,6 +269,7 @@ InitPgFdwOptions(void)
 		/* batch_size is available on both server and table */
 		{"batch_size", ForeignServerRelationId, false},
 		{"batch_size", ForeignTableRelationId, false},
+		{"check_partial_aggregate_support", ForeignServerRelationId, false},
 		/* async_capable is available on both server and table */
 		{"async_capable", ForeignServerRelationId, false},
 		{"async_capable", ForeignTableRelationId, false},
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c5cada55fb..5c48c5d50d 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);
@@ -650,6 +650,8 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	fpinfo->fdw_tuple_cost = DEFAULT_FDW_TUPLE_COST;
 	fpinfo->shippable_extensions = NIL;
 	fpinfo->fetch_size = 100;
+	fpinfo->check_partial_aggregate_support = false;
+	fpinfo->remoteversion = 0;
 	fpinfo->async_capable = false;
 
 	apply_server_options(fpinfo);
@@ -661,7 +663,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	 * should match what ExecCheckPermissions() does.  If we fail due to lack
 	 * of permissions, the query would have failed at runtime anyway.
 	 */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
 		Oid			userid;
 
@@ -6127,6 +6129,8 @@ apply_server_options(PgFdwRelationInfo *fpinfo)
 				ExtractExtensionList(defGetString(def), false);
 		else if (strcmp(def->defname, "fetch_size") == 0)
 			(void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
+		else if (strcmp(def->defname, "check_partial_aggregate_support") == 0)
+			fpinfo->check_partial_aggregate_support = defGetBoolean(def);
 		else if (strcmp(def->defname, "async_capable") == 0)
 			fpinfo->async_capable = defGetBoolean(def);
 	}
@@ -6186,6 +6190,8 @@ merge_fdw_options(PgFdwRelationInfo *fpinfo,
 	fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
 	fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
 	fpinfo->fetch_size = fpinfo_o->fetch_size;
+	fpinfo->check_partial_aggregate_support = fpinfo_o->check_partial_aggregate_support;
+	fpinfo->remoteversion = fpinfo_o->remoteversion;
 	fpinfo->async_capable = fpinfo_o->async_capable;
 
 	/* Merge the table level options from either side of the join. */
@@ -6366,7 +6372,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 +6386,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 +6631,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 +6648,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 +6692,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 +6713,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..96e73a10a8 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -86,6 +86,15 @@ typedef struct PgFdwRelationInfo
 	ForeignServer *server;
 	UserMapping *user;			/* only set in use_remote_estimate mode */
 
+	/* for partial aggregate pushdown */
+	bool		check_partial_aggregate_support;
+
+	/*
+	 * if remoteversion is zero, then that means the remote server version is
+	 * unacquired.
+	 */
+	int			remoteversion;
+
 	int			fetch_size;		/* fetch size for this remote table */
 
 	/*
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index b54903ad8f..827637b001 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,275 @@ 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;
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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;
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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;
+
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+
+-- 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..4f5ff32e96 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="partial-aggregate-pushdown"/> 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..676abc6fa5 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -451,6 +451,37 @@ OPTIONS (ADD password_required 'false');
 
   </sect3>
 
+  <sect3 id="postgres-fdw-options-partial-aggregate-pushdown">
+   <title>Partial Aggregate Pushdown Options</title>
+
+   <para>
+    By default, <filename>postgres_fdw</filename> assumes that for each built-in aggregate
+    function, the partial aggregate function is defined on the remote server. See
+    <xref linkend="partial-aggregate-pushdown"/> for the definition of the partial aggregate
+    function. This may be overridden using the following option:
+   </para>
+
+   <variablelist>
+
+    <varlistentry>
+     <term><literal>check_partial_aggregate_support</literal> (<type>boolean</type>)</term>
+     <listitem>
+      <para>
+       When this option is true, <filename>postgres_fdw</filename> connects to the remote server
+       and gets its remote server version during query planning.
+       If the remote server version is older than the local server version,
+       <filename>postgres_fdw</filename>
+       assumes that for each aggregate function, the partial aggregate function is not defined
+       on the remote server unless the partial aggregate function and the aggregate
+       function match.
+       The default is <literal>false</literal>.
+      </para>
+     </listitem>
+    </varlistentry>
+
+   </variablelist>
+  </sect3>
+
   <sect3 id="postgres-fdw-options-asynchronous-execution">
    <title>Asynchronous Execution Options</title>
 
@@ -929,13 +960,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 +999,68 @@ postgres=# SELECT postgres_fdw_disconnect_all();
   </para>
  </sect2>
 
+ <sect2 id="partial-aggregate-pushdown">
+  <title>Partial aggregate pushdown</title>
+  <para>
+   Partial aggregate pushdown is an optimization for a query that contains aggregate
+   expressions for a partitioned table across one or more remote servers. If the multiple
+   conditions are true for a remote server, an aggregate expression is sent to that
+   remote server and the aggregate is a partial aggregate on the local server.
+   The conditions under which this sending is active are as follows.
+   <itemizedlist spacing="compact">
+     <listitem>
+      <para>
+       An aggregate function, called the partial aggregate function for partial aggregate
+       that corresponding to the aggregate expression, is defined on the local server and
+       the remote server.
+      </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>
+       The aggregate expressions in the query don't contain
+       a <literal>DISTINCT</literal> or <literal>ORDER BY</literal> clause.
+      </para>
+     </listitem>
+    </itemizedlist>
+   When this sending is active for aggregate expressions, any aggregate
+   function is replaced by the partial aggregate function in the remote query.
+   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.
+   By default, <filename>postgres_fdw</filename> assumes that for each built-in
+   aggregate function, the partial aggregate function is defined on the local server
+   and the remote server.
+   If <literal>check_partial_aggregate_support</literal> is true and the remote server version is
+   older than the local server version, <filename>postgres_fdw</filename> does not assume
+   that the partial aggregate function is on the remote server unless
+   the partial aggregate function and the aggregate function match.
+   <filename>postgres_fdw</filename> assumes that a user-defined aggregate
+   function has the partial aggregate function on the remote server
+   only if both belong to an extension that is listed in the foreign server's
+   extensions option.
+  </para>
+ </sect2>
+
  <sect2 id="postgres-fdw-remote-query-execution-environment">
   <title>Remote Query Execution Environment</title>
 
@@ -1031,14 +1126,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 partial aggregate pushdown feature.
+   When using the partial aggregate pushdown feature,
+   the partial aggregate function of the aggregate function is sent to
+   the remote server if the conditions in
+   <xref linkend="partial-aggregate-pushdown"/> are met.
+   A limitation is that by default, <filename>postgres_fdw</filename> assumes that
+   for each built-in aggregate function, the partial aggregate function is defined
+   on the remote server.
+   This limitation might cause <quote>function does not exist</quote>
+   or a similar error if the remote server version is older than the local server version.
+   This problem can be worked around by setting
+   <literal>check_partial_aggregate_support</literal> to <literal>true</literal>
+   or similar query rewriting as above.
   </para>
  </sect2>
 
diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml
index 222e0aa5c9..2875734dea 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>
@@ -246,6 +248,18 @@ CREATE [ OR REPLACE ] AGGREGATE <replaceable class="parameter">name</replaceable
    marked <literal>PARALLEL SAFE</literal> to enable parallel aggregation.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize a query containing aggregate
+   expressions for a partitioned table across one or more remote servers.
+   This optimization sends an aggregate expression to a remote server and the
+   aggregate is a partial aggregate on the local server. This optimization
+   requires an aggregate function called a <firstterm>partial aggregate
+   function</firstterm>. See
+   <xref linkend="partial-aggregate-pushdown"/> for the definition of
+   partial aggregate function. <literal>AGGPARTIALFUNC</literal> is a parameter
+   of the partial aggregate function.
+  </para>
+
   <para>
    Aggregates that behave like <function>MIN</function> or <function>MAX</function> can
    sometimes be optimized by looking into an index instead of scanning every
@@ -653,6 +667,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..607c859974 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,18 @@ SELECT percentile_disc(0.5) WITHIN GROUP (ORDER BY income) FROM households;
    parallel-safety markings on its support functions are not consulted.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize a query containing
+   aggregate expressions for a partitioned table across one or more
+   remote servers. This optimization sends an aggregate expression to
+   a remote server and the aggregate is a partial aggregate on the
+   local server. This optimization requires an aggregate function
+   called the <firstterm>partial aggregate function</firstterm>.
+   See
+   <xref linkend="partial-aggregate-pushdown"/> 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] 38+ messages in thread

* Re: Partial aggregates pushdown
@ 2023-07-14 13:40  Alexander Pyhalov <[email protected]>
  parent: [email protected] <[email protected]>
  1 sibling, 1 reply; 38+ messages in thread

From: Alexander Pyhalov @ 2023-07-14 13:40 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-07-10 10:35:
> I have modified the program except for the point "if the version of
> the remote server is less than PG17".
> Instead, we have addressed the following.
> "If check_partial_aggregate_support is true and the remote server
> version is older than the local server
> version, postgres_fdw does not assume that the partial aggregate
> function is on the remote server unless
> the partial aggregate function and the aggregate function match."
> The reason for this is to maintain compatibility with any aggregate
> function that does not support partial
> aggregate in one version of V1 (V1 is PG17 or higher), even if the
> next version supports partial aggregate.
> For example, string_agg does not support partial aggregation in PG15,
> but it will support partial aggregation
> in PG16.
> 

Hi.

1) In foreign_join_ok() should we set fpinfo->user if 
fpinfo->check_partial_aggregate_support is set like it's done for 
fpinfo->use_remote_estimate? It seems we can end up with fpinfo->user = 
NULL if use_remote_estimate is not set.

2) It seeems we found an additional issue with original patch, which is 
present in current one. I'm attaching a patch which seems to fix it, but 
I'm not quite sure in it.


> We have not been able to add a test for the case where the remote
> server version is older than the
> local server version to the regression test. Is there any way to add
> such tests to the existing regression
> tests?
> 

-- 
Best regards,
Alexander Pyhalov,
Postgres Professional

Attachments:

  [text/x-diff] 0001-For-partial-aggregation-we-can-t-rely-on-the-fact-th.diff (11.8K, ../../[email protected]/2-0001-For-partial-aggregation-we-can-t-rely-on-the-fact-th.diff)
  download | inline diff:
From 187d15185200aabc22c5219bbe636bc950670a78 Mon Sep 17 00:00:00 2001
From: Alexander Pyhalov <[email protected]>
Date: Fri, 14 Jul 2023 16:34:02 +0300
Subject: [PATCH] For partial aggregation we can't rely on the fact that every
 var is a part of some GROUP BY expression

---
 .../postgres_fdw/expected/postgres_fdw.out    | 120 ++++++++++++++++++
 contrib/postgres_fdw/postgres_fdw.c           |  31 +++--
 contrib/postgres_fdw/sql/postgres_fdw.sql     |   9 ++
 3 files changed, 152 insertions(+), 8 deletions(-)

diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 8d80ba0a6be..31ee461045c 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -9955,6 +9955,126 @@ SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b ORDER BY 1;
  49 | 19.0000000000000000 |  29 |    60
 (50 rows)
 
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT b/2, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+                                                           QUERY PLAN                                                           
+--------------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: ((pagg_tab.b / 2)), (avg(pagg_tab.a)), (max(pagg_tab.a)), (count(*))
+   Sort Key: ((pagg_tab.b / 2))
+   ->  Finalize HashAggregate
+         Output: ((pagg_tab.b / 2)), avg(pagg_tab.a), max(pagg_tab.a), count(*)
+         Group Key: ((pagg_tab.b / 2))
+         ->  Append
+               ->  Foreign Scan
+                     Output: ((pagg_tab.b / 2)), (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 / 2), avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p1 GROUP BY 1
+               ->  Foreign Scan
+                     Output: ((pagg_tab_1.b / 2)), (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 / 2), avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p2 GROUP BY 1
+               ->  Foreign Scan
+                     Output: ((pagg_tab_2.b / 2)), (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 / 2), avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p3 GROUP BY 1
+(19 rows)
+
+SELECT b/2, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+ ?column? |         avg         | max | count 
+----------+---------------------+-----+-------
+        0 | 10.5000000000000000 |  21 |   120
+        1 | 12.5000000000000000 |  23 |   120
+        2 | 14.5000000000000000 |  25 |   120
+        3 | 16.5000000000000000 |  27 |   120
+        4 | 18.5000000000000000 |  29 |   120
+        5 | 10.5000000000000000 |  21 |   120
+        6 | 12.5000000000000000 |  23 |   120
+        7 | 14.5000000000000000 |  25 |   120
+        8 | 16.5000000000000000 |  27 |   120
+        9 | 18.5000000000000000 |  29 |   120
+       10 | 10.5000000000000000 |  21 |   120
+       11 | 12.5000000000000000 |  23 |   120
+       12 | 14.5000000000000000 |  25 |   120
+       13 | 16.5000000000000000 |  27 |   120
+       14 | 18.5000000000000000 |  29 |   120
+       15 | 10.5000000000000000 |  21 |   120
+       16 | 12.5000000000000000 |  23 |   120
+       17 | 14.5000000000000000 |  25 |   120
+       18 | 16.5000000000000000 |  27 |   120
+       19 | 18.5000000000000000 |  29 |   120
+       20 | 10.5000000000000000 |  21 |   120
+       21 | 12.5000000000000000 |  23 |   120
+       22 | 14.5000000000000000 |  25 |   120
+       23 | 16.5000000000000000 |  27 |   120
+       24 | 18.5000000000000000 |  29 |   120
+(25 rows)
+
+-- It's unsafe to pushdown variables if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+                                                                  QUERY PLAN                                                                  
+----------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: ((((pagg_tab.b / 2)))::numeric), (avg(pagg_tab.a)), (max(pagg_tab.a)), (count(*)), ((pagg_tab.b / 2))
+   Sort Key: ((((pagg_tab.b / 2)))::numeric)
+   ->  Finalize GroupAggregate
+         Output: (((pagg_tab.b / 2)))::numeric, avg(pagg_tab.a), max(pagg_tab.a), count(*), ((pagg_tab.b / 2))
+         Group Key: ((pagg_tab.b / 2))
+         ->  Sort
+               Output: ((pagg_tab.b / 2)), pagg_tab.b, (PARTIAL avg(pagg_tab.a)), (PARTIAL max(pagg_tab.a)), (PARTIAL count(*))
+               Sort Key: ((pagg_tab.b / 2))
+               ->  Append
+                     ->  Partial HashAggregate
+                           Output: ((pagg_tab.b / 2)), pagg_tab.b, PARTIAL avg(pagg_tab.a), PARTIAL max(pagg_tab.a), PARTIAL count(*)
+                           Group Key: (pagg_tab.b / 2)
+                           ->  Foreign Scan on public.fpagg_tab_p1 pagg_tab
+                                 Output: (pagg_tab.b / 2), pagg_tab.b, pagg_tab.a
+                                 Remote SQL: SELECT a, b FROM public.pagg_tab_p1
+                     ->  Partial HashAggregate
+                           Output: ((pagg_tab_1.b / 2)), pagg_tab_1.b, PARTIAL avg(pagg_tab_1.a), PARTIAL max(pagg_tab_1.a), PARTIAL count(*)
+                           Group Key: (pagg_tab_1.b / 2)
+                           ->  Foreign Scan on public.fpagg_tab_p2 pagg_tab_1
+                                 Output: (pagg_tab_1.b / 2), 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 / 2)), pagg_tab_2.b, PARTIAL avg(pagg_tab_2.a), PARTIAL max(pagg_tab_2.a), PARTIAL count(*)
+                           Group Key: (pagg_tab_2.b / 2)
+                           ->  Foreign Scan on public.fpagg_tab_p3 pagg_tab_2
+                                 Output: (pagg_tab_2.b / 2), pagg_tab_2.b, pagg_tab_2.a
+                                 Remote SQL: SELECT a, b FROM public.pagg_tab_p3
+(28 rows)
+
+SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+ numeric |         avg         | max | count 
+---------+---------------------+-----+-------
+       0 | 10.5000000000000000 |  21 |   120
+       1 | 12.5000000000000000 |  23 |   120
+       2 | 14.5000000000000000 |  25 |   120
+       3 | 16.5000000000000000 |  27 |   120
+       4 | 18.5000000000000000 |  29 |   120
+       5 | 10.5000000000000000 |  21 |   120
+       6 | 12.5000000000000000 |  23 |   120
+       7 | 14.5000000000000000 |  25 |   120
+       8 | 16.5000000000000000 |  27 |   120
+       9 | 18.5000000000000000 |  29 |   120
+      10 | 10.5000000000000000 |  21 |   120
+      11 | 12.5000000000000000 |  23 |   120
+      12 | 14.5000000000000000 |  25 |   120
+      13 | 16.5000000000000000 |  27 |   120
+      14 | 18.5000000000000000 |  29 |   120
+      15 | 10.5000000000000000 |  21 |   120
+      16 | 12.5000000000000000 |  23 |   120
+      17 | 14.5000000000000000 |  25 |   120
+      18 | 16.5000000000000000 |  27 |   120
+      19 | 18.5000000000000000 |  29 |   120
+      20 | 10.5000000000000000 |  21 |   120
+      21 | 12.5000000000000000 |  23 |   120
+      22 | 14.5000000000000000 |  25 |   120
+      23 | 16.5000000000000000 |  27 |   120
+      24 | 18.5000000000000000 |  29 |   120
+(25 rows)
+
 -- Partial aggregates are safe to push down for all built-in aggregates
 EXPLAIN (VERBOSE, COSTS OFF)
 SELECT /* aggregate function <> aggpartialfunc */
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 5c48c5d50dc..eec76a00488 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -6381,13 +6381,17 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	ListCell   *lc;
 	int			i;
 	List	   *tlist = NIL;
+	bool	partial = false;
 
 	/* We currently don't support pushing Grouping Sets. */
 	if (query->groupingSets)
 		return false;
 
+	if (patype == PARTITIONWISE_AGGREGATE_PARTIAL)
+		partial = true;
+
 	/* It's unsafe to push having statements with partial aggregates */
-	if ((patype == PARTITIONWISE_AGGREGATE_PARTIAL) && havingQual)
+	if (partial && havingQual)
 		return false;
 
 	/* Get the fpinfo of the underlying scan relation. */
@@ -6461,11 +6465,17 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 		}
 		else
 		{
+			/*
+			 * For partial aggregation we can't rely on the fact that every
+			 * var is a part of some GROUP BY expression, so extract variables
+			 * and if we found some, avoid to push it down.
+			 */
+
 			/*
 			 * Non-grouping expression we need to compute.  Can we ship it
 			 * as-is to the foreign server?
 			 */
-			if (is_foreign_expr(root, grouped_rel, expr) &&
+			if (!partial && is_foreign_expr(root, grouped_rel, expr) &&
 				!is_foreign_param(root, grouped_rel, expr))
 			{
 				/* Yes, so add to tlist as-is; OK to suppress duplicates */
@@ -6489,14 +6499,17 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 					return false;
 
 				/*
-				 * Add aggregates, if any, into the targetlist.  Plain Vars
-				 * outside an aggregate can be ignored, because they should be
+				 * Add aggregates, if any, into the targetlist.  When dealing
+				 * with usual aggregations,  plain Vars outside an aggregate
+				 * can be ignored, because they should be
 				 * either same as some GROUP BY column or part of some GROUP
 				 * BY expression.  In either case, they are already part of
-				 * the targetlist and thus no need to add them again.  In fact
-				 * including plain Vars in the tlist when they do not match a
-				 * GROUP BY column would cause the foreign server to complain
-				 * that the shipped query is invalid.
+				 * the targetlist and thus no need to add them again. When
+				 * looking at partial aggregation, we can get additional vars
+				 * needed for partial aggregation itself, which do not match
+				 * group by column. In fact including plain Vars in the tlist
+				 * when they do not match a GROUP BY column would cause the
+				 * foreign server to complain that the shipped query is invalid.
 				 */
 				foreach(l, aggvars)
 				{
@@ -6504,6 +6517,8 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 
 					if (IsA(aggref, Aggref))
 						tlist = add_to_flat_tlist(tlist, list_make1(aggref));
+					else if (partial)
+						return false;
 				}
 			}
 		}
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 827637b001a..4f93e1931de 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -3030,6 +3030,15 @@ 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;
 
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT b/2, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+SELECT b/2, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+
+-- It's unsafe to pushdown variables if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+
 -- Partial aggregates are safe to push down for all built-in aggregates
 EXPLAIN (VERBOSE, COSTS OFF)
 SELECT /* aggregate function <> aggpartialfunc */
-- 
2.34.1



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

* RE: Partial aggregates pushdown
@ 2023-07-18 01:35  [email protected] <[email protected]>
  parent: Alexander Pyhalov <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: [email protected] @ 2023-07-18 01:35 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.

> From: Alexander Pyhalov <[email protected]>
> Sent: Friday, July 14, 2023 10:40 PM
> 1) In foreign_join_ok() should we set fpinfo->user if
> fpinfo->check_partial_aggregate_support is set like it's done for 
> fpinfo->use_remote_estimate? It seems we can end up with fpinfo->user 
> fpinfo->=
> NULL if use_remote_estimate is not set.
You are right. I will modify this patch according to your advice.
Thank you for advice.

> 2) It seeems we found an additional issue with original patch, which 
> is present in current one. I'm attaching a patch which seems to fix 
> it, but I'm not quite sure in it.
Thank you for pointing out the issue.
If a query's group-by clause contains variable based expression(not variable)
and the query's select clause contains another expression,
the partial aggregate could be unsafe to push down.

An example of such queries:
SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2

Your patch disables partial aggregate pushdown for such queries.
I'll see if we can modify the patch to safely do a partial aggregate pushdown for such queries as well.
Such a query expects the variable in the select clause expression to be included in the target of the grouped rel
(let see make_partial_grouping_target), 
but the original groupby clause has no reference to this variable,
this seems to be the direct cause(let see foreign_grouping_ok). 
I will examine whether a safe pushdown can be achieved by matching the
groupby clause information referenced by foreign_grouping_ok with the grouped rel target information.

Sincerely yours,
Yuuki Fujii

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


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

* RE: Partial aggregates pushdown
@ 2023-07-19 00:43  [email protected] <[email protected]>
  parent: [email protected] <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: [email protected] @ 2023-07-19 00:43 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, hackers.

I have made the following three modifications about this patch.

1)
> <[email protected]>
> Sent: Tuesday, July 18, 2023 10:36 AM
> > From: Alexander Pyhalov <[email protected]>
> > Sent: Friday, July 14, 2023 10:40 PM
> > 1) In foreign_join_ok() should we set fpinfo->user if
> > fpinfo->check_partial_aggregate_support is set like it's done for
> > fpinfo->use_remote_estimate? It seems we can end up with fpinfo->user
> > fpinfo->=
> > NULL if use_remote_estimate is not set.
> You are right. I will modify this patch according to your advice.
> Thank you for advice.
Done.

2)
> <[email protected]>
> Sent: Tuesday, July 18, 2023 10:36 AM
> > 2) It seeems we found an additional issue with original patch, which
> > is present in current one. I'm attaching a patch which seems to fix
> > it, but I'm not quite sure in it.
> Thank you for pointing out the issue.
> If a query's group-by clause contains variable based expression(not variable)
> and the query's select clause contains another expression,
> the partial aggregate could be unsafe to push down.
>
> An example of such queries:
> SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2
>
> Your patch disables partial aggregate pushdown for such queries.
> I'll see if we can modify the patch to safely do a partial aggregate pushdown for such queries as well.
> Such a query expects the variable in the select clause expression to be included in the target of the grouped rel
> (let see make_partial_grouping_target),
> but the original groupby clause has no reference to this variable,
> this seems to be the direct cause(let see foreign_grouping_ok).
> I will examine whether a safe pushdown can be achieved by matching the
> groupby clause information referenced by foreign_grouping_ok with the grouped rel target information.
I modified the patch to safely do a partial aggregate pushdown for such queries as well
 by matching the groupby clause information referenced by foreign_grouping_ok with the grouped rel target information.

3)
I modified the patch to safely do a partial aggregate pushdown for queries which contain having clauses.

Sincerely yours,
Yuuki Fujii

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


Attachments:

  [application/octet-stream] 0001-Partial-aggregates-push-down-v24.patch (264.7K, ../../OS3PR01MB66607B671A5C29DED7A41DF39539A@OS3PR01MB6660.jpnprd01.prod.outlook.com/2-0001-Partial-aggregates-push-down-v24.patch)
  download | inline diff:
From: Yuki Fujii <[email protected]>
Date: Tue, 18 Jul 2023 21:40:27 +0900
Subject: [PATCH] Partial aggregates push down v24

---
 contrib/postgres_fdw/deparse.c                | 103 ++-
 .../postgres_fdw/expected/postgres_fdw.out    | 869 +++++++++++++++++-
 contrib/postgres_fdw/option.c                 |   4 +-
 contrib/postgres_fdw/postgres_fdw.c           |  56 +-
 contrib/postgres_fdw/postgres_fdw.h           |  10 +
 contrib/postgres_fdw/sql/postgres_fdw.sql     | 311 ++++++-
 doc/src/sgml/catalogs.sgml                    |  13 +
 doc/src/sgml/postgres-fdw.sgml                | 130 ++-
 doc/src/sgml/ref/create_aggregate.sgml        |  42 +
 doc/src/sgml/xaggr.sgml                       |  15 +-
 src/backend/catalog/pg_aggregate.c            | 101 ++
 src/backend/commands/aggregatecmds.c          |   4 +
 src/backend/optimizer/plan/planner.c          |  69 +-
 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 ++++
 src/include/nodes/pathnodes.h                 |   2 +
 .../regress/expected/create_aggregate.out     | 184 ++++
 src/test/regress/expected/oidjoins.out        |   1 +
 src/test/regress/sql/create_aggregate.sql     | 183 ++++
 21 files changed, 2782 insertions(+), 204 deletions(-)

diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 09d6dd60dd..e957611879 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 */
@@ -3723,6 +3744,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	Query	   *query = context->root->parse;
 	ListCell   *lc;
 	bool		first = true;
+	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) context->foreignrel->fdw_private;
 
 	/* Nothing to be done, if there's no GROUP BY clause in the query. */
 	if (!query->groupClause)
@@ -3743,7 +3765,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	 * to empty, and in any case the redundancy situation on the remote might
 	 * be different than what we think here.
 	 */
-	foreach(lc, query->groupClause)
+	foreach(lc, fpinfo->group_clause)
 	{
 		SortGroupClause *grp = (SortGroupClause *) lfirst(lc);
 
@@ -4047,3 +4069,68 @@ get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
 	/* Shouldn't get here */
 	elog(ERROR, "unexpected expression in subquery output");
 }
+
+/*
+ * Check that a buit-in aggpartialfunc exists on the remote server. If
+ * check_partial_aggregate_support is false, we assume the partial aggregate
+ * function exsits on the remote server. Otherwise we assume the partial
+ * aggregate function exsits on the remote server only if the remote server
+ * version is not less than the local server version.
+ */
+static bool
+is_builtin_aggpartialfunc_shippable(Oid aggpartialfn, PgFdwRelationInfo *fpinfo)
+{
+	bool		shippable = true;
+
+	if (fpinfo->check_partial_aggregate_support)
+	{
+		if (fpinfo->remoteversion == 0)
+		{
+			PGconn	   *conn = GetConnection(fpinfo->user, false, NULL);
+
+			fpinfo->remoteversion = PQserverVersion(conn);
+		}
+		if (fpinfo->remoteversion < PG_VERSION_NUM)
+			shippable = false;
+	}
+	return shippable;
+}
+
+/*
+ * 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 exists on the remote server
+ */
+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_builtin(aggform->aggpartialfn))
+		partial_agg_ok = is_builtin_aggpartialfunc_shippable(
+															 aggform->aggpartialfn, fpinfo);
+	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..3a0fa79151 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -9700,18 +9700,35 @@ 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.1');
+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');
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -9765,8 +9782,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 +9794,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;
@@ -9805,28 +9822,834 @@ SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
  21 |   100
 (6 rows)
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are safe 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                            
------------------------------------------------------------------
+                                                                     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
+               ->  Foreign Scan
+                     Output: pagg_tab.b, (PARTIAL avg(pagg_tab.a)), (PARTIAL max(pagg_tab.a)), (PARTIAL count(*)), (PARTIAL sum(pagg_tab.a))
+                     Filter: ((PARTIAL sum(pagg_tab.a)) < 700)
+                     Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab)
+                     Remote SQL: SELECT b, avg_p_int4(a), max(a), count(*), sum(a) 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(*)), (PARTIAL sum(pagg_tab_1.a))
+                     Filter: ((PARTIAL sum(pagg_tab_1.a)) < 700)
+                     Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1)
+                     Remote SQL: SELECT b, avg_p_int4(a), max(a), count(*), sum(a) 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(*)), (PARTIAL sum(pagg_tab_2.a))
+                     Filter: ((PARTIAL sum(pagg_tab_2.a)) < 700)
+                     Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2)
+                     Remote SQL: SELECT b, avg_p_int4(a), max(a), count(*), sum(a) FROM public.pagg_tab_p3 GROUP BY 1
+(23 rows)
+
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
+ b  |         avg         | max | count 
+----+---------------------+-----+-------
+  0 | 10.0000000000000000 |  20 |    60
+  1 | 11.0000000000000000 |  21 |    60
+ 10 | 10.0000000000000000 |  20 |    60
+ 11 | 11.0000000000000000 |  21 |    60
+ 20 | 10.0000000000000000 |  20 |    60
+ 21 | 11.0000000000000000 |  21 |    60
+ 30 | 10.0000000000000000 |  20 |    60
+ 31 | 11.0000000000000000 |  21 |    60
+ 40 | 10.0000000000000000 |  20 |    60
+ 41 | 11.0000000000000000 |  21 |    60
+(10 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
-         Filter: (sum(pagg_tab.a) < 700)
          ->  Append
-               ->  Partial HashAggregate
-                     Group Key: pagg_tab.b
-                     ->  Foreign Scan on fpagg_tab_p1 pagg_tab
-               ->  Partial HashAggregate
-                     Group Key: pagg_tab_1.b
-                     ->  Foreign Scan on fpagg_tab_p2 pagg_tab_1
-               ->  Partial HashAggregate
-                     Group Key: pagg_tab_2.b
-                     ->  Foreign Scan on fpagg_tab_p3 pagg_tab_2
+               ->  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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+                                                                  QUERY PLAN                                                                  
+----------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: ((((pagg_tab.b / 2)))::numeric), (avg(pagg_tab.a)), (max(pagg_tab.a)), (count(*)), ((pagg_tab.b / 2))
+   Sort Key: ((((pagg_tab.b / 2)))::numeric)
+   ->  Finalize HashAggregate
+         Output: (((pagg_tab.b / 2)))::numeric, avg(pagg_tab.a), max(pagg_tab.a), count(*), ((pagg_tab.b / 2))
+         Group Key: ((pagg_tab.b / 2))
+         ->  Append
+               ->  Foreign Scan
+                     Output: ((pagg_tab.b / 2)), 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 / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p1 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_1.b / 2)), 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 / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p2 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_2.b / 2)), 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 / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p3 GROUP BY 1, 2
+(19 rows)
+
+SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+ numeric |         avg         | max | count 
+---------+---------------------+-----+-------
+       0 | 10.5000000000000000 |  21 |   120
+       1 | 12.5000000000000000 |  23 |   120
+       2 | 14.5000000000000000 |  25 |   120
+       3 | 16.5000000000000000 |  27 |   120
+       4 | 18.5000000000000000 |  29 |   120
+       5 | 10.5000000000000000 |  21 |   120
+       6 | 12.5000000000000000 |  23 |   120
+       7 | 14.5000000000000000 |  25 |   120
+       8 | 16.5000000000000000 |  27 |   120
+       9 | 18.5000000000000000 |  29 |   120
+      10 | 10.5000000000000000 |  21 |   120
+      11 | 12.5000000000000000 |  23 |   120
+      12 | 14.5000000000000000 |  25 |   120
+      13 | 16.5000000000000000 |  27 |   120
+      14 | 18.5000000000000000 |  29 |   120
+      15 | 10.5000000000000000 |  21 |   120
+      16 | 12.5000000000000000 |  23 |   120
+      17 | 14.5000000000000000 |  25 |   120
+      18 | 16.5000000000000000 |  27 |   120
+      19 | 18.5000000000000000 |  29 |   120
+      20 | 10.5000000000000000 |  21 |   120
+      21 | 12.5000000000000000 |  23 |   120
+      22 | 14.5000000000000000 |  25 |   120
+      23 | 16.5000000000000000 |  27 |   120
+      24 | 18.5000000000000000 |  29 |   120
+(25 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)
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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)
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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)
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+                                                                           QUERY PLAN                                                                           
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: avg(t1.b), avg((t1.b)::bigint)
+   ->  Append
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1.b)), (PARTIAL avg((t1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p1 t1) INNER JOIN (public.fpagg_tab_p1 t2))
+               Remote SQL: SELECT avg_p_int4(r4.b), avg_p_int8(r4.b::bigint) FROM (public.pagg_tab_p1 r4 INNER JOIN public.pagg_tab_p1 r7 ON (((r4.a = r7.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_1.b)), (PARTIAL avg((t1_1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p2 t1_1) INNER JOIN (public.fpagg_tab_p2 t2_1))
+               Remote SQL: SELECT avg_p_int4(r5.b), avg_p_int8(r5.b::bigint) FROM (public.pagg_tab_p2 r5 INNER JOIN public.pagg_tab_p2 r8 ON (((r5.a = r8.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_2.b)), (PARTIAL avg((t1_2.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p3 t1_2) INNER JOIN (public.fpagg_tab_p3 t2_2))
+               Remote SQL: SELECT avg_p_int4(r6.b), avg_p_int8(r6.b::bigint) FROM (public.pagg_tab_p3 r6 INNER JOIN public.pagg_tab_p3 r9 ON (((r6.a = r9.a))))
+(15 rows)
+
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+         avg         |         avg         
+---------------------+---------------------
+ 24.5000000000000000 | 24.5000000000000000
+(1 row)
+
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+-- 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/option.c b/contrib/postgres_fdw/option.c
index 8c822f4ef9..67e15c8bb9 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -126,7 +126,8 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
 			strcmp(def->defname, "async_capable") == 0 ||
 			strcmp(def->defname, "parallel_commit") == 0 ||
 			strcmp(def->defname, "parallel_abort") == 0 ||
-			strcmp(def->defname, "keep_connections") == 0)
+			strcmp(def->defname, "keep_connections") == 0 ||
+			strcmp(def->defname, "check_partial_aggregate_support") == 0)
 		{
 			/* these accept only boolean values */
 			(void) defGetBoolean(def);
@@ -268,6 +269,7 @@ InitPgFdwOptions(void)
 		/* batch_size is available on both server and table */
 		{"batch_size", ForeignServerRelationId, false},
 		{"batch_size", ForeignTableRelationId, false},
+		{"check_partial_aggregate_support", ForeignServerRelationId, false},
 		/* async_capable is available on both server and table */
 		{"async_capable", ForeignServerRelationId, false},
 		{"async_capable", ForeignTableRelationId, false},
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c5cada55fb..de71a1600e 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -36,6 +36,7 @@
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
+#include "optimizer/planner.h"
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
@@ -519,7 +520,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);
+								GroupPathExtraData *extra);
 static List *get_useful_pathkeys_for_relation(PlannerInfo *root,
 											  RelOptInfo *rel);
 static List *get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel);
@@ -650,6 +651,8 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	fpinfo->fdw_tuple_cost = DEFAULT_FDW_TUPLE_COST;
 	fpinfo->shippable_extensions = NIL;
 	fpinfo->fetch_size = 100;
+	fpinfo->check_partial_aggregate_support = false;
+	fpinfo->remoteversion = 0;
 	fpinfo->async_capable = false;
 
 	apply_server_options(fpinfo);
@@ -661,7 +664,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	 * should match what ExecCheckPermissions() does.  If we fail due to lack
 	 * of permissions, the query would have failed at runtime anyway.
 	 */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
 		Oid			userid;
 
@@ -5946,9 +5949,10 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
 	fpinfo->pushdown_safe = true;
 
 	/* Get user mapping */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
-		if (fpinfo_o->use_remote_estimate)
+		if (fpinfo_o->use_remote_estimate ||
+			fpinfo_o->check_partial_aggregate_support)
 			fpinfo->user = fpinfo_o->user;
 		else
 			fpinfo->user = fpinfo_i->user;
@@ -6127,6 +6131,8 @@ apply_server_options(PgFdwRelationInfo *fpinfo)
 				ExtractExtensionList(defGetString(def), false);
 		else if (strcmp(def->defname, "fetch_size") == 0)
 			(void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
+		else if (strcmp(def->defname, "check_partial_aggregate_support") == 0)
+			fpinfo->check_partial_aggregate_support = defGetBoolean(def);
 		else if (strcmp(def->defname, "async_capable") == 0)
 			fpinfo->async_capable = defGetBoolean(def);
 	}
@@ -6186,6 +6192,8 @@ merge_fdw_options(PgFdwRelationInfo *fpinfo,
 	fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
 	fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
 	fpinfo->fetch_size = fpinfo_o->fetch_size;
+	fpinfo->check_partial_aggregate_support = fpinfo_o->check_partial_aggregate_support;
+	fpinfo->remoteversion = fpinfo_o->remoteversion;
 	fpinfo->async_capable = fpinfo_o->async_capable;
 
 	/* Merge the table level options from either side of the join. */
@@ -6366,7 +6374,7 @@ postgresGetForeignJoinPaths(PlannerInfo *root,
  */
 static bool
 foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
-					Node *havingQual)
+					GroupPathExtraData *extra)
 {
 	Query	   *query = root->parse;
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
@@ -6375,6 +6383,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	ListCell   *lc;
 	int			i;
 	List	   *tlist = NIL;
+	bool		partial = extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL;
 
 	/* We currently don't support pushing Grouping Sets. */
 	if (query->groupingSets)
@@ -6408,6 +6417,12 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * a node, as long as it's not at top level; then no match is possible.
 	 */
 	i = 0;
+	fpinfo->group_clause = query->groupClause;
+	if (partial)
+	{
+		fpinfo->group_clause = extra->groupClausePartial;
+		grouping_target = extra->partial_target;
+	}
 	foreach(lc, grouping_target->exprs)
 	{
 		Expr	   *expr = (Expr *) lfirst(lc);
@@ -6419,7 +6434,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 		 * check the whole GROUP BY clause not just processed_groupClause,
 		 * because we will ship all of it, cf. appendGroupByClause.
 		 */
-		if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
+		if (sgref && get_sortgroupref_clause_noerr(sgref, fpinfo->group_clause))
 		{
 			TargetEntry *tle;
 
@@ -6505,9 +6520,9 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * Classify the pushable and non-pushable HAVING clauses and save them in
 	 * remote_conds and local_conds of the grouped rel's fpinfo.
 	 */
-	if (havingQual)
+	if (extra->havingQual)
 	{
-		foreach(lc, (List *) havingQual)
+		foreach(lc, (List *) extra->havingQual)
 		{
 			Expr	   *expr = (Expr *) lfirst(lc);
 			RestrictInfo *rinfo;
@@ -6527,7 +6542,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 									  grouped_rel->relids,
 									  NULL,
 									  NULL);
-			if (is_foreign_expr(root, grouped_rel, expr))
+			if (is_foreign_expr(root, grouped_rel, expr) && !partial)
 				fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
 			else
 				fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
@@ -6546,6 +6561,12 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 		{
 			RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
 
+			if (partial)
+			{
+				rinfo = copyObject(rinfo);
+				lfirst(lc) = rinfo;
+			}
+
 			aggvars = list_concat(aggvars,
 								  pull_var_clause((Node *) rinfo->clause,
 												  PVC_INCLUDE_AGGREGATES));
@@ -6564,7 +6585,12 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 			 */
 			if (IsA(expr, Aggref))
 			{
-				if (!is_foreign_expr(root, grouped_rel, expr))
+				if (partial)
+				{
+					mark_partial_aggref((Aggref *) expr, AGGSPLIT_INITIAL_SERIAL);
+					continue;
+				}
+				else if (!is_foreign_expr(root, grouped_rel, expr))
 					return false;
 
 				tlist = add_to_flat_tlist(tlist, list_make1(expr));
@@ -6621,6 +6647,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 +6664,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 +6708,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 +6729,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))
 		return;
 
 	/*
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index 02c1152319..c60f3f5465 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -86,6 +86,15 @@ typedef struct PgFdwRelationInfo
 	ForeignServer *server;
 	UserMapping *user;			/* only set in use_remote_estimate mode */
 
+	/* for partial aggregate pushdown */
+	bool		check_partial_aggregate_support;
+
+	/*
+	 * if remoteversion is zero, then that means the remote server version is
+	 * unacquired.
+	 */
+	int			remoteversion;
+
 	int			fetch_size;		/* fetch size for this remote table */
 
 	/*
@@ -110,6 +119,7 @@ typedef struct PgFdwRelationInfo
 
 	/* Grouping information */
 	List	   *grouped_tlist;
+	List	   *group_clause;
 
 	/* Subquery information */
 	bool		make_outerrel_subquery; /* do we deparse outerrel as a
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index b54903ad8f..1824cb67fe 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.1');
+
+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');
@@ -2980,6 +2995,9 @@ CREATE FOREIGN TABLE fpagg_tab_p2 PARTITION OF pagg_tab FOR VALUES FROM (10) TO
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -3002,10 +3020,289 @@ EXPLAIN (VERBOSE, COSTS OFF)
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are safe 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;
 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 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;
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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;
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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;
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+
+-- 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..4f5ff32e96 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="partial-aggregate-pushdown"/> 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..676abc6fa5 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -451,6 +451,37 @@ OPTIONS (ADD password_required 'false');
 
   </sect3>
 
+  <sect3 id="postgres-fdw-options-partial-aggregate-pushdown">
+   <title>Partial Aggregate Pushdown Options</title>
+
+   <para>
+    By default, <filename>postgres_fdw</filename> assumes that for each built-in aggregate
+    function, the partial aggregate function is defined on the remote server. See
+    <xref linkend="partial-aggregate-pushdown"/> for the definition of the partial aggregate
+    function. This may be overridden using the following option:
+   </para>
+
+   <variablelist>
+
+    <varlistentry>
+     <term><literal>check_partial_aggregate_support</literal> (<type>boolean</type>)</term>
+     <listitem>
+      <para>
+       When this option is true, <filename>postgres_fdw</filename> connects to the remote server
+       and gets its remote server version during query planning.
+       If the remote server version is older than the local server version,
+       <filename>postgres_fdw</filename>
+       assumes that for each aggregate function, the partial aggregate function is not defined
+       on the remote server unless the partial aggregate function and the aggregate
+       function match.
+       The default is <literal>false</literal>.
+      </para>
+     </listitem>
+    </varlistentry>
+
+   </variablelist>
+  </sect3>
+
   <sect3 id="postgres-fdw-options-asynchronous-execution">
    <title>Asynchronous Execution Options</title>
 
@@ -929,13 +960,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 +999,68 @@ postgres=# SELECT postgres_fdw_disconnect_all();
   </para>
  </sect2>
 
+ <sect2 id="partial-aggregate-pushdown">
+  <title>Partial aggregate pushdown</title>
+  <para>
+   Partial aggregate pushdown is an optimization for a query that contains aggregate
+   expressions for a partitioned table across one or more remote servers. If the multiple
+   conditions are true for a remote server, an aggregate expression is sent to that
+   remote server and the aggregate is a partial aggregate on the local server.
+   The conditions under which this sending is active are as follows.
+   <itemizedlist spacing="compact">
+     <listitem>
+      <para>
+       An aggregate function, called the partial aggregate function for partial aggregate
+       that corresponding to the aggregate expression, is defined on the local server and
+       the remote server.
+      </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>
+       The aggregate expressions in the query don't contain
+       a <literal>DISTINCT</literal> or <literal>ORDER BY</literal> clause.
+      </para>
+     </listitem>
+    </itemizedlist>
+   When this sending is active for aggregate expressions, any aggregate
+   function is replaced by the partial aggregate function in the remote query.
+   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.
+   By default, <filename>postgres_fdw</filename> assumes that for each built-in
+   aggregate function, the partial aggregate function is defined on the local server
+   and the remote server.
+   If <literal>check_partial_aggregate_support</literal> is true and the remote server version is
+   older than the local server version, <filename>postgres_fdw</filename> does not assume
+   that the partial aggregate function is on the remote server unless
+   the partial aggregate function and the aggregate function match.
+   <filename>postgres_fdw</filename> assumes that a user-defined aggregate
+   function has the partial aggregate function on the remote server
+   only if both belong to an extension that is listed in the foreign server's
+   extensions option.
+  </para>
+ </sect2>
+
  <sect2 id="postgres-fdw-remote-query-execution-environment">
   <title>Remote Query Execution Environment</title>
 
@@ -1031,14 +1126,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 partial aggregate pushdown feature.
+   When using the partial aggregate pushdown feature,
+   the partial aggregate function of the aggregate function is sent to
+   the remote server if the conditions in
+   <xref linkend="partial-aggregate-pushdown"/> are met.
+   A limitation is that by default, <filename>postgres_fdw</filename> assumes that
+   for each built-in aggregate function, the partial aggregate function is defined
+   on the remote server.
+   This limitation might cause <quote>function does not exist</quote>
+   or a similar error if the remote server version is older than the local server version.
+   This problem can be worked around by setting
+   <literal>check_partial_aggregate_support</literal> to <literal>true</literal>
+   or similar query rewriting as above.
   </para>
  </sect2>
 
diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml
index 222e0aa5c9..2875734dea 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>
@@ -246,6 +248,18 @@ CREATE [ OR REPLACE ] AGGREGATE <replaceable class="parameter">name</replaceable
    marked <literal>PARALLEL SAFE</literal> to enable parallel aggregation.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize a query containing aggregate
+   expressions for a partitioned table across one or more remote servers.
+   This optimization sends an aggregate expression to a remote server and the
+   aggregate is a partial aggregate on the local server. This optimization
+   requires an aggregate function called a <firstterm>partial aggregate
+   function</firstterm>. See
+   <xref linkend="partial-aggregate-pushdown"/> for the definition of
+   partial aggregate function. <literal>AGGPARTIALFUNC</literal> is a parameter
+   of the partial aggregate function.
+  </para>
+
   <para>
    Aggregates that behave like <function>MIN</function> or <function>MAX</function> can
    sometimes be optimized by looking into an index instead of scanning every
@@ -653,6 +667,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..607c859974 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,18 @@ SELECT percentile_disc(0.5) WITHIN GROUP (ORDER BY income) FROM households;
    parallel-safety markings on its support functions are not consulted.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize a query containing
+   aggregate expressions for a partitioned table across one or more
+   remote servers. This optimization sends an aggregate expression to
+   a remote server and the aggregate is a partial aggregate on the
+   local server. This optimization requires an aggregate function
+   called the <firstterm>partial aggregate function</firstterm>.
+   See
+   <xref linkend="partial-aggregate-pushdown"/> 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/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 1e4dd27dba..f4b8781e1e 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -206,7 +206,7 @@ static PathTarget *make_group_input_target(PlannerInfo *root,
 										   PathTarget *final_target);
 static PathTarget *make_partial_grouping_target(PlannerInfo *root,
 												PathTarget *grouping_target,
-												Node *havingQual);
+												GroupPathExtraData *extra);
 static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
 static void optimize_window_clauses(PlannerInfo *root,
 									WindowFuncLists *wflists);
@@ -5398,6 +5398,64 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
 	return set_pathtarget_cost_width(root, input_target);
 }
 
+/*
+ * setGroupClausePartial
+ *	  Generate a groupClause for partial aggregate and set it to GroupPathExtraData.
+ */
+static void
+setGroupClausePartial(PathTarget *partial_target, List *non_group_exprs,
+					  List *groupClause, GroupPathExtraData *extra)
+{
+	int			exprno,
+				refno;
+	ListCell   *lc;
+	Index		maxRef = 0;
+	List	   *exprs_processed = NIL;
+
+	foreach(lc, groupClause)
+	{
+		SortGroupClause *sgc = (SortGroupClause *) lfirst(lc);
+
+		if (sgc->tleSortGroupRef > maxRef)
+			maxRef = sgc->tleSortGroupRef;
+	}
+	maxRef++;
+
+	extra->groupClausePartial = list_copy_deep(groupClause);
+	extra->partial_target = copy_pathtarget(partial_target);
+
+	if (!partial_target->exprs)
+		return;
+
+	foreach(lc, non_group_exprs)
+	{
+		Expr	   *expr = (Expr *) lfirst(lc);
+
+		refno = -1;
+		if (list_member(exprs_processed, expr) || !IsA(expr, Var))
+			continue;
+		exprs_processed = lappend(exprs_processed, expr);
+		for (exprno = 0; exprno < partial_target->exprs->length; exprno++)
+		{
+			Expr	   *target_expr = (Expr *) list_nth(partial_target->exprs, exprno);
+
+			if (equal(target_expr, expr))
+			{
+				refno = exprno;
+				break;
+			}
+		}
+		if (refno < 0)
+		{
+			SortGroupClause *grpcl = makeNode(SortGroupClause);
+
+			grpcl->tleSortGroupRef = maxRef++;
+			extra->groupClausePartial = lappend(extra->groupClausePartial, grpcl);
+			add_column_to_pathtarget(extra->partial_target, expr, grpcl->tleSortGroupRef);
+		}
+	}
+}
+
 /*
  * make_partial_grouping_target
  *	  Generate appropriate PathTarget for output of partial aggregate
@@ -5417,7 +5473,7 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
 static PathTarget *
 make_partial_grouping_target(PlannerInfo *root,
 							 PathTarget *grouping_target,
-							 Node *havingQual)
+							 GroupPathExtraData *extra)
 {
 	PathTarget *partial_target;
 	List	   *non_group_cols;
@@ -5459,8 +5515,8 @@ make_partial_grouping_target(PlannerInfo *root,
 	/*
 	 * If there's a HAVING clause, we'll need the Vars/Aggrefs it uses, too.
 	 */
-	if (havingQual)
-		non_group_cols = lappend(non_group_cols, havingQual);
+	if (extra->havingQual)
+		non_group_cols = lappend(non_group_cols, extra->havingQual);
 
 	/*
 	 * Pull out all the Vars, PlaceHolderVars, and Aggrefs mentioned in
@@ -5473,7 +5529,7 @@ make_partial_grouping_target(PlannerInfo *root,
 									  PVC_INCLUDE_AGGREGATES |
 									  PVC_RECURSE_WINDOWFUNCS |
 									  PVC_INCLUDE_PLACEHOLDERS);
-
+	setGroupClausePartial(partial_target, non_group_exprs, root->parse->groupClause, extra);
 	add_new_columns_to_pathtarget(partial_target, non_group_exprs);
 
 	/*
@@ -5502,6 +5558,7 @@ make_partial_grouping_target(PlannerInfo *root,
 			lfirst(lc) = newaggref;
 		}
 	}
+	extra->partial_target->exprs = partial_target->exprs;
 
 	/* clean up cruft */
 	list_free(non_group_exprs);
@@ -7096,7 +7153,7 @@ create_partial_grouping_paths(PlannerInfo *root,
 	 */
 	partially_grouped_rel->reltarget =
 		make_partial_grouping_target(root, grouped_rel->reltarget,
-									 extra->havingQual);
+									 extra);
 
 	if (!extra->partial_costs_set)
 	{
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/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index c17b53f7ad..443de6fb24 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -3244,6 +3244,8 @@ typedef struct
 	Node	   *havingQual;
 	List	   *targetList;
 	PartitionwiseAggregateType patype;
+	List	   *groupClausePartial;
+	PathTarget *partial_target;
 } GroupPathExtraData;
 
 /*
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] 38+ messages in thread

* Re: Partial aggregates pushdown
@ 2023-07-20 10:23  Alexander Pyhalov <[email protected]>
  parent: [email protected] <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: Alexander Pyhalov @ 2023-07-20 10:23 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-07-19 03:43:
> Hi Mr.Pyhalov, hackers.

> 3)
> I modified the patch to safely do a partial aggregate pushdown for
> queries which contain having clauses.
> 

Hi.
Sorry, but I don't see how it could work.
For example, the attached test returns wrong result:

CREATE FUNCTION f() RETURNS INT AS $$
begin
   return 10;
end
$$ LANGUAGE PLPGSQL;

SELECT b, sum(a) FROM pagg_tab GROUP BY b HAVING sum(a) < f() ORDER BY 
1;
  b  | sum
----+-----
   0 |   0
  10 |   0
  20 |   0
  30 |   0
  40 |   0
+(5 rows)

In fact the above query should have returned 0 rows, as

SELECT b, sum(a) FROM pagg_tab GROUP BY b ORDER BY 1;
  b  | sum
----+------
   0 |  600
   1 |  660
   2 |  720
   3 |  780
   4 |  840
   5 |  900
   6 |  960
   7 | 1020
   8 | 1080
   9 | 1140
  10 |  600
  11 |  660
  12 |  720
....
shows no such rows.

Or, on the same data

SELECT b, sum(a) FROM pagg_tab GROUP BY b HAVING sum(a) > 660 ORDER BY 
1;

You'll get 0 rows.

But
SELECT b, sum(a) FROM pagg_tab GROUP BY b;
  b  | sum
----+------
  42 |  720
  29 | 1140
   4 |  840
  34 |  840
  41 |  660
   0 |  600
  40 |  600
gives.

The issue is that you can't calculate "partial" having. You should 
compare full aggregate in filter, but it's not possible on the level of 
one partition.
And you have this in plans

  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
                ->  Foreign Scan
                      Output: pagg_tab.b, (PARTIAL avg(pagg_tab.a)), 
(PARTIAL max(pagg_tab.a)), (PARTIAL count(*)), (PARTIAL sum(pagg_tab.a))
                      Filter: ((PARTIAL sum(pagg_tab.a)) < 700)   !!!! 
<--- here we can't compare anything yet, sum is incomplete.
                      Relations: Aggregate on (public.fpagg_tab_p1 
pagg_tab)
                      Remote SQL: SELECT b, avg_p_int4(a), max(a), 
count(*), sum(a) 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(*)), (PARTIAL 
sum(pagg_tab_1.a))
                      Filter: ((PARTIAL sum(pagg_tab_1.a)) < 700)
                      Relations: Aggregate on (public.fpagg_tab_p2 
pagg_tab_1)
                      Remote SQL: SELECT b, avg_p_int4(a), max(a), 
count(*), sum(a) 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(*)), (PARTIAL 
sum(pagg_tab_2.a))
                      Filter: ((PARTIAL sum(pagg_tab_2.a)) < 700)
                      Relations: Aggregate on (public.fpagg_tab_p3 
pagg_tab_2)
                      Remote SQL: SELECT b, avg_p_int4(a), max(a), 
count(*), sum(a) FROM public.pagg_tab_p3 GROUP BY 1

In foreign_grouping_ok()
6586                         if (IsA(expr, Aggref))
6587                         {
6588                                 if (partial)
6589                                 {
6590                                         mark_partial_aggref((Aggref 
*) expr, AGGSPLIT_INITIAL_SERIAL);
6591                                         continue;
6592                                 }
6593                                 else if (!is_foreign_expr(root, 
grouped_rel, expr))
6594                                         return false;
6595
6596                                 tlist = add_to_flat_tlist(tlist, 
list_make1(expr));
6597                         }

at least you shouldn't do anything with expr, if is_foreign_expr() 
returned false. If we restrict pushing down queries with havingQuals, 
I'm not quite sure how Aggref can appear in local_conds.

As for changes in planner.c (setGroupClausePartial()) I have several 
questions.

1) Why don't we add non_group_exprs to pathtarget->exprs when 
partial_target->exprs is not set?

2) We replace extra->partial_target->exprs with partial_target->exprs 
after processing. Why are we sure that after this tleSortGroupRef is 
correct?

-- 
Best regards,
Alexander Pyhalov,
Postgres Professional

Attachments:

  [text/x-diff] check.patch (1.1K, ../../[email protected]/2-check.patch)
  download | inline diff:
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 1824cb67fe9..c6f613019c3 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -3025,6 +3025,22 @@ EXPLAIN (VERBOSE, COSTS OFF)
 SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
 SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
 
+CREATE FUNCTION f() RETURNS INT AS $$
+begin
+  return 10;
+end 
+$$ LANGUAGE PLPGSQL;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT b, sum(a) FROM pagg_tab GROUP BY b HAVING sum(a) < f() ORDER BY 1;
+SELECT b, sum(a) FROM pagg_tab GROUP BY b HAVING sum(a) < f() ORDER BY 1;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT b, sum(a) FROM pagg_tab GROUP BY b ORDER BY 1;
+SELECT b, sum(a) FROM pagg_tab GROUP BY b ORDER BY 1;
+
+DROP function f();
+
 -- 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;


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

* Re: Partial aggregates pushdown
@ 2023-08-01 20:25  Finnerty, Jim <[email protected]>
  parent: Alexander Pyhalov <[email protected]>
  0 siblings, 0 replies; 38+ messages in thread

From: Finnerty, Jim @ 2023-08-01 20:25 UTC (permalink / raw)
  To: Alexander Pyhalov <[email protected]>; [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]>

When it is valid to filter based on a HAVING clause predicate, it should already have been converted into a WHERE clause predicate, except in the special case of an LIMIT TO .k .. ORDER BY case where the HAVING clause predicate can be determined approximately after having found k fully qualified tuples and then that predicate is successively tightened as more qualified records are found.

*that*, by the way, is a very powerful optimization.

    /Jim F

On 7/20/23, 6:24 AM, "Alexander Pyhalov" <[email protected] <mailto:[email protected]>> wrote:


CAUTION: This email originated from outside of the organization. Do not click links or open attachments unless you can confirm the sender and know the content is safe.






[email protected] <mailto:[email protected]> писал 2023-07-19 03:43:
> Hi Mr.Pyhalov, hackers.


> 3)
> I modified the patch to safely do a partial aggregate pushdown for
> queries which contain having clauses.
>


Hi.
Sorry, but I don't see how it could work.
For example, the attached test returns wrong result:


CREATE FUNCTION f() RETURNS INT AS $$
begin
return 10;
end
$$ LANGUAGE PLPGSQL;


SELECT b, sum(a) FROM pagg_tab GROUP BY b HAVING sum(a) < f() ORDER BY
1;
b | sum
----+-----
0 | 0
10 | 0
20 | 0
30 | 0
40 | 0
+(5 rows)


In fact the above query should have returned 0 rows, as


SELECT b, sum(a) FROM pagg_tab GROUP BY b ORDER BY 1;
b | sum
----+------
0 | 600
1 | 660
2 | 720
3 | 780
4 | 840
5 | 900
6 | 960
7 | 1020
8 | 1080
9 | 1140
10 | 600
11 | 660
12 | 720
....
shows no such rows.


Or, on the same data


SELECT b, sum(a) FROM pagg_tab GROUP BY b HAVING sum(a) > 660 ORDER BY
1;


You'll get 0 rows.


But
SELECT b, sum(a) FROM pagg_tab GROUP BY b;
b | sum
----+------
42 | 720
29 | 1140
4 | 840
34 | 840
41 | 660
0 | 600
40 | 600
gives.


The issue is that you can't calculate "partial" having. You should
compare full aggregate in filter, but it's not possible on the level of
one partition.
And you have this in plans


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
-> Foreign Scan
Output: pagg_tab.b, (PARTIAL avg(pagg_tab.a)),
(PARTIAL max(pagg_tab.a)), (PARTIAL count(*)), (PARTIAL sum(pagg_tab.a))
Filter: ((PARTIAL sum(pagg_tab.a)) < 700) !!!!
<--- here we can't compare anything yet, sum is incomplete.
Relations: Aggregate on (public.fpagg_tab_p1
pagg_tab)
Remote SQL: SELECT b, avg_p_int4(a), max(a),
count(*), sum(a) 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(*)), (PARTIAL
sum(pagg_tab_1.a))
Filter: ((PARTIAL sum(pagg_tab_1.a)) < 700)
Relations: Aggregate on (public.fpagg_tab_p2
pagg_tab_1)
Remote SQL: SELECT b, avg_p_int4(a), max(a),
count(*), sum(a) 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(*)), (PARTIAL
sum(pagg_tab_2.a))
Filter: ((PARTIAL sum(pagg_tab_2.a)) < 700)
Relations: Aggregate on (public.fpagg_tab_p3
pagg_tab_2)
Remote SQL: SELECT b, avg_p_int4(a), max(a),
count(*), sum(a) FROM public.pagg_tab_p3 GROUP BY 1


In foreign_grouping_ok()
6586 if (IsA(expr, Aggref))
6587 {
6588 if (partial)
6589 {
6590 mark_partial_aggref((Aggref
*) expr, AGGSPLIT_INITIAL_SERIAL);
6591 continue;
6592 }
6593 else if (!is_foreign_expr(root,
grouped_rel, expr))
6594 return false;
6595
6596 tlist = add_to_flat_tlist(tlist,
list_make1(expr));
6597 }


at least you shouldn't do anything with expr, if is_foreign_expr()
returned false. If we restrict pushing down queries with havingQuals,
I'm not quite sure how Aggref can appear in local_conds.


As for changes in planner.c (setGroupClausePartial()) I have several
questions.


1) Why don't we add non_group_exprs to pathtarget->exprs when
partial_target->exprs is not set?


2) We replace extra->partial_target->exprs with partial_target->exprs
after processing. Why are we sure that after this tleSortGroupRef is
correct?


--
Best regards,
Alexander Pyhalov,
Postgres Professional





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

* Re: Partial aggregates pushdown
@ 2023-08-07 18:30  Bruce Momjian <[email protected]>
  parent: [email protected] <[email protected]>
  1 sibling, 1 reply; 38+ messages in thread

From: Bruce Momjian @ 2023-08-07 18:30 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 Mon, Jul 10, 2023 at 07:35:27AM +0000, [email protected] wrote:
> > > I will add a postgres_fdw option "check_partial_aggregate_support".
> > > This option is false, default.
> > > Only if this option is true, postgres_fdw connect to the remote server and get the version of the remote server.
> > > And if the version of the remote server is less than PG17, then partial aggregate push down to the remote server is
> > disable.
> > 
> > Great!
> I have modified the program except for the point "if the version of the remote server is less than PG17".
> Instead, we have addressed the following.
> "If check_partial_aggregate_support is true and the remote server version is older than the local server
> version, postgres_fdw does not assume that the partial aggregate function is on the remote server unless
> the partial aggregate function and the aggregate function match."
> The reason for this is to maintain compatibility with any aggregate function that does not support partial
> aggregate in one version of V1 (V1 is PG17 or higher), even if the next version supports partial aggregate.
> For example, string_agg does not support partial aggregation in PG15, but it will support partial aggregation
> in PG16.

Just to clarify, I think you are saying:

	If check_partial_aggregate_support is true and the remote server
	version is older than the local server version, postgres_fdw
	checks if the partial aggregate function exists on the remote
	server during planning and only uses it if it does.

I tried to phrase it in a positive way, and mentioned the plan time
distinction.  Also, I am sorry I was away for most of July and am just
getting to this.

-- 
  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] 38+ messages in thread

* RE: Partial aggregates pushdown
@ 2023-09-25 03:18  [email protected] <[email protected]>
  parent: Bruce Momjian <[email protected]>
  0 siblings, 2 replies; 38+ messages in thread

From: [email protected] @ 2023-09-25 03:18 UTC (permalink / raw)
  To: Bruce Momjian <[email protected]>; Alexander Pyhalov <[email protected]>; Finnerty, Jim <[email protected]>; pgsql-hackers; +Cc: 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.Bruce, Mr.Pyhalov, Mr.Finnerty, hackers.

Thank you for your valuable comments. I sincerely apologize for the very late reply.
Here is a response to your comments or a fix to the patch.

Tuesday, August 8, 2023 at 3:31 Bruce Momjian
> > I have modified the program except for the point "if the version of the remote server is less than PG17".
> > Instead, we have addressed the following.
> > "If check_partial_aggregate_support is true and the remote server version is older than the local server
> > version, postgres_fdw does not assume that the partial aggregate function is on the remote server unless
> > the partial aggregate function and the aggregate function match."
> > The reason for this is to maintain compatibility with any aggregate function that does not support partial
> > aggregate in one version of V1 (V1 is PG17 or higher), even if the next version supports partial aggregate.
> > For example, string_agg does not support partial aggregation in PG15, but it will support partial aggregation
> > in PG16.
>
> Just to clarify, I think you are saying:
>
>         If check_partial_aggregate_support is true and the remote server
>         version is older than the local server version, postgres_fdw
>         checks if the partial aggregate function exists on the remote
>         server during planning and only uses it if it does.
>
> I tried to phrase it in a positive way, and mentioned the plan time
> distinction.  Also, I am sorry I was away for most of July and am just
> getting to this.
Thanks for your comment. In the documentation, the description of check_partial_aggregate_support is as follows
(please see postgres-fdw.sgml).
--
check_partial_aggregate_support (boolean)
Only if this option is true, during query planning, postgres_fdw connects to the remote server and check if the remote server version is older than the local server version. If so, postgres_fdw assumes that for each built-in aggregate function, the partial aggregate function is not defined on the remote server unless the partial aggregate function and the aggregate function match. The default is false.
--

Thursday, 20 July 2023 19:23 Alexander Pyhalov <[email protected]>:
> [email protected] писал 2023-07-19 03:43:
> > Hi Mr.Pyhalov, hackers.
>
> > 3)
> > I modified the patch to safely do a partial aggregate pushdown for
> > queries which contain having clauses.
> >
>
> Hi.
> Sorry, but I don't see how it could work.
We apologize for any inconvenience caused.
Thanks to Pyhalov's and Jim's comments, I have realized that I have made a fundamental mistake regarding the pushdown of the HAVING clause and the difficulty of achieving it performing Partial aggregate pushdown.
So, I removed the codes about pushdown of the HAVING clause performing Partial aggregate pushdown.

Thursday, 20 July 2023 19:23 Alexander Pyhalov <[email protected]>:
> As for changes in planner.c (setGroupClausePartial()) I have several
> questions.
>
> 1) Why don't we add non_group_exprs to pathtarget->exprs when
> partial_target->exprs is not set?
>
> 2) We replace extra->partial_target->exprs with partial_target->exprs
> after processing. Why are we sure that after this tleSortGroupRef is
> correct?
Response to 1)
The code you pointed out was unnecessary. I have removed this code.
Also, the process of adding PlaceHolderVar's expr to partial_target was missing.
So I fixed this.

Response to 2)
The making procedures extra->groupClausePartial and extra->partial_target 
in make_partial_grouping_target for this patch is as follows.
STEP1. From grouping_target->exprs, extract Aggref, Var and Placeholdervar that are not included in Aggref.
STEP2. setGroupClausePartial sets the copy of original groupClause to extra->groupClausePartial
and sets the copy of original partial_target to extra->partial_target.
STEP3. setGroupClausePartial adds Var and Placeholdervar in STEP1 to partial_target.
The sortgroupref of partial_target->sortgrouprefs to be added to value is set to
(the maximum value of the existing sortgroupref) + 1.
setGroupClausePartial adds data sgc of sortgroupclause type where sgc->tlesortgroupref
matches the sortgroupref to GroupClause.
STEP4. add_new_columns_to_pathtarget adds STEP1's Aggref to partial_target.

Due to STEP2, the list of tlesortgrouprefs set in extra->groupClausePartial is not duplicated.
Also, sortgrouprefs added to extra->partial_target matches with corresponding 
tlesortgrouprefs added to extra->groupClausePartial.
So these tlesortgrouprefs are correct.

Sincerely yours,
Yuuki Fujii

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


Attachments:

  [application/octet-stream] 0001-Partial-aggregates-push-down-v25.patch (264.2K, ../../OS3PR01MB6660FA1EC43389E75E0CB9C795FCA@OS3PR01MB6660.jpnprd01.prod.outlook.com/2-0001-Partial-aggregates-push-down-v25.patch)
  download | inline diff:
From 48393d5b50585f888fca126b969380b6b5121978 Mon Sep 17 00:00:00 2001
From: Yuki Fujii <[email protected]>
Date: Mon, 25 Sep 2023 10:56:32 +0900
Subject: [PATCH] Partial aggregates push down v25

---
 contrib/postgres_fdw/deparse.c                | 103 ++-
 .../postgres_fdw/expected/postgres_fdw.out    | 868 +++++++++++++++++-
 contrib/postgres_fdw/option.c                 |   4 +-
 contrib/postgres_fdw/postgres_fdw.c           |  47 +-
 contrib/postgres_fdw/postgres_fdw.h           |  10 +
 contrib/postgres_fdw/sql/postgres_fdw.sql     | 311 ++++++-
 doc/src/sgml/catalogs.sgml                    |  13 +
 doc/src/sgml/postgres-fdw.sgml                | 130 ++-
 doc/src/sgml/ref/create_aggregate.sgml        |  42 +
 doc/src/sgml/xaggr.sgml                       |  15 +-
 src/backend/catalog/pg_aggregate.c            | 101 ++
 src/backend/commands/aggregatecmds.c          |   4 +
 src/backend/optimizer/plan/planner.c          |  73 +-
 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 ++++
 src/include/nodes/pathnodes.h                 |   2 +
 .../regress/expected/create_aggregate.out     | 184 ++++
 src/test/regress/expected/oidjoins.out        |   1 +
 src/test/regress/sql/create_aggregate.sql     | 183 ++++
 21 files changed, 2779 insertions(+), 201 deletions(-)

diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 09d6dd60dd..e957611879 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 */
@@ -3723,6 +3744,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	Query	   *query = context->root->parse;
 	ListCell   *lc;
 	bool		first = true;
+	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) context->foreignrel->fdw_private;
 
 	/* Nothing to be done, if there's no GROUP BY clause in the query. */
 	if (!query->groupClause)
@@ -3743,7 +3765,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	 * to empty, and in any case the redundancy situation on the remote might
 	 * be different than what we think here.
 	 */
-	foreach(lc, query->groupClause)
+	foreach(lc, fpinfo->group_clause)
 	{
 		SortGroupClause *grp = (SortGroupClause *) lfirst(lc);
 
@@ -4047,3 +4069,68 @@ get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
 	/* Shouldn't get here */
 	elog(ERROR, "unexpected expression in subquery output");
 }
+
+/*
+ * Check that a buit-in aggpartialfunc exists on the remote server. If
+ * check_partial_aggregate_support is false, we assume the partial aggregate
+ * function exsits on the remote server. Otherwise we assume the partial
+ * aggregate function exsits on the remote server only if the remote server
+ * version is not less than the local server version.
+ */
+static bool
+is_builtin_aggpartialfunc_shippable(Oid aggpartialfn, PgFdwRelationInfo *fpinfo)
+{
+	bool		shippable = true;
+
+	if (fpinfo->check_partial_aggregate_support)
+	{
+		if (fpinfo->remoteversion == 0)
+		{
+			PGconn	   *conn = GetConnection(fpinfo->user, false, NULL);
+
+			fpinfo->remoteversion = PQserverVersion(conn);
+		}
+		if (fpinfo->remoteversion < PG_VERSION_NUM)
+			shippable = false;
+	}
+	return shippable;
+}
+
+/*
+ * 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 exists on the remote server
+ */
+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_builtin(aggform->aggpartialfn))
+		partial_agg_ok = is_builtin_aggpartialfunc_shippable(
+															 aggform->aggpartialfn, fpinfo);
+	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 77df7eb8e4..087624bdf3 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -9758,18 +9758,35 @@ 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.1');
+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');
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -9823,8 +9840,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
@@ -9835,21 +9852,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;
@@ -9863,28 +9880,837 @@ SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
  21 |   100
 (6 rows)
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are 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                            
------------------------------------------------------------------
- Sort
-   Sort Key: pagg_tab.b
-   ->  Finalize HashAggregate
-         Group Key: pagg_tab.b
-         Filter: (sum(pagg_tab.a) < 700)
+                                                                 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 fpagg_tab_p1 pagg_tab
+                     ->  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 fpagg_tab_p2 pagg_tab_1
+                     ->  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 fpagg_tab_p3 pagg_tab_2
+                     ->  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)
+
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
+ b  |         avg         | max | count 
+----+---------------------+-----+-------
+  0 | 10.0000000000000000 |  20 |    60
+  1 | 11.0000000000000000 |  21 |    60
+ 10 | 10.0000000000000000 |  20 |    60
+ 11 | 11.0000000000000000 |  21 |    60
+ 20 | 10.0000000000000000 |  20 |    60
+ 21 | 11.0000000000000000 |  21 |    60
+ 30 | 10.0000000000000000 |  20 |    60
+ 31 | 11.0000000000000000 |  21 |    60
+ 40 | 10.0000000000000000 |  20 |    60
+ 41 | 11.0000000000000000 |  21 |    60
+(10 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+                                                                  QUERY PLAN                                                                  
+----------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: ((((pagg_tab.b / 2)))::numeric), (avg(pagg_tab.a)), (max(pagg_tab.a)), (count(*)), ((pagg_tab.b / 2))
+   Sort Key: ((((pagg_tab.b / 2)))::numeric)
+   ->  Finalize HashAggregate
+         Output: (((pagg_tab.b / 2)))::numeric, avg(pagg_tab.a), max(pagg_tab.a), count(*), ((pagg_tab.b / 2))
+         Group Key: ((pagg_tab.b / 2))
+         ->  Append
+               ->  Foreign Scan
+                     Output: ((pagg_tab.b / 2)), 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 / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p1 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_1.b / 2)), 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 / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p2 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_2.b / 2)), 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 / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p3 GROUP BY 1, 2
+(19 rows)
+
+SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+ numeric |         avg         | max | count 
+---------+---------------------+-----+-------
+       0 | 10.5000000000000000 |  21 |   120
+       1 | 12.5000000000000000 |  23 |   120
+       2 | 14.5000000000000000 |  25 |   120
+       3 | 16.5000000000000000 |  27 |   120
+       4 | 18.5000000000000000 |  29 |   120
+       5 | 10.5000000000000000 |  21 |   120
+       6 | 12.5000000000000000 |  23 |   120
+       7 | 14.5000000000000000 |  25 |   120
+       8 | 16.5000000000000000 |  27 |   120
+       9 | 18.5000000000000000 |  29 |   120
+      10 | 10.5000000000000000 |  21 |   120
+      11 | 12.5000000000000000 |  23 |   120
+      12 | 14.5000000000000000 |  25 |   120
+      13 | 16.5000000000000000 |  27 |   120
+      14 | 18.5000000000000000 |  29 |   120
+      15 | 10.5000000000000000 |  21 |   120
+      16 | 12.5000000000000000 |  23 |   120
+      17 | 14.5000000000000000 |  25 |   120
+      18 | 16.5000000000000000 |  27 |   120
+      19 | 18.5000000000000000 |  29 |   120
+      20 | 10.5000000000000000 |  21 |   120
+      21 | 12.5000000000000000 |  23 |   120
+      22 | 14.5000000000000000 |  25 |   120
+      23 | 16.5000000000000000 |  27 |   120
+      24 | 18.5000000000000000 |  29 |   120
+(25 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)
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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)
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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)
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+                                                                           QUERY PLAN                                                                           
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: avg(t1.b), avg((t1.b)::bigint)
+   ->  Append
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1.b)), (PARTIAL avg((t1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p1 t1) INNER JOIN (public.fpagg_tab_p1 t2))
+               Remote SQL: SELECT avg_p_int4(r4.b), avg_p_int8(r4.b::bigint) FROM (public.pagg_tab_p1 r4 INNER JOIN public.pagg_tab_p1 r7 ON (((r4.a = r7.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_1.b)), (PARTIAL avg((t1_1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p2 t1_1) INNER JOIN (public.fpagg_tab_p2 t2_1))
+               Remote SQL: SELECT avg_p_int4(r5.b), avg_p_int8(r5.b::bigint) FROM (public.pagg_tab_p2 r5 INNER JOIN public.pagg_tab_p2 r8 ON (((r5.a = r8.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_2.b)), (PARTIAL avg((t1_2.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p3 t1_2) INNER JOIN (public.fpagg_tab_p3 t2_2))
+               Remote SQL: SELECT avg_p_int4(r6.b), avg_p_int8(r6.b::bigint) FROM (public.pagg_tab_p3 r6 INNER JOIN public.pagg_tab_p3 r9 ON (((r6.a = r9.a))))
 (15 rows)
 
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+         avg         |         avg         
+---------------------+---------------------
+ 24.5000000000000000 | 24.5000000000000000
+(1 row)
+
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+-- 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/option.c b/contrib/postgres_fdw/option.c
index 8c822f4ef9..67e15c8bb9 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -126,7 +126,8 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
 			strcmp(def->defname, "async_capable") == 0 ||
 			strcmp(def->defname, "parallel_commit") == 0 ||
 			strcmp(def->defname, "parallel_abort") == 0 ||
-			strcmp(def->defname, "keep_connections") == 0)
+			strcmp(def->defname, "keep_connections") == 0 ||
+			strcmp(def->defname, "check_partial_aggregate_support") == 0)
 		{
 			/* these accept only boolean values */
 			(void) defGetBoolean(def);
@@ -268,6 +269,7 @@ InitPgFdwOptions(void)
 		/* batch_size is available on both server and table */
 		{"batch_size", ForeignServerRelationId, false},
 		{"batch_size", ForeignTableRelationId, false},
+		{"check_partial_aggregate_support", ForeignServerRelationId, false},
 		/* async_capable is available on both server and table */
 		{"async_capable", ForeignServerRelationId, false},
 		{"async_capable", ForeignTableRelationId, false},
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 1393716587..8a905ad8c4 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -36,6 +36,7 @@
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
+#include "optimizer/planner.h"
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
@@ -519,7 +520,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);
+								GroupPathExtraData *extra);
 static List *get_useful_pathkeys_for_relation(PlannerInfo *root,
 											  RelOptInfo *rel);
 static List *get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel);
@@ -650,6 +651,8 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	fpinfo->fdw_tuple_cost = DEFAULT_FDW_TUPLE_COST;
 	fpinfo->shippable_extensions = NIL;
 	fpinfo->fetch_size = 100;
+	fpinfo->check_partial_aggregate_support = false;
+	fpinfo->remoteversion = 0;
 	fpinfo->async_capable = false;
 
 	apply_server_options(fpinfo);
@@ -661,7 +664,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	 * should match what ExecCheckPermissions() does.  If we fail due to lack
 	 * of permissions, the query would have failed at runtime anyway.
 	 */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
 		Oid			userid;
 
@@ -5948,9 +5951,10 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
 	fpinfo->pushdown_safe = true;
 
 	/* Get user mapping */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
-		if (fpinfo_o->use_remote_estimate)
+		if (fpinfo_o->use_remote_estimate ||
+			fpinfo_o->check_partial_aggregate_support)
 			fpinfo->user = fpinfo_o->user;
 		else
 			fpinfo->user = fpinfo_i->user;
@@ -6132,6 +6136,8 @@ apply_server_options(PgFdwRelationInfo *fpinfo)
 				ExtractExtensionList(defGetString(def), false);
 		else if (strcmp(def->defname, "fetch_size") == 0)
 			(void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
+		else if (strcmp(def->defname, "check_partial_aggregate_support") == 0)
+			fpinfo->check_partial_aggregate_support = defGetBoolean(def);
 		else if (strcmp(def->defname, "async_capable") == 0)
 			fpinfo->async_capable = defGetBoolean(def);
 	}
@@ -6191,6 +6197,8 @@ merge_fdw_options(PgFdwRelationInfo *fpinfo,
 	fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
 	fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
 	fpinfo->fetch_size = fpinfo_o->fetch_size;
+	fpinfo->check_partial_aggregate_support = fpinfo_o->check_partial_aggregate_support;
+	fpinfo->remoteversion = fpinfo_o->remoteversion;
 	fpinfo->async_capable = fpinfo_o->async_capable;
 
 	/* Merge the table level options from either side of the join. */
@@ -6373,7 +6381,7 @@ postgresGetForeignJoinPaths(PlannerInfo *root,
  */
 static bool
 foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
-					Node *havingQual)
+					GroupPathExtraData *extra)
 {
 	Query	   *query = root->parse;
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
@@ -6382,11 +6390,16 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	ListCell   *lc;
 	int			i;
 	List	   *tlist = NIL;
+	bool		partial = extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL;
 
 	/* We currently don't support pushing Grouping Sets. */
 	if (query->groupingSets)
 		return false;
 
+	/* It's unsafe to push having statements with partial aggregates */
+	if ((extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL) && extra->havingQual)
+		return false;
+
 	/* Get the fpinfo of the underlying scan relation. */
 	ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
 
@@ -6415,6 +6428,12 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * a node, as long as it's not at top level; then no match is possible.
 	 */
 	i = 0;
+	fpinfo->group_clause = query->groupClause;
+	if (partial)
+	{
+		fpinfo->group_clause = extra->groupClausePartial;
+		grouping_target = extra->partial_target;
+	}
 	foreach(lc, grouping_target->exprs)
 	{
 		Expr	   *expr = (Expr *) lfirst(lc);
@@ -6426,7 +6445,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 		 * check the whole GROUP BY clause not just processed_groupClause,
 		 * because we will ship all of it, cf. appendGroupByClause.
 		 */
-		if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
+		if (sgref && get_sortgroupref_clause_noerr(sgref, fpinfo->group_clause))
 		{
 			TargetEntry *tle;
 
@@ -6512,9 +6531,9 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * Classify the pushable and non-pushable HAVING clauses and save them in
 	 * remote_conds and local_conds of the grouped rel's fpinfo.
 	 */
-	if (havingQual)
+	if (extra->havingQual)
 	{
-		foreach(lc, (List *) havingQual)
+		foreach(lc, (List *) extra->havingQual)
 		{
 			Expr	   *expr = (Expr *) lfirst(lc);
 			RestrictInfo *rinfo;
@@ -6534,7 +6553,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 									  grouped_rel->relids,
 									  NULL,
 									  NULL);
-			if (is_foreign_expr(root, grouped_rel, expr))
+			if (is_foreign_expr(root, grouped_rel, expr) && !partial)
 				fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
 			else
 				fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
@@ -6628,6 +6647,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)
@@ -6644,6 +6664,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;
@@ -6684,7 +6708,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;
@@ -6704,7 +6729,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))
 		return;
 
 	/*
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index 02c1152319..c60f3f5465 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -86,6 +86,15 @@ typedef struct PgFdwRelationInfo
 	ForeignServer *server;
 	UserMapping *user;			/* only set in use_remote_estimate mode */
 
+	/* for partial aggregate pushdown */
+	bool		check_partial_aggregate_support;
+
+	/*
+	 * if remoteversion is zero, then that means the remote server version is
+	 * unacquired.
+	 */
+	int			remoteversion;
+
 	int			fetch_size;		/* fetch size for this remote table */
 
 	/*
@@ -110,6 +119,7 @@ typedef struct PgFdwRelationInfo
 
 	/* Grouping information */
 	List	   *grouped_tlist;
+	List	   *group_clause;
 
 	/* Subquery information */
 	bool		make_outerrel_subquery; /* do we deparse outerrel as a
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index cfb1b57e33..9f4319894e 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -2989,16 +2989,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.1');
+
+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,9 @@ CREATE FOREIGN TABLE fpagg_tab_p2 PARTITION OF pagg_tab FOR VALUES FROM (10) TO
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -3028,10 +3046,289 @@ EXPLAIN (VERBOSE, COSTS OFF)
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are 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;
 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 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;
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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;
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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;
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+
+-- 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 307ad88b50..9202775944 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="partial-aggregate-pushdown"/> 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..75a6670481 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -451,6 +451,37 @@ OPTIONS (ADD password_required 'false');
 
   </sect3>
 
+  <sect3 id="postgres-fdw-options-partial-aggregate-pushdown">
+   <title>Partial Aggregate Pushdown Options</title>
+
+   <para>
+    By default, <filename>postgres_fdw</filename> assumes that for each built-in aggregate
+    function, the partial aggregate function is defined on the remote server. See
+    <xref linkend="partial-aggregate-pushdown"/> for the definition of the partial aggregate
+    function. This may be overridden using the following option:
+   </para>
+
+   <variablelist>
+
+    <varlistentry>
+     <term><literal>check_partial_aggregate_support</literal> (<type>boolean</type>)</term>
+     <listitem>
+      <para>
+       Only if this option is true, during query planning,
+       <filename>postgres_fdw</filename> connects to the remote server
+       and checks if the remote server version is older than the local server version.
+       If so,
+       <filename>postgres_fdw</filename>
+       assumes that for each built-in aggregate function, the partial aggregate function is not defined
+       on the remote server unless the partial aggregate function and the aggregate
+       function match. The default is <literal>false</literal>.
+      </para>
+     </listitem>
+    </varlistentry>
+
+   </variablelist>
+  </sect3>
+
   <sect3 id="postgres-fdw-options-asynchronous-execution">
    <title>Asynchronous Execution Options</title>
 
@@ -929,13 +960,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 +999,68 @@ postgres=# SELECT postgres_fdw_disconnect_all();
   </para>
  </sect2>
 
+ <sect2 id="partial-aggregate-pushdown">
+  <title>Partial aggregate pushdown</title>
+  <para>
+   Partial aggregate pushdown is an optimization for a query that contains aggregate
+   expressions for a partitioned table across one or more remote servers. If the multiple
+   conditions are true for a remote server, an aggregate expression is sent to that
+   remote server and the aggregate is a partial aggregate on the local server.
+   The conditions under which this sending is active are as follows.
+   <itemizedlist spacing="compact">
+     <listitem>
+      <para>
+       An aggregate function, called the partial aggregate function for partial aggregate
+       that corresponding to the aggregate expression, is defined on the local server and
+       the remote server.
+      </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>
+       The aggregate expressions in the query don't contain
+       a <literal>DISTINCT</literal> or <literal>ORDER BY</literal> clause.
+      </para>
+     </listitem>
+    </itemizedlist>
+   When this sending is active for aggregate expressions, any aggregate
+   function is replaced by the partial aggregate function in the remote query.
+   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.
+   By default, <filename>postgres_fdw</filename> assumes that for each built-in
+   aggregate function, the partial aggregate function is defined on the local server
+   and the remote server.
+   If <literal>check_partial_aggregate_support</literal> is true and the remote server version is
+   older than the local server version, <filename>postgres_fdw</filename> does not assume
+   that the partial aggregate function is on the remote server unless
+   the partial aggregate function and the aggregate function match.
+   <filename>postgres_fdw</filename> assumes that a user-defined aggregate
+   function has the partial aggregate function on the remote server
+   only if both belong to an extension that is listed in the foreign server's
+   extensions option.
+  </para>
+ </sect2>
+
  <sect2 id="postgres-fdw-remote-query-execution-environment">
   <title>Remote Query Execution Environment</title>
 
@@ -1031,14 +1126,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 partial aggregate pushdown feature.
+   When using the partial aggregate pushdown feature,
+   the partial aggregate function of the aggregate function is sent to
+   the remote server if the conditions in
+   <xref linkend="partial-aggregate-pushdown"/> are met.
+   A limitation is that by default, <filename>postgres_fdw</filename> assumes that
+   for each built-in aggregate function, the partial aggregate function is defined
+   on the remote server.
+   This limitation might cause <quote>function does not exist</quote>
+   or a similar error if the remote server version is older than the local server version.
+   This problem can be worked around by setting
+   <literal>check_partial_aggregate_support</literal> to <literal>true</literal>
+   or similar query rewriting as above.
   </para>
  </sect2>
 
diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml
index 222e0aa5c9..2875734dea 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>
@@ -246,6 +248,18 @@ CREATE [ OR REPLACE ] AGGREGATE <replaceable class="parameter">name</replaceable
    marked <literal>PARALLEL SAFE</literal> to enable parallel aggregation.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize a query containing aggregate
+   expressions for a partitioned table across one or more remote servers.
+   This optimization sends an aggregate expression to a remote server and the
+   aggregate is a partial aggregate on the local server. This optimization
+   requires an aggregate function called a <firstterm>partial aggregate
+   function</firstterm>. See
+   <xref linkend="partial-aggregate-pushdown"/> for the definition of
+   partial aggregate function. <literal>AGGPARTIALFUNC</literal> is a parameter
+   of the partial aggregate function.
+  </para>
+
   <para>
    Aggregates that behave like <function>MIN</function> or <function>MAX</function> can
    sometimes be optimized by looking into an index instead of scanning every
@@ -653,6 +667,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..607c859974 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,18 @@ SELECT percentile_disc(0.5) WITHIN GROUP (ORDER BY income) FROM households;
    parallel-safety markings on its support functions are not consulted.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize a query containing
+   aggregate expressions for a partitioned table across one or more
+   remote servers. This optimization sends an aggregate expression to
+   a remote server and the aggregate is a partial aggregate on the
+   local server. This optimization requires an aggregate function
+   called the <firstterm>partial aggregate function</firstterm>.
+   See
+   <xref linkend="partial-aggregate-pushdown"/> 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/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 44efb1f4eb..a6c2fa1102 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -206,7 +206,7 @@ static PathTarget *make_group_input_target(PlannerInfo *root,
 										   PathTarget *final_target);
 static PathTarget *make_partial_grouping_target(PlannerInfo *root,
 												PathTarget *grouping_target,
-												Node *havingQual);
+												GroupPathExtraData *extra);
 static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
 static void optimize_window_clauses(PlannerInfo *root,
 									WindowFuncLists *wflists);
@@ -5420,6 +5420,66 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
 	return set_pathtarget_cost_width(root, input_target);
 }
 
+/*
+ * setGroupClausePartial
+ *	  Generate a groupClause for partial aggregate and set it to GroupPathExtraData.
+ */
+static void
+setGroupClausePartial(PathTarget *partial_target, List *non_group_exprs,
+					  List *groupClause, GroupPathExtraData *extra)
+{
+	int			exprno,
+				refno;
+	ListCell   *lc;
+	Index		maxRef = 0;
+	List	   *exprs_processed = NIL;
+	int			exprs_num = 0;
+
+	foreach(lc, groupClause)
+	{
+		SortGroupClause *sgc = (SortGroupClause *) lfirst(lc);
+
+		if (sgc->tleSortGroupRef > maxRef)
+			maxRef = sgc->tleSortGroupRef;
+	}
+	maxRef++;
+
+	extra->groupClausePartial = list_copy_deep(groupClause);
+	extra->partial_target = copy_pathtarget(partial_target);
+
+	if(partial_target->exprs)
+		exprs_num = partial_target->exprs->length;
+
+	foreach(lc, non_group_exprs)
+	{
+		Expr	   *expr = (Expr *) lfirst(lc);
+
+		refno = -1;
+		if (list_member(exprs_processed, expr) ||
+			(!IsA(expr, Var) && !IsA(expr, PlaceHolderVar)))
+			continue;
+		exprs_processed = lappend(exprs_processed, expr);
+		for (exprno = 0; exprno < exprs_num; exprno++)
+		{
+			Expr	   *target_expr = (Expr *) list_nth(partial_target->exprs, exprno);
+
+			if (equal(target_expr, expr))
+			{
+				refno = exprno;
+				break;
+			}
+		}
+		if (refno < 0)
+		{
+			SortGroupClause *grpcl = makeNode(SortGroupClause);
+
+			grpcl->tleSortGroupRef = maxRef++;
+			extra->groupClausePartial = lappend(extra->groupClausePartial, grpcl);
+			add_column_to_pathtarget(extra->partial_target, expr, grpcl->tleSortGroupRef);
+		}
+	}
+}
+
 /*
  * make_partial_grouping_target
  *	  Generate appropriate PathTarget for output of partial aggregate
@@ -5439,7 +5499,7 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
 static PathTarget *
 make_partial_grouping_target(PlannerInfo *root,
 							 PathTarget *grouping_target,
-							 Node *havingQual)
+							 GroupPathExtraData *extra)
 {
 	PathTarget *partial_target;
 	List	   *non_group_cols;
@@ -5481,8 +5541,8 @@ make_partial_grouping_target(PlannerInfo *root,
 	/*
 	 * If there's a HAVING clause, we'll need the Vars/Aggrefs it uses, too.
 	 */
-	if (havingQual)
-		non_group_cols = lappend(non_group_cols, havingQual);
+	if (extra->havingQual)
+		non_group_cols = lappend(non_group_cols, extra->havingQual);
 
 	/*
 	 * Pull out all the Vars, PlaceHolderVars, and Aggrefs mentioned in
@@ -5495,7 +5555,7 @@ make_partial_grouping_target(PlannerInfo *root,
 									  PVC_INCLUDE_AGGREGATES |
 									  PVC_RECURSE_WINDOWFUNCS |
 									  PVC_INCLUDE_PLACEHOLDERS);
-
+	setGroupClausePartial(partial_target, non_group_exprs, root->parse->groupClause, extra);
 	add_new_columns_to_pathtarget(partial_target, non_group_exprs);
 
 	/*
@@ -5524,6 +5584,7 @@ make_partial_grouping_target(PlannerInfo *root,
 			lfirst(lc) = newaggref;
 		}
 	}
+	extra->partial_target->exprs = partial_target->exprs;
 
 	/* clean up cruft */
 	list_free(non_group_exprs);
@@ -7153,7 +7214,7 @@ create_partial_grouping_paths(PlannerInfo *root,
 	 */
 	partially_grouped_rel->reltarget =
 		make_partial_grouping_target(root, grouped_rel->reltarget,
-									 extra->havingQual);
+									 extra);
 
 	if (!extra->partial_costs_set)
 	{
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index c73e9a11da..356325fc71 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -13716,6 +13716,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;
@@ -13798,11 +13799,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 "
@@ -13828,6 +13829,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"));
@@ -13917,6 +13919,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 9805bc6118..f7b2f19845 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',
@@ -5001,6 +5008,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',
@@ -5013,6 +5024,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',
@@ -6597,36 +6612,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' },
@@ -6649,6 +6689,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' },
@@ -6807,153 +6850,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',
@@ -6964,52 +7115,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/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 5702fbba60..fa2638749c 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -3257,6 +3257,8 @@ typedef struct
 	Node	   *havingQual;
 	List	   *targetList;
 	PartitionwiseAggregateType patype;
+	List	   *groupClausePartial;
+	PathTarget *partial_target;
 } GroupPathExtraData;
 
 /*
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] 38+ messages in thread

* Re: Partial aggregates pushdown
@ 2023-09-25 22:30  Bruce Momjian <[email protected]>
  parent: [email protected] <[email protected]>
  1 sibling, 1 reply; 38+ messages in thread

From: Bruce Momjian @ 2023-09-25 22:30 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: Alexander Pyhalov <[email protected]>; Finnerty, Jim <[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 Mon, Sep 25, 2023 at 03:18:13AM +0000, [email protected] wrote:
> Hi Mr.Bruce, Mr.Pyhalov, Mr.Finnerty, hackers.
> 
> Thank you for your valuable comments. I sincerely apologize for the very late reply.
> Here is a response to your comments or a fix to the patch.
> 
> Tuesday, August 8, 2023 at 3:31 Bruce Momjian
> > > I have modified the program except for the point "if the version of the remote server is less than PG17".
> > > Instead, we have addressed the following.
> > > "If check_partial_aggregate_support is true and the remote server version is older than the local server
> > > version, postgres_fdw does not assume that the partial aggregate function is on the remote server unless
> > > the partial aggregate function and the aggregate function match."
> > > The reason for this is to maintain compatibility with any aggregate function that does not support partial
> > > aggregate in one version of V1 (V1 is PG17 or higher), even if the next version supports partial aggregate.
> > > For example, string_agg does not support partial aggregation in PG15, but it will support partial aggregation
> > > in PG16.
> >
> > Just to clarify, I think you are saying:
> >
> >         If check_partial_aggregate_support is true and the remote server
> >         version is older than the local server version, postgres_fdw
> >         checks if the partial aggregate function exists on the remote
> >         server during planning and only uses it if it does.
> >
> > I tried to phrase it in a positive way, and mentioned the plan time
> > distinction.  Also, I am sorry I was away for most of July and am just
> > getting to this.
> Thanks for your comment. In the documentation, the description of check_partial_aggregate_support is as follows
> (please see postgres-fdw.sgml).
> --
> check_partial_aggregate_support (boolean)
> Only if this option is true, during query planning, postgres_fdw connects to the remote server and check if the remote server version is older than the local server version. If so, postgres_fdw assumes that for each built-in aggregate function, the partial aggregate function is not defined on the remote server unless the partial aggregate function and the aggregate function match. The default is false.
> --

My point is that there are three behaviors:

*  false - no check
*  true, remote version >= sender - no check
*  true, remove version < sender - check

Here is your code:

	+ * Check that a buit-in aggpartialfunc exists on the remote server. If
	+ * check_partial_aggregate_support is false, we assume the partial aggregate
	+ * function exsits on the remote server. Otherwise we assume the partial
	+ * aggregate function exsits on the remote server only if the remote server
	+ * version is not less than the local server version.
	+ */
	+static bool
	+is_builtin_aggpartialfunc_shippable(Oid aggpartialfn, PgFdwRelationInfo *fpinfo)
	+{
	+       bool            shippable = true;
	+
	+       if (fpinfo->check_partial_aggregate_support)
	+       {
	+               if (fpinfo->remoteversion == 0)
	+               {
	+                       PGconn     *conn = GetConnection(fpinfo->user, false, NULL);
	+
	+                       fpinfo->remoteversion = PQserverVersion(conn);
	+               }
	+               if (fpinfo->remoteversion < PG_VERSION_NUM)
	+                       shippable = false;
	+       }
	+       return shippable;
	+}

I think this needs to be explained in the docs.  I am ready to adjust
the patch to improve the wording whenever you are ready.  Should I do it
now and post an updated version for you to use?

-- 
  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] 38+ messages in thread

* RE: Partial aggregates pushdown
@ 2023-09-26 06:26  [email protected] <[email protected]>
  parent: Bruce Momjian <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: [email protected] @ 2023-09-26 06:26 UTC (permalink / raw)
  To: Bruce Momjian <[email protected]>; +Cc: Alexander Pyhalov <[email protected]>; Finnerty, Jim <[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.Bruce.

Tuesday, September 26, 2023 7:31 Bruce Momjian
> On Mon, Sep 25, 2023 at 03:18:13AM +0000, [email protected] wrote:
> > Hi Mr.Bruce, Mr.Pyhalov, Mr.Finnerty, hackers.
> >
> > Thank you for your valuable comments. I sincerely apologize for the very late reply.
> > Here is a response to your comments or a fix to the patch.
> >
> > Tuesday, August 8, 2023 at 3:31 Bruce Momjian
> > > > I have modified the program except for the point "if the version of the remote server is less than PG17".
> > > > Instead, we have addressed the following.
> > > > "If check_partial_aggregate_support is true and the remote server
> > > > version is older than the local server version, postgres_fdw does
> > > > not assume that the partial aggregate function is on the remote server unless the partial aggregate function and the
> aggregate function match."
> > > > The reason for this is to maintain compatibility with any
> > > > aggregate function that does not support partial aggregate in one version of V1 (V1 is PG17 or higher), even if the
> next version supports partial aggregate.
> > > > For example, string_agg does not support partial aggregation in
> > > > PG15, but it will support partial aggregation in PG16.
> > >
> > > Just to clarify, I think you are saying:
> > >
> > >         If check_partial_aggregate_support is true and the remote server
> > >         version is older than the local server version, postgres_fdw
> > >         checks if the partial aggregate function exists on the remote
> > >         server during planning and only uses it if it does.
> > >
> > > I tried to phrase it in a positive way, and mentioned the plan time
> > > distinction.  Also, I am sorry I was away for most of July and am
> > > just getting to this.
> > Thanks for your comment. In the documentation, the description of
> > check_partial_aggregate_support is as follows (please see postgres-fdw.sgml).
> > --
> > check_partial_aggregate_support (boolean) Only if this option is true,
> > during query planning, postgres_fdw connects to the remote server and check if the remote server version is older than
> the local server version. If so, postgres_fdw assumes that for each built-in aggregate function, the partial aggregate
> function is not defined on the remote server unless the partial aggregate function and the aggregate function match. The
> default is false.
> > --
> 
> My point is that there are three behaviors:
> 
> *  false - no check
> *  true, remote version >= sender - no check
> *  true, remove version < sender - check
> 
> Here is your code:
> 
> 	+ * Check that a buit-in aggpartialfunc exists on the remote server. If
> 	+ * check_partial_aggregate_support is false, we assume the partial aggregate
> 	+ * function exsits on the remote server. Otherwise we assume the partial
> 	+ * aggregate function exsits on the remote server only if the remote server
> 	+ * version is not less than the local server version.
> 	+ */
> 	+static bool
> 	+is_builtin_aggpartialfunc_shippable(Oid aggpartialfn, PgFdwRelationInfo *fpinfo)
> 	+{
> 	+       bool            shippable = true;
> 	+
> 	+       if (fpinfo->check_partial_aggregate_support)
> 	+       {
> 	+               if (fpinfo->remoteversion == 0)
> 	+               {
> 	+                       PGconn     *conn = GetConnection(fpinfo->user, false, NULL);
> 	+
> 	+                       fpinfo->remoteversion = PQserverVersion(conn);
> 	+               }
> 	+               if (fpinfo->remoteversion < PG_VERSION_NUM)
> 	+                       shippable = false;
> 	+       }
> 	+       return shippable;
> 	+}
> 
> I think this needs to be explained in the docs.  I am ready to adjust the patch to improve the wording whenever you are
> ready.  Should I do it now and post an updated version for you to use?
The following explanation was omitted from the documentation, so I added it.
> *  false - no check
> *  true, remove version < sender - check
I have responded to your comment, but if there is a problem with the wording, could you please suggest a correction?

Sincerely yours,
Yuuki Fujii

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

> -----Original Message-----
> From: Bruce Momjian <[email protected]>
> Sent: Tuesday, September 26, 2023 7:31 AM
> To: Fujii Yuki/藤井 雄規(MELCO/情報総研 DM最適G) <[email protected]>
> Cc: Alexander Pyhalov <[email protected]>; Finnerty, Jim <[email protected]>; PostgreSQL-development
> <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Tomas
> Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson
> <[email protected]>; Ilya Gladyshev <[email protected]>
> Subject: Re: Partial aggregates pushdown
> 
> On Mon, Sep 25, 2023 at 03:18:13AM +0000, [email protected] wrote:
> > Hi Mr.Bruce, Mr.Pyhalov, Mr.Finnerty, hackers.
> >
> > Thank you for your valuable comments. I sincerely apologize for the very late reply.
> > Here is a response to your comments or a fix to the patch.
> >
> > Tuesday, August 8, 2023 at 3:31 Bruce Momjian
> > > > I have modified the program except for the point "if the version of the remote server is less than PG17".
> > > > Instead, we have addressed the following.
> > > > "If check_partial_aggregate_support is true and the remote server
> > > > version is older than the local server version, postgres_fdw does
> > > > not assume that the partial aggregate function is on the remote server unless the partial aggregate function and the
> aggregate function match."
> > > > The reason for this is to maintain compatibility with any
> > > > aggregate function that does not support partial aggregate in one version of V1 (V1 is PG17 or higher), even if the
> next version supports partial aggregate.
> > > > For example, string_agg does not support partial aggregation in
> > > > PG15, but it will support partial aggregation in PG16.
> > >
> > > Just to clarify, I think you are saying:
> > >
> > >         If check_partial_aggregate_support is true and the remote server
> > >         version is older than the local server version, postgres_fdw
> > >         checks if the partial aggregate function exists on the remote
> > >         server during planning and only uses it if it does.
> > >
> > > I tried to phrase it in a positive way, and mentioned the plan time
> > > distinction.  Also, I am sorry I was away for most of July and am
> > > just getting to this.
> > Thanks for your comment. In the documentation, the description of
> > check_partial_aggregate_support is as follows (please see postgres-fdw.sgml).
> > --
> > check_partial_aggregate_support (boolean) Only if this option is true,
> > during query planning, postgres_fdw connects to the remote server and check if the remote server version is older than
> the local server version. If so, postgres_fdw assumes that for each built-in aggregate function, the partial aggregate
> function is not defined on the remote server unless the partial aggregate function and the aggregate function match. The
> default is false.
> > --
> 
> My point is that there are three behaviors:
> 
> *  false - no check
> *  true, remote version >= sender - no check
> *  true, remove version < sender - check
> 
> Here is your code:
> 
> 	+ * Check that a buit-in aggpartialfunc exists on the remote server. If
> 	+ * check_partial_aggregate_support is false, we assume the partial aggregate
> 	+ * function exsits on the remote server. Otherwise we assume the partial
> 	+ * aggregate function exsits on the remote server only if the remote server
> 	+ * version is not less than the local server version.
> 	+ */
> 	+static bool
> 	+is_builtin_aggpartialfunc_shippable(Oid aggpartialfn, PgFdwRelationInfo *fpinfo)
> 	+{
> 	+       bool            shippable = true;
> 	+
> 	+       if (fpinfo->check_partial_aggregate_support)
> 	+       {
> 	+               if (fpinfo->remoteversion == 0)
> 	+               {
> 	+                       PGconn     *conn = GetConnection(fpinfo->user, false, NULL);
> 	+
> 	+                       fpinfo->remoteversion = PQserverVersion(conn);
> 	+               }
> 	+               if (fpinfo->remoteversion < PG_VERSION_NUM)
> 	+                       shippable = false;
> 	+       }
> 	+       return shippable;
> 	+}
> 
> I think this needs to be explained in the docs.  I am ready to adjust the patch to improve the wording whenever you are
> ready.  Should I do it now and post an updated version for you to use?
> 
> --
>   Bruce Momjian  <[email protected]>        https://momjian.us
>   EDB                                      https://enterprisedb.com
> 
>   Only you can decide what is important to you.


Attachments:

  [application/octet-stream] 0001-Partial-aggregates-push-down-v26.patch (264.6K, ../../OS3PR01MB6660CF2F3643AE7B7C8F797495C3A@OS3PR01MB6660.jpnprd01.prod.outlook.com/2-0001-Partial-aggregates-push-down-v26.patch)
  download | inline diff:
From a2632ba69f7cfc7c5a81bd7d0490dbad015e38e2 Mon Sep 17 00:00:00 2001
From: Yuki Fujii <[email protected]>
Date: Tue, 26 Sep 2023 11:10:40 -0400
Subject: [PATCH] Partial aggregates push down v26

---
 contrib/postgres_fdw/deparse.c                | 103 ++-
 .../postgres_fdw/expected/postgres_fdw.out    | 868 +++++++++++++++++-
 contrib/postgres_fdw/option.c                 |   4 +-
 contrib/postgres_fdw/postgres_fdw.c           |  47 +-
 contrib/postgres_fdw/postgres_fdw.h           |  10 +
 contrib/postgres_fdw/sql/postgres_fdw.sql     | 311 ++++++-
 doc/src/sgml/catalogs.sgml                    |  13 +
 doc/src/sgml/postgres-fdw.sgml                | 137 ++-
 doc/src/sgml/ref/create_aggregate.sgml        |  42 +
 doc/src/sgml/xaggr.sgml                       |  15 +-
 src/backend/catalog/pg_aggregate.c            | 101 ++
 src/backend/commands/aggregatecmds.c          |   4 +
 src/backend/optimizer/plan/planner.c          |  73 +-
 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 ++++
 src/include/nodes/pathnodes.h                 |   2 +
 .../regress/expected/create_aggregate.out     | 184 ++++
 src/test/regress/expected/oidjoins.out        |   1 +
 src/test/regress/sql/create_aggregate.sql     | 183 ++++
 21 files changed, 2786 insertions(+), 201 deletions(-)

diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 09d6dd60dd..e957611879 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 */
@@ -3723,6 +3744,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	Query	   *query = context->root->parse;
 	ListCell   *lc;
 	bool		first = true;
+	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) context->foreignrel->fdw_private;
 
 	/* Nothing to be done, if there's no GROUP BY clause in the query. */
 	if (!query->groupClause)
@@ -3743,7 +3765,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	 * to empty, and in any case the redundancy situation on the remote might
 	 * be different than what we think here.
 	 */
-	foreach(lc, query->groupClause)
+	foreach(lc, fpinfo->group_clause)
 	{
 		SortGroupClause *grp = (SortGroupClause *) lfirst(lc);
 
@@ -4047,3 +4069,68 @@ get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
 	/* Shouldn't get here */
 	elog(ERROR, "unexpected expression in subquery output");
 }
+
+/*
+ * Check that a buit-in aggpartialfunc exists on the remote server. If
+ * check_partial_aggregate_support is false, we assume the partial aggregate
+ * function exsits on the remote server. Otherwise we assume the partial
+ * aggregate function exsits on the remote server only if the remote server
+ * version is not less than the local server version.
+ */
+static bool
+is_builtin_aggpartialfunc_shippable(Oid aggpartialfn, PgFdwRelationInfo *fpinfo)
+{
+	bool		shippable = true;
+
+	if (fpinfo->check_partial_aggregate_support)
+	{
+		if (fpinfo->remoteversion == 0)
+		{
+			PGconn	   *conn = GetConnection(fpinfo->user, false, NULL);
+
+			fpinfo->remoteversion = PQserverVersion(conn);
+		}
+		if (fpinfo->remoteversion < PG_VERSION_NUM)
+			shippable = false;
+	}
+	return shippable;
+}
+
+/*
+ * 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 exists on the remote server
+ */
+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_builtin(aggform->aggpartialfn))
+		partial_agg_ok = is_builtin_aggpartialfunc_shippable(
+															 aggform->aggpartialfn, fpinfo);
+	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 b1b22e3a28..dfcf348e2a 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -9768,18 +9768,35 @@ 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.1');
+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');
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -9833,8 +9850,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
@@ -9845,21 +9862,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;
@@ -9873,28 +9890,837 @@ SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
  21 |   100
 (6 rows)
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are 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                            
------------------------------------------------------------------
- Sort
-   Sort Key: pagg_tab.b
-   ->  Finalize HashAggregate
-         Group Key: pagg_tab.b
-         Filter: (sum(pagg_tab.a) < 700)
+                                                                 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 fpagg_tab_p1 pagg_tab
+                     ->  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 fpagg_tab_p2 pagg_tab_1
+                     ->  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 fpagg_tab_p3 pagg_tab_2
+                     ->  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)
+
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
+ b  |         avg         | max | count 
+----+---------------------+-----+-------
+  0 | 10.0000000000000000 |  20 |    60
+  1 | 11.0000000000000000 |  21 |    60
+ 10 | 10.0000000000000000 |  20 |    60
+ 11 | 11.0000000000000000 |  21 |    60
+ 20 | 10.0000000000000000 |  20 |    60
+ 21 | 11.0000000000000000 |  21 |    60
+ 30 | 10.0000000000000000 |  20 |    60
+ 31 | 11.0000000000000000 |  21 |    60
+ 40 | 10.0000000000000000 |  20 |    60
+ 41 | 11.0000000000000000 |  21 |    60
+(10 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+                                                                  QUERY PLAN                                                                  
+----------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: ((((pagg_tab.b / 2)))::numeric), (avg(pagg_tab.a)), (max(pagg_tab.a)), (count(*)), ((pagg_tab.b / 2))
+   Sort Key: ((((pagg_tab.b / 2)))::numeric)
+   ->  Finalize HashAggregate
+         Output: (((pagg_tab.b / 2)))::numeric, avg(pagg_tab.a), max(pagg_tab.a), count(*), ((pagg_tab.b / 2))
+         Group Key: ((pagg_tab.b / 2))
+         ->  Append
+               ->  Foreign Scan
+                     Output: ((pagg_tab.b / 2)), 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 / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p1 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_1.b / 2)), 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 / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p2 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_2.b / 2)), 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 / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p3 GROUP BY 1, 2
+(19 rows)
+
+SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+ numeric |         avg         | max | count 
+---------+---------------------+-----+-------
+       0 | 10.5000000000000000 |  21 |   120
+       1 | 12.5000000000000000 |  23 |   120
+       2 | 14.5000000000000000 |  25 |   120
+       3 | 16.5000000000000000 |  27 |   120
+       4 | 18.5000000000000000 |  29 |   120
+       5 | 10.5000000000000000 |  21 |   120
+       6 | 12.5000000000000000 |  23 |   120
+       7 | 14.5000000000000000 |  25 |   120
+       8 | 16.5000000000000000 |  27 |   120
+       9 | 18.5000000000000000 |  29 |   120
+      10 | 10.5000000000000000 |  21 |   120
+      11 | 12.5000000000000000 |  23 |   120
+      12 | 14.5000000000000000 |  25 |   120
+      13 | 16.5000000000000000 |  27 |   120
+      14 | 18.5000000000000000 |  29 |   120
+      15 | 10.5000000000000000 |  21 |   120
+      16 | 12.5000000000000000 |  23 |   120
+      17 | 14.5000000000000000 |  25 |   120
+      18 | 16.5000000000000000 |  27 |   120
+      19 | 18.5000000000000000 |  29 |   120
+      20 | 10.5000000000000000 |  21 |   120
+      21 | 12.5000000000000000 |  23 |   120
+      22 | 14.5000000000000000 |  25 |   120
+      23 | 16.5000000000000000 |  27 |   120
+      24 | 18.5000000000000000 |  29 |   120
+(25 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)
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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)
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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)
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+                                                                           QUERY PLAN                                                                           
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: avg(t1.b), avg((t1.b)::bigint)
+   ->  Append
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1.b)), (PARTIAL avg((t1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p1 t1) INNER JOIN (public.fpagg_tab_p1 t2))
+               Remote SQL: SELECT avg_p_int4(r4.b), avg_p_int8(r4.b::bigint) FROM (public.pagg_tab_p1 r4 INNER JOIN public.pagg_tab_p1 r7 ON (((r4.a = r7.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_1.b)), (PARTIAL avg((t1_1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p2 t1_1) INNER JOIN (public.fpagg_tab_p2 t2_1))
+               Remote SQL: SELECT avg_p_int4(r5.b), avg_p_int8(r5.b::bigint) FROM (public.pagg_tab_p2 r5 INNER JOIN public.pagg_tab_p2 r8 ON (((r5.a = r8.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_2.b)), (PARTIAL avg((t1_2.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p3 t1_2) INNER JOIN (public.fpagg_tab_p3 t2_2))
+               Remote SQL: SELECT avg_p_int4(r6.b), avg_p_int8(r6.b::bigint) FROM (public.pagg_tab_p3 r6 INNER JOIN public.pagg_tab_p3 r9 ON (((r6.a = r9.a))))
 (15 rows)
 
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+         avg         |         avg         
+---------------------+---------------------
+ 24.5000000000000000 | 24.5000000000000000
+(1 row)
+
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+-- 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/option.c b/contrib/postgres_fdw/option.c
index 8c822f4ef9..67e15c8bb9 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -126,7 +126,8 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
 			strcmp(def->defname, "async_capable") == 0 ||
 			strcmp(def->defname, "parallel_commit") == 0 ||
 			strcmp(def->defname, "parallel_abort") == 0 ||
-			strcmp(def->defname, "keep_connections") == 0)
+			strcmp(def->defname, "keep_connections") == 0 ||
+			strcmp(def->defname, "check_partial_aggregate_support") == 0)
 		{
 			/* these accept only boolean values */
 			(void) defGetBoolean(def);
@@ -268,6 +269,7 @@ InitPgFdwOptions(void)
 		/* batch_size is available on both server and table */
 		{"batch_size", ForeignServerRelationId, false},
 		{"batch_size", ForeignTableRelationId, false},
+		{"check_partial_aggregate_support", ForeignServerRelationId, false},
 		/* async_capable is available on both server and table */
 		{"async_capable", ForeignServerRelationId, false},
 		{"async_capable", ForeignTableRelationId, false},
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c5cada55fb..8dc6ec12a3 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -36,6 +36,7 @@
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
+#include "optimizer/planner.h"
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
@@ -519,7 +520,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);
+								GroupPathExtraData *extra);
 static List *get_useful_pathkeys_for_relation(PlannerInfo *root,
 											  RelOptInfo *rel);
 static List *get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel);
@@ -650,6 +651,8 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	fpinfo->fdw_tuple_cost = DEFAULT_FDW_TUPLE_COST;
 	fpinfo->shippable_extensions = NIL;
 	fpinfo->fetch_size = 100;
+	fpinfo->check_partial_aggregate_support = false;
+	fpinfo->remoteversion = 0;
 	fpinfo->async_capable = false;
 
 	apply_server_options(fpinfo);
@@ -661,7 +664,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	 * should match what ExecCheckPermissions() does.  If we fail due to lack
 	 * of permissions, the query would have failed at runtime anyway.
 	 */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
 		Oid			userid;
 
@@ -5946,9 +5949,10 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
 	fpinfo->pushdown_safe = true;
 
 	/* Get user mapping */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
-		if (fpinfo_o->use_remote_estimate)
+		if (fpinfo_o->use_remote_estimate ||
+			fpinfo_o->check_partial_aggregate_support)
 			fpinfo->user = fpinfo_o->user;
 		else
 			fpinfo->user = fpinfo_i->user;
@@ -6127,6 +6131,8 @@ apply_server_options(PgFdwRelationInfo *fpinfo)
 				ExtractExtensionList(defGetString(def), false);
 		else if (strcmp(def->defname, "fetch_size") == 0)
 			(void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
+		else if (strcmp(def->defname, "check_partial_aggregate_support") == 0)
+			fpinfo->check_partial_aggregate_support = defGetBoolean(def);
 		else if (strcmp(def->defname, "async_capable") == 0)
 			fpinfo->async_capable = defGetBoolean(def);
 	}
@@ -6186,6 +6192,8 @@ merge_fdw_options(PgFdwRelationInfo *fpinfo,
 	fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
 	fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
 	fpinfo->fetch_size = fpinfo_o->fetch_size;
+	fpinfo->check_partial_aggregate_support = fpinfo_o->check_partial_aggregate_support;
+	fpinfo->remoteversion = fpinfo_o->remoteversion;
 	fpinfo->async_capable = fpinfo_o->async_capable;
 
 	/* Merge the table level options from either side of the join. */
@@ -6366,7 +6374,7 @@ postgresGetForeignJoinPaths(PlannerInfo *root,
  */
 static bool
 foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
-					Node *havingQual)
+					GroupPathExtraData *extra)
 {
 	Query	   *query = root->parse;
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
@@ -6375,11 +6383,16 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	ListCell   *lc;
 	int			i;
 	List	   *tlist = NIL;
+	bool		partial = extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL;
 
 	/* We currently don't support pushing Grouping Sets. */
 	if (query->groupingSets)
 		return false;
 
+	/* It's unsafe to push having statements with partial aggregates */
+	if ((extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL) && extra->havingQual)
+		return false;
+
 	/* Get the fpinfo of the underlying scan relation. */
 	ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
 
@@ -6408,6 +6421,12 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * a node, as long as it's not at top level; then no match is possible.
 	 */
 	i = 0;
+	fpinfo->group_clause = query->groupClause;
+	if (partial)
+	{
+		fpinfo->group_clause = extra->groupClausePartial;
+		grouping_target = extra->partial_target;
+	}
 	foreach(lc, grouping_target->exprs)
 	{
 		Expr	   *expr = (Expr *) lfirst(lc);
@@ -6419,7 +6438,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 		 * check the whole GROUP BY clause not just processed_groupClause,
 		 * because we will ship all of it, cf. appendGroupByClause.
 		 */
-		if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
+		if (sgref && get_sortgroupref_clause_noerr(sgref, fpinfo->group_clause))
 		{
 			TargetEntry *tle;
 
@@ -6505,9 +6524,9 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * Classify the pushable and non-pushable HAVING clauses and save them in
 	 * remote_conds and local_conds of the grouped rel's fpinfo.
 	 */
-	if (havingQual)
+	if (extra->havingQual)
 	{
-		foreach(lc, (List *) havingQual)
+		foreach(lc, (List *) extra->havingQual)
 		{
 			Expr	   *expr = (Expr *) lfirst(lc);
 			RestrictInfo *rinfo;
@@ -6527,7 +6546,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 									  grouped_rel->relids,
 									  NULL,
 									  NULL);
-			if (is_foreign_expr(root, grouped_rel, expr))
+			if (is_foreign_expr(root, grouped_rel, expr) && !partial)
 				fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
 			else
 				fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
@@ -6621,6 +6640,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 +6657,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 +6701,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 +6722,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))
 		return;
 
 	/*
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index 02c1152319..c60f3f5465 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -86,6 +86,15 @@ typedef struct PgFdwRelationInfo
 	ForeignServer *server;
 	UserMapping *user;			/* only set in use_remote_estimate mode */
 
+	/* for partial aggregate pushdown */
+	bool		check_partial_aggregate_support;
+
+	/*
+	 * if remoteversion is zero, then that means the remote server version is
+	 * unacquired.
+	 */
+	int			remoteversion;
+
 	int			fetch_size;		/* fetch size for this remote table */
 
 	/*
@@ -110,6 +119,7 @@ typedef struct PgFdwRelationInfo
 
 	/* Grouping information */
 	List	   *grouped_tlist;
+	List	   *group_clause;
 
 	/* Subquery information */
 	bool		make_outerrel_subquery; /* do we deparse outerrel as a
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 848229bf31..980af18651 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -2989,16 +2989,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.1');
+
+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,9 @@ CREATE FOREIGN TABLE fpagg_tab_p2 PARTITION OF pagg_tab FOR VALUES FROM (10) TO
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -3028,10 +3046,289 @@ EXPLAIN (VERBOSE, COSTS OFF)
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are 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;
 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 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;
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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;
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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;
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+
+-- 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 37c6a2bb73..444731ac06 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="partial-aggregate-pushdown"/> 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..3a88cac3c1 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -451,6 +451,44 @@ OPTIONS (ADD password_required 'false');
 
   </sect3>
 
+  <sect3 id="postgres-fdw-options-partial-aggregate-pushdown">
+   <title>Partial Aggregate Pushdown Options</title>
+
+   <para>
+    By default, <filename>postgres_fdw</filename> assumes that for each built-in aggregate
+    function, the partial aggregate function is defined on the remote server. See
+    <xref linkend="partial-aggregate-pushdown"/> for the definition of the partial aggregate
+    function. This may be overridden using the following option:
+   </para>
+
+   <variablelist>
+
+    <varlistentry>
+     <term><literal>check_partial_aggregate_support</literal> (<type>boolean</type>)</term>
+     <listitem>
+      <para>
+       If this option is false, <filename>postgres_fdw</filename> assumes
+       that for each built-in aggregate function,
+       the partial aggregate function is defined on the remote server
+       without checking the remote server version.
+       If this option is true, during query planning,
+       <filename>postgres_fdw</filename> connects to the remote server
+       and checks if the remote server version is older than the local server version.
+       If so,
+       <filename>postgres_fdw</filename>
+       assumes that for each built-in aggregate function, the partial aggregate function is not defined
+       on the remote server unless the partial aggregate function and the aggregate
+       function match.
+       Otherwise <filename>postgres_fdw</filename> assumes that for each built-in aggregate function,
+       the partial aggregate function is defined on the remote server.
+       The default is <literal>false</literal>.
+      </para>
+     </listitem>
+    </varlistentry>
+
+   </variablelist>
+  </sect3>
+
   <sect3 id="postgres-fdw-options-asynchronous-execution">
    <title>Asynchronous Execution Options</title>
 
@@ -929,13 +967,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 +1006,68 @@ postgres=# SELECT postgres_fdw_disconnect_all();
   </para>
  </sect2>
 
+ <sect2 id="partial-aggregate-pushdown">
+  <title>Partial aggregate pushdown</title>
+  <para>
+   Partial aggregate pushdown is an optimization for a query that contains aggregate
+   expressions for a partitioned table across one or more remote servers. If the multiple
+   conditions are true for a remote server, an aggregate expression is sent to that
+   remote server and the aggregate is a partial aggregate on the local server.
+   The conditions under which this sending is active are as follows.
+   <itemizedlist spacing="compact">
+     <listitem>
+      <para>
+       An aggregate function, called the partial aggregate function for partial aggregate
+       that corresponding to the aggregate expression, is defined on the local server and
+       the remote server.
+      </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>
+       The aggregate expressions in the query don't contain
+       a <literal>DISTINCT</literal> or <literal>ORDER BY</literal> clause.
+      </para>
+     </listitem>
+    </itemizedlist>
+   When this sending is active for aggregate expressions, any aggregate
+   function is replaced by the partial aggregate function in the remote query.
+   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.
+   By default, <filename>postgres_fdw</filename> assumes that for each built-in
+   aggregate function, the partial aggregate function is defined on the local server
+   and the remote server.
+   If <literal>check_partial_aggregate_support</literal> is true and the remote server version is
+   older than the local server version, <filename>postgres_fdw</filename> does not assume
+   that the partial aggregate function is on the remote server unless
+   the partial aggregate function and the aggregate function match.
+   <filename>postgres_fdw</filename> assumes that a user-defined aggregate
+   function has the partial aggregate function on the remote server
+   only if both belong to an extension that is listed in the foreign server's
+   extensions option.
+  </para>
+ </sect2>
+
  <sect2 id="postgres-fdw-remote-query-execution-environment">
   <title>Remote Query Execution Environment</title>
 
@@ -1031,14 +1133,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 partial aggregate pushdown feature.
+   When using the partial aggregate pushdown feature,
+   the partial aggregate function of the aggregate function is sent to
+   the remote server if the conditions in
+   <xref linkend="partial-aggregate-pushdown"/> are met.
+   A limitation is that by default, <filename>postgres_fdw</filename> assumes that
+   for each built-in aggregate function, the partial aggregate function is defined
+   on the remote server.
+   This limitation might cause <quote>function does not exist</quote>
+   or a similar error if the remote server version is older than the local server version.
+   This problem can be worked around by setting
+   <literal>check_partial_aggregate_support</literal> to <literal>true</literal>
+   or similar query rewriting as above.
   </para>
  </sect2>
 
diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml
index 222e0aa5c9..2875734dea 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>
@@ -246,6 +248,18 @@ CREATE [ OR REPLACE ] AGGREGATE <replaceable class="parameter">name</replaceable
    marked <literal>PARALLEL SAFE</literal> to enable parallel aggregation.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize a query containing aggregate
+   expressions for a partitioned table across one or more remote servers.
+   This optimization sends an aggregate expression to a remote server and the
+   aggregate is a partial aggregate on the local server. This optimization
+   requires an aggregate function called a <firstterm>partial aggregate
+   function</firstterm>. See
+   <xref linkend="partial-aggregate-pushdown"/> for the definition of
+   partial aggregate function. <literal>AGGPARTIALFUNC</literal> is a parameter
+   of the partial aggregate function.
+  </para>
+
   <para>
    Aggregates that behave like <function>MIN</function> or <function>MAX</function> can
    sometimes be optimized by looking into an index instead of scanning every
@@ -653,6 +667,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..607c859974 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,18 @@ SELECT percentile_disc(0.5) WITHIN GROUP (ORDER BY income) FROM households;
    parallel-safety markings on its support functions are not consulted.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize a query containing
+   aggregate expressions for a partitioned table across one or more
+   remote servers. This optimization sends an aggregate expression to
+   a remote server and the aggregate is a partial aggregate on the
+   local server. This optimization requires an aggregate function
+   called the <firstterm>partial aggregate function</firstterm>.
+   See
+   <xref linkend="partial-aggregate-pushdown"/> 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/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 7d679996e3..9f846ebfb3 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -206,7 +206,7 @@ static PathTarget *make_group_input_target(PlannerInfo *root,
 										   PathTarget *final_target);
 static PathTarget *make_partial_grouping_target(PlannerInfo *root,
 												PathTarget *grouping_target,
-												Node *havingQual);
+												GroupPathExtraData *extra);
 static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
 static void optimize_window_clauses(PlannerInfo *root,
 									WindowFuncLists *wflists);
@@ -5400,6 +5400,66 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
 	return set_pathtarget_cost_width(root, input_target);
 }
 
+/*
+ * setGroupClausePartial
+ *	  Generate a groupClause for partial aggregate and set it to GroupPathExtraData.
+ */
+static void
+setGroupClausePartial(PathTarget *partial_target, List *non_group_exprs,
+					  List *groupClause, GroupPathExtraData *extra)
+{
+	int			exprno,
+				refno;
+	ListCell   *lc;
+	Index		maxRef = 0;
+	List	   *exprs_processed = NIL;
+	int			exprs_num = 0;
+
+	foreach(lc, groupClause)
+	{
+		SortGroupClause *sgc = (SortGroupClause *) lfirst(lc);
+
+		if (sgc->tleSortGroupRef > maxRef)
+			maxRef = sgc->tleSortGroupRef;
+	}
+	maxRef++;
+
+	extra->groupClausePartial = list_copy_deep(groupClause);
+	extra->partial_target = copy_pathtarget(partial_target);
+
+	if(partial_target->exprs)
+		exprs_num = partial_target->exprs->length;
+
+	foreach(lc, non_group_exprs)
+	{
+		Expr	   *expr = (Expr *) lfirst(lc);
+
+		refno = -1;
+		if (list_member(exprs_processed, expr) ||
+			(!IsA(expr, Var) && !IsA(expr, PlaceHolderVar)))
+			continue;
+		exprs_processed = lappend(exprs_processed, expr);
+		for (exprno = 0; exprno < exprs_num; exprno++)
+		{
+			Expr	   *target_expr = (Expr *) list_nth(partial_target->exprs, exprno);
+
+			if (equal(target_expr, expr))
+			{
+				refno = exprno;
+				break;
+			}
+		}
+		if (refno < 0)
+		{
+			SortGroupClause *grpcl = makeNode(SortGroupClause);
+
+			grpcl->tleSortGroupRef = maxRef++;
+			extra->groupClausePartial = lappend(extra->groupClausePartial, grpcl);
+			add_column_to_pathtarget(extra->partial_target, expr, grpcl->tleSortGroupRef);
+		}
+	}
+}
+
 /*
  * make_partial_grouping_target
  *	  Generate appropriate PathTarget for output of partial aggregate
@@ -5419,7 +5479,7 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
 static PathTarget *
 make_partial_grouping_target(PlannerInfo *root,
 							 PathTarget *grouping_target,
-							 Node *havingQual)
+							 GroupPathExtraData *extra)
 {
 	PathTarget *partial_target;
 	List	   *non_group_cols;
@@ -5461,8 +5521,8 @@ make_partial_grouping_target(PlannerInfo *root,
 	/*
 	 * If there's a HAVING clause, we'll need the Vars/Aggrefs it uses, too.
 	 */
-	if (havingQual)
-		non_group_cols = lappend(non_group_cols, havingQual);
+	if (extra->havingQual)
+		non_group_cols = lappend(non_group_cols, extra->havingQual);
 
 	/*
 	 * Pull out all the Vars, PlaceHolderVars, and Aggrefs mentioned in
@@ -5475,7 +5535,7 @@ make_partial_grouping_target(PlannerInfo *root,
 									  PVC_INCLUDE_AGGREGATES |
 									  PVC_RECURSE_WINDOWFUNCS |
 									  PVC_INCLUDE_PLACEHOLDERS);
-
+	setGroupClausePartial(partial_target, non_group_exprs, root->parse->groupClause, extra);
 	add_new_columns_to_pathtarget(partial_target, non_group_exprs);
 
 	/*
@@ -5504,6 +5564,7 @@ make_partial_grouping_target(PlannerInfo *root,
 			lfirst(lc) = newaggref;
 		}
 	}
+	extra->partial_target->exprs = partial_target->exprs;
 
 	/* clean up cruft */
 	list_free(non_group_exprs);
@@ -7098,7 +7159,7 @@ create_partial_grouping_paths(PlannerInfo *root,
 	 */
 	partially_grouped_rel->reltarget =
 		make_partial_grouping_target(root, grouped_rel->reltarget,
-									 extra->havingQual);
+									 extra);
 
 	if (!extra->partial_costs_set)
 	{
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 7977d6a9c0..5ab983b82e 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -13716,6 +13716,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;
@@ -13798,11 +13799,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 "
@@ -13828,6 +13829,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"));
@@ -13917,6 +13919,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/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index a1dc1d07e1..b1e75aaf59 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -3247,6 +3247,8 @@ typedef struct
 	Node	   *havingQual;
 	List	   *targetList;
 	PartitionwiseAggregateType patype;
+	List	   *groupClausePartial;
+	PathTarget *partial_target;
 } GroupPathExtraData;
 
 /*
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.39.3



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

* Re: Partial aggregates pushdown
@ 2023-09-26 13:15  Alexander Pyhalov <[email protected]>
  parent: [email protected] <[email protected]>
  1 sibling, 0 replies; 38+ messages in thread

From: Alexander Pyhalov @ 2023-09-26 13:15 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: Bruce Momjian <[email protected]>; Finnerty, Jim <[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-09-25 06:18:
> Hi Mr.Bruce, Mr.Pyhalov, Mr.Finnerty, hackers.
> 
> Thank you for your valuable comments. I sincerely apologize for the
> very late reply.
> Here is a response to your comments or a fix to the patch.
> 
> Tuesday, August 8, 2023 at 3:31 Bruce Momjian
>> > I have modified the program except for the point "if the version of the remote server is less than PG17".
>> > Instead, we have addressed the following.
>> > "If check_partial_aggregate_support is true and the remote server version is older than the local server
>> > version, postgres_fdw does not assume that the partial aggregate function is on the remote server unless
>> > the partial aggregate function and the aggregate function match."
>> > The reason for this is to maintain compatibility with any aggregate function that does not support partial
>> > aggregate in one version of V1 (V1 is PG17 or higher), even if the next version supports partial aggregate.
>> > For example, string_agg does not support partial aggregation in PG15, but it will support partial aggregation
>> > in PG16.
>> 
>> Just to clarify, I think you are saying:
>> 
>>         If check_partial_aggregate_support is true and the remote 
>> server
>>         version is older than the local server version, postgres_fdw
>>         checks if the partial aggregate function exists on the remote
>>         server during planning and only uses it if it does.
>> 
>> I tried to phrase it in a positive way, and mentioned the plan time
>> distinction.  Also, I am sorry I was away for most of July and am just
>> getting to this.
> Thanks for your comment. In the documentation, the description of
> check_partial_aggregate_support is as follows
> (please see postgres-fdw.sgml).
> --
> check_partial_aggregate_support (boolean)
> Only if this option is true, during query planning, postgres_fdw
> connects to the remote server and check if the remote server version
> is older than the local server version. If so, postgres_fdw assumes
> that for each built-in aggregate function, the partial aggregate
> function is not defined on the remote server unless the partial
> aggregate function and the aggregate function match. The default is
> false.
> --
> 
> Thursday, 20 July 2023 19:23 Alexander Pyhalov 
> <[email protected]>:
>> [email protected] писал 2023-07-19 03:43:
>> > Hi Mr.Pyhalov, hackers.
>> 
>> > 3)
>> > I modified the patch to safely do a partial aggregate pushdown for
>> > queries which contain having clauses.
>> >
>> 
>> Hi.
>> Sorry, but I don't see how it could work.
> We apologize for any inconvenience caused.
> Thanks to Pyhalov's and Jim's comments, I have realized that I have
> made a fundamental mistake regarding the pushdown of the HAVING clause
> and the difficulty of achieving it performing Partial aggregate
> pushdown.
> So, I removed the codes about pushdown of the HAVING clause performing
> Partial aggregate pushdown.
> 
> Thursday, 20 July 2023 19:23 Alexander Pyhalov 
> <[email protected]>:
>> As for changes in planner.c (setGroupClausePartial()) I have several
>> questions.
>> 
>> 1) Why don't we add non_group_exprs to pathtarget->exprs when
>> partial_target->exprs is not set?
>> 
>> 2) We replace extra->partial_target->exprs with partial_target->exprs
>> after processing. Why are we sure that after this tleSortGroupRef is
>> correct?
> Response to 1)
> The code you pointed out was unnecessary. I have removed this code.
> Also, the process of adding PlaceHolderVar's expr to partial_target was 
> missing.
> So I fixed this.
> 
> Response to 2)
> The making procedures extra->groupClausePartial and 
> extra->partial_target
> in make_partial_grouping_target for this patch is as follows.
> STEP1. From grouping_target->exprs, extract Aggref, Var and
> Placeholdervar that are not included in Aggref.
> STEP2. setGroupClausePartial sets the copy of original groupClause to
> extra->groupClausePartial
> and sets the copy of original partial_target to extra->partial_target.
> STEP3. setGroupClausePartial adds Var and Placeholdervar in STEP1 to
> partial_target.
> The sortgroupref of partial_target->sortgrouprefs to be added to value 
> is set to
> (the maximum value of the existing sortgroupref) + 1.
> setGroupClausePartial adds data sgc of sortgroupclause type where
> sgc->tlesortgroupref
> matches the sortgroupref to GroupClause.
> STEP4. add_new_columns_to_pathtarget adds STEP1's Aggref to 
> partial_target.
> 
> Due to STEP2, the list of tlesortgrouprefs set in
> extra->groupClausePartial is not duplicated.

Do you mean that extra->partial_target->sortgrouprefs is not replaced, 
and so we preserve tlesortgroupref numbers?
I'm suspicious about rewriting extra->partial_target->exprs with 
partial_target->exprs - I'm still not sure why we
  don't we loose information, added by add_column_to_pathtarget() to 
extra->partial_target->exprs?

Also look at the following example.

EXPLAIN VERBOSE SELECT  count(*) , (b/2)::numeric FROM pagg_tab GROUP BY 
b/2 ORDER BY 1;
                                             QUERY PLAN
---------------------------------------------------------------------------------------------------
  Sort  (cost=511.35..511.47 rows=50 width=44)
    Output: (count(*)), ((((pagg_tab.b / 2)))::numeric), ((pagg_tab.b / 
2))
    Sort Key: (count(*))
    ->  Finalize HashAggregate  (cost=509.06..509.94 rows=50 width=44)
          Output: count(*), (((pagg_tab.b / 2)))::numeric, ((pagg_tab.b / 
2))
          Group Key: ((pagg_tab.b / 2))
          ->  Append  (cost=114.62..506.06 rows=600 width=16)
                ->  Foreign Scan  (cost=114.62..167.69 rows=200 width=16)
                      Output: ((pagg_tab.b / 2)), (PARTIAL count(*)), 
pagg_tab.b
                      Relations: Aggregate on (public.fpagg_tab_p1 
pagg_tab)
                      Remote SQL: SELECT (b / 2), count(*), b FROM 
public.pagg_tab_p1 GROUP BY 1, 2
                ->  Foreign Scan  (cost=114.62..167.69 rows=200 width=16)
                      Output: ((pagg_tab_1.b / 2)), (PARTIAL count(*)), 
pagg_tab_1.b
                      Relations: Aggregate on (public.fpagg_tab_p2 
pagg_tab_1)
                      Remote SQL: SELECT (b / 2), count(*), b FROM 
public.pagg_tab_p2 GROUP BY 1, 2
                ->  Foreign Scan  (cost=114.62..167.69 rows=200 width=16)
                      Output: ((pagg_tab_2.b / 2)), (PARTIAL count(*)), 
pagg_tab_2.b
                      Relations: Aggregate on (public.fpagg_tab_p3 
pagg_tab_2)
                      Remote SQL: SELECT (b / 2), count(*), b FROM 
public.pagg_tab_p3 GROUP BY 1, 2

Note that group by is still deparsed incorrectly.
-- 
Best regards,
Alexander Pyhalov,
Postgres Professional






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

* Re: Partial aggregates pushdown
@ 2023-09-26 14:16  Bruce Momjian <[email protected]>
  parent: [email protected] <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: Bruce Momjian @ 2023-09-26 14:16 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: Alexander Pyhalov <[email protected]>; Finnerty, Jim <[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, Sep 26, 2023 at 06:26:25AM +0000, [email protected] wrote:
> Hi Mr.Bruce.
> > I think this needs to be explained in the docs.  I am ready to adjust the patch to improve the wording whenever you are
> > ready.  Should I do it now and post an updated version for you to use?
> The following explanation was omitted from the documentation, so I added it.
> > *  false - no check
> > *  true, remove version < sender - check
> I have responded to your comment, but if there is a problem with the wording, could you please suggest a correction?

I like your new wording, thanks.

-- 
  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] 38+ messages in thread

* RE: Partial aggregates pushdown
@ 2023-09-26 22:35  [email protected] <[email protected]>
  parent: Bruce Momjian <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: [email protected] @ 2023-09-26 22:35 UTC (permalink / raw)
  To: Bruce Momjian <[email protected]>; Alexander Pyhalov <[email protected]>; pgsql-hackers; +Cc: Finnerty, Jim <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; [email protected] <[email protected]>

Hi Mr.Momjian, Mr.Pyhalov.

Tuesday, 26 September 2023 22:15 Alexander Pyhalov <[email protected]>:
> Do you mean that extra->partial_target->sortgrouprefs is not replaced,
> and so we preserve tlesortgroupref numbers?
Yes, that is correct.

> I'm suspicious about rewriting extra->partial_target->exprs with
> partial_target->exprs - I'm still not sure why we
>   don't we loose information, added by add_column_to_pathtarget() to
> extra->partial_target->exprs?
>
> Also look at the following example.
>
> EXPLAIN VERBOSE SELECT  count(*) , (b/2)::numeric FROM pagg_tab GROUP BY
> b/2 ORDER BY 1;
>                                              QUERY PLAN
> ---------------------------------------------------------------------------------------------------
>   Sort  (cost=511.35..511.47 rows=50 width=44)
>     Output: (count(*)), ((((pagg_tab.b / 2)))::numeric), ((pagg_tab.b /
> 2))
>     Sort Key: (count(*))
>     ->  Finalize HashAggregate  (cost=509.06..509.94 rows=50 width=44)
>           Output: count(*), (((pagg_tab.b / 2)))::numeric, ((pagg_tab.b /
> 2))
>           Group Key: ((pagg_tab.b / 2))
>           ->  Append  (cost=114.62..506.06 rows=600 width=16)
>                 ->  Foreign Scan  (cost=114.62..167.69 rows=200 width=16)
>                       Output: ((pagg_tab.b / 2)), (PARTIAL count(*)),
> pagg_tab.b
>                       Relations: Aggregate on (public.fpagg_tab_p1
> pagg_tab)
>                       Remote SQL: SELECT (b / 2), count(*), b FROM
> public.pagg_tab_p1 GROUP BY 1, 2
>                 ->  Foreign Scan  (cost=114.62..167.69 rows=200 width=16)
>                       Output: ((pagg_tab_1.b / 2)), (PARTIAL count(*)),
> pagg_tab_1.b
>                       Relations: Aggregate on (public.fpagg_tab_p2
> pagg_tab_1)
>                       Remote SQL: SELECT (b / 2), count(*), b FROM
> public.pagg_tab_p2 GROUP BY 1, 2
>                 ->  Foreign Scan  (cost=114.62..167.69 rows=200 width=16)
>                       Output: ((pagg_tab_2.b / 2)), (PARTIAL count(*)),
> pagg_tab_2.b
>                       Relations: Aggregate on (public.fpagg_tab_p3
> pagg_tab_2)
>                       Remote SQL: SELECT (b / 2), count(*), b FROM
> public.pagg_tab_p3 GROUP BY 1, 2
>
> Note that group by is still deparsed incorrectly.
Thank you for comments. You are right.
It is a mistake to rewrite extra->partial_target->exprs with partial_target->exprs.
I fixed this point.

September 26, 2023 (Fire) 23:16 Bruce Momjian <[email protected]>.
> On Tue, Sep 26, 2023 at 06:26:25AM +0000, [email protected] wrote:
> > Hi Mr.Bruce.
> > > I think this needs to be explained in the docs.  I am ready to adjust the patch to improve the wording whenever you are
> > > ready.  Should I do it now and post an updated version for you to use?
> > The following explanation was omitted from the documentation, so I added it.
> > > *  false - no check
> > > *  true, remove version < sender - check
> > I have responded to your comment, but if there is a problem with the wording, could you please suggest a correction?
>
> I like your new wording, thanks.
Thanks.

Sincerely yours,
Yuuki Fujii

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


Attachments:

  [application/octet-stream] 0001-Partial-aggregates-push-down-v27.patch (266.1K, ../../OS3PR01MB66608567A5DE381D99BEE1A395C3A@OS3PR01MB6660.jpnprd01.prod.outlook.com/2-0001-Partial-aggregates-push-down-v27.patch)
  download | inline diff:
From 71a82604c545a7fc841576faf0477ec61cc3aec7 Mon Sep 17 00:00:00 2001
From: Yuki Fujii <[email protected]>
Date: Wed, 27 Sep 2023 07:20:06 +0900
Subject: [PATCH] Partial aggregates push down v27

---
 contrib/postgres_fdw/deparse.c                | 103 ++-
 .../postgres_fdw/expected/postgres_fdw.out    | 868 +++++++++++++++++-
 contrib/postgres_fdw/option.c                 |   4 +-
 contrib/postgres_fdw/postgres_fdw.c           |  47 +-
 contrib/postgres_fdw/postgres_fdw.h           |  10 +
 contrib/postgres_fdw/sql/postgres_fdw.sql     | 313 ++++++-
 doc/src/sgml/catalogs.sgml                    |  13 +
 doc/src/sgml/postgres-fdw.sgml                | 137 ++-
 doc/src/sgml/ref/create_aggregate.sgml        |  42 +
 doc/src/sgml/xaggr.sgml                       |  15 +-
 src/backend/catalog/pg_aggregate.c            | 101 ++
 src/backend/commands/aggregatecmds.c          |   4 +
 src/backend/optimizer/plan/planner.c          | 127 ++-
 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 ++++
 src/include/nodes/pathnodes.h                 |   2 +
 .../regress/expected/create_aggregate.out     | 184 ++++
 src/test/regress/expected/oidjoins.out        |   1 +
 src/test/regress/sql/create_aggregate.sql     | 183 ++++
 21 files changed, 2820 insertions(+), 223 deletions(-)

diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 09d6dd60dd..e957611879 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 */
@@ -3723,6 +3744,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	Query	   *query = context->root->parse;
 	ListCell   *lc;
 	bool		first = true;
+	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) context->foreignrel->fdw_private;
 
 	/* Nothing to be done, if there's no GROUP BY clause in the query. */
 	if (!query->groupClause)
@@ -3743,7 +3765,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	 * to empty, and in any case the redundancy situation on the remote might
 	 * be different than what we think here.
 	 */
-	foreach(lc, query->groupClause)
+	foreach(lc, fpinfo->group_clause)
 	{
 		SortGroupClause *grp = (SortGroupClause *) lfirst(lc);
 
@@ -4047,3 +4069,68 @@ get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
 	/* Shouldn't get here */
 	elog(ERROR, "unexpected expression in subquery output");
 }
+
+/*
+ * Check that a buit-in aggpartialfunc exists on the remote server. If
+ * check_partial_aggregate_support is false, we assume the partial aggregate
+ * function exsits on the remote server. Otherwise we assume the partial
+ * aggregate function exsits on the remote server only if the remote server
+ * version is not less than the local server version.
+ */
+static bool
+is_builtin_aggpartialfunc_shippable(Oid aggpartialfn, PgFdwRelationInfo *fpinfo)
+{
+	bool		shippable = true;
+
+	if (fpinfo->check_partial_aggregate_support)
+	{
+		if (fpinfo->remoteversion == 0)
+		{
+			PGconn	   *conn = GetConnection(fpinfo->user, false, NULL);
+
+			fpinfo->remoteversion = PQserverVersion(conn);
+		}
+		if (fpinfo->remoteversion < PG_VERSION_NUM)
+			shippable = false;
+	}
+	return shippable;
+}
+
+/*
+ * 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 exists on the remote server
+ */
+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_builtin(aggform->aggpartialfn))
+		partial_agg_ok = is_builtin_aggpartialfunc_shippable(
+															 aggform->aggpartialfn, fpinfo);
+	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 144c114d0f..3968bb4e17 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -9758,18 +9758,35 @@ 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.1');
+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');
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -9823,8 +9840,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
@@ -9835,21 +9852,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;
@@ -9863,28 +9880,837 @@ SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
  21 |   100
 (6 rows)
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are 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                            
------------------------------------------------------------------
- Sort
-   Sort Key: pagg_tab.b
-   ->  Finalize HashAggregate
-         Group Key: pagg_tab.b
-         Filter: (sum(pagg_tab.a) < 700)
+                                                                 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 fpagg_tab_p1 pagg_tab
+                     ->  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 fpagg_tab_p2 pagg_tab_1
+                     ->  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 fpagg_tab_p3 pagg_tab_2
+                     ->  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)
+
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
+ b  |         avg         | max | count 
+----+---------------------+-----+-------
+  0 | 10.0000000000000000 |  20 |    60
+  1 | 11.0000000000000000 |  21 |    60
+ 10 | 10.0000000000000000 |  20 |    60
+ 11 | 11.0000000000000000 |  21 |    60
+ 20 | 10.0000000000000000 |  20 |    60
+ 21 | 11.0000000000000000 |  21 |    60
+ 30 | 10.0000000000000000 |  20 |    60
+ 31 | 11.0000000000000000 |  21 |    60
+ 40 | 10.0000000000000000 |  20 |    60
+ 41 | 11.0000000000000000 |  21 |    60
+(10 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+                                                                  QUERY PLAN                                                                  
+----------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: ((((pagg_tab.b / 2)))::numeric), (avg(pagg_tab.a)), (max(pagg_tab.a)), (count(*)), ((pagg_tab.b / 2))
+   Sort Key: ((((pagg_tab.b / 2)))::numeric)
+   ->  Finalize HashAggregate
+         Output: (((pagg_tab.b / 2)))::numeric, avg(pagg_tab.a), max(pagg_tab.a), count(*), ((pagg_tab.b / 2))
+         Group Key: ((pagg_tab.b / 2))
+         ->  Append
+               ->  Foreign Scan
+                     Output: ((pagg_tab.b / 2)), 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 / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p1 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_1.b / 2)), 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 / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p2 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_2.b / 2)), 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 / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p3 GROUP BY 1, 2
+(19 rows)
+
+SELECT (b/2)::numeric, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+ numeric |         avg         | max | count 
+---------+---------------------+-----+-------
+       0 | 10.5000000000000000 |  21 |   120
+       1 | 12.5000000000000000 |  23 |   120
+       2 | 14.5000000000000000 |  25 |   120
+       3 | 16.5000000000000000 |  27 |   120
+       4 | 18.5000000000000000 |  29 |   120
+       5 | 10.5000000000000000 |  21 |   120
+       6 | 12.5000000000000000 |  23 |   120
+       7 | 14.5000000000000000 |  25 |   120
+       8 | 16.5000000000000000 |  27 |   120
+       9 | 18.5000000000000000 |  29 |   120
+      10 | 10.5000000000000000 |  21 |   120
+      11 | 12.5000000000000000 |  23 |   120
+      12 | 14.5000000000000000 |  25 |   120
+      13 | 16.5000000000000000 |  27 |   120
+      14 | 18.5000000000000000 |  29 |   120
+      15 | 10.5000000000000000 |  21 |   120
+      16 | 12.5000000000000000 |  23 |   120
+      17 | 14.5000000000000000 |  25 |   120
+      18 | 16.5000000000000000 |  27 |   120
+      19 | 18.5000000000000000 |  29 |   120
+      20 | 10.5000000000000000 |  21 |   120
+      21 | 12.5000000000000000 |  23 |   120
+      22 | 14.5000000000000000 |  25 |   120
+      23 | 16.5000000000000000 |  27 |   120
+      24 | 18.5000000000000000 |  29 |   120
+(25 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)
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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)
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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)
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+                                                                           QUERY PLAN                                                                           
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: avg(t1.b), avg((t1.b)::bigint)
+   ->  Append
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1.b)), (PARTIAL avg((t1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p1 t1) INNER JOIN (public.fpagg_tab_p1 t2))
+               Remote SQL: SELECT avg_p_int4(r4.b), avg_p_int8(r4.b::bigint) FROM (public.pagg_tab_p1 r4 INNER JOIN public.pagg_tab_p1 r7 ON (((r4.a = r7.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_1.b)), (PARTIAL avg((t1_1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p2 t1_1) INNER JOIN (public.fpagg_tab_p2 t2_1))
+               Remote SQL: SELECT avg_p_int4(r5.b), avg_p_int8(r5.b::bigint) FROM (public.pagg_tab_p2 r5 INNER JOIN public.pagg_tab_p2 r8 ON (((r5.a = r8.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_2.b)), (PARTIAL avg((t1_2.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p3 t1_2) INNER JOIN (public.fpagg_tab_p3 t2_2))
+               Remote SQL: SELECT avg_p_int4(r6.b), avg_p_int8(r6.b::bigint) FROM (public.pagg_tab_p3 r6 INNER JOIN public.pagg_tab_p3 r9 ON (((r6.a = r9.a))))
 (15 rows)
 
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+         avg         |         avg         
+---------------------+---------------------
+ 24.5000000000000000 | 24.5000000000000000
+(1 row)
+
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+-- 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/option.c b/contrib/postgres_fdw/option.c
index 8c822f4ef9..67e15c8bb9 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -126,7 +126,8 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
 			strcmp(def->defname, "async_capable") == 0 ||
 			strcmp(def->defname, "parallel_commit") == 0 ||
 			strcmp(def->defname, "parallel_abort") == 0 ||
-			strcmp(def->defname, "keep_connections") == 0)
+			strcmp(def->defname, "keep_connections") == 0 ||
+			strcmp(def->defname, "check_partial_aggregate_support") == 0)
 		{
 			/* these accept only boolean values */
 			(void) defGetBoolean(def);
@@ -268,6 +269,7 @@ InitPgFdwOptions(void)
 		/* batch_size is available on both server and table */
 		{"batch_size", ForeignServerRelationId, false},
 		{"batch_size", ForeignTableRelationId, false},
+		{"check_partial_aggregate_support", ForeignServerRelationId, false},
 		/* async_capable is available on both server and table */
 		{"async_capable", ForeignServerRelationId, false},
 		{"async_capable", ForeignTableRelationId, false},
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 1393716587..8a905ad8c4 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -36,6 +36,7 @@
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
+#include "optimizer/planner.h"
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
@@ -519,7 +520,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);
+								GroupPathExtraData *extra);
 static List *get_useful_pathkeys_for_relation(PlannerInfo *root,
 											  RelOptInfo *rel);
 static List *get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel);
@@ -650,6 +651,8 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	fpinfo->fdw_tuple_cost = DEFAULT_FDW_TUPLE_COST;
 	fpinfo->shippable_extensions = NIL;
 	fpinfo->fetch_size = 100;
+	fpinfo->check_partial_aggregate_support = false;
+	fpinfo->remoteversion = 0;
 	fpinfo->async_capable = false;
 
 	apply_server_options(fpinfo);
@@ -661,7 +664,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	 * should match what ExecCheckPermissions() does.  If we fail due to lack
 	 * of permissions, the query would have failed at runtime anyway.
 	 */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
 		Oid			userid;
 
@@ -5948,9 +5951,10 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
 	fpinfo->pushdown_safe = true;
 
 	/* Get user mapping */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
-		if (fpinfo_o->use_remote_estimate)
+		if (fpinfo_o->use_remote_estimate ||
+			fpinfo_o->check_partial_aggregate_support)
 			fpinfo->user = fpinfo_o->user;
 		else
 			fpinfo->user = fpinfo_i->user;
@@ -6132,6 +6136,8 @@ apply_server_options(PgFdwRelationInfo *fpinfo)
 				ExtractExtensionList(defGetString(def), false);
 		else if (strcmp(def->defname, "fetch_size") == 0)
 			(void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
+		else if (strcmp(def->defname, "check_partial_aggregate_support") == 0)
+			fpinfo->check_partial_aggregate_support = defGetBoolean(def);
 		else if (strcmp(def->defname, "async_capable") == 0)
 			fpinfo->async_capable = defGetBoolean(def);
 	}
@@ -6191,6 +6197,8 @@ merge_fdw_options(PgFdwRelationInfo *fpinfo,
 	fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
 	fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
 	fpinfo->fetch_size = fpinfo_o->fetch_size;
+	fpinfo->check_partial_aggregate_support = fpinfo_o->check_partial_aggregate_support;
+	fpinfo->remoteversion = fpinfo_o->remoteversion;
 	fpinfo->async_capable = fpinfo_o->async_capable;
 
 	/* Merge the table level options from either side of the join. */
@@ -6373,7 +6381,7 @@ postgresGetForeignJoinPaths(PlannerInfo *root,
  */
 static bool
 foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
-					Node *havingQual)
+					GroupPathExtraData *extra)
 {
 	Query	   *query = root->parse;
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
@@ -6382,11 +6390,16 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	ListCell   *lc;
 	int			i;
 	List	   *tlist = NIL;
+	bool		partial = extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL;
 
 	/* We currently don't support pushing Grouping Sets. */
 	if (query->groupingSets)
 		return false;
 
+	/* It's unsafe to push having statements with partial aggregates */
+	if ((extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL) && extra->havingQual)
+		return false;
+
 	/* Get the fpinfo of the underlying scan relation. */
 	ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
 
@@ -6415,6 +6428,12 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * a node, as long as it's not at top level; then no match is possible.
 	 */
 	i = 0;
+	fpinfo->group_clause = query->groupClause;
+	if (partial)
+	{
+		fpinfo->group_clause = extra->groupClausePartial;
+		grouping_target = extra->partial_target;
+	}
 	foreach(lc, grouping_target->exprs)
 	{
 		Expr	   *expr = (Expr *) lfirst(lc);
@@ -6426,7 +6445,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 		 * check the whole GROUP BY clause not just processed_groupClause,
 		 * because we will ship all of it, cf. appendGroupByClause.
 		 */
-		if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
+		if (sgref && get_sortgroupref_clause_noerr(sgref, fpinfo->group_clause))
 		{
 			TargetEntry *tle;
 
@@ -6512,9 +6531,9 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * Classify the pushable and non-pushable HAVING clauses and save them in
 	 * remote_conds and local_conds of the grouped rel's fpinfo.
 	 */
-	if (havingQual)
+	if (extra->havingQual)
 	{
-		foreach(lc, (List *) havingQual)
+		foreach(lc, (List *) extra->havingQual)
 		{
 			Expr	   *expr = (Expr *) lfirst(lc);
 			RestrictInfo *rinfo;
@@ -6534,7 +6553,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 									  grouped_rel->relids,
 									  NULL,
 									  NULL);
-			if (is_foreign_expr(root, grouped_rel, expr))
+			if (is_foreign_expr(root, grouped_rel, expr) && !partial)
 				fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
 			else
 				fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
@@ -6628,6 +6647,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)
@@ -6644,6 +6664,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;
@@ -6684,7 +6708,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;
@@ -6704,7 +6729,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))
 		return;
 
 	/*
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index 02c1152319..c60f3f5465 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -86,6 +86,15 @@ typedef struct PgFdwRelationInfo
 	ForeignServer *server;
 	UserMapping *user;			/* only set in use_remote_estimate mode */
 
+	/* for partial aggregate pushdown */
+	bool		check_partial_aggregate_support;
+
+	/*
+	 * if remoteversion is zero, then that means the remote server version is
+	 * unacquired.
+	 */
+	int			remoteversion;
+
 	int			fetch_size;		/* fetch size for this remote table */
 
 	/*
@@ -110,6 +119,7 @@ typedef struct PgFdwRelationInfo
 
 	/* Grouping information */
 	List	   *grouped_tlist;
+	List	   *group_clause;
 
 	/* Subquery information */
 	bool		make_outerrel_subquery; /* do we deparse outerrel as a
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index a303bfb322..018dae70d9 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -2989,16 +2989,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.1');
+
+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,9 @@ CREATE FOREIGN TABLE fpagg_tab_p2 PARTITION OF pagg_tab FOR VALUES FROM (10) TO
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -3028,9 +3046,288 @@ EXPLAIN (VERBOSE, COSTS OFF)
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (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 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) < 1300 ORDER BY 1;
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 1300 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 1;
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 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;
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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;
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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;
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+
+-- 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 e09adb45e4..de27544573 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="partial-aggregate-pushdown"/> 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..3a88cac3c1 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -451,6 +451,44 @@ OPTIONS (ADD password_required 'false');
 
   </sect3>
 
+  <sect3 id="postgres-fdw-options-partial-aggregate-pushdown">
+   <title>Partial Aggregate Pushdown Options</title>
+
+   <para>
+    By default, <filename>postgres_fdw</filename> assumes that for each built-in aggregate
+    function, the partial aggregate function is defined on the remote server. See
+    <xref linkend="partial-aggregate-pushdown"/> for the definition of the partial aggregate
+    function. This may be overridden using the following option:
+   </para>
+
+   <variablelist>
+
+    <varlistentry>
+     <term><literal>check_partial_aggregate_support</literal> (<type>boolean</type>)</term>
+     <listitem>
+      <para>
+       If this option is false, <filename>postgres_fdw</filename> assumes
+       that for each built-in aggregate function,
+       the partial aggregate function is defined on the remote server
+       without checking the remote server version.
+       If this option is true, during query planning,
+       <filename>postgres_fdw</filename> connects to the remote server
+       and checks if the remote server version is older than the local server version.
+       If so,
+       <filename>postgres_fdw</filename>
+       assumes that for each built-in aggregate function, the partial aggregate function is not defined
+       on the remote server unless the partial aggregate function and the aggregate
+       function match.
+       Otherwise <filename>postgres_fdw</filename> assumes that for each built-in aggregate function,
+       the partial aggregate function is defined on the remote server.
+       The default is <literal>false</literal>.
+      </para>
+     </listitem>
+    </varlistentry>
+
+   </variablelist>
+  </sect3>
+
   <sect3 id="postgres-fdw-options-asynchronous-execution">
    <title>Asynchronous Execution Options</title>
 
@@ -929,13 +967,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 +1006,68 @@ postgres=# SELECT postgres_fdw_disconnect_all();
   </para>
  </sect2>
 
+ <sect2 id="partial-aggregate-pushdown">
+  <title>Partial aggregate pushdown</title>
+  <para>
+   Partial aggregate pushdown is an optimization for a query that contains aggregate
+   expressions for a partitioned table across one or more remote servers. If the multiple
+   conditions are true for a remote server, an aggregate expression is sent to that
+   remote server and the aggregate is a partial aggregate on the local server.
+   The conditions under which this sending is active are as follows.
+   <itemizedlist spacing="compact">
+     <listitem>
+      <para>
+       An aggregate function, called the partial aggregate function for partial aggregate
+       that corresponding to the aggregate expression, is defined on the local server and
+       the remote server.
+      </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>
+       The aggregate expressions in the query don't contain
+       a <literal>DISTINCT</literal> or <literal>ORDER BY</literal> clause.
+      </para>
+     </listitem>
+    </itemizedlist>
+   When this sending is active for aggregate expressions, any aggregate
+   function is replaced by the partial aggregate function in the remote query.
+   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.
+   By default, <filename>postgres_fdw</filename> assumes that for each built-in
+   aggregate function, the partial aggregate function is defined on the local server
+   and the remote server.
+   If <literal>check_partial_aggregate_support</literal> is true and the remote server version is
+   older than the local server version, <filename>postgres_fdw</filename> does not assume
+   that the partial aggregate function is on the remote server unless
+   the partial aggregate function and the aggregate function match.
+   <filename>postgres_fdw</filename> assumes that a user-defined aggregate
+   function has the partial aggregate function on the remote server
+   only if both belong to an extension that is listed in the foreign server's
+   extensions option.
+  </para>
+ </sect2>
+
  <sect2 id="postgres-fdw-remote-query-execution-environment">
   <title>Remote Query Execution Environment</title>
 
@@ -1031,14 +1133,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 partial aggregate pushdown feature.
+   When using the partial aggregate pushdown feature,
+   the partial aggregate function of the aggregate function is sent to
+   the remote server if the conditions in
+   <xref linkend="partial-aggregate-pushdown"/> are met.
+   A limitation is that by default, <filename>postgres_fdw</filename> assumes that
+   for each built-in aggregate function, the partial aggregate function is defined
+   on the remote server.
+   This limitation might cause <quote>function does not exist</quote>
+   or a similar error if the remote server version is older than the local server version.
+   This problem can be worked around by setting
+   <literal>check_partial_aggregate_support</literal> to <literal>true</literal>
+   or similar query rewriting as above.
   </para>
  </sect2>
 
diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml
index 222e0aa5c9..2875734dea 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>
@@ -246,6 +248,18 @@ CREATE [ OR REPLACE ] AGGREGATE <replaceable class="parameter">name</replaceable
    marked <literal>PARALLEL SAFE</literal> to enable parallel aggregation.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize a query containing aggregate
+   expressions for a partitioned table across one or more remote servers.
+   This optimization sends an aggregate expression to a remote server and the
+   aggregate is a partial aggregate on the local server. This optimization
+   requires an aggregate function called a <firstterm>partial aggregate
+   function</firstterm>. See
+   <xref linkend="partial-aggregate-pushdown"/> for the definition of
+   partial aggregate function. <literal>AGGPARTIALFUNC</literal> is a parameter
+   of the partial aggregate function.
+  </para>
+
   <para>
    Aggregates that behave like <function>MIN</function> or <function>MAX</function> can
    sometimes be optimized by looking into an index instead of scanning every
@@ -653,6 +667,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..607c859974 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,18 @@ SELECT percentile_disc(0.5) WITHIN GROUP (ORDER BY income) FROM households;
    parallel-safety markings on its support functions are not consulted.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize a query containing
+   aggregate expressions for a partitioned table across one or more
+   remote servers. This optimization sends an aggregate expression to
+   a remote server and the aggregate is a partial aggregate on the
+   local server. This optimization requires an aggregate function
+   called the <firstterm>partial aggregate function</firstterm>.
+   See
+   <xref linkend="partial-aggregate-pushdown"/> 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/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 44efb1f4eb..d0c3556745 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -206,7 +206,7 @@ static PathTarget *make_group_input_target(PlannerInfo *root,
 										   PathTarget *final_target);
 static PathTarget *make_partial_grouping_target(PlannerInfo *root,
 												PathTarget *grouping_target,
-												Node *havingQual);
+												GroupPathExtraData *extra);
 static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
 static void optimize_window_clauses(PlannerInfo *root,
 									WindowFuncLists *wflists);
@@ -5420,6 +5420,97 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
 	return set_pathtarget_cost_width(root, input_target);
 }
 
+/*
+ * setGroupClausePartial
+ *	  Generate a groupClause for partial aggregate and set it to GroupPathExtraData.
+ */
+static void
+setGroupClausePartial(PathTarget *partial_target, List *non_group_exprs,
+					  List *groupClause, GroupPathExtraData *extra)
+{
+	int			exprno,
+				refno;
+	ListCell   *lc;
+	Index		maxRef = 0;
+	List	   *exprs_processed = NIL;
+	int			exprs_num = 0;
+
+	foreach(lc, groupClause)
+	{
+		SortGroupClause *sgc = (SortGroupClause *) lfirst(lc);
+
+		if (sgc->tleSortGroupRef > maxRef)
+			maxRef = sgc->tleSortGroupRef;
+	}
+	maxRef++;
+
+	extra->groupClausePartial = list_copy_deep(groupClause);
+	extra->partial_target = copy_pathtarget(partial_target);
+
+	if(partial_target->exprs)
+		exprs_num = partial_target->exprs->length;
+
+	foreach(lc, non_group_exprs)
+	{
+		Expr	   *expr = (Expr *) lfirst(lc);
+
+		refno = -1;
+		if (list_member(exprs_processed, expr) ||
+			(!IsA(expr, Var) && !IsA(expr, PlaceHolderVar)))
+			continue;
+		exprs_processed = lappend(exprs_processed, expr);
+		for (exprno = 0; exprno < exprs_num; exprno++)
+		{
+			Expr	   *target_expr = (Expr *) list_nth(partial_target->exprs, exprno);
+
+			if (equal(target_expr, expr))
+			{
+				refno = exprno;
+				break;
+			}
+		}
+		if (refno < 0)
+		{
+			SortGroupClause *grpcl = makeNode(SortGroupClause);
+
+			grpcl->tleSortGroupRef = maxRef++;
+			extra->groupClausePartial = lappend(extra->groupClausePartial, grpcl);
+			add_column_to_pathtarget(extra->partial_target, expr, grpcl->tleSortGroupRef);
+		}
+	}
+}
+
+/*
+ * adjustAggrefForPartial
+ * Adjust Aggrefs to put them in partial mode
+ */
+static void
+adjustAggrefForPartial(List *exprs)
+{
+	ListCell   *lc;
+	foreach(lc, exprs)
+	{
+		Aggref	   *aggref = (Aggref *) lfirst(lc);
+
+		if (IsA(aggref, Aggref))
+		{
+			Aggref	   *newaggref;
+
+			/*
+			 * We shouldn't need to copy the substructure of the Aggref node,
+			 * but flat-copy the node itself to avoid damaging other trees.
+			 */
+			newaggref = makeNode(Aggref);
+			memcpy(newaggref, aggref, sizeof(Aggref));
+
+			/* For now, assume serialization is required */
+			mark_partial_aggref(newaggref, AGGSPLIT_INITIAL_SERIAL);
+
+			lfirst(lc) = newaggref;
+		}
+	}
+}
+
 /*
  * make_partial_grouping_target
  *	  Generate appropriate PathTarget for output of partial aggregate
@@ -5439,7 +5530,7 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
 static PathTarget *
 make_partial_grouping_target(PlannerInfo *root,
 							 PathTarget *grouping_target,
-							 Node *havingQual)
+							 GroupPathExtraData *extra)
 {
 	PathTarget *partial_target;
 	List	   *non_group_cols;
@@ -5481,8 +5572,8 @@ make_partial_grouping_target(PlannerInfo *root,
 	/*
 	 * If there's a HAVING clause, we'll need the Vars/Aggrefs it uses, too.
 	 */
-	if (havingQual)
-		non_group_cols = lappend(non_group_cols, havingQual);
+	if (extra->havingQual)
+		non_group_cols = lappend(non_group_cols, extra->havingQual);
 
 	/*
 	 * Pull out all the Vars, PlaceHolderVars, and Aggrefs mentioned in
@@ -5495,35 +5586,17 @@ make_partial_grouping_target(PlannerInfo *root,
 									  PVC_INCLUDE_AGGREGATES |
 									  PVC_RECURSE_WINDOWFUNCS |
 									  PVC_INCLUDE_PLACEHOLDERS);
-
+	setGroupClausePartial(partial_target, non_group_exprs, root->parse->groupClause, extra);
 	add_new_columns_to_pathtarget(partial_target, non_group_exprs);
+	add_new_columns_to_pathtarget(extra->partial_target, non_group_exprs);
 
 	/*
 	 * Adjust Aggrefs to put them in partial mode.  At this point all Aggrefs
 	 * are at the top level of the target list, so we can just scan the list
 	 * rather than recursing through the expression trees.
 	 */
-	foreach(lc, partial_target->exprs)
-	{
-		Aggref	   *aggref = (Aggref *) lfirst(lc);
-
-		if (IsA(aggref, Aggref))
-		{
-			Aggref	   *newaggref;
-
-			/*
-			 * We shouldn't need to copy the substructure of the Aggref node,
-			 * but flat-copy the node itself to avoid damaging other trees.
-			 */
-			newaggref = makeNode(Aggref);
-			memcpy(newaggref, aggref, sizeof(Aggref));
-
-			/* For now, assume serialization is required */
-			mark_partial_aggref(newaggref, AGGSPLIT_INITIAL_SERIAL);
-
-			lfirst(lc) = newaggref;
-		}
-	}
+	adjustAggrefForPartial(partial_target->exprs);
+	adjustAggrefForPartial(extra->partial_target->exprs);
 
 	/* clean up cruft */
 	list_free(non_group_exprs);
@@ -7153,7 +7226,7 @@ create_partial_grouping_paths(PlannerInfo *root,
 	 */
 	partially_grouped_rel->reltarget =
 		make_partial_grouping_target(root, grouped_rel->reltarget,
-									 extra->havingQual);
+									 extra);
 
 	if (!extra->partial_costs_set)
 	{
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index f7b6176692..a65f0be079 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -13904,6 +13904,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;
@@ -13986,11 +13987,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 "
@@ -14016,6 +14017,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"));
@@ -14105,6 +14107,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 085149e258..3e4f8331ee 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 f0b7b9cbd8..0622b2aee4 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',
@@ -5001,6 +5008,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',
@@ -5013,6 +5024,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',
@@ -6597,36 +6612,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' },
@@ -6649,6 +6689,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' },
@@ -6807,153 +6850,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',
@@ -6964,52 +7115,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/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 5702fbba60..fa2638749c 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -3257,6 +3257,8 @@ typedef struct
 	Node	   *havingQual;
 	List	   *targetList;
 	PartitionwiseAggregateType patype;
+	List	   *groupClausePartial;
+	PathTarget *partial_target;
 } GroupPathExtraData;
 
 /*
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] 38+ messages in thread

* Re: Partial aggregates pushdown
@ 2023-09-27 12:36  Alexander Pyhalov <[email protected]>
  parent: [email protected] <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: Alexander Pyhalov @ 2023-09-27 12:36 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: Bruce Momjian <[email protected]>; pgsql-hackers; Finnerty, Jim <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>

[email protected] писал 2023-09-27 01:35:
> Hi Mr.Momjian, Mr.Pyhalov.
> 
> Tuesday, 26 September 2023 22:15 Alexander Pyhalov 
> <[email protected]>:
>> Do you mean that extra->partial_target->sortgrouprefs is not replaced,
>> and so we preserve tlesortgroupref numbers?
> Yes, that is correct.
> 
>> I'm suspicious about rewriting extra->partial_target->exprs with
>> partial_target->exprs - I'm still not sure why we
>>   don't we loose information, added by add_column_to_pathtarget() to
>> extra->partial_target->exprs?
>> 

Hi.

In postgres_fdw.sql

"Partial aggregates are unsafe to push down having clause when there are 
partial aggregates" - this comment likely should be fixed.

Some comments should be added to setGroupClausePartial() and to 
make_partial_grouping_target() - especially why setGroupClausePartial()
is called prior to add_new_columns_to_pathtarget().

I'm not sure that I like this mechanics of adding sort group clauses - 
it seems we do in core additional work, which is of use only for
one extension, but at least it seems to be working.

-- 
Best regards,
Alexander Pyhalov,
Postgres Professional






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

* RE: Partial aggregates pushdown
@ 2023-09-28 04:40  [email protected] <[email protected]>
  parent: Alexander Pyhalov <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: [email protected] @ 2023-09-28 04:40 UTC (permalink / raw)
  To: Alexander Pyhalov <[email protected]>; pgsql-hackers; +Cc: Bruce Momjian <[email protected]>; Finnerty, Jim <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>

Hi Mr.Pyhalov.

Thank you for comments.
> In postgres_fdw.sql
>
> "Partial aggregates are unsafe to push down having clause when there are
> partial aggregates" - this comment likely should be fixed.
Fixed.

> Some comments should be added to setGroupClausePartial() and to
> make_partial_grouping_target() - especially why setGroupClausePartial()
> is called prior to add_new_columns_to_pathtarget().
I have added comments to setGroupClausePartial() and to make_partial_grouping_target().

> I'm not sure that I like this mechanics of adding sort group clauses -
> it seems we do in core additional work, which is of use only for
> one extension, but at least it seems to be working.
We cannot deparse the original sort group clauses and pathtarget
when performing partial aggreggate pushdown by any FDWs.
So I think the additional sort group clauses and pathtarget are
needed by any FDWs, not only postgres_fdw.

Sincerely yours,
Yuuki Fujii

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


Attachments:

  [application/octet-stream] 0001-Partial-aggregates-push-down-v28.patch (266.9K, ../../OS3PR01MB6660E61EB659850022B9607195C1A@OS3PR01MB6660.jpnprd01.prod.outlook.com/2-0001-Partial-aggregates-push-down-v28.patch)
  download | inline diff:
From eaff2e4eb7c5b5ef7ea957ef3c9ad9b01b02a031 Mon Sep 17 00:00:00 2001
From: Yuki Fujii <[email protected]>
Date: Thu, 28 Sep 2023 12:59:40 +0900
Subject: [PATCH] Partial aggregates push down v28

---
 contrib/postgres_fdw/deparse.c                | 103 ++-
 .../postgres_fdw/expected/postgres_fdw.out    | 868 +++++++++++++++++-
 contrib/postgres_fdw/option.c                 |   4 +-
 contrib/postgres_fdw/postgres_fdw.c           |  47 +-
 contrib/postgres_fdw/postgres_fdw.h           |  10 +
 contrib/postgres_fdw/sql/postgres_fdw.sql     | 311 ++++++-
 doc/src/sgml/catalogs.sgml                    |  13 +
 doc/src/sgml/postgres-fdw.sgml                | 137 ++-
 doc/src/sgml/ref/create_aggregate.sgml        |  42 +
 doc/src/sgml/xaggr.sgml                       |  15 +-
 src/backend/catalog/pg_aggregate.c            | 101 ++
 src/backend/commands/aggregatecmds.c          |   4 +
 src/backend/optimizer/plan/planner.c          | 135 ++-
 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 ++++
 src/include/nodes/pathnodes.h                 |   5 +
 .../regress/expected/create_aggregate.out     | 184 ++++
 src/test/regress/expected/oidjoins.out        |   1 +
 src/test/regress/sql/create_aggregate.sql     | 183 ++++
 21 files changed, 2830 insertions(+), 222 deletions(-)

diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 09d6dd60dd..e957611879 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 */
@@ -3723,6 +3744,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	Query	   *query = context->root->parse;
 	ListCell   *lc;
 	bool		first = true;
+	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) context->foreignrel->fdw_private;
 
 	/* Nothing to be done, if there's no GROUP BY clause in the query. */
 	if (!query->groupClause)
@@ -3743,7 +3765,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	 * to empty, and in any case the redundancy situation on the remote might
 	 * be different than what we think here.
 	 */
-	foreach(lc, query->groupClause)
+	foreach(lc, fpinfo->group_clause)
 	{
 		SortGroupClause *grp = (SortGroupClause *) lfirst(lc);
 
@@ -4047,3 +4069,68 @@ get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
 	/* Shouldn't get here */
 	elog(ERROR, "unexpected expression in subquery output");
 }
+
+/*
+ * Check that a buit-in aggpartialfunc exists on the remote server. If
+ * check_partial_aggregate_support is false, we assume the partial aggregate
+ * function exsits on the remote server. Otherwise we assume the partial
+ * aggregate function exsits on the remote server only if the remote server
+ * version is not less than the local server version.
+ */
+static bool
+is_builtin_aggpartialfunc_shippable(Oid aggpartialfn, PgFdwRelationInfo *fpinfo)
+{
+	bool		shippable = true;
+
+	if (fpinfo->check_partial_aggregate_support)
+	{
+		if (fpinfo->remoteversion == 0)
+		{
+			PGconn	   *conn = GetConnection(fpinfo->user, false, NULL);
+
+			fpinfo->remoteversion = PQserverVersion(conn);
+		}
+		if (fpinfo->remoteversion < PG_VERSION_NUM)
+			shippable = false;
+	}
+	return shippable;
+}
+
+/*
+ * 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 exists on the remote server
+ */
+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_builtin(aggform->aggpartialfn))
+		partial_agg_ok = is_builtin_aggpartialfunc_shippable(
+															 aggform->aggpartialfn, fpinfo);
+	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 144c114d0f..7e1e4eecca 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -9758,18 +9758,35 @@ 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.1');
+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');
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -9823,8 +9840,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
@@ -9835,21 +9852,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;
@@ -9863,28 +9880,837 @@ SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
  21 |   100
 (6 rows)
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are unsafe to push down when there is a having clause
+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                            
------------------------------------------------------------------
- Sort
-   Sort Key: pagg_tab.b
-   ->  Finalize HashAggregate
-         Group Key: pagg_tab.b
-         Filter: (sum(pagg_tab.a) < 700)
+                                                                 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 fpagg_tab_p1 pagg_tab
+                     ->  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 fpagg_tab_p2 pagg_tab_1
+                     ->  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 fpagg_tab_p3 pagg_tab_2
+                     ->  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)
+
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
+ b  |         avg         | max | count 
+----+---------------------+-----+-------
+  0 | 10.0000000000000000 |  20 |    60
+  1 | 11.0000000000000000 |  21 |    60
+ 10 | 10.0000000000000000 |  20 |    60
+ 11 | 11.0000000000000000 |  21 |    60
+ 20 | 10.0000000000000000 |  20 |    60
+ 21 | 11.0000000000000000 |  21 |    60
+ 30 | 10.0000000000000000 |  20 |    60
+ 31 | 11.0000000000000000 |  21 |    60
+ 40 | 10.0000000000000000 |  20 |    60
+ 41 | 11.0000000000000000 |  21 |    60
+(10 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+                                                                  QUERY PLAN                                                                  
+----------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: (avg(pagg_tab.a)), (max(pagg_tab.a)), (count(*)), ((((pagg_tab.b / 2)))::numeric), ((pagg_tab.b / 2))
+   Sort Key: ((((pagg_tab.b / 2)))::numeric)
+   ->  Finalize HashAggregate
+         Output: avg(pagg_tab.a), max(pagg_tab.a), count(*), (((pagg_tab.b / 2)))::numeric, ((pagg_tab.b / 2))
+         Group Key: ((pagg_tab.b / 2))
+         ->  Append
+               ->  Foreign Scan
+                     Output: ((pagg_tab.b / 2)), (PARTIAL avg(pagg_tab.a)), (PARTIAL max(pagg_tab.a)), (PARTIAL count(*)), pagg_tab.b
+                     Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p1 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_1.b / 2)), (PARTIAL avg(pagg_tab_1.a)), (PARTIAL max(pagg_tab_1.a)), (PARTIAL count(*)), pagg_tab_1.b
+                     Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p2 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_2.b / 2)), (PARTIAL avg(pagg_tab_2.a)), (PARTIAL max(pagg_tab_2.a)), (PARTIAL count(*)), pagg_tab_2.b
+                     Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p3 GROUP BY 1, 2
+(19 rows)
+
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+         avg         | max | count | numeric 
+---------------------+-----+-------+---------
+ 10.5000000000000000 |  21 |   120 |       0
+ 12.5000000000000000 |  23 |   120 |       1
+ 14.5000000000000000 |  25 |   120 |       2
+ 16.5000000000000000 |  27 |   120 |       3
+ 18.5000000000000000 |  29 |   120 |       4
+ 10.5000000000000000 |  21 |   120 |       5
+ 12.5000000000000000 |  23 |   120 |       6
+ 14.5000000000000000 |  25 |   120 |       7
+ 16.5000000000000000 |  27 |   120 |       8
+ 18.5000000000000000 |  29 |   120 |       9
+ 10.5000000000000000 |  21 |   120 |      10
+ 12.5000000000000000 |  23 |   120 |      11
+ 14.5000000000000000 |  25 |   120 |      12
+ 16.5000000000000000 |  27 |   120 |      13
+ 18.5000000000000000 |  29 |   120 |      14
+ 10.5000000000000000 |  21 |   120 |      15
+ 12.5000000000000000 |  23 |   120 |      16
+ 14.5000000000000000 |  25 |   120 |      17
+ 16.5000000000000000 |  27 |   120 |      18
+ 18.5000000000000000 |  29 |   120 |      19
+ 10.5000000000000000 |  21 |   120 |      20
+ 12.5000000000000000 |  23 |   120 |      21
+ 14.5000000000000000 |  25 |   120 |      22
+ 16.5000000000000000 |  27 |   120 |      23
+ 18.5000000000000000 |  29 |   120 |      24
+(25 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)
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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)
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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)
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+                                                                           QUERY PLAN                                                                           
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: avg(t1.b), avg((t1.b)::bigint)
+   ->  Append
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1.b)), (PARTIAL avg((t1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p1 t1) INNER JOIN (public.fpagg_tab_p1 t2))
+               Remote SQL: SELECT avg_p_int4(r4.b), avg_p_int8(r4.b::bigint) FROM (public.pagg_tab_p1 r4 INNER JOIN public.pagg_tab_p1 r7 ON (((r4.a = r7.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_1.b)), (PARTIAL avg((t1_1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p2 t1_1) INNER JOIN (public.fpagg_tab_p2 t2_1))
+               Remote SQL: SELECT avg_p_int4(r5.b), avg_p_int8(r5.b::bigint) FROM (public.pagg_tab_p2 r5 INNER JOIN public.pagg_tab_p2 r8 ON (((r5.a = r8.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_2.b)), (PARTIAL avg((t1_2.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p3 t1_2) INNER JOIN (public.fpagg_tab_p3 t2_2))
+               Remote SQL: SELECT avg_p_int4(r6.b), avg_p_int8(r6.b::bigint) FROM (public.pagg_tab_p3 r6 INNER JOIN public.pagg_tab_p3 r9 ON (((r6.a = r9.a))))
 (15 rows)
 
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+         avg         |         avg         
+---------------------+---------------------
+ 24.5000000000000000 | 24.5000000000000000
+(1 row)
+
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+-- 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/option.c b/contrib/postgres_fdw/option.c
index 8c822f4ef9..67e15c8bb9 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -126,7 +126,8 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
 			strcmp(def->defname, "async_capable") == 0 ||
 			strcmp(def->defname, "parallel_commit") == 0 ||
 			strcmp(def->defname, "parallel_abort") == 0 ||
-			strcmp(def->defname, "keep_connections") == 0)
+			strcmp(def->defname, "keep_connections") == 0 ||
+			strcmp(def->defname, "check_partial_aggregate_support") == 0)
 		{
 			/* these accept only boolean values */
 			(void) defGetBoolean(def);
@@ -268,6 +269,7 @@ InitPgFdwOptions(void)
 		/* batch_size is available on both server and table */
 		{"batch_size", ForeignServerRelationId, false},
 		{"batch_size", ForeignTableRelationId, false},
+		{"check_partial_aggregate_support", ForeignServerRelationId, false},
 		/* async_capable is available on both server and table */
 		{"async_capable", ForeignServerRelationId, false},
 		{"async_capable", ForeignTableRelationId, false},
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 1393716587..1d59fee2f3 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -36,6 +36,7 @@
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
+#include "optimizer/planner.h"
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
@@ -519,7 +520,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);
+								GroupPathExtraData *extra);
 static List *get_useful_pathkeys_for_relation(PlannerInfo *root,
 											  RelOptInfo *rel);
 static List *get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel);
@@ -650,6 +651,8 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	fpinfo->fdw_tuple_cost = DEFAULT_FDW_TUPLE_COST;
 	fpinfo->shippable_extensions = NIL;
 	fpinfo->fetch_size = 100;
+	fpinfo->check_partial_aggregate_support = false;
+	fpinfo->remoteversion = 0;
 	fpinfo->async_capable = false;
 
 	apply_server_options(fpinfo);
@@ -661,7 +664,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	 * should match what ExecCheckPermissions() does.  If we fail due to lack
 	 * of permissions, the query would have failed at runtime anyway.
 	 */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
 		Oid			userid;
 
@@ -5948,9 +5951,10 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
 	fpinfo->pushdown_safe = true;
 
 	/* Get user mapping */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
-		if (fpinfo_o->use_remote_estimate)
+		if (fpinfo_o->use_remote_estimate ||
+			fpinfo_o->check_partial_aggregate_support)
 			fpinfo->user = fpinfo_o->user;
 		else
 			fpinfo->user = fpinfo_i->user;
@@ -6132,6 +6136,8 @@ apply_server_options(PgFdwRelationInfo *fpinfo)
 				ExtractExtensionList(defGetString(def), false);
 		else if (strcmp(def->defname, "fetch_size") == 0)
 			(void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
+		else if (strcmp(def->defname, "check_partial_aggregate_support") == 0)
+			fpinfo->check_partial_aggregate_support = defGetBoolean(def);
 		else if (strcmp(def->defname, "async_capable") == 0)
 			fpinfo->async_capable = defGetBoolean(def);
 	}
@@ -6191,6 +6197,8 @@ merge_fdw_options(PgFdwRelationInfo *fpinfo,
 	fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
 	fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
 	fpinfo->fetch_size = fpinfo_o->fetch_size;
+	fpinfo->check_partial_aggregate_support = fpinfo_o->check_partial_aggregate_support;
+	fpinfo->remoteversion = fpinfo_o->remoteversion;
 	fpinfo->async_capable = fpinfo_o->async_capable;
 
 	/* Merge the table level options from either side of the join. */
@@ -6373,7 +6381,7 @@ postgresGetForeignJoinPaths(PlannerInfo *root,
  */
 static bool
 foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
-					Node *havingQual)
+					GroupPathExtraData *extra)
 {
 	Query	   *query = root->parse;
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
@@ -6382,11 +6390,16 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	ListCell   *lc;
 	int			i;
 	List	   *tlist = NIL;
+	bool		partial = extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL;
 
 	/* We currently don't support pushing Grouping Sets. */
 	if (query->groupingSets)
 		return false;
 
+	/* It's unsafe to push having statements with partial aggregates */
+	if ((extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL) && extra->havingQual)
+		return false;
+
 	/* Get the fpinfo of the underlying scan relation. */
 	ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
 
@@ -6415,6 +6428,12 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * a node, as long as it's not at top level; then no match is possible.
 	 */
 	i = 0;
+	fpinfo->group_clause = query->groupClause;
+	if (partial)
+	{
+		fpinfo->group_clause = extra->groupClausePartialFdw;
+		grouping_target = extra->partial_target_fdw;
+	}
 	foreach(lc, grouping_target->exprs)
 	{
 		Expr	   *expr = (Expr *) lfirst(lc);
@@ -6426,7 +6445,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 		 * check the whole GROUP BY clause not just processed_groupClause,
 		 * because we will ship all of it, cf. appendGroupByClause.
 		 */
-		if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
+		if (sgref && get_sortgroupref_clause_noerr(sgref, fpinfo->group_clause))
 		{
 			TargetEntry *tle;
 
@@ -6512,9 +6531,9 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * Classify the pushable and non-pushable HAVING clauses and save them in
 	 * remote_conds and local_conds of the grouped rel's fpinfo.
 	 */
-	if (havingQual)
+	if (extra->havingQual)
 	{
-		foreach(lc, (List *) havingQual)
+		foreach(lc, (List *) extra->havingQual)
 		{
 			Expr	   *expr = (Expr *) lfirst(lc);
 			RestrictInfo *rinfo;
@@ -6534,7 +6553,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 									  grouped_rel->relids,
 									  NULL,
 									  NULL);
-			if (is_foreign_expr(root, grouped_rel, expr))
+			if (is_foreign_expr(root, grouped_rel, expr) && !partial)
 				fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
 			else
 				fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
@@ -6628,6 +6647,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)
@@ -6644,6 +6664,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;
@@ -6684,7 +6708,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;
@@ -6704,7 +6729,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))
 		return;
 
 	/*
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index 02c1152319..c60f3f5465 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -86,6 +86,15 @@ typedef struct PgFdwRelationInfo
 	ForeignServer *server;
 	UserMapping *user;			/* only set in use_remote_estimate mode */
 
+	/* for partial aggregate pushdown */
+	bool		check_partial_aggregate_support;
+
+	/*
+	 * if remoteversion is zero, then that means the remote server version is
+	 * unacquired.
+	 */
+	int			remoteversion;
+
 	int			fetch_size;		/* fetch size for this remote table */
 
 	/*
@@ -110,6 +119,7 @@ typedef struct PgFdwRelationInfo
 
 	/* Grouping information */
 	List	   *grouped_tlist;
+	List	   *group_clause;
 
 	/* Subquery information */
 	bool		make_outerrel_subquery; /* do we deparse outerrel as a
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index a303bfb322..6419f9d8b9 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -2989,16 +2989,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.1');
+
+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,9 @@ CREATE FOREIGN TABLE fpagg_tab_p2 PARTITION OF pagg_tab FOR VALUES FROM (10) TO
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -3028,10 +3046,289 @@ EXPLAIN (VERBOSE, COSTS OFF)
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are unsafe to push down when there is a having clause
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+
+-- 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;
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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;
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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;
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+
+-- 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 e09adb45e4..de27544573 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="partial-aggregate-pushdown"/> 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..3a88cac3c1 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -451,6 +451,44 @@ OPTIONS (ADD password_required 'false');
 
   </sect3>
 
+  <sect3 id="postgres-fdw-options-partial-aggregate-pushdown">
+   <title>Partial Aggregate Pushdown Options</title>
+
+   <para>
+    By default, <filename>postgres_fdw</filename> assumes that for each built-in aggregate
+    function, the partial aggregate function is defined on the remote server. See
+    <xref linkend="partial-aggregate-pushdown"/> for the definition of the partial aggregate
+    function. This may be overridden using the following option:
+   </para>
+
+   <variablelist>
+
+    <varlistentry>
+     <term><literal>check_partial_aggregate_support</literal> (<type>boolean</type>)</term>
+     <listitem>
+      <para>
+       If this option is false, <filename>postgres_fdw</filename> assumes
+       that for each built-in aggregate function,
+       the partial aggregate function is defined on the remote server
+       without checking the remote server version.
+       If this option is true, during query planning,
+       <filename>postgres_fdw</filename> connects to the remote server
+       and checks if the remote server version is older than the local server version.
+       If so,
+       <filename>postgres_fdw</filename>
+       assumes that for each built-in aggregate function, the partial aggregate function is not defined
+       on the remote server unless the partial aggregate function and the aggregate
+       function match.
+       Otherwise <filename>postgres_fdw</filename> assumes that for each built-in aggregate function,
+       the partial aggregate function is defined on the remote server.
+       The default is <literal>false</literal>.
+      </para>
+     </listitem>
+    </varlistentry>
+
+   </variablelist>
+  </sect3>
+
   <sect3 id="postgres-fdw-options-asynchronous-execution">
    <title>Asynchronous Execution Options</title>
 
@@ -929,13 +967,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 +1006,68 @@ postgres=# SELECT postgres_fdw_disconnect_all();
   </para>
  </sect2>
 
+ <sect2 id="partial-aggregate-pushdown">
+  <title>Partial aggregate pushdown</title>
+  <para>
+   Partial aggregate pushdown is an optimization for a query that contains aggregate
+   expressions for a partitioned table across one or more remote servers. If the multiple
+   conditions are true for a remote server, an aggregate expression is sent to that
+   remote server and the aggregate is a partial aggregate on the local server.
+   The conditions under which this sending is active are as follows.
+   <itemizedlist spacing="compact">
+     <listitem>
+      <para>
+       An aggregate function, called the partial aggregate function for partial aggregate
+       that corresponding to the aggregate expression, is defined on the local server and
+       the remote server.
+      </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>
+       The aggregate expressions in the query don't contain
+       a <literal>DISTINCT</literal> or <literal>ORDER BY</literal> clause.
+      </para>
+     </listitem>
+    </itemizedlist>
+   When this sending is active for aggregate expressions, any aggregate
+   function is replaced by the partial aggregate function in the remote query.
+   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.
+   By default, <filename>postgres_fdw</filename> assumes that for each built-in
+   aggregate function, the partial aggregate function is defined on the local server
+   and the remote server.
+   If <literal>check_partial_aggregate_support</literal> is true and the remote server version is
+   older than the local server version, <filename>postgres_fdw</filename> does not assume
+   that the partial aggregate function is on the remote server unless
+   the partial aggregate function and the aggregate function match.
+   <filename>postgres_fdw</filename> assumes that a user-defined aggregate
+   function has the partial aggregate function on the remote server
+   only if both belong to an extension that is listed in the foreign server's
+   extensions option.
+  </para>
+ </sect2>
+
  <sect2 id="postgres-fdw-remote-query-execution-environment">
   <title>Remote Query Execution Environment</title>
 
@@ -1031,14 +1133,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 partial aggregate pushdown feature.
+   When using the partial aggregate pushdown feature,
+   the partial aggregate function of the aggregate function is sent to
+   the remote server if the conditions in
+   <xref linkend="partial-aggregate-pushdown"/> are met.
+   A limitation is that by default, <filename>postgres_fdw</filename> assumes that
+   for each built-in aggregate function, the partial aggregate function is defined
+   on the remote server.
+   This limitation might cause <quote>function does not exist</quote>
+   or a similar error if the remote server version is older than the local server version.
+   This problem can be worked around by setting
+   <literal>check_partial_aggregate_support</literal> to <literal>true</literal>
+   or similar query rewriting as above.
   </para>
  </sect2>
 
diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml
index 222e0aa5c9..2875734dea 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>
@@ -246,6 +248,18 @@ CREATE [ OR REPLACE ] AGGREGATE <replaceable class="parameter">name</replaceable
    marked <literal>PARALLEL SAFE</literal> to enable parallel aggregation.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize a query containing aggregate
+   expressions for a partitioned table across one or more remote servers.
+   This optimization sends an aggregate expression to a remote server and the
+   aggregate is a partial aggregate on the local server. This optimization
+   requires an aggregate function called a <firstterm>partial aggregate
+   function</firstterm>. See
+   <xref linkend="partial-aggregate-pushdown"/> for the definition of
+   partial aggregate function. <literal>AGGPARTIALFUNC</literal> is a parameter
+   of the partial aggregate function.
+  </para>
+
   <para>
    Aggregates that behave like <function>MIN</function> or <function>MAX</function> can
    sometimes be optimized by looking into an index instead of scanning every
@@ -653,6 +667,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..607c859974 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,18 @@ SELECT percentile_disc(0.5) WITHIN GROUP (ORDER BY income) FROM households;
    parallel-safety markings on its support functions are not consulted.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize a query containing
+   aggregate expressions for a partitioned table across one or more
+   remote servers. This optimization sends an aggregate expression to
+   a remote server and the aggregate is a partial aggregate on the
+   local server. This optimization requires an aggregate function
+   called the <firstterm>partial aggregate function</firstterm>.
+   See
+   <xref linkend="partial-aggregate-pushdown"/> 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/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 44efb1f4eb..1308688646 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -206,7 +206,7 @@ static PathTarget *make_group_input_target(PlannerInfo *root,
 										   PathTarget *final_target);
 static PathTarget *make_partial_grouping_target(PlannerInfo *root,
 												PathTarget *grouping_target,
-												Node *havingQual);
+												GroupPathExtraData *extra);
 static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
 static void optimize_window_clauses(PlannerInfo *root,
 									WindowFuncLists *wflists);
@@ -5420,6 +5420,99 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
 	return set_pathtarget_cost_width(root, input_target);
 }
 
+/*
+ * setGroupClausePartial
+ *	  Generate a groupClause and a pathtarget for partial aggregate
+ *	  pushdown by FDW and set them to GroupPathExtraData.
+ */
+static void
+setGroupClausePartial(PathTarget *partial_target, List *non_group_exprs,
+					  List *groupClause, GroupPathExtraData *extra)
+{
+	int			exprno,
+				refno;
+	ListCell   *lc;
+	Index		maxRef = 0;
+	List	   *exprs_processed = NIL;
+	int			exprs_num = 0;
+
+	foreach(lc, groupClause)
+	{
+		SortGroupClause *sgc = (SortGroupClause *) lfirst(lc);
+
+		if (sgc->tleSortGroupRef > maxRef)
+			maxRef = sgc->tleSortGroupRef;
+	}
+	maxRef++;
+
+	extra->groupClausePartialFdw = list_copy_deep(groupClause);
+	extra->partial_target_fdw = copy_pathtarget(partial_target);
+
+	if (partial_target->exprs)
+		exprs_num = partial_target->exprs->length;
+
+	foreach(lc, non_group_exprs)
+	{
+		Expr	   *expr = (Expr *) lfirst(lc);
+
+		refno = -1;
+		if (list_member(exprs_processed, expr) ||
+			(!IsA(expr, Var) && !IsA(expr, PlaceHolderVar)))
+			continue;
+		exprs_processed = lappend(exprs_processed, expr);
+		for (exprno = 0; exprno < exprs_num; exprno++)
+		{
+			Expr	   *target_expr = (Expr *) list_nth(partial_target->exprs, exprno);
+
+			if (equal(target_expr, expr))
+			{
+				refno = exprno;
+				break;
+			}
+		}
+		if (refno < 0)
+		{
+			SortGroupClause *grpcl = makeNode(SortGroupClause);
+
+			grpcl->tleSortGroupRef = maxRef++;
+			extra->groupClausePartialFdw = lappend(extra->groupClausePartialFdw, grpcl);
+			add_column_to_pathtarget(extra->partial_target_fdw, expr, grpcl->tleSortGroupRef);
+		}
+	}
+}
+
+/*
+ * adjustAggrefForPartial
+ * Adjust Aggrefs to put them in partial mode
+ */
+static void
+adjustAggrefForPartial(List *exprs)
+{
+	ListCell   *lc;
+
+	foreach(lc, exprs)
+	{
+		Aggref	   *aggref = (Aggref *) lfirst(lc);
+
+		if (IsA(aggref, Aggref))
+		{
+			Aggref	   *newaggref;
+
+			/*
+			 * We shouldn't need to copy the substructure of the Aggref node,
+			 * but flat-copy the node itself to avoid damaging other trees.
+			 */
+			newaggref = makeNode(Aggref);
+			memcpy(newaggref, aggref, sizeof(Aggref));
+
+			/* For now, assume serialization is required */
+			mark_partial_aggref(newaggref, AGGSPLIT_INITIAL_SERIAL);
+
+			lfirst(lc) = newaggref;
+		}
+	}
+}
+
 /*
  * make_partial_grouping_target
  *	  Generate appropriate PathTarget for output of partial aggregate
@@ -5435,11 +5528,17 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
  *
  * grouping_target is the tlist to be emitted by the topmost aggregation step.
  * havingQual represents the HAVING clause.
+ *
+ * Adding these Vars and PlaceHolderVars to PathTarget,
+ * FDW cannot deparse this by the original List of SortGroupClauses.
+ * So, before this adding process,
+ * setGroupClausePartial generates another Pathtarget and another
+ * List of SortGroupClauses for FDW.
  */
 static PathTarget *
 make_partial_grouping_target(PlannerInfo *root,
 							 PathTarget *grouping_target,
-							 Node *havingQual)
+							 GroupPathExtraData *extra)
 {
 	PathTarget *partial_target;
 	List	   *non_group_cols;
@@ -5481,8 +5580,8 @@ make_partial_grouping_target(PlannerInfo *root,
 	/*
 	 * If there's a HAVING clause, we'll need the Vars/Aggrefs it uses, too.
 	 */
-	if (havingQual)
-		non_group_cols = lappend(non_group_cols, havingQual);
+	if (extra->havingQual)
+		non_group_cols = lappend(non_group_cols, extra->havingQual);
 
 	/*
 	 * Pull out all the Vars, PlaceHolderVars, and Aggrefs mentioned in
@@ -5495,35 +5594,17 @@ make_partial_grouping_target(PlannerInfo *root,
 									  PVC_INCLUDE_AGGREGATES |
 									  PVC_RECURSE_WINDOWFUNCS |
 									  PVC_INCLUDE_PLACEHOLDERS);
-
+	setGroupClausePartial(partial_target, non_group_exprs, root->parse->groupClause, extra);
 	add_new_columns_to_pathtarget(partial_target, non_group_exprs);
+	add_new_columns_to_pathtarget(extra->partial_target_fdw, non_group_exprs);
 
 	/*
 	 * Adjust Aggrefs to put them in partial mode.  At this point all Aggrefs
 	 * are at the top level of the target list, so we can just scan the list
 	 * rather than recursing through the expression trees.
 	 */
-	foreach(lc, partial_target->exprs)
-	{
-		Aggref	   *aggref = (Aggref *) lfirst(lc);
-
-		if (IsA(aggref, Aggref))
-		{
-			Aggref	   *newaggref;
-
-			/*
-			 * We shouldn't need to copy the substructure of the Aggref node,
-			 * but flat-copy the node itself to avoid damaging other trees.
-			 */
-			newaggref = makeNode(Aggref);
-			memcpy(newaggref, aggref, sizeof(Aggref));
-
-			/* For now, assume serialization is required */
-			mark_partial_aggref(newaggref, AGGSPLIT_INITIAL_SERIAL);
-
-			lfirst(lc) = newaggref;
-		}
-	}
+	adjustAggrefForPartial(partial_target->exprs);
+	adjustAggrefForPartial(extra->partial_target_fdw->exprs);
 
 	/* clean up cruft */
 	list_free(non_group_exprs);
@@ -7153,7 +7234,7 @@ create_partial_grouping_paths(PlannerInfo *root,
 	 */
 	partially_grouped_rel->reltarget =
 		make_partial_grouping_target(root, grouped_rel->reltarget,
-									 extra->havingQual);
+									 extra);
 
 	if (!extra->partial_costs_set)
 	{
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index f7b6176692..a65f0be079 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -13904,6 +13904,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;
@@ -13986,11 +13987,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 "
@@ -14016,6 +14017,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"));
@@ -14105,6 +14107,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 085149e258..3e4f8331ee 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 f0b7b9cbd8..0622b2aee4 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',
@@ -5001,6 +5008,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',
@@ -5013,6 +5024,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',
@@ -6597,36 +6612,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' },
@@ -6649,6 +6689,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' },
@@ -6807,153 +6850,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',
@@ -6964,52 +7115,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/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 5702fbba60..85ef273a41 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -3243,6 +3243,9 @@ typedef enum
  * havingQual gives list of quals to be applied after aggregation.
  * targetList gives list of columns to be projected.
  * patype is the type of partitionwise aggregation that is being performed.
+ * groupClausePartialFdw is List of SortGroupClauses for partial aggregate
+ * 		pushdown by FDW
+ * partial_target_fdw is PathTarget for partial aggregate pushdown by FDW
  */
 typedef struct
 {
@@ -3257,6 +3260,8 @@ typedef struct
 	Node	   *havingQual;
 	List	   *targetList;
 	PartitionwiseAggregateType patype;
+	List	   *groupClausePartialFdw;
+	PathTarget *partial_target_fdw;
 } GroupPathExtraData;
 
 /*
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] 38+ messages in thread

* Re: Partial aggregates pushdown
@ 2023-09-28 06:15  Alexander Pyhalov <[email protected]>
  parent: [email protected] <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

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

[email protected] писал 2023-09-28 07:40:

>> I'm not sure that I like this mechanics of adding sort group clauses -
>> it seems we do in core additional work, which is of use only for
>> one extension, but at least it seems to be working.
> We cannot deparse the original sort group clauses and pathtarget
> when performing partial aggreggate pushdown by any FDWs.
> So I think the additional sort group clauses and pathtarget are
> needed by any FDWs, not only postgres_fdw.
> 

Hi.
It seems to me that *fdw postfixes don't clarify things, but just make 
naming more ugly.

+ * Adding these Vars and PlaceHolderVars to PathTarget,
+ * FDW cannot deparse this by the original List of SortGroupClauses.
+ * So, before this adding process,
+ * setGroupClausePartial generates another Pathtarget and another
+ * List of SortGroupClauses for FDW.

It seems that something like:

/*
  * Modified PathTarget cannot be used by FDW as-is to deparse this 
statement.
  * So, before modifying PathTarget, setGroupClausePartial generates
  * another Pathtarget and another list List of SortGroupClauses
  * to make deparsing possible.
  */

sounds better.

-- 
Best regards,
Alexander Pyhalov,
Postgres Professional






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

* RE: Partial aggregates pushdown
@ 2023-09-28 07:21  [email protected] <[email protected]>
  parent: Alexander Pyhalov <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

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

Hi Mr.Pyhalov.

Alexander Pyhalov <[email protected]>
Thursday, September 28, 2023 3:16 PM
> It seems to me that *fdw postfixes don't clarify things, but just make naming more ugly.
I have removed *fdw postfixes.

> + * Adding these Vars and PlaceHolderVars to PathTarget,
> + * FDW cannot deparse this by the original List of SortGroupClauses.
> + * So, before this adding process,
> + * setGroupClausePartial generates another Pathtarget and another
> + * List of SortGroupClauses for FDW.
> 
> It seems that something like:
> 
> /*
>   * Modified PathTarget cannot be used by FDW as-is to deparse this statement.
>   * So, before modifying PathTarget, setGroupClausePartial generates
>   * another Pathtarget and another list List of SortGroupClauses
>   * to make deparsing possible.
>   */
> 
> sounds better.
Thank you for the suggested modifications. I have modified it according to your suggestion.

Sincerely yours,
Yuuki Fujii

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


Attachments:

  [application/octet-stream] 0001-Partial-aggregates-push-down-v29.patch (266.8K, ../../OS3PR01MB66601F4809974F6A8275131195C1A@OS3PR01MB6660.jpnprd01.prod.outlook.com/2-0001-Partial-aggregates-push-down-v29.patch)
  download | inline diff:
From e8bd6a6dc187d6ce59130b61171b117a0eb5b0e7 Mon Sep 17 00:00:00 2001
From: Yuki Fujii <[email protected]>
Date: Thu, 28 Sep 2023 11:49:49 -0400
Subject: [PATCH] Partial aggregates push down v29

---
 contrib/postgres_fdw/deparse.c                | 103 ++-
 .../postgres_fdw/expected/postgres_fdw.out    | 868 +++++++++++++++++-
 contrib/postgres_fdw/option.c                 |   4 +-
 contrib/postgres_fdw/postgres_fdw.c           |  47 +-
 contrib/postgres_fdw/postgres_fdw.h           |  10 +
 contrib/postgres_fdw/sql/postgres_fdw.sql     | 311 ++++++-
 doc/src/sgml/catalogs.sgml                    |  13 +
 doc/src/sgml/postgres-fdw.sgml                | 137 ++-
 doc/src/sgml/ref/create_aggregate.sgml        |  42 +
 doc/src/sgml/xaggr.sgml                       |  15 +-
 src/backend/catalog/pg_aggregate.c            | 101 ++
 src/backend/commands/aggregatecmds.c          |   4 +
 src/backend/optimizer/plan/planner.c          | 133 ++-
 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 ++++
 src/include/nodes/pathnodes.h                 |   5 +
 .../regress/expected/create_aggregate.out     | 184 ++++
 src/test/regress/expected/oidjoins.out        |   1 +
 src/test/regress/sql/create_aggregate.sql     | 183 ++++
 21 files changed, 2828 insertions(+), 222 deletions(-)

diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 09d6dd60dd..e957611879 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 */
@@ -3723,6 +3744,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	Query	   *query = context->root->parse;
 	ListCell   *lc;
 	bool		first = true;
+	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) context->foreignrel->fdw_private;
 
 	/* Nothing to be done, if there's no GROUP BY clause in the query. */
 	if (!query->groupClause)
@@ -3743,7 +3765,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	 * to empty, and in any case the redundancy situation on the remote might
 	 * be different than what we think here.
 	 */
-	foreach(lc, query->groupClause)
+	foreach(lc, fpinfo->group_clause)
 	{
 		SortGroupClause *grp = (SortGroupClause *) lfirst(lc);
 
@@ -4047,3 +4069,68 @@ get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
 	/* Shouldn't get here */
 	elog(ERROR, "unexpected expression in subquery output");
 }
+
+/*
+ * Check that a buit-in aggpartialfunc exists on the remote server. If
+ * check_partial_aggregate_support is false, we assume the partial aggregate
+ * function exsits on the remote server. Otherwise we assume the partial
+ * aggregate function exsits on the remote server only if the remote server
+ * version is not less than the local server version.
+ */
+static bool
+is_builtin_aggpartialfunc_shippable(Oid aggpartialfn, PgFdwRelationInfo *fpinfo)
+{
+	bool		shippable = true;
+
+	if (fpinfo->check_partial_aggregate_support)
+	{
+		if (fpinfo->remoteversion == 0)
+		{
+			PGconn	   *conn = GetConnection(fpinfo->user, false, NULL);
+
+			fpinfo->remoteversion = PQserverVersion(conn);
+		}
+		if (fpinfo->remoteversion < PG_VERSION_NUM)
+			shippable = false;
+	}
+	return shippable;
+}
+
+/*
+ * 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 exists on the remote server
+ */
+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_builtin(aggform->aggpartialfn))
+		partial_agg_ok = is_builtin_aggpartialfunc_shippable(
+															 aggform->aggpartialfn, fpinfo);
+	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 852b5b4707..bacfeb258c 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -9742,18 +9742,35 @@ 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.1');
+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');
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -9807,8 +9824,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
@@ -9819,21 +9836,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;
@@ -9847,28 +9864,837 @@ SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
  21 |   100
 (6 rows)
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are unsafe to push down when there is a having clause
+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                            
------------------------------------------------------------------
- Sort
-   Sort Key: pagg_tab.b
-   ->  Finalize HashAggregate
-         Group Key: pagg_tab.b
-         Filter: (sum(pagg_tab.a) < 700)
+                                                                 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 fpagg_tab_p1 pagg_tab
+                     ->  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 fpagg_tab_p2 pagg_tab_1
+                     ->  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 fpagg_tab_p3 pagg_tab_2
+                     ->  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)
+
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
+ b  |         avg         | max | count 
+----+---------------------+-----+-------
+  0 | 10.0000000000000000 |  20 |    60
+  1 | 11.0000000000000000 |  21 |    60
+ 10 | 10.0000000000000000 |  20 |    60
+ 11 | 11.0000000000000000 |  21 |    60
+ 20 | 10.0000000000000000 |  20 |    60
+ 21 | 11.0000000000000000 |  21 |    60
+ 30 | 10.0000000000000000 |  20 |    60
+ 31 | 11.0000000000000000 |  21 |    60
+ 40 | 10.0000000000000000 |  20 |    60
+ 41 | 11.0000000000000000 |  21 |    60
+(10 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+                                                                  QUERY PLAN                                                                  
+----------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: (avg(pagg_tab.a)), (max(pagg_tab.a)), (count(*)), ((((pagg_tab.b / 2)))::numeric), ((pagg_tab.b / 2))
+   Sort Key: ((((pagg_tab.b / 2)))::numeric)
+   ->  Finalize HashAggregate
+         Output: avg(pagg_tab.a), max(pagg_tab.a), count(*), (((pagg_tab.b / 2)))::numeric, ((pagg_tab.b / 2))
+         Group Key: ((pagg_tab.b / 2))
+         ->  Append
+               ->  Foreign Scan
+                     Output: ((pagg_tab.b / 2)), (PARTIAL avg(pagg_tab.a)), (PARTIAL max(pagg_tab.a)), (PARTIAL count(*)), pagg_tab.b
+                     Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p1 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_1.b / 2)), (PARTIAL avg(pagg_tab_1.a)), (PARTIAL max(pagg_tab_1.a)), (PARTIAL count(*)), pagg_tab_1.b
+                     Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p2 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_2.b / 2)), (PARTIAL avg(pagg_tab_2.a)), (PARTIAL max(pagg_tab_2.a)), (PARTIAL count(*)), pagg_tab_2.b
+                     Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p3 GROUP BY 1, 2
+(19 rows)
+
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+         avg         | max | count | numeric 
+---------------------+-----+-------+---------
+ 10.5000000000000000 |  21 |   120 |       0
+ 12.5000000000000000 |  23 |   120 |       1
+ 14.5000000000000000 |  25 |   120 |       2
+ 16.5000000000000000 |  27 |   120 |       3
+ 18.5000000000000000 |  29 |   120 |       4
+ 10.5000000000000000 |  21 |   120 |       5
+ 12.5000000000000000 |  23 |   120 |       6
+ 14.5000000000000000 |  25 |   120 |       7
+ 16.5000000000000000 |  27 |   120 |       8
+ 18.5000000000000000 |  29 |   120 |       9
+ 10.5000000000000000 |  21 |   120 |      10
+ 12.5000000000000000 |  23 |   120 |      11
+ 14.5000000000000000 |  25 |   120 |      12
+ 16.5000000000000000 |  27 |   120 |      13
+ 18.5000000000000000 |  29 |   120 |      14
+ 10.5000000000000000 |  21 |   120 |      15
+ 12.5000000000000000 |  23 |   120 |      16
+ 14.5000000000000000 |  25 |   120 |      17
+ 16.5000000000000000 |  27 |   120 |      18
+ 18.5000000000000000 |  29 |   120 |      19
+ 10.5000000000000000 |  21 |   120 |      20
+ 12.5000000000000000 |  23 |   120 |      21
+ 14.5000000000000000 |  25 |   120 |      22
+ 16.5000000000000000 |  27 |   120 |      23
+ 18.5000000000000000 |  29 |   120 |      24
+(25 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)
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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)
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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)
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+                                                                           QUERY PLAN                                                                           
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: avg(t1.b), avg((t1.b)::bigint)
+   ->  Append
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1.b)), (PARTIAL avg((t1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p1 t1) INNER JOIN (public.fpagg_tab_p1 t2))
+               Remote SQL: SELECT avg_p_int4(r4.b), avg_p_int8(r4.b::bigint) FROM (public.pagg_tab_p1 r4 INNER JOIN public.pagg_tab_p1 r7 ON (((r4.a = r7.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_1.b)), (PARTIAL avg((t1_1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p2 t1_1) INNER JOIN (public.fpagg_tab_p2 t2_1))
+               Remote SQL: SELECT avg_p_int4(r5.b), avg_p_int8(r5.b::bigint) FROM (public.pagg_tab_p2 r5 INNER JOIN public.pagg_tab_p2 r8 ON (((r5.a = r8.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_2.b)), (PARTIAL avg((t1_2.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p3 t1_2) INNER JOIN (public.fpagg_tab_p3 t2_2))
+               Remote SQL: SELECT avg_p_int4(r6.b), avg_p_int8(r6.b::bigint) FROM (public.pagg_tab_p3 r6 INNER JOIN public.pagg_tab_p3 r9 ON (((r6.a = r9.a))))
 (15 rows)
 
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+         avg         |         avg         
+---------------------+---------------------
+ 24.5000000000000000 | 24.5000000000000000
+(1 row)
+
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+-- 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/option.c b/contrib/postgres_fdw/option.c
index 8c822f4ef9..67e15c8bb9 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -126,7 +126,8 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
 			strcmp(def->defname, "async_capable") == 0 ||
 			strcmp(def->defname, "parallel_commit") == 0 ||
 			strcmp(def->defname, "parallel_abort") == 0 ||
-			strcmp(def->defname, "keep_connections") == 0)
+			strcmp(def->defname, "keep_connections") == 0 ||
+			strcmp(def->defname, "check_partial_aggregate_support") == 0)
 		{
 			/* these accept only boolean values */
 			(void) defGetBoolean(def);
@@ -268,6 +269,7 @@ InitPgFdwOptions(void)
 		/* batch_size is available on both server and table */
 		{"batch_size", ForeignServerRelationId, false},
 		{"batch_size", ForeignTableRelationId, false},
+		{"check_partial_aggregate_support", ForeignServerRelationId, false},
 		/* async_capable is available on both server and table */
 		{"async_capable", ForeignServerRelationId, false},
 		{"async_capable", ForeignTableRelationId, false},
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c5cada55fb..8dc6ec12a3 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -36,6 +36,7 @@
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
+#include "optimizer/planner.h"
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
@@ -519,7 +520,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);
+								GroupPathExtraData *extra);
 static List *get_useful_pathkeys_for_relation(PlannerInfo *root,
 											  RelOptInfo *rel);
 static List *get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel);
@@ -650,6 +651,8 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	fpinfo->fdw_tuple_cost = DEFAULT_FDW_TUPLE_COST;
 	fpinfo->shippable_extensions = NIL;
 	fpinfo->fetch_size = 100;
+	fpinfo->check_partial_aggregate_support = false;
+	fpinfo->remoteversion = 0;
 	fpinfo->async_capable = false;
 
 	apply_server_options(fpinfo);
@@ -661,7 +664,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	 * should match what ExecCheckPermissions() does.  If we fail due to lack
 	 * of permissions, the query would have failed at runtime anyway.
 	 */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
 		Oid			userid;
 
@@ -5946,9 +5949,10 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
 	fpinfo->pushdown_safe = true;
 
 	/* Get user mapping */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
-		if (fpinfo_o->use_remote_estimate)
+		if (fpinfo_o->use_remote_estimate ||
+			fpinfo_o->check_partial_aggregate_support)
 			fpinfo->user = fpinfo_o->user;
 		else
 			fpinfo->user = fpinfo_i->user;
@@ -6127,6 +6131,8 @@ apply_server_options(PgFdwRelationInfo *fpinfo)
 				ExtractExtensionList(defGetString(def), false);
 		else if (strcmp(def->defname, "fetch_size") == 0)
 			(void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
+		else if (strcmp(def->defname, "check_partial_aggregate_support") == 0)
+			fpinfo->check_partial_aggregate_support = defGetBoolean(def);
 		else if (strcmp(def->defname, "async_capable") == 0)
 			fpinfo->async_capable = defGetBoolean(def);
 	}
@@ -6186,6 +6192,8 @@ merge_fdw_options(PgFdwRelationInfo *fpinfo,
 	fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
 	fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
 	fpinfo->fetch_size = fpinfo_o->fetch_size;
+	fpinfo->check_partial_aggregate_support = fpinfo_o->check_partial_aggregate_support;
+	fpinfo->remoteversion = fpinfo_o->remoteversion;
 	fpinfo->async_capable = fpinfo_o->async_capable;
 
 	/* Merge the table level options from either side of the join. */
@@ -6366,7 +6374,7 @@ postgresGetForeignJoinPaths(PlannerInfo *root,
  */
 static bool
 foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
-					Node *havingQual)
+					GroupPathExtraData *extra)
 {
 	Query	   *query = root->parse;
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
@@ -6375,11 +6383,16 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	ListCell   *lc;
 	int			i;
 	List	   *tlist = NIL;
+	bool		partial = extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL;
 
 	/* We currently don't support pushing Grouping Sets. */
 	if (query->groupingSets)
 		return false;
 
+	/* It's unsafe to push having statements with partial aggregates */
+	if ((extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL) && extra->havingQual)
+		return false;
+
 	/* Get the fpinfo of the underlying scan relation. */
 	ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
 
@@ -6408,6 +6421,12 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * a node, as long as it's not at top level; then no match is possible.
 	 */
 	i = 0;
+	fpinfo->group_clause = query->groupClause;
+	if (partial)
+	{
+		fpinfo->group_clause = extra->groupClausePartial;
+		grouping_target = extra->partial_target;
+	}
 	foreach(lc, grouping_target->exprs)
 	{
 		Expr	   *expr = (Expr *) lfirst(lc);
@@ -6419,7 +6438,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 		 * check the whole GROUP BY clause not just processed_groupClause,
 		 * because we will ship all of it, cf. appendGroupByClause.
 		 */
-		if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
+		if (sgref && get_sortgroupref_clause_noerr(sgref, fpinfo->group_clause))
 		{
 			TargetEntry *tle;
 
@@ -6505,9 +6524,9 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * Classify the pushable and non-pushable HAVING clauses and save them in
 	 * remote_conds and local_conds of the grouped rel's fpinfo.
 	 */
-	if (havingQual)
+	if (extra->havingQual)
 	{
-		foreach(lc, (List *) havingQual)
+		foreach(lc, (List *) extra->havingQual)
 		{
 			Expr	   *expr = (Expr *) lfirst(lc);
 			RestrictInfo *rinfo;
@@ -6527,7 +6546,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 									  grouped_rel->relids,
 									  NULL,
 									  NULL);
-			if (is_foreign_expr(root, grouped_rel, expr))
+			if (is_foreign_expr(root, grouped_rel, expr) && !partial)
 				fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
 			else
 				fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
@@ -6621,6 +6640,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 +6657,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 +6701,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 +6722,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))
 		return;
 
 	/*
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index 02c1152319..c60f3f5465 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -86,6 +86,15 @@ typedef struct PgFdwRelationInfo
 	ForeignServer *server;
 	UserMapping *user;			/* only set in use_remote_estimate mode */
 
+	/* for partial aggregate pushdown */
+	bool		check_partial_aggregate_support;
+
+	/*
+	 * if remoteversion is zero, then that means the remote server version is
+	 * unacquired.
+	 */
+	int			remoteversion;
+
 	int			fetch_size;		/* fetch size for this remote table */
 
 	/*
@@ -110,6 +119,7 @@ typedef struct PgFdwRelationInfo
 
 	/* Grouping information */
 	List	   *grouped_tlist;
+	List	   *group_clause;
 
 	/* Subquery information */
 	bool		make_outerrel_subquery; /* do we deparse outerrel as a
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 2fe8abc7af..7e78809bad 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -2986,16 +2986,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.1');
+
+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');
@@ -3003,6 +3018,9 @@ CREATE FOREIGN TABLE fpagg_tab_p2 PARTITION OF pagg_tab FOR VALUES FROM (10) TO
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -3025,10 +3043,289 @@ EXPLAIN (VERBOSE, COSTS OFF)
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are unsafe to push down when there is a having clause
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+
+-- 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;
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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;
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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;
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+
+-- 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 307ad88b50..9202775944 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="partial-aggregate-pushdown"/> 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..3a88cac3c1 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -451,6 +451,44 @@ OPTIONS (ADD password_required 'false');
 
   </sect3>
 
+  <sect3 id="postgres-fdw-options-partial-aggregate-pushdown">
+   <title>Partial Aggregate Pushdown Options</title>
+
+   <para>
+    By default, <filename>postgres_fdw</filename> assumes that for each built-in aggregate
+    function, the partial aggregate function is defined on the remote server. See
+    <xref linkend="partial-aggregate-pushdown"/> for the definition of the partial aggregate
+    function. This may be overridden using the following option:
+   </para>
+
+   <variablelist>
+
+    <varlistentry>
+     <term><literal>check_partial_aggregate_support</literal> (<type>boolean</type>)</term>
+     <listitem>
+      <para>
+       If this option is false, <filename>postgres_fdw</filename> assumes
+       that for each built-in aggregate function,
+       the partial aggregate function is defined on the remote server
+       without checking the remote server version.
+       If this option is true, during query planning,
+       <filename>postgres_fdw</filename> connects to the remote server
+       and checks if the remote server version is older than the local server version.
+       If so,
+       <filename>postgres_fdw</filename>
+       assumes that for each built-in aggregate function, the partial aggregate function is not defined
+       on the remote server unless the partial aggregate function and the aggregate
+       function match.
+       Otherwise <filename>postgres_fdw</filename> assumes that for each built-in aggregate function,
+       the partial aggregate function is defined on the remote server.
+       The default is <literal>false</literal>.
+      </para>
+     </listitem>
+    </varlistentry>
+
+   </variablelist>
+  </sect3>
+
   <sect3 id="postgres-fdw-options-asynchronous-execution">
    <title>Asynchronous Execution Options</title>
 
@@ -929,13 +967,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 +1006,68 @@ postgres=# SELECT postgres_fdw_disconnect_all();
   </para>
  </sect2>
 
+ <sect2 id="partial-aggregate-pushdown">
+  <title>Partial aggregate pushdown</title>
+  <para>
+   Partial aggregate pushdown is an optimization for a query that contains aggregate
+   expressions for a partitioned table across one or more remote servers. If the multiple
+   conditions are true for a remote server, an aggregate expression is sent to that
+   remote server and the aggregate is a partial aggregate on the local server.
+   The conditions under which this sending is active are as follows.
+   <itemizedlist spacing="compact">
+     <listitem>
+      <para>
+       An aggregate function, called the partial aggregate function for partial aggregate
+       that corresponding to the aggregate expression, is defined on the local server and
+       the remote server.
+      </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>
+       The aggregate expressions in the query don't contain
+       a <literal>DISTINCT</literal> or <literal>ORDER BY</literal> clause.
+      </para>
+     </listitem>
+    </itemizedlist>
+   When this sending is active for aggregate expressions, any aggregate
+   function is replaced by the partial aggregate function in the remote query.
+   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.
+   By default, <filename>postgres_fdw</filename> assumes that for each built-in
+   aggregate function, the partial aggregate function is defined on the local server
+   and the remote server.
+   If <literal>check_partial_aggregate_support</literal> is true and the remote server version is
+   older than the local server version, <filename>postgres_fdw</filename> does not assume
+   that the partial aggregate function is on the remote server unless
+   the partial aggregate function and the aggregate function match.
+   <filename>postgres_fdw</filename> assumes that a user-defined aggregate
+   function has the partial aggregate function on the remote server
+   only if both belong to an extension that is listed in the foreign server's
+   extensions option.
+  </para>
+ </sect2>
+
  <sect2 id="postgres-fdw-remote-query-execution-environment">
   <title>Remote Query Execution Environment</title>
 
@@ -1031,14 +1133,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 partial aggregate pushdown feature.
+   When using the partial aggregate pushdown feature,
+   the partial aggregate function of the aggregate function is sent to
+   the remote server if the conditions in
+   <xref linkend="partial-aggregate-pushdown"/> are met.
+   A limitation is that by default, <filename>postgres_fdw</filename> assumes that
+   for each built-in aggregate function, the partial aggregate function is defined
+   on the remote server.
+   This limitation might cause <quote>function does not exist</quote>
+   or a similar error if the remote server version is older than the local server version.
+   This problem can be worked around by setting
+   <literal>check_partial_aggregate_support</literal> to <literal>true</literal>
+   or similar query rewriting as above.
   </para>
  </sect2>
 
diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml
index 222e0aa5c9..2875734dea 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>
@@ -246,6 +248,18 @@ CREATE [ OR REPLACE ] AGGREGATE <replaceable class="parameter">name</replaceable
    marked <literal>PARALLEL SAFE</literal> to enable parallel aggregation.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize a query containing aggregate
+   expressions for a partitioned table across one or more remote servers.
+   This optimization sends an aggregate expression to a remote server and the
+   aggregate is a partial aggregate on the local server. This optimization
+   requires an aggregate function called a <firstterm>partial aggregate
+   function</firstterm>. See
+   <xref linkend="partial-aggregate-pushdown"/> for the definition of
+   partial aggregate function. <literal>AGGPARTIALFUNC</literal> is a parameter
+   of the partial aggregate function.
+  </para>
+
   <para>
    Aggregates that behave like <function>MIN</function> or <function>MAX</function> can
    sometimes be optimized by looking into an index instead of scanning every
@@ -653,6 +667,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..607c859974 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,18 @@ SELECT percentile_disc(0.5) WITHIN GROUP (ORDER BY income) FROM households;
    parallel-safety markings on its support functions are not consulted.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize a query containing
+   aggregate expressions for a partitioned table across one or more
+   remote servers. This optimization sends an aggregate expression to
+   a remote server and the aggregate is a partial aggregate on the
+   local server. This optimization requires an aggregate function
+   called the <firstterm>partial aggregate function</firstterm>.
+   See
+   <xref linkend="partial-aggregate-pushdown"/> 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/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 44efb1f4eb..1b5e944f42 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -206,7 +206,7 @@ static PathTarget *make_group_input_target(PlannerInfo *root,
 										   PathTarget *final_target);
 static PathTarget *make_partial_grouping_target(PlannerInfo *root,
 												PathTarget *grouping_target,
-												Node *havingQual);
+												GroupPathExtraData *extra);
 static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
 static void optimize_window_clauses(PlannerInfo *root,
 									WindowFuncLists *wflists);
@@ -5420,6 +5420,99 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
 	return set_pathtarget_cost_width(root, input_target);
 }
 
+/*
+ * setGroupClausePartial
+ *	  Generate a groupClause and a pathtarget for partial aggregate
+ *	  pushdown by FDW and set them to GroupPathExtraData.
+ */
+static void
+setGroupClausePartial(PathTarget *partial_target, List *non_group_exprs,
+					  List *groupClause, GroupPathExtraData *extra)
+{
+	int			exprno,
+				refno;
+	ListCell   *lc;
+	Index		maxRef = 0;
+	List	   *exprs_processed = NIL;
+	int			exprs_num = 0;
+
+	foreach(lc, groupClause)
+	{
+		SortGroupClause *sgc = (SortGroupClause *) lfirst(lc);
+
+		if (sgc->tleSortGroupRef > maxRef)
+			maxRef = sgc->tleSortGroupRef;
+	}
+	maxRef++;
+
+	extra->groupClausePartial = list_copy_deep(groupClause);
+	extra->partial_target = copy_pathtarget(partial_target);
+
+	if (partial_target->exprs)
+		exprs_num = partial_target->exprs->length;
+
+	foreach(lc, non_group_exprs)
+	{
+		Expr	   *expr = (Expr *) lfirst(lc);
+
+		refno = -1;
+		if (list_member(exprs_processed, expr) ||
+			(!IsA(expr, Var) && !IsA(expr, PlaceHolderVar)))
+			continue;
+		exprs_processed = lappend(exprs_processed, expr);
+		for (exprno = 0; exprno < exprs_num; exprno++)
+		{
+			Expr	   *target_expr = (Expr *) list_nth(partial_target->exprs, exprno);
+
+			if (equal(target_expr, expr))
+			{
+				refno = exprno;
+				break;
+			}
+		}
+		if (refno < 0)
+		{
+			SortGroupClause *grpcl = makeNode(SortGroupClause);
+
+			grpcl->tleSortGroupRef = maxRef++;
+			extra->groupClausePartial = lappend(extra->groupClausePartial, grpcl);
+			add_column_to_pathtarget(extra->partial_target, expr, grpcl->tleSortGroupRef);
+		}
+	}
+}
+
+/*
+ * adjustAggrefForPartial
+ * Adjust Aggrefs to put them in partial mode
+ */
+static void
+adjustAggrefForPartial(List *exprs)
+{
+	ListCell   *lc;
+
+	foreach(lc, exprs)
+	{
+		Aggref	   *aggref = (Aggref *) lfirst(lc);
+
+		if (IsA(aggref, Aggref))
+		{
+			Aggref	   *newaggref;
+
+			/*
+			 * We shouldn't need to copy the substructure of the Aggref node,
+			 * but flat-copy the node itself to avoid damaging other trees.
+			 */
+			newaggref = makeNode(Aggref);
+			memcpy(newaggref, aggref, sizeof(Aggref));
+
+			/* For now, assume serialization is required */
+			mark_partial_aggref(newaggref, AGGSPLIT_INITIAL_SERIAL);
+
+			lfirst(lc) = newaggref;
+		}
+	}
+}
+
 /*
  * make_partial_grouping_target
  *	  Generate appropriate PathTarget for output of partial aggregate
@@ -5435,11 +5528,15 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
  *
  * grouping_target is the tlist to be emitted by the topmost aggregation step.
  * havingQual represents the HAVING clause.
+ *
+ * Modified PathTarget cannot be used by FDW as-is to deparse this statement.
+ * So, before modifying PathTarget, setGroupClausePartial generates
+ * another Pathtarget and another list of SortGroupClauses.
  */
 static PathTarget *
 make_partial_grouping_target(PlannerInfo *root,
 							 PathTarget *grouping_target,
-							 Node *havingQual)
+							 GroupPathExtraData *extra)
 {
 	PathTarget *partial_target;
 	List	   *non_group_cols;
@@ -5481,8 +5578,8 @@ make_partial_grouping_target(PlannerInfo *root,
 	/*
 	 * If there's a HAVING clause, we'll need the Vars/Aggrefs it uses, too.
 	 */
-	if (havingQual)
-		non_group_cols = lappend(non_group_cols, havingQual);
+	if (extra->havingQual)
+		non_group_cols = lappend(non_group_cols, extra->havingQual);
 
 	/*
 	 * Pull out all the Vars, PlaceHolderVars, and Aggrefs mentioned in
@@ -5495,35 +5592,17 @@ make_partial_grouping_target(PlannerInfo *root,
 									  PVC_INCLUDE_AGGREGATES |
 									  PVC_RECURSE_WINDOWFUNCS |
 									  PVC_INCLUDE_PLACEHOLDERS);
-
+	setGroupClausePartial(partial_target, non_group_exprs, root->parse->groupClause, extra);
 	add_new_columns_to_pathtarget(partial_target, non_group_exprs);
+	add_new_columns_to_pathtarget(extra->partial_target, non_group_exprs);
 
 	/*
 	 * Adjust Aggrefs to put them in partial mode.  At this point all Aggrefs
 	 * are at the top level of the target list, so we can just scan the list
 	 * rather than recursing through the expression trees.
 	 */
-	foreach(lc, partial_target->exprs)
-	{
-		Aggref	   *aggref = (Aggref *) lfirst(lc);
-
-		if (IsA(aggref, Aggref))
-		{
-			Aggref	   *newaggref;
-
-			/*
-			 * We shouldn't need to copy the substructure of the Aggref node,
-			 * but flat-copy the node itself to avoid damaging other trees.
-			 */
-			newaggref = makeNode(Aggref);
-			memcpy(newaggref, aggref, sizeof(Aggref));
-
-			/* For now, assume serialization is required */
-			mark_partial_aggref(newaggref, AGGSPLIT_INITIAL_SERIAL);
-
-			lfirst(lc) = newaggref;
-		}
-	}
+	adjustAggrefForPartial(partial_target->exprs);
+	adjustAggrefForPartial(extra->partial_target->exprs);
 
 	/* clean up cruft */
 	list_free(non_group_exprs);
@@ -7153,7 +7232,7 @@ create_partial_grouping_paths(PlannerInfo *root,
 	 */
 	partially_grouped_rel->reltarget =
 		make_partial_grouping_target(root, grouped_rel->reltarget,
-									 extra->havingQual);
+									 extra);
 
 	if (!extra->partial_costs_set)
 	{
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/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index c17b53f7ad..82795fe4a4 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -3230,6 +3230,9 @@ typedef enum
  * havingQual gives list of quals to be applied after aggregation.
  * targetList gives list of columns to be projected.
  * patype is the type of partitionwise aggregation that is being performed.
+ * groupClausePartial is List of SortGroupClauses for partial aggregate
+ * 		pushdown by FDW
+ * partial_target is PathTarget for partial aggregate pushdown by FDW
  */
 typedef struct
 {
@@ -3244,6 +3247,8 @@ typedef struct
 	Node	   *havingQual;
 	List	   *targetList;
 	PartitionwiseAggregateType patype;
+	List	   *groupClausePartial;
+	PathTarget *partial_target;
 } GroupPathExtraData;
 
 /*
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.39.3



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

* RE: Partial aggregates pushdown
@ 2023-10-18 05:22  [email protected] <[email protected]>
  parent: [email protected] <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

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

Hi hackers.

Because there is a degrade in pg_dump.c, I fixed it.

Sincerely yours,
Yuuki Fujii

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


Attachments:

  [application/octet-stream] 0001-Partial-aggregates-push-down-v30.patch (267.0K, ../../TY3PR01MB10956C71BF25436F242C9829795D5A@TY3PR01MB10956.jpnprd01.prod.outlook.com/2-0001-Partial-aggregates-push-down-v30.patch)
  download | inline diff:
From 3e38dc0597c224cace13cac2c4cbda8291108823 Mon Sep 17 00:00:00 2001
From: Yuki Fujii <[email protected]>
Date: Wed, 18 Oct 2023 13:36:43 +0900
Subject: [PATCH] Partial aggregates push down v30

---
 contrib/postgres_fdw/deparse.c                | 103 ++-
 .../postgres_fdw/expected/postgres_fdw.out    | 868 +++++++++++++++++-
 contrib/postgres_fdw/option.c                 |   4 +-
 contrib/postgres_fdw/postgres_fdw.c           |  47 +-
 contrib/postgres_fdw/postgres_fdw.h           |  10 +
 contrib/postgres_fdw/sql/postgres_fdw.sql     | 311 ++++++-
 doc/src/sgml/catalogs.sgml                    |  13 +
 doc/src/sgml/postgres-fdw.sgml                | 137 ++-
 doc/src/sgml/ref/create_aggregate.sgml        |  42 +
 doc/src/sgml/xaggr.sgml                       |  15 +-
 src/backend/catalog/pg_aggregate.c            | 101 ++
 src/backend/commands/aggregatecmds.c          |   4 +
 src/backend/optimizer/plan/planner.c          | 133 ++-
 src/bin/pg_dump/pg_dump.c                     |  16 +-
 src/include/catalog/pg_aggregate.dat          | 680 +++++++++++---
 src/include/catalog/pg_aggregate.h            |   4 +
 src/include/catalog/pg_proc.dat               | 196 ++++
 src/include/nodes/pathnodes.h                 |   5 +
 .../regress/expected/create_aggregate.out     | 184 ++++
 src/test/regress/expected/oidjoins.out        |   1 +
 src/test/regress/sql/create_aggregate.sql     | 183 ++++
 21 files changed, 2835 insertions(+), 222 deletions(-)

diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 09d6dd60dd..e957611879 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 */
@@ -3723,6 +3744,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	Query	   *query = context->root->parse;
 	ListCell   *lc;
 	bool		first = true;
+	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) context->foreignrel->fdw_private;
 
 	/* Nothing to be done, if there's no GROUP BY clause in the query. */
 	if (!query->groupClause)
@@ -3743,7 +3765,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	 * to empty, and in any case the redundancy situation on the remote might
 	 * be different than what we think here.
 	 */
-	foreach(lc, query->groupClause)
+	foreach(lc, fpinfo->group_clause)
 	{
 		SortGroupClause *grp = (SortGroupClause *) lfirst(lc);
 
@@ -4047,3 +4069,68 @@ get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
 	/* Shouldn't get here */
 	elog(ERROR, "unexpected expression in subquery output");
 }
+
+/*
+ * Check that a buit-in aggpartialfunc exists on the remote server. If
+ * check_partial_aggregate_support is false, we assume the partial aggregate
+ * function exsits on the remote server. Otherwise we assume the partial
+ * aggregate function exsits on the remote server only if the remote server
+ * version is not less than the local server version.
+ */
+static bool
+is_builtin_aggpartialfunc_shippable(Oid aggpartialfn, PgFdwRelationInfo *fpinfo)
+{
+	bool		shippable = true;
+
+	if (fpinfo->check_partial_aggregate_support)
+	{
+		if (fpinfo->remoteversion == 0)
+		{
+			PGconn	   *conn = GetConnection(fpinfo->user, false, NULL);
+
+			fpinfo->remoteversion = PQserverVersion(conn);
+		}
+		if (fpinfo->remoteversion < PG_VERSION_NUM)
+			shippable = false;
+	}
+	return shippable;
+}
+
+/*
+ * 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 exists on the remote server
+ */
+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_builtin(aggform->aggpartialfn))
+		partial_agg_ok = is_builtin_aggpartialfunc_shippable(
+															 aggform->aggpartialfn, fpinfo);
+	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 144c114d0f..986af1634a 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -9758,18 +9758,35 @@ 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.1');
+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');
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -9823,8 +9840,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
@@ -9835,21 +9852,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;
@@ -9863,28 +9880,837 @@ SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
  21 |   100
 (6 rows)
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are unsafe to push down when there is a having clause
+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                            
------------------------------------------------------------------
- Sort
-   Sort Key: pagg_tab.b
-   ->  Finalize HashAggregate
-         Group Key: pagg_tab.b
-         Filter: (sum(pagg_tab.a) < 700)
+                                                                 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 fpagg_tab_p1 pagg_tab
+                     ->  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 fpagg_tab_p2 pagg_tab_1
+                     ->  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 fpagg_tab_p3 pagg_tab_2
+                     ->  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)
+
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
+ b  |         avg         | max | count 
+----+---------------------+-----+-------
+  0 | 10.0000000000000000 |  20 |    60
+  1 | 11.0000000000000000 |  21 |    60
+ 10 | 10.0000000000000000 |  20 |    60
+ 11 | 11.0000000000000000 |  21 |    60
+ 20 | 10.0000000000000000 |  20 |    60
+ 21 | 11.0000000000000000 |  21 |    60
+ 30 | 10.0000000000000000 |  20 |    60
+ 31 | 11.0000000000000000 |  21 |    60
+ 40 | 10.0000000000000000 |  20 |    60
+ 41 | 11.0000000000000000 |  21 |    60
+(10 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+                                                                  QUERY PLAN                                                                  
+----------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: (avg(pagg_tab.a)), (max(pagg_tab.a)), (count(*)), ((((pagg_tab.b / 2)))::numeric), ((pagg_tab.b / 2))
+   Sort Key: ((((pagg_tab.b / 2)))::numeric)
+   ->  Finalize HashAggregate
+         Output: avg(pagg_tab.a), max(pagg_tab.a), count(*), (((pagg_tab.b / 2)))::numeric, ((pagg_tab.b / 2))
+         Group Key: ((pagg_tab.b / 2))
+         ->  Append
+               ->  Foreign Scan
+                     Output: ((pagg_tab.b / 2)), (PARTIAL avg(pagg_tab.a)), (PARTIAL max(pagg_tab.a)), (PARTIAL count(*)), pagg_tab.b
+                     Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p1 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_1.b / 2)), (PARTIAL avg(pagg_tab_1.a)), (PARTIAL max(pagg_tab_1.a)), (PARTIAL count(*)), pagg_tab_1.b
+                     Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p2 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_2.b / 2)), (PARTIAL avg(pagg_tab_2.a)), (PARTIAL max(pagg_tab_2.a)), (PARTIAL count(*)), pagg_tab_2.b
+                     Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p3 GROUP BY 1, 2
+(19 rows)
+
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+         avg         | max | count | numeric 
+---------------------+-----+-------+---------
+ 10.5000000000000000 |  21 |   120 |       0
+ 12.5000000000000000 |  23 |   120 |       1
+ 14.5000000000000000 |  25 |   120 |       2
+ 16.5000000000000000 |  27 |   120 |       3
+ 18.5000000000000000 |  29 |   120 |       4
+ 10.5000000000000000 |  21 |   120 |       5
+ 12.5000000000000000 |  23 |   120 |       6
+ 14.5000000000000000 |  25 |   120 |       7
+ 16.5000000000000000 |  27 |   120 |       8
+ 18.5000000000000000 |  29 |   120 |       9
+ 10.5000000000000000 |  21 |   120 |      10
+ 12.5000000000000000 |  23 |   120 |      11
+ 14.5000000000000000 |  25 |   120 |      12
+ 16.5000000000000000 |  27 |   120 |      13
+ 18.5000000000000000 |  29 |   120 |      14
+ 10.5000000000000000 |  21 |   120 |      15
+ 12.5000000000000000 |  23 |   120 |      16
+ 14.5000000000000000 |  25 |   120 |      17
+ 16.5000000000000000 |  27 |   120 |      18
+ 18.5000000000000000 |  29 |   120 |      19
+ 10.5000000000000000 |  21 |   120 |      20
+ 12.5000000000000000 |  23 |   120 |      21
+ 14.5000000000000000 |  25 |   120 |      22
+ 16.5000000000000000 |  27 |   120 |      23
+ 18.5000000000000000 |  29 |   120 |      24
+(25 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)
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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)
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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)
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+                                                                           QUERY PLAN                                                                           
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: avg(t1.b), avg((t1.b)::bigint)
+   ->  Append
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1.b)), (PARTIAL avg((t1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p1 t1) INNER JOIN (public.fpagg_tab_p1 t2))
+               Remote SQL: SELECT avg_p_int4(r4.b), avg_p_int8(r4.b::bigint) FROM (public.pagg_tab_p1 r4 INNER JOIN public.pagg_tab_p1 r7 ON (((r4.a = r7.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_1.b)), (PARTIAL avg((t1_1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p2 t1_1) INNER JOIN (public.fpagg_tab_p2 t2_1))
+               Remote SQL: SELECT avg_p_int4(r5.b), avg_p_int8(r5.b::bigint) FROM (public.pagg_tab_p2 r5 INNER JOIN public.pagg_tab_p2 r8 ON (((r5.a = r8.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_2.b)), (PARTIAL avg((t1_2.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p3 t1_2) INNER JOIN (public.fpagg_tab_p3 t2_2))
+               Remote SQL: SELECT avg_p_int4(r6.b), avg_p_int8(r6.b::bigint) FROM (public.pagg_tab_p3 r6 INNER JOIN public.pagg_tab_p3 r9 ON (((r6.a = r9.a))))
 (15 rows)
 
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+         avg         |         avg         
+---------------------+---------------------
+ 24.5000000000000000 | 24.5000000000000000
+(1 row)
+
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+-- 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/option.c b/contrib/postgres_fdw/option.c
index 8c822f4ef9..67e15c8bb9 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -126,7 +126,8 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
 			strcmp(def->defname, "async_capable") == 0 ||
 			strcmp(def->defname, "parallel_commit") == 0 ||
 			strcmp(def->defname, "parallel_abort") == 0 ||
-			strcmp(def->defname, "keep_connections") == 0)
+			strcmp(def->defname, "keep_connections") == 0 ||
+			strcmp(def->defname, "check_partial_aggregate_support") == 0)
 		{
 			/* these accept only boolean values */
 			(void) defGetBoolean(def);
@@ -268,6 +269,7 @@ InitPgFdwOptions(void)
 		/* batch_size is available on both server and table */
 		{"batch_size", ForeignServerRelationId, false},
 		{"batch_size", ForeignTableRelationId, false},
+		{"check_partial_aggregate_support", ForeignServerRelationId, false},
 		/* async_capable is available on both server and table */
 		{"async_capable", ForeignServerRelationId, false},
 		{"async_capable", ForeignTableRelationId, false},
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 1393716587..8a905ad8c4 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -36,6 +36,7 @@
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
+#include "optimizer/planner.h"
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
@@ -519,7 +520,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);
+								GroupPathExtraData *extra);
 static List *get_useful_pathkeys_for_relation(PlannerInfo *root,
 											  RelOptInfo *rel);
 static List *get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel);
@@ -650,6 +651,8 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	fpinfo->fdw_tuple_cost = DEFAULT_FDW_TUPLE_COST;
 	fpinfo->shippable_extensions = NIL;
 	fpinfo->fetch_size = 100;
+	fpinfo->check_partial_aggregate_support = false;
+	fpinfo->remoteversion = 0;
 	fpinfo->async_capable = false;
 
 	apply_server_options(fpinfo);
@@ -661,7 +664,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	 * should match what ExecCheckPermissions() does.  If we fail due to lack
 	 * of permissions, the query would have failed at runtime anyway.
 	 */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
 		Oid			userid;
 
@@ -5948,9 +5951,10 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
 	fpinfo->pushdown_safe = true;
 
 	/* Get user mapping */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
-		if (fpinfo_o->use_remote_estimate)
+		if (fpinfo_o->use_remote_estimate ||
+			fpinfo_o->check_partial_aggregate_support)
 			fpinfo->user = fpinfo_o->user;
 		else
 			fpinfo->user = fpinfo_i->user;
@@ -6132,6 +6136,8 @@ apply_server_options(PgFdwRelationInfo *fpinfo)
 				ExtractExtensionList(defGetString(def), false);
 		else if (strcmp(def->defname, "fetch_size") == 0)
 			(void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
+		else if (strcmp(def->defname, "check_partial_aggregate_support") == 0)
+			fpinfo->check_partial_aggregate_support = defGetBoolean(def);
 		else if (strcmp(def->defname, "async_capable") == 0)
 			fpinfo->async_capable = defGetBoolean(def);
 	}
@@ -6191,6 +6197,8 @@ merge_fdw_options(PgFdwRelationInfo *fpinfo,
 	fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
 	fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
 	fpinfo->fetch_size = fpinfo_o->fetch_size;
+	fpinfo->check_partial_aggregate_support = fpinfo_o->check_partial_aggregate_support;
+	fpinfo->remoteversion = fpinfo_o->remoteversion;
 	fpinfo->async_capable = fpinfo_o->async_capable;
 
 	/* Merge the table level options from either side of the join. */
@@ -6373,7 +6381,7 @@ postgresGetForeignJoinPaths(PlannerInfo *root,
  */
 static bool
 foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
-					Node *havingQual)
+					GroupPathExtraData *extra)
 {
 	Query	   *query = root->parse;
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
@@ -6382,11 +6390,16 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	ListCell   *lc;
 	int			i;
 	List	   *tlist = NIL;
+	bool		partial = extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL;
 
 	/* We currently don't support pushing Grouping Sets. */
 	if (query->groupingSets)
 		return false;
 
+	/* It's unsafe to push having statements with partial aggregates */
+	if ((extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL) && extra->havingQual)
+		return false;
+
 	/* Get the fpinfo of the underlying scan relation. */
 	ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
 
@@ -6415,6 +6428,12 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * a node, as long as it's not at top level; then no match is possible.
 	 */
 	i = 0;
+	fpinfo->group_clause = query->groupClause;
+	if (partial)
+	{
+		fpinfo->group_clause = extra->groupClausePartial;
+		grouping_target = extra->partial_target;
+	}
 	foreach(lc, grouping_target->exprs)
 	{
 		Expr	   *expr = (Expr *) lfirst(lc);
@@ -6426,7 +6445,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 		 * check the whole GROUP BY clause not just processed_groupClause,
 		 * because we will ship all of it, cf. appendGroupByClause.
 		 */
-		if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
+		if (sgref && get_sortgroupref_clause_noerr(sgref, fpinfo->group_clause))
 		{
 			TargetEntry *tle;
 
@@ -6512,9 +6531,9 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * Classify the pushable and non-pushable HAVING clauses and save them in
 	 * remote_conds and local_conds of the grouped rel's fpinfo.
 	 */
-	if (havingQual)
+	if (extra->havingQual)
 	{
-		foreach(lc, (List *) havingQual)
+		foreach(lc, (List *) extra->havingQual)
 		{
 			Expr	   *expr = (Expr *) lfirst(lc);
 			RestrictInfo *rinfo;
@@ -6534,7 +6553,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 									  grouped_rel->relids,
 									  NULL,
 									  NULL);
-			if (is_foreign_expr(root, grouped_rel, expr))
+			if (is_foreign_expr(root, grouped_rel, expr) && !partial)
 				fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
 			else
 				fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
@@ -6628,6 +6647,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)
@@ -6644,6 +6664,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;
@@ -6684,7 +6708,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;
@@ -6704,7 +6729,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))
 		return;
 
 	/*
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index 02c1152319..c60f3f5465 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -86,6 +86,15 @@ typedef struct PgFdwRelationInfo
 	ForeignServer *server;
 	UserMapping *user;			/* only set in use_remote_estimate mode */
 
+	/* for partial aggregate pushdown */
+	bool		check_partial_aggregate_support;
+
+	/*
+	 * if remoteversion is zero, then that means the remote server version is
+	 * unacquired.
+	 */
+	int			remoteversion;
+
 	int			fetch_size;		/* fetch size for this remote table */
 
 	/*
@@ -110,6 +119,7 @@ typedef struct PgFdwRelationInfo
 
 	/* Grouping information */
 	List	   *grouped_tlist;
+	List	   *group_clause;
 
 	/* Subquery information */
 	bool		make_outerrel_subquery; /* do we deparse outerrel as a
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index a303bfb322..1aabeac274 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -2989,16 +2989,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.1');
+
+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,9 @@ CREATE FOREIGN TABLE fpagg_tab_p2 PARTITION OF pagg_tab FOR VALUES FROM (10) TO
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -3028,10 +3046,289 @@ EXPLAIN (VERBOSE, COSTS OFF)
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are unsafe to push down when there is a having clause
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+
+-- 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;
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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;
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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;
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+
+-- 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 d3458840fb..de7df50383 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="partial-aggregate-pushdown"/> 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 33cc6e07b7..218f97a59c 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -451,6 +451,44 @@ OPTIONS (ADD password_required 'false');
 
   </sect3>
 
+  <sect3 id="postgres-fdw-options-partial-aggregate-pushdown">
+   <title>Partial Aggregate Pushdown Options</title>
+
+   <para>
+    By default, <filename>postgres_fdw</filename> assumes that for each built-in aggregate
+    function, the partial aggregate function is defined on the remote server. See
+    <xref linkend="partial-aggregate-pushdown"/> for the definition of the partial aggregate
+    function. This may be overridden using the following option:
+   </para>
+
+   <variablelist>
+
+    <varlistentry>
+     <term><literal>check_partial_aggregate_support</literal> (<type>boolean</type>)</term>
+     <listitem>
+      <para>
+       If this option is false, <filename>postgres_fdw</filename> assumes
+       that for each built-in aggregate function,
+       the partial aggregate function is defined on the remote server
+       without checking the remote server version.
+       If this option is true, during query planning,
+       <filename>postgres_fdw</filename> connects to the remote server
+       and checks if the remote server version is older than the local server version.
+       If so,
+       <filename>postgres_fdw</filename>
+       assumes that for each built-in aggregate function, the partial aggregate function is not defined
+       on the remote server unless the partial aggregate function and the aggregate
+       function match.
+       Otherwise <filename>postgres_fdw</filename> assumes that for each built-in aggregate function,
+       the partial aggregate function is defined on the remote server.
+       The default is <literal>false</literal>.
+      </para>
+     </listitem>
+    </varlistentry>
+
+   </variablelist>
+  </sect3>
+
   <sect3 id="postgres-fdw-options-asynchronous-execution">
    <title>Asynchronous Execution Options</title>
 
@@ -929,13 +967,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 +1006,68 @@ postgres=# SELECT postgres_fdw_disconnect_all();
   </para>
  </sect2>
 
+ <sect2 id="partial-aggregate-pushdown">
+  <title>Partial aggregate pushdown</title>
+  <para>
+   Partial aggregate pushdown is an optimization for a query that contains aggregate
+   expressions for a partitioned table across one or more remote servers. If the multiple
+   conditions are true for a remote server, an aggregate expression is sent to that
+   remote server and the aggregate is a partial aggregate on the local server.
+   The conditions under which this sending is active are as follows.
+   <itemizedlist spacing="compact">
+     <listitem>
+      <para>
+       An aggregate function, called the partial aggregate function for partial aggregate
+       that corresponding to the aggregate expression, is defined on the local server and
+       the remote server.
+      </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>
+       The aggregate expressions in the query don't contain
+       a <literal>DISTINCT</literal> or <literal>ORDER BY</literal> clause.
+      </para>
+     </listitem>
+    </itemizedlist>
+   When this sending is active for aggregate expressions, any aggregate
+   function is replaced by the partial aggregate function in the remote query.
+   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.
+   By default, <filename>postgres_fdw</filename> assumes that for each built-in
+   aggregate function, the partial aggregate function is defined on the local server
+   and the remote server.
+   If <literal>check_partial_aggregate_support</literal> is true and the remote server version is
+   older than the local server version, <filename>postgres_fdw</filename> does not assume
+   that the partial aggregate function is on the remote server unless
+   the partial aggregate function and the aggregate function match.
+   <filename>postgres_fdw</filename> assumes that a user-defined aggregate
+   function has the partial aggregate function on the remote server
+   only if both belong to an extension that is listed in the foreign server's
+   extensions option.
+  </para>
+ </sect2>
+
  <sect2 id="postgres-fdw-remote-query-execution-environment">
   <title>Remote Query Execution Environment</title>
 
@@ -1031,14 +1133,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 partial aggregate pushdown feature.
+   When using the partial aggregate pushdown feature,
+   the partial aggregate function of the aggregate function is sent to
+   the remote server if the conditions in
+   <xref linkend="partial-aggregate-pushdown"/> are met.
+   A limitation is that by default, <filename>postgres_fdw</filename> assumes that
+   for each built-in aggregate function, the partial aggregate function is defined
+   on the remote server.
+   This limitation might cause <quote>function does not exist</quote>
+   or a similar error if the remote server version is older than the local server version.
+   This problem can be worked around by setting
+   <literal>check_partial_aggregate_support</literal> to <literal>true</literal>
+   or similar query rewriting as above.
   </para>
  </sect2>
 
diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml
index 222e0aa5c9..2875734dea 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>
@@ -246,6 +248,18 @@ CREATE [ OR REPLACE ] AGGREGATE <replaceable class="parameter">name</replaceable
    marked <literal>PARALLEL SAFE</literal> to enable parallel aggregation.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize a query containing aggregate
+   expressions for a partitioned table across one or more remote servers.
+   This optimization sends an aggregate expression to a remote server and the
+   aggregate is a partial aggregate on the local server. This optimization
+   requires an aggregate function called a <firstterm>partial aggregate
+   function</firstterm>. See
+   <xref linkend="partial-aggregate-pushdown"/> for the definition of
+   partial aggregate function. <literal>AGGPARTIALFUNC</literal> is a parameter
+   of the partial aggregate function.
+  </para>
+
   <para>
    Aggregates that behave like <function>MIN</function> or <function>MAX</function> can
    sometimes be optimized by looking into an index instead of scanning every
@@ -653,6 +667,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..607c859974 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,18 @@ SELECT percentile_disc(0.5) WITHIN GROUP (ORDER BY income) FROM households;
    parallel-safety markings on its support functions are not consulted.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize a query containing
+   aggregate expressions for a partitioned table across one or more
+   remote servers. This optimization sends an aggregate expression to
+   a remote server and the aggregate is a partial aggregate on the
+   local server. This optimization requires an aggregate function
+   called the <firstterm>partial aggregate function</firstterm>.
+   See
+   <xref linkend="partial-aggregate-pushdown"/> 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/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index aafef24cf1..c112273fac 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -206,7 +206,7 @@ static PathTarget *make_group_input_target(PlannerInfo *root,
 										   PathTarget *final_target);
 static PathTarget *make_partial_grouping_target(PlannerInfo *root,
 												PathTarget *grouping_target,
-												Node *havingQual);
+												GroupPathExtraData *extra);
 static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
 static void optimize_window_clauses(PlannerInfo *root,
 									WindowFuncLists *wflists);
@@ -5421,6 +5421,99 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
 	return set_pathtarget_cost_width(root, input_target);
 }
 
+/*
+ * setGroupClausePartial
+ *	  Generate a groupClause and a pathtarget for partial aggregate
+ *	  pushdown by FDW and set them to GroupPathExtraData.
+ */
+static void
+setGroupClausePartial(PathTarget *partial_target, List *non_group_exprs,
+					  List *groupClause, GroupPathExtraData *extra)
+{
+	int			exprno,
+				refno;
+	ListCell   *lc;
+	Index		maxRef = 0;
+	List	   *exprs_processed = NIL;
+	int			exprs_num = 0;
+
+	foreach(lc, groupClause)
+	{
+		SortGroupClause *sgc = (SortGroupClause *) lfirst(lc);
+
+		if (sgc->tleSortGroupRef > maxRef)
+			maxRef = sgc->tleSortGroupRef;
+	}
+	maxRef++;
+
+	extra->groupClausePartial = list_copy_deep(groupClause);
+	extra->partial_target = copy_pathtarget(partial_target);
+
+	if (partial_target->exprs)
+		exprs_num = partial_target->exprs->length;
+
+	foreach(lc, non_group_exprs)
+	{
+		Expr	   *expr = (Expr *) lfirst(lc);
+
+		refno = -1;
+		if (list_member(exprs_processed, expr) ||
+			(!IsA(expr, Var) && !IsA(expr, PlaceHolderVar)))
+			continue;
+		exprs_processed = lappend(exprs_processed, expr);
+		for (exprno = 0; exprno < exprs_num; exprno++)
+		{
+			Expr	   *target_expr = (Expr *) list_nth(partial_target->exprs, exprno);
+
+			if (equal(target_expr, expr))
+			{
+				refno = exprno;
+				break;
+			}
+		}
+		if (refno < 0)
+		{
+			SortGroupClause *grpcl = makeNode(SortGroupClause);
+
+			grpcl->tleSortGroupRef = maxRef++;
+			extra->groupClausePartial = lappend(extra->groupClausePartial, grpcl);
+			add_column_to_pathtarget(extra->partial_target, expr, grpcl->tleSortGroupRef);
+		}
+	}
+}
+
+/*
+ * adjustAggrefForPartial
+ * Adjust Aggrefs to put them in partial mode
+ */
+static void
+adjustAggrefForPartial(List *exprs)
+{
+	ListCell   *lc;
+
+	foreach(lc, exprs)
+	{
+		Aggref	   *aggref = (Aggref *) lfirst(lc);
+
+		if (IsA(aggref, Aggref))
+		{
+			Aggref	   *newaggref;
+
+			/*
+			 * We shouldn't need to copy the substructure of the Aggref node,
+			 * but flat-copy the node itself to avoid damaging other trees.
+			 */
+			newaggref = makeNode(Aggref);
+			memcpy(newaggref, aggref, sizeof(Aggref));
+
+			/* For now, assume serialization is required */
+			mark_partial_aggref(newaggref, AGGSPLIT_INITIAL_SERIAL);
+
+			lfirst(lc) = newaggref;
+		}
+	}
+}
+
 /*
  * make_partial_grouping_target
  *	  Generate appropriate PathTarget for output of partial aggregate
@@ -5436,11 +5529,15 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
  *
  * grouping_target is the tlist to be emitted by the topmost aggregation step.
  * havingQual represents the HAVING clause.
+ *
+ * Modified PathTarget cannot be used by FDW as-is to deparse this statement.
+ * So, before modifying PathTarget, setGroupClausePartial generates
+ * another Pathtarget and another list of SortGroupClauses.
  */
 static PathTarget *
 make_partial_grouping_target(PlannerInfo *root,
 							 PathTarget *grouping_target,
-							 Node *havingQual)
+							 GroupPathExtraData *extra)
 {
 	PathTarget *partial_target;
 	List	   *non_group_cols;
@@ -5482,8 +5579,8 @@ make_partial_grouping_target(PlannerInfo *root,
 	/*
 	 * If there's a HAVING clause, we'll need the Vars/Aggrefs it uses, too.
 	 */
-	if (havingQual)
-		non_group_cols = lappend(non_group_cols, havingQual);
+	if (extra->havingQual)
+		non_group_cols = lappend(non_group_cols, extra->havingQual);
 
 	/*
 	 * Pull out all the Vars, PlaceHolderVars, and Aggrefs mentioned in
@@ -5496,35 +5593,17 @@ make_partial_grouping_target(PlannerInfo *root,
 									  PVC_INCLUDE_AGGREGATES |
 									  PVC_RECURSE_WINDOWFUNCS |
 									  PVC_INCLUDE_PLACEHOLDERS);
-
+	setGroupClausePartial(partial_target, non_group_exprs, root->parse->groupClause, extra);
 	add_new_columns_to_pathtarget(partial_target, non_group_exprs);
+	add_new_columns_to_pathtarget(extra->partial_target, non_group_exprs);
 
 	/*
 	 * Adjust Aggrefs to put them in partial mode.  At this point all Aggrefs
 	 * are at the top level of the target list, so we can just scan the list
 	 * rather than recursing through the expression trees.
 	 */
-	foreach(lc, partial_target->exprs)
-	{
-		Aggref	   *aggref = (Aggref *) lfirst(lc);
-
-		if (IsA(aggref, Aggref))
-		{
-			Aggref	   *newaggref;
-
-			/*
-			 * We shouldn't need to copy the substructure of the Aggref node,
-			 * but flat-copy the node itself to avoid damaging other trees.
-			 */
-			newaggref = makeNode(Aggref);
-			memcpy(newaggref, aggref, sizeof(Aggref));
-
-			/* For now, assume serialization is required */
-			mark_partial_aggref(newaggref, AGGSPLIT_INITIAL_SERIAL);
-
-			lfirst(lc) = newaggref;
-		}
-	}
+	adjustAggrefForPartial(partial_target->exprs);
+	adjustAggrefForPartial(extra->partial_target->exprs);
 
 	/* clean up cruft */
 	list_free(non_group_exprs);
@@ -7154,7 +7233,7 @@ create_partial_grouping_paths(PlannerInfo *root,
 	 */
 	partially_grouped_rel->reltarget =
 		make_partial_grouping_target(root, grouped_rel->reltarget,
-									 extra->havingQual);
+									 extra);
 
 	if (!extra->partial_costs_set)
 	{
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 83aeef2751..b23d4f7994 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -13909,6 +13909,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;
@@ -13991,11 +13992,18 @@ 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 >= 170000)
+			appendPQExpBufferStr(query,
+								 "aggpartialfn\n");
+		else
+			appendPQExpBufferStr(query,
+								 "'-' AS aggpartialfn\n");
 
 		appendPQExpBufferStr(query,
 							 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
@@ -14021,6 +14029,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"));
@@ -14110,6 +14119,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 085149e258..3e4f8331ee 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 72ea4aa8b8..713cc8ae2f 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',
@@ -5004,6 +5011,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',
@@ -5016,6 +5027,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',
@@ -6606,36 +6621,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' },
@@ -6658,6 +6698,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' },
@@ -6816,153 +6859,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',
@@ -6973,52 +7124,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/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 5702fbba60..a08b2fac5f 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -3243,6 +3243,9 @@ typedef enum
  * havingQual gives list of quals to be applied after aggregation.
  * targetList gives list of columns to be projected.
  * patype is the type of partitionwise aggregation that is being performed.
+ * groupClausePartial is List of SortGroupClauses for partial aggregate
+ * 		pushdown by FDW
+ * partial_target is PathTarget for partial aggregate pushdown by FDW
  */
 typedef struct
 {
@@ -3257,6 +3260,8 @@ typedef struct
 	Node	   *havingQual;
 	List	   *targetList;
 	PartitionwiseAggregateType patype;
+	List	   *groupClausePartial;
+	PathTarget *partial_target;
 } GroupPathExtraData;
 
 /*
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] 38+ messages in thread

* Re: Partial aggregates pushdown
@ 2023-10-23 16:55  Bruce Momjian <[email protected]>
  parent: [email protected] <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: Bruce Momjian @ 2023-10-23 16:55 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: pgsql-hackers; Finnerty, Jim <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Alexander Pyhalov <[email protected]>

On Wed, Oct 18, 2023 at 05:22:34AM +0000, [email protected] wrote:
> Hi hackers.
> 
> Because there is a degrade in pg_dump.c, I fixed it.

Fujii-san, to get this patch closer to finished, can I modify this
version of the patch to improve some wording and post an updated version
you can use for future changes?

-- 
  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] 38+ messages in thread

* RE: Partial aggregates pushdown
@ 2023-10-24 00:12  [email protected] <[email protected]>
  parent: Bruce Momjian <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

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

Hi Mr.Momjian.

> Fujii-san, to get this patch closer to finished, can I modify this version of the patch to improve some wording and post an
> updated version you can use for future changes?
Yes, I greatly appreciate your offer.
I would very much appreciate your modifications.

Sincerely yours,
Yuuki Fujii

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






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

* Re: Partial aggregates pushdown
@ 2023-10-25 22:08  Bruce Momjian <[email protected]>
  parent: [email protected] <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: Bruce Momjian @ 2023-10-25 22:08 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: pgsql-hackers; Finnerty, Jim <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Alexander Pyhalov <[email protected]>

On Tue, Oct 24, 2023 at 12:12:41AM +0000, [email protected] wrote:
> Hi Mr.Momjian.
> 
> > Fujii-san, to get this patch closer to finished, can I modify this version of the patch to improve some wording and post an
> > updated version you can use for future changes?
> Yes, I greatly appreciate your offer.
> I would very much appreciate your modifications.

I am almost done updating the patch, but I got stuck on how the feature
is supposed to work.  This documentation sentence is where I got
confused:

	<varlistentry>
	 <term><literal>check_partial_aggregate_support</literal> (<type>boolean</type>)</term>
	 <listitem>
	  <para>
	   If this option is false, <filename>postgres_fdw</filename> assumes
	   that for each built-in aggregate function,
	   the partial aggregate function is defined on the remote server
	   without checking the remote server version.
	   If this option is true, during query planning,
	   <filename>postgres_fdw</filename> connects to the remote server
	   and checks if the remote server version is older than the local server version.
	   If so,
	   <filename>postgres_fdw</filename>
-->	   assumes that for each built-in aggregate function, the partial aggregate function is not defined
-->	   on the remote server unless the partial aggregate function and the aggregate
-->	   function match.
	   Otherwise <filename>postgres_fdw</filename> assumes that for each built-in aggregate function,
	   the partial aggregate function is defined on the remote server.
	   The default is <literal>false</literal>.
	  </para>
	 </listitem>
	</varlistentry>

What does that marked sentence mean?  What is match?  Are one or both of
these remote?  It sounds like you are checking the local aggregate
against the remote partial aggregate, but I don't see any code that does
this in the 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] 38+ messages in thread

* RE: Partial aggregates pushdown
@ 2023-10-26 11:11  [email protected] <[email protected]>
  parent: Bruce Momjian <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

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

Hi Mr.Momjian

> From: Bruce Momjian <[email protected]>
> Sent: Thursday, October 26, 2023 7:08 AM
> I am almost done updating the patch, but I got stuck on how the feature is supposed to work.  This documentation
> sentence is where I got
> confused:
> 
> 	<varlistentry>
> 	 <term><literal>check_partial_aggregate_support</literal> (<type>boolean</type>)</term>
> 	 <listitem>
> 	  <para>
> 	   If this option is false, <filename>postgres_fdw</filename> assumes
> 	   that for each built-in aggregate function,
> 	   the partial aggregate function is defined on the remote server
> 	   without checking the remote server version.
> 	   If this option is true, during query planning,
> 	   <filename>postgres_fdw</filename> connects to the remote server
> 	   and checks if the remote server version is older than the local server version.
> 	   If so,
> 	   <filename>postgres_fdw</filename>
> -->	   assumes that for each built-in aggregate function, the partial aggregate function is not defined
> -->	   on the remote server unless the partial aggregate function and the aggregate
> -->	   function match.
> 	   Otherwise <filename>postgres_fdw</filename> assumes that for each built-in aggregate function,
> 	   the partial aggregate function is defined on the remote server.
> 	   The default is <literal>false</literal>.
> 	  </para>
> 	 </listitem>
> 	</varlistentry>
> 
> What does that marked sentence mean?  What is match?  Are one or both of these remote?  It sounds like you are
> checking the local aggregate against the remote partial aggregate, but I don't see any code that does this in the patch.
This sentence means that
"If the partial aggregate function has the same OID as the aggregate function,
then postgres_fdw assumes that for each built-in aggregate function, the partial aggregate function is not defined
 on the remote server."
"Match" means that the partial aggregate function has the same OID as the aggregate function in local server.
But, in v30, there is no code which checks the partial aggregate function has the same OID as the aggregate function in local server.
So I modified the code of is_builtin_aggpartialfunc_shippable().
Also, I modified wording postgres-fdw.sgml.

Sincerely yours,
Yuuki Fujii

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


Attachments:

  [application/octet-stream] 0001-Partial-aggregates-push-down-v31.patch (267.1K, ../../TYTPR01MB10954D890D8F729E1BCE42D4F95DDA@TYTPR01MB10954.jpnprd01.prod.outlook.com/2-0001-Partial-aggregates-push-down-v31.patch)
  download | inline diff:
From 9c06e338a2142f5371f3292789deb5e88ea2fcd0 Mon Sep 17 00:00:00 2001
From: Yuki Fujii <[email protected]>
Date: Thu, 26 Oct 2023 13:32:16 -0400
Subject: [PATCH] Partial aggregates push down v31

---
 contrib/postgres_fdw/deparse.c                | 104 ++-
 .../postgres_fdw/expected/postgres_fdw.out    | 868 +++++++++++++++++-
 contrib/postgres_fdw/option.c                 |   4 +-
 contrib/postgres_fdw/postgres_fdw.c           |  47 +-
 contrib/postgres_fdw/postgres_fdw.h           |  10 +
 contrib/postgres_fdw/sql/postgres_fdw.sql     | 311 ++++++-
 doc/src/sgml/catalogs.sgml                    |  13 +
 doc/src/sgml/postgres-fdw.sgml                | 139 ++-
 doc/src/sgml/ref/create_aggregate.sgml        |  42 +
 doc/src/sgml/xaggr.sgml                       |  15 +-
 src/backend/catalog/pg_aggregate.c            | 101 ++
 src/backend/commands/aggregatecmds.c          |   4 +
 src/backend/optimizer/plan/planner.c          | 133 ++-
 src/bin/pg_dump/pg_dump.c                     |  16 +-
 src/include/catalog/pg_aggregate.dat          | 680 +++++++++++---
 src/include/catalog/pg_aggregate.h            |   4 +
 src/include/catalog/pg_proc.dat               | 196 ++++
 src/include/nodes/pathnodes.h                 |   5 +
 .../regress/expected/create_aggregate.out     | 184 ++++
 src/test/regress/expected/oidjoins.out        |   1 +
 src/test/regress/sql/create_aggregate.sql     | 183 ++++
 21 files changed, 2838 insertions(+), 222 deletions(-)

diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 09d6dd60dd..fbeed33da2 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 */
@@ -3723,6 +3744,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	Query	   *query = context->root->parse;
 	ListCell   *lc;
 	bool		first = true;
+	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) context->foreignrel->fdw_private;
 
 	/* Nothing to be done, if there's no GROUP BY clause in the query. */
 	if (!query->groupClause)
@@ -3743,7 +3765,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	 * to empty, and in any case the redundancy situation on the remote might
 	 * be different than what we think here.
 	 */
-	foreach(lc, query->groupClause)
+	foreach(lc, fpinfo->group_clause)
 	{
 		SortGroupClause *grp = (SortGroupClause *) lfirst(lc);
 
@@ -4047,3 +4069,69 @@ get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
 	/* Shouldn't get here */
 	elog(ERROR, "unexpected expression in subquery output");
 }
+
+/*
+ * Check that a buit-in aggpartialfunc exists on the remote server. If
+ * check_partial_aggregate_support is false or the aggpartialfunc has
+ * the same OID as the aggregate function, we assume the partial aggregate
+ * function exsits on the remote server. Otherwise we assume the partial
+ * aggregate function exsits on the remote server only if the remote server
+ * version is not less than the local server version.
+ */
+static bool
+is_builtin_aggpartialfunc_shippable(Form_pg_aggregate aggform, PgFdwRelationInfo *fpinfo)
+{
+	bool		shippable = true;
+
+	if ((aggform->aggfnoid != aggform->aggpartialfn) &&
+		(fpinfo->check_partial_aggregate_support))
+	{
+		if (fpinfo->remoteversion == 0)
+		{
+			PGconn	   *conn = GetConnection(fpinfo->user, false, NULL);
+
+			fpinfo->remoteversion = PQserverVersion(conn);
+		}
+		if (fpinfo->remoteversion < PG_VERSION_NUM)
+			shippable = false;
+	}
+	return shippable;
+}
+
+/*
+ * 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 exists on the remote server
+ */
+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_builtin(aggform->aggpartialfn))
+		partial_agg_ok = is_builtin_aggpartialfunc_shippable(aggform, fpinfo);
+	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 144c114d0f..986af1634a 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -9758,18 +9758,35 @@ 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.1');
+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');
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -9823,8 +9840,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
@@ -9835,21 +9852,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;
@@ -9863,28 +9880,837 @@ SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
  21 |   100
 (6 rows)
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are unsafe to push down when there is a having clause
+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                            
------------------------------------------------------------------
- Sort
-   Sort Key: pagg_tab.b
-   ->  Finalize HashAggregate
-         Group Key: pagg_tab.b
-         Filter: (sum(pagg_tab.a) < 700)
+                                                                 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 fpagg_tab_p1 pagg_tab
+                     ->  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 fpagg_tab_p2 pagg_tab_1
+                     ->  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 fpagg_tab_p3 pagg_tab_2
+                     ->  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)
+
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
+ b  |         avg         | max | count 
+----+---------------------+-----+-------
+  0 | 10.0000000000000000 |  20 |    60
+  1 | 11.0000000000000000 |  21 |    60
+ 10 | 10.0000000000000000 |  20 |    60
+ 11 | 11.0000000000000000 |  21 |    60
+ 20 | 10.0000000000000000 |  20 |    60
+ 21 | 11.0000000000000000 |  21 |    60
+ 30 | 10.0000000000000000 |  20 |    60
+ 31 | 11.0000000000000000 |  21 |    60
+ 40 | 10.0000000000000000 |  20 |    60
+ 41 | 11.0000000000000000 |  21 |    60
+(10 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+                                                                  QUERY PLAN                                                                  
+----------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: (avg(pagg_tab.a)), (max(pagg_tab.a)), (count(*)), ((((pagg_tab.b / 2)))::numeric), ((pagg_tab.b / 2))
+   Sort Key: ((((pagg_tab.b / 2)))::numeric)
+   ->  Finalize HashAggregate
+         Output: avg(pagg_tab.a), max(pagg_tab.a), count(*), (((pagg_tab.b / 2)))::numeric, ((pagg_tab.b / 2))
+         Group Key: ((pagg_tab.b / 2))
+         ->  Append
+               ->  Foreign Scan
+                     Output: ((pagg_tab.b / 2)), (PARTIAL avg(pagg_tab.a)), (PARTIAL max(pagg_tab.a)), (PARTIAL count(*)), pagg_tab.b
+                     Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p1 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_1.b / 2)), (PARTIAL avg(pagg_tab_1.a)), (PARTIAL max(pagg_tab_1.a)), (PARTIAL count(*)), pagg_tab_1.b
+                     Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p2 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_2.b / 2)), (PARTIAL avg(pagg_tab_2.a)), (PARTIAL max(pagg_tab_2.a)), (PARTIAL count(*)), pagg_tab_2.b
+                     Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p3 GROUP BY 1, 2
+(19 rows)
+
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+         avg         | max | count | numeric 
+---------------------+-----+-------+---------
+ 10.5000000000000000 |  21 |   120 |       0
+ 12.5000000000000000 |  23 |   120 |       1
+ 14.5000000000000000 |  25 |   120 |       2
+ 16.5000000000000000 |  27 |   120 |       3
+ 18.5000000000000000 |  29 |   120 |       4
+ 10.5000000000000000 |  21 |   120 |       5
+ 12.5000000000000000 |  23 |   120 |       6
+ 14.5000000000000000 |  25 |   120 |       7
+ 16.5000000000000000 |  27 |   120 |       8
+ 18.5000000000000000 |  29 |   120 |       9
+ 10.5000000000000000 |  21 |   120 |      10
+ 12.5000000000000000 |  23 |   120 |      11
+ 14.5000000000000000 |  25 |   120 |      12
+ 16.5000000000000000 |  27 |   120 |      13
+ 18.5000000000000000 |  29 |   120 |      14
+ 10.5000000000000000 |  21 |   120 |      15
+ 12.5000000000000000 |  23 |   120 |      16
+ 14.5000000000000000 |  25 |   120 |      17
+ 16.5000000000000000 |  27 |   120 |      18
+ 18.5000000000000000 |  29 |   120 |      19
+ 10.5000000000000000 |  21 |   120 |      20
+ 12.5000000000000000 |  23 |   120 |      21
+ 14.5000000000000000 |  25 |   120 |      22
+ 16.5000000000000000 |  27 |   120 |      23
+ 18.5000000000000000 |  29 |   120 |      24
+(25 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)
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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)
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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)
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+                                                                           QUERY PLAN                                                                           
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: avg(t1.b), avg((t1.b)::bigint)
+   ->  Append
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1.b)), (PARTIAL avg((t1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p1 t1) INNER JOIN (public.fpagg_tab_p1 t2))
+               Remote SQL: SELECT avg_p_int4(r4.b), avg_p_int8(r4.b::bigint) FROM (public.pagg_tab_p1 r4 INNER JOIN public.pagg_tab_p1 r7 ON (((r4.a = r7.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_1.b)), (PARTIAL avg((t1_1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p2 t1_1) INNER JOIN (public.fpagg_tab_p2 t2_1))
+               Remote SQL: SELECT avg_p_int4(r5.b), avg_p_int8(r5.b::bigint) FROM (public.pagg_tab_p2 r5 INNER JOIN public.pagg_tab_p2 r8 ON (((r5.a = r8.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_2.b)), (PARTIAL avg((t1_2.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p3 t1_2) INNER JOIN (public.fpagg_tab_p3 t2_2))
+               Remote SQL: SELECT avg_p_int4(r6.b), avg_p_int8(r6.b::bigint) FROM (public.pagg_tab_p3 r6 INNER JOIN public.pagg_tab_p3 r9 ON (((r6.a = r9.a))))
 (15 rows)
 
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+         avg         |         avg         
+---------------------+---------------------
+ 24.5000000000000000 | 24.5000000000000000
+(1 row)
+
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+-- 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/option.c b/contrib/postgres_fdw/option.c
index 8c822f4ef9..67e15c8bb9 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -126,7 +126,8 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
 			strcmp(def->defname, "async_capable") == 0 ||
 			strcmp(def->defname, "parallel_commit") == 0 ||
 			strcmp(def->defname, "parallel_abort") == 0 ||
-			strcmp(def->defname, "keep_connections") == 0)
+			strcmp(def->defname, "keep_connections") == 0 ||
+			strcmp(def->defname, "check_partial_aggregate_support") == 0)
 		{
 			/* these accept only boolean values */
 			(void) defGetBoolean(def);
@@ -268,6 +269,7 @@ InitPgFdwOptions(void)
 		/* batch_size is available on both server and table */
 		{"batch_size", ForeignServerRelationId, false},
 		{"batch_size", ForeignTableRelationId, false},
+		{"check_partial_aggregate_support", ForeignServerRelationId, false},
 		/* async_capable is available on both server and table */
 		{"async_capable", ForeignServerRelationId, false},
 		{"async_capable", ForeignTableRelationId, false},
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 1393716587..8a905ad8c4 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -36,6 +36,7 @@
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
+#include "optimizer/planner.h"
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
@@ -519,7 +520,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);
+								GroupPathExtraData *extra);
 static List *get_useful_pathkeys_for_relation(PlannerInfo *root,
 											  RelOptInfo *rel);
 static List *get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel);
@@ -650,6 +651,8 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	fpinfo->fdw_tuple_cost = DEFAULT_FDW_TUPLE_COST;
 	fpinfo->shippable_extensions = NIL;
 	fpinfo->fetch_size = 100;
+	fpinfo->check_partial_aggregate_support = false;
+	fpinfo->remoteversion = 0;
 	fpinfo->async_capable = false;
 
 	apply_server_options(fpinfo);
@@ -661,7 +664,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	 * should match what ExecCheckPermissions() does.  If we fail due to lack
 	 * of permissions, the query would have failed at runtime anyway.
 	 */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
 		Oid			userid;
 
@@ -5948,9 +5951,10 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
 	fpinfo->pushdown_safe = true;
 
 	/* Get user mapping */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
-		if (fpinfo_o->use_remote_estimate)
+		if (fpinfo_o->use_remote_estimate ||
+			fpinfo_o->check_partial_aggregate_support)
 			fpinfo->user = fpinfo_o->user;
 		else
 			fpinfo->user = fpinfo_i->user;
@@ -6132,6 +6136,8 @@ apply_server_options(PgFdwRelationInfo *fpinfo)
 				ExtractExtensionList(defGetString(def), false);
 		else if (strcmp(def->defname, "fetch_size") == 0)
 			(void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
+		else if (strcmp(def->defname, "check_partial_aggregate_support") == 0)
+			fpinfo->check_partial_aggregate_support = defGetBoolean(def);
 		else if (strcmp(def->defname, "async_capable") == 0)
 			fpinfo->async_capable = defGetBoolean(def);
 	}
@@ -6191,6 +6197,8 @@ merge_fdw_options(PgFdwRelationInfo *fpinfo,
 	fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
 	fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
 	fpinfo->fetch_size = fpinfo_o->fetch_size;
+	fpinfo->check_partial_aggregate_support = fpinfo_o->check_partial_aggregate_support;
+	fpinfo->remoteversion = fpinfo_o->remoteversion;
 	fpinfo->async_capable = fpinfo_o->async_capable;
 
 	/* Merge the table level options from either side of the join. */
@@ -6373,7 +6381,7 @@ postgresGetForeignJoinPaths(PlannerInfo *root,
  */
 static bool
 foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
-					Node *havingQual)
+					GroupPathExtraData *extra)
 {
 	Query	   *query = root->parse;
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
@@ -6382,11 +6390,16 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	ListCell   *lc;
 	int			i;
 	List	   *tlist = NIL;
+	bool		partial = extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL;
 
 	/* We currently don't support pushing Grouping Sets. */
 	if (query->groupingSets)
 		return false;
 
+	/* It's unsafe to push having statements with partial aggregates */
+	if ((extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL) && extra->havingQual)
+		return false;
+
 	/* Get the fpinfo of the underlying scan relation. */
 	ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
 
@@ -6415,6 +6428,12 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * a node, as long as it's not at top level; then no match is possible.
 	 */
 	i = 0;
+	fpinfo->group_clause = query->groupClause;
+	if (partial)
+	{
+		fpinfo->group_clause = extra->groupClausePartial;
+		grouping_target = extra->partial_target;
+	}
 	foreach(lc, grouping_target->exprs)
 	{
 		Expr	   *expr = (Expr *) lfirst(lc);
@@ -6426,7 +6445,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 		 * check the whole GROUP BY clause not just processed_groupClause,
 		 * because we will ship all of it, cf. appendGroupByClause.
 		 */
-		if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
+		if (sgref && get_sortgroupref_clause_noerr(sgref, fpinfo->group_clause))
 		{
 			TargetEntry *tle;
 
@@ -6512,9 +6531,9 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * Classify the pushable and non-pushable HAVING clauses and save them in
 	 * remote_conds and local_conds of the grouped rel's fpinfo.
 	 */
-	if (havingQual)
+	if (extra->havingQual)
 	{
-		foreach(lc, (List *) havingQual)
+		foreach(lc, (List *) extra->havingQual)
 		{
 			Expr	   *expr = (Expr *) lfirst(lc);
 			RestrictInfo *rinfo;
@@ -6534,7 +6553,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 									  grouped_rel->relids,
 									  NULL,
 									  NULL);
-			if (is_foreign_expr(root, grouped_rel, expr))
+			if (is_foreign_expr(root, grouped_rel, expr) && !partial)
 				fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
 			else
 				fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
@@ -6628,6 +6647,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)
@@ -6644,6 +6664,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;
@@ -6684,7 +6708,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;
@@ -6704,7 +6729,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))
 		return;
 
 	/*
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index 02c1152319..c60f3f5465 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -86,6 +86,15 @@ typedef struct PgFdwRelationInfo
 	ForeignServer *server;
 	UserMapping *user;			/* only set in use_remote_estimate mode */
 
+	/* for partial aggregate pushdown */
+	bool		check_partial_aggregate_support;
+
+	/*
+	 * if remoteversion is zero, then that means the remote server version is
+	 * unacquired.
+	 */
+	int			remoteversion;
+
 	int			fetch_size;		/* fetch size for this remote table */
 
 	/*
@@ -110,6 +119,7 @@ typedef struct PgFdwRelationInfo
 
 	/* Grouping information */
 	List	   *grouped_tlist;
+	List	   *group_clause;
 
 	/* Subquery information */
 	bool		make_outerrel_subquery; /* do we deparse outerrel as a
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index a303bfb322..1aabeac274 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -2989,16 +2989,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.1');
+
+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,9 @@ CREATE FOREIGN TABLE fpagg_tab_p2 PARTITION OF pagg_tab FOR VALUES FROM (10) TO
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -3028,10 +3046,289 @@ EXPLAIN (VERBOSE, COSTS OFF)
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are unsafe to push down when there is a having clause
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+
+-- 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;
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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;
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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;
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+
+-- 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 3ec7391ec5..c5d826c585 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="partial-aggregate-pushdown"/> 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 33cc6e07b7..d9b3238d8b 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -451,6 +451,44 @@ OPTIONS (ADD password_required 'false');
 
   </sect3>
 
+  <sect3 id="postgres-fdw-options-partial-aggregate-pushdown">
+   <title>Partial Aggregate Pushdown Options</title>
+
+   <para>
+    By default, <filename>postgres_fdw</filename> assumes that for each built-in aggregate
+    function, the partial aggregate function is defined on the remote server. See
+    <xref linkend="partial-aggregate-pushdown"/> for the definition of the partial aggregate
+    function. This may be overridden using the following option:
+   </para>
+
+   <variablelist>
+
+    <varlistentry>
+     <term><literal>check_partial_aggregate_support</literal> (<type>boolean</type>)</term>
+     <listitem>
+      <para>
+       For each built-in aggregate function,
+       if this option is false or the partial aggregate function
+       has the same OID as the aggregate function,
+       <filename>postgres_fdw</filename> assumes that
+       the partial aggregate function is defined on the remote server
+       without checking the remote server version.
+       If this option is true, during query planning,
+       <filename>postgres_fdw</filename> connects to the remote server
+       and checks if the remote server version is older than the local server version.
+       If so,
+       <filename>postgres_fdw</filename>
+       assumes that the partial aggregate function is not defined on the remote server.
+       Otherwise <filename>postgres_fdw</filename> assumes that
+       the partial aggregate function is defined on the remote server.
+       The default is <literal>false</literal>.
+      </para>
+     </listitem>
+    </varlistentry>
+
+   </variablelist>
+  </sect3>
+
   <sect3 id="postgres-fdw-options-asynchronous-execution">
    <title>Asynchronous Execution Options</title>
 
@@ -929,13 +967,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 +1006,70 @@ postgres=# SELECT postgres_fdw_disconnect_all();
   </para>
  </sect2>
 
+ <sect2 id="partial-aggregate-pushdown">
+  <title>Partial aggregate pushdown</title>
+  <para>
+   Partial aggregate pushdown is an optimization for a query that contains aggregate
+   expressions for a partitioned table across one or more remote servers. If the multiple
+   conditions are true for a remote server, an aggregate expression is sent to that
+   remote server and the aggregate is a partial aggregate on the local server.
+   The conditions under which this sending is active are as follows.
+   <itemizedlist spacing="compact">
+     <listitem>
+      <para>
+       An aggregate function, called the partial aggregate function for partial aggregate
+       that corresponding to the aggregate expression, is defined on the local server and
+       the remote server.
+      </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>
+       The aggregate expressions in the query don't contain
+       a <literal>DISTINCT</literal> or <literal>ORDER BY</literal> clause.
+      </para>
+     </listitem>
+    </itemizedlist>
+   When this sending is active for aggregate expressions, any aggregate
+   function is replaced by the partial aggregate function in the remote query.
+   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.
+   By default, <filename>postgres_fdw</filename> assumes that for each built-in
+   aggregate function, the partial aggregate function is defined on the local server
+   and the remote server.
+   If the partial aggregate function does not have the same OID as the aggregate function
+   and <literal>check_partial_aggregate_support</literal> is true,
+   <filename>postgres_fdw</filename> checks if the remote server version is
+   older than the local server version.
+   If so, <filename>postgres_fdw</filename> does not assume
+   that the partial aggregate function is on the remote server.
+   <filename>postgres_fdw</filename> assumes that a user-defined aggregate
+   function has its partial aggregate function on the remote server
+   only if both belong to an extension that is listed in the foreign server's
+   extensions option.
+  </para>
+ </sect2>
+
  <sect2 id="postgres-fdw-remote-query-execution-environment">
   <title>Remote Query Execution Environment</title>
 
@@ -1031,14 +1135,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 partial aggregate pushdown feature.
+   When using the partial aggregate pushdown feature,
+   the partial aggregate function of the aggregate function is sent to
+   the remote server if the conditions in
+   <xref linkend="partial-aggregate-pushdown"/> are met.
+   A limitation is that by default, <filename>postgres_fdw</filename> assumes that
+   for each built-in aggregate function, the partial aggregate function is defined
+   on the remote server.
+   This limitation might cause <quote>function does not exist</quote>
+   or a similar error if the remote server version is older than the local server version.
+   This problem can be worked around by setting
+   <literal>check_partial_aggregate_support</literal> to <literal>true</literal>
+   or similar query rewriting as above.
   </para>
  </sect2>
 
diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml
index 222e0aa5c9..2875734dea 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>
@@ -246,6 +248,18 @@ CREATE [ OR REPLACE ] AGGREGATE <replaceable class="parameter">name</replaceable
    marked <literal>PARALLEL SAFE</literal> to enable parallel aggregation.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize a query containing aggregate
+   expressions for a partitioned table across one or more remote servers.
+   This optimization sends an aggregate expression to a remote server and the
+   aggregate is a partial aggregate on the local server. This optimization
+   requires an aggregate function called a <firstterm>partial aggregate
+   function</firstterm>. See
+   <xref linkend="partial-aggregate-pushdown"/> for the definition of
+   partial aggregate function. <literal>AGGPARTIALFUNC</literal> is a parameter
+   of the partial aggregate function.
+  </para>
+
   <para>
    Aggregates that behave like <function>MIN</function> or <function>MAX</function> can
    sometimes be optimized by looking into an index instead of scanning every
@@ -653,6 +667,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..607c859974 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,18 @@ SELECT percentile_disc(0.5) WITHIN GROUP (ORDER BY income) FROM households;
    parallel-safety markings on its support functions are not consulted.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize a query containing
+   aggregate expressions for a partitioned table across one or more
+   remote servers. This optimization sends an aggregate expression to
+   a remote server and the aggregate is a partial aggregate on the
+   local server. This optimization requires an aggregate function
+   called the <firstterm>partial aggregate function</firstterm>.
+   See
+   <xref linkend="partial-aggregate-pushdown"/> 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/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index a8cea5efe1..f195970967 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -206,7 +206,7 @@ static PathTarget *make_group_input_target(PlannerInfo *root,
 										   PathTarget *final_target);
 static PathTarget *make_partial_grouping_target(PlannerInfo *root,
 												PathTarget *grouping_target,
-												Node *havingQual);
+												GroupPathExtraData *extra);
 static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
 static void optimize_window_clauses(PlannerInfo *root,
 									WindowFuncLists *wflists);
@@ -5415,6 +5415,99 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
 	return set_pathtarget_cost_width(root, input_target);
 }
 
+/*
+ * setGroupClausePartial
+ *	  Generate a groupClause and a pathtarget for partial aggregate
+ *	  pushdown by FDW and set them to GroupPathExtraData.
+ */
+static void
+setGroupClausePartial(PathTarget *partial_target, List *non_group_exprs,
+					  List *groupClause, GroupPathExtraData *extra)
+{
+	int			exprno,
+				refno;
+	ListCell   *lc;
+	Index		maxRef = 0;
+	List	   *exprs_processed = NIL;
+	int			exprs_num = 0;
+
+	foreach(lc, groupClause)
+	{
+		SortGroupClause *sgc = (SortGroupClause *) lfirst(lc);
+
+		if (sgc->tleSortGroupRef > maxRef)
+			maxRef = sgc->tleSortGroupRef;
+	}
+	maxRef++;
+
+	extra->groupClausePartial = list_copy_deep(groupClause);
+	extra->partial_target = copy_pathtarget(partial_target);
+
+	if (partial_target->exprs)
+		exprs_num = partial_target->exprs->length;
+
+	foreach(lc, non_group_exprs)
+	{
+		Expr	   *expr = (Expr *) lfirst(lc);
+
+		refno = -1;
+		if (list_member(exprs_processed, expr) ||
+			(!IsA(expr, Var) && !IsA(expr, PlaceHolderVar)))
+			continue;
+		exprs_processed = lappend(exprs_processed, expr);
+		for (exprno = 0; exprno < exprs_num; exprno++)
+		{
+			Expr	   *target_expr = (Expr *) list_nth(partial_target->exprs, exprno);
+
+			if (equal(target_expr, expr))
+			{
+				refno = exprno;
+				break;
+			}
+		}
+		if (refno < 0)
+		{
+			SortGroupClause *grpcl = makeNode(SortGroupClause);
+
+			grpcl->tleSortGroupRef = maxRef++;
+			extra->groupClausePartial = lappend(extra->groupClausePartial, grpcl);
+			add_column_to_pathtarget(extra->partial_target, expr, grpcl->tleSortGroupRef);
+		}
+	}
+}
+
+/*
+ * adjustAggrefForPartial
+ * Adjust Aggrefs to put them in partial mode
+ */
+static void
+adjustAggrefForPartial(List *exprs)
+{
+	ListCell   *lc;
+
+	foreach(lc, exprs)
+	{
+		Aggref	   *aggref = (Aggref *) lfirst(lc);
+
+		if (IsA(aggref, Aggref))
+		{
+			Aggref	   *newaggref;
+
+			/*
+			 * We shouldn't need to copy the substructure of the Aggref node,
+			 * but flat-copy the node itself to avoid damaging other trees.
+			 */
+			newaggref = makeNode(Aggref);
+			memcpy(newaggref, aggref, sizeof(Aggref));
+
+			/* For now, assume serialization is required */
+			mark_partial_aggref(newaggref, AGGSPLIT_INITIAL_SERIAL);
+
+			lfirst(lc) = newaggref;
+		}
+	}
+}
+
 /*
  * make_partial_grouping_target
  *	  Generate appropriate PathTarget for output of partial aggregate
@@ -5430,11 +5523,15 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
  *
  * grouping_target is the tlist to be emitted by the topmost aggregation step.
  * havingQual represents the HAVING clause.
+ *
+ * Modified PathTarget cannot be used by FDW as-is to deparse this statement.
+ * So, before modifying PathTarget, setGroupClausePartial generates
+ * another Pathtarget and another list of SortGroupClauses.
  */
 static PathTarget *
 make_partial_grouping_target(PlannerInfo *root,
 							 PathTarget *grouping_target,
-							 Node *havingQual)
+							 GroupPathExtraData *extra)
 {
 	PathTarget *partial_target;
 	List	   *non_group_cols;
@@ -5476,8 +5573,8 @@ make_partial_grouping_target(PlannerInfo *root,
 	/*
 	 * If there's a HAVING clause, we'll need the Vars/Aggrefs it uses, too.
 	 */
-	if (havingQual)
-		non_group_cols = lappend(non_group_cols, havingQual);
+	if (extra->havingQual)
+		non_group_cols = lappend(non_group_cols, extra->havingQual);
 
 	/*
 	 * Pull out all the Vars, PlaceHolderVars, and Aggrefs mentioned in
@@ -5490,35 +5587,17 @@ make_partial_grouping_target(PlannerInfo *root,
 									  PVC_INCLUDE_AGGREGATES |
 									  PVC_RECURSE_WINDOWFUNCS |
 									  PVC_INCLUDE_PLACEHOLDERS);
-
+	setGroupClausePartial(partial_target, non_group_exprs, root->parse->groupClause, extra);
 	add_new_columns_to_pathtarget(partial_target, non_group_exprs);
+	add_new_columns_to_pathtarget(extra->partial_target, non_group_exprs);
 
 	/*
 	 * Adjust Aggrefs to put them in partial mode.  At this point all Aggrefs
 	 * are at the top level of the target list, so we can just scan the list
 	 * rather than recursing through the expression trees.
 	 */
-	foreach(lc, partial_target->exprs)
-	{
-		Aggref	   *aggref = (Aggref *) lfirst(lc);
-
-		if (IsA(aggref, Aggref))
-		{
-			Aggref	   *newaggref;
-
-			/*
-			 * We shouldn't need to copy the substructure of the Aggref node,
-			 * but flat-copy the node itself to avoid damaging other trees.
-			 */
-			newaggref = makeNode(Aggref);
-			memcpy(newaggref, aggref, sizeof(Aggref));
-
-			/* For now, assume serialization is required */
-			mark_partial_aggref(newaggref, AGGSPLIT_INITIAL_SERIAL);
-
-			lfirst(lc) = newaggref;
-		}
-	}
+	adjustAggrefForPartial(partial_target->exprs);
+	adjustAggrefForPartial(extra->partial_target->exprs);
 
 	/* clean up cruft */
 	list_free(non_group_exprs);
@@ -7148,7 +7227,7 @@ create_partial_grouping_paths(PlannerInfo *root,
 	 */
 	partially_grouped_rel->reltarget =
 		make_partial_grouping_target(root, grouped_rel->reltarget,
-									 extra->havingQual);
+									 extra);
 
 	if (!extra->partial_costs_set)
 	{
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 83aeef2751..b23d4f7994 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -13909,6 +13909,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;
@@ -13991,11 +13992,18 @@ 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 >= 170000)
+			appendPQExpBufferStr(query,
+								 "aggpartialfn\n");
+		else
+			appendPQExpBufferStr(query,
+								 "'-' AS aggpartialfn\n");
 
 		appendPQExpBufferStr(query,
 							 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
@@ -14021,6 +14029,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"));
@@ -14110,6 +14119,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 085149e258..3e4f8331ee 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 06435e8b92..b2f676fdc5 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',
@@ -5004,6 +5011,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',
@@ -5016,6 +5027,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',
@@ -6606,36 +6621,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' },
@@ -6658,6 +6698,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' },
@@ -6816,153 +6859,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',
@@ -6973,52 +7124,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/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 2027265b31..f97a70bf1a 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -3243,6 +3243,9 @@ typedef enum
  * havingQual gives list of quals to be applied after aggregation.
  * targetList gives list of columns to be projected.
  * patype is the type of partitionwise aggregation that is being performed.
+ * groupClausePartial is List of SortGroupClauses for partial aggregate
+ * 		pushdown by FDW
+ * partial_target is PathTarget for partial aggregate pushdown by FDW
  */
 typedef struct
 {
@@ -3257,6 +3260,8 @@ typedef struct
 	Node	   *havingQual;
 	List	   *targetList;
 	PartitionwiseAggregateType patype;
+	List	   *groupClausePartial;
+	PathTarget *partial_target;
 } GroupPathExtraData;
 
 /*
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.39.3



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

* Re: Partial aggregates pushdown
@ 2023-10-26 19:43  Bruce Momjian <[email protected]>
  parent: [email protected] <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: Bruce Momjian @ 2023-10-26 19:43 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: pgsql-hackers; Finnerty, Jim <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Alexander Pyhalov <[email protected]>

On Thu, Oct 26, 2023 at 11:11:09AM +0000, [email protected] wrote:
> > 	   and checks if the remote server version is older than the local server version.
> > 	   If so,
> > 	   <filename>postgres_fdw</filename>
> > -->	   assumes that for each built-in aggregate function, the partial aggregate function is not defined
> > -->	   on the remote server unless the partial aggregate function and the aggregate
> > -->	   function match.
> > 	   Otherwise <filename>postgres_fdw</filename> assumes that for each built-in aggregate function,
> > 	   the partial aggregate function is defined on the remote server.
> > 	   The default is <literal>false</literal>.
> > 	  </para>
> > 	 </listitem>
> > 	</varlistentry>
> > 
> > What does that marked sentence mean?  What is match?  Are one or both of these remote?  It sounds like you are
> > checking the local aggregate against the remote partial aggregate, but I don't see any code that does this in the patch.
> This sentence means that
> "If the partial aggregate function has the same OID as the aggregate function,
> then postgres_fdw assumes that for each built-in aggregate function, the partial aggregate function is not defined
>  on the remote server."
> "Match" means that the partial aggregate function has the same OID as the aggregate function in local server.
> But, in v30, there is no code which checks the partial aggregate function has the same OID as the aggregate function in local server.
> So I modified the code of is_builtin_aggpartialfunc_shippable().
> Also, I modified wording postgres-fdw.sgml.

Yes, that is what I needed.  Attached is a modification of your v31
patch (the most recent) that mostly improves the documentation and
comments.  What else needs to be done before committers start to review
this?

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

  Only you can decide what is important to you.


Attachments:

  [application/gzip] fdw_partial.diff.gz (33.4K, ../../[email protected]/2-fdw_partial.diff.gz)
  download

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

* RE: Partial aggregates pushdown
@ 2023-10-27 02:44  [email protected] <[email protected]>
  parent: Bruce Momjian <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

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

Hi Momjian.

Thank you for your improvement.
As a matter of detail, I think that the areas marked below are erroneous.

--
+   Pushdown causes aggregate function cals to send partial aggregate
                                         ^
+   function calls to the remote server. If the partial aggregate
+   function doesn't doesn't exist on the remote server, it causes
                     ^^^^^^^
--

> What else needs to be done before committers start to review
> this?
There are no others. May I make a new version of v31 with your
suggested improvements for the committer's review?

Sincerely yours,
Yuuki Fujii

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






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

* Re: Partial aggregates pushdown
@ 2023-10-27 03:06  Bruce Momjian <[email protected]>
  parent: [email protected] <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

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

On Fri, Oct 27, 2023 at 02:44:42AM +0000, [email protected] wrote:
> Hi Momjian.
> 
> Thank you for your improvement.
> As a matter of detail, I think that the areas marked below are erroneous.
> 
> --
> +   Pushdown causes aggregate function cals to send partial aggregate
>                                          ^
> +   function calls to the remote server. If the partial aggregate
> +   function doesn't doesn't exist on the remote server, it causes
>                      ^^^^^^^
> --

Agreed.  Do you want to fix that on your vesion?  I don't have any more
improvements to make.

> > What else needs to be done before committers start to review
> > this?
> There are no others. May I make a new version of v31 with your
> suggested improvements for the committer's review?

Yes, please.  I think the updated docs will help people understand how
the patch works.

-- 
  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] 38+ messages in thread

* RE: Partial aggregates pushdown
@ 2023-10-27 05:32  [email protected] <[email protected]>
  parent: Bruce Momjian <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: [email protected] @ 2023-10-27 05:32 UTC (permalink / raw)
  To: Bruce Momjian <[email protected]>; +Cc: pgsql-hackers; Finnerty, Jim <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Alexander Pyhalov <[email protected]>; [email protected] <[email protected]>

Hi Mr.Momjian.

> From: Bruce Momjian <[email protected]>
> Sent: Friday, October 27, 2023 12:06 PM
> > Thank you for your improvement.
> > As a matter of detail, I think that the areas marked below are erroneous.
> >
> > --
> > +   Pushdown causes aggregate function cals to send partial aggregate
> >                                          ^
> > +   function calls to the remote server. If the partial aggregate
> > +   function doesn't doesn't exist on the remote server, it causes
> >                      ^^^^^^^
> > --
> 
> Agreed.  Do you want to fix that on your vesion?  I don't have any more improvements to make.
Yes, I have fixed that in v32(Attached).

> > > What else needs to be done before committers start to review this?
> > There are no others. May I make a new version of v31 with your
> > suggested improvements for the committer's review?
> 
> Yes, please.  I think the updated docs will help people understand how the patch works.
Thank you. v32(Attached) is tha updated version.

Sincerely yours,
Yuuki Fujii

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


Attachments:

  [application/octet-stream] 0001-Partial-aggregates-push-down-v32.patch (265.9K, ../../TYTPR01MB10954B96601911FE868BF88E895DCA@TYTPR01MB10954.jpnprd01.prod.outlook.com/2-0001-Partial-aggregates-push-down-v32.patch)
  download | inline diff:
From 44915ac8f5b56b0ef5cdba217e727e2c729aa64a Mon Sep 17 00:00:00 2001
From: Yuki Fujii <[email protected]>
Date: Fri, 27 Oct 2023 09:47:28 -0400
Subject: [PATCH] Partial aggregates push down v32

---
 contrib/postgres_fdw/deparse.c                | 113 ++-
 .../postgres_fdw/expected/postgres_fdw.out    | 868 +++++++++++++++++-
 contrib/postgres_fdw/option.c                 |   4 +-
 contrib/postgres_fdw/postgres_fdw.c           |  47 +-
 contrib/postgres_fdw/postgres_fdw.h           |  10 +
 contrib/postgres_fdw/sql/postgres_fdw.sql     | 311 ++++++-
 doc/src/sgml/catalogs.sgml                    |  12 +
 doc/src/sgml/postgres-fdw.sgml                | 130 ++-
 doc/src/sgml/ref/create_aggregate.sgml        |  34 +
 doc/src/sgml/xaggr.sgml                       |  11 +-
 src/backend/catalog/pg_aggregate.c            | 101 ++
 src/backend/commands/aggregatecmds.c          |   4 +
 src/backend/optimizer/plan/planner.c          | 133 ++-
 src/bin/pg_dump/pg_dump.c                     |  16 +-
 src/include/catalog/pg_aggregate.dat          | 680 +++++++++++---
 src/include/catalog/pg_aggregate.h            |   4 +
 src/include/catalog/pg_proc.dat               | 196 ++++
 src/include/nodes/pathnodes.h                 |   5 +
 .../regress/expected/create_aggregate.out     | 184 ++++
 src/test/regress/expected/oidjoins.out        |   1 +
 src/test/regress/sql/create_aggregate.sql     | 183 ++++
 21 files changed, 2824 insertions(+), 223 deletions(-)

diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 09fd489a90..c443f8974a 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 */
@@ -3723,6 +3744,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	Query	   *query = context->root->parse;
 	ListCell   *lc;
 	bool		first = true;
+	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) context->foreignrel->fdw_private;
 
 	/* Nothing to be done, if there's no GROUP BY clause in the query. */
 	if (!query->groupClause)
@@ -3743,7 +3765,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	 * to empty, and in any case the redundancy situation on the remote might
 	 * be different than what we think here.
 	 */
-	foreach(lc, query->groupClause)
+	foreach(lc, fpinfo->group_clause)
 	{
 		SortGroupClause *grp = (SortGroupClause *) lfirst(lc);
 
@@ -4047,3 +4069,78 @@ get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
 	/* Shouldn't get here */
 	elog(ERROR, "unexpected expression in subquery output");
 }
+
+/*
+ * Can we assume a buit-in aggpartialfunc exists on the remote server? If
+ * check_partial_aggregate_support is false, we assume the partial aggregate
+ * function exists on the remote server. If true, we assume the partial
+ * aggregate function exists on the remote server if the aggregate is so
+ * simple that the local aggregate's OID and its partial aggregate function
+ * OID are the same.  If not, we check the remote server version and assume
+ * the remote partial aggregate function exists if the remote server version
+ * is equal to or greater than the local server version.
+ */
+static bool
+is_builtin_aggpartialfunc_shippable(Form_pg_aggregate aggform,
+									PgFdwRelationInfo *fpinfo)
+{
+	bool		shippable = true;
+
+	/* So simple, we assume it exists. */
+	if (aggform->aggfnoid != aggform->aggpartialfn &&
+		fpinfo->check_partial_aggregate_support)
+	{
+		if (fpinfo->remoteversion == 0)
+		{
+			PGconn	   *conn = GetConnection(fpinfo->user, false, NULL);
+
+			fpinfo->remoteversion = PQserverVersion(conn);
+		}
+
+		if (fpinfo->remoteversion < PG_VERSION_NUM)
+			shippable = false;
+	}
+
+	return shippable;
+}
+
+/*
+ * ----------
+ * Check that partial aggregate "agg" is safe to push down.
+ *
+ * It is pushdown-safe when all of the following conditions are true:
+ *
+ *    * agg is an AGGKIND_NORMAL aggregate which contains no DISTINCT or
+ *    ORDER BY clauses
+ *    * There is a local aggregate function for partial aggregation
+ *    (aggpartialfunc) and it is assumed to exist on the remote server
+ * ----------
+ */
+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_builtin(aggform->aggpartialfn))
+		partial_agg_ok = is_builtin_aggpartialfunc_shippable(aggform, fpinfo);
+	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 144c114d0f..d9febca7a0 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -9758,18 +9758,35 @@ 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.1');
+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');
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -9823,8 +9840,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
@@ -9835,21 +9852,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;
@@ -9863,28 +9880,837 @@ SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
  21 |   100
 (6 rows)
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are unsafe to push down when there is a HAVING clause
+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                            
------------------------------------------------------------------
- Sort
-   Sort Key: pagg_tab.b
-   ->  Finalize HashAggregate
-         Group Key: pagg_tab.b
-         Filter: (sum(pagg_tab.a) < 700)
+                                                                 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 fpagg_tab_p1 pagg_tab
+                     ->  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 fpagg_tab_p2 pagg_tab_1
+                     ->  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 fpagg_tab_p3 pagg_tab_2
+                     ->  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)
+
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
+ b  |         avg         | max | count 
+----+---------------------+-----+-------
+  0 | 10.0000000000000000 |  20 |    60
+  1 | 11.0000000000000000 |  21 |    60
+ 10 | 10.0000000000000000 |  20 |    60
+ 11 | 11.0000000000000000 |  21 |    60
+ 20 | 10.0000000000000000 |  20 |    60
+ 21 | 11.0000000000000000 |  21 |    60
+ 30 | 10.0000000000000000 |  20 |    60
+ 31 | 11.0000000000000000 |  21 |    60
+ 40 | 10.0000000000000000 |  20 |    60
+ 41 | 11.0000000000000000 |  21 |    60
+(10 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+                                                                  QUERY PLAN                                                                  
+----------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: (avg(pagg_tab.a)), (max(pagg_tab.a)), (count(*)), ((((pagg_tab.b / 2)))::numeric), ((pagg_tab.b / 2))
+   Sort Key: ((((pagg_tab.b / 2)))::numeric)
+   ->  Finalize HashAggregate
+         Output: avg(pagg_tab.a), max(pagg_tab.a), count(*), (((pagg_tab.b / 2)))::numeric, ((pagg_tab.b / 2))
+         Group Key: ((pagg_tab.b / 2))
+         ->  Append
+               ->  Foreign Scan
+                     Output: ((pagg_tab.b / 2)), (PARTIAL avg(pagg_tab.a)), (PARTIAL max(pagg_tab.a)), (PARTIAL count(*)), pagg_tab.b
+                     Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p1 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_1.b / 2)), (PARTIAL avg(pagg_tab_1.a)), (PARTIAL max(pagg_tab_1.a)), (PARTIAL count(*)), pagg_tab_1.b
+                     Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p2 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_2.b / 2)), (PARTIAL avg(pagg_tab_2.a)), (PARTIAL max(pagg_tab_2.a)), (PARTIAL count(*)), pagg_tab_2.b
+                     Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p3 GROUP BY 1, 2
+(19 rows)
+
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+         avg         | max | count | numeric 
+---------------------+-----+-------+---------
+ 10.5000000000000000 |  21 |   120 |       0
+ 12.5000000000000000 |  23 |   120 |       1
+ 14.5000000000000000 |  25 |   120 |       2
+ 16.5000000000000000 |  27 |   120 |       3
+ 18.5000000000000000 |  29 |   120 |       4
+ 10.5000000000000000 |  21 |   120 |       5
+ 12.5000000000000000 |  23 |   120 |       6
+ 14.5000000000000000 |  25 |   120 |       7
+ 16.5000000000000000 |  27 |   120 |       8
+ 18.5000000000000000 |  29 |   120 |       9
+ 10.5000000000000000 |  21 |   120 |      10
+ 12.5000000000000000 |  23 |   120 |      11
+ 14.5000000000000000 |  25 |   120 |      12
+ 16.5000000000000000 |  27 |   120 |      13
+ 18.5000000000000000 |  29 |   120 |      14
+ 10.5000000000000000 |  21 |   120 |      15
+ 12.5000000000000000 |  23 |   120 |      16
+ 14.5000000000000000 |  25 |   120 |      17
+ 16.5000000000000000 |  27 |   120 |      18
+ 18.5000000000000000 |  29 |   120 |      19
+ 10.5000000000000000 |  21 |   120 |      20
+ 12.5000000000000000 |  23 |   120 |      21
+ 14.5000000000000000 |  25 |   120 |      22
+ 16.5000000000000000 |  27 |   120 |      23
+ 18.5000000000000000 |  29 |   120 |      24
+(25 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)
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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)
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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)
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+                                                                           QUERY PLAN                                                                           
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: avg(t1.b), avg((t1.b)::bigint)
+   ->  Append
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1.b)), (PARTIAL avg((t1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p1 t1) INNER JOIN (public.fpagg_tab_p1 t2))
+               Remote SQL: SELECT avg_p_int4(r4.b), avg_p_int8(r4.b::bigint) FROM (public.pagg_tab_p1 r4 INNER JOIN public.pagg_tab_p1 r7 ON (((r4.a = r7.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_1.b)), (PARTIAL avg((t1_1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p2 t1_1) INNER JOIN (public.fpagg_tab_p2 t2_1))
+               Remote SQL: SELECT avg_p_int4(r5.b), avg_p_int8(r5.b::bigint) FROM (public.pagg_tab_p2 r5 INNER JOIN public.pagg_tab_p2 r8 ON (((r5.a = r8.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_2.b)), (PARTIAL avg((t1_2.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p3 t1_2) INNER JOIN (public.fpagg_tab_p3 t2_2))
+               Remote SQL: SELECT avg_p_int4(r6.b), avg_p_int8(r6.b::bigint) FROM (public.pagg_tab_p3 r6 INNER JOIN public.pagg_tab_p3 r9 ON (((r6.a = r9.a))))
 (15 rows)
 
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+         avg         |         avg         
+---------------------+---------------------
+ 24.5000000000000000 | 24.5000000000000000
+(1 row)
+
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+-- 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 is unsafe to push down partial aggregates which contain DISTINCT clauses
+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 is unsafe to push down partial aggregates which contain ORDER BY clauses
+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 aggregates for user-defined aggregate
+-- when it belongs to an extension that is listed in the extension setting
+-- and when its aggpartialfunc belongs to an extension that is listed
+-- in the extension setting.
+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 is unsafe to push down partial aggregates for user-defined aggregates
+-- when they have 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 is unsafe to push down partial aggregates for user-defined aggregates
+-- when itself is aggpartialfunc and when it belongs to an extension
+-- that's listed in the extension setting 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 is unsafe to push down partial aggregates for user-defined aggregates
+-- when it doesn't belong to an extension that is listed in the extension setting
+-- and when its aggpartialfunc belongs to an extension that is listed
+-- in the extension setting.
+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 aggregates for user-defined aggregate
+-- whose stype is not internal
+-- when it belongs to an extension that is listed in the extension setting
+-- and when its aggpartialfunc belongs to an extension that is listed
+-- in the extension setting.
+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 aggregates for user-defined aggregate
+-- when itself is aggpartialfunc and when it belongs to an extension
+-- that's listed in the extension setting 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/option.c b/contrib/postgres_fdw/option.c
index 8c822f4ef9..67e15c8bb9 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -126,7 +126,8 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
 			strcmp(def->defname, "async_capable") == 0 ||
 			strcmp(def->defname, "parallel_commit") == 0 ||
 			strcmp(def->defname, "parallel_abort") == 0 ||
-			strcmp(def->defname, "keep_connections") == 0)
+			strcmp(def->defname, "keep_connections") == 0 ||
+			strcmp(def->defname, "check_partial_aggregate_support") == 0)
 		{
 			/* these accept only boolean values */
 			(void) defGetBoolean(def);
@@ -268,6 +269,7 @@ InitPgFdwOptions(void)
 		/* batch_size is available on both server and table */
 		{"batch_size", ForeignServerRelationId, false},
 		{"batch_size", ForeignTableRelationId, false},
+		{"check_partial_aggregate_support", ForeignServerRelationId, false},
 		/* async_capable is available on both server and table */
 		{"async_capable", ForeignServerRelationId, false},
 		{"async_capable", ForeignTableRelationId, false},
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 8b3206ceaa..20e2bfa2d7 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -36,6 +36,7 @@
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
+#include "optimizer/planner.h"
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
@@ -519,7 +520,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);
+								GroupPathExtraData *extra);
 static List *get_useful_pathkeys_for_relation(PlannerInfo *root,
 											  RelOptInfo *rel);
 static List *get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel);
@@ -650,6 +651,8 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	fpinfo->fdw_tuple_cost = DEFAULT_FDW_TUPLE_COST;
 	fpinfo->shippable_extensions = NIL;
 	fpinfo->fetch_size = 100;
+	fpinfo->check_partial_aggregate_support = false;
+	fpinfo->remoteversion = 0;
 	fpinfo->async_capable = false;
 
 	apply_server_options(fpinfo);
@@ -661,7 +664,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	 * should match what ExecCheckPermissions() does.  If we fail due to lack
 	 * of permissions, the query would have failed at runtime anyway.
 	 */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
 		Oid			userid;
 
@@ -5948,9 +5951,10 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
 	fpinfo->pushdown_safe = true;
 
 	/* Get user mapping */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
-		if (fpinfo_o->use_remote_estimate)
+		if (fpinfo_o->use_remote_estimate ||
+			fpinfo_o->check_partial_aggregate_support)
 			fpinfo->user = fpinfo_o->user;
 		else
 			fpinfo->user = fpinfo_i->user;
@@ -6132,6 +6136,8 @@ apply_server_options(PgFdwRelationInfo *fpinfo)
 				ExtractExtensionList(defGetString(def), false);
 		else if (strcmp(def->defname, "fetch_size") == 0)
 			(void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
+		else if (strcmp(def->defname, "check_partial_aggregate_support") == 0)
+			fpinfo->check_partial_aggregate_support = defGetBoolean(def);
 		else if (strcmp(def->defname, "async_capable") == 0)
 			fpinfo->async_capable = defGetBoolean(def);
 	}
@@ -6191,6 +6197,8 @@ merge_fdw_options(PgFdwRelationInfo *fpinfo,
 	fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
 	fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
 	fpinfo->fetch_size = fpinfo_o->fetch_size;
+	fpinfo->check_partial_aggregate_support = fpinfo_o->check_partial_aggregate_support;
+	fpinfo->remoteversion = fpinfo_o->remoteversion;
 	fpinfo->async_capable = fpinfo_o->async_capable;
 
 	/* Merge the table level options from either side of the join. */
@@ -6373,7 +6381,7 @@ postgresGetForeignJoinPaths(PlannerInfo *root,
  */
 static bool
 foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
-					Node *havingQual)
+					GroupPathExtraData *extra)
 {
 	Query	   *query = root->parse;
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
@@ -6382,11 +6390,16 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	ListCell   *lc;
 	int			i;
 	List	   *tlist = NIL;
+	bool		partial = extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL;
 
 	/* We currently don't support pushing Grouping Sets. */
 	if (query->groupingSets)
 		return false;
 
+	/* It is unsafe to push HAVING statements with partial aggregates */
+	if ((extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL) && extra->havingQual)
+		return false;
+
 	/* Get the fpinfo of the underlying scan relation. */
 	ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
 
@@ -6415,6 +6428,12 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * a node, as long as it's not at top level; then no match is possible.
 	 */
 	i = 0;
+	fpinfo->group_clause = query->groupClause;
+	if (partial)
+	{
+		fpinfo->group_clause = extra->groupClausePartial;
+		grouping_target = extra->partial_target;
+	}
 	foreach(lc, grouping_target->exprs)
 	{
 		Expr	   *expr = (Expr *) lfirst(lc);
@@ -6426,7 +6445,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 		 * check the whole GROUP BY clause not just processed_groupClause,
 		 * because we will ship all of it, cf. appendGroupByClause.
 		 */
-		if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
+		if (sgref && get_sortgroupref_clause_noerr(sgref, fpinfo->group_clause))
 		{
 			TargetEntry *tle;
 
@@ -6512,9 +6531,9 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * Classify the pushable and non-pushable HAVING clauses and save them in
 	 * remote_conds and local_conds of the grouped rel's fpinfo.
 	 */
-	if (havingQual)
+	if (extra->havingQual)
 	{
-		foreach(lc, (List *) havingQual)
+		foreach(lc, (List *) extra->havingQual)
 		{
 			Expr	   *expr = (Expr *) lfirst(lc);
 			RestrictInfo *rinfo;
@@ -6534,7 +6553,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 									  grouped_rel->relids,
 									  NULL,
 									  NULL);
-			if (is_foreign_expr(root, grouped_rel, expr))
+			if (is_foreign_expr(root, grouped_rel, expr) && !partial)
 				fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
 			else
 				fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
@@ -6628,6 +6647,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)
@@ -6644,6 +6664,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;
@@ -6684,7 +6708,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;
@@ -6704,7 +6729,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))
 		return;
 
 	/*
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index 47157ac887..a1a9688c9f 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -86,6 +86,15 @@ typedef struct PgFdwRelationInfo
 	ForeignServer *server;
 	UserMapping *user;			/* only set in use_remote_estimate mode */
 
+	/* for partial aggregate pushdown */
+	bool		check_partial_aggregate_support;
+
+	/*
+	 * If remoteversion is zero, it means the remote server version has not
+	 * been acquired.
+	 */
+	int			remoteversion;
+
 	int			fetch_size;		/* fetch size for this remote table */
 
 	/*
@@ -110,6 +119,7 @@ typedef struct PgFdwRelationInfo
 
 	/* Grouping information */
 	List	   *grouped_tlist;
+	List	   *group_clause;
 
 	/* Subquery information */
 	bool		make_outerrel_subquery; /* do we deparse outerrel as a
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index a303bfb322..92326c07eb 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -2989,16 +2989,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.1');
+
+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,9 @@ CREATE FOREIGN TABLE fpagg_tab_p2 PARTITION OF pagg_tab FOR VALUES FROM (10) TO
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -3028,10 +3046,289 @@ EXPLAIN (VERBOSE, COSTS OFF)
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are unsafe to push down when there is a HAVING clause
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+
+-- 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;
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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;
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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;
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+
+-- 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 is unsafe to push down partial aggregates which contain DISTINCT clauses
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT max(a), count(distinct b) FROM pagg_tab;
+SELECT max(a), count(distinct b) FROM pagg_tab;
+
+-- It is unsafe to push down partial aggregates which contain ORDER BY clauses
+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 aggregates for user-defined aggregate
+-- when it belongs to an extension that is listed in the extension setting
+-- and when its aggpartialfunc belongs to an extension that is listed
+-- in the extension setting.
+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 is unsafe to push down partial aggregates for user-defined aggregates
+-- when they have 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 is unsafe to push down partial aggregates for user-defined aggregates
+-- when itself is aggpartialfunc and when it belongs to an extension
+-- that's listed in the extension setting 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 is unsafe to push down partial aggregates for user-defined aggregates
+-- when it doesn't belong to an extension that is listed in the extension setting
+-- and when its aggpartialfunc belongs to an extension that is listed
+-- in the extension setting.
+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 aggregates for user-defined aggregate
+-- whose stype is not internal
+-- when it belongs to an extension that is listed in the extension setting
+-- and when its aggpartialfunc belongs to an extension that is listed
+-- in the extension setting.
+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 aggregates for user-defined aggregate
+-- when itself is aggpartialfunc and when it belongs to an extension
+-- that's listed in the extension setting 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 3ec7391ec5..a6dc334726 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -644,6 +644,18 @@
        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="partial-aggregate-pushdown"/> 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 33cc6e07b7..1623e46054 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -451,6 +451,43 @@ OPTIONS (ADD password_required 'false');
 
   </sect3>
 
+  <sect3 id="postgres-fdw-options-partial-aggregate-pushdown">
+   <title>Partial Aggregate Pushdown Options</title>
+
+   <para>
+    By default, <filename>postgres_fdw</filename> assumes that each
+    built-in aggregate function has a partial aggregate function defined on
+    the remote server;  see <xref linkend="partial-aggregate-pushdown"/>
+    for a definition of the partial aggregate functions. This may be
+    overridden using the following option:
+   </para>
+
+   <variablelist>
+
+    <varlistentry>
+     <term><literal>check_partial_aggregate_support</literal> (<type>boolean</type>)</term>
+     <listitem>
+      <para>
+       If this option is false, <filename>postgres_fdw</filename> always
+       uses partial aggregate pushdown by assuming that each built-in
+       aggregate function has a partial aggregate function defined on
+       the remote server.  If this option is true, local aggregates
+       whose partial computation function references itself are assumed
+       to exist on the remote server.  If not, during query planning,
+       <filename>postgres_fdw</filename> will connect to the remote
+       server and retrieve the remote server version.  If the remote
+       version is the same or newer, partial aggregate functions will be
+       assumed to exist.  If older, <filename>postgres_fdw</filename>
+       checks that the remote server has a matching partial aggregate
+       function before performing partial aggregate pushdown.  The default
+       is <literal>false</literal>.
+      </para>
+     </listitem>
+    </varlistentry>
+
+   </variablelist>
+  </sect3>
+
   <sect3 id="postgres-fdw-options-asynchronous-execution">
    <title>Asynchronous Execution Options</title>
 
@@ -929,14 +966,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
-   be <literal>IMMUTABLE</literal> as well.
+   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 only use data types, operators, and functions that are built-in
+   or belong to an extension that is 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
    sending the whole query to the remote server if there are no query
@@ -966,6 +1004,64 @@ postgres=# SELECT postgres_fdw_disconnect_all();
   </para>
  </sect2>
 
+ <sect2 id="partial-aggregate-pushdown">
+  <title>Partial aggregate pushdown</title>
+  <para>
+   Partial aggregate pushdown is an optimization for queries that contains
+   aggregate expressions for a partitioned table across one or more remote
+   servers. If multiple conditions are met, partial aggregate function
+   calls are sent to the remote servers.  The conditions under which this
+   sending is active are as follows.
+
+   <itemizedlist spacing="compact">
+    <listitem>
+     <para>
+      A partial aggregate function which corresponds to the aggregate
+      expression is defined on the local server and assumed to exist on
+      the remote server
+     </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>
+      The aggregate expressions in the query do not contain
+      <literal>DISTINCT</literal> or <literal>ORDER BY</literal> clauses
+     </para>
+    </listitem>
+   </itemizedlist>
+
+   When partial aggregate pushdown is used for aggregate expressions,
+   remote queries replace aggregate function calls with partial
+   aggregate function calls.  If the data type of the state value is not
+   <type>internal</type>, the partial aggregate function must not have
+   a final function.  If <type>internal</type>, the partial aggregate
+   function must have the same final function as the aggregate function's
+   serialization function, and 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 the
+   corresponding members in the aggregate function.  Built-in aggregate
+   functions that support partial aggregation must also have their
+   partial aggregate functions defined as built-in aggregates.  See <xref
+   linkend="postgres-fdw-options-partial-aggregate-pushdown"/> for details
+   on how the existance of remote-server partial aggregates are checked.
+   <filename>postgres_fdw</filename> assumes user-defined aggregate
+   functions have partial aggregate functions on the remote server only
+   if both belong to extensions listed in the foreign server's extensions
+   option.
+  </para>
+ </sect2>
+
  <sect2 id="postgres-fdw-remote-query-execution-environment">
   <title>Remote Query Execution Environment</title>
 
@@ -1031,15 +1127,25 @@ 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>.
   </para>
+
+  <para>
+   A similar problem can occur with partial aggregate pushdown.
+   Pushdown causes aggregate function calls to send partial aggregate
+   function calls to the remote server. If the partial aggregate
+   function doesn't exist on the remote server, it causes
+   <quote>function does not exist</quote> or a similar error; see
+   <xref linkend="postgres-fdw-options-partial-aggregate-pushdown"/>
+   on how to enable remote server checks.
+  </para>
  </sect2>
 
  <sect2 id="postgres-fdw-wait-events">
diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml
index 222e0aa5c9..12beb79dc7 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>
@@ -246,6 +248,12 @@ CREATE [ OR REPLACE ] AGGREGATE <replaceable class="parameter">name</replaceable
    marked <literal>PARALLEL SAFE</literal> to enable parallel aggregation.
   </para>
 
+  <para>
+   Paraemter <literal>AGGPARTIALFUNC</literal> optionally defines a
+   partial aggregate function used for partial aggregate pushdown; see
+   <xref linkend="xaggr-partial-aggregates"/> for details.
+  </para>
+
   <para>
    Aggregates that behave like <function>MIN</function> or <function>MAX</function> can
    sometimes be optimized by looking into an index instead of scanning every
@@ -653,6 +661,32 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">aggpartialfunc</replaceable></term>
+    <listitem>
+     <para>
+      Parameter <replaceable class="parameter">aggpartialfunc</replaceable>
+      reprsents the name of this aggregate function's
+      partial aggregate function.  If specified, <replaceable
+      class="parameter">combinefunc</replaceable> must also be specified.
+      If <replaceable class="parameter">state_data_type</replaceable>
+      is not <type>internal</type>, the partial aggregate function must
+      not have the <replaceable class="parameter">ffunc</replaceable>.
+      If <replaceable class="parameter">state_data_type</replaceable>
+      is <type>internal</type>, the partial aggregate function
+      must have the same final function as <replaceable
+      class="parameter">serialfunc</replaceable>.  The partial
+      aggregate function and the aggregate function must 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..2863731d00 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,14 @@ SELECT percentile_disc(0.5) WITHIN GROUP (ORDER BY income) FROM households;
    parallel-safety markings on its support functions are not consulted.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize queries containing
+   aggregate expressions on partitioned tables that span multiple
+   remote servers. This optimization sends <firstterm>partial
+   aggregate functions</firstterm> to remote servers;  see <xref
+   linkend="partial-aggregate-pushdown"/> for details.
+  </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/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index a8cea5efe1..f195970967 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -206,7 +206,7 @@ static PathTarget *make_group_input_target(PlannerInfo *root,
 										   PathTarget *final_target);
 static PathTarget *make_partial_grouping_target(PlannerInfo *root,
 												PathTarget *grouping_target,
-												Node *havingQual);
+												GroupPathExtraData *extra);
 static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
 static void optimize_window_clauses(PlannerInfo *root,
 									WindowFuncLists *wflists);
@@ -5415,6 +5415,99 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
 	return set_pathtarget_cost_width(root, input_target);
 }
 
+/*
+ * setGroupClausePartial
+ *	  Generate a groupClause and a pathtarget for partial aggregate
+ *	  pushdown by FDW and set them to GroupPathExtraData.
+ */
+static void
+setGroupClausePartial(PathTarget *partial_target, List *non_group_exprs,
+					  List *groupClause, GroupPathExtraData *extra)
+{
+	int			exprno,
+				refno;
+	ListCell   *lc;
+	Index		maxRef = 0;
+	List	   *exprs_processed = NIL;
+	int			exprs_num = 0;
+
+	foreach(lc, groupClause)
+	{
+		SortGroupClause *sgc = (SortGroupClause *) lfirst(lc);
+
+		if (sgc->tleSortGroupRef > maxRef)
+			maxRef = sgc->tleSortGroupRef;
+	}
+	maxRef++;
+
+	extra->groupClausePartial = list_copy_deep(groupClause);
+	extra->partial_target = copy_pathtarget(partial_target);
+
+	if (partial_target->exprs)
+		exprs_num = partial_target->exprs->length;
+
+	foreach(lc, non_group_exprs)
+	{
+		Expr	   *expr = (Expr *) lfirst(lc);
+
+		refno = -1;
+		if (list_member(exprs_processed, expr) ||
+			(!IsA(expr, Var) && !IsA(expr, PlaceHolderVar)))
+			continue;
+		exprs_processed = lappend(exprs_processed, expr);
+		for (exprno = 0; exprno < exprs_num; exprno++)
+		{
+			Expr	   *target_expr = (Expr *) list_nth(partial_target->exprs, exprno);
+
+			if (equal(target_expr, expr))
+			{
+				refno = exprno;
+				break;
+			}
+		}
+		if (refno < 0)
+		{
+			SortGroupClause *grpcl = makeNode(SortGroupClause);
+
+			grpcl->tleSortGroupRef = maxRef++;
+			extra->groupClausePartial = lappend(extra->groupClausePartial, grpcl);
+			add_column_to_pathtarget(extra->partial_target, expr, grpcl->tleSortGroupRef);
+		}
+	}
+}
+
+/*
+ * adjustAggrefForPartial
+ * Adjust Aggrefs to put them in partial mode
+ */
+static void
+adjustAggrefForPartial(List *exprs)
+{
+	ListCell   *lc;
+
+	foreach(lc, exprs)
+	{
+		Aggref	   *aggref = (Aggref *) lfirst(lc);
+
+		if (IsA(aggref, Aggref))
+		{
+			Aggref	   *newaggref;
+
+			/*
+			 * We shouldn't need to copy the substructure of the Aggref node,
+			 * but flat-copy the node itself to avoid damaging other trees.
+			 */
+			newaggref = makeNode(Aggref);
+			memcpy(newaggref, aggref, sizeof(Aggref));
+
+			/* For now, assume serialization is required */
+			mark_partial_aggref(newaggref, AGGSPLIT_INITIAL_SERIAL);
+
+			lfirst(lc) = newaggref;
+		}
+	}
+}
+
 /*
  * make_partial_grouping_target
  *	  Generate appropriate PathTarget for output of partial aggregate
@@ -5430,11 +5523,15 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
  *
  * grouping_target is the tlist to be emitted by the topmost aggregation step.
  * havingQual represents the HAVING clause.
+ *
+ * Modified PathTarget cannot be used by FDW as-is to deparse this statement.
+ * So, before modifying PathTarget, setGroupClausePartial generates
+ * another Pathtarget and another list of SortGroupClauses.
  */
 static PathTarget *
 make_partial_grouping_target(PlannerInfo *root,
 							 PathTarget *grouping_target,
-							 Node *havingQual)
+							 GroupPathExtraData *extra)
 {
 	PathTarget *partial_target;
 	List	   *non_group_cols;
@@ -5476,8 +5573,8 @@ make_partial_grouping_target(PlannerInfo *root,
 	/*
 	 * If there's a HAVING clause, we'll need the Vars/Aggrefs it uses, too.
 	 */
-	if (havingQual)
-		non_group_cols = lappend(non_group_cols, havingQual);
+	if (extra->havingQual)
+		non_group_cols = lappend(non_group_cols, extra->havingQual);
 
 	/*
 	 * Pull out all the Vars, PlaceHolderVars, and Aggrefs mentioned in
@@ -5490,35 +5587,17 @@ make_partial_grouping_target(PlannerInfo *root,
 									  PVC_INCLUDE_AGGREGATES |
 									  PVC_RECURSE_WINDOWFUNCS |
 									  PVC_INCLUDE_PLACEHOLDERS);
-
+	setGroupClausePartial(partial_target, non_group_exprs, root->parse->groupClause, extra);
 	add_new_columns_to_pathtarget(partial_target, non_group_exprs);
+	add_new_columns_to_pathtarget(extra->partial_target, non_group_exprs);
 
 	/*
 	 * Adjust Aggrefs to put them in partial mode.  At this point all Aggrefs
 	 * are at the top level of the target list, so we can just scan the list
 	 * rather than recursing through the expression trees.
 	 */
-	foreach(lc, partial_target->exprs)
-	{
-		Aggref	   *aggref = (Aggref *) lfirst(lc);
-
-		if (IsA(aggref, Aggref))
-		{
-			Aggref	   *newaggref;
-
-			/*
-			 * We shouldn't need to copy the substructure of the Aggref node,
-			 * but flat-copy the node itself to avoid damaging other trees.
-			 */
-			newaggref = makeNode(Aggref);
-			memcpy(newaggref, aggref, sizeof(Aggref));
-
-			/* For now, assume serialization is required */
-			mark_partial_aggref(newaggref, AGGSPLIT_INITIAL_SERIAL);
-
-			lfirst(lc) = newaggref;
-		}
-	}
+	adjustAggrefForPartial(partial_target->exprs);
+	adjustAggrefForPartial(extra->partial_target->exprs);
 
 	/* clean up cruft */
 	list_free(non_group_exprs);
@@ -7148,7 +7227,7 @@ create_partial_grouping_paths(PlannerInfo *root,
 	 */
 	partially_grouped_rel->reltarget =
 		make_partial_grouping_target(root, grouped_rel->reltarget,
-									 extra->havingQual);
+									 extra);
 
 	if (!extra->partial_costs_set)
 	{
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 7afdbf4d9d..ec9ca8e7f4 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -13909,6 +13909,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;
@@ -13991,11 +13992,18 @@ 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 >= 170000)
+			appendPQExpBufferStr(query,
+								 "aggpartialfn\n");
+		else
+			appendPQExpBufferStr(query,
+								 "'-' AS aggpartialfn\n");
 
 		appendPQExpBufferStr(query,
 							 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
@@ -14021,6 +14029,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"));
@@ -14110,6 +14119,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 085149e258..3e4f8331ee 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 bc41e92677..7b27e8cebf 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',
@@ -5004,6 +5011,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',
@@ -5016,6 +5027,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',
@@ -6597,36 +6612,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' },
@@ -6649,6 +6689,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' },
@@ -6807,153 +6850,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',
@@ -6964,52 +7115,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/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index ed85dc7414..411a249482 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -3243,6 +3243,9 @@ typedef enum
  * havingQual gives list of quals to be applied after aggregation.
  * targetList gives list of columns to be projected.
  * patype is the type of partitionwise aggregation that is being performed.
+ * groupClausePartial is List of SortGroupClauses for partial aggregate
+ * 		pushdown by FDW
+ * partial_target is PathTarget for partial aggregate pushdown by FDW
  */
 typedef struct
 {
@@ -3257,6 +3260,8 @@ typedef struct
 	Node	   *havingQual;
 	List	   *targetList;
 	PartitionwiseAggregateType patype;
+	List	   *groupClausePartial;
+	PathTarget *partial_target;
 } GroupPathExtraData;
 
 /*
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.39.3



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

* RE: Partial aggregates pushdown
@ 2023-11-13 05:48  [email protected] <[email protected]>
  parent: [email protected] <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: [email protected] @ 2023-11-13 05:48 UTC (permalink / raw)
  To: pgsql-hackers; +Cc: Finnerty, Jim <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Bruce Momjian <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Alexander Pyhalov <[email protected]>; [email protected] <[email protected]>

Hi Hackers.

I have rebased this patch.

Sincerely yours,
Yuuki Fujii

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


Attachments:

  [application/octet-stream] 0001-Partial-aggregates-push-down-v33.patch (268.0K, ../../OSZPR01MB9424BB7E885791D88B80F7B395B3A@OSZPR01MB9424.jpnprd01.prod.outlook.com/2-0001-Partial-aggregates-push-down-v33.patch)
  download | inline diff:
From 10f21023283c61ba2e610dd49317a5be49fff610 Mon Sep 17 00:00:00 2001
From: Yuki Fujii <[email protected]>
Date: Mon, 13 Nov 2023 07:54:10 -0500
Subject: [PATCH] Partial aggregates push down v33

---
 contrib/postgres_fdw/deparse.c                | 113 ++-
 .../postgres_fdw/expected/postgres_fdw.out    | 937 +++++++++++++++++-
 contrib/postgres_fdw/option.c                 |   4 +-
 contrib/postgres_fdw/postgres_fdw.c           |  47 +-
 contrib/postgres_fdw/postgres_fdw.h           |  10 +
 contrib/postgres_fdw/sql/postgres_fdw.sql     | 311 +++++-
 doc/src/sgml/catalogs.sgml                    |  12 +
 doc/src/sgml/postgres-fdw.sgml                | 130 ++-
 doc/src/sgml/ref/create_aggregate.sgml        |  34 +
 doc/src/sgml/xaggr.sgml                       |  11 +-
 src/backend/catalog/pg_aggregate.c            | 101 ++
 src/backend/commands/aggregatecmds.c          |   4 +
 src/backend/optimizer/plan/planner.c          | 133 ++-
 src/bin/pg_dump/pg_dump.c                     |  16 +-
 src/include/catalog/pg_aggregate.dat          | 680 ++++++++++---
 src/include/catalog/pg_aggregate.h            |   4 +
 src/include/catalog/pg_proc.dat               | 196 ++++
 src/include/nodes/pathnodes.h                 |   5 +
 .../regress/expected/create_aggregate.out     | 184 ++++
 src/test/regress/expected/oidjoins.out        |   1 +
 src/test/regress/sql/create_aggregate.sql     | 183 ++++
 21 files changed, 2861 insertions(+), 255 deletions(-)

diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 09fd489a90..c443f8974a 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 */
@@ -3723,6 +3744,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	Query	   *query = context->root->parse;
 	ListCell   *lc;
 	bool		first = true;
+	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) context->foreignrel->fdw_private;
 
 	/* Nothing to be done, if there's no GROUP BY clause in the query. */
 	if (!query->groupClause)
@@ -3743,7 +3765,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	 * to empty, and in any case the redundancy situation on the remote might
 	 * be different than what we think here.
 	 */
-	foreach(lc, query->groupClause)
+	foreach(lc, fpinfo->group_clause)
 	{
 		SortGroupClause *grp = (SortGroupClause *) lfirst(lc);
 
@@ -4047,3 +4069,78 @@ get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
 	/* Shouldn't get here */
 	elog(ERROR, "unexpected expression in subquery output");
 }
+
+/*
+ * Can we assume a buit-in aggpartialfunc exists on the remote server? If
+ * check_partial_aggregate_support is false, we assume the partial aggregate
+ * function exists on the remote server. If true, we assume the partial
+ * aggregate function exists on the remote server if the aggregate is so
+ * simple that the local aggregate's OID and its partial aggregate function
+ * OID are the same.  If not, we check the remote server version and assume
+ * the remote partial aggregate function exists if the remote server version
+ * is equal to or greater than the local server version.
+ */
+static bool
+is_builtin_aggpartialfunc_shippable(Form_pg_aggregate aggform,
+									PgFdwRelationInfo *fpinfo)
+{
+	bool		shippable = true;
+
+	/* So simple, we assume it exists. */
+	if (aggform->aggfnoid != aggform->aggpartialfn &&
+		fpinfo->check_partial_aggregate_support)
+	{
+		if (fpinfo->remoteversion == 0)
+		{
+			PGconn	   *conn = GetConnection(fpinfo->user, false, NULL);
+
+			fpinfo->remoteversion = PQserverVersion(conn);
+		}
+
+		if (fpinfo->remoteversion < PG_VERSION_NUM)
+			shippable = false;
+	}
+
+	return shippable;
+}
+
+/*
+ * ----------
+ * Check that partial aggregate "agg" is safe to push down.
+ *
+ * It is pushdown-safe when all of the following conditions are true:
+ *
+ *    * agg is an AGGKIND_NORMAL aggregate which contains no DISTINCT or
+ *    ORDER BY clauses
+ *    * There is a local aggregate function for partial aggregation
+ *    (aggpartialfunc) and it is assumed to exist on the remote server
+ * ----------
+ */
+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_builtin(aggform->aggpartialfn))
+		partial_agg_ok = is_builtin_aggpartialfunc_shippable(aggform, fpinfo);
+	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 64bcc66b8d..1d4438e6ea 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -9757,18 +9757,35 @@ 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.1');
+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');
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -9777,16 +9794,18 @@ ANALYZE fpagg_tab_p3;
 SET enable_partitionwise_aggregate TO false;
 EXPLAIN (COSTS OFF)
 SELECT a, sum(b), min(b), count(*) FROM pagg_tab GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
-                     QUERY PLAN                      
------------------------------------------------------
- GroupAggregate
-   Group Key: pagg_tab.a
-   Filter: (avg(pagg_tab.b) < '22'::numeric)
-   ->  Append
-         ->  Foreign Scan on fpagg_tab_p1 pagg_tab_1
-         ->  Foreign Scan on fpagg_tab_p2 pagg_tab_2
-         ->  Foreign Scan on fpagg_tab_p3 pagg_tab_3
-(7 rows)
+                        QUERY PLAN                         
+-----------------------------------------------------------
+ Sort
+   Sort Key: pagg_tab.a
+   ->  HashAggregate
+         Group Key: pagg_tab.a
+         Filter: (avg(pagg_tab.b) < '22'::numeric)
+         ->  Append
+               ->  Foreign Scan on fpagg_tab_p1 pagg_tab_1
+               ->  Foreign Scan on fpagg_tab_p2 pagg_tab_2
+               ->  Foreign Scan on fpagg_tab_p3 pagg_tab_3
+(9 rows)
 
 -- Plan with partitionwise aggregates is enabled
 SET enable_partitionwise_aggregate TO true;
@@ -9820,32 +9839,34 @@ 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                                         
---------------------------------------------------------------------------------------------
- Merge Append
+                                                                                                                                           QUERY PLAN                                                                                                                                            
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, (count(((t1.*)::pagg_tab)))
    Sort Key: t1.a
-   ->  GroupAggregate
-         Output: t1.a, count(((t1.*)::pagg_tab))
-         Group Key: t1.a
-         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 ORDER BY a ASC NULLS LAST
-   ->  GroupAggregate
-         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 ORDER BY a ASC NULLS LAST
-   ->  GroupAggregate
-         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 ORDER BY a ASC NULLS LAST
-(23 rows)
+   ->  Append
+         ->  HashAggregate
+               Output: t1.a, count(((t1.*)::pagg_tab))
+               Group Key: t1.a
+               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, 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, 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, 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;
  a  | count 
@@ -9858,27 +9879,837 @@ SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
  21 |   100
 (6 rows)
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are unsafe to push down when there is a HAVING clause
+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                         
------------------------------------------------------------
+                                                                 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)
+
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
+ b  |         avg         | max | count 
+----+---------------------+-----+-------
+  0 | 10.0000000000000000 |  20 |    60
+  1 | 11.0000000000000000 |  21 |    60
+ 10 | 10.0000000000000000 |  20 |    60
+ 11 | 11.0000000000000000 |  21 |    60
+ 20 | 10.0000000000000000 |  20 |    60
+ 21 | 11.0000000000000000 |  21 |    60
+ 30 | 10.0000000000000000 |  20 |    60
+ 31 | 11.0000000000000000 |  21 |    60
+ 40 | 10.0000000000000000 |  20 |    60
+ 41 | 11.0000000000000000 |  21 |    60
+(10 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+                                                                  QUERY PLAN                                                                  
+----------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: (avg(pagg_tab.a)), (max(pagg_tab.a)), (count(*)), ((((pagg_tab.b / 2)))::numeric), ((pagg_tab.b / 2))
+   Sort Key: ((((pagg_tab.b / 2)))::numeric)
+   ->  Finalize HashAggregate
+         Output: avg(pagg_tab.a), max(pagg_tab.a), count(*), (((pagg_tab.b / 2)))::numeric, ((pagg_tab.b / 2))
+         Group Key: ((pagg_tab.b / 2))
+         ->  Append
+               ->  Foreign Scan
+                     Output: ((pagg_tab.b / 2)), (PARTIAL avg(pagg_tab.a)), (PARTIAL max(pagg_tab.a)), (PARTIAL count(*)), pagg_tab.b
+                     Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p1 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_1.b / 2)), (PARTIAL avg(pagg_tab_1.a)), (PARTIAL max(pagg_tab_1.a)), (PARTIAL count(*)), pagg_tab_1.b
+                     Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p2 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_2.b / 2)), (PARTIAL avg(pagg_tab_2.a)), (PARTIAL max(pagg_tab_2.a)), (PARTIAL count(*)), pagg_tab_2.b
+                     Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p3 GROUP BY 1, 2
+(19 rows)
+
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+         avg         | max | count | numeric 
+---------------------+-----+-------+---------
+ 10.5000000000000000 |  21 |   120 |       0
+ 12.5000000000000000 |  23 |   120 |       1
+ 14.5000000000000000 |  25 |   120 |       2
+ 16.5000000000000000 |  27 |   120 |       3
+ 18.5000000000000000 |  29 |   120 |       4
+ 10.5000000000000000 |  21 |   120 |       5
+ 12.5000000000000000 |  23 |   120 |       6
+ 14.5000000000000000 |  25 |   120 |       7
+ 16.5000000000000000 |  27 |   120 |       8
+ 18.5000000000000000 |  29 |   120 |       9
+ 10.5000000000000000 |  21 |   120 |      10
+ 12.5000000000000000 |  23 |   120 |      11
+ 14.5000000000000000 |  25 |   120 |      12
+ 16.5000000000000000 |  27 |   120 |      13
+ 18.5000000000000000 |  29 |   120 |      14
+ 10.5000000000000000 |  21 |   120 |      15
+ 12.5000000000000000 |  23 |   120 |      16
+ 14.5000000000000000 |  25 |   120 |      17
+ 16.5000000000000000 |  27 |   120 |      18
+ 18.5000000000000000 |  29 |   120 |      19
+ 10.5000000000000000 |  21 |   120 |      20
+ 12.5000000000000000 |  23 |   120 |      21
+ 14.5000000000000000 |  25 |   120 |      22
+ 16.5000000000000000 |  27 |   120 |      23
+ 18.5000000000000000 |  29 |   120 |      24
+(25 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-08 | 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-08 | 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)
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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)
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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)
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+                                                                           QUERY PLAN                                                                           
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: avg(t1.b), avg((t1.b)::bigint)
+   ->  Append
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1.b)), (PARTIAL avg((t1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p1 t1) INNER JOIN (public.fpagg_tab_p1 t2))
+               Remote SQL: SELECT avg_p_int4(r4.b), avg_p_int8(r4.b::bigint) FROM (public.pagg_tab_p1 r4 INNER JOIN public.pagg_tab_p1 r7 ON (((r4.a = r7.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_1.b)), (PARTIAL avg((t1_1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p2 t1_1) INNER JOIN (public.fpagg_tab_p2 t2_1))
+               Remote SQL: SELECT avg_p_int4(r5.b), avg_p_int8(r5.b::bigint) FROM (public.pagg_tab_p2 r5 INNER JOIN public.pagg_tab_p2 r8 ON (((r5.a = r8.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_2.b)), (PARTIAL avg((t1_2.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p3 t1_2) INNER JOIN (public.fpagg_tab_p3 t2_2))
+               Remote SQL: SELECT avg_p_int4(r6.b), avg_p_int8(r6.b::bigint) FROM (public.pagg_tab_p3 r6 INNER JOIN public.pagg_tab_p3 r9 ON (((r6.a = r9.a))))
+(15 rows)
+
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+         avg         |         avg         
+---------------------+---------------------
+ 24.5000000000000000 | 24.5000000000000000
+(1 row)
+
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+-- 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 is unsafe to push down partial aggregates which contain DISTINCT clauses
+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
-         ->  Partial GroupAggregate
-               Group Key: pagg_tab.b
-               ->  Foreign Scan on fpagg_tab_p1 pagg_tab
-         ->  Partial GroupAggregate
-               Group Key: pagg_tab_1.b
-               ->  Foreign Scan on fpagg_tab_p2 pagg_tab_1
-         ->  Partial GroupAggregate
-               Group Key: pagg_tab_2.b
-               ->  Foreign Scan on fpagg_tab_p3 pagg_tab_2
-(14 rows)
+         ->  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 is unsafe to push down partial aggregates which contain ORDER BY clauses
+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 aggregates for user-defined aggregate
+-- when it belongs to an extension that is listed in the extension setting
+-- and when its aggpartialfunc belongs to an extension that is listed
+-- in the extension setting.
+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 is unsafe to push down partial aggregates for user-defined aggregates
+-- when they have 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 is unsafe to push down partial aggregates for user-defined aggregates
+-- when itself is aggpartialfunc and when it belongs to an extension
+-- that's listed in the extension setting 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 is unsafe to push down partial aggregates for user-defined aggregates
+-- when it doesn't belong to an extension that is listed in the extension setting
+-- and when its aggpartialfunc belongs to an extension that is listed
+-- in the extension setting.
+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 aggregates for user-defined aggregate
+-- whose stype is not internal
+-- when it belongs to an extension that is listed in the extension setting
+-- and when its aggpartialfunc belongs to an extension that is listed
+-- in the extension setting.
+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 aggregates for user-defined aggregate
+-- when itself is aggpartialfunc and when it belongs to an extension
+-- that's listed in the extension setting 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/option.c b/contrib/postgres_fdw/option.c
index 8c822f4ef9..67e15c8bb9 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -126,7 +126,8 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
 			strcmp(def->defname, "async_capable") == 0 ||
 			strcmp(def->defname, "parallel_commit") == 0 ||
 			strcmp(def->defname, "parallel_abort") == 0 ||
-			strcmp(def->defname, "keep_connections") == 0)
+			strcmp(def->defname, "keep_connections") == 0 ||
+			strcmp(def->defname, "check_partial_aggregate_support") == 0)
 		{
 			/* these accept only boolean values */
 			(void) defGetBoolean(def);
@@ -268,6 +269,7 @@ InitPgFdwOptions(void)
 		/* batch_size is available on both server and table */
 		{"batch_size", ForeignServerRelationId, false},
 		{"batch_size", ForeignTableRelationId, false},
+		{"check_partial_aggregate_support", ForeignServerRelationId, false},
 		/* async_capable is available on both server and table */
 		{"async_capable", ForeignServerRelationId, false},
 		{"async_capable", ForeignTableRelationId, false},
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 6de2bec3b7..209354e771 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -36,6 +36,7 @@
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
+#include "optimizer/planner.h"
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
@@ -519,7 +520,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);
+								GroupPathExtraData *extra);
 static List *get_useful_pathkeys_for_relation(PlannerInfo *root,
 											  RelOptInfo *rel);
 static List *get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel);
@@ -650,6 +651,8 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	fpinfo->fdw_tuple_cost = DEFAULT_FDW_TUPLE_COST;
 	fpinfo->shippable_extensions = NIL;
 	fpinfo->fetch_size = 100;
+	fpinfo->check_partial_aggregate_support = false;
+	fpinfo->remoteversion = 0;
 	fpinfo->async_capable = false;
 
 	apply_server_options(fpinfo);
@@ -661,7 +664,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	 * should match what ExecCheckPermissions() does.  If we fail due to lack
 	 * of permissions, the query would have failed at runtime anyway.
 	 */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
 		Oid			userid;
 
@@ -5948,9 +5951,10 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
 	fpinfo->pushdown_safe = true;
 
 	/* Get user mapping */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
-		if (fpinfo_o->use_remote_estimate)
+		if (fpinfo_o->use_remote_estimate ||
+			fpinfo_o->check_partial_aggregate_support)
 			fpinfo->user = fpinfo_o->user;
 		else
 			fpinfo->user = fpinfo_i->user;
@@ -6132,6 +6136,8 @@ apply_server_options(PgFdwRelationInfo *fpinfo)
 				ExtractExtensionList(defGetString(def), false);
 		else if (strcmp(def->defname, "fetch_size") == 0)
 			(void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
+		else if (strcmp(def->defname, "check_partial_aggregate_support") == 0)
+			fpinfo->check_partial_aggregate_support = defGetBoolean(def);
 		else if (strcmp(def->defname, "async_capable") == 0)
 			fpinfo->async_capable = defGetBoolean(def);
 	}
@@ -6191,6 +6197,8 @@ merge_fdw_options(PgFdwRelationInfo *fpinfo,
 	fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
 	fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
 	fpinfo->fetch_size = fpinfo_o->fetch_size;
+	fpinfo->check_partial_aggregate_support = fpinfo_o->check_partial_aggregate_support;
+	fpinfo->remoteversion = fpinfo_o->remoteversion;
 	fpinfo->async_capable = fpinfo_o->async_capable;
 
 	/* Merge the table level options from either side of the join. */
@@ -6373,7 +6381,7 @@ postgresGetForeignJoinPaths(PlannerInfo *root,
  */
 static bool
 foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
-					Node *havingQual)
+					GroupPathExtraData *extra)
 {
 	Query	   *query = root->parse;
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
@@ -6382,11 +6390,16 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	ListCell   *lc;
 	int			i;
 	List	   *tlist = NIL;
+	bool		partial = extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL;
 
 	/* We currently don't support pushing Grouping Sets. */
 	if (query->groupingSets)
 		return false;
 
+	/* It is unsafe to push HAVING statements with partial aggregates */
+	if ((extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL) && extra->havingQual)
+		return false;
+
 	/* Get the fpinfo of the underlying scan relation. */
 	ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
 
@@ -6415,6 +6428,12 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * a node, as long as it's not at top level; then no match is possible.
 	 */
 	i = 0;
+	fpinfo->group_clause = query->groupClause;
+	if (partial)
+	{
+		fpinfo->group_clause = extra->groupClausePartial;
+		grouping_target = extra->partial_target;
+	}
 	foreach(lc, grouping_target->exprs)
 	{
 		Expr	   *expr = (Expr *) lfirst(lc);
@@ -6426,7 +6445,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 		 * check the whole GROUP BY clause not just processed_groupClause,
 		 * because we will ship all of it, cf. appendGroupByClause.
 		 */
-		if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
+		if (sgref && get_sortgroupref_clause_noerr(sgref, fpinfo->group_clause))
 		{
 			TargetEntry *tle;
 
@@ -6512,9 +6531,9 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * Classify the pushable and non-pushable HAVING clauses and save them in
 	 * remote_conds and local_conds of the grouped rel's fpinfo.
 	 */
-	if (havingQual)
+	if (extra->havingQual)
 	{
-		foreach(lc, (List *) havingQual)
+		foreach(lc, (List *) extra->havingQual)
 		{
 			Expr	   *expr = (Expr *) lfirst(lc);
 			RestrictInfo *rinfo;
@@ -6534,7 +6553,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 									  grouped_rel->relids,
 									  NULL,
 									  NULL);
-			if (is_foreign_expr(root, grouped_rel, expr))
+			if (is_foreign_expr(root, grouped_rel, expr) && !partial)
 				fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
 			else
 				fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
@@ -6628,6 +6647,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)
@@ -6644,6 +6664,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;
@@ -6684,7 +6708,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;
@@ -6704,7 +6729,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))
 		return;
 
 	/*
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index 47157ac887..a1a9688c9f 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -86,6 +86,15 @@ typedef struct PgFdwRelationInfo
 	ForeignServer *server;
 	UserMapping *user;			/* only set in use_remote_estimate mode */
 
+	/* for partial aggregate pushdown */
+	bool		check_partial_aggregate_support;
+
+	/*
+	 * If remoteversion is zero, it means the remote server version has not
+	 * been acquired.
+	 */
+	int			remoteversion;
+
 	int			fetch_size;		/* fetch size for this remote table */
 
 	/*
@@ -110,6 +119,7 @@ typedef struct PgFdwRelationInfo
 
 	/* Grouping information */
 	List	   *grouped_tlist;
+	List	   *group_clause;
 
 	/* Subquery information */
 	bool		make_outerrel_subquery; /* do we deparse outerrel as a
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 2d14eeadb5..8d7075fb92 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -2993,16 +2993,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.1');
+
+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');
@@ -3010,6 +3025,9 @@ CREATE FOREIGN TABLE fpagg_tab_p2 PARTITION OF pagg_tab FOR VALUES FROM (10) TO
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
@@ -3032,10 +3050,289 @@ EXPLAIN (VERBOSE, COSTS OFF)
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are unsafe to push down when there is a HAVING clause
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+
+-- 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;
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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;
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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;
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+
+-- 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 is unsafe to push down partial aggregates which contain DISTINCT clauses
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT max(a), count(distinct b) FROM pagg_tab;
+SELECT max(a), count(distinct b) FROM pagg_tab;
+
+-- It is unsafe to push down partial aggregates which contain ORDER BY clauses
+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 aggregates for user-defined aggregate
+-- when it belongs to an extension that is listed in the extension setting
+-- and when its aggpartialfunc belongs to an extension that is listed
+-- in the extension setting.
+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 is unsafe to push down partial aggregates for user-defined aggregates
+-- when they have 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 is unsafe to push down partial aggregates for user-defined aggregates
+-- when itself is aggpartialfunc and when it belongs to an extension
+-- that's listed in the extension setting 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 is unsafe to push down partial aggregates for user-defined aggregates
+-- when it doesn't belong to an extension that is listed in the extension setting
+-- and when its aggpartialfunc belongs to an extension that is listed
+-- in the extension setting.
+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 aggregates for user-defined aggregate
+-- whose stype is not internal
+-- when it belongs to an extension that is listed in the extension setting
+-- and when its aggpartialfunc belongs to an extension that is listed
+-- in the extension setting.
+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 aggregates for user-defined aggregate
+-- when itself is aggpartialfunc and when it belongs to an extension
+-- that's listed in the extension setting 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 3ec7391ec5..a6dc334726 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -644,6 +644,18 @@
        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="partial-aggregate-pushdown"/> 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 2ba96aa295..286666d9a9 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -451,6 +451,43 @@ OPTIONS (ADD password_required 'false');
 
   </sect3>
 
+  <sect3 id="postgres-fdw-options-partial-aggregate-pushdown">
+   <title>Partial Aggregate Pushdown Options</title>
+
+   <para>
+    By default, <filename>postgres_fdw</filename> assumes that each
+    built-in aggregate function has a partial aggregate function defined on
+    the remote server;  see <xref linkend="partial-aggregate-pushdown"/>
+    for a definition of the partial aggregate functions. This may be
+    overridden using the following option:
+   </para>
+
+   <variablelist>
+
+    <varlistentry>
+     <term><literal>check_partial_aggregate_support</literal> (<type>boolean</type>)</term>
+     <listitem>
+      <para>
+       If this option is false, <filename>postgres_fdw</filename> always
+       uses partial aggregate pushdown by assuming that each built-in
+       aggregate function has a partial aggregate function defined on
+       the remote server.  If this option is true, local aggregates
+       whose partial computation function references itself are assumed
+       to exist on the remote server.  If not, during query planning,
+       <filename>postgres_fdw</filename> will connect to the remote
+       server and retrieve the remote server version.  If the remote
+       version is the same or newer, partial aggregate functions will be
+       assumed to exist.  If older, <filename>postgres_fdw</filename>
+       checks that the remote server has a matching partial aggregate
+       function before performing partial aggregate pushdown.  The default
+       is <literal>false</literal>.
+      </para>
+     </listitem>
+    </varlistentry>
+
+   </variablelist>
+  </sect3>
+
   <sect3 id="postgres-fdw-options-asynchronous-execution">
    <title>Asynchronous Execution Options</title>
 
@@ -929,14 +966,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
-   be <literal>IMMUTABLE</literal> as well.
+   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 only use data types, operators, and functions that are built-in
+   or belong to an extension that is 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
    sending the whole query to the remote server if there are no query
@@ -966,6 +1004,64 @@ postgres=# SELECT postgres_fdw_disconnect_all();
   </para>
  </sect2>
 
+ <sect2 id="partial-aggregate-pushdown">
+  <title>Partial aggregate pushdown</title>
+  <para>
+   Partial aggregate pushdown is an optimization for queries that contains
+   aggregate expressions for a partitioned table across one or more remote
+   servers. If multiple conditions are met, partial aggregate function
+   calls are sent to the remote servers.  The conditions under which this
+   sending is active are as follows.
+
+   <itemizedlist spacing="compact">
+    <listitem>
+     <para>
+      A partial aggregate function which corresponds to the aggregate
+      expression is defined on the local server and assumed to exist on
+      the remote server
+     </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>
+      The aggregate expressions in the query do not contain
+      <literal>DISTINCT</literal> or <literal>ORDER BY</literal> clauses
+     </para>
+    </listitem>
+   </itemizedlist>
+
+   When partial aggregate pushdown is used for aggregate expressions,
+   remote queries replace aggregate function calls with partial
+   aggregate function calls.  If the data type of the state value is not
+   <type>internal</type>, the partial aggregate function must not have
+   a final function.  If <type>internal</type>, the partial aggregate
+   function must have the same final function as the aggregate function's
+   serialization function, and 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 the
+   corresponding members in the aggregate function.  Built-in aggregate
+   functions that support partial aggregation must also have their
+   partial aggregate functions defined as built-in aggregates.  See <xref
+   linkend="postgres-fdw-options-partial-aggregate-pushdown"/> for details
+   on how the existance of remote-server partial aggregates are checked.
+   <filename>postgres_fdw</filename> assumes user-defined aggregate
+   functions have partial aggregate functions on the remote server only
+   if both belong to extensions listed in the foreign server's extensions
+   option.
+  </para>
+ </sect2>
+
  <sect2 id="postgres-fdw-remote-query-execution-environment">
   <title>Remote Query Execution Environment</title>
 
@@ -1031,15 +1127,25 @@ 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>.
   </para>
+
+  <para>
+   A similar problem can occur with partial aggregate pushdown.
+   Pushdown causes aggregate function calls to send partial aggregate
+   function calls to the remote server. If the partial aggregate
+   function doesn't exist on the remote server, it causes
+   <quote>function does not exist</quote> or a similar error; see
+   <xref linkend="postgres-fdw-options-partial-aggregate-pushdown"/>
+   on how to enable remote server checks.
+  </para>
  </sect2>
 
  <sect2 id="postgres-fdw-wait-events">
diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml
index 222e0aa5c9..12beb79dc7 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>
@@ -246,6 +248,12 @@ CREATE [ OR REPLACE ] AGGREGATE <replaceable class="parameter">name</replaceable
    marked <literal>PARALLEL SAFE</literal> to enable parallel aggregation.
   </para>
 
+  <para>
+   Paraemter <literal>AGGPARTIALFUNC</literal> optionally defines a
+   partial aggregate function used for partial aggregate pushdown; see
+   <xref linkend="xaggr-partial-aggregates"/> for details.
+  </para>
+
   <para>
    Aggregates that behave like <function>MIN</function> or <function>MAX</function> can
    sometimes be optimized by looking into an index instead of scanning every
@@ -653,6 +661,32 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">aggpartialfunc</replaceable></term>
+    <listitem>
+     <para>
+      Parameter <replaceable class="parameter">aggpartialfunc</replaceable>
+      reprsents the name of this aggregate function's
+      partial aggregate function.  If specified, <replaceable
+      class="parameter">combinefunc</replaceable> must also be specified.
+      If <replaceable class="parameter">state_data_type</replaceable>
+      is not <type>internal</type>, the partial aggregate function must
+      not have the <replaceable class="parameter">ffunc</replaceable>.
+      If <replaceable class="parameter">state_data_type</replaceable>
+      is <type>internal</type>, the partial aggregate function
+      must have the same final function as <replaceable
+      class="parameter">serialfunc</replaceable>.  The partial
+      aggregate function and the aggregate function must 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..2863731d00 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,14 @@ SELECT percentile_disc(0.5) WITHIN GROUP (ORDER BY income) FROM households;
    parallel-safety markings on its support functions are not consulted.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize queries containing
+   aggregate expressions on partitioned tables that span multiple
+   remote servers. This optimization sends <firstterm>partial
+   aggregate functions</firstterm> to remote servers;  see <xref
+   linkend="partial-aggregate-pushdown"/> for details.
+  </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/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index a8cea5efe1..f195970967 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -206,7 +206,7 @@ static PathTarget *make_group_input_target(PlannerInfo *root,
 										   PathTarget *final_target);
 static PathTarget *make_partial_grouping_target(PlannerInfo *root,
 												PathTarget *grouping_target,
-												Node *havingQual);
+												GroupPathExtraData *extra);
 static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
 static void optimize_window_clauses(PlannerInfo *root,
 									WindowFuncLists *wflists);
@@ -5415,6 +5415,99 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
 	return set_pathtarget_cost_width(root, input_target);
 }
 
+/*
+ * setGroupClausePartial
+ *	  Generate a groupClause and a pathtarget for partial aggregate
+ *	  pushdown by FDW and set them to GroupPathExtraData.
+ */
+static void
+setGroupClausePartial(PathTarget *partial_target, List *non_group_exprs,
+					  List *groupClause, GroupPathExtraData *extra)
+{
+	int			exprno,
+				refno;
+	ListCell   *lc;
+	Index		maxRef = 0;
+	List	   *exprs_processed = NIL;
+	int			exprs_num = 0;
+
+	foreach(lc, groupClause)
+	{
+		SortGroupClause *sgc = (SortGroupClause *) lfirst(lc);
+
+		if (sgc->tleSortGroupRef > maxRef)
+			maxRef = sgc->tleSortGroupRef;
+	}
+	maxRef++;
+
+	extra->groupClausePartial = list_copy_deep(groupClause);
+	extra->partial_target = copy_pathtarget(partial_target);
+
+	if (partial_target->exprs)
+		exprs_num = partial_target->exprs->length;
+
+	foreach(lc, non_group_exprs)
+	{
+		Expr	   *expr = (Expr *) lfirst(lc);
+
+		refno = -1;
+		if (list_member(exprs_processed, expr) ||
+			(!IsA(expr, Var) && !IsA(expr, PlaceHolderVar)))
+			continue;
+		exprs_processed = lappend(exprs_processed, expr);
+		for (exprno = 0; exprno < exprs_num; exprno++)
+		{
+			Expr	   *target_expr = (Expr *) list_nth(partial_target->exprs, exprno);
+
+			if (equal(target_expr, expr))
+			{
+				refno = exprno;
+				break;
+			}
+		}
+		if (refno < 0)
+		{
+			SortGroupClause *grpcl = makeNode(SortGroupClause);
+
+			grpcl->tleSortGroupRef = maxRef++;
+			extra->groupClausePartial = lappend(extra->groupClausePartial, grpcl);
+			add_column_to_pathtarget(extra->partial_target, expr, grpcl->tleSortGroupRef);
+		}
+	}
+}
+
+/*
+ * adjustAggrefForPartial
+ * Adjust Aggrefs to put them in partial mode
+ */
+static void
+adjustAggrefForPartial(List *exprs)
+{
+	ListCell   *lc;
+
+	foreach(lc, exprs)
+	{
+		Aggref	   *aggref = (Aggref *) lfirst(lc);
+
+		if (IsA(aggref, Aggref))
+		{
+			Aggref	   *newaggref;
+
+			/*
+			 * We shouldn't need to copy the substructure of the Aggref node,
+			 * but flat-copy the node itself to avoid damaging other trees.
+			 */
+			newaggref = makeNode(Aggref);
+			memcpy(newaggref, aggref, sizeof(Aggref));
+
+			/* For now, assume serialization is required */
+			mark_partial_aggref(newaggref, AGGSPLIT_INITIAL_SERIAL);
+
+			lfirst(lc) = newaggref;
+		}
+	}
+}
+
 /*
  * make_partial_grouping_target
  *	  Generate appropriate PathTarget for output of partial aggregate
@@ -5430,11 +5523,15 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
  *
  * grouping_target is the tlist to be emitted by the topmost aggregation step.
  * havingQual represents the HAVING clause.
+ *
+ * Modified PathTarget cannot be used by FDW as-is to deparse this statement.
+ * So, before modifying PathTarget, setGroupClausePartial generates
+ * another Pathtarget and another list of SortGroupClauses.
  */
 static PathTarget *
 make_partial_grouping_target(PlannerInfo *root,
 							 PathTarget *grouping_target,
-							 Node *havingQual)
+							 GroupPathExtraData *extra)
 {
 	PathTarget *partial_target;
 	List	   *non_group_cols;
@@ -5476,8 +5573,8 @@ make_partial_grouping_target(PlannerInfo *root,
 	/*
 	 * If there's a HAVING clause, we'll need the Vars/Aggrefs it uses, too.
 	 */
-	if (havingQual)
-		non_group_cols = lappend(non_group_cols, havingQual);
+	if (extra->havingQual)
+		non_group_cols = lappend(non_group_cols, extra->havingQual);
 
 	/*
 	 * Pull out all the Vars, PlaceHolderVars, and Aggrefs mentioned in
@@ -5490,35 +5587,17 @@ make_partial_grouping_target(PlannerInfo *root,
 									  PVC_INCLUDE_AGGREGATES |
 									  PVC_RECURSE_WINDOWFUNCS |
 									  PVC_INCLUDE_PLACEHOLDERS);
-
+	setGroupClausePartial(partial_target, non_group_exprs, root->parse->groupClause, extra);
 	add_new_columns_to_pathtarget(partial_target, non_group_exprs);
+	add_new_columns_to_pathtarget(extra->partial_target, non_group_exprs);
 
 	/*
 	 * Adjust Aggrefs to put them in partial mode.  At this point all Aggrefs
 	 * are at the top level of the target list, so we can just scan the list
 	 * rather than recursing through the expression trees.
 	 */
-	foreach(lc, partial_target->exprs)
-	{
-		Aggref	   *aggref = (Aggref *) lfirst(lc);
-
-		if (IsA(aggref, Aggref))
-		{
-			Aggref	   *newaggref;
-
-			/*
-			 * We shouldn't need to copy the substructure of the Aggref node,
-			 * but flat-copy the node itself to avoid damaging other trees.
-			 */
-			newaggref = makeNode(Aggref);
-			memcpy(newaggref, aggref, sizeof(Aggref));
-
-			/* For now, assume serialization is required */
-			mark_partial_aggref(newaggref, AGGSPLIT_INITIAL_SERIAL);
-
-			lfirst(lc) = newaggref;
-		}
-	}
+	adjustAggrefForPartial(partial_target->exprs);
+	adjustAggrefForPartial(extra->partial_target->exprs);
 
 	/* clean up cruft */
 	list_free(non_group_exprs);
@@ -7148,7 +7227,7 @@ create_partial_grouping_paths(PlannerInfo *root,
 	 */
 	partially_grouped_rel->reltarget =
 		make_partial_grouping_target(root, grouped_rel->reltarget,
-									 extra->havingQual);
+									 extra);
 
 	if (!extra->partial_costs_set)
 	{
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index e863913849..751a1222b3 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -13921,6 +13921,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;
@@ -14003,11 +14004,18 @@ 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 >= 170000)
+			appendPQExpBufferStr(query,
+								 "aggpartialfn\n");
+		else
+			appendPQExpBufferStr(query,
+								 "'-' AS aggpartialfn\n");
 
 		appendPQExpBufferStr(query,
 							 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
@@ -14033,6 +14041,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"));
@@ -14122,6 +14131,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 085149e258..3e4f8331ee 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 bd0b8873d3..3d2d4faa1f 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',
@@ -5004,6 +5011,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',
+  proargnames => '{value,delimiter}', prosrc => 'aggregate_dummy' },
 { oid => '3538', descr => 'concatenate aggregate input into a string',
   proname => 'string_agg', prokind => 'a', proisstrict => 'f',
   prorettype => 'text', proargtypes => 'text text',
@@ -5016,6 +5027,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',
+  proargnames => '{value,delimiter}', prosrc => 'aggregate_dummy' },
 { oid => '3545', descr => 'concatenate aggregate input into a bytea',
   proname => 'string_agg', prokind => 'a', proisstrict => 'f',
   prorettype => 'bytea', proargtypes => 'bytea bytea',
@@ -6602,36 +6617,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' },
@@ -6654,6 +6694,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' },
@@ -6812,153 +6855,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',
@@ -6969,52 +7120,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 => '2775', 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 => '3063', 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 => '3814', 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 => '4551', 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 => '4552', 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 => '4553',
+  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 => '4554', 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 => '4555', 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 => '4556', 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/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index ed85dc7414..411a249482 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -3243,6 +3243,9 @@ typedef enum
  * havingQual gives list of quals to be applied after aggregation.
  * targetList gives list of columns to be projected.
  * patype is the type of partitionwise aggregation that is being performed.
+ * groupClausePartial is List of SortGroupClauses for partial aggregate
+ * 		pushdown by FDW
+ * partial_target is PathTarget for partial aggregate pushdown by FDW
  */
 typedef struct
 {
@@ -3257,6 +3260,8 @@ typedef struct
 	Node	   *havingQual;
 	List	   *targetList;
 	PartitionwiseAggregateType patype;
+	List	   *groupClausePartial;
+	PathTarget *partial_target;
 } GroupPathExtraData;
 
 /*
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.39.3



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

* RE: Partial aggregates pushdown
@ 2023-11-13 08:25  [email protected] <[email protected]>
  parent: [email protected] <[email protected]>
  0 siblings, 1 reply; 38+ messages in thread

From: [email protected] @ 2023-11-13 08:25 UTC (permalink / raw)
  To: pgsql-hackers; +Cc: Finnerty, Jim <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Bruce Momjian <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Alexander Pyhalov <[email protected]>; [email protected] <[email protected]>

Hi Hackers.

In postgres_fdw.sql, I have corrected the output format for floating point numbers
by extra_float_digits.

Sincerely yours,
Yuuki Fujii

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


Attachments:

  [application/octet-stream] 0001-Partial-aggregates-push-down-v34.patch (268.2K, ../../OSZPR01MB94244C52ABBE67C4A033B5E495B3A@OSZPR01MB9424.jpnprd01.prod.outlook.com/2-0001-Partial-aggregates-push-down-v34.patch)
  download | inline diff:
From cd392a99dc8ee8f44cf6e23bae2ce5658d4cc15f Mon Sep 17 00:00:00 2001
From: Yuki Fujii <[email protected]>
Date: Mon, 13 Nov 2023 12:16:01 -0500
Subject: [PATCH] Partial aggregates push down v34

---
 contrib/postgres_fdw/deparse.c                | 113 ++-
 .../postgres_fdw/expected/postgres_fdw.out    | 938 +++++++++++++++++-
 contrib/postgres_fdw/option.c                 |   4 +-
 contrib/postgres_fdw/postgres_fdw.c           |  47 +-
 contrib/postgres_fdw/postgres_fdw.h           |  10 +
 contrib/postgres_fdw/sql/postgres_fdw.sql     | 312 +++++-
 doc/src/sgml/catalogs.sgml                    |  12 +
 doc/src/sgml/postgres-fdw.sgml                | 130 ++-
 doc/src/sgml/ref/create_aggregate.sgml        |  34 +
 doc/src/sgml/xaggr.sgml                       |  11 +-
 src/backend/catalog/pg_aggregate.c            | 101 ++
 src/backend/commands/aggregatecmds.c          |   4 +
 src/backend/optimizer/plan/planner.c          | 133 ++-
 src/bin/pg_dump/pg_dump.c                     |  16 +-
 src/include/catalog/pg_aggregate.dat          | 680 ++++++++++---
 src/include/catalog/pg_aggregate.h            |   4 +
 src/include/catalog/pg_proc.dat               | 196 ++++
 src/include/nodes/pathnodes.h                 |   5 +
 .../regress/expected/create_aggregate.out     | 184 ++++
 src/test/regress/expected/oidjoins.out        |   1 +
 src/test/regress/sql/create_aggregate.sql     | 183 ++++
 21 files changed, 2863 insertions(+), 255 deletions(-)

diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 09fd489a90..c443f8974a 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 */
@@ -3723,6 +3744,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	Query	   *query = context->root->parse;
 	ListCell   *lc;
 	bool		first = true;
+	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) context->foreignrel->fdw_private;
 
 	/* Nothing to be done, if there's no GROUP BY clause in the query. */
 	if (!query->groupClause)
@@ -3743,7 +3765,7 @@ appendGroupByClause(List *tlist, deparse_expr_cxt *context)
 	 * to empty, and in any case the redundancy situation on the remote might
 	 * be different than what we think here.
 	 */
-	foreach(lc, query->groupClause)
+	foreach(lc, fpinfo->group_clause)
 	{
 		SortGroupClause *grp = (SortGroupClause *) lfirst(lc);
 
@@ -4047,3 +4069,78 @@ get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel,
 	/* Shouldn't get here */
 	elog(ERROR, "unexpected expression in subquery output");
 }
+
+/*
+ * Can we assume a buit-in aggpartialfunc exists on the remote server? If
+ * check_partial_aggregate_support is false, we assume the partial aggregate
+ * function exists on the remote server. If true, we assume the partial
+ * aggregate function exists on the remote server if the aggregate is so
+ * simple that the local aggregate's OID and its partial aggregate function
+ * OID are the same.  If not, we check the remote server version and assume
+ * the remote partial aggregate function exists if the remote server version
+ * is equal to or greater than the local server version.
+ */
+static bool
+is_builtin_aggpartialfunc_shippable(Form_pg_aggregate aggform,
+									PgFdwRelationInfo *fpinfo)
+{
+	bool		shippable = true;
+
+	/* So simple, we assume it exists. */
+	if (aggform->aggfnoid != aggform->aggpartialfn &&
+		fpinfo->check_partial_aggregate_support)
+	{
+		if (fpinfo->remoteversion == 0)
+		{
+			PGconn	   *conn = GetConnection(fpinfo->user, false, NULL);
+
+			fpinfo->remoteversion = PQserverVersion(conn);
+		}
+
+		if (fpinfo->remoteversion < PG_VERSION_NUM)
+			shippable = false;
+	}
+
+	return shippable;
+}
+
+/*
+ * ----------
+ * Check that partial aggregate "agg" is safe to push down.
+ *
+ * It is pushdown-safe when all of the following conditions are true:
+ *
+ *    * agg is an AGGKIND_NORMAL aggregate which contains no DISTINCT or
+ *    ORDER BY clauses
+ *    * There is a local aggregate function for partial aggregation
+ *    (aggpartialfunc) and it is assumed to exist on the remote server
+ * ----------
+ */
+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_builtin(aggform->aggpartialfn))
+		partial_agg_ok = is_builtin_aggpartialfunc_shippable(aggform, fpinfo);
+	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 64bcc66b8d..2c392bcdd2 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -9757,36 +9757,56 @@ 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.1');
+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');
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
+SET extra_float_digits = 0;
 -- When GROUP BY clause matches with PARTITION KEY.
 -- Plan with partitionwise aggregates is disabled
 SET enable_partitionwise_aggregate TO false;
 EXPLAIN (COSTS OFF)
 SELECT a, sum(b), min(b), count(*) FROM pagg_tab GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
-                     QUERY PLAN                      
------------------------------------------------------
- GroupAggregate
-   Group Key: pagg_tab.a
-   Filter: (avg(pagg_tab.b) < '22'::numeric)
-   ->  Append
-         ->  Foreign Scan on fpagg_tab_p1 pagg_tab_1
-         ->  Foreign Scan on fpagg_tab_p2 pagg_tab_2
-         ->  Foreign Scan on fpagg_tab_p3 pagg_tab_3
-(7 rows)
+                        QUERY PLAN                         
+-----------------------------------------------------------
+ Sort
+   Sort Key: pagg_tab.a
+   ->  HashAggregate
+         Group Key: pagg_tab.a
+         Filter: (avg(pagg_tab.b) < '22'::numeric)
+         ->  Append
+               ->  Foreign Scan on fpagg_tab_p1 pagg_tab_1
+               ->  Foreign Scan on fpagg_tab_p2 pagg_tab_2
+               ->  Foreign Scan on fpagg_tab_p3 pagg_tab_3
+(9 rows)
 
 -- Plan with partitionwise aggregates is enabled
 SET enable_partitionwise_aggregate TO true;
@@ -9820,32 +9840,34 @@ 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                                         
---------------------------------------------------------------------------------------------
- Merge Append
+                                                                                                                                           QUERY PLAN                                                                                                                                            
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: t1.a, (count(((t1.*)::pagg_tab)))
    Sort Key: t1.a
-   ->  GroupAggregate
-         Output: t1.a, count(((t1.*)::pagg_tab))
-         Group Key: t1.a
-         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 ORDER BY a ASC NULLS LAST
-   ->  GroupAggregate
-         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 ORDER BY a ASC NULLS LAST
-   ->  GroupAggregate
-         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 ORDER BY a ASC NULLS LAST
-(23 rows)
+   ->  Append
+         ->  HashAggregate
+               Output: t1.a, count(((t1.*)::pagg_tab))
+               Group Key: t1.a
+               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, 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, 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, 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;
  a  | count 
@@ -9858,27 +9880,837 @@ SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
  21 |   100
 (6 rows)
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are unsafe to push down when there is a HAVING clause
+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                         
------------------------------------------------------------
+                                                                 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)
+
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
+ b  |         avg         | max | count 
+----+---------------------+-----+-------
+  0 | 10.0000000000000000 |  20 |    60
+  1 | 11.0000000000000000 |  21 |    60
+ 10 | 10.0000000000000000 |  20 |    60
+ 11 | 11.0000000000000000 |  21 |    60
+ 20 | 10.0000000000000000 |  20 |    60
+ 21 | 11.0000000000000000 |  21 |    60
+ 30 | 10.0000000000000000 |  20 |    60
+ 31 | 11.0000000000000000 |  21 |    60
+ 40 | 10.0000000000000000 |  20 |    60
+ 41 | 11.0000000000000000 |  21 |    60
+(10 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+                                                                  QUERY PLAN                                                                  
+----------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+   Output: (avg(pagg_tab.a)), (max(pagg_tab.a)), (count(*)), ((((pagg_tab.b / 2)))::numeric), ((pagg_tab.b / 2))
+   Sort Key: ((((pagg_tab.b / 2)))::numeric)
+   ->  Finalize HashAggregate
+         Output: avg(pagg_tab.a), max(pagg_tab.a), count(*), (((pagg_tab.b / 2)))::numeric, ((pagg_tab.b / 2))
+         Group Key: ((pagg_tab.b / 2))
+         ->  Append
+               ->  Foreign Scan
+                     Output: ((pagg_tab.b / 2)), (PARTIAL avg(pagg_tab.a)), (PARTIAL max(pagg_tab.a)), (PARTIAL count(*)), pagg_tab.b
+                     Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p1 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_1.b / 2)), (PARTIAL avg(pagg_tab_1.a)), (PARTIAL max(pagg_tab_1.a)), (PARTIAL count(*)), pagg_tab_1.b
+                     Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab_1)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p2 GROUP BY 1, 2
+               ->  Foreign Scan
+                     Output: ((pagg_tab_2.b / 2)), (PARTIAL avg(pagg_tab_2.a)), (PARTIAL max(pagg_tab_2.a)), (PARTIAL count(*)), pagg_tab_2.b
+                     Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab_2)
+                     Remote SQL: SELECT (b / 2), b, avg_p_int4(a), max(a), count(*) FROM public.pagg_tab_p3 GROUP BY 1, 2
+(19 rows)
+
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+         avg         | max | count | numeric 
+---------------------+-----+-------+---------
+ 10.5000000000000000 |  21 |   120 |       0
+ 12.5000000000000000 |  23 |   120 |       1
+ 14.5000000000000000 |  25 |   120 |       2
+ 16.5000000000000000 |  27 |   120 |       3
+ 18.5000000000000000 |  29 |   120 |       4
+ 10.5000000000000000 |  21 |   120 |       5
+ 12.5000000000000000 |  23 |   120 |       6
+ 14.5000000000000000 |  25 |   120 |       7
+ 16.5000000000000000 |  27 |   120 |       8
+ 18.5000000000000000 |  29 |   120 |       9
+ 10.5000000000000000 |  21 |   120 |      10
+ 12.5000000000000000 |  23 |   120 |      11
+ 14.5000000000000000 |  25 |   120 |      12
+ 16.5000000000000000 |  27 |   120 |      13
+ 18.5000000000000000 |  29 |   120 |      14
+ 10.5000000000000000 |  21 |   120 |      15
+ 12.5000000000000000 |  23 |   120 |      16
+ 14.5000000000000000 |  25 |   120 |      17
+ 16.5000000000000000 |  27 |   120 |      18
+ 18.5000000000000000 |  29 |   120 |      19
+ 10.5000000000000000 |  21 |   120 |      20
+ 12.5000000000000000 |  23 |   120 |      21
+ 14.5000000000000000 |  25 |   120 |      22
+ 16.5000000000000000 |  27 |   120 |      23
+ 18.5000000000000000 |  29 |   120 |      24
+(25 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.970298913589258 | 2322.41666666667 |     2402.5 |      15.5 |        31 |              3 | 0.941479981712494 |          2 |   2247.5 |  69672.5 |     8990 | 8.8034084308295 | 8.8034084308295 | 8.8034084308295046 | 8.8034084308295046 | 8.8034084308295046 | 8.8034084308295046 | 8.65544144839919 | 8.65544144839919 | 8.6554414483991899 | 8.6554414483991899 | 8.6554414483991899 | 8.6554414483991899 | 8.8034084308295 | 8.8034084308295 | 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.9166666666667 | 74.9166666666667 | 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-08 | 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-08 | 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)
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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)
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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)
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+                                                                           QUERY PLAN                                                                           
+----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Finalize Aggregate
+   Output: avg(t1.b), avg((t1.b)::bigint)
+   ->  Append
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1.b)), (PARTIAL avg((t1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p1 t1) INNER JOIN (public.fpagg_tab_p1 t2))
+               Remote SQL: SELECT avg_p_int4(r4.b), avg_p_int8(r4.b::bigint) FROM (public.pagg_tab_p1 r4 INNER JOIN public.pagg_tab_p1 r7 ON (((r4.a = r7.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_1.b)), (PARTIAL avg((t1_1.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p2 t1_1) INNER JOIN (public.fpagg_tab_p2 t2_1))
+               Remote SQL: SELECT avg_p_int4(r5.b), avg_p_int8(r5.b::bigint) FROM (public.pagg_tab_p2 r5 INNER JOIN public.pagg_tab_p2 r8 ON (((r5.a = r8.a))))
+         ->  Foreign Scan
+               Output: (PARTIAL avg(t1_2.b)), (PARTIAL avg((t1_2.b)::bigint))
+               Relations: Aggregate on ((public.fpagg_tab_p3 t1_2) INNER JOIN (public.fpagg_tab_p3 t2_2))
+               Remote SQL: SELECT avg_p_int4(r6.b), avg_p_int8(r6.b::bigint) FROM (public.pagg_tab_p3 r6 INNER JOIN public.pagg_tab_p3 r9 ON (((r6.a = r9.a))))
+(15 rows)
+
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+         avg         |         avg         
+---------------------+---------------------
+ 24.5000000000000000 | 24.5000000000000000
+(1 row)
+
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+-- 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 is unsafe to push down partial aggregates which contain DISTINCT clauses
+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
-         ->  Partial GroupAggregate
-               Group Key: pagg_tab.b
-               ->  Foreign Scan on fpagg_tab_p1 pagg_tab
-         ->  Partial GroupAggregate
-               Group Key: pagg_tab_1.b
-               ->  Foreign Scan on fpagg_tab_p2 pagg_tab_1
-         ->  Partial GroupAggregate
-               Group Key: pagg_tab_2.b
-               ->  Foreign Scan on fpagg_tab_p3 pagg_tab_2
-(14 rows)
+         ->  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 is unsafe to push down partial aggregates which contain ORDER BY clauses
+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 aggregates for user-defined aggregate
+-- when it belongs to an extension that is listed in the extension setting
+-- and when its aggpartialfunc belongs to an extension that is listed
+-- in the extension setting.
+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 is unsafe to push down partial aggregates for user-defined aggregates
+-- when they have 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 is unsafe to push down partial aggregates for user-defined aggregates
+-- when itself is aggpartialfunc and when it belongs to an extension
+-- that's listed in the extension setting 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 is unsafe to push down partial aggregates for user-defined aggregates
+-- when it doesn't belong to an extension that is listed in the extension setting
+-- and when its aggpartialfunc belongs to an extension that is listed
+-- in the extension setting.
+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 aggregates for user-defined aggregate
+-- whose stype is not internal
+-- when it belongs to an extension that is listed in the extension setting
+-- and when its aggpartialfunc belongs to an extension that is listed
+-- in the extension setting.
+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 aggregates for user-defined aggregate
+-- when itself is aggpartialfunc and when it belongs to an extension
+-- that's listed in the extension setting 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/option.c b/contrib/postgres_fdw/option.c
index 8c822f4ef9..67e15c8bb9 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -126,7 +126,8 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
 			strcmp(def->defname, "async_capable") == 0 ||
 			strcmp(def->defname, "parallel_commit") == 0 ||
 			strcmp(def->defname, "parallel_abort") == 0 ||
-			strcmp(def->defname, "keep_connections") == 0)
+			strcmp(def->defname, "keep_connections") == 0 ||
+			strcmp(def->defname, "check_partial_aggregate_support") == 0)
 		{
 			/* these accept only boolean values */
 			(void) defGetBoolean(def);
@@ -268,6 +269,7 @@ InitPgFdwOptions(void)
 		/* batch_size is available on both server and table */
 		{"batch_size", ForeignServerRelationId, false},
 		{"batch_size", ForeignTableRelationId, false},
+		{"check_partial_aggregate_support", ForeignServerRelationId, false},
 		/* async_capable is available on both server and table */
 		{"async_capable", ForeignServerRelationId, false},
 		{"async_capable", ForeignTableRelationId, false},
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 6de2bec3b7..209354e771 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -36,6 +36,7 @@
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
+#include "optimizer/planner.h"
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
@@ -519,7 +520,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);
+								GroupPathExtraData *extra);
 static List *get_useful_pathkeys_for_relation(PlannerInfo *root,
 											  RelOptInfo *rel);
 static List *get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel);
@@ -650,6 +651,8 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	fpinfo->fdw_tuple_cost = DEFAULT_FDW_TUPLE_COST;
 	fpinfo->shippable_extensions = NIL;
 	fpinfo->fetch_size = 100;
+	fpinfo->check_partial_aggregate_support = false;
+	fpinfo->remoteversion = 0;
 	fpinfo->async_capable = false;
 
 	apply_server_options(fpinfo);
@@ -661,7 +664,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
 	 * should match what ExecCheckPermissions() does.  If we fail due to lack
 	 * of permissions, the query would have failed at runtime anyway.
 	 */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
 		Oid			userid;
 
@@ -5948,9 +5951,10 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
 	fpinfo->pushdown_safe = true;
 
 	/* Get user mapping */
-	if (fpinfo->use_remote_estimate)
+	if (fpinfo->use_remote_estimate || fpinfo->check_partial_aggregate_support)
 	{
-		if (fpinfo_o->use_remote_estimate)
+		if (fpinfo_o->use_remote_estimate ||
+			fpinfo_o->check_partial_aggregate_support)
 			fpinfo->user = fpinfo_o->user;
 		else
 			fpinfo->user = fpinfo_i->user;
@@ -6132,6 +6136,8 @@ apply_server_options(PgFdwRelationInfo *fpinfo)
 				ExtractExtensionList(defGetString(def), false);
 		else if (strcmp(def->defname, "fetch_size") == 0)
 			(void) parse_int(defGetString(def), &fpinfo->fetch_size, 0, NULL);
+		else if (strcmp(def->defname, "check_partial_aggregate_support") == 0)
+			fpinfo->check_partial_aggregate_support = defGetBoolean(def);
 		else if (strcmp(def->defname, "async_capable") == 0)
 			fpinfo->async_capable = defGetBoolean(def);
 	}
@@ -6191,6 +6197,8 @@ merge_fdw_options(PgFdwRelationInfo *fpinfo,
 	fpinfo->shippable_extensions = fpinfo_o->shippable_extensions;
 	fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate;
 	fpinfo->fetch_size = fpinfo_o->fetch_size;
+	fpinfo->check_partial_aggregate_support = fpinfo_o->check_partial_aggregate_support;
+	fpinfo->remoteversion = fpinfo_o->remoteversion;
 	fpinfo->async_capable = fpinfo_o->async_capable;
 
 	/* Merge the table level options from either side of the join. */
@@ -6373,7 +6381,7 @@ postgresGetForeignJoinPaths(PlannerInfo *root,
  */
 static bool
 foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
-					Node *havingQual)
+					GroupPathExtraData *extra)
 {
 	Query	   *query = root->parse;
 	PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
@@ -6382,11 +6390,16 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	ListCell   *lc;
 	int			i;
 	List	   *tlist = NIL;
+	bool		partial = extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL;
 
 	/* We currently don't support pushing Grouping Sets. */
 	if (query->groupingSets)
 		return false;
 
+	/* It is unsafe to push HAVING statements with partial aggregates */
+	if ((extra->patype == PARTITIONWISE_AGGREGATE_PARTIAL) && extra->havingQual)
+		return false;
+
 	/* Get the fpinfo of the underlying scan relation. */
 	ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
 
@@ -6415,6 +6428,12 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * a node, as long as it's not at top level; then no match is possible.
 	 */
 	i = 0;
+	fpinfo->group_clause = query->groupClause;
+	if (partial)
+	{
+		fpinfo->group_clause = extra->groupClausePartial;
+		grouping_target = extra->partial_target;
+	}
 	foreach(lc, grouping_target->exprs)
 	{
 		Expr	   *expr = (Expr *) lfirst(lc);
@@ -6426,7 +6445,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 		 * check the whole GROUP BY clause not just processed_groupClause,
 		 * because we will ship all of it, cf. appendGroupByClause.
 		 */
-		if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
+		if (sgref && get_sortgroupref_clause_noerr(sgref, fpinfo->group_clause))
 		{
 			TargetEntry *tle;
 
@@ -6512,9 +6531,9 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 	 * Classify the pushable and non-pushable HAVING clauses and save them in
 	 * remote_conds and local_conds of the grouped rel's fpinfo.
 	 */
-	if (havingQual)
+	if (extra->havingQual)
 	{
-		foreach(lc, (List *) havingQual)
+		foreach(lc, (List *) extra->havingQual)
 		{
 			Expr	   *expr = (Expr *) lfirst(lc);
 			RestrictInfo *rinfo;
@@ -6534,7 +6553,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 									  grouped_rel->relids,
 									  NULL,
 									  NULL);
-			if (is_foreign_expr(root, grouped_rel, expr))
+			if (is_foreign_expr(root, grouped_rel, expr) && !partial)
 				fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
 			else
 				fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
@@ -6628,6 +6647,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)
@@ -6644,6 +6664,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;
@@ -6684,7 +6708,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;
@@ -6704,7 +6729,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))
 		return;
 
 	/*
diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h
index 47157ac887..a1a9688c9f 100644
--- a/contrib/postgres_fdw/postgres_fdw.h
+++ b/contrib/postgres_fdw/postgres_fdw.h
@@ -86,6 +86,15 @@ typedef struct PgFdwRelationInfo
 	ForeignServer *server;
 	UserMapping *user;			/* only set in use_remote_estimate mode */
 
+	/* for partial aggregate pushdown */
+	bool		check_partial_aggregate_support;
+
+	/*
+	 * If remoteversion is zero, it means the remote server version has not
+	 * been acquired.
+	 */
+	int			remoteversion;
+
 	int			fetch_size;		/* fetch size for this remote table */
 
 	/*
@@ -110,6 +119,7 @@ typedef struct PgFdwRelationInfo
 
 	/* Grouping information */
 	List	   *grouped_tlist;
+	List	   *group_clause;
 
 	/* Subquery information */
 	bool		make_outerrel_subquery; /* do we deparse outerrel as a
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 2d14eeadb5..20f8b7fae9 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -2993,16 +2993,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.1');
+
+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');
@@ -3010,9 +3025,13 @@ CREATE FOREIGN TABLE fpagg_tab_p2 PARTITION OF pagg_tab FOR VALUES FROM (10) TO
 CREATE FOREIGN TABLE fpagg_tab_p3 PARTITION OF pagg_tab FOR VALUES FROM (20) TO (30) SERVER loopback OPTIONS (table_name 'pagg_tab_p3');
 
 ANALYZE pagg_tab;
+ANALYZE pagg_tab_p1;
+ANALYZE pagg_tab_p2;
+ANALYZE pagg_tab_p3;
 ANALYZE fpagg_tab_p1;
 ANALYZE fpagg_tab_p2;
 ANALYZE fpagg_tab_p3;
+SET extra_float_digits = 0;
 
 -- When GROUP BY clause matches with PARTITION KEY.
 -- Plan with partitionwise aggregates is disabled
@@ -3032,10 +3051,289 @@ EXPLAIN (VERBOSE, COSTS OFF)
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 SELECT a, count(t1) FROM pagg_tab t1 GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
 
--- When GROUP BY clause does not match with PARTITION KEY.
-EXPLAIN (COSTS OFF)
+-- Partial aggregates are unsafe to push down when there is a HAVING clause
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT b, avg(a), max(a), count(*) FROM pagg_tab GROUP BY b HAVING sum(a) < 700 ORDER BY 1;
 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 even if we need both variable and variable-based expression
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+SELECT avg(a), max(a), count(*), (b/2)::numeric FROM pagg_tab GROUP BY b/2 ORDER BY 4;
+
+-- 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;
+
+-- Tests for backward compatibility
+ALTER SERVER loopback OPTIONS (ADD check_partial_aggregate_support 'false');
+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;
+
+ALTER SERVER loopback OPTIONS (SET check_partial_aggregate_support 'true');
+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;
+
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '1.0');
+SET enable_partitionwise_join=on;
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+SELECT avg(t1.b), avg(t1.b::int8) FROM pagg_tab t1 JOIN pagg_tab t2 USING(a);
+RESET enable_partitionwise_join;
+ALTER SERVER loopback OPTIONS (SET fdw_tuple_cost '0.1');
+
+ALTER SERVER loopback OPTIONS (DROP check_partial_aggregate_support);
+
+-- 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 is unsafe to push down partial aggregates which contain DISTINCT clauses
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT max(a), count(distinct b) FROM pagg_tab;
+SELECT max(a), count(distinct b) FROM pagg_tab;
+
+-- It is unsafe to push down partial aggregates which contain ORDER BY clauses
+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 aggregates for user-defined aggregate
+-- when it belongs to an extension that is listed in the extension setting
+-- and when its aggpartialfunc belongs to an extension that is listed
+-- in the extension setting.
+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 is unsafe to push down partial aggregates for user-defined aggregates
+-- when they have 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 is unsafe to push down partial aggregates for user-defined aggregates
+-- when itself is aggpartialfunc and when it belongs to an extension
+-- that's listed in the extension setting 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 is unsafe to push down partial aggregates for user-defined aggregates
+-- when it doesn't belong to an extension that is listed in the extension setting
+-- and when its aggpartialfunc belongs to an extension that is listed
+-- in the extension setting.
+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 aggregates for user-defined aggregate
+-- whose stype is not internal
+-- when it belongs to an extension that is listed in the extension setting
+-- and when its aggpartialfunc belongs to an extension that is listed
+-- in the extension setting.
+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 aggregates for user-defined aggregate
+-- when itself is aggpartialfunc and when it belongs to an extension
+-- that's listed in the extension setting 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 3ec7391ec5..a6dc334726 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -644,6 +644,18 @@
        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="partial-aggregate-pushdown"/> 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 2ba96aa295..286666d9a9 100644
--- a/doc/src/sgml/postgres-fdw.sgml
+++ b/doc/src/sgml/postgres-fdw.sgml
@@ -451,6 +451,43 @@ OPTIONS (ADD password_required 'false');
 
   </sect3>
 
+  <sect3 id="postgres-fdw-options-partial-aggregate-pushdown">
+   <title>Partial Aggregate Pushdown Options</title>
+
+   <para>
+    By default, <filename>postgres_fdw</filename> assumes that each
+    built-in aggregate function has a partial aggregate function defined on
+    the remote server;  see <xref linkend="partial-aggregate-pushdown"/>
+    for a definition of the partial aggregate functions. This may be
+    overridden using the following option:
+   </para>
+
+   <variablelist>
+
+    <varlistentry>
+     <term><literal>check_partial_aggregate_support</literal> (<type>boolean</type>)</term>
+     <listitem>
+      <para>
+       If this option is false, <filename>postgres_fdw</filename> always
+       uses partial aggregate pushdown by assuming that each built-in
+       aggregate function has a partial aggregate function defined on
+       the remote server.  If this option is true, local aggregates
+       whose partial computation function references itself are assumed
+       to exist on the remote server.  If not, during query planning,
+       <filename>postgres_fdw</filename> will connect to the remote
+       server and retrieve the remote server version.  If the remote
+       version is the same or newer, partial aggregate functions will be
+       assumed to exist.  If older, <filename>postgres_fdw</filename>
+       checks that the remote server has a matching partial aggregate
+       function before performing partial aggregate pushdown.  The default
+       is <literal>false</literal>.
+      </para>
+     </listitem>
+    </varlistentry>
+
+   </variablelist>
+  </sect3>
+
   <sect3 id="postgres-fdw-options-asynchronous-execution">
    <title>Asynchronous Execution Options</title>
 
@@ -929,14 +966,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
-   be <literal>IMMUTABLE</literal> as well.
+   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 only use data types, operators, and functions that are built-in
+   or belong to an extension that is 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
    sending the whole query to the remote server if there are no query
@@ -966,6 +1004,64 @@ postgres=# SELECT postgres_fdw_disconnect_all();
   </para>
  </sect2>
 
+ <sect2 id="partial-aggregate-pushdown">
+  <title>Partial aggregate pushdown</title>
+  <para>
+   Partial aggregate pushdown is an optimization for queries that contains
+   aggregate expressions for a partitioned table across one or more remote
+   servers. If multiple conditions are met, partial aggregate function
+   calls are sent to the remote servers.  The conditions under which this
+   sending is active are as follows.
+
+   <itemizedlist spacing="compact">
+    <listitem>
+     <para>
+      A partial aggregate function which corresponds to the aggregate
+      expression is defined on the local server and assumed to exist on
+      the remote server
+     </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>
+      The aggregate expressions in the query do not contain
+      <literal>DISTINCT</literal> or <literal>ORDER BY</literal> clauses
+     </para>
+    </listitem>
+   </itemizedlist>
+
+   When partial aggregate pushdown is used for aggregate expressions,
+   remote queries replace aggregate function calls with partial
+   aggregate function calls.  If the data type of the state value is not
+   <type>internal</type>, the partial aggregate function must not have
+   a final function.  If <type>internal</type>, the partial aggregate
+   function must have the same final function as the aggregate function's
+   serialization function, and 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 the
+   corresponding members in the aggregate function.  Built-in aggregate
+   functions that support partial aggregation must also have their
+   partial aggregate functions defined as built-in aggregates.  See <xref
+   linkend="postgres-fdw-options-partial-aggregate-pushdown"/> for details
+   on how the existance of remote-server partial aggregates are checked.
+   <filename>postgres_fdw</filename> assumes user-defined aggregate
+   functions have partial aggregate functions on the remote server only
+   if both belong to extensions listed in the foreign server's extensions
+   option.
+  </para>
+ </sect2>
+
  <sect2 id="postgres-fdw-remote-query-execution-environment">
   <title>Remote Query Execution Environment</title>
 
@@ -1031,15 +1127,25 @@ 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>.
   </para>
+
+  <para>
+   A similar problem can occur with partial aggregate pushdown.
+   Pushdown causes aggregate function calls to send partial aggregate
+   function calls to the remote server. If the partial aggregate
+   function doesn't exist on the remote server, it causes
+   <quote>function does not exist</quote> or a similar error; see
+   <xref linkend="postgres-fdw-options-partial-aggregate-pushdown"/>
+   on how to enable remote server checks.
+  </para>
  </sect2>
 
  <sect2 id="postgres-fdw-wait-events">
diff --git a/doc/src/sgml/ref/create_aggregate.sgml b/doc/src/sgml/ref/create_aggregate.sgml
index 222e0aa5c9..12beb79dc7 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>
@@ -246,6 +248,12 @@ CREATE [ OR REPLACE ] AGGREGATE <replaceable class="parameter">name</replaceable
    marked <literal>PARALLEL SAFE</literal> to enable parallel aggregation.
   </para>
 
+  <para>
+   Paraemter <literal>AGGPARTIALFUNC</literal> optionally defines a
+   partial aggregate function used for partial aggregate pushdown; see
+   <xref linkend="xaggr-partial-aggregates"/> for details.
+  </para>
+
   <para>
    Aggregates that behave like <function>MIN</function> or <function>MAX</function> can
    sometimes be optimized by looking into an index instead of scanning every
@@ -653,6 +661,32 @@ SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">aggpartialfunc</replaceable></term>
+    <listitem>
+     <para>
+      Parameter <replaceable class="parameter">aggpartialfunc</replaceable>
+      reprsents the name of this aggregate function's
+      partial aggregate function.  If specified, <replaceable
+      class="parameter">combinefunc</replaceable> must also be specified.
+      If <replaceable class="parameter">state_data_type</replaceable>
+      is not <type>internal</type>, the partial aggregate function must
+      not have the <replaceable class="parameter">ffunc</replaceable>.
+      If <replaceable class="parameter">state_data_type</replaceable>
+      is <type>internal</type>, the partial aggregate function
+      must have the same final function as <replaceable
+      class="parameter">serialfunc</replaceable>.  The partial
+      aggregate function and the aggregate function must 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..2863731d00 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,14 @@ SELECT percentile_disc(0.5) WITHIN GROUP (ORDER BY income) FROM households;
    parallel-safety markings on its support functions are not consulted.
   </para>
 
+  <para>
+   <filename>postgres_fdw</filename> can optimize queries containing
+   aggregate expressions on partitioned tables that span multiple
+   remote servers. This optimization sends <firstterm>partial
+   aggregate functions</firstterm> to remote servers;  see <xref
+   linkend="partial-aggregate-pushdown"/> for details.
+  </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/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index a8cea5efe1..f195970967 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -206,7 +206,7 @@ static PathTarget *make_group_input_target(PlannerInfo *root,
 										   PathTarget *final_target);
 static PathTarget *make_partial_grouping_target(PlannerInfo *root,
 												PathTarget *grouping_target,
-												Node *havingQual);
+												GroupPathExtraData *extra);
 static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
 static void optimize_window_clauses(PlannerInfo *root,
 									WindowFuncLists *wflists);
@@ -5415,6 +5415,99 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
 	return set_pathtarget_cost_width(root, input_target);
 }
 
+/*
+ * setGroupClausePartial
+ *	  Generate a groupClause and a pathtarget for partial aggregate
+ *	  pushdown by FDW and set them to GroupPathExtraData.
+ */
+static void
+setGroupClausePartial(PathTarget *partial_target, List *non_group_exprs,
+					  List *groupClause, GroupPathExtraData *extra)
+{
+	int			exprno,
+				refno;
+	ListCell   *lc;
+	Index		maxRef = 0;
+	List	   *exprs_processed = NIL;
+	int			exprs_num = 0;
+
+	foreach(lc, groupClause)
+	{
+		SortGroupClause *sgc = (SortGroupClause *) lfirst(lc);
+
+		if (sgc->tleSortGroupRef > maxRef)
+			maxRef = sgc->tleSortGroupRef;
+	}
+	maxRef++;
+
+	extra->groupClausePartial = list_copy_deep(groupClause);
+	extra->partial_target = copy_pathtarget(partial_target);
+
+	if (partial_target->exprs)
+		exprs_num = partial_target->exprs->length;
+
+	foreach(lc, non_group_exprs)
+	{
+		Expr	   *expr = (Expr *) lfirst(lc);
+
+		refno = -1;
+		if (list_member(exprs_processed, expr) ||
+			(!IsA(expr, Var) && !IsA(expr, PlaceHolderVar)))
+			continue;
+		exprs_processed = lappend(exprs_processed, expr);
+		for (exprno = 0; exprno < exprs_num; exprno++)
+		{
+			Expr	   *target_expr = (Expr *) list_nth(partial_target->exprs, exprno);
+
+			if (equal(target_expr, expr))
+			{
+				refno = exprno;
+				break;
+			}
+		}
+		if (refno < 0)
+		{
+			SortGroupClause *grpcl = makeNode(SortGroupClause);
+
+			grpcl->tleSortGroupRef = maxRef++;
+			extra->groupClausePartial = lappend(extra->groupClausePartial, grpcl);
+			add_column_to_pathtarget(extra->partial_target, expr, grpcl->tleSortGroupRef);
+		}
+	}
+}
+
+/*
+ * adjustAggrefForPartial
+ * Adjust Aggrefs to put them in partial mode
+ */
+static void
+adjustAggrefForPartial(List *exprs)
+{
+	ListCell   *lc;
+
+	foreach(lc, exprs)
+	{
+		Aggref	   *aggref = (Aggref *) lfirst(lc);
+
+		if (IsA(aggref, Aggref))
+		{
+			Aggref	   *newaggref;
+
+			/*
+			 * We shouldn't need to copy the substructure of the Aggref node,
+			 * but flat-copy the node itself to avoid damaging other trees.
+			 */
+			newaggref = makeNode(Aggref);
+			memcpy(newaggref, aggref, sizeof(Aggref));
+
+			/* For now, assume serialization is required */
+			mark_partial_aggref(newaggref, AGGSPLIT_INITIAL_SERIAL);
+
+			lfirst(lc) = newaggref;
+		}
+	}
+}
+
 /*
  * make_partial_grouping_target
  *	  Generate appropriate PathTarget for output of partial aggregate
@@ -5430,11 +5523,15 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
  *
  * grouping_target is the tlist to be emitted by the topmost aggregation step.
  * havingQual represents the HAVING clause.
+ *
+ * Modified PathTarget cannot be used by FDW as-is to deparse this statement.
+ * So, before modifying PathTarget, setGroupClausePartial generates
+ * another Pathtarget and another list of SortGroupClauses.
  */
 static PathTarget *
 make_partial_grouping_target(PlannerInfo *root,
 							 PathTarget *grouping_target,
-							 Node *havingQual)
+							 GroupPathExtraData *extra)
 {
 	PathTarget *partial_target;
 	List	   *non_group_cols;
@@ -5476,8 +5573,8 @@ make_partial_grouping_target(PlannerInfo *root,
 	/*
 	 * If there's a HAVING clause, we'll need the Vars/Aggrefs it uses, too.
 	 */
-	if (havingQual)
-		non_group_cols = lappend(non_group_cols, havingQual);
+	if (extra->havingQual)
+		non_group_cols = lappend(non_group_cols, extra->havingQual);
 
 	/*
 	 * Pull out all the Vars, PlaceHolderVars, and Aggrefs mentioned in
@@ -5490,35 +5587,17 @@ make_partial_grouping_target(PlannerInfo *root,
 									  PVC_INCLUDE_AGGREGATES |
 									  PVC_RECURSE_WINDOWFUNCS |
 									  PVC_INCLUDE_PLACEHOLDERS);
-
+	setGroupClausePartial(partial_target, non_group_exprs, root->parse->groupClause, extra);
 	add_new_columns_to_pathtarget(partial_target, non_group_exprs);
+	add_new_columns_to_pathtarget(extra->partial_target, non_group_exprs);
 
 	/*
 	 * Adjust Aggrefs to put them in partial mode.  At this point all Aggrefs
 	 * are at the top level of the target list, so we can just scan the list
 	 * rather than recursing through the expression trees.
 	 */
-	foreach(lc, partial_target->exprs)
-	{
-		Aggref	   *aggref = (Aggref *) lfirst(lc);
-
-		if (IsA(aggref, Aggref))
-		{
-			Aggref	   *newaggref;
-
-			/*
-			 * We shouldn't need to copy the substructure of the Aggref node,
-			 * but flat-copy the node itself to avoid damaging other trees.
-			 */
-			newaggref = makeNode(Aggref);
-			memcpy(newaggref, aggref, sizeof(Aggref));
-
-			/* For now, assume serialization is required */
-			mark_partial_aggref(newaggref, AGGSPLIT_INITIAL_SERIAL);
-
-			lfirst(lc) = newaggref;
-		}
-	}
+	adjustAggrefForPartial(partial_target->exprs);
+	adjustAggrefForPartial(extra->partial_target->exprs);
 
 	/* clean up cruft */
 	list_free(non_group_exprs);
@@ -7148,7 +7227,7 @@ create_partial_grouping_paths(PlannerInfo *root,
 	 */
 	partially_grouped_rel->reltarget =
 		make_partial_grouping_target(root, grouped_rel->reltarget,
-									 extra->havingQual);
+									 extra);
 
 	if (!extra->partial_costs_set)
 	{
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index e863913849..751a1222b3 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -13921,6 +13921,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;
@@ -14003,11 +14004,18 @@ 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 >= 170000)
+			appendPQExpBufferStr(query,
+								 "aggpartialfn\n");
+		else
+			appendPQExpBufferStr(query,
+								 "'-' AS aggpartialfn\n");
 
 		appendPQExpBufferStr(query,
 							 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
@@ -14033,6 +14041,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"));
@@ -14122,6 +14131,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 085149e258..3e4f8331ee 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 bd0b8873d3..3d2d4faa1f 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',
@@ -5004,6 +5011,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',
+  proargnames => '{value,delimiter}', prosrc => 'aggregate_dummy' },
 { oid => '3538', descr => 'concatenate aggregate input into a string',
   proname => 'string_agg', prokind => 'a', proisstrict => 'f',
   prorettype => 'text', proargtypes => 'text text',
@@ -5016,6 +5027,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',
+  proargnames => '{value,delimiter}', prosrc => 'aggregate_dummy' },
 { oid => '3545', descr => 'concatenate aggregate input into a bytea',
   proname => 'string_agg', prokind => 'a', proisstrict => 'f',
   prorettype => 'bytea', proargtypes => 'bytea bytea',
@@ -6602,36 +6617,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' },
@@ -6654,6 +6694,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' },
@@ -6812,153 +6855,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',
@@ -6969,52 +7120,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 => '2775', 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 => '3063', 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 => '3814', 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 => '4551', 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 => '4552', 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 => '4553',
+  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 => '4554', 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 => '4555', 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 => '4556', 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/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index ed85dc7414..411a249482 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -3243,6 +3243,9 @@ typedef enum
  * havingQual gives list of quals to be applied after aggregation.
  * targetList gives list of columns to be projected.
  * patype is the type of partitionwise aggregation that is being performed.
+ * groupClausePartial is List of SortGroupClauses for partial aggregate
+ * 		pushdown by FDW
+ * partial_target is PathTarget for partial aggregate pushdown by FDW
  */
 typedef struct
 {
@@ -3257,6 +3260,8 @@ typedef struct
 	Node	   *havingQual;
 	List	   *targetList;
 	PartitionwiseAggregateType patype;
+	List	   *groupClausePartial;
+	PathTarget *partial_target;
 } GroupPathExtraData;
 
 /*
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.39.3



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

* Re: Partial aggregates pushdown
@ 2023-11-20 20:51  Robert Haas <[email protected]>
  parent: [email protected] <[email protected]>
  0 siblings, 0 replies; 38+ messages in thread

From: Robert Haas @ 2023-11-20 20:51 UTC (permalink / raw)
  To: [email protected] <[email protected]>; +Cc: pgsql-hackers; Finnerty, Jim <[email protected]>; Andres Freund <[email protected]>; Tom Lane <[email protected]>; Bruce Momjian <[email protected]>; Tomas Vondra <[email protected]>; Julien Rouhaud <[email protected]>; Daniel Gustafsson <[email protected]>; Alexander Pyhalov <[email protected]>

On Mon, Nov 13, 2023 at 3:26 AM [email protected]
<[email protected]> wrote:
> In postgres_fdw.sql, I have corrected the output format for floating point numbers
> by extra_float_digits.

Looking at this, I find that it's not at all clear to me how the
partial aggregate function is defined. Let's look at what we have for
documentation:

+  <para>
+   Paraemter <literal>AGGPARTIALFUNC</literal> optionally defines a
+   partial aggregate function used for partial aggregate pushdown; see
+   <xref linkend="xaggr-partial-aggregates"/> for details.
+  </para>

+       Partial aggregate function (zero if none).
+       See <xref linkend="partial-aggregate-pushdown"/> for the definition
+       of partial aggregate function.

+   Partial aggregate pushdown is an optimization for queries that contains
+   aggregate expressions for a partitioned table across one or more remote
+   servers. If multiple conditions are met, partial aggregate function

+   When partial aggregate pushdown is used for aggregate expressions,
+   remote queries replace aggregate function calls with partial
+   aggregate function calls.  If the data type of the state value is not

But there's no definition of what the behavior of the function is
anywhere that I can see, not even in <sect2
id="partial-aggregate-pushdown">. Everywhere it only describes how the
partial aggregate function is used, not what it is supposed to do.

Looking at the changes in pg_aggregate.dat, it seems like the partial
aggregate function is a second aggregate defined in a way that mostly
matches the original, except that (1) if the original final function
would have returned a data type other than internal, then the final
function is removed; and (2) if the original final function would have
returned a value of internal type, then the final function is the
serialization function of the original aggregate. I think that's a
reasonable definition, but the documentation and code comments need to
be a lot clearer.

I do have a concern about this, though. It adds a lot of bloat. It
adds a whole lot of additional entries to pg_aggregate, and every new
aggregate we add in the future will require a bonus entry for this,
and it needs a bunch of new pg_proc entries as well. One idea that
I've had in the past is to instead introduce syntax that just does
this, without requiring a separate aggregate definition in each case.
For example, maybe instead of changing string_agg(whatever) to
string_agg_p_text_text(whatever), you can say PARTIAL_AGGREGATE
string_agg(whatever) or string_agg(PARTIAL_AGGREGATE whatever) or
something. Then all aggregates could be treated in a generic way. I'm
not completely sure that's better, but I think it's worth considering.

I think that the control mechanism needs some thought. Right now,
there are two possible behaviors: either we assume that the local and
remote sides are the same unconditionally, or we assume that they're
the same if the remote side is a new enough version. I do like having
those behaviors available, but I wonder if we need to do something
better or different. What if somebody wants to push down a
non-built-in aggregate, for example? I realize that we don't have
great solutions to the problem of knowing which functions are
push-downable in general, and I don't know that partial aggregation
needs to be any better than anything else, but it's probably worth
comparing and contrasting the approach we take here with the
approaches we've taken in other, similar cases. From that point of
view, I think check_partial_aggregate_support is a novelty: we don't
do those kinds of checks in other cases, AFAIK. But on the other hand,
there is the 'extensions' argument to postgres_fdw.

I don't think the patch does a good job explaining why HAVING,
DISTINCT, and ORDER BY are a problem. It seems to me that HAVING
shouldn't really be a problem, because HAVING is basically a WHERE
clause that occurs after aggregation is complete, and whether or not
the aggregation is safe shouldn't depend on what we're going to do
with the value afterward. The HAVING clause can't necessarily be
pushed to the remote side, but I don't see how or why it could make
the aggregate itself unsafe to push down. DISTINCT and ORDER BY are a
little trickier: if we pushed down DISTINCT, we'd still have to
re-DISTINCT-ify when combining locally, and if we pushed down ORDER
BY, we'd have to do a merge pass to combine the returned values unless
we could prove that the partitions were non-overlapping ranges that
would be visited in the correct order. Although that all sounds
doable, I think it's probably a good thing that the current patch
doesn't try to handle it -- this is complicated already. But it should
explain why it's not handling it and maybe even a bit about how it
could be handling in the future, rather than just saying "well, this
kind of thing is not safe." The trouble with that explanation is that
it does nothing to help the reader understand whether the thing in
question is *fundamentally* unsafe or whether we just don't have the
right code to make it work.

Typo: Paraemter

I'm so sorry to keep complaining about comments, but I think the
comments in src/backend/optimizer are very far from being adequate.
They are strictly formulaic and don't really explain anything. For
example, I see that the patch adds a partial_target to
GroupPathExtraData, but how do I understand the reason why we now need
a second pathtarget beside the one that already exists? Certainly not
from the comments in setGroupClausePartial, because there basically
aren't any. True, there's a header comment, but it just says we
generate this thing, not WHY we generate this thing. There's nothing
meaningful to be found in src/include/nodes/pathnodes.h about why
we're doing this, either.

And this problem really extends throughout the patch: comments are
mostly short and just describe what the code does, not WHY it does
that. And the WHY is really the important part. Otherwise we will not
be able to maintain this code going forward.

-- 
Robert Haas
EDB: http://www.enterprisedb.com






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

* [PATCH v3 08/17] Make opp freeze heuristic compatible with prune+freeze record
@ 2024-01-07 21:11  Melanie Plageman <[email protected]>
  0 siblings, 0 replies; 38+ messages in thread

From: Melanie Plageman @ 2024-01-07 21:11 UTC (permalink / raw)

Once the prune and freeze records are combined, we will no longer be
able to use a test of whether or not pruning emitted an FPI to decide
whether or not to opportunistically freeze a freezable page.

While this heuristic should be improved, for now, approximate the
previous logic by keeping track of whether or not a hint bit FPI was
emitted during visibility checks (when checksums are on) and combine
that with checking XLogCheckBufferNeedsBackup(). If we just finished
deciding whether or not to prune and the current buffer seems to need an
FPI after modification, it is likely that pruning would have emitted an
FPI.
---
 src/backend/access/heap/pruneheap.c | 58 +++++++++++++++++++++--------
 1 file changed, 43 insertions(+), 15 deletions(-)

diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index abf6bdb2d99..f164b7957ed 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -255,6 +255,10 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer,
 	PruneState	prstate;
 	HeapTupleData tup;
 	bool		do_freeze;
+	bool		do_prune;
+	bool		whole_page_freezable;
+	bool		hint_bit_fpi;
+	bool		prune_fpi = false;
 	int64		fpi_before = pgWalUsage.wal_fpi;
 	TransactionId frz_conflict_horizon = InvalidTransactionId;
 
@@ -424,6 +428,13 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer,
 		}
 	}
 
+	/*
+	 * If checksums are enabled, heap_prune_satisfies_vacuum() may have caused
+	 * an FPI to be emitted. Then reset fpi_before for no prune case.
+	 */
+	hint_bit_fpi = fpi_before != pgWalUsage.wal_fpi;
+	fpi_before = pgWalUsage.wal_fpi;
+
 	/*
 	 * For vacuum, if the whole page will become frozen, we consider
 	 * opportunistically freezing tuples. Dead tuples which will be removed by
@@ -481,11 +492,42 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer,
 	if (off_loc)
 		*off_loc = InvalidOffsetNumber;
 
+	do_prune = prstate.nredirected > 0 ||
+		prstate.ndead > 0 ||
+		prstate.nunused > 0;
+
+	/*
+	 * Only incur overhead of checking if we will do an FPI if we might use
+	 * the information.
+	 */
+	if (do_prune && pagefrz)
+		prune_fpi = XLogCheckBufferNeedsBackup(buffer);
+
+	/* Is the whole page freezable? And is there something to freeze */
+	whole_page_freezable = presult->all_visible_except_removable &&
+		presult->all_frozen;
+
+	/*
+	 * Freeze the page when heap_prepare_freeze_tuple indicates that at least
+	 * one XID/MXID from before FreezeLimit/MultiXactCutoff is present.  Also
+	 * freeze when pruning generated an FPI, if doing so means that we set the
+	 * page all-frozen afterwards (might not happen until final heap pass).
+	 * XXX: Previously, we knew if pruning emitted an FPI by checking
+	 * pgWalUsage.wal_fpi before and after pruning. Once the freeze and prune
+	 * records are combined, this heuristic couldn't be used anymore. The
+	 * opportunistic freeze heuristic must be improved; however, for now, try
+	 * to approximate it.
+	 */
+
+	do_freeze = pagefrz &&
+		(pagefrz->freeze_required ||
+		 (whole_page_freezable && presult->nfrozen > 0 && (prune_fpi || hint_bit_fpi)));
+
 	/* Any error while applying the changes is critical */
 	START_CRIT_SECTION();
 
 	/* Have we found any prunable items? */
-	if (prstate.nredirected > 0 || prstate.ndead > 0 || prstate.nunused > 0)
+	if (do_prune)
 	{
 		/*
 		 * Apply the planned item changes, then repair page fragmentation, and
@@ -577,20 +619,6 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer,
 	/* Record number of newly-set-LP_DEAD items for caller */
 	presult->nnewlpdead = prstate.ndead;
 
-	/*
-	 * Freeze the page when heap_prepare_freeze_tuple indicates that at least
-	 * one XID/MXID from before FreezeLimit/MultiXactCutoff is present.  Also
-	 * freeze when pruning generated an FPI, if doing so means that we set the
-	 * page all-frozen afterwards (might not happen until final heap pass).
-	 */
-	if (pagefrz)
-		do_freeze = pagefrz->freeze_required ||
-			(presult->all_visible_except_removable && presult->all_frozen &&
-			 presult->nfrozen > 0 &&
-			 fpi_before != pgWalUsage.wal_fpi);
-	else
-		do_freeze = false;
-
 	if (do_freeze)
 	{
 		frz_conflict_horizon = heap_frz_conflict_horizon(presult, pagefrz);
-- 
2.40.1


--racicctn4wry6xe5
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v3-0009-Separate-tuple-pre-freeze-checks-and-invoke-earli.patch"



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


end of thread, other threads:[~2024-01-07 21:11 UTC | newest]

Thread overview: 38+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2023-06-12 08:51 RE: Partial aggregates pushdown [email protected] <[email protected]>
2023-06-12 13:37 ` Bruce Momjian <[email protected]>
2023-06-13 02:18   ` [email protected] <[email protected]>
2023-06-20 00:42     ` Bruce Momjian <[email protected]>
2023-06-20 06:59       ` Alexander Pyhalov <[email protected]>
2023-06-21 15:43         ` Bruce Momjian <[email protected]>
2023-06-22 05:23           ` [email protected] <[email protected]>
2023-06-22 11:39             ` Bruce Momjian <[email protected]>
2023-07-10 07:35               ` [email protected] <[email protected]>
2023-07-14 13:40                 ` Alexander Pyhalov <[email protected]>
2023-07-18 01:35                   ` [email protected] <[email protected]>
2023-07-19 00:43                     ` [email protected] <[email protected]>
2023-07-20 10:23                       ` Alexander Pyhalov <[email protected]>
2023-08-01 20:25                         ` Finnerty, Jim <[email protected]>
2023-08-07 18:30                 ` Bruce Momjian <[email protected]>
2023-09-25 03:18                   ` [email protected] <[email protected]>
2023-09-25 22:30                     ` Bruce Momjian <[email protected]>
2023-09-26 06:26                       ` [email protected] <[email protected]>
2023-09-26 14:16                         ` Bruce Momjian <[email protected]>
2023-09-26 22:35                           ` [email protected] <[email protected]>
2023-09-27 12:36                             ` Alexander Pyhalov <[email protected]>
2023-09-28 04:40                               ` [email protected] <[email protected]>
2023-09-28 06:15                                 ` Alexander Pyhalov <[email protected]>
2023-09-28 07:21                                   ` [email protected] <[email protected]>
2023-10-18 05:22                                     ` [email protected] <[email protected]>
2023-10-23 16:55                                       ` Bruce Momjian <[email protected]>
2023-10-24 00:12                                         ` [email protected] <[email protected]>
2023-10-25 22:08                                           ` Bruce Momjian <[email protected]>
2023-10-26 11:11                                             ` [email protected] <[email protected]>
2023-10-26 19:43                                               ` Bruce Momjian <[email protected]>
2023-10-27 02:44                                                 ` [email protected] <[email protected]>
2023-10-27 03:06                                                   ` Bruce Momjian <[email protected]>
2023-10-27 05:32                                                     ` [email protected] <[email protected]>
2023-11-13 05:48                                                       ` [email protected] <[email protected]>
2023-11-13 08:25                                                         ` [email protected] <[email protected]>
2023-11-20 20:51                                                           ` Robert Haas <[email protected]>
2023-09-26 13:15                     ` Alexander Pyhalov <[email protected]>
2024-01-07 21:11 [PATCH v3 08/17] Make opp freeze heuristic compatible with prune+freeze record Melanie Plageman <[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