From fb6700b6a419af45107bb038aa30dafd661a3a00 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Wed, 17 Oct 2018 11:18:12 +0900
Subject: [PATCH v19 1/4] Overhaul inheritance update/delete planning

With the current method, inheritance_planner, which handles the
planning for update/delete commands targeting inheritance trees,
repeatedly calls query_planner for each child table.

That's inefficient for two reasons:

 1. partprune.c cannot be used to perform partition pruning, because
    it can only be invoked from query_planner.  With the current
    approach, query_planner only sees the individual partitions in
    the query tree it receives, not the parent partitioned table.
    This leaves each partition to be pruned using constraint
    exclusion.  (constraint exclusion cannon prune hash partitions
    which means UPDATE/DELETE don't support hash partition pruning.)

 2. Repeated invocation of query_planner results in allocating large
    amounts of memory, especially if there are many child tables.
    Also, it involves repeatedly performing the same processing,
    such as jointree processing.

This commit addresses both of the issues by modifying query_planner
to handle the inheritance target case directly.  Instead of having
the caller create separate PlannerInfo's for individual child
target relations and pass them to query_planner (actually, via
grouping_planner), this teaches query_planner itself to create
the child PlannerInfo's by the original parent PlannerInfo.
It creates them *after* it has generated enough state in the latter,
such as RelOptInfos for various relations, EquivalenceClasses
after processing the join tree, and *before* generating the scan and
join paths.  Scan paths are generated normally, except that Paths of
target children are not "appended".  Join paths are generated
separately for each child target relation, that is, by replacing the
parent relation references in the original join tree with the child
relation references.  Joinrels (RelOptInfos thereof) corresponding
to various child target relation are saved in the parent PlannerInfo
for further processing.

inheritance_planner now calls query_planner just once at the beginning
with the original unmodified query, which takes care of generating
top-level join rels for the child queries as described above.  For each
child join rel, it then calls grouping_planner using previously created
child PlannerInfo to finish up the paths such that they produce
query's top-level target list expanded according to a given child
relation's descriptor.  grouping_planner()'s interface is modified so
that we can pass it what's called a 'planned_rel', a RelOptInfo that
already contains the necessary paths needed to produce its output.

This removes some existing code in inheritance_planner that dealt
with any subquery RTEs in the query.  The rationale of that code
was that the subquery RTEs may change during each iteration of
planning (that is, for different children), so different iterations
better use different copies of those RTEs.  That was handled by
making fresh copies of those RTEs for each iteration of planning
which were appended to the range table, accompanied by modifying all
expressions and auxiliary structures that referenced the original
subquery RTEs to instead reference the copies (that is, change the
varnos).  This copying meant we would end up adding S * N new entries
to the original range table by the time we got to the last unpruned
child, where S is the number of subquery RTEs in the original query
and N the number of unpruned children.  Since with the new code
we perform planning just once, I think we don't need this special
handling.  Actually, there is a regression test output change due
to no longer having copies of subquery RTEs (see the diff of
partition_join.out file.)

Also we no longer need the hack in relation_excluded_by_constraints()
involving setting PlannerInfo.inhTargetKind to enable constraint
exclusion for target child relation, which is no longer needed.
Constraint exclusion now runs during query_planner step described
above.

Regression test output change in partition_join.out is due to the fact
that we no longer create duplicates of subquery RTEs in child range
tables.
---
 doc/src/sgml/ddl.sgml                        |  17 +-
 src/backend/nodes/outfuncs.c                 |   2 +-
 src/backend/optimizer/path/allpaths.c        | 511 ++++++++++++++++---
 src/backend/optimizer/path/equivclass.c      |   5 +-
 src/backend/optimizer/path/joinrels.c        |  10 +-
 src/backend/optimizer/plan/createplan.c      |  10 -
 src/backend/optimizer/plan/planmain.c        |  41 +-
 src/backend/optimizer/plan/planner.c         | 444 +++++-----------
 src/backend/optimizer/prep/prepjointree.c    |   1 -
 src/backend/optimizer/util/appendinfo.c      | 160 +++++-
 src/backend/optimizer/util/inherit.c         | 107 ++++
 src/backend/optimizer/util/plancat.c         |  73 +--
 src/include/nodes/pathnodes.h                |  39 +-
 src/include/optimizer/inherit.h              |   1 +
 src/include/optimizer/paths.h                |   3 +
 src/test/regress/expected/partition_join.out |   4 +-
 16 files changed, 936 insertions(+), 492 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 85e4358988..1bb5ca6b08 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -4438,25 +4438,12 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
    </para>
 
    <note>
-    <para>
-     Currently, pruning of partitions during the planning of an
-     <command>UPDATE</command> or <command>DELETE</command> command is
-     implemented using the constraint exclusion method (however, it is
-     controlled by the <literal>enable_partition_pruning</literal> rather than
-     <literal>constraint_exclusion</literal>) &mdash; see the following section
-     for details and caveats that apply.
-    </para>
-
     <para>
      Execution-time partition pruning currently only occurs for the
      <literal>Append</literal> and <literal>MergeAppend</literal> node types.
      It is not yet implemented for the <literal>ModifyTable</literal> node
-     type.
-    </para>
-
-    <para>
-     Both of these behaviors are likely to be changed in a future release
-     of <productname>PostgreSQL</productname>.
+     type, but that is likely to be changed in a future release of
+     <productname>PostgreSQL</productname>.
     </para>
    </note>
   </sect2>
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 9d44e3e4c6..e7a65985cf 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2210,7 +2210,6 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node)
 	WRITE_FLOAT_FIELD(tuple_fraction, "%.4f");
 	WRITE_FLOAT_FIELD(limit_tuples, "%.0f");
 	WRITE_UINT_FIELD(qual_security_level);
-	WRITE_ENUM_FIELD(inhTargetKind, InheritanceKind);
 	WRITE_BOOL_FIELD(hasJoinRTEs);
 	WRITE_BOOL_FIELD(hasLateralRTEs);
 	WRITE_BOOL_FIELD(hasHavingQual);
@@ -2220,6 +2219,7 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node)
 	WRITE_BITMAPSET_FIELD(curOuterRels);
 	WRITE_NODE_FIELD(curOuterParams);
 	WRITE_BOOL_FIELD(partColsUpdated);
+	WRITE_BOOL_FIELD(inherited_update);
 }
 
 static void
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 55b871c02c..f07db81a80 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -39,6 +39,7 @@
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/plancat.h"
+#include "optimizer/planmain.h"
 #include "optimizer/planner.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
@@ -92,8 +93,12 @@ static void set_foreign_size(PlannerInfo *root, RelOptInfo *rel,
 				 RangeTblEntry *rte);
 static void set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel,
 					 RangeTblEntry *rte);
+static void set_inherit_target_rel_sizes(PlannerInfo *root, RelOptInfo *rel,
+							 Index rti, RangeTblEntry *rte);
 static void set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 					Index rti, RangeTblEntry *rte);
+static void set_inherit_target_rel_pathlists(PlannerInfo *root,
+							 RelOptInfo *rel, Index rti, RangeTblEntry *rte);
 static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 						Index rti, RangeTblEntry *rte);
 static void generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel,
@@ -122,6 +127,8 @@ static void set_result_pathlist(PlannerInfo *root, RelOptInfo *rel,
 static void set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel,
 					   RangeTblEntry *rte);
 static RelOptInfo *make_rel_from_joinlist(PlannerInfo *root, List *joinlist);
+static RelOptInfo *inheritance_make_rel_from_joinlist(PlannerInfo *root,
+						List *joinlist);
 static bool subquery_is_pushdown_safe(Query *subquery, Query *topquery,
 						  pushdown_safety_info *safetyInfo);
 static bool recurse_pushdown_safe(Node *setOp, Query *topquery,
@@ -155,27 +162,6 @@ make_one_rel(PlannerInfo *root, List *joinlist)
 	Index		rti;
 	double		total_pages;
 
-	/*
-	 * Construct the all_baserels Relids set.
-	 */
-	root->all_baserels = NULL;
-	for (rti = 1; rti < root->simple_rel_array_size; rti++)
-	{
-		RelOptInfo *brel = root->simple_rel_array[rti];
-
-		/* there may be empty slots corresponding to non-baserel RTEs */
-		if (brel == NULL)
-			continue;
-
-		Assert(brel->relid == rti); /* sanity check on array */
-
-		/* ignore RTEs that are "other rels" */
-		if (brel->reloptkind != RELOPT_BASEREL)
-			continue;
-
-		root->all_baserels = bms_add_member(root->all_baserels, brel->relid);
-	}
-
 	/* Mark base rels as to whether we care about fast-start plans */
 	set_base_rel_consider_startup(root);
 
@@ -223,13 +209,34 @@ make_one_rel(PlannerInfo *root, List *joinlist)
 
 	/*
 	 * Generate access paths for the entire join tree.
+	 *
+	 * For UPDATE/DELETE on an inheritance parent, join paths should be
+	 * generated for each child result rel separately.
 	 */
-	rel = make_rel_from_joinlist(root, joinlist);
+	if (root->inherited_update)
+	{
+		/*
+		 * RelOptInfo corresponding to the query's original target relation
+		 * is returned.  Join paths (if any) are attached to child joinrels,
+		 * not this rel.  Also, this rel's sole path (ModifyTable) will be set
+		 * by inheritance_planner later, so we can't check its paths yet.
+		 */
+		rel = inheritance_make_rel_from_joinlist(root, joinlist);
+	}
+	else
+	{
+		rel = make_rel_from_joinlist(root, joinlist);
 
-	/*
-	 * The result should join all and only the query's base rels.
-	 */
-	Assert(bms_equal(rel->relids, root->all_baserels));
+		/*
+		 * The result should join all and only the query's base rels.
+		 */
+		Assert(bms_equal(rel->relids, root->all_baserels));
+
+		/* Check that we got at least one usable path */
+		if (!rel || !rel->cheapest_total_path ||
+			rel->cheapest_total_path->param_info != NULL)
+			elog(ERROR, "failed to construct the join relation");
+	}
 
 	return rel;
 }
@@ -313,9 +320,10 @@ set_base_rel_sizes(PlannerInfo *root)
 		 * If parallelism is allowable for this query in general, see whether
 		 * it's allowable for this rel in particular.  We have to do this
 		 * before set_rel_size(), because (a) if this rel is an inheritance
-		 * parent, set_append_rel_size() will use and perhaps change the rel's
-		 * consider_parallel flag, and (b) for some RTE types, set_rel_size()
-		 * goes ahead and makes paths immediately.
+		 * parent, set_append_rel_size() or set_inherit_target_rel_sizes()
+		 * will use and perhaps change the rel's consider_parallel flag, and
+		 * (b) for some RTE types, set_rel_size() goes ahead and makes paths
+		 * immediately.
 		 */
 		if (root->glob->parallelModeOK)
 			set_rel_consider_parallel(root, rel, rte);
@@ -379,8 +387,15 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
 	}
 	else if (rte->inh)
 	{
-		/* It's an "append relation", process accordingly */
-		set_append_rel_size(root, rel, rti, rte);
+		/*
+		 * If it's a target relation, set the sizes of children instead.
+		 * Otherwise, we'll append the outputs of children, so process it as
+		 * an "append relation".
+		 */
+		if (rti == root->parse->resultRelation)
+			set_inherit_target_rel_sizes(root, rel, rti, rte);
+		else
+			set_append_rel_size(root, rel, rti, rte);
 	}
 	else
 	{
@@ -469,14 +484,26 @@ static void
 set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 				 Index rti, RangeTblEntry *rte)
 {
+	bool	inherited_update = false;
+
 	if (IS_DUMMY_REL(rel))
 	{
 		/* We already proved the relation empty, so nothing more to do */
 	}
 	else if (rte->inh)
 	{
-		/* It's an "append relation", process accordingly */
-		set_append_rel_pathlist(root, rel, rti, rte);
+		/*
+		 * If it's a target relation, set the pathlists of children instead.
+		 * Otherwise, we'll append the outputs of children, so process it as
+		 * an "append relation".
+		 */
+		if (root->inherited_update && root->parse->resultRelation == rti)
+		{
+			inherited_update = true;
+			set_inherit_target_rel_pathlists(root, rel, rti, rte);
+		}
+		else
+			set_append_rel_pathlist(root, rel, rti, rte);
 	}
 	else
 	{
@@ -554,8 +581,12 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 	if (set_rel_pathlist_hook)
 		(*set_rel_pathlist_hook) (root, rel, rti, rte);
 
-	/* Now find the cheapest of the paths for this rel */
-	set_cheapest(rel);
+	/*
+	 * Now find the cheapest of the paths for this rel, unless it's an
+	 * inheritance parent and this is an update/delete operation.
+	 */
+	if (!inherited_update)
+		set_cheapest(rel);
 
 #ifdef OPTIMIZER_DEBUG
 	debug_print_rel(root, rel);
@@ -920,6 +951,225 @@ set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 	rel->fdwroutine->GetForeignPaths(root, rel, rte->relid);
 }
 
+/*
+ * set_inherit_target_rel_sizes
+ *	  Set size estimates for the child target relations
+ *
+ * The passed-in rel represents the target relation of the query that is
+ * known to have inheritance children.  This is very much like
+ * set_append_rel_size, except it doesn't set the size estimates for the
+ * passed-in rel itself, because we don't need to "append" the children
+ * in this case.
+ */
+static void
+set_inherit_target_rel_sizes(PlannerInfo *root, RelOptInfo *rel,
+							 Index rti, RangeTblEntry *rte)
+{
+	int			parentRTindex = rti;
+	bool		has_live_children = false;
+	ListCell   *l;
+	Relids		live_children = NULL;
+	bool		did_pruning = false;
+
+	/* Guard against stack overflow due to overly deep inheritance tree. */
+	check_stack_depth();
+
+	Assert(IS_SIMPLE_REL(rel));
+
+	/*
+	 * If the partitioned relation has any baserestrictinfo quals then we
+	 * attempt to use these quals to prune away partitions that cannot
+	 * possibly contain any tuples matching these quals.  In this case we'll
+	 * store the relids of all partitions which could possibly contain a
+	 * matching tuple, and skip anything else in the loop below.
+	 */
+	if (enable_partition_pruning &&
+		rte->relkind == RELKIND_PARTITIONED_TABLE &&
+		rel->baserestrictinfo != NIL)
+	{
+		live_children = prune_append_rel_partitions(rel);
+		did_pruning = true;
+	}
+
+	foreach(l, root->append_rel_list)
+	{
+		AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
+		int			childRTindex;
+		RangeTblEntry *childRTE;
+		RelOptInfo *childrel;
+		PlannerInfo *subroot;
+		ListCell   *lc;
+		List	   *translated_exprs,
+				   *child_target_exprs;
+		bool		childpruned;
+
+		/* append_rel_list contains all append rels; ignore others */
+		if (appinfo->parent_relid != parentRTindex)
+			continue;
+
+		childRTindex = appinfo->child_relid;
+		childRTE = root->simple_rte_array[childRTindex];
+
+		/*
+		 * The child rel's RelOptInfo was already created during
+		 * add_base_rels_to_query.
+		 */
+		childrel = find_base_rel(root, childRTindex);
+		Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
+
+		childpruned = (did_pruning &&
+					   !bms_is_member(appinfo->child_relid, live_children));
+
+		/*
+		 * Unless the child is pruned, we have to copy the parent's targetlist
+		 * and quals to the child, with appropriate substitution of variables.
+		 * If any constant false or NULL clauses turn up, we can disregard the
+		 * child right away.  If not, we can apply constraint exclusion with
+		 * just the baserestrictinfo quals.
+		 */
+		if (childpruned ||
+			!apply_child_basequals(root, rel, childrel, childRTE, appinfo) ||
+			relation_excluded_by_constraints(root, childrel, childRTE))
+		{
+			/* This partition needn't be scanned; skip it. */
+			set_dummy_rel_pathlist(childrel);
+			continue;
+		}
+
+		/*
+		 * Add missing Vars to child's reltarget.
+		 *
+		 * add_inherit_target_child_root() would've added only those that are
+		 * needed to be present in the top-level tlist (or ones that
+		 * preprocess_targetlist thinks are needed to be in the tlist.)  We
+		 * may need other attributes such as those contained in WHERE clauses,
+		 * which are already computed for the parent during
+		 * deconstruct_jointree processing of the original query (child's
+		 * query never goes through deconstruct_jointree.)
+		 */
+		translated_exprs = (List *)
+			adjust_appendrel_attrs(root,
+								   (Node *) rel->reltarget->exprs,
+								   1, &appinfo);
+		child_target_exprs = childrel->reltarget->exprs;
+		foreach(lc, translated_exprs)
+		{
+			Expr *expr = lfirst(lc);
+
+			if (!list_member(child_target_exprs, expr))
+				child_target_exprs = lappend(child_target_exprs, expr);
+		}
+
+		subroot = root->inh_target_child_roots[childRTindex];
+
+		/*
+		 * Also, We have to find any ECs containing parent's expressions and
+		 * *replace* them with their copies containing child expressions.
+		 */
+		if (rel->has_eclass_joins || has_useful_pathkeys(root, rel))
+			subroot->eq_classes = (List *)
+						adjust_appendrel_attrs(root,
+											   (Node *) root->eq_classes,
+											   1, &appinfo);
+		childrel->has_eclass_joins = rel->has_eclass_joins;
+
+		/* Translate join quals. */
+		childrel->joininfo = (List *)
+			adjust_appendrel_attrs(subroot,
+								   (Node *) rel->joininfo,
+								   1, &appinfo);
+
+		/*
+		 * Compute the child's size using possibly modified subroot.
+		 */
+		set_rel_size(subroot, childrel, childRTindex, childRTE);
+
+		/* If the child itself is partitioned it may turn into a dummy rel. */
+		if (IS_DUMMY_REL(childrel))
+			continue;
+
+		/* We have at least one live child. */
+		has_live_children = true;
+
+		Assert(childrel->rows > 0);
+	}
+
+	if (has_live_children)
+	{
+		/*
+		 * Set a non-zero value here to cope with the caller's requirement
+		 * that non-dummy relations are actually not empty.  We don't try to
+		 * be accurate here, because we're not going to create a path that
+		 * combines the children outputs.
+		 */
+		rel->rows = 1;
+	}
+	else
+	{
+		/*
+		 * All children were excluded by constraints, so mark the relation
+		 * as dummy.  We must do this in this phase so that the rel's
+		 * dummy-ness is visible when we generate paths for other rels.
+		 */
+		set_dummy_rel_pathlist(rel);
+	}
+}
+
+/*
+ * set_inherit_target_rel_pathlists
+ *	  Build access paths for the child target relations
+ *
+ * Similar to set_append_rel_pathlist, except that we build paths of the
+ * children, but don't build an Append path.
+ */
+static void
+set_inherit_target_rel_pathlists(PlannerInfo *root, RelOptInfo *rel,
+								 Index rti, RangeTblEntry *rte)
+{
+	int			parentRTindex = rti;
+	ListCell   *l;
+
+	/* Nothing to do if all the children were excluded. */
+	if (IS_DUMMY_REL(rel))
+		return;
+
+	/* Generate access paths for each of the children of passed-in rel */
+	foreach(l, root->append_rel_list)
+	{
+		AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
+		int			childRTindex;
+		RangeTblEntry *childRTE;
+		RelOptInfo *childrel;
+		PlannerInfo *subroot;
+
+		/* append_rel_list contains all append rels; ignore others */
+		if (appinfo->parent_relid != parentRTindex)
+			continue;
+
+		/* Re-locate the child RTE and RelOptInfo */
+		childRTindex = appinfo->child_relid;
+		childRTE = root->simple_rte_array[childRTindex];
+		childrel = root->simple_rel_array[childRTindex];
+		subroot = root->inh_target_child_roots[childRTindex];
+		/* Transfer the value from main root to subroot. */
+		subroot->total_table_pages = root->total_table_pages;
+
+		/*
+		 * If set_append_rel_size() decided the parent appendrel was
+		 * parallel-unsafe at some point after visiting this child rel, we
+		 * need to propagate the unsafety marking down to the child, so that
+		 * we don't generate useless partial paths for it.
+		 */
+		if (!rel->consider_parallel)
+			childrel->consider_parallel = false;
+
+		/*
+		 * Compute the child's access paths.
+		 */
+		set_rel_pathlist(subroot, childrel, childRTindex, childRTE);
+	}
+}
+
 /*
  * set_append_rel_size
  *	  Set size estimates for a simple "append relation"
@@ -1015,6 +1265,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 		RelOptInfo *childrel;
 		ListCell   *parentvars;
 		ListCell   *childvars;
+		bool		childpruned;
 
 		/* append_rel_list contains all append rels; ignore others */
 		if (appinfo->parent_relid != parentRTindex)
@@ -1030,36 +1281,20 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 		childrel = find_base_rel(root, childRTindex);
 		Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
 
-		if (did_pruning && !bms_is_member(appinfo->child_relid, live_children))
-		{
-			/* This partition was pruned; skip it. */
-			set_dummy_rel_pathlist(childrel);
-			continue;
-		}
-
+		childpruned = (did_pruning &&
+					   !bms_is_member(appinfo->child_relid, live_children));
 		/*
-		 * We have to copy the parent's targetlist and quals to the child,
-		 * with appropriate substitution of variables.  If any constant false
-		 * or NULL clauses turn up, we can disregard the child right away.
-		 * If not, we can apply constraint exclusion with just the
-		 * baserestrictinfo quals.
+		 * Unless the child is pruned, we have to copy the parent's targetlist
+		 * and quals to the child, with appropriate substitution of variables.
+		 * If any constant false or NULL clauses turn up, we can disregard the
+		 * child right away.  If not, we can apply constraint exclusion with
+		 * just the baserestrictinfo quals.
 		 */
-		if (!apply_child_basequals(root, rel, childrel, childRTE, appinfo))
+		if (childpruned ||
+			!apply_child_basequals(root, rel, childrel, childRTE, appinfo) ||
+			relation_excluded_by_constraints(root, childrel, childRTE))
 		{
-			/*
-			 * Some restriction clause reduced to constant FALSE or NULL after
-			 * substitution, so this child need not be scanned.
-			 */
-			set_dummy_rel_pathlist(childrel);
-			continue;
-		}
-
-		if (relation_excluded_by_constraints(root, childrel, childRTE))
-		{
-			/*
-			 * This child need not be scanned, so we can omit it from the
-			 * appendrel.
-			 */
+			/* This partition needn't be scanned; skip it. */
 			set_dummy_rel_pathlist(childrel);
 			continue;
 		}
@@ -2562,6 +2797,158 @@ generate_gather_paths(PlannerInfo *root, RelOptInfo *rel, bool override_rows)
 	}
 }
 
