diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 2175dff824..77f7366cf8 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -1939,7 +1939,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate,
 	if (plan && plan->operation == CMD_UPDATE &&
 		(resultRelInfo->ri_usesFdwDirectModify ||
 		 resultRelInfo->ri_FdwState) &&
-		resultRelInfo > mtstate->resultRelInfo + mtstate->mt_whichplan)
+		resultRelInfo > mtstate->resultRelInfos[mtstate->mt_whichplan])
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot route tuples into foreign table to be updated \"%s\"",
@@ -1993,7 +1993,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate,
 		 */
 		if (plan && plan->operation == CMD_UPDATE &&
 			resultRelation == plan->rootRelation)
-			resultRelation = mtstate->resultRelInfo[0].ri_RangeTableIndex;
+			resultRelation = mtstate->resultRelInfos[0]->ri_RangeTableIndex;
 	}
 
 	/* Construct the SQL command string. */
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index e79ede4cb8..f97d62efc8 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2854,7 +2854,7 @@ CopyFrom(CopyState cstate)
 	mtstate->ps.plan = NULL;
 	mtstate->ps.state = estate;
 	mtstate->operation = CMD_INSERT;
-	mtstate->resultRelInfo = estate->es_result_relations;
+	mtstate->resultRelInfos = &estate->es_result_relations;
 
 	if (resultRelInfo->ri_FdwRoutine != NULL &&
 		resultRelInfo->ri_FdwRoutine->BeginForeignInsert != NULL)
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index d901dc4a50..18b0697591 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -1988,6 +1988,11 @@ ExplainNode(PlanState *planstate, List *ancestors,
 								  list_length(((MergeAppend *) plan)->mergeplans),
 								  es);
 			break;
+		case T_ModifyTable:
+			ExplainMissingMembers(((ModifyTableState *) planstate)->mt_nplans,
+								  list_length(((ModifyTable *) plan)->plans),
+								  es);
+			break;
 		default:
 			break;
 	}
