Fire create_upper_paths_hook for UPPERREL_PARTIAL_GROUP_AGG

Started by Andrew Dunstanabout 1 month ago7 messageshackers
Beta feature

Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.

appliessuccessCI history

You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:

docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t253395
psql -h localhost -U postgres

Built from patchset v5 (message #5), September 20, 2026 at 12:54 AM.

Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:

git clone --branch t253395_5 https://github.com/hackorum-dev/postgres.git

In a checkout you already have, add the fork once:

git remote add hackorum https://github.com/hackorum-dev/postgres.git

then, for this patchset and every later one:

git fetch hackorum t253395_5 && git checkout t253395_5

Patchset v5 (message #5) is on t253395_5

Jump to latest
#1Andrew Dunstan
andrew@dunslane.net

Greetings

create_partial_grouping_paths() builds the UPPERREL_PARTIAL_GROUP_AGG
upper relation.  It already calls the FDW callback GetForeignUpperPaths
there, but never calls create_upper_paths_hook, the general-purpose
hook that non-FDW extensions use to add paths. UPPERREL_PARTIAL_DISTINCT
doesn't have this gap: create_partial_distinct_paths() calls both
GetForeignUpperPaths and create_upper_paths_hook for it.

This patch adds the missing create_upper_paths_hook call right next to
the existing GetForeignUpperPaths call, so a non-FDW extension can add
partial aggregation paths at the same point an FDW already can, before
those paths are gathered and a Finalize Aggregate is built on top.

It's a small, self-contained planner change with no effect on existing
plans unless an extension registers create_upper_paths_hook and adds
paths at this new call site.

cheers

andrew

--
Andrew Dunstan
EDB: https://www.enterprisedb.com

Attachments:

t253395_1
v2-0001-Fire-create_upper_paths_hook-for-UPPERREL_PARTIAL.patchtext/x-patch; charset=UTF-8; name=v2-0001-Fire-create_upper_paths_hook-for-UPPERREL_PARTIAL.patchDownload+18-1
#2Bryan Green
dbryan.green@gmail.com
In reply to: Andrew Dunstan (#1)
Re: Fire create_upper_paths_hook for UPPERREL_PARTIAL_GROUP_AGG

On 8/12/26 17:32, Andrew Dunstan wrote:

Greetings

create_partial_grouping_paths() builds the UPPERREL_PARTIAL_GROUP_AGG
upper relation.  It already calls the FDW callback GetForeignUpperPaths
there, but never calls create_upper_paths_hook, the general-purpose
hook that non-FDW extensions use to add paths. UPPERREL_PARTIAL_DISTINCT
doesn't have this gap: create_partial_distinct_paths() calls both
GetForeignUpperPaths and create_upper_paths_hook for it.

This patch adds the missing create_upper_paths_hook call right next to
the existing GetForeignUpperPaths call, so a non-FDW extension can add
partial aggregation paths at the same point an FDW already can, before
those paths are gathered and a Finalize Aggregate is built on top.

It's a small, self-contained planner change with no effect on existing
plans unless an extension registers create_upper_paths_hook and adds
paths at this new call site.

cheers

andrew

--
Andrew Dunstan
EDB: https://www.enterprisedb.com

Andrew,

If you use enable_partitionwise_aggregate then
create_partial_grouping_paths will run once for the parent and then
again for each child. This leads to the
upper_targets[UPPERREL_PARTIAL_GROUP_AGG] assignment being overwritten
per child, resulting in upper_targets holding the last child's
reltarget. This happens whenever partitionwise aggregation is
considered-- it doesn't have to win. So any extension reading the slot
after the grouping stage gets the wrong target. I reproduced this with a
test hook.

The hook already gets the target as output_rel->reltarget, so I think we
could just drop the assignment?

bg

--
Bryan Green
EDB: https://www.enterprisedb.com

#3Andrew Dunstan
andrew@dunslane.net
In reply to: Bryan Green (#2)
Re: Fire create_upper_paths_hook for UPPERREL_PARTIAL_GROUP_AGG

On 2026-08-26 We 2:44 PM, Bryan Green wrote:

On 8/12/26 17:32, Andrew Dunstan wrote:

Greetings

create_partial_grouping_paths() builds the UPPERREL_PARTIAL_GROUP_AGG
upper relation.  It already calls the FDW callback GetForeignUpperPaths
there, but never calls create_upper_paths_hook, the general-purpose
hook that non-FDW extensions use to add paths. UPPERREL_PARTIAL_DISTINCT
doesn't have this gap: create_partial_distinct_paths() calls both
GetForeignUpperPaths and create_upper_paths_hook for it.

This patch adds the missing create_upper_paths_hook call right next to
the existing GetForeignUpperPaths call, so a non-FDW extension can add
partial aggregation paths at the same point an FDW already can, before
those paths are gathered and a Finalize Aggregate is built on top.

It's a small, self-contained planner change with no effect on existing
plans unless an extension registers create_upper_paths_hook and adds
paths at this new call site.

Andrew,

If you use enable_partitionwise_aggregate then
create_partial_grouping_paths will run once for the parent and then
again for each child. This leads to the
upper_targets[UPPERREL_PARTIAL_GROUP_AGG] assignment being overwritten
per child, resulting in upper_targets holding the last child's
reltarget. This happens whenever partitionwise aggregation is
considered-- it doesn't have to win. So any extension reading the slot
after the grouping stage gets the wrong target. I reproduced this with a
test hook.

The hook already gets the target as output_rel->reltarget, so I think we
could just drop the assignment?

Quite right. That was left over from an earlier draft, and is clearly an
error, thanks for catching it. Here's an updated patch.

cheers

andrew

--
Andrew Dunstan
EDB: https://www.enterprisedb.com

Attachments:

t253395_3
v3-0001-Fire-create_upper_paths_hook-for-UPPERREL_PARTIAL.patchtext/x-patch; charset=UTF-8; name=v3-0001-Fire-create_upper_paths_hook-for-UPPERREL_PARTIAL.patchDownload+13-1
#4Himanshu Upadhyaya
upadhyaya.himanshu@gmail.com
In reply to: Andrew Dunstan (#3)
Re: Fire create_upper_paths_hook for UPPERREL_PARTIAL_GROUP_AGG

The following review has been posted through the commitfest application:
make installcheck-world: tested, passed
Implements feature: tested, passed
Spec compliant: tested, passed
Documentation: not tested

Patch looks good. Just a note: create_upper_paths_hook has no test coverage anywhere in the tree, at any call site — a pre-existing gap, not something to fix here. Could be a separate follow-up.
--
Regards,
Himanshu Upadhyaya
EnterpriseDB: http://www.enterprisedb.com

#5shihao zhong
zhong950419@gmail.com
In reply to: Himanshu Upadhyaya (#4)
Re: Fire create_upper_paths_hook for UPPERREL_PARTIAL_GROUP_AGG

create_upper_paths_hook has no test coverage anywhere in the tree

I agree this patch needs test coverage, so I have attached 0002. It
reuses test_extensible, which already installs a planner hook: a new
GUC makes it add a partial aggregate path from create_upper_paths_hook,
and the test checks that the planner gathers it and builds Finalize
Aggregate on top. It fails without 0001.

Nit on the code comment: the other five call sites use a single line,
so maybe just "/* Let extensions possibly add some more partial paths
*/" and move the rest to the commit message. "Heap and other AMs are
unaffected" also looks like a leftover from v2.

I will leave it to the author to decide whether to take these.
Thanks,
Shihao

Attachments:

t253395_5
0002-Test-create_upper_paths_hook-for-UPPERREL_PARTIAL_GR.patchapplication/x-patch; name=0002-Test-create_upper_paths_hook-for-UPPERREL_PARTIAL_GR.patchDownload+56-2
v3-0001-Fire-create_upper_paths_hook-for-UPPERREL_PARTIAL.patchapplication/x-patch; name=v3-0001-Fire-create_upper_paths_hook-for-UPPERREL_PARTIAL.patchDownload+13-1
#6Tom Lane
tgl@sss.pgh.pa.us
In reply to: shihao zhong (#5)
Re: Fire create_upper_paths_hook for UPPERREL_PARTIAL_GROUP_AGG

shihao zhong <zhong950419@gmail.com> writes:

create_upper_paths_hook has no test coverage anywhere in the tree

I agree this patch needs test coverage, so I have attached 0002. It
reuses test_extensible, which already installs a planner hook: a new
GUC makes it add a partial aggregate path from create_upper_paths_hook,
and the test checks that the planner gathers it and builds Finalize
Aggregate on top. It fails without 0001.

What does this test, other than the test module itself? The hook
call in itself has no interesting behavior, and I don't think that
we need to spin up a whole database session just to pass through
that code line.

In general, I think people have become far too enamored of tests
for the sake of tests, and are not thinking about the downstream
costs in CI and buildfarm cycles, developer time spent waiting for
tests to run, and for that matter the nonzero ongoing maintenance
effort that every test script has. Sure, those costs are tiny
on a one-time basis, but they add up fast.

It's a good idea to write something like this to convince yourself
that the hook is usable for something useful, but that doesn't mean
we need to carry that proof in-tree forevermore.

regards, tom lane

#7shihao zhong
zhong950419@gmail.com
In reply to: Tom Lane (#6)
Re: Fire create_upper_paths_hook for UPPERREL_PARTIAL_GROUP_AGG

It's a good idea to write something like this to convince yourself
that the hook is usable for something useful, but that doesn't mean
we need to carry that proof in-tree forevermore.

You're right. The test only checks that the hook gets called. It
doesn't check that anything behaves differently, and that isn't worth
spinning up a session for. Writing it did convince me the hook is
usable, like you say, but that doesn't mean it has to stay in the tree.
Dropping 0002.

I should also correct what I wrote about it. That description was from
an different version which added a path and checked that the planner
gathered it and built Finalize Aggregate on top. Even that is existing
planner behavior, not something 0001 adds.

Review is otherwise unchanged. I only have the Nit commit message and
comment suggestions from my earlier mail.

Given there is no major feedback open, I change the patch to Ready for
Committer.

Thanks,
Shihao