diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index 5ff986a..8b12a24 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -78,7 +78,6 @@ struct ParallelAppendState
 };
 
 #define INVALID_SUBPLAN_INDEX		-1
-#define NO_MATCHING_SUBPLANS		-2
 
 static TupleTableSlot *ExecAppend(PlanState *pstate);
 static bool choose_next_subplan_locally(AppendState *node);
@@ -141,23 +140,6 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
 			validsubplans = ExecFindInitialMatchingSubPlans(prunestate,
 															list_length(node->appendplans));
 
-			/*
-			 * The case where no subplans survive pruning must be handled
-			 * specially.  The problem here is that code in explain.c requires
-			 * an Append to have at least one subplan in order for it to
-			 * properly determine the Vars in that subplan's targetlist.  We
-			 * sidestep this issue by just initializing the first subplan and
-			 * setting as_whichplan to NO_MATCHING_SUBPLANS to indicate that
-			 * we don't really need to scan any subnodes.
-			 */
-			if (bms_is_empty(validsubplans))
-			{
-				appendstate->as_whichplan = NO_MATCHING_SUBPLANS;
-
-				/* Mark the first as valid so that it's initialized below */
-				validsubplans = bms_make_singleton(0);
-			}
-
 			nplans = bms_num_members(validsubplans);
 		}
 		else
@@ -169,14 +151,12 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
 		}
 
 		/*
-		 * If no runtime pruning is required, we can fill as_valid_subplans
-		 * immediately, preventing later calls to ExecFindMatchingSubPlans.
+		 * When no run-time pruning is required and there's at least one
+		 * subplan, we can fill as_valid_subplans immediately, preventing
+		 * later calls to ExecFindMatchingSubPlans.
 		 */
-		if (!prunestate->do_exec_prune)
-		{
-			Assert(nplans > 0);
+		if (!prunestate->do_exec_prune && nplans > 0)
 			appendstate->as_valid_subplans = bms_add_range(NULL, 0, nplans - 1);
-		}
 	}
 	else
 	{
@@ -255,6 +235,10 @@ ExecAppend(PlanState *pstate)
 
 	if (node->as_whichplan < 0)
 	{
+		/* Nothing to do if there are no subplans */
+		if (node->as_nplans == 0)
+			return ExecClearTuple(node->ps.ps_ResultTupleSlot);
+
 		/*
 		 * If no subplan has been chosen, we must choose one before
 		 * proceeding.
@@ -262,10 +246,6 @@ ExecAppend(PlanState *pstate)
 		if (node->as_whichplan == INVALID_SUBPLAN_INDEX &&
 			!node->choose_next_subplan(node))
 			return ExecClearTuple(node->ps.ps_ResultTupleSlot);
-
-		/* Nothing to do if there are no matching subplans */
-		else if (node->as_whichplan == NO_MATCHING_SUBPLANS)
-			return ExecClearTuple(node->ps.ps_ResultTupleSlot);
 	}
 
 	for (;;)
@@ -460,7 +440,7 @@ choose_next_subplan_locally(AppendState *node)
 	int			nextplan;
 
 	/* We should never be called when there are no subplans */
-	Assert(whichplan != NO_MATCHING_SUBPLANS);
+	Assert(node->as_nplans > 0);
 
 	/*
 	 * If first call then have the bms member function choose the first valid
@@ -511,7 +491,7 @@ choose_next_subplan_for_leader(AppendState *node)
 	Assert(ScanDirectionIsForward(node->ps.state->es_direction));
 
 	/* We should never be called when there are no subplans */
-	Assert(node->as_whichplan != NO_MATCHING_SUBPLANS);
+	Assert(node->as_nplans > 0);
 
 	LWLockAcquire(&pstate->pa_lock, LW_EXCLUSIVE);
 
@@ -592,7 +572,7 @@ choose_next_subplan_for_worker(AppendState *node)
 	Assert(ScanDirectionIsForward(node->ps.state->es_direction));
 
 	/* We should never be called when there are no subplans */
-	Assert(node->as_whichplan != NO_MATCHING_SUBPLANS);
+	Assert(node->as_nplans > 0);
 
 	LWLockAcquire(&pstate->pa_lock, LW_EXCLUSIVE);
 
diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c
index 18d1337..e6896ef 100644
--- a/src/backend/executor/nodeMergeAppend.c
+++ b/src/backend/executor/nodeMergeAppend.c
@@ -80,7 +80,6 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
 	mergestate->ps.plan = (Plan *) node;
 	mergestate->ps.state = estate;
 	mergestate->ps.ExecProcNode = ExecMergeAppend;
-	mergestate->ms_noopscan = false;
 
 	/* If run-time partition pruning is enabled, then set that up now */
 	if (node->part_prune_info != NULL)
@@ -101,23 +100,6 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
 			validsubplans = ExecFindInitialMatchingSubPlans(prunestate,
 															list_length(node->mergeplans));
 
-			/*
-			 * The case where no subplans survive pruning must be handled
-			 * specially.  The problem here is that code in explain.c requires
-			 * a MergeAppend to have at least one subplan in order for it to
-			 * properly determine the Vars in that subplan's targetlist.  We
-			 * sidestep this issue by just initializing the first subplan and
-			 * setting ms_noopscan to true to indicate that we don't really
-			 * need to scan any subnodes.
-			 */
-			if (bms_is_empty(validsubplans))
-			{
-				mergestate->ms_noopscan = true;
-
-				/* Mark the first as valid so that it's initialized below */
-				validsubplans = bms_make_singleton(0);
-			}
-
 			nplans = bms_num_members(validsubplans);
 		}
 		else
