From da4f376a16d2fbde48900b654147f251782f3a76 Mon Sep 17 00:00:00 2001
From: "dgrowley@gmail.com" <dgrowley@gmail.com>
Date: Thu, 5 Apr 2018 12:53:02 +1200
Subject: [PATCH v19 5/5] Improve planning speed of partitioned table
 UPDATE/DELETEs

By making a call to grouping_planner for the complete parse of the query we
can make use of the faster partition pruning code used there.  This will
identify all partitions which could be pruned as IS_DUMMY_RELs, of which we
can skip performing each individual grouping_planner call inside
inheritance_planner.  This can improve planner performance significantly when
there are many partitions. There may be a slight slowdown when no partitions
could be pruned or when there are very few (1 or 2) partitions.  However it
seems better to optimize the case when partitions are pruned, rather than the
case where they're not, as those queries are less likely to be fast to
execute. The case for partitioned tables with just 1 or 2 leaf partitions does
not seem worth worrying about too much. The measured regression on 1 partition
was just 10% of overall planning time.

This commit also implements run-time partition pruning for UPDATE/DELETE.
---
 src/backend/commands/explain.c          |   4 +-
 src/backend/executor/execPartition.c    |  18 ++--
 src/backend/executor/nodeMerge.c        |   4 +-
 src/backend/executor/nodeModifyTable.c  | 160 +++++++++++++++++++++++++-------
 src/backend/nodes/copyfuncs.c           |   1 +
 src/backend/nodes/outfuncs.c            |   1 +
 src/backend/nodes/readfuncs.c           |   1 +
 src/backend/optimizer/plan/createplan.c |  32 ++++++-
 src/backend/optimizer/plan/planner.c    |  59 ++++++++++++
 src/include/nodes/execnodes.h           |  11 ++-
 src/include/nodes/plannodes.h           |   2 +
 11 files changed, 239 insertions(+), 54 deletions(-)

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index fa86212769..c593748d84 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -3028,14 +3028,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 17da8cdbd3..1e67308a7a 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -103,7 +103,7 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel)
 
 	if (is_update)
 	{
-		update_rri = mtstate->resultRelInfo;
+		update_rri = mtstate->resultRelInfos[0];
 		num_update_rri = list_length(node->plans);
 		proute->subplan_partition_offsets =
 			palloc(num_update_rri * sizeof(int));
@@ -418,8 +418,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
 		List	   *wcoList;
 		List	   *wcoExprs = NIL;
 		ListCell   *ll;
-		int			firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
-		Relation	firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
+		int			firstVarno = mtstate->resultRelInfos[0]->ri_RangeTableIndex;
+		Relation	firstResultRel = mtstate->resultRelInfos[0]->ri_RelationDesc;
 
 		/*
 		 * In the case of INSERT on a partitioned table, there is only one
@@ -474,8 +474,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
 		TupleTableSlot *slot;
 		ExprContext *econtext;
 		List	   *returningList;
-		int			firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
-		Relation	firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
+		int			firstVarno = mtstate->resultRelInfos[0]->ri_RangeTableIndex;
+		Relation	firstResultRel = mtstate->resultRelInfos[0]->ri_RelationDesc;
 
 		/* See the comment above for WCO lists. */
 		Assert((node->operation == CMD_INSERT &&
@@ -530,8 +530,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
 	if (node && node->onConflictAction != ONCONFLICT_NONE)
 	{
 		TupleConversionMap *map = proute->parent_child_tupconv_maps[partidx];
-		int			firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
-		Relation	firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
+		int			firstVarno = mtstate->resultRelInfos[0]->ri_RangeTableIndex;
+		Relation	firstResultRel = mtstate->resultRelInfos[0]->ri_RelationDesc;
 		TupleDesc	partrelDesc = RelationGetDescr(partrel);
 		ExprContext *econtext = mtstate->ps.ps_ExprContext;
 		ListCell   *lc;
@@ -671,8 +671,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
 	{
 		TupleDesc	partrelDesc = RelationGetDescr(partrel);
 		TupleConversionMap *map = proute->parent_child_tupconv_maps[partidx];
-		int			firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
-		Relation	firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
+		int			firstVarno = mtstate->resultRelInfos[0]->ri_RangeTableIndex;
+		Relation	firstResultRel = mtstate->resultRelInfos[0]->ri_RelationDesc;
 
 		/*
 		 * If the root parent and partition have the same tuple
diff --git a/src/backend/executor/nodeMerge.c b/src/backend/executor/nodeMerge.c
index 0e0d0795d4..29d8c544a2 100644
--- a/src/backend/executor/nodeMerge.c
+++ b/src/backend/executor/nodeMerge.c
@@ -101,7 +101,7 @@ ExecMergeMatched(ModifyTableState *mtstate, EState *estate,
 		if (resultRelInfo == NULL)
 		{
 			resultRelInfo = ExecInitPartitionInfo(mtstate,
-					mtstate->resultRelInfo,
+					mtstate->resultRelInfos[0],
 					proute, estate, leaf_part_index);
 			Assert(resultRelInfo != NULL);
 		}
@@ -397,7 +397,7 @@ ExecMergeNotMatched(ModifyTableState *mtstate, EState *estate,
 	 * currently active result relation, which should be of the root of the
 	 * partition tree.
 	 */
-	resultRelInfo = mtstate->resultRelInfo;
+	resultRelInfo = mtstate->resultRelInfos[0];
 
 	/*
 	 * For INSERT actions, root relation's merge action is OK since the
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index b03db64e8e..adf09b9609 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1219,12 +1219,12 @@ lreplace:;
 				map_index = resultRelInfo->ri_PartitionLeafIndex;
 				Assert(mtstate->rootResultRelInfo == NULL);
 				tupconv_map = TupConvMapForLeaf(proute,
-								mtstate->resultRelInfo,
+								mtstate->resultRelInfos[0],
 								map_index);
 			}
 			else
 			{
-				map_index = resultRelInfo - mtstate->resultRelInfo;
+				map_index = mtstate->mt_whichplan;
 				Assert(map_index >= 0 && map_index < mtstate->mt_nplans);
 				tupconv_map = tupconv_map_for_subplan(mtstate, map_index);
 			}
@@ -1637,12 +1637,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;
@@ -1688,13 +1688,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];
 }
 
 /*
@@ -1915,7 +1916,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;
@@ -1946,7 +1947,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,
 								   gettext_noop("could not convert row type"));
 	}
@@ -2085,7 +2086,7 @@ ExecModifyTable(PlanState *pstate)
 	}
 
 	/* 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;
 
@@ -2123,7 +2124,7 @@ ExecModifyTable(PlanState *pstate)
 
 			if (node->mt_whichplan < node->mt_nplans)
 			{
-				resultRelInfo++;
+				resultRelInfo = node->resultRelInfos[node->mt_whichplan];
 				subplanstate = node->mt_plans[node->mt_whichplan];
 				junkfilter = resultRelInfo->ri_junkFilter;
 				estate->es_result_relation_info = resultRelInfo;
@@ -2314,9 +2315,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	ResultRelInfo *resultRelInfo;
 	Plan	   *subplan;
 	ListCell   *l;
-	int			i;
+	int			i, j;
 	Relation	rel;
 	bool		update_tuple_routing_needed = node->partColsUpdated;
+	Bitmapset  *validsubplans;
 
 	/* check for unsupported flags */
 	Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
@@ -2333,8 +2335,70 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	mtstate->canSetTag = node->canSetTag;
 	mtstate->mt_done = false;
 
+	/* If run-time partition pruning is enabled, then setup that up now */
+	if (node->part_prune_infos != NIL)
+	{
+		PartitionPruning *partprune;
+
+		ExecAssignExprContext(estate, &mtstate->ps);
+
+		partprune = ExecSetupPartitionPruning(&mtstate->ps,
+											  node->part_prune_infos);
+
+		/*
+		 * When there are external params matching the partition key we may be
+		 * able to prune away ModifyTable plans.
+		 */
+		if (!bms_is_empty(partprune->extparams))
+		{
+			/* Determine which subplans match the external params */
+			validsubplans = ExecFindInitialMatchingSubPlans(partprune,
+											list_length(node->plans));
+
+			/*
+			 * If no plans match the given parameters then we must handle this
+			 * case in a special way.  The problem here is that code in
+			 * explain.c requires a ModifyTable to have at least one plan in
+			 * order for it to properly determine the Vars in that plan's
+			 * targetlist.  We sidestep this issue by just initializing the
+			 * first subplan, but we set the mt_done flag so that we never
+			 * actually bother scanning it.
+			 */
+			if (bms_is_empty(validsubplans))
+			{
+				mtstate->mt_done = true;
+
+				/* Mark the first as valid so that it's initialized below */
+				validsubplans = bms_make_singleton(0);
+			}
+
+			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);
+		}
+
+		mtstate->partition_pruning = partprune;
+	}
+	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->partition_pruning = NULL;
+	}
+
+
 	mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * nplans);
-	mtstate->resultRelInfo = estate->es_result_relations + node->resultRelIndex;
+	mtstate->resultRelInfos = (ResultRelInfo **)
+									palloc(sizeof(ResultRelInfo *) * nplans);
 
 	/* If modifying a partitioned table, initialize the root table info */
 	if (node->rootResultRelIndex >= 0)
@@ -2358,8 +2422,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	 */
 	saved_resultRelInfo = estate->es_result_relation_info;
 
-	resultRelInfo = mtstate->resultRelInfo;
-
 	/*
 	 * mergeTargetRelation must be set if we're running MERGE and mustn't be
 	 * set if we're not.
@@ -2367,13 +2429,20 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	Assert(operation != CMD_MERGE || node->mergeTargetRelation > 0);
 	Assert(operation == CMD_MERGE || node->mergeTargetRelation == 0);
 
-	resultRelInfo->ri_mergeTargetRTI = node->mergeTargetRelation;
-
-	i = 0;
+	j = i = 0;
 	foreach(l, node->plans)
 	{
+		if (!bms_is_member(i, validsubplans))
+		{
+			i++;
+			continue;
+		}
+
 		subplan = (Plan *) lfirst(l);
 
+		resultRelInfo = estate->es_result_relations + node->resultRelIndex + i;
+		mtstate->resultRelInfos[j] = resultRelInfo;
+
 		/* Initialize the usesFdwDirectModify flag */
 		resultRelInfo->ri_usesFdwDirectModify = bms_is_member(i,
 															  node->fdwDirectModifyPlans);
@@ -2410,7 +2479,7 @@ 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_plans[j] = ExecInitNode(subplan, estate, eflags);
 
 		/* Also let FDWs init themselves for foreign-table result rels */
 		if (!resultRelInfo->ri_usesFdwDirectModify &&
@@ -2426,10 +2495,12 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 															 eflags);
 		}
 
-		resultRelInfo++;
 		i++;
+		j++;
 	}
 
+	mtstate->resultRelInfos[0]->ri_mergeTargetRTI = node->mergeTargetRelation;
+
 	estate->es_result_relation_info = saved_resultRelInfo;
 
 	/* Get the target relation */
@@ -2482,26 +2553,34 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	/*
 	 * Initialize any WITH CHECK OPTION constraints if needed.
 	 */
-	resultRelInfo = mtstate->resultRelInfo;
-	i = 0;
+	j = i = 0;
 	foreach(l, node->withCheckOptionLists)
 	{
-		List	   *wcoList = (List *) lfirst(l);
+		List	   *wcoList;
 		List	   *wcoExprs = NIL;
 		ListCell   *ll;
 
+		if (!bms_is_member(i, validsubplans))
+		{
+			i++;
+			continue;
+		}
+
+		wcoList = (List *) lfirst(l);
+
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
 			ExprState  *wcoExpr = ExecInitQual((List *) wco->qual,
-											   mtstate->mt_plans[i]);
+											   mtstate->mt_plans[j]);
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
-
+		resultRelInfo = mtstate->resultRelInfos[j];
 		resultRelInfo->ri_WithCheckOptions = wcoList;
 		resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
-		resultRelInfo++;
+
+		j++;
 		i++;
 	}
 
@@ -2531,15 +2610,25 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		/*
 		 * Build a projection for each result rel.
 		 */
-		resultRelInfo = mtstate->resultRelInfo;
+		j = i = 0;
 		foreach(l, node->returningLists)
 		{
-			List	   *rlist = (List *) lfirst(l);
+			List	   *rlist;
+
+			if (!bms_is_member(i, validsubplans))
+			{
+				i++;
+				continue;
+			}
+
+			rlist  = (List *) lfirst(l);
 
+			resultRelInfo = mtstate->resultRelInfos[j];
 			resultRelInfo->ri_projectReturning =
 				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
 										resultRelInfo->ri_RelationDesc->rd_att);
-			resultRelInfo++;
+			j++;
+			i++;
 		}
 	}
 	else
@@ -2555,7 +2644,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	}
 
 	/* 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;
 
@@ -2659,7 +2748,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		}
 	}
 
-	resultRelInfo = mtstate->resultRelInfo;
+	resultRelInfo = mtstate->resultRelInfos[0];
 
 	if (node->mergeActionList)
 	{
@@ -2810,11 +2899,11 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 
 		if (junk_filter_needed)
 		{
-			resultRelInfo = mtstate->resultRelInfo;
 			for (i = 0; i < nplans; i++)
 			{
 				JunkFilter *j;
 
+				resultRelInfo = mtstate->resultRelInfos[i];
 				subplan = mtstate->mt_plans[i]->plan;
 
 				if (operation == CMD_INSERT || operation == CMD_UPDATE)
@@ -2867,13 +2956,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);
 		}
 	}
@@ -2920,7 +3008,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 739a023965..87339d5e79 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -225,6 +225,7 @@ _copyModifyTable(const ModifyTable *from)
 	COPY_NODE_FIELD(exclRelTlist);
 	COPY_NODE_FIELD(mergeSourceTargetList);
 	COPY_NODE_FIELD(mergeActionList);
+	COPY_NODE_FIELD(part_prune_infos);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e31b6a9c33..b748a1d204 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -393,6 +393,7 @@ _outModifyTable(StringInfo str, const ModifyTable *node)
 	WRITE_NODE_FIELD(exclRelTlist);
 	WRITE_NODE_FIELD(mergeSourceTargetList);
 	WRITE_NODE_FIELD(mergeActionList);
+	WRITE_NODE_FIELD(part_prune_infos);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 5bf3d28c51..85d7f38d72 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1640,6 +1640,7 @@ _readModifyTable(void)
 	READ_NODE_FIELD(exclRelTlist);
 	READ_NODE_FIELD(mergeSourceTargetList);
 	READ_NODE_FIELD(mergeActionList);
+	READ_NODE_FIELD(part_prune_infos);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index d6c94846d3..c31bb7ea64 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -295,7 +295,8 @@ static ModifyTable *make_modifytable(PlannerInfo *root,
 				 List *withCheckOptionLists, List *returningLists,
 				 List *rowMarks, OnConflictExpr *onconflict,
 				 List *mergeSourceTargetList,
-				 List *mergeActionList, int epqParam);
+				 List *mergeActionList, int epqParam,
+				 List *partpruneinfos);
 static GatherMerge *create_gather_merge_plan(PlannerInfo *root,
 						 GatherMergePath *best_path);
 
@@ -2484,6 +2485,7 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path)
 	List	   *subplans = NIL;
 	ListCell   *subpaths,
 			   *subroots;
+	List	   *partpruneinfos = NIL;
 
 	/* Build the plan for each input path */
 	forboth(subpaths, best_path->subpaths,
@@ -2512,6 +2514,27 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path)
 		subplans = lappend(subplans, subplan);
 	}
 