+/*
+ * inheritance_make_rel_from_joinlist
+ *		Perform join planning for all non-dummy leaf inheritance children
+ *		in their role as an UPDATE/DELETE query's target relation
+ *
+ * If a child relation is a partitioned table, its children are processed in
+ * turn by recursively calling this function.
+ */
+static RelOptInfo *
+inheritance_make_rel_from_joinlist(PlannerInfo *root, List *joinlist)
+{
+	Index		resultRelation = root->parse->resultRelation;
+	RelOptInfo *resultrel;
+	ListCell   *lc;
+#ifdef USE_ASSERT_CHECKING
+	Relids	all_baserels;
+#endif
+
+	/* For UPDATE/DELETE queries, the top parent can only ever be a table. */
+	Assert(root->parse->commandType == CMD_UPDATE ||
+		   root->parse->commandType == CMD_DELETE);
+	Assert(planner_rt_fetch(resultRelation, root)->rtekind == RTE_RELATION);
+	resultrel = find_base_rel(root, resultRelation);
+
+	/* Nothing to do. */
+	if (IS_DUMMY_REL(resultrel))
+		return resultrel;
+
+	foreach(lc, root->append_rel_list)
+	{
+		AppendRelInfo *appinfo = lfirst(lc);
+		PlannerInfo *subroot;
+		RelOptInfo *childrel;
+		RelOptInfo *childjoinrel;
+		List	   *translated_joinlist;
+
+		if (appinfo->parent_relid != resultRelation)
+			continue;
+
+		childrel = find_base_rel(root, appinfo->child_relid);
+
+		/* Ignore excluded/pruned children. */
+		if (IS_DUMMY_REL(childrel))
+			continue;
+
+		/* Add this child. */
+		root->inh_target_child_rels = lappend_int(root->inh_target_child_rels,
+												  appinfo->child_relid);
+
+		/* Perform join planning with child subroot. */
+		subroot = root->inh_target_child_roots[appinfo->child_relid];
+		Assert(subroot->parse->resultRelation > 0);
+
+		/*
+		 * Modify joinlist such that relations joined to the top parent rel
+		 * appear to be joined to the child rel instead.  Do the same for
+		 * any SpecialJoinInfo structs.
+		 */
+		translated_joinlist = (List *)
+						adjust_appendrel_attrs(subroot,
+											   (Node *) joinlist,
+											   1, &appinfo);
+		subroot->join_info_list = (List *)
+						adjust_appendrel_attrs(subroot,
+											   (Node *) root->join_info_list,
+											   1, &appinfo);
+
+		/*
+		 * Sub-partitioned tables have to be processed recursively using the
+		 * translated subroot as the parent, because AppendRelInfos link
+		 * sub-partitions to their immediate parents, not the root partitioned
+		 * table.
+		 */
+		if (childrel->part_scheme != NULL)
+		{
+			/*
+			 * inheritance_make_rel_from_joinlist() return the target relation
+			 * RelOptInfo.
+			 */
+			childrel =
+				inheritance_make_rel_from_joinlist(subroot,
+												   translated_joinlist);
+
+			/*
+			 * Add this child relation as a placeholder in the parent root's
+			 * inh_target_child_path_rels so that inheritance_planner sees
+			 * same number of entries in it as inh_target_child_rels.
+			 */
+			root->inh_target_child_path_rels =
+					lappend(root->inh_target_child_path_rels, childrel);
+
+			/*
+			 * Also propagate this child's own children into the parent's
+			 * list.
+			 */
+			if (subroot->inh_target_child_rels != NIL)
+			{
+				root->inh_target_child_rels =
+					list_concat(root->inh_target_child_rels,
+								subroot->inh_target_child_rels);
+				root->inh_target_child_path_rels =
+					list_concat(root->inh_target_child_path_rels,
+								subroot->inh_target_child_path_rels);
+			}
+			continue;
+		}
+
+		/*
+		 * Since we added the child rel directly into the join tree, we must
+		 * modify it to be a "base" rel instead of an "other" rel, which the
+		 * join planning code expects the relations being joined to be.
+		 */
+		childrel->reloptkind = RELOPT_BASEREL;
+
+		Assert(subroot->join_rel_list == NIL);
+		Assert(subroot->join_rel_hash == NULL);
+
+		/* Perform join planning and save the resulting RelOptInfo. */
+		childjoinrel = make_rel_from_joinlist(subroot, translated_joinlist);
+
+		/* Check that we got at least one usable path */
+		if (!childjoinrel || !childjoinrel->cheapest_total_path ||
+			childjoinrel->cheapest_total_path->param_info != NULL)
+			elog(ERROR, "failed to construct the child join relation");
+
+		/*
+		 * Remember the paths of child target rel.  inheritance_planner will
+		 * perform the remaining steps of planning for each child relation
+		 * separately.  Specifically, it will call grouping_planner on every
+		 * RelOptInfo contained in the inh_target_child_rels list, each of
+		 * which represents the source of tuples to be modified for a given
+		 * target child rel.
+		 */
+		root->inh_target_child_path_rels =
+					lappend(root->inh_target_child_path_rels, childjoinrel);
+#ifdef USE_ASSERT_CHECKING
+		/*
+		 * The following implements essentially the same Assert as in
+		 * make_one_rel, our caller.
+		 */
+		all_baserels = bms_copy(root->all_baserels);
+		all_baserels = bms_del_member(all_baserels,
+									  root->parse->resultRelation);
+		all_baserels = bms_add_member(all_baserels,
+									  subroot->parse->resultRelation);
+		Assert(bms_equal(childjoinrel->relids, all_baserels));
+#endif
+	}
+
+	return resultrel;
+}
+
 /*
  * make_rel_from_joinlist
  *	  Build access paths using a "joinlist" to guide the join path search.
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 3454f12912..332dc34276 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -32,9 +32,6 @@
 #include "utils/lsyscache.h"
 
 
-static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-			  Expr *expr, Relids relids, Relids nullable_relids,
-			  bool is_child, Oid datatype);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
 									   EquivalenceClass *ec);
 static void generate_base_implied_equalities_no_const(PlannerInfo *root,
@@ -540,7 +537,7 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
 /*
  * add_eq_member - build a new EquivalenceMember and add it to an EC
  */
-static EquivalenceMember *
+EquivalenceMember *
 add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 			  Relids nullable_relids, bool is_child, Oid datatype)
 {
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index dfbbfdac6d..a02321486f 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -1462,8 +1462,14 @@ update_child_rel_info(PlannerInfo *root,
 							   (Node *) rel->reltarget->exprs,
 							   1, &appinfo);
 
-	/* Make child entries in the EquivalenceClass as well */
-	if (rel->has_eclass_joins || has_useful_pathkeys(root, rel))
+	/*
+	 * Make child entries in the EquivalenceClass as well.  If the childrel
+	 * appears to be a dummy one (one built by build_dummy_partition_rel()),
+	 * no need to make any new entries, because anything that'd need those
+	 * can instead use the parent's (rel).
+	 */
+	if (childrel->relid != rel->relid &&
+		(rel->has_eclass_joins || has_useful_pathkeys(root, rel)))
 		add_child_rel_equivalences(root, appinfo, rel, childrel);
 	childrel->has_eclass_joins = rel->has_eclass_joins;
 }
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 1b4f7db649..496c560cad 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -2049,12 +2049,7 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path)
 	/*
 	 * During setrefs.c, we'll need the grouping_map to fix up the cols lists
 	 * in GroupingFunc nodes.  Save it for setrefs.c to use.
-	 *
-	 * This doesn't work if we're in an inheritance subtree (see notes in
-	 * create_modifytable_plan).  Fortunately we can't be because there would
-	 * never be grouping in an UPDATE/DELETE; but let's Assert that.
 	 */
-	Assert(root->inhTargetKind == INHKIND_NONE);
 	Assert(root->grouping_map == NULL);
 	root->grouping_map = grouping_map;
 
@@ -2211,12 +2206,7 @@ create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path)
 	 * with InitPlan output params.  (We can't just do that locally in the
 	 * MinMaxAgg node, because path nodes above here may have Agg references
 	 * as well.)  Save the mmaggregates list to tell setrefs.c to do that.
-	 *
-	 * This doesn't work if we're in an inheritance subtree (see notes in
-	 * create_modifytable_plan).  Fortunately we can't be because there would
-	 * never be aggregates in an UPDATE/DELETE; but let's Assert that.
 	 */
-	Assert(root->inhTargetKind == INHKIND_NONE);
 	Assert(root->minmax_aggs == NIL);
 	root->minmax_aggs = best_path->mmaggregates;
 
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 3cedd01c98..acb1461624 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -29,7 +29,7 @@
 #include "optimizer/paths.h"
 #include "optimizer/placeholder.h"
 #include "optimizer/planmain.h"
-
+#include "optimizer/prep.h"
 
 /*
  * query_planner
@@ -60,6 +60,7 @@ query_planner(PlannerInfo *root, List *tlist,
 	Query	   *parse = root->parse;
 	List	   *joinlist;
 	RelOptInfo *final_rel;
+	Index		rti;
 
 	/*
 	 * Init planner lists to empty.
@@ -259,15 +260,43 @@ query_planner(PlannerInfo *root, List *tlist,
 	 */
 	extract_restriction_or_clauses(root);
 
+	/*
+	 * Construct the all_baserels Relids set.
+	 */
+	root->all_baserels = NULL;
+	for (rti = 1; rti < root->simple_rel_array_size; rti++)
+	{
+		RelOptInfo *brel = root->simple_rel_array[rti];
+
+		/* there may be empty slots corresponding to non-baserel RTEs */
+		if (brel == NULL)
+			continue;
+
+		Assert(brel->relid == rti); /* sanity check on array */
+
+		/* ignore RTEs that are "other rels" */
+		if (brel->reloptkind != RELOPT_BASEREL)
+			continue;
+
+		root->all_baserels = bms_add_member(root->all_baserels, brel->relid);
+	}
+
+	/*
+	 * Add child subroots needed to use during planning for individual child
+	 * targets
+	 */
+	if (root->inherited_update)
+	{
+		root->inh_target_child_roots = (PlannerInfo **)
+										palloc0(root->simple_rel_array_size *
+												sizeof(PlannerInfo *));
+		add_inherited_target_child_roots(root);
+	}
+
 	/*
 	 * Ready to do the primary planning.
 	 */
 	final_rel = make_one_rel(root, joinlist);
 
-	/* Check that we got at least one usable path */
-	if (!final_rel || !final_rel->cheapest_total_path ||
-		final_rel->cheapest_total_path->param_info != NULL)
-		elog(ERROR, "failed to construct the join relation");
-
 	return final_rel;
 }
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index b2239728cf..71aa8dc72a 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -36,6 +36,7 @@
 #include "lib/knapsack.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
+#include "nodes/pathnodes.h"
 #ifdef OPTIMIZER_DEBUG
 #include "nodes/print.h"
 #endif
@@ -131,7 +132,7 @@ static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
 static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode);
 static void inheritance_planner(PlannerInfo *root);
 static void grouping_planner(PlannerInfo *root, bool inheritance_update,
-				 double tuple_fraction);
+				 RelOptInfo *planned_rel, double tuple_fraction);
 static grouping_sets_data *preprocess_grouping_sets(PlannerInfo *root);
 static List *remap_to_groupclause_idx(List *groupClause, List *gsets,
 						 int *tleref_to_colnum_map);
@@ -636,7 +637,6 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->grouping_map = NULL;
 	root->minmax_aggs = NIL;
 	root->qual_security_level = 0;
-	root->inhTargetKind = INHKIND_NONE;
 	root->hasRecursion = hasRecursion;
 	if (hasRecursion)
 		root->wt_param_id = assign_special_exec_param(root);
@@ -737,6 +737,13 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	 */
 	expand_inherited_tables(root);
 
+	/*
+	 * Now that we have figured out "actual" inheritance situation of the
+	 * relations, set whether the query is an inherited UPDATE/DELETE.
+	 */
+	root->inherited_update = (parse->resultRelation &&
+					  rt_fetch(parse->resultRelation, parse->rtable)->inh);
+
 	/*
 	 * Set hasHavingQual to remember if HAVING clause is present.  Needed
 	 * because preprocess_expression will reduce a constant-true condition to
@@ -997,7 +1004,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 		rt_fetch(parse->resultRelation, parse->rtable)->inh)
 		inheritance_planner(root);
 	else
-		grouping_planner(root, false, tuple_fraction);
+		grouping_planner(root, false, NULL, tuple_fraction);
 
 	/*
 	 * Capture the set of outer-level param IDs we have access to, for use in
@@ -1173,13 +1180,41 @@ preprocess_phv_expression(PlannerInfo *root, Expr *expr)
  *	  inheritance set.
  *
  * We have to handle this case differently from cases where a source relation
- * is an inheritance set. Source inheritance is expanded at the bottom of the
- * plan tree (see allpaths.c), but target inheritance has to be expanded at
- * the top.  The reason is that for UPDATE, each target relation needs a
- * different targetlist matching its own column set.  Fortunately,
+ * is an inheritance set, where outputs of all the children are combined and
+ * combined output consumed using the source relation's (parent's) column set.
+ * For UPDATE, each target relation, where the query's output will go, needs
+ * a different targetlist matching its own column set.  So, we will need to
+ * translate the query such that it produces the desired output required by
+ * each of the child target relations and plan each separately.  Fortunately,
  * the UPDATE/DELETE target can never be the nullable side of an outer join,
  * so it's OK to generate the plan this way.
  *
+ * Planning steps that need to be performed separately for each child and
+ * any preliminary processing needed for that are handled by the following
+ * sub-routines of query_planner:
+ *
+ *	add_inherit_target_roots - this creates copies of PlannerInfo for each
+ *	child after query_planner has finished processing the join tree and
+ *	creating EquivalenceClasses, so that working state of planning need
+ *	not be created afresh for each child, especially the various arrays
+ *
+ *	set_inherit_target_rel_sizes - this sets size estimates for child
+ *	relations, replace the parent EC members by corresponding child ones
+ *	in their respective subroots
+ *
+ *	set_inherit_target_rel_pathlists - this creates Paths for scanning
+ *	individual child relations
+ *
+ *	inherit_make_rel_from_joinlist - this translates the jointree, replacing
+ *	the target relation in the original jointree (the root parent) by
+ *	individual child target relations and performs join planning on the
+ *	resulting join tree, saving the RelOptInfos of resulting join relations
+ *	into the top-level PlannerInfo
+ *
+ * Finally, here we apply grouping_planner to each child join relation
+ * produced by set_inherit_target_rel_pathlists to so that its path produces
+ * the desired targetlist.
+ *
  * Returns nothing; the useful output is in the Paths we attach to
  * the (UPPERREL_FINAL, NULL) upperrel stored in *root.
  *
@@ -1191,14 +1226,8 @@ inheritance_planner(PlannerInfo *root)
 {
 	Query	   *parse = root->parse;
 	int			top_parentRTindex = parse->resultRelation;
-	Bitmapset  *subqueryRTindexes;
-	Bitmapset  *modifiableARIindexes;
 	int			nominalRelation = -1;
 	Index		rootRelation = 0;
-	List	   *final_rtable = NIL;
-	int			save_rel_array_size = 0;
-	RelOptInfo **save_rel_array = NULL;
-	AppendRelInfo **save_append_rel_array = NULL;
 	List	   *subpaths = NIL;
 	List	   *subroots = NIL;
 	List	   *resultRelations = NIL;
@@ -1206,70 +1235,49 @@ inheritance_planner(PlannerInfo *root)
 	List	   *returningLists = NIL;
 	List	   *rowMarks;
 	RelOptInfo *final_rel;
-	ListCell   *lc;
-	Index		rti;
+	ListCell   *lc1,
+			   *lc2;
 	RangeTblEntry *parent_rte;
-	PlannerInfo *parent_root;
-	Query	   *parent_parse;
-	Bitmapset  *parent_relids = bms_make_singleton(top_parentRTindex);
-	PlannerInfo **parent_roots = NULL;
+	List	   *tlist;
+	standard_qp_extra qp_extra;
+	RelOptInfo *planned_rel;
 
+	/* Inheritance is never used for insert. */
 	Assert(parse->commandType != CMD_INSERT);
 
 	/*
-	 * We generate a modified instance of the original Query for each target
-	 * relation, plan that, and put all the plans into a list that will be
-	 * controlled by a single ModifyTable node.  All the instances share the
-	 * same rangetable, but each instance must have its own set of subquery
-	 * RTEs within the finished rangetable because (1) they are likely to get
-	 * scribbled on during planning, and (2) it's not inconceivable that
-	 * subqueries could get planned differently in different cases.  We need
-	 * not create duplicate copies of other RTE kinds, in particular not the
-	 * target relations, because they don't have either of those issues.  Not
-	 * having to duplicate the target relations is important because doing so
-	 * (1) would result in a rangetable of length O(N^2) for N targets, with
-	 * at least O(N^3) work expended here; and (2) would greatly complicate
-	 * management of the rowMarks list.
+	 * Let query_planner generate the access paths for the query for each
+	 * target child relation.
 	 *
-	 * To begin with, generate a bitmapset of the relids of the subquery RTEs.
+	 * First, save the unexpanded version of the query's targetlist.
+	 * create_inherit_target_child_root will use it as base when expanding
+	 * it for a given child relation as the query's target relation instead
+	 * of the parent.
 	 */
