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
From 9274b32c9a551d6eda17d1ed023f3fdd5e7ad813 Mon Sep 17 00:00:00 2001
From: Andrew Dunstan <[email protected]>
Date: Sat, 9 May 2026 14:57:09 -0400
Subject: [PATCH v2 1/1] Fire create_upper_paths_hook for
 UPPERREL_PARTIAL_GROUP_AGG

The planner already invites FDWs and extensions to add partial paths
on UPPERREL_PARTIAL_DISTINCT (see create_partial_distinct_paths), but
the symmetric create_upper_paths_hook call on UPPERREL_PARTIAL_GROUP_AGG
is missing.  Without it, an extension cannot register a partial-aggregate
path through the standard hook surface: by the time its
create_upper_paths_hook runs on UPPERREL_GROUP_AGG, the planner has
already gathered partial paths from partially_grouped_rel and built
Finalize Aggregate on top, so paths added late are dead code.

Fire create_upper_paths_hook on partially_grouped_rel at the end of
create_partial_grouping_paths, right next to the existing
GetForeignUpperPaths call for UPPERREL_PARTIAL_GROUP_AGG.  This mirrors
create_partial_distinct_paths, which pairs its GetForeignUpperPaths and
create_upper_paths_hook calls together at the end of that function for
UPPERREL_PARTIAL_DISTINCT.  Extensions adding partial paths here are
picked up naturally by the caller's subsequent gather_grouping_paths
call and become candidates for the upstream Finalize Aggregate built by
add_paths_to_grouping_rel.

The motivating use case is a CustomScan that wants to participate in
partial aggregation -- e.g. a table access method extension whose pushdown
runs cheaper than nodeAgg's standard transition path.  Without this
hook, the extension is forced to choose between sequential agg
pushdown (losing parallelism) or letting nodeAgg run unmodified
above its parallel scan (losing the pushdown).

Heap and other AMs are unaffected: this commit only adds a single
create_upper_paths_hook call at a place where it wasn't being fired
before.
---
 src/backend/optimizer/plan/planner.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index f4689e7c9f8..1ad6fee1cf2 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8016,6 +8016,24 @@ create_partial_grouping_paths(PlannerInfo *root,
 										 extra);
 	}

+	/*
+	 * Let extensions possibly add some more partial paths, mirroring the
+	 * FDW call above and the analogous extension hook on
+	 * UPPERREL_PARTIAL_DISTINCT in create_partial_distinct_paths.  Paths
+	 * added here are picked up by the caller's subsequent
+	 * gather_grouping_paths call and become candidates for the upstream
+	 * Finalize Aggregate that add_paths_to_grouping_rel builds.
+	 */
+	if (create_upper_paths_hook)
+	{
+		root->upper_targets[UPPERREL_PARTIAL_GROUP_AGG] =
+			partially_grouped_rel->reltarget;
+
+		(*create_upper_paths_hook) (root, UPPERREL_PARTIAL_GROUP_AGG,
+									input_rel, partially_grouped_rel,
+									extra);
+	}
+
 	return partially_grouped_rel;
 }

--
2.43.0

Reply via email to