@@ -129,14 +111,12 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
 		}
 
 		/*
-		 * If no runtime pruning is required, we can fill ms_valid_subplans
-		 * immediately, preventing later calls to ExecFindMatchingSubPlans.
+		 * When no run-time pruning is required and there's at least one
+		 * subplan, we can fill as_valid_subplans immediately, preventing
+		 * later calls to ExecFindMatchingSubPlans.
 		 */
-		if (!prunestate->do_exec_prune)
-		{
-			Assert(nplans > 0);
+		if (!prunestate->do_exec_prune && nplans > 0)
 			mergestate->ms_valid_subplans = bms_add_range(NULL, 0, nplans - 1);
-		}
 	}
 	else
 	{
@@ -240,7 +220,7 @@ ExecMergeAppend(PlanState *pstate)
 	if (!node->ms_initialized)
 	{
 		/* Nothing to do if all subplans were pruned */
-		if (node->ms_noopscan)
+		if (node->ms_nplans == 0)
 			return ExecClearTuple(node->ps.ps_ResultTupleSlot);
 
 		/*
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 9d1fc18..0290a8a 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1234,8 +1234,6 @@ struct AppendState
  *		slots			current output tuple of each subplan
  *		heap			heap of active tuples
  *		initialized		true if we have fetched first tuple from each subplan
- *		noopscan		true if partition pruning proved that none of the
- *						mergeplans can contain a record to satisfy this query.
  *		prune_state		details required to allow partitions to be
  *						eliminated from the scan, or NULL if not possible.
  *		valid_subplans	for runtime pruning, valid mergeplans indexes to
@@ -1252,7 +1250,6 @@ typedef struct MergeAppendState
 	TupleTableSlot **ms_slots;	/* array of length ms_nplans */
 	struct binaryheap *ms_heap; /* binary heap of slot indices */
 	bool		ms_initialized; /* are subplans started? */
-	bool		ms_noopscan;
 	struct PartitionPruneState *ms_prune_state;
 	Bitmapset  *ms_valid_subplans;
 } MergeAppendState;
diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
index 57680f1..6dc50e0 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -2005,20 +2005,17 @@ select explain_parallel_append('execute ab_q5 (2, 3, 3)');
 (19 rows)
 
 -- Try some params whose values do not belong to any partition.
--- We'll still get a single subplan in this case, but it should not be scanned.
 select explain_parallel_append('execute ab_q5 (33, 44, 55)');
-                            explain_parallel_append                            
--------------------------------------------------------------------------------
+                  explain_parallel_append                  
+-----------------------------------------------------------
  Finalize Aggregate (actual rows=N loops=N)
    ->  Gather (actual rows=N loops=N)
          Workers Planned: 2
          Workers Launched: N
          ->  Partial Aggregate (actual rows=N loops=N)
                ->  Parallel Append (actual rows=N loops=N)
-                     Subplans Removed: 8
-                     ->  Parallel Seq Scan on ab_a1_b1 ab_1 (never executed)
-                           Filter: ((b < 4) AND (a = ANY (ARRAY[$1, $2, $3])))
-(9 rows)
+                     Subplans Removed: 9
+(7 rows)
 
 -- Test Parallel Append with PARAM_EXEC Params
 select explain_parallel_append('select count(*) from ab where (a = (select 1) or a = (select 3)) and b = 2');
@@ -2854,16 +2851,13 @@ explain (analyze, costs off, summary off, timing off)  execute q1 (2,2);
          Filter: (b = ANY (ARRAY[$1, $2]))
 (4 rows)
 
--- Try with no matching partitions. One subplan should remain in this case,
--- but it shouldn't be executed.
+-- Try with no matching partitions.
 explain (analyze, costs off, summary off, timing off)  execute q1 (0,0);
-                      QUERY PLAN                      
-------------------------------------------------------
+           QUERY PLAN           
+--------------------------------
  Append (actual rows=0 loops=1)
-   Subplans Removed: 1
-   ->  Seq Scan on listp_1_1 listp_1 (never executed)
-         Filter: (b = ANY (ARRAY[$1, $2]))
-(4 rows)
+   Subplans Removed: 2
+(2 rows)
 
 deallocate q1;
 -- Test more complex cases where a not-equal condition further eliminates partitions.
@@ -2879,15 +2873,12 @@ explain (analyze, costs off, summary off, timing off)  execute q1 (1,2,2,0);
 (4 rows)
 
 -- Both partitions allowed by IN clause, then both excluded again by <> clauses.
--- One subplan will remain in this case, but it should not be executed.
 explain (analyze, costs off, summary off, timing off)  execute q1 (1,2,2,1);
-                               QUERY PLAN                                
--------------------------------------------------------------------------
+           QUERY PLAN           
+--------------------------------
  Append (actual rows=0 loops=1)
-   Subplans Removed: 1
-   ->  Seq Scan on listp_1_1 listp_1 (never executed)
-         Filter: ((b = ANY (ARRAY[$1, $2])) AND ($3 <> b) AND ($4 <> b))
-(4 rows)
+   Subplans Removed: 2
+(2 rows)
 
 -- Ensure Params that evaluate to NULL properly prune away all partitions
 explain (analyze, costs off, summary off, timing off)
@@ -2971,13 +2962,11 @@ select * from stable_qual_pruning
 explain (analyze, costs off, summary off, timing off)
 select * from stable_qual_pruning
   where a = any(array['2010-02-01', '2020-01-01']::timestamptz[]);
-                                                        QUERY PLAN                                                         
----------------------------------------------------------------------------------------------------------------------------
+           QUERY PLAN           
+--------------------------------
  Append (actual rows=0 loops=1)
-   Subplans Removed: 2
-   ->  Seq Scan on stable_qual_pruning1 stable_qual_pruning_1 (never executed)
-         Filter: (a = ANY ('{"Mon Feb 01 00:00:00 2010 PST","Wed Jan 01 00:00:00 2020 PST"}'::timestamp with time zone[]))
-(4 rows)
+   Subplans Removed: 3
+(2 rows)
 
 explain (analyze, costs off, summary off, timing off)
 select * from stable_qual_pruning
@@ -3159,14 +3148,12 @@ execute mt_q1(25);
 
 -- Ensure MergeAppend behaves correctly when no subplans match
 explain (analyze, costs off, summary off, timing off) execute mt_q1(35);
-                                    QUERY PLAN                                    
-----------------------------------------------------------------------------------
+              QUERY PLAN              
+--------------------------------------
  Merge Append (actual rows=0 loops=1)
    Sort Key: ma_test.b
-   Subplans Removed: 2
-   ->  Index Scan using ma_test_p1_b_idx on ma_test_p1 ma_test_1 (never executed)
-         Filter: ((a >= $1) AND ((a % 10) = 5))
-(5 rows)
+   Subplans Removed: 3
+(3 rows)
 
 execute mt_q1(35);
  a 
@@ -3174,6 +3161,21 @@ execute mt_q1(35);
 (0 rows)
 
 deallocate mt_q1;
+set plan_cache_mode = force_generic_plan;
+prepare mt_q2 (int) as select * from ma_test where a >= $1 order by b limit 1;
+-- Ensure output list looks sane when the MergeAppend has no subplans.
+explain (analyze, verbose, costs off, summary off, timing off) execute mt_q2 (35);
+                 QUERY PLAN                 
+--------------------------------------------
+ Limit (actual rows=0 loops=1)
+   Output: ma_test.a, ma_test.b
+   ->  Merge Append (actual rows=0 loops=1)
+         Sort Key: ma_test.b
+         Subplans Removed: 3
+(5 rows)
+
+deallocate mt_q2;
+reset plan_cache_mode;
 -- ensure initplan params properly prune partitions
 explain (analyze, costs off, summary off, timing off) select * from ma_test where a >= (select min(b) from ma_test_p2) order by b;
                                           QUERY PLAN                                           
@@ -3591,19 +3593,18 @@ from (
      ) s(a, b, c)
 where s.a = $1 and s.b = $2 and s.c = (select 1);
 explain (costs off) execute q (1, 1);
-                          QUERY PLAN                           
----------------------------------------------------------------
+                     QUERY PLAN                     
+----------------------------------------------------
  Append
    InitPlan 1 (returns $0)
      ->  Result
-   Subplans Removed: 1
    ->  Seq Scan on p1 p
-         Filter: ((a = $1) AND (b = $2) AND (c = $0))
+         Filter: ((a = 1) AND (b = 1) AND (c = $0))
    ->  Seq Scan on q111 q1
-         Filter: ((a = $1) AND (b = $2) AND (c = $0))
+         Filter: ((a = 1) AND (b = 1) AND (c = $0))
    ->  Result
-         One-Time Filter: ((1 = $1) AND (1 = $2) AND (1 = $0))
-(10 rows)
+         One-Time Filter: (1 = $0)
+(9 rows)
 
 execute q (1, 1);
  a | b | c 
diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql
index 41f0b6f..e00984e 100644
--- a/src/test/regress/sql/partition_prune.sql
+++ b/src/test/regress/sql/partition_prune.sql
@@ -477,7 +477,6 @@ select explain_parallel_append('execute ab_q5 (1, 1, 1)');
 select explain_parallel_append('execute ab_q5 (2, 3, 3)');
 
 -- Try some params whose values do not belong to any partition.
--- We'll still get a single subplan in this case, but it should not be scanned.
 select explain_parallel_append('execute ab_q5 (33, 44, 55)');
 
 -- Test Parallel Append with PARAM_EXEC Params
@@ -702,8 +701,7 @@ explain (analyze, costs off, summary off, timing off)  execute q1 (1,1);
 
 explain (analyze, costs off, summary off, timing off)  execute q1 (2,2);
 
--- Try with no matching partitions. One subplan should remain in this case,
--- but it shouldn't be executed.
+-- Try with no matching partitions.
 explain (analyze, costs off, summary off, timing off)  execute q1 (0,0);
 
 deallocate q1;
@@ -715,7 +713,6 @@ prepare q1 (int,int,int,int) as select * from listp where b in($1,$2) and $3 <>
 explain (analyze, costs off, summary off, timing off)  execute q1 (1,2,2,0);
 
 -- Both partitions allowed by IN clause, then both excluded again by <> clauses.
--- One subplan will remain in this case, but it should not be executed.
 explain (analyze, costs off, summary off, timing off)  execute q1 (1,2,2,1);
 
 -- Ensure Params that evaluate to NULL properly prune away all partitions
@@ -841,6 +838,16 @@ execute mt_q1(35);
 
 deallocate mt_q1;
 
+set plan_cache_mode = force_generic_plan;
+
+prepare mt_q2 (int) as select * from ma_test where a >= $1 order by b limit 1;
+
+-- Ensure output list looks sane when the MergeAppend has no subplans.
+explain (analyze, verbose, costs off, summary off, timing off) execute mt_q2 (35);
+
+deallocate mt_q2;
+reset plan_cache_mode;
+
 -- ensure initplan params properly prune partitions
 explain (analyze, costs off, summary off, timing off) select * from ma_test where a >= (select min(b) from ma_test_p2) order by b;
 