-	subqueryRTindexes = NULL;
-	rti = 1;
-	foreach(lc, parse->rtable)
-	{
-		RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
+	root->unexpanded_tlist = list_copy(root->parse->targetList);
 
-		if (rte->rtekind == RTE_SUBQUERY)
-			subqueryRTindexes = bms_add_member(subqueryRTindexes, rti);
-		rti++;
-	}
+	/* We haven't expanded inheritance yet, so pass false. */
+	tlist = preprocess_targetlist(root);
+	root->processed_tlist = tlist;
+	qp_extra.tlist = tlist;
+	qp_extra.activeWindows = NIL;
+	qp_extra.groupClause = NIL;
+	planned_rel = query_planner(root, tlist, standard_qp_callback, &qp_extra);
 
 	/*
-	 * Next, we want to identify which AppendRelInfo items contain references
-	 * to any of the aforesaid subquery RTEs.  These items will need to be
-	 * copied and modified to adjust their subquery references; whereas the
-	 * other ones need not be touched.  It's worth being tense over this
-	 * because we can usually avoid processing most of the AppendRelInfo
-	 * items, thereby saving O(N^2) space and time when the target is a large
-	 * inheritance tree.  We can identify AppendRelInfo items by their
-	 * child_relid, since that should be unique within the list.
+	 * If it turned out during query planning that all the children are dummy
+	 * (pruned or excluded by constraints), no need to do the steps below.
+	 * Let grouping_planner finish up the final path.
 	 */
-	modifiableARIindexes = NULL;
-	if (subqueryRTindexes != NULL)
+	if (IS_DUMMY_REL(planned_rel))
 	{
-		foreach(lc, root->append_rel_list)
-		{
-			AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);
-
-			if (bms_is_member(appinfo->parent_relid, subqueryRTindexes) ||
-				bms_is_member(appinfo->child_relid, subqueryRTindexes) ||
-				bms_overlap(pull_varnos((Node *) appinfo->translated_vars),
-							subqueryRTindexes))
-				modifiableARIindexes = bms_add_member(modifiableARIindexes,
-													  appinfo->child_relid);
-		}
+		grouping_planner(root, false, planned_rel, 0.0);
+		return;
 	}
 
+	Assert(planned_rel->relid == top_parentRTindex);
+	Assert(planned_rel->reloptkind == RELOPT_BASEREL);
+
 	/*
 	 * If the parent RTE is a partitioned table, we should use that as the
 	 * nominal target relation, because the RTEs added for partitioned tables
@@ -1277,7 +1285,7 @@ inheritance_planner(PlannerInfo *root)
 	 * not appear anywhere else in the plan, so the confusion explained below
 	 * for non-partitioning inheritance cases is not possible.
 	 */
-	parent_rte = rt_fetch(top_parentRTindex, root->parse->rtable);
+	parent_rte = planner_rt_fetch(top_parentRTindex, root);
 	if (parent_rte->relkind == RELKIND_PARTITIONED_TABLE)
 	{
 		nominalRelation = top_parentRTindex;
@@ -1285,95 +1293,39 @@ inheritance_planner(PlannerInfo *root)
 	}
 
 	/*
-	 * The PlannerInfo for each child is obtained by translating the relevant
-	 * members of the PlannerInfo for its immediate parent, which we find
-	 * using the parent_relid in its AppendRelInfo.  We save the PlannerInfo
-	 * for each parent in an array indexed by relid for fast retrieval. Since
-	 * the maximum number of parents is limited by the number of RTEs in the
-	 * query, we use that number to allocate the array. An extra entry is
-	 * needed since relids start from 1.
+	 * Get on with finalizing the path for each child target relation by
+	 * calling grouping_planner on its joinrel.
 	 */
-	parent_roots = (PlannerInfo **) palloc0((list_length(parse->rtable) + 1) *
-											sizeof(PlannerInfo *));
-	parent_roots[top_parentRTindex] = root;
-
-	/*
-	 * And now we can get on with generating a plan for each child table.
-	 */
-	foreach(lc, root->append_rel_list)
+	forboth(lc1, root->inh_target_child_rels,
+			lc2, root->inh_target_child_path_rels)
 	{
-		AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);
-		PlannerInfo *subroot;
+		Index		childRTindex = lfirst_int(lc1);
+		RelOptInfo *childjoinrel = lfirst(lc2);
+		PlannerInfo *subroot = root->inh_target_child_roots[childRTindex];
+		AppendRelInfo *appinfo = root->append_rel_array[childRTindex];
 		RangeTblEntry *child_rte;
 		RelOptInfo *sub_final_rel;
 		Path	   *subpath;
 
-		/* append_rel_list contains all append rels; ignore others */
-		if (!bms_is_member(appinfo->parent_relid, parent_relids))
-			continue;
-
-		/*
-		 * expand_inherited_rtentry() always processes a parent before any of
-		 * that parent's children, so the parent_root for this relation should
-		 * already be available.
-		 */
-		parent_root = parent_roots[appinfo->parent_relid];
-		Assert(parent_root != NULL);
-		parent_parse = parent_root->parse;
-
-		/*
-		 * We need a working copy of the PlannerInfo so that we can control
-		 * propagation of information back to the main copy.
-		 */
-		subroot = makeNode(PlannerInfo);
-		memcpy(subroot, parent_root, sizeof(PlannerInfo));
-
-		/*
-		 * Generate modified query with this rel as target.  We first apply
-		 * adjust_appendrel_attrs, which copies the Query and changes
-		 * references to the parent RTE to refer to the current child RTE,
-		 * then fool around with subquery RTEs.
-		 */
-		subroot->parse = (Query *)
-			adjust_appendrel_attrs(parent_root,
-								   (Node *) parent_parse,
-								   1, &appinfo);
+		Assert(subroot != NULL);
+		Assert(subroot->parse->resultRelation == childRTindex);
 
 		/*
 		 * If there are securityQuals attached to the parent, move them to the
 		 * child rel (they've already been transformed properly for that).
 		 */
-		parent_rte = rt_fetch(appinfo->parent_relid, subroot->parse->rtable);
-		child_rte = rt_fetch(appinfo->child_relid, subroot->parse->rtable);
+		parent_rte = planner_rt_fetch(appinfo->parent_relid, subroot);
+		child_rte = planner_rt_fetch(appinfo->child_relid, subroot);
 		child_rte->securityQuals = parent_rte->securityQuals;
 		parent_rte->securityQuals = NIL;
 
 		/*
-		 * Mark whether we're planning a query to a partitioned table or an
-		 * inheritance parent.
+		 * Ignore a partitioned child.  Instead, the paths of its children will
+		 * be added to subpaths.
 		 */
-		subroot->inhTargetKind =
-			(rootRelation != 0) ? INHKIND_PARTITIONED : INHKIND_INHERITED;
-
-		/*
-		 * If this child is further partitioned, remember it as a parent.
-		 * Since a partitioned table does not have any data, we don't need to
-		 * create a plan for it, and we can stop processing it here.  We do,
-		 * however, need to remember its modified PlannerInfo for use when
-		 * processing its children, since we'll update their varnos based on
-		 * the delta from immediate parent to child, not from top to child.
-		 *
-		 * Note: a very non-obvious point is that we have not yet added
-		 * duplicate subquery RTEs to the subroot's rtable.  We mustn't,
-		 * because then its children would have two sets of duplicates,
-		 * confusing matters.
-		 */
-		if (child_rte->inh)
+		if (childjoinrel->part_scheme)
 		{
-			Assert(child_rte->relkind == RELKIND_PARTITIONED_TABLE);
-			parent_relids = bms_add_member(parent_relids, appinfo->child_relid);
-			parent_roots[appinfo->child_relid] = subroot;
-
+			Assert(child_rte->inh);
 			continue;
 		}
 
@@ -1401,111 +1353,8 @@ inheritance_planner(PlannerInfo *root)
 		if (nominalRelation < 0)
 			nominalRelation = appinfo->child_relid;
 
