From c10fa47d30783412edb5d26e6f9f77d994755ed9 Mon Sep 17 00:00:00 2001
From: Alexander Korotkov <akorotkov@postgresql.org>
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)