@@ -3244,14 +3249,14 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
 	/* Should we explicitly label target relations? */
 	labeltargets = (mtstate->mt_nplans > 1 ||
 					(mtstate->mt_nplans == 1 &&
-					 mtstate->resultRelInfo->ri_RangeTableIndex != node->nominalRelation));
+					 mtstate->resultRelInfos[0]->ri_RangeTableIndex != node->nominalRelation));
 
 	if (labeltargets)
 		ExplainOpenGroup("Target Tables", "Target Tables", false, es);
 
 	for (j = 0; j < mtstate->mt_nplans; j++)
 	{
-		ResultRelInfo *resultRelInfo = mtstate->resultRelInfo + j;
+		ResultRelInfo *resultRelInfo = mtstate->resultRelInfos[j];
 		FdwRoutine *fdwroutine = resultRelInfo->ri_FdwRoutine;
 
 		if (labeltargets)
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index c13b1d3501..f84110a54c 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -470,7 +470,7 @@ ExecHashSubPlanResultRelsByOid(ModifyTableState *mtstate,
 	/* Hash all subplans by their Oid */
 	for (i = 0; i < mtstate->mt_nplans; i++)
 	{
-		ResultRelInfo *rri = &mtstate->resultRelInfo[i];
+		ResultRelInfo *rri = mtstate->resultRelInfos[i];
 		bool		found;
 		Oid			partoid = RelationGetRelid(rri->ri_RelationDesc);
 		SubplanResultRelHashElem *elem;
@@ -507,7 +507,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
 	ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
 	Relation	rootrel = rootResultRelInfo->ri_RelationDesc,
 				partrel;
-	Relation	firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
+	Relation	firstResultRel = mtstate->resultRelInfos[0]->ri_RelationDesc;
 	ResultRelInfo *leaf_part_rri;
 	MemoryContext oldcxt;
 	AttrMap    *part_attmap = NULL;
@@ -555,7 +555,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
 		List	   *wcoList;
 		List	   *wcoExprs = NIL;
 		ListCell   *ll;
-		int			firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
+		int			firstVarno = mtstate->resultRelInfos[0]->ri_RangeTableIndex;
 
 		/*
 		 * In the case of INSERT on a partitioned table, there is only one
@@ -619,7 +619,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
 		TupleTableSlot *slot;
 		ExprContext *econtext;
 		List	   *returningList;
-		int			firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
+		int			firstVarno = mtstate->resultRelInfos[0]->ri_RangeTableIndex;
 
 		/* See the comment above for WCO lists. */
 		Assert((node->operation == CMD_INSERT &&
@@ -678,7 +678,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
 	 */
 	if (node && node->onConflictAction != ONCONFLICT_NONE)
 	{
-		int			firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
+		int			firstVarno = mtstate->resultRelInfos[0]->ri_RangeTableIndex;
 		TupleDesc	partrelDesc = RelationGetDescr(partrel);
 		ExprContext *econtext = mtstate->ps.ps_ExprContext;
 		ListCell   *lc;
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index d71c0a4322..0d987e9651 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -57,6 +57,8 @@
 #include "utils/memutils.h"
 #include "utils/rel.h"
 
+ /* Special value for mt_whichplan */
+#define WHICHPLAN_CHOOSE_SUBPLAN			-1
 
 static bool ExecOnConflictUpdate(ModifyTableState *mtstate,
 								 ResultRelInfo *resultRelInfo,
@@ -1198,7 +1200,6 @@ lreplace:;
 			TupleTableSlot *ret_slot;
 			TupleTableSlot *epqslot = NULL;
 			PartitionTupleRouting *proute = mtstate->mt_partition_tuple_routing;
-			int			map_index;
 			TupleConversionMap *tupconv_map;
 
 			/*
@@ -1277,17 +1278,8 @@ lreplace:;
 			if (mtstate->mt_transition_capture)
 				saved_tcs_map = mtstate->mt_transition_capture->tcs_map;
 
-			/*
-			 * resultRelInfo is one of the per-subplan resultRelInfos.  So we
-			 * should convert the tuple into root's tuple descriptor, since
-			 * ExecInsert() starts the search from root.  The tuple conversion
-			 * map list is in the order of mtstate->resultRelInfo[], so to
-			 * retrieve the one for this resultRel, we need to know the
-			 * position of the resultRel in mtstate->resultRelInfo[].
-			 */
-			map_index = resultRelInfo - mtstate->resultRelInfo;
-			Assert(map_index >= 0 && map_index < mtstate->mt_nplans);
-			tupconv_map = tupconv_map_for_subplan(mtstate, map_index);
+			/* Fetch the tuple conversion map for the current subplan */
+			tupconv_map = tupconv_map_for_subplan(mtstate, mtstate->mt_whichplan);
 			if (tupconv_map != NULL)
 				slot = execute_attr_map_slot(tupconv_map->attrMap,
 											 slot,
@@ -1731,12 +1723,12 @@ static void
 fireBSTriggers(ModifyTableState *node)
 {
 	ModifyTable *plan = (ModifyTable *) node->ps.plan;
-	ResultRelInfo *resultRelInfo = node->resultRelInfo;
+	ResultRelInfo *resultRelInfo = node->resultRelInfos[0];
 
 	/*
 	 * If the node modifies a partitioned table, we must fire its triggers.
-	 * Note that in that case, node->resultRelInfo points to the first leaf
-	 * partition, not the root table.
+	 * Note that in that case, node->resultRelInfos[0] points to the first
+	 * leaf partition, not the root table.
 	 */
 	if (node->rootResultRelInfo != NULL)
 		resultRelInfo = node->rootResultRelInfo;
@@ -1774,13 +1766,14 @@ static ResultRelInfo *
 getTargetResultRelInfo(ModifyTableState *node)
 {
 	/*
-	 * Note that if the node modifies a partitioned table, node->resultRelInfo
-	 * points to the first leaf partition, not the root table.
+	 * Note that if the node modifies a partitioned table,
+	 * node->resultRelInfos[0] points to the first leaf partition, not the
+	 * root table.
 	 */
 	if (node->rootResultRelInfo != NULL)
 		return node->rootResultRelInfo;
 	else
-		return node->resultRelInfo;
+		return node->resultRelInfos[0];
 }
 
 /*
@@ -1959,7 +1952,7 @@ static void
 ExecSetupChildParentMapForSubplan(ModifyTableState *mtstate)
 {
 	ResultRelInfo *targetRelInfo = getTargetResultRelInfo(mtstate);
-	ResultRelInfo *resultRelInfos = mtstate->resultRelInfo;
+	ResultRelInfo **resultRelInfos = mtstate->resultRelInfos;
 	TupleDesc	outdesc;
 	int			numResultRelInfos = mtstate->mt_nplans;
 	int			i;
@@ -1979,7 +1972,7 @@ ExecSetupChildParentMapForSubplan(ModifyTableState *mtstate)
 	for (i = 0; i < numResultRelInfos; ++i)
 	{
 		mtstate->mt_per_subplan_tupconv_maps[i] =
-			convert_tuples_by_name(RelationGetDescr(resultRelInfos[i].ri_RelationDesc),
+			convert_tuples_by_name(RelationGetDescr(resultRelInfos[i]->ri_RelationDesc),
 								   outdesc);
 	}
 }
@@ -2055,8 +2048,33 @@ ExecModifyTable(PlanState *pstate)
 		node->fireBSTriggers = false;
 	}
 
+
+	/* Handle choosing the valid subplans */
+	if (node->mt_whichplan == WHICHPLAN_CHOOSE_SUBPLAN)
+	{
+		PartitionPruneState *prunestate = node->mt_prune_state;
+
+		/* Bail if all partitions were run-time pruned */
+		if (node->mt_nplans == 0)
+			goto done;
+
+		/*
+		 * mt_whichplan should never get set to WHICHPLAN_CHOOSE_SUBPLAN
+		 * unless we're performing do_exec_prune
+		 */
+		Assert(prunestate != NULL && prunestate->do_exec_prune);
+
+		/* Determine the minimum set of matching subplans */
+		node->mt_valid_subplans = ExecFindMatchingSubPlans(prunestate);
+		node->mt_whichplan = bms_next_member(node->mt_valid_subplans, -1);
+
+		/* If no subplan matches these params then we're done */
+		if (node->mt_whichplan < 0)
+			goto done;
+	}
+
 	/* Preload local variables */
-	resultRelInfo = node->resultRelInfo + node->mt_whichplan;
+	resultRelInfo = node->resultRelInfos[node->mt_whichplan];
 	subplanstate = node->mt_plans[node->mt_whichplan];
 	junkfilter = resultRelInfo->ri_junkFilter;
 
@@ -2097,11 +2115,13 @@ ExecModifyTable(PlanState *pstate)
 
 		if (TupIsNull(planSlot))
 		{
-			/* advance to next subplan if any */
-			node->mt_whichplan++;
-			if (node->mt_whichplan < node->mt_nplans)
+			/* advance to the next valid subplan if any */
+			node->mt_whichplan = bms_next_member(node->mt_valid_subplans,
+												 node->mt_whichplan);
+
+			if (node->mt_whichplan >= 0)
 			{
-				resultRelInfo++;
+				resultRelInfo = node->resultRelInfos[node->mt_whichplan];
 				subplanstate = node->mt_plans[node->mt_whichplan];
 				junkfilter = resultRelInfo->ri_junkFilter;
 				estate->es_result_relation_info = resultRelInfo;
@@ -2271,6 +2291,8 @@ ExecModifyTable(PlanState *pstate)
 	/* Restore es_result_relation_info before exiting */
 	estate->es_result_relation_info = saved_resultRelInfo;
 
+done:
+
 	/*
 	 * We're done, but fire AFTER STATEMENT triggers before exiting.
 	 */
@@ -2293,9 +2315,11 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	int			nplans = list_length(node->plans);
 	ResultRelInfo *saved_resultRelInfo;
 	ResultRelInfo *resultRelInfo;
+	Bitmapset  *validsubplans;
 	Plan	   *subplan;
 	ListCell   *l;
-	int			i;
+	int			i,
+				j;
 	Relation	rel;
 	bool		update_tuple_routing_needed = node->partColsUpdated;
 
@@ -2314,8 +2338,74 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	mtstate->canSetTag = node->canSetTag;
 	mtstate->mt_done = false;
 
+	/* If run-time partition pruning is enabled, then set that up now */
+	if (node->part_prune_info != NULL)
+	{
+		PartitionPruneState *prunestate;
+
+		ExecAssignExprContext(estate, &mtstate->ps);
+
+		prunestate = ExecCreatePartitionPruneState(&mtstate->ps,
+												   node->part_prune_info);
+		mtstate->mt_prune_state = prunestate;
+
+		/* Perform an initial partition prune, if required. */
+		if (prunestate->do_initial_prune)
+		{
+			/* Determine which subplans match the external params */
+			validsubplans = ExecFindInitialMatchingSubPlans(prunestate,
+													list_length(node->plans));
+
+			nplans = bms_num_members(validsubplans);
+		}
+		else
+		{
+			/* We'll need to initialize all subplans */
+			nplans = list_length(node->plans);
+			validsubplans = bms_add_range(NULL, 0, nplans - 1);
+		}
+
+		/*
+		 * When no run-time pruning is required and there's at least one
+		 * subplan, we can fill mt_valid_subplans immediately, preventing
+		 * later calls to ExecFindMatchingSubPlans.  Additionally, we can also
+		 * select the first subplan to execute.
+		 */
+		if (!prunestate->do_exec_prune && nplans > 0)
+		{
+			mtstate->mt_valid_subplans = bms_add_range(NULL, 0, nplans - 1);
+			mtstate->mt_whichplan = 0;
+		}
+		else
+		{
+			/*
+			 * When performing do_exec_prune or we pruned all subplans above
+			 * then we set mt_whichplan to WHICHPLAN_CHOOSE_SUBPLAN so that
+			 * we properly handle those cases during execution.
+			 */
+			mtstate->mt_valid_subplans = NULL;
+			mtstate->mt_whichplan = WHICHPLAN_CHOOSE_SUBPLAN;
+		}
+	}
+	else
+	{
+		nplans = list_length(node->plans);
+
+		/*
+		 * When run-time partition pruning is not enabled we can just mark all
+		 * plans as valid, they must also all be initialized.
+		 */
+		validsubplans = bms_add_range(NULL, 0, nplans - 1);
+		mtstate->mt_valid_subplans = validsubplans;
+		mtstate->mt_prune_state = NULL;
+
+		/* Also set the plan to execute first */
+		mtstate->mt_whichplan = 0;
+	}
+
 	mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * nplans);
-	mtstate->resultRelInfo = estate->es_result_relations + node->resultRelIndex;
+	mtstate->resultRelInfos = (ResultRelInfo **)
+		palloc(sizeof(ResultRelInfo *) * nplans);
 	mtstate->mt_scans = (TupleTableSlot **) palloc0(sizeof(TupleTableSlot *) * nplans);
 
 	/* If modifying a partitioned table, initialize the root table info */
@@ -2342,11 +2432,14 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	 */
 	saved_resultRelInfo = estate->es_result_relation_info;
 
-	resultRelInfo = mtstate->resultRelInfo;
-	i = 0;
-	foreach(l, node->plans)
+	j = 0;
+	i = -1;
+	while ((i = bms_next_member(validsubplans, i)) >= 0)
 	{
-		subplan = (Plan *) lfirst(l);
+		subplan = (Plan *) list_nth(node->plans, i);
+
+		resultRelInfo = &estate->es_result_relations[node->resultRelIndex + i];
+		mtstate->resultRelInfos[j] = resultRelInfo;
 
 		/* Initialize the usesFdwDirectModify flag */
 		resultRelInfo->ri_usesFdwDirectModify = bms_is_member(i,
@@ -2384,9 +2477,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 
 		/* Now init the plan for this result rel */
 		estate->es_result_relation_info = resultRelInfo;
-		mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags);
-		mtstate->mt_scans[i] =
-			ExecInitExtraTupleSlot(mtstate->ps.state, ExecGetResultType(mtstate->mt_plans[i]),
+		mtstate->mt_plans[j] = ExecInitNode(subplan, estate, eflags);
+		mtstate->mt_scans[j] =
+			ExecInitExtraTupleSlot(mtstate->ps.state, ExecGetResultType(mtstate->mt_plans[j]),
 								   table_slot_callbacks(resultRelInfo->ri_RelationDesc));
 
 		/* Also let FDWs init themselves for foreign-table result rels */
@@ -2402,9 +2495,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 															 i,
 															 eflags);
 		}
-
-		resultRelInfo++;
-		i++;
+		j++;
 	}
 
 	estate->es_result_relation_info = saved_resultRelInfo;
@@ -2453,33 +2544,35 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	/*
 	 * Initialize any WITH CHECK OPTION constraints if needed.
 	 */
-	resultRelInfo = mtstate->resultRelInfo;
-	i = 0;
-	foreach(l, node->withCheckOptionLists)
+	if (node->withCheckOptionLists != NIL)
 	{
-		List	   *wcoList = (List *) lfirst(l);
-		List	   *wcoExprs = NIL;
-		ListCell   *ll;
-
-		foreach(ll, wcoList)
+		j = 0;
+		i = -1;
+		while ((i = bms_next_member(validsubplans, i)) >= 0)
 		{
-			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitQual((List *) wco->qual,
-											   &mtstate->ps);
+			List	   *wcoList = (List *) list_nth(node->withCheckOptionLists, i);;
+			List	   *wcoExprs = NIL;
 
-			wcoExprs = lappend(wcoExprs, wcoExpr);
-		}
+			foreach(l, wcoList)
+			{
+				WithCheckOption *wco = (WithCheckOption *) lfirst(l);
+				ExprState  *wcoExpr = ExecInitQual((List *) wco->qual,
+												   &mtstate->ps);
+
+				wcoExprs = lappend(wcoExprs, wcoExpr);
+			}
 
-		resultRelInfo->ri_WithCheckOptions = wcoList;
-		resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
-		resultRelInfo++;
-		i++;
+			resultRelInfo = mtstate->resultRelInfos[j];
+			resultRelInfo->ri_WithCheckOptions = wcoList;
+			resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
+			j++;
+		}
 	}
 
 	/*
 	 * Initialize RETURNING projections if needed.
 	 */
-	if (node->returningLists)
+	if (node->returningLists != NIL)
 	{
 		TupleTableSlot *slot;
 		ExprContext *econtext;
@@ -2502,16 +2595,18 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		/*
 		 * Build a projection for each result rel.
 		 */
-		resultRelInfo = mtstate->resultRelInfo;
-		foreach(l, node->returningLists)
+		j = 0;
+		i = -1;
+		while ((i = bms_next_member(validsubplans, i)) >= 0)
 		{
-			List	   *rlist = (List *) lfirst(l);
+			List	   *rlist = (List *) list_nth(node->returningLists, i);
 
+			resultRelInfo = mtstate->resultRelInfos[j];
 			resultRelInfo->ri_returningList = rlist;
 			resultRelInfo->ri_projectReturning =
 				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
 										resultRelInfo->ri_RelationDesc->rd_att);
-			resultRelInfo++;
+			j++;
 		}
 	}
 	else
@@ -2522,12 +2617,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		 */
 		mtstate->ps.plan->targetlist = NIL;
 		ExecInitResultTypeTL(&mtstate->ps);
-
-		mtstate->ps.ps_ExprContext = NULL;
 	}
 
 	/* Set the list of arbiter indexes if needed for ON CONFLICT */
-	resultRelInfo = mtstate->resultRelInfo;
+	resultRelInfo = mtstate->resultRelInfos[0];
 	if (node->onConflictAction != ONCONFLICT_NONE)
 		resultRelInfo->ri_onConflictArbiterIndexes = node->arbiterIndexes;
 
@@ -2618,7 +2711,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	}
 
 	/* select first subplan */
-	mtstate->mt_whichplan = 0;
 	subplan = (Plan *) linitial(node->plans);
 	EvalPlanQualSetPlan(&mtstate->mt_epqstate, subplan,
 						mtstate->mt_arowmarks[0]);
@@ -2667,12 +2759,12 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 
 		if (junk_filter_needed)
 		{
-			resultRelInfo = mtstate->resultRelInfo;
 			for (i = 0; i < nplans; i++)
 			{
 				JunkFilter *j;
 				TupleTableSlot *junkresslot;
 
+				resultRelInfo = mtstate->resultRelInfos[i];
 				subplan = mtstate->mt_plans[i]->plan;
 				if (operation == CMD_INSERT || operation == CMD_UPDATE)
 					ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc,
@@ -2715,13 +2807,12 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 				}
 
 				resultRelInfo->ri_junkFilter = j;
-				resultRelInfo++;
 			}
 		}
 		else
 		{
 			if (operation == CMD_INSERT)
-				ExecCheckPlanOutput(mtstate->resultRelInfo->ri_RelationDesc,
+				ExecCheckPlanOutput(mtstate->resultRelInfos[0]->ri_RelationDesc,
 									subplan->targetlist);
 		}
 	}
@@ -2760,7 +2851,7 @@ ExecEndModifyTable(ModifyTableState *node)
 	 */
 	for (i = 0; i < node->mt_nplans; i++)
 	{
-		ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
+		ResultRelInfo *resultRelInfo = node->resultRelInfos[i];
 
 		if (!resultRelInfo->ri_usesFdwDirectModify &&
 			resultRelInfo->ri_FdwRoutine != NULL &&
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index eaab97f753..8270bf53eb 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -216,6 +216,7 @@ _copyModifyTable(const ModifyTable *from)
 	COPY_BITMAPSET_FIELD(fdwDirectModifyPlans);
 	COPY_NODE_FIELD(rowMarks);
 	COPY_SCALAR_FIELD(epqParam);
+	COPY_NODE_FIELD(part_prune_info);
 	COPY_SCALAR_FIELD(onConflictAction);
 	COPY_NODE_FIELD(arbiterIndexes);
 	COPY_NODE_FIELD(onConflictSet);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e084c3f069..0e9da7f086 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -417,6 +417,7 @@ _outModifyTable(StringInfo str, const ModifyTable *node)
 	WRITE_BITMAPSET_FIELD(fdwDirectModifyPlans);
 	WRITE_NODE_FIELD(rowMarks);
 	WRITE_INT_FIELD(epqParam);
+	WRITE_NODE_FIELD(part_prune_info);
 	WRITE_ENUM_FIELD(onConflictAction, OnConflictAction);
 	WRITE_NODE_FIELD(arbiterIndexes);
 	WRITE_NODE_FIELD(onConflictSet);
@@ -2095,6 +2096,7 @@ _outModifyTablePath(StringInfo str, const ModifyTablePath *node)
 	WRITE_UINT_FIELD(rootRelation);
 	WRITE_BOOL_FIELD(partColsUpdated);
 	WRITE_NODE_FIELD(resultRelations);
+	WRITE_NODE_FIELD(partitioned_rels);
 	WRITE_NODE_FIELD(subpaths);
 	WRITE_NODE_FIELD(subroots);
 	WRITE_NODE_FIELD(withCheckOptionLists);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index d5b23a3479..03ecf56fef 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1647,6 +1647,7 @@ _readModifyTable(void)
 	READ_BITMAPSET_FIELD(fdwDirectModifyPlans);
 	READ_NODE_FIELD(rowMarks);
 	READ_INT_FIELD(epqParam);
+	READ_NODE_FIELD(part_prune_info);
 	READ_ENUM_FIELD(onConflictAction, OnConflictAction);
 	READ_NODE_FIELD(arbiterIndexes);
 	READ_NODE_FIELD(onConflictSet);
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index fc25908dc6..c1096023c6 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -289,7 +289,8 @@ static ModifyTable *make_modifytable(PlannerInfo *root,
 									 bool partColsUpdated,
 									 List *resultRelations, List *subplans, List *subroots,
 									 List *withCheckOptionLists, List *returningLists,
-									 List *rowMarks, OnConflictExpr *onconflict, int epqParam);
+									 List *rowMarks, OnConflictExpr *onconflict, int epqParam,
+									 PartitionPruneInfo *partpruneinfos);
 static GatherMerge *create_gather_merge_plan(PlannerInfo *root,
 											 GatherMergePath *best_path);
 
@@ -1236,6 +1237,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 		if (prunequal != NIL)
 			partpruneinfo =
 				make_partition_pruneinfo(root, rel,
+										 NIL,
 										 best_path->subpaths,
 										 best_path->partitioned_rels,
 										 prunequal);
@@ -1402,6 +1404,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
 
 		if (prunequal != NIL)
 			partpruneinfo = make_partition_pruneinfo(root, rel,
+													 NIL,
 													 best_path->subpaths,
 													 best_path->partitioned_rels,
 													 prunequal);
@@ -2587,6 +2590,7 @@ static ModifyTable *
 create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path)
 {
 	ModifyTable *plan;
+	PartitionPruneInfo *partpruneinfos = NULL;
 	List	   *subplans = NIL;
 	ListCell   *subpaths,
 			   *subroots;
@@ -2618,6 +2622,30 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path)
 		subplans = lappend(subplans, subplan);
 	}
 
+	if (enable_partition_pruning &&
+		best_path->partitioned_rels != NIL &&
+		!IS_DUMMY_MODIFYTABLE(best_path))
+	{
+		RelOptInfo *rel = best_path->path.parent;
+		List	   *prunequal = NIL;
+
+		prunequal = extract_actual_clauses(rel->baserestrictinfo, false);
+
+		/*
+		 * If any quals exist, then these may be useful to allow us to perform
+		 * further partition pruning during execution.  We'll generate a
+		 * PartitionPruneInfo for each partitioned rel to store these quals
+		 * and allow translation of partition indexes into subpath indexes.
+		 */
+		if (prunequal != NIL)
+			partpruneinfos = make_partition_pruneinfo(root,
+													  best_path->path.parent,
+													  best_path->resultRelations,
+													  best_path->subpaths,
+													  best_path->partitioned_rels,
+													  prunequal);
+	}
+
 	plan = make_modifytable(root,
 							best_path->operation,
 							best_path->canSetTag,
@@ -2631,7 +2659,8 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path)
 							best_path->returningLists,
 							best_path->rowMarks,
 							best_path->onconflict,
-							best_path->epqParam);
+							best_path->epqParam,
+							partpruneinfos);
 
 	copy_generic_path_info(&plan->plan, &best_path->path);
 
@@ -6624,7 +6653,8 @@ make_modifytable(PlannerInfo *root,
 				 bool partColsUpdated,
 				 List *resultRelations, List *subplans, List *subroots,
 				 List *withCheckOptionLists, List *returningLists,
-				 List *rowMarks, OnConflictExpr *onconflict, int epqParam)
+				 List *rowMarks, OnConflictExpr *onconflict, int epqParam,
+				 PartitionPruneInfo *partpruneinfos)
 {
 	ModifyTable *node = makeNode(ModifyTable);
 	List	   *fdw_private_list;
@@ -6685,6 +6715,7 @@ make_modifytable(PlannerInfo *root,
 	node->returningLists = returningLists;
 	node->rowMarks = rowMarks;
 	node->epqParam = epqParam;
+	node->part_prune_info = partpruneinfos;
 
 	/*
 	 * For each result relation that is a foreign table, allow the FDW to
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index b44efd6314..6e1300f792 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1239,6 +1239,9 @@ inheritance_planner(PlannerInfo *root)
 	RangeTblEntry *parent_rte;
 	Bitmapset  *parent_relids;
 	Query	  **parent_parses;
+	PlannerInfo *partition_root = NULL;
+	List	   *partitioned_rels = NIL;
+	bool		dummy_modifytbl = false;
 
 	/* Should only get here for UPDATE or DELETE */
 	Assert(parse->commandType == CMD_UPDATE ||
@@ -1350,6 +1353,13 @@ inheritance_planner(PlannerInfo *root)
 		 * expand_partitioned_rtentry for the UPDATE target.)
 		 */
 		root->partColsUpdated = subroot->partColsUpdated;
+
+		/*
+		 * Save this for later so that we can enable run-time pruning on
+		 * the partitioned table(s).
+		 */
+		if (parent_rte->relkind == RELKIND_PARTITIONED_TABLE)
+			partition_root = subroot;
 	}
 
 	/*----------
@@ -1386,6 +1396,9 @@ inheritance_planner(PlannerInfo *root)
 	{
 		AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);
 		RangeTblEntry *child_rte;
+		int				old_rti;
+		int				new_rti;
+
 
 		/* append_rel_list contains all append rels; ignore others */
 		if (!bms_is_member(appinfo->parent_relid, parent_relids))
@@ -1400,11 +1413,18 @@ inheritance_planner(PlannerInfo *root)
 		/* and append it to the original rtable */
 		parse->rtable = lappend(parse->rtable, child_rte);
 
-		/* remember child's index in the SELECT rtable */
-		old_child_rtis = lappend_int(old_child_rtis, appinfo->child_relid);
+		old_rti = appinfo->child_relid;
+		new_rti = list_length(parse->rtable);
 
-		/* and its new index in the final rtable */
-		new_child_rtis = lappend_int(new_child_rtis, list_length(parse->rtable));
+		/* record RT indexes for any RTEs that have changed RT index */
+		if (old_rti != new_rti)
+		{
+			/* remember child's index in the SELECT rtable */
+			old_child_rtis = lappend_int(old_child_rtis, old_rti);
+
+			/* and its new index in the final rtable */
+			new_child_rtis = lappend_int(new_child_rtis, new_rti);
+		}
 
 		/* if child is itself partitioned, update parent_relids */
 		if (child_rte->inh)
@@ -1426,9 +1446,6 @@ inheritance_planner(PlannerInfo *root)
 		int			old_child_rti = lfirst_int(lc);
 		int			new_child_rti = lfirst_int(lc2);
 
-		if (old_child_rti == new_child_rti)
-			continue;			/* nothing to do */
-
 		Assert(old_child_rti > new_child_rti);
 
 		ChangeVarNodes((Node *) child_appinfos,
@@ -1745,6 +1762,9 @@ inheritance_planner(PlannerInfo *root)
 			returningLists = list_make1(parse->returningList);
 		/* Disable tuple routing, too, just to be safe */
 		root->partColsUpdated = false;
+
+		/* Mark that we're performing a dummy modify table */
+		dummy_modifytbl = true;
 	}
 	else
 	{
@@ -1784,6 +1804,105 @@ inheritance_planner(PlannerInfo *root)
 	else
 		rowMarks = root->rowMarks;
 
+
+	/*
+	 * When performing UPDATE/DELETE on a partitioned table, if the query has
+	 * a WHERE clause which supports it, we may be able to perform run-time
+	 * partition pruning.  The following code makes use of PlannerInfo fields
+	 * from the SELECT planning invocation we performed above.  We must
+	 * translate the RT indexes to allow us to use these in 'root'.
+	 */
+	if (partition_root && !dummy_modifytbl)
+	{
+		RelOptInfo *parent_rel;
+		int		   *relid_map;
+		int			i;
+
+		/* create a map to translate old RT indexes into new ones */
+		relid_map = palloc(sizeof(int) * partition_root->simple_rel_array_size);
+
+		/*
+		 * initialize the map assuming everything is the same.  We'll make
+		 * the required adjustments by looking at the old_child_rtis and
+		 * new_child_rtis lists shortly.
+		 */
+		for (i = 1; i < partition_root->simple_rel_array_size; i++)
+			relid_map[i] = i;
+
+		/*
+		 * Fetch the target partitioned table's RelOptInfo.  This may have
+		 * base quals which we can use for run-time pruning.
+		 */
+		parent_rel = partition_root->simple_rel_array[top_parentRTindex];
+
+		final_rel->baserestrictinfo = parent_rel->baserestrictinfo;
+
+		/* adjust varnos of any RTEs that have changed their RT index */
+		forboth(lc, old_child_rtis, lc2, new_child_rtis)
+		{
+			int			old_child_rti = lfirst_int(lc);
+			int			new_child_rti = lfirst_int(lc2);
+
+			Assert(old_child_rti > new_child_rti);
+
+			ChangeVarNodes((Node *) final_rel->baserestrictinfo,
+						   old_child_rti, new_child_rti, 0);
+
+			/*
+			 * While we're at it, adjust the map to account for the changed RT
+			 * index.
+			 */
+			relid_map[old_child_rti] = new_child_rti;
+		}
+
+		/*
+		 * Build a list of each non-leaf partitioned rels which are being
+		 * UPDATEd / DELETEd.
+		 */
+		i = -1;
+		while ((i = bms_next_member(parent_relids, i)) > 0)
+			partitioned_rels = lappend_int(partitioned_rels, relid_map[i]);
+
+		/*
+		 * In order to build the run-time pruning data we'll need append rels
+		 * of any sub-partitioned tables.  If there are some of those and the
+		 * append_rel_array is not already allocated, then do that now.
+		 */
+		if (list_length(partitioned_rels) > 1 &&
+			root->append_rel_array == NULL)
+			root->append_rel_array = palloc0(sizeof(AppendRelInfo *) *
+											 root->simple_rel_array_size);
+
+		/*
+		 * There can only be a single partition hierarchy, so it's fine to
+		 * just make a single element list of the partitioned_rels.
+		 */
+		partitioned_rels = list_make1(partitioned_rels);
+
+		i = -1;
+		while ((i = bms_next_member(parent_relids, i)) >= 0)
+		{
+			int new_rti = relid_map[i];
+
+			Assert(root->simple_rel_array[new_rti] == NULL);
+
+			root->simple_rel_array[new_rti] = partition_root->simple_rel_array[i];
+
+			/*
+			 * The root partition won't have an append rel entry, so we can
+			 * skip that.  We'll need to take the partition_root's version for
+			 * any sub-partitioned table's
+			 */
+			if (i != top_parentRTindex)
+			{
+				Assert(root->append_rel_array[new_rti] == NULL);
+				root->append_rel_array[new_rti] = partition_root->append_rel_array[i];
+			}
+		}
+
+		pfree(relid_map);
+	}
+
 	/* Create Path representing a ModifyTable to do the UPDATE/DELETE work */
 	add_path(final_rel, (Path *)
 			 create_modifytable_path(root, final_rel,
@@ -1793,6 +1912,7 @@ inheritance_planner(PlannerInfo *root)
 									 rootRelation,
 									 root->partColsUpdated,
 									 resultRelations,
+									 partitioned_rels,
 									 subpaths,
 									 subroots,
 									 withCheckOptionLists,
@@ -2376,6 +2496,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
 										rootRelation,
 										false,
 										list_make1_int(parse->resultRelation),
+										NIL,
 										list_make1(path),
 										list_make1(root),
 										withCheckOptionLists,
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index d9ce516211..6fa7de546d 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -3433,6 +3433,9 @@ create_lockrows_path(PlannerInfo *root, RelOptInfo *rel,
  * 'partColsUpdated' is true if any partitioning columns are being updated,
  *		either from the target relation or a descendent partitioned table.
  * 'resultRelations' is an integer list of actual RT indexes of target rel(s)
+ * 'partitioned_rels' is an integer list of RT indexes of non-leaf tables in
+ *		the partition tree, if this is an UPDATE/DELETE to a partitioned table.
+ *		Otherwise NIL.
  * 'subpaths' is a list of Path(s) producing source data (one per rel)
  * 'subroots' is a list of PlannerInfo structs (one per rel)
  * 'withCheckOptionLists' is a list of WCO lists (one per rel)
@@ -3446,8 +3449,8 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel,
 						CmdType operation, bool canSetTag,
 						Index nominalRelation, Index rootRelation,
 						bool partColsUpdated,
-						List *resultRelations, List *subpaths,
-						List *subroots,
+						List *resultRelations, List *partitioned_rels,
+						List *subpaths, List *subroots,
 						List *withCheckOptionLists, List *returningLists,
 						List *rowMarks, OnConflictExpr *onconflict,
 						int epqParam)
@@ -3515,6 +3518,7 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel,
 	pathnode->rootRelation = rootRelation;
 	pathnode->partColsUpdated = partColsUpdated;
 	pathnode->resultRelations = resultRelations;
+	pathnode->partitioned_rels = list_copy(partitioned_rels);
 	pathnode->subpaths = subpaths;
 	pathnode->subroots = subroots;
 	pathnode->withCheckOptionLists = withCheckOptionLists;
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index eac52e6ec8..ecda23f878 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -214,7 +214,12 @@ static void partkey_datum_from_expr(PartitionPruneContext *context,
  * 'parentrel' is the RelOptInfo for an appendrel, and 'subpaths' is the list
  * of scan paths for its child rels.
  *
- * 'partitioned_rels' is a List containing Lists of relids of partitioned
+ * If 'resultRelations' is non-NIL, then this List of RT indexes is used to
+ * build the mapping structures.  Otherwise the 'subpaths' List is used.  Both
+ * of these lists correspond to the planner node's subplans list that we're
+ * building the PartitionPruneInfo for.
+ *
+ * 'partitioned_rels' is a List containing Lists of RT indexes of partitioned
  * tables (a/k/a non-leaf partitions) that are parents of some of the child
  * rels.  Here we attempt to populate the PartitionPruneInfo by adding a
  * 'prune_infos' item for each sublist in the 'partitioned_rels' list.
@@ -228,8 +233,8 @@ static void partkey_datum_from_expr(PartitionPruneContext *context,
  */
 PartitionPruneInfo *
 make_partition_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
-						 List *subpaths, List *partitioned_rels,
-						 List *prunequal)
+						 List *resultRelations, List *subpaths,
+						 List *partitioned_rels, List *prunequal)
 {
 	PartitionPruneInfo *pruneinfo;
 	Bitmapset  *allmatchedsubplans = NULL;
@@ -246,21 +251,39 @@ make_partition_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
 	relid_subplan_map = palloc0(sizeof(int) * root->simple_rel_array_size);
 
 	/*
-	 * relid_subplan_map maps relid of a leaf partition to the index in
-	 * 'subpaths' of the scan plan for that partition.
+	 * If 'resultRelations' are present then map these, otherwise, we map the
+	 * 'subpaths' List.  Later, this will allow us to map partition indexes
+	 * into the executor node's subpath list index during query execution.
 	 */
-	i = 1;
-	foreach(lc, subpaths)
+	if (resultRelations != NIL)
 	{
-		Path	   *path = (Path *) lfirst(lc);
-		RelOptInfo *pathrel = path->parent;
+		i = 1;
+		foreach(lc, resultRelations)
+		{
+			int			resultrel = lfirst_int(lc);
 
-		Assert(IS_SIMPLE_REL(pathrel));
-		Assert(pathrel->relid < root->simple_rel_array_size);
-		/* No duplicates please */
-		Assert(relid_subplan_map[pathrel->relid] == 0);
+			Assert(resultrel < root->simple_rel_array_size);
+			/* No duplicates please */
+			Assert(relid_subplan_map[resultrel] == 0);
+
+			relid_subplan_map[resultrel] = i++;
+		}
+	}
+	else
+	{
+		i = 1;
+		foreach(lc, subpaths)
+		{
+			Path	   *path = (Path *) lfirst(lc);
+			RelOptInfo *pathrel = path->parent;
+
+			Assert(IS_SIMPLE_REL(pathrel));
+			Assert(pathrel->relid < root->simple_rel_array_size);
+			/* No duplicates please */
+			Assert(relid_subplan_map[pathrel->relid] == 0);
 
-		relid_subplan_map[pathrel->relid] = i++;
+			relid_subplan_map[pathrel->relid] = i++;
+		}
 	}
 
 	/* We now build a PartitionedRelPruneInfo for each partitioned rel. */
@@ -404,9 +427,12 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
 			 * an adjust_appendrel_attrs step.  But it might not be, and then
 			 * we have to translate.  We update the prunequal parameter here,
 			 * because in later iterations of the loop for child partitions,
-			 * we want to translate from parent to child variables.
+			 * we want to translate from parent to child variables.  We don't
+			 * need to do this when planning a non-SELECT as, in that case,
+			 * we're only working with a single partition hierarchy.
 			 */
-			if (!bms_equal(parentrel->relids, subpart->relids))
+			if (root->parse->commandType == CMD_SELECT &&
+				!bms_equal(parentrel->relids, subpart->relids))
 			{
 				int			nappinfos;
 				AppendRelInfo **appinfos = find_appinfos_by_relids(root,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index cd3ddf781f..eb0975f47d 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1169,7 +1169,7 @@ typedef struct ModifyTableState
 	int			mt_whichplan;	/* which one is being executed (0..n-1) */
 	TupleTableSlot **mt_scans;	/* input tuple corresponding to underlying
 								 * plans */
-	ResultRelInfo *resultRelInfo;	/* per-subplan target relations */
+	ResultRelInfo **resultRelInfos;	/* per-subplan target relations */
 	ResultRelInfo *rootResultRelInfo;	/* root target relation (partitioned
 										 * table root) */
 	List	  **mt_arowmarks;	/* per-subplan ExecAuxRowMark lists */
@@ -1193,6 +1193,14 @@ typedef struct ModifyTableState
 
 	/* Per plan map for tuple conversion from child to root */
 	TupleConversionMap **mt_per_subplan_tupconv_maps;
+
+	/*
+	 * Details required to allow partitions to be eliminated from the scan, or
+	 * NULL if not possible.
+	 */
+	struct PartitionPruneState *mt_prune_state;
+	Bitmapset  *mt_valid_subplans;	/* for runtime pruning, valid mt_plans
+									 * indexes to execute. */
 } ModifyTableState;
 
 /* ----------------
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 0ceb809644..31b0f8fde0 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1779,6 +1779,8 @@ typedef struct ModifyTablePath
 	Index		rootRelation;	/* Root RT index, if target is partitioned */
 	bool		partColsUpdated;	/* some part key in hierarchy updated */
 	List	   *resultRelations;	/* integer list of RT indexes */
+	/* RT indexes of non-leaf tables in a partition tree */
+	List	   *partitioned_rels;
 	List	   *subpaths;		/* Path(s) producing source data */
 	List	   *subroots;		/* per-target-table PlannerInfos */
 	List	   *withCheckOptionLists;	/* per-target-table WCO lists */
@@ -1788,6 +1790,11 @@ typedef struct ModifyTablePath
 	int			epqParam;		/* ID of Param for EvalPlanQual re-eval */
 } ModifyTablePath;
 
+#define IS_DUMMY_MODIFYTABLE(p) \
+	(IsA((p), ModifyTablePath) && \
+		list_length((p)->subpaths) == 1 && \
+		IS_DUMMY_APPEND(linitial((p)->subpaths)))
+
 /*
  * LimitPath represents applying LIMIT/OFFSET restrictions
  */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 4869fe7b6d..e350acd7ff 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -233,6 +233,8 @@ typedef struct ModifyTable
 	Bitmapset  *fdwDirectModifyPlans;	/* indices of FDW DM plans */
 	List	   *rowMarks;		/* PlanRowMarks (non-locking only) */
 	int			epqParam;		/* ID of Param for EvalPlanQual re-eval */
+	/* Info for run-time subplan pruning; NULL if we're not doing that */
+	struct PartitionPruneInfo *part_prune_info;
 	OnConflictAction onConflictAction;	/* ON CONFLICT action */
 	List	   *arbiterIndexes; /* List of ON CONFLICT arbiter index OIDs  */
 	List	   *onConflictSet;	/* SET for INSERT ON CONFLICT DO UPDATE */
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index e450fe112a..e6e1240135 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -254,8 +254,9 @@ extern ModifyTablePath *create_modifytable_path(PlannerInfo *root,
 												CmdType operation, bool canSetTag,
 												Index nominalRelation, Index rootRelation,
 												bool partColsUpdated,
-												List *resultRelations, List *subpaths,
-												List *subroots,
+												List *resultRelations,
+												List *partitioned_rels,
+												List *subpaths, List *subroots,
 												List *withCheckOptionLists, List *returningLists,
 												List *rowMarks, OnConflictExpr *onconflict,
 												int epqParam);
diff --git a/src/include/partitioning/partprune.h b/src/include/partitioning/partprune.h
index babdad2c3e..92bf3522a2 100644
--- a/src/include/partitioning/partprune.h
+++ b/src/include/partitioning/partprune.h
@@ -70,6 +70,7 @@ typedef struct PartitionPruneContext
 
 extern PartitionPruneInfo *make_partition_pruneinfo(struct PlannerInfo *root,
 													struct RelOptInfo *parentrel,
+													List *resultRelations,
 													List *subpaths,
 													List *partitioned_rels,
 													List *prunequal);
diff --git a/src/test/regress/expected/insert_conflict.out b/src/test/regress/expected/insert_conflict.out
index 1338b2b23e..b6a13fa596 100644
--- a/src/test/regress/expected/insert_conflict.out
+++ b/src/test/regress/expected/insert_conflict.out
@@ -211,6 +211,7 @@ explain (costs off, format json) insert into insertconflicttest values (0, 'Bilb
        "Conflict Resolution": "UPDATE",                                +
        "Conflict Arbiter Indexes": ["key_index"],                      +
        "Conflict Filter": "(insertconflicttest.fruit <> 'Lime'::text)",+
+       "Subplans Removed": 0,                                          +
        "Plans": [                                                      +
          {                                                             +
            "Node Type": "Result",                                      +
diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
index 9c8f80da87..33afaa03f6 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -3196,6 +3196,107 @@ explain (analyze, costs off, summary off, timing off) select * from ma_test wher
 
 reset enable_seqscan;
 reset enable_sort;
+--
+-- Test run-time pruning of ModifyTable subnodes
+--
+-- Ensure only ma_test_p3 is scanned.
+explain (analyze, costs off, summary off, timing off) delete from ma_test where a = (select 29);
+                           QUERY PLAN                           
+----------------------------------------------------------------
+ Delete on ma_test (actual rows=0 loops=1)
+   Delete on ma_test_p1 ma_test_1
+   Delete on ma_test_p2 ma_test_2
+   Delete on ma_test_p3 ma_test_3
+   InitPlan 1 (returns $0)
+     ->  Result (actual rows=1 loops=1)
+   ->  Seq Scan on ma_test_p1 ma_test_1 (never executed)
+         Filter: (a = $0)
+   ->  Seq Scan on ma_test_p2 ma_test_2 (never executed)
+         Filter: (a = $0)
+   ->  Seq Scan on ma_test_p3 ma_test_3 (actual rows=1 loops=1)
+         Filter: (a = $0)
+         Rows Removed by Filter: 9
+(13 rows)
+
+-- Ensure no partitions are scanned.
+explain (analyze, costs off, summary off, timing off) delete from ma_test where a = (select 30);
+                       QUERY PLAN                        
+---------------------------------------------------------
+ Delete on ma_test (actual rows=0 loops=1)
+   Delete on ma_test_p1 ma_test_1
+   Delete on ma_test_p2 ma_test_2
+   Delete on ma_test_p3 ma_test_3
+   InitPlan 1 (returns $0)
+     ->  Result (actual rows=1 loops=1)
+   ->  Seq Scan on ma_test_p1 ma_test_1 (never executed)
+         Filter: (a = $0)
+   ->  Seq Scan on ma_test_p2 ma_test_2 (never executed)
+         Filter: (a = $0)
+   ->  Seq Scan on ma_test_p3 ma_test_3 (never executed)
+         Filter: (a = $0)
+(12 rows)
+
+-- Ensure partition pruning works with an update of the partition key.
+explain (analyze, costs off, summary off, timing off) update ma_test set a = 29 where a = (select 1);
+                           QUERY PLAN                           
+----------------------------------------------------------------
+ Update on ma_test (actual rows=0 loops=1)
+   Update on ma_test_p1 ma_test_1
+   Update on ma_test_p2 ma_test_2
+   Update on ma_test_p3 ma_test_3
+   InitPlan 1 (returns $0)
+     ->  Result (actual rows=1 loops=1)
+   ->  Seq Scan on ma_test_p1 ma_test_1 (actual rows=1 loops=1)
+         Filter: (a = $0)
+         Rows Removed by Filter: 9
+   ->  Seq Scan on ma_test_p2 ma_test_2 (never executed)
+         Filter: (a = $0)
+   ->  Seq Scan on ma_test_p3 ma_test_3 (never executed)
+         Filter: (a = $0)
+(13 rows)
+
+-- Verify the above command
+select tableoid::regclass,a from ma_test where a = 29;
+  tableoid  | a  
+------------+----
+ ma_test_p3 | 29
+(1 row)
+
+truncate ma_test;
+prepare mt_q1 (int) as
+delete from ma_test where a > $1;
+set plan_cache_mode = force_generic_plan;
+explain (analyze, costs off, summary off, timing off) execute mt_q1(15);
+                           QUERY PLAN                           
+----------------------------------------------------------------
+ Delete on ma_test (actual rows=0 loops=1)
+   Delete on ma_test_p2 ma_test_1
+   Delete on ma_test_p3 ma_test_2
+   Subplans Removed: 1
+   ->  Seq Scan on ma_test_p2 ma_test_1 (actual rows=0 loops=1)
+         Filter: (a > $1)
+   ->  Seq Scan on ma_test_p3 ma_test_2 (actual rows=0 loops=1)
+         Filter: (a > $1)
+(8 rows)
+
+explain (analyze, costs off, summary off, timing off) execute mt_q1(25);
+                           QUERY PLAN                           
+----------------------------------------------------------------
+ Delete on ma_test (actual rows=0 loops=1)
+   Delete on ma_test_p3 ma_test_1
+   Subplans Removed: 2
+   ->  Seq Scan on ma_test_p3 ma_test_1 (actual rows=0 loops=1)
+         Filter: (a > $1)
+(5 rows)
+
+-- Ensure ModifyTable behaves correctly when no subplans match exec params
+explain (analyze, costs off, summary off, timing off) execute mt_q1(35);
+                QUERY PLAN                 
+-------------------------------------------
+ Delete on ma_test (actual rows=0 loops=1)
+   Subplans Removed: 3
+(2 rows)
+
 drop table ma_test;
 reset enable_indexonlyscan;
 --
diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql
index d9daba3af3..047ed00ed3 100644
--- a/src/test/regress/sql/partition_prune.sql
+++ b/src/test/regress/sql/partition_prune.sql
@@ -851,6 +851,34 @@ explain (analyze, costs off, summary off, timing off) select * from ma_test wher
 reset enable_seqscan;
 reset enable_sort;
 
+--
+-- Test run-time pruning of ModifyTable subnodes
+--
+
+-- Ensure only ma_test_p3 is scanned.
+explain (analyze, costs off, summary off, timing off) delete from ma_test where a = (select 29);
+
+-- Ensure no partitions are scanned.
+explain (analyze, costs off, summary off, timing off) delete from ma_test where a = (select 30);
+
+-- Ensure partition pruning works with an update of the partition key.
+explain (analyze, costs off, summary off, timing off) update ma_test set a = 29 where a = (select 1);
+
+-- Verify the above command
+select tableoid::regclass,a from ma_test where a = 29;
+
+truncate ma_test;
+
+prepare mt_q1 (int) as
+delete from ma_test where a > $1;
+
+set plan_cache_mode = force_generic_plan;
+
+explain (analyze, costs off, summary off, timing off) execute mt_q1(15);
+explain (analyze, costs off, summary off, timing off) execute mt_q1(25);
+-- Ensure ModifyTable behaves correctly when no subplans match exec params
+explain (analyze, costs off, summary off, timing off) execute mt_q1(35);
+
 drop table ma_test;
 
 reset enable_indexonlyscan;