-		/*
-		 * The rowMarks list might contain references to subquery RTEs, so
-		 * make a copy that we can apply ChangeVarNodes to.  (Fortunately, the
-		 * executor doesn't need to see the modified copies --- we can just
-		 * pass it the original rowMarks list.)
-		 */
-		subroot->rowMarks = copyObject(parent_root->rowMarks);
-
-		/*
-		 * The append_rel_list likewise might contain references to subquery
-		 * RTEs (if any subqueries were flattenable UNION ALLs).  So prepare
-		 * to apply ChangeVarNodes to that, too.  As explained above, we only
-		 * want to copy items that actually contain such references; the rest
-		 * can just get linked into the subroot's append_rel_list.
-		 *
-		 * If we know there are no such references, we can just use the outer
-		 * append_rel_list unmodified.
-		 */
-		if (modifiableARIindexes != NULL)
-		{
-			ListCell   *lc2;
-
-			subroot->append_rel_list = NIL;
-			foreach(lc2, parent_root->append_rel_list)
-			{
-				AppendRelInfo *appinfo2 = lfirst_node(AppendRelInfo, lc2);
-
-				if (bms_is_member(appinfo2->child_relid, modifiableARIindexes))
-					appinfo2 = copyObject(appinfo2);
-
-				subroot->append_rel_list = lappend(subroot->append_rel_list,
-												   appinfo2);
-			}
-		}
-
-		/*
-		 * Add placeholders to the child Query's rangetable list to fill the
-		 * RT indexes already reserved for subqueries in previous children.
-		 * These won't be referenced, so there's no need to make them very
-		 * valid-looking.
-		 */
-		while (list_length(subroot->parse->rtable) < list_length(final_rtable))
-			subroot->parse->rtable = lappend(subroot->parse->rtable,
-											 makeNode(RangeTblEntry));
-
-		/*
-		 * If this isn't the first child Query, generate duplicates of all
-		 * subquery RTEs, and adjust Var numbering to reference the
-		 * duplicates. To simplify the loop logic, we scan the original rtable
-		 * not the copy just made by adjust_appendrel_attrs; that should be OK
-		 * since subquery RTEs couldn't contain any references to the target
-		 * rel.
-		 */
-		if (final_rtable != NIL && subqueryRTindexes != NULL)
-		{
-			ListCell   *lr;
-
-			rti = 1;
-			foreach(lr, parent_parse->rtable)
-			{
-				RangeTblEntry *rte = lfirst_node(RangeTblEntry, lr);
-
-				if (bms_is_member(rti, subqueryRTindexes))
-				{
-					Index		newrti;
-
-					/*
-					 * The RTE can't contain any references to its own RT
-					 * index, except in its securityQuals, so we can save a
-					 * few cycles by applying ChangeVarNodes to the rest of
-					 * the rangetable before we append the RTE to it.
-					 */
-					newrti = list_length(subroot->parse->rtable) + 1;
-					ChangeVarNodes((Node *) subroot->parse, rti, newrti, 0);
-					ChangeVarNodes((Node *) subroot->rowMarks, rti, newrti, 0);
-					/* Skip processing unchanging parts of append_rel_list */
-					if (modifiableARIindexes != NULL)
-					{
-						ListCell   *lc2;
-
-						foreach(lc2, subroot->append_rel_list)
-						{
-							AppendRelInfo *appinfo2 = lfirst_node(AppendRelInfo, lc2);
-
-							if (bms_is_member(appinfo2->child_relid,
-											  modifiableARIindexes))
-								ChangeVarNodes((Node *) appinfo2, rti, newrti, 0);
-						}
-					}
-					rte = copyObject(rte);
-					ChangeVarNodes((Node *) rte->securityQuals, rti, newrti, 0);
-					subroot->parse->rtable = lappend(subroot->parse->rtable,
-													 rte);
-				}
-				rti++;
-			}
-		}
-
-		/* There shouldn't be any OJ info to translate, as yet */
-		Assert(subroot->join_info_list == NIL);
-		/* and we haven't created PlaceHolderInfos, either */
-		Assert(subroot->placeholder_list == NIL);
-
-		/* Generate Path(s) for accessing this result relation */
-		grouping_planner(subroot, true, 0.0 /* retrieve all tuples */ );
+		/* Apply the top-level targetlist to childjoinrel's path. */
+		grouping_planner(subroot, true, childjoinrel, 0.0);
 
 		/*
 		 * Select cheapest path in case there's more than one.  We always run
@@ -1517,45 +1366,10 @@ inheritance_planner(PlannerInfo *root)
 		subpath = sub_final_rel->cheapest_total_path;
 
 		/*
-		 * If this child rel was excluded by constraint exclusion, exclude it
-		 * from the result plan.
+		 * child rel cannot be empty, or inheritance_make_rel_from_joinlist
+		 * wouldn't have put it in the list to begin with.
 		 */
-		if (IS_DUMMY_PATH(subpath))
-			continue;
-
-		/*
-		 * If this is the first non-excluded child, its post-planning rtable
-		 * becomes the initial contents of final_rtable; otherwise, append
-		 * just its modified subquery RTEs to final_rtable.
-		 */
-		if (final_rtable == NIL)
-			final_rtable = subroot->parse->rtable;
-		else
-			final_rtable = list_concat(final_rtable,
-									   list_copy_tail(subroot->parse->rtable,
-													  list_length(final_rtable)));
-
-		/*
-		 * We need to collect all the RelOptInfos from all child plans into
-		 * the main PlannerInfo, since setrefs.c will need them.  We use the
-		 * last child's simple_rel_array (previous ones are too short), so we
-		 * have to propagate forward the RelOptInfos that were already built
-		 * in previous children.
-		 */
-		Assert(subroot->simple_rel_array_size >= save_rel_array_size);
-		for (rti = 1; rti < save_rel_array_size; rti++)
-		{
-			RelOptInfo *brel = save_rel_array[rti];
-
-			if (brel)
-				subroot->simple_rel_array[rti] = brel;
-		}
-		save_rel_array_size = subroot->simple_rel_array_size;
-		save_rel_array = subroot->simple_rel_array;
-		save_append_rel_array = subroot->append_rel_array;
-
-		/* Make sure any initplans from this rel get into the outer list */
-		root->init_plans = subroot->init_plans;
+		Assert(!IS_DUMMY_PATH(subpath));
 
 		/* Build list of sub-paths */
 		subpaths = lappend(subpaths, subpath);
@@ -1586,36 +1400,6 @@ inheritance_planner(PlannerInfo *root)
 	 * to get control here.
 	 */
 
