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
