From c10fa47d30783412edb5d26e6f9f77d994755ed9 Mon Sep 17 00:00:00 2001 From: Alexander Korotkov Date: Wed, 5 Nov 2025 01:40:03 +0200 Subject: [PATCH v3] Fix assertion failure in generate_orderedappend_paths() Reported-by: Bug: Discussion: Author: Co-authored-by: Reviewed-by: Tested-by: Backpatch-through: --- src/backend/optimizer/path/allpaths.c | 16 ++-------- src/backend/optimizer/path/pathkeys.c | 9 ++++-- src/backend/optimizer/plan/planagg.c | 2 +- src/include/optimizer/paths.h | 2 +- .../regress/expected/partition_aggregate.out | 31 +++++++++++++++++++ src/test/regress/sql/partition_aggregate.sql | 5 +++ 6 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 9c6436eb72f..b93e00f6cb3 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -1974,23 +1974,11 @@ generate_orderedappend_paths(PlannerInfo *root, RelOptInfo *rel, */ if (root->tuple_fraction > 0) { - double path_fraction = root->tuple_fraction; - - /* - * Merge Append considers only live children relations. Dummy - * relations must be filtered out before. - */ - Assert(childrel->rows > 0); - - /* Convert absolute limit to a path fraction */ - if (path_fraction >= 1.0) - path_fraction /= childrel->rows; - cheapest_fractional = - get_cheapest_fractional_path_for_pathkeys(childrel->pathlist, + get_cheapest_fractional_path_for_pathkeys(childrel, pathkeys, NULL, - path_fraction); + root->tuple_fraction); /* * If we found no path with matching pathkeys, use the diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index 139fa1f875a..630aa598747 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -663,15 +663,20 @@ get_cheapest_path_for_pathkeys(List *paths, List *pathkeys, * 'fraction' is the fraction of the total tuples expected to be retrieved */ Path * -get_cheapest_fractional_path_for_pathkeys(List *paths, +get_cheapest_fractional_path_for_pathkeys(RelOptInfo *rel, List *pathkeys, Relids required_outer, double fraction) { + Path *best_path = rel->cheapest_total_path; Path *matched_path = NULL; ListCell *l; - foreach(l, paths) + /* Convert absolute # of tuples to a fraction; no need to clamp to 0..1 */ + if (fraction >= 1.0 && best_path->rows > 0) + fraction /= best_path->rows; + + foreach(l, rel->pathlist) { Path *path = (Path *) lfirst(l); diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c index a2ac58d246e..0fa91cd6b5f 100644 --- a/src/backend/optimizer/plan/planagg.c +++ b/src/backend/optimizer/plan/planagg.c @@ -443,7 +443,7 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo, path_fraction = 1.0; sorted_path = - get_cheapest_fractional_path_for_pathkeys(final_rel->pathlist, + get_cheapest_fractional_path_for_pathkeys(final_rel, subroot->query_pathkeys, NULL, path_fraction); diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index f6a62df0b43..23f65176eb4 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -223,7 +223,7 @@ extern Path *get_cheapest_path_for_pathkeys(List *paths, List *pathkeys, Relids required_outer, CostSelector cost_criterion, bool require_parallel_safe); -extern Path *get_cheapest_fractional_path_for_pathkeys(List *paths, +extern Path *get_cheapest_fractional_path_for_pathkeys(RelOptInfo *rel, List *pathkeys, Relids required_outer, double fraction); diff --git a/src/test/regress/expected/partition_aggregate.out b/src/test/regress/expected/partition_aggregate.out index fc84929a002..c30304b99c7 100644 --- a/src/test/regress/expected/partition_aggregate.out +++ b/src/test/regress/expected/partition_aggregate.out @@ -339,6 +339,37 @@ SELECT a FROM pagg_tab WHERE a < 3 GROUP BY a ORDER BY 1; 2 (3 rows) +-- Test partitionwise aggregation with ordered append path built from fractional paths +EXPLAIN (COSTS OFF) +SELECT count(*) FROM pagg_tab GROUP BY c ORDER BY c LIMIT 1; + QUERY PLAN +------------------------------------------------------------ + Limit + -> Merge Append + Sort Key: pagg_tab.c + -> GroupAggregate + Group Key: pagg_tab.c + -> Sort + Sort Key: pagg_tab.c + -> Seq Scan on pagg_tab_p1 pagg_tab + -> GroupAggregate + Group Key: pagg_tab_1.c + -> Sort + Sort Key: pagg_tab_1.c + -> Seq Scan on pagg_tab_p2 pagg_tab_1 + -> GroupAggregate + Group Key: pagg_tab_2.c + -> Sort + Sort Key: pagg_tab_2.c + -> Seq Scan on pagg_tab_p3 pagg_tab_2 +(18 rows) + +SELECT count(*) FROM pagg_tab GROUP BY c ORDER BY c LIMIT 1; + count +------- + 250 +(1 row) + RESET enable_hashagg; -- ROLLUP, partitionwise aggregation does not apply EXPLAIN (COSTS OFF) diff --git a/src/test/regress/sql/partition_aggregate.sql b/src/test/regress/sql/partition_aggregate.sql index 124cc260461..7c725e2663a 100644 --- a/src/test/regress/sql/partition_aggregate.sql +++ b/src/test/regress/sql/partition_aggregate.sql @@ -76,6 +76,11 @@ EXPLAIN (COSTS OFF) SELECT a FROM pagg_tab WHERE a < 3 GROUP BY a ORDER BY 1; SELECT a FROM pagg_tab WHERE a < 3 GROUP BY a ORDER BY 1; +-- Test partitionwise aggregation with ordered append path built from fractional paths +EXPLAIN (COSTS OFF) +SELECT count(*) FROM pagg_tab GROUP BY c ORDER BY c LIMIT 1; +SELECT count(*) FROM pagg_tab GROUP BY c ORDER BY c LIMIT 1; + RESET enable_hashagg; -- ROLLUP, partitionwise aggregation does not apply -- 2.39.5 (Apple Git-154)