-	/*
-	 * If we managed to exclude every child rel, return a dummy plan; it
-	 * doesn't even need a ModifyTable node.
-	 */
-	if (subpaths == NIL)
-	{
-		set_dummy_rel_pathlist(final_rel);
-		return;
-	}
-
-	/*
-	 * Put back the final adjusted rtable into the master copy of the Query.
-	 * (We mustn't do this if we found no non-excluded children.)
-	 */
-	parse->rtable = final_rtable;
-	root->simple_rel_array_size = save_rel_array_size;
-	root->simple_rel_array = save_rel_array;
-	root->append_rel_array = save_append_rel_array;
-
-	/* Must reconstruct master's simple_rte_array, too */
-	root->simple_rte_array = (RangeTblEntry **)
-		palloc0((list_length(final_rtable) + 1) * sizeof(RangeTblEntry *));
-	rti = 1;
-	foreach(lc, final_rtable)
-	{
-		RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
-
-		root->simple_rte_array[rti++] = rte;
-	}
-
 	/*
 	 * If there was a FOR [KEY] UPDATE/SHARE clause, the LockRows node will
 	 * have dealt with fetching non-locked marked rows, else we need to have
@@ -1656,6 +1440,12 @@ inheritance_planner(PlannerInfo *root)
  * (inheritance_planner will create a single ModifyTable node covering all the
  * target tables.)
  *
+ * If non-NULL, planned_rel is a RelOptInfo containing paths for the query's
+ * top-level joinrel, which the caller produced by itself.  In that case, this
+ * function only needs to adjust the targetlist of its cheapest_total_path.
+ * The only caller that may pass such a RelOptInfo currently is
+ * inheritance_planner.
+ *
  * tuple_fraction is the fraction of tuples we expect will be retrieved.
  * tuple_fraction is interpreted as follows:
  *	  0: expect all tuples to be retrieved (normal case)
@@ -1674,6 +1464,7 @@ inheritance_planner(PlannerInfo *root)
  */
 static void
 grouping_planner(PlannerInfo *root, bool inheritance_update,
+				 RelOptInfo *planned_rel,
 				 double tuple_fraction)
 {
 	Query	   *parse = root->parse;
@@ -1686,7 +1477,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
 	List	   *final_targets;
 	List	   *final_targets_contain_srfs;
 	bool		final_target_parallel_safe;
-	RelOptInfo *current_rel;
+	RelOptInfo *current_rel = planned_rel;
 	RelOptInfo *final_rel;
 	ListCell   *lc;
 
@@ -1726,6 +1517,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
 		 * special work for recursive unions is the responsibility of
 		 * plan_set_operations.
 		 */
+		Assert(current_rel == NULL);
 		current_rel = plan_set_operations(root);
 
 		/*
@@ -1815,17 +1607,26 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
 				parse->groupClause = preprocess_groupclause(root, NIL);
 		}
 
-		/* Preprocess targetlist */
-		tlist = preprocess_targetlist(root);
-
 		/*
-		 * We are now done hacking up the query's targetlist.  Most of the
-		 * remaining planning work will be done with the PathTarget
-		 * representation of tlists, but save aside the full representation so
-		 * that we can transfer its decoration (resnames etc) to the topmost
-		 * tlist of the finished Plan.
+		 * Preprocess targetlist, if needed.  If the caller has already done
+		 * query planning, root->processed_tlist already contains the desired
+		 * targetlist.
 		 */
-		root->processed_tlist = tlist;
+		if (planned_rel == NULL)
+		{
+			tlist = preprocess_targetlist(root);
+
+			/*
+			 * We are now done hacking up the query's targetlist.  Most of the
+			 * remaining planning work will be done with the PathTarget
+			 * representation of tlists, but save aside the full representation
+			 * so that we can transfer its decoration (resnames etc) to the
+			 * topmost tlist of the finished Plan.
+			 */
+			root->processed_tlist = tlist;
+		}
+		else
+			tlist = root->processed_tlist;
 
 		/*
 		 * Collect statistics about aggregates for estimating costs, and mark
@@ -1905,8 +1706,9 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
 		 * We also generate (in standard_qp_callback) pathkey representations
 		 * of the query's sort clause, distinct clause, etc.
 		 */
-		current_rel = query_planner(root, tlist,
-									standard_qp_callback, &qp_extra);
+		if (current_rel == NULL)
+			current_rel = query_planner(root, tlist,
+										standard_qp_callback, &qp_extra);
 
 		/*
 		 * Convert the query's result tlist into PathTarget format.
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 9e001c1bdc..2132c086cc 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -894,7 +894,6 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	subroot->grouping_map = NULL;
 	subroot->minmax_aggs = NIL;
 	subroot->qual_security_level = 0;
-	subroot->inhTargetKind = INHKIND_NONE;
 	subroot->hasRecursion = false;
 	subroot->wt_param_id = -1;
 	subroot->non_recursive_path = NULL;
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index ca6622ece9..7447853db9 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -18,6 +18,8 @@
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/appendinfo.h"
+#include "optimizer/pathnode.h"
+#include "optimizer/paths.h"
 #include "parser/parsetree.h"
 #include "utils/lsyscache.h"
 #include "utils/rel.h"
@@ -394,8 +396,164 @@ adjust_appendrel_attrs_mutator(Node *node,
 											  context->appinfos);
 		return (Node *) phv;
 	}
+
+	/*
+	 * This is needed, because inheritance_make_rel_from_joinlist needs to
+	 * translate root->join_info_list executing make_rel_from_joinlist for a
+	 * given child.
+	 */
+	if (IsA(node, SpecialJoinInfo))
+	{
+		SpecialJoinInfo *oldinfo = (SpecialJoinInfo *) node;
+		SpecialJoinInfo *newinfo = makeNode(SpecialJoinInfo);
+
+		memcpy(newinfo, oldinfo, sizeof(SpecialJoinInfo));
+		newinfo->min_lefthand = adjust_child_relids(oldinfo->min_lefthand,
+													context->nappinfos,
+													context->appinfos);
+		newinfo->min_righthand = adjust_child_relids(oldinfo->min_righthand,
+													 context->nappinfos,
+													 context->appinfos);
+		newinfo->syn_lefthand = adjust_child_relids(oldinfo->syn_lefthand,
+													context->nappinfos,
+													context->appinfos);
+		newinfo->syn_righthand = adjust_child_relids(oldinfo->syn_righthand,
+													 context->nappinfos,
+													 context->appinfos);
+		newinfo->semi_rhs_exprs =
+			(List *) expression_tree_mutator((Node *)
+											 oldinfo->semi_rhs_exprs,
+											 adjust_appendrel_attrs_mutator,
+											 (void *) context);
+		return (Node *) newinfo;
+	}
+
+	if (IsA(node, EquivalenceClass))
+	{
+		EquivalenceClass *ec = (EquivalenceClass *) node;
+		EquivalenceClass *new_ec = makeNode(EquivalenceClass);
+		AppendRelInfo *appinfo = NULL;
+		RelOptInfo *parent_rel,
+				   *child_rel;
+		ListCell *lc;
+
+		/*
+		 * First memcpy the existing EC to the new EC, which creates shallow
+		 * copies of all the members and then make copies of members that
+		 * we'll change below.
+		 *
+		 * XXX comment in _copyPathKey says it's OK to recycle EC
+		 * pointers, but as long as we do the whole planning for a
+		 * given child using a given root, copying ECs like this
+		 * shouldn't be a problem.  Maybe, the following code could
+		 * be in _copyEquivalenceClass()?
+		 */
+		memcpy(new_ec, ec, sizeof(EquivalenceClass));
+		new_ec->ec_opfamilies = list_copy(ec->ec_opfamilies);
+		new_ec->ec_sources = list_copy(ec->ec_sources);
+		new_ec->ec_derives = list_copy(ec->ec_derives);
+		new_ec->ec_relids = bms_copy(ec->ec_relids);
+		new_ec->ec_members = list_copy(ec->ec_members);
+
+		/*
+		 * No point in searching if parent rel not mentioned in eclass; but we
+		 * can't tell that for sure if parent rel is itself a child.
+		 */
+		for (cnt = 0; cnt < nappinfos; cnt++)
+		{
+			if (bms_is_member(appinfos[cnt]->parent_relid, ec->ec_relids))
+			{
+				appinfo = appinfos[cnt];
+				break;
+			}
+		}
+
+		if (appinfo == NULL)
+			return (Node *) new_ec;
+
+		parent_rel = find_base_rel(context->root, appinfo->parent_relid);
+		child_rel = find_base_rel(context->root, appinfo->child_relid);
+		if (parent_rel->reloptkind != RELOPT_BASEREL)
+			return (Node *) new_ec;
+
+		/*
+		 * Check if this EC contains an expression referencing the parent
+		 * relation, translate it to child, and store it in place of
+		 * the original parent expression.
+		 */
+		foreach(lc, new_ec->ec_members)
+		{
+			EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
+
+			if (cur_em->em_is_const)
+				continue;		/* ignore consts here */
+
+			/* Does it reference parent_rel? */
+			if (bms_overlap(cur_em->em_relids, parent_rel->relids))
+			{
+				/* Yes, generate transformed child version */
+				Expr	   *new_expr;
+				Relids		new_relids;
+				Relids		new_nullable_relids;
+
+				new_expr = (Expr *)
+					adjust_appendrel_attrs(context->root,
+										   (Node *) cur_em->em_expr,
+										   1, &appinfo);
+
+				/*
+				 * Transform em_relids to match.  Note we do *not* do
+				 * pull_varnos(child_expr) here, as for example the
+				 * transformation might have substituted a constant, but we
+				 * don't want the child member to be marked as constant.
+				 */
+				new_relids = bms_difference(cur_em->em_relids,
+											parent_rel->relids);
+				new_relids = bms_add_members(new_relids, child_rel->relids);
+
+				/*
+				 * And likewise for nullable_relids.  Note this code assumes
+				 * parent and child relids are singletons.
+				 */
+				new_nullable_relids = cur_em->em_nullable_relids;
+				if (bms_overlap(new_nullable_relids, parent_rel->relids))
+				{
+					new_nullable_relids = bms_difference(new_nullable_relids,
+														 parent_rel->relids);
+					new_nullable_relids = bms_add_members(new_nullable_relids,
+														  child_rel->relids);
+				}
+
+				/*
+				 * The new expression simply replaces the old parent one, and
+				 * em_is_child is set to true so that it's recognized as such
+				 * during child planning.
+				 */
+				lfirst(lc) = add_eq_member(new_ec, new_expr,
+										   new_relids, new_nullable_relids,
+										   false, cur_em->em_datatype);
+
+				/*
+				 * We have found and replaced the parent expression, so done
+				 * with EC.
+				 */
+				break;
+			}
+		}
+
+		/*
+		 * Now fix up EC's relids set.  It's OK to modify EC like this,
+		 * because caller must have made a copy of the original EC.
+		 * For example, see adjust_inherited_target_child_root.
+		 */
+		new_ec->ec_relids = bms_del_members(new_ec->ec_relids,
+											parent_rel->relids);
+		new_ec->ec_relids = bms_add_members(new_ec->ec_relids,
+											child_rel->relids);
+		return (Node *) new_ec;
+	}
+
 	/* Shouldn't need to handle planner auxiliary nodes here */
-	Assert(!IsA(node, SpecialJoinInfo));
 	Assert(!IsA(node, AppendRelInfo));
 	Assert(!IsA(node, PlaceHolderInfo));
 	Assert(!IsA(node, MinMaxAggInfo));
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index eaf788e578..68ec4aab6a 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -21,11 +21,14 @@
 #include "miscadmin.h"
 #include "optimizer/appendinfo.h"
 #include "optimizer/inherit.h"
+#include "optimizer/planmain.h"
 #include "optimizer/planner.h"
 #include "optimizer/prep.h"
 #include "utils/rel.h"
 
 
+static PlannerInfo *create_inherited_target_child_root(PlannerInfo *root,
+									AppendRelInfo *appinfo);
 static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte,
 						 Index rti);
 static void expand_partitioned_rtentry(PlannerInfo *root,
@@ -492,3 +495,107 @@ translate_col_privs(const Bitmapset *parent_privs,
 
 	return child_privs;
 }
+
+/*
+ * add_inherited_target_child_roots
+ *		For each child of the query's result relation, this translates the
+ *		original query to match the child and creates a PlannerInfo containing
+ *		the translated query
+ *
+ * Child PlannerInfo reuses most of the parent PlannerInfo's fields unchanged,
+ * except unexpanded_tlist, processed_tlist, and all_baserels, all of which
+ * are based on the child relation.
+ */
+void
+add_inherited_target_child_roots(PlannerInfo *root)
+{
+	Index		resultRelation = root->parse->resultRelation;
+	ListCell   *lc;
+
+	Assert(root->inh_target_child_roots != NULL);
+
+	foreach(lc, root->append_rel_list)
+	{
+		AppendRelInfo *appinfo = lfirst(lc);
+		RangeTblEntry *childRTE;
+		PlannerInfo   *subroot;
+
+		if (appinfo->parent_relid != resultRelation)
+			continue;
+
+		/*
+		 * Create a PlannerInfo for processing this child target relation
+		 * with.
+		 */
+		subroot = create_inherited_target_child_root(root, appinfo);
+		root->inh_target_child_roots[appinfo->child_relid] = subroot;
+
+		/*
+		 * If the child is a partitioned table, recurse to do this for its
+		 * partitions.
+		 */
+		childRTE = root->simple_rte_array[appinfo->child_relid];
+		if (childRTE->inh)
+			add_inherited_target_child_roots(subroot);
+	}
+}
+
+/*
+ * create_inherited_target_child_root
+ *		Workhorse of add_inherited_target_child_roots
+ */
+static PlannerInfo *
+create_inherited_target_child_root(PlannerInfo *root, AppendRelInfo *appinfo)
+{
+	PlannerInfo *subroot;
+	List	   *tlist;
+
+	Assert(root->parse->commandType == CMD_UPDATE ||
+		   root->parse->commandType == CMD_DELETE);
+
+	/*
+	 * Translate the original query to replace Vars of the parent table
+	 * by the corresponding Vars of the child table and to make child the main
+	 * target relation of the query.
+	 */
+	subroot = makeNode(PlannerInfo);
+	memcpy(subroot, root, sizeof(PlannerInfo));
+
+	/*
+	 * Restore the original, unexpanded targetlist, that is, the one before
+	 * preprocess_targetlist was run on the original query.  We'll run
+	 * preprocess_targetlist after translating the query and the targetlist,
+	 * so that it is expanded according to child's tuple descriptor.
+	 */
+	root->parse->targetList = root->unexpanded_tlist;
+	subroot->parse = (Query *) adjust_appendrel_attrs(root,
+													  (Node *) root->parse,
+													  1, &appinfo);
+
+	/*
+	 * Save the just translated targetlist as unexpanded_tlist in the child's
+	 * subroot, so that this child's own children can use it.  Must use copy
+	 * because subroot->parse->targetList will be modified soon.
+	 */
+	subroot->unexpanded_tlist = list_copy(subroot->parse->targetList);
+
+	/*
+	 * Apply planner's expansion of targetlist, such as adding various junk
+	 * column, filling placeholder entries for dropped columns, etc., all of
+	 * which occurs with the child's TupleDesc.
+	 */
+	tlist = preprocess_targetlist(subroot);
+	subroot->processed_tlist = tlist;
+
+	/* Add any newly added Vars to the child RelOptInfo. */
+	build_base_rel_tlists(subroot, tlist);
+
+	/*
+	 * Adjust all_baserels to replace the original target relation with the
+	 * child target relation.  Copy it before modifying though.
+	 */
+	subroot->all_baserels = adjust_child_relids(subroot->all_baserels,
+												1, &appinfo);
+
+	return subroot;
+}
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 3efa1bdc1a..f2ee83c81b 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1264,36 +1264,6 @@ get_relation_constraints(PlannerInfo *root,
 		}
 	}
 