+	if (best_path->partitioned_rels != NIL)
+	{
+		int			partrelid = linitial_int(best_path->partitioned_rels);
+		RelOptInfo *rel = root->simple_rel_array[partrelid];
+		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->partitioned_rels,
+											best_path->resultRelations,
+											best_path->subpaths, prunequal);
+	}
+
 	plan = make_modifytable(root,
 							best_path->operation,
 							best_path->canSetTag,
@@ -2527,7 +2550,8 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path)
 							best_path->onconflict,
 							best_path->mergeSourceTargetList,
 							best_path->mergeActionList,
-							best_path->epqParam);
+							best_path->epqParam,
+							partpruneinfos);
 
 	copy_generic_path_info(&plan->plan, &best_path->path);
 
@@ -6600,7 +6624,8 @@ make_modifytable(PlannerInfo *root,
 				 List *withCheckOptionLists, List *returningLists,
 				 List *rowMarks, OnConflictExpr *onconflict,
 				 List *mergeSourceTargetList,
-				 List *mergeActionList, int epqParam)
+				 List *mergeActionList, int epqParam,
+				 List *partpruneinfos)
 {
 	ModifyTable *node = makeNode(ModifyTable);
 	List	   *fdw_private_list;
@@ -6662,6 +6687,7 @@ make_modifytable(PlannerInfo *root,
 	node->mergeSourceTargetList = mergeSourceTargetList;
 	node->mergeActionList = mergeActionList;
 	node->epqParam = epqParam;
+	node->part_prune_infos = 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 421dc79cc4..74fd2db4f7 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1197,6 +1197,7 @@ inheritance_planner(PlannerInfo *root)
 	Query	   *parent_parse;
 	Bitmapset  *parent_relids = bms_make_singleton(top_parentRTindex);
 	PlannerInfo **parent_roots = NULL;
+	PlannerInfo *partition_root = NULL;
 
 	Assert(parse->commandType != CMD_INSERT);
 
@@ -1274,6 +1275,32 @@ inheritance_planner(PlannerInfo *root)
 		 * the ModifyTable node, if one is needed at all.
 		 */
 		partitioned_relids = bms_make_singleton(top_parentRTindex);
+
+		/*
+		 * For partitioned tables, since we're able to determine the minimum
+		 * set of partitions required much more easily than what we can do
+		 * with an inheritance hierarchy, we invoke the grouping_planner on
+		 * the entire given query in order to determine the minimum set of
+		 * partitions which will be required below.  This may mean that we
+		 * invoke the grouping planner far fewer times, as otherwise we'd
+		 * have to invoke it once for each partition.
+		 */
+
+		/*
+		 * Since the planner tends to scribble on the parse, we must make a
+		 * copy of it.  We also must make copies of the PlannerInfo and
+		 * PlannerGlobal since these will also be modified from the call to
+		 * grouping_planner.
+		 */
+		partition_root = makeNode(PlannerInfo);
+		partition_root->glob = makeNode(PlannerGlobal);
+
+		memcpy(partition_root, root, sizeof(PlannerInfo));
+		memcpy(partition_root->glob, root->glob, sizeof(PlannerGlobal));
+
+		partition_root->parse = copyObject(partition_root->parse);
+
+		grouping_planner(partition_root, true, 0.0 /* retrieve all tuples */ );
 	}
 
 	/*
@@ -1304,6 +1331,21 @@ inheritance_planner(PlannerInfo *root)
 		if (!bms_is_member(appinfo->parent_relid, parent_relids))
 			continue;
 
+		/*
+		 * If the target rel is a partitioned table then skip any child
+		 * partitions which were found to be dummies by the grouping_planner
+		 * call performed above.
+		 */
+		if (partition_root)
+		{
+			RelOptInfo *rel;
+
+			rel = find_base_rel(partition_root, appinfo->child_relid);
+
+			if (IS_DUMMY_REL(rel))
+				continue;
+		}
+
 		/*
 		 * expand_inherited_rtentry() always processes a parent before any of
 		 * that parent's children, so the parent_root for this relation should
@@ -1629,6 +1671,23 @@ inheritance_planner(PlannerInfo *root)
 		Assert(list_length(partitioned_rels) >= 1);
 	}
 
+	/*
+	 * The individual grouping_planner calls per partition above performed
+	 * no planning on the actual partitioned tables, however, in order to
+	 * allow partition pruning at run-time we must know the baserestrictinfo
+	 * of each partition.  We simply replace the RelOptInfos from the initial
+	 * full plan which was generated and replace the non-complete RelOptInfos
+	 * which are stored in root.
+	 */
+	if (partition_root)
+	{
+		int		i;
+
+		i = -1;
+		while ((i = bms_next_member(partitioned_relids, i)) >= 0)
+			root->simple_rel_array[i] = partition_root->simple_rel_array[i];
+	}
+
 	/* Create Path representing a ModifyTable to do the UPDATE/DELETE work */
 	add_path(final_rel, (Path *)
 			 create_modifytable_path(root, final_rel,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 9851f35f77..6bbf4a8cfa 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1027,6 +1027,8 @@ typedef struct EPQState
 } EPQState;
 
 
+struct PartitionPruning;
+
 /* ----------------
  *	 ResultState information
  * ----------------
@@ -1083,7 +1085,7 @@ typedef struct ModifyTableState
 	PlanState **mt_plans;		/* subplans (one per target rel) */
 	int			mt_nplans;		/* number of plans in the array */
 	int			mt_whichplan;	/* which one is being executed (0..n-1) */
-	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 */
@@ -1109,6 +1111,12 @@ typedef struct ModifyTableState
 
 	/* Flags showing which subcommands are present INS/UPD/DEL/DO NOTHING */
 	int			mt_merge_subcommands;
+
+	/*
+	 * Details required to allow partitions to be eliminated from the scan, or
+	 * NULL if not possible.
+	 */
+	struct PartitionPruning *partition_pruning;
 } ModifyTableState;
 
 /* ----------------
@@ -1130,7 +1138,6 @@ struct AppendState;
 typedef struct AppendState AppendState;
 struct ParallelAppendState;
 typedef struct ParallelAppendState ParallelAppendState;
-struct PartitionPruning;
 
 struct AppendState
 {
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 0b5189aa7d..bfebd48635 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -242,6 +242,8 @@ typedef struct ModifyTable
 	List	   *exclRelTlist;	/* tlist of the EXCLUDED pseudo relation */
 	List	   *mergeSourceTargetList;
 	List	   *mergeActionList;	/* actions for MERGE */
+	List	   *part_prune_infos; /* Mapping details for run-time subplan
+								   * pruning, one per partitioned_rels */
 } ModifyTable;
 
 /* ----------------
-- 
2.16.2.windows.1

