From 51f4739a003b487699886a6fb22a26d7a90a7d95 Mon Sep 17 00:00:00 2001
From: Tender Wang <tndrwang@gmail.com>
Date: Mon, 17 Aug 2026 14:47:25 +0800
Subject: [PATCH] Fix disabled_nodes propagation for single-child Append paths

create_append_path() avoids calling cost_append() when an Append has a single child with the same parallel-awareness, since such an Append is effectively a no-op.
In that case, it copies the child's row estimate and costs directly, but failed to copy disabled_nodes.

This could cause a path containing a disabled plan node to be treated as having no disabled nodes.
For example, an ordered Append over a disabled IndexScan could then be preferred over an unordered, non-disabled path that would otherwise be sorted.

Propagate disabled_nodes from the child along with the other cost information in the single-child case.
---
 src/backend/optimizer/util/pathnode.c         |  1 +
 src/test/regress/expected/partition_prune.out | 20 +++++++++++++++++++
 src/test/regress/sql/partition_prune.sql      | 19 ++++++++++++++++++
 3 files changed, 40 insertions(+)

diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index a8dcad72958..298761491fa 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -1453,6 +1453,7 @@ create_append_path(PlannerInfo *root,
 		if (child->parallel_aware == parallel_aware)
 		{
 			pathnode->path.rows = child->rows;
+			pathnode->path.disabled_nodes = child->disabled_nodes;
 			pathnode->path.startup_cost = child->startup_cost;
 			pathnode->path.total_cost = child->total_cost;
 		}
diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
index aa821646011..23d0964401a 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -5002,3 +5002,23 @@ select * from (select a, b from phv_boolpart) t
 (2 rows)
 
 drop table phv_boolpart;
+-- Prefer a non-disabled path even when a disabled path provides ordering.
+CREATE TABLE append_disabled_test (a int, b int) PARTITION BY LIST (a);
+CREATE TABLE append_disabled_test_1
+  PARTITION OF append_disabled_test FOR VALUES IN (1);
+CREATE INDEX append_disabled_test_1_a_idx
+  ON append_disabled_test_1 (a);
+INSERT INTO append_disabled_test VALUES (1, 1), (1, 2), (1, 3);
+ANALYZE append_disabled_test;
+SET enable_indexscan = off;
+EXPLAIN (COSTS OFF)
+SELECT * FROM append_disabled_test ORDER BY a;
+                          QUERY PLAN                           
+---------------------------------------------------------------
+ Sort
+   Sort Key: append_disabled_test.a
+   ->  Seq Scan on append_disabled_test_1 append_disabled_test
+(3 rows)
+
+RESET enable_indexscan;
+DROP TABLE append_disabled_test;
diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql
index dac673ef80a..455972b837c 100644
--- a/src/test/regress/sql/partition_prune.sql
+++ b/src/test/regress/sql/partition_prune.sql
@@ -1538,3 +1538,22 @@ select * from (select a, b from phv_boolpart) t
   group by grouping sets (a, b);
 
 drop table phv_boolpart;
+
+-- Prefer a non-disabled path even when a disabled path provides ordering.
+CREATE TABLE append_disabled_test (a int, b int) PARTITION BY LIST (a);
+CREATE TABLE append_disabled_test_1
+  PARTITION OF append_disabled_test FOR VALUES IN (1);
+CREATE INDEX append_disabled_test_1_a_idx
+  ON append_disabled_test_1 (a);
+
+INSERT INTO append_disabled_test VALUES (1, 1), (1, 2), (1, 3);
+ANALYZE append_disabled_test;
+
+SET enable_indexscan = off;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM append_disabled_test ORDER BY a;
+
+RESET enable_indexscan;
+
+DROP TABLE append_disabled_test;
-- 
2.43.0