-	/*
-	 * Append partition predicates, if any.
-	 *
-	 * For selects, partition pruning uses the parent table's partition bound
-	 * descriptor, instead of constraint exclusion which is driven by the
-	 * individual partition's partition constraint.
-	 */
-	if (enable_partition_pruning && root->parse->commandType != CMD_SELECT)
-	{
-		List	   *pcqual = RelationGetPartitionQual(relation);
-
-		if (pcqual)
-		{
-			/*
-			 * Run the partition quals through const-simplification similar to
-			 * check constraints.  We skip canonicalize_qual, though, because
-			 * partition quals should be in canonical form already; also,
-			 * since the qual is in implicit-AND format, we'd have to
-			 * explicitly convert it to explicit-AND format and back again.
-			 */
-			pcqual = (List *) eval_const_expressions(root, (Node *) pcqual);
-
-			/* Fix Vars to have the desired varno */
-			if (varno != 1)
-				ChangeVarNodes((Node *) pcqual, 1, varno, 0);
-
-			result = list_concat(result, pcqual);
-		}
-	}
-
 	table_close(relation, NoLock);
 
 	return result;
@@ -1417,40 +1387,15 @@ relation_excluded_by_constraints(PlannerInfo *root,
 	/*
 	 * Skip further tests, depending on constraint_exclusion.
 	 */
-	switch (constraint_exclusion)
-	{
-		case CONSTRAINT_EXCLUSION_OFF:
-
-			/*
-			 * Don't prune if feature turned off -- except if the relation is
-			 * a partition.  While partprune.c-style partition pruning is not
-			 * yet in use for all cases (update/delete is not handled), it
-			 * would be a UI horror to use different user-visible controls
-			 * depending on such a volatile implementation detail.  Therefore,
-			 * for partitioned tables we use enable_partition_pruning to
-			 * control this behavior.
-			 */
-			if (root->inhTargetKind == INHKIND_PARTITIONED)
-				break;
-			return false;
-
-		case CONSTRAINT_EXCLUSION_PARTITION:
-
-			/*
-			 * When constraint_exclusion is set to 'partition' we only handle
-			 * OTHER_MEMBER_RELs, or BASERELs in cases where the result target
-			 * is an inheritance parent or a partitioned table.
-			 */
-			if ((rel->reloptkind != RELOPT_OTHER_MEMBER_REL) &&
-				!(rel->reloptkind == RELOPT_BASEREL &&
-				  root->inhTargetKind != INHKIND_NONE &&
-				  rel->relid == root->parse->resultRelation))
-				return false;
-			break;
-
-		case CONSTRAINT_EXCLUSION_ON:
-			break;				/* always try to exclude */
-	}
+	if (constraint_exclusion == CONSTRAINT_EXCLUSION_OFF)
+		return false;
+	/*
+	 * When constraint_exclusion is set to 'partition' we only handle
+	 * OTHER_MEMBER_RELs.
+	 */
+	else if (constraint_exclusion == CONSTRAINT_EXCLUSION_PARTITION &&
+			 rel->reloptkind != RELOPT_OTHER_MEMBER_REL)
+		return false;
 
 	/*
 	 * Check for self-contradictory restriction clauses.  We dare not make
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index d3c477a542..b46947ffe9 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -325,9 +325,6 @@ struct PlannerInfo
 	Index		qual_security_level;	/* minimum security_level for quals */
 	/* Note: qual_security_level is zero if there are no securityQuals */
 
-	InheritanceKind inhTargetKind;	/* indicates if the target relation is an
-									 * inheritance child or partition or a
-									 * partitioned table */
 	bool		hasJoinRTEs;	/* true if any RTEs are RTE_JOIN kind */
 	bool		hasLateralRTEs; /* true if any RTEs are marked LATERAL */
 	bool		hasHavingQual;	/* true if havingQual was non-null */
@@ -348,9 +345,45 @@ struct PlannerInfo
 
 	/* Does this query modify any partition key columns? */
 	bool		partColsUpdated;
+
+	/*
+	 * The following fields are set during query planning portion of an
+	 * inherited UPDATE/DELETE operation.
+	 */
+	bool		inherited_update;	/* UPDATE/DELETE on inheritance parent? */
+
+	/*
+	 * This stores the original version of the query's targetlist that's
+	 * not modified by the planner.
+	 */
+	List	   *unexpanded_tlist;
+
+	/*
+	 * Array containing simple_rel_array_size elements, indexed by rangetable
+	 * index (entry 0 is wasted like simple_rel_array).  Only elements
+	 * corresponding to individual inheritance child target relations are
+	 * non-NULL.  Content of each PlannerInfo is mostly same as the parent
+	 * PlannerInfo, except for few fields such as the parse tree which is
+	 * a translated copy of the parent's parse tree, EC list which contain
+	 * child member expressions, etc.
+	 */
+	struct PlannerInfo **inh_target_child_roots;
+
+	/* List of RT indexes of child target relations. */
+	List	   *inh_target_child_rels;
+
+	/*
+	 * RelOptInfos corresponding to each child target rel.  For leaf children,
+	 * it's the RelOptInfo representing the output of make_rel_from_joinlist()
+	 * called with the parent rel in the original join tree replaced by a
+	 * given leaf child.  For non-leaf children, it's the baserel RelOptInfo
+	 * itself, left as a placeholder.
+	 */
+	List	   *inh_target_child_path_rels;
 };
 
 
+
 /*
  * In places where it's known that simple_rte_array[] must have been prepared
  * already, we just index into it to fetch RTEs.  In code that might be
diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h
index d2418f15cf..4f06cdd322 100644
--- a/src/include/optimizer/inherit.h
+++ b/src/include/optimizer/inherit.h
@@ -18,5 +18,6 @@
 
 
 extern void expand_inherited_tables(PlannerInfo *root);
+extern void add_inherited_target_child_roots(PlannerInfo *root);
 
 #endif							/* INHERIT_H */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 1b02b3b889..749c1f7f3d 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -132,6 +132,9 @@ typedef bool (*ec_matches_callback_type) (PlannerInfo *root,
 extern bool process_equivalence(PlannerInfo *root,
 					RestrictInfo **p_restrictinfo,
 					bool below_outer_join);
+extern EquivalenceMember *add_eq_member(EquivalenceClass *ec,
+			  Expr *expr, Relids relids, Relids nullable_relids,
+			  bool is_child, Oid datatype);
 extern Expr *canonicalize_ec_expression(Expr *expr,
 						   Oid req_type, Oid req_collation);
 extern void reconsider_outer_join_clauses(PlannerInfo *root);
diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out
index c55de5d476..c9f02c1fb0 100644
--- a/src/test/regress/expected/partition_join.out
+++ b/src/test/regress/expected/partition_join.out
@@ -1780,7 +1780,7 @@ WHERE EXISTS (
                Filter: (c IS NULL)
          ->  Nested Loop
                ->  Seq Scan on int4_tbl
-               ->  Subquery Scan on ss_1
+               ->  Subquery Scan on ss
                      ->  Limit
                            ->  Seq Scan on int8_tbl int8_tbl_1
    ->  Nested Loop Semi Join
@@ -1788,7 +1788,7 @@ WHERE EXISTS (
                Filter: (c IS NULL)
          ->  Nested Loop
                ->  Seq Scan on int4_tbl
-               ->  Subquery Scan on ss_2
+               ->  Subquery Scan on ss
                      ->  Limit
                            ->  Seq Scan on int8_tbl int8_tbl_2
 (28 rows)
-- 
2.17.2 (Apple Git-113)

