pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
From: Amit Langote <Langote_Amit_f8@lab.ntt.co.jp>
To: Tom Lane <tgl@sss.pgh.pa.us>
Cc: David Rowley <david.rowley@2ndquadrant.com>
Cc: Imai, Yoshikazu <imai.yoshikazu@jp.fujitsu.com>
Cc: Amit Langote <amitlangote09@gmail.com>
Cc: Alvaro Herrera <alvherre@2ndquadrant.com>
Cc: Pg Hackers <pgsql-hackers@postgresql.org>
Subject: Re: speeding up planning with partitions
Date: Tue, 19 Feb 2019 19:42:49 +0900
Message-ID: <0a70234e-b2e6-debc-2fb5-e8fd927caa00@lab.ntt.co.jp> (raw)
In-Reply-To: <7453.1550518972@sss.pgh.pa.us>
References: <8ea8bbb1-31ff-a469-02a7-281c3eba4bea@lab.ntt.co.jp>
	<201902011001.4v25uw6w6kb5@alvherre.pgsql>
	<CAKJS1f9LkQ=w4cq2_OaW05=mw8H_jQofWHiK0w3VVP-nSkUUmA@mail.gmail.com>
	<CA+HiwqFioAonMAgE0OWZeK=NEEOKmq-c=iYR2pkPOO=q6NxMkg@mail.gmail.com>
	<e85c4b41-80d6-1045-d934-feeca4a463ab@lab.ntt.co.jp>
	<0F97FA9ABBDBE54F91744A9B37151A5127865E@g01jpexmbkw24>
	<5d00eb35-375d-0672-0626-96bdb86829d0@lab.ntt.co.jp>
	<CAKJS1f9XAePd2+8F4=wtHKdDdckgKcSoXb-ZbU8Wbt86mhLxKA@mail.gmail.com>
	<b8a139b8-2f5d-6079-67b2-99957af3fc36@lab.ntt.co.jp>
	<7453.1550518972@sss.pgh.pa.us>

Thanks for the review.

On 2019/02/19 4:42, Tom Lane wrote:
> Amit Langote <Langote_Amit_f8@lab.ntt.co.jp> writes:
>> [ v22 patch set ]
> 
> I did some review of the 0002 patch.  I like the general idea,
> but there are a lot of details I don't much like.
> 
> Probably the biggest complaint is that I don't like what you did to the
> API of grouping_planner(): it's ugly and unprincipled.  Considering that
> only a tiny fraction of what grouping_planner does is actually relevant to
> inherited UPDATES/DELETEs, maybe we should factor that part out --- or,
> heavens, just duplicate some code --- so that inheritance_planner doesn't
> need to call grouping_planner at all.  (I see that you have another patch
> in the queue that proposes to fix this through a rather massive
> refactoring of grouping_planner, but I do not think I like that approach.
> grouping_planner does perhaps need refactoring, but I don't want to drive
> such work off an arbitrary-and-dubious requirement that it shouldn't call
> query_planner.)

OK, modified the patch to leave grouping_planner unchanged (except got rid
of inheritance_update argument).  Now inheritance_planner directly
modifies the paths produced by query_planner (per child target relation
that is), so that they have a PathTarget suitable for producing query's
top-level targetlist.  I refactored portion of
apply_scanjoin_target_to_paths() that does that work and
inheritance_planner calls it directly.

> Another point is that I don't like this division of labor between
> equivclass.c and appendinfo.c.  I don't like exposing add_eq_member
> globally --- that's just an invitation for code outside equivclass.c to
> break the fundamental invariants of ECs --- and I also think you've taught
> appendinfo.c far more than it ought to know about the innards of ECs.
> I'd suggest putting all of that logic into equivclass.c, with a name along
> the lines of translate_equivalence_class_to_child.  That would reverse the
> dependency, in that equivclass.c would now need to call
> adjust_appendrel_attrs ... but that function is already globally exposed.

That makes sense.  I've tried implementing EC translation to occur this
way in the updated patch.

> I don't much care for re-calling build_base_rel_tlists to add extra
> Vars to the appropriate relations; 99% of the work it does will be
> wasted, and with enough child rels you could run into an O(N^2)
> problem.  Maybe you could call add_vars_to_targetlist directly,
> since you know exactly what Vars you're adding?

Assuming you're talking about the build_base_rel_tlists() call in
create_inherited_target_child_root(), it's necessary because *all* tlist
Vars are new, because the targetlist has just been translated at that
point.  But maybe I missed your point?

By the way, later patch in the series will cause partition pruning to
occur before these child PlannerInfos are generated, so
create_inherited_target_child_root will be called only as many times as
there are un-pruned child target relations.

> What is the point of moving the calculation of all_baserels?  The earlier
> you construct that, the more likelihood that code will have to be written
> to modify it (like, say, what you had to put into
> create_inherited_target_child_root), and I do not see anything in this
> patch series that needs it to be available earlier.

all_baserels needs to be built in original PlannerInfo before child
PlannerInfos are constructed, so that they can simply copy it and have the
parent target baserel RT index in it replaced by child target baserel RT
index.  set_inherited_target_rel_sizes/pathlists use child PlannerInfos,
so all_baserels must be set in them just like it is in the original
PlannerInfo.

> The business with translated_exprs and child_target_exprs in
> set_inherited_target_rel_sizes seems to be dead code --- nothing is done
> with the list.  Should that be updating childrel->reltarget?  Or is that
> now done elsewhere, and if so, why isn't the elsewhere also handling
> childrel->joininfo?

Actually, child_target_exprs simply refers to childrel->reltarget->exprs,
so modifying it modifies the latter too, but I've found that confusing
myself.  So, I have removed it.

childrel->reltarget->exprs would only contain the targetlist Vars at this
point (added by build_base_rel_tlists called by
create_inherited_target_child_root), but translated_exprs will also
contain Vars that are referenced in WHERE clauses, which have not been
added to childrel->reltarget->exprs yet.  That's what's getting added to
childrel->reltarget in this code.

> +         * 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
> 
> What caller is that?  Perhaps we should change that rather than inventing
> a bogus value here?

OK, modified relevant Asserts in the callers.  One was set_rel_size and
other set_inherited_target_rel_sizes itself (may be called recursively).

> Couldn't inh_target_child_rels list be removed in favor of looking
> at the relid fields of the inh_target_child_path_rels entries?
> Having to keep those two lists in sync seems messy.

Don't like having two lists either, but inh_target_child_path_rels entries
can be RELOPT_JOINREL, so relid can be 0.

> If you're adding fields to PlannerInfo (or pretty much any other
> planner data structure) you should update outfuncs.c to print them
> if feasible.  Also, please avoid "add new fields at the end" syndrome.
> Put them where they logically belong.  For example, if
> inh_target_child_roots has to be the same length as simple_rel_array,
> it's not just confusing for it not to be near that field, it's
> outright dangerous: it increases the risk that somebody will forget
> to manipulate both fields.

I've moved these fields around in the struct definition.  Also, I've added
unexpanded_tlist and inh_target_child_rels to _outPlannerInfo.

Attached updated patches.  0001 that I'd previously posted is no longer
included, as I said in the other email.

Thanks,
Amit
From 81632e4088bf1b711c80ff07762ac72cd74d9327 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Fri, 8 Feb 2019 16:16:38 +0900
Subject: [PATCH 1/5] 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 child
PlannerInfo's from the informaton in the original parent PlannerInfo.
It creates them *after* it has generated enough state in the original,
PlannerInfo, 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 then generated separately for each child target
relation, after replacing the parent relation references in the
original join tree with the child relation references.  Join paths
corresponding to each child target are remembered for further
processing by stuffing stuffing the joinrel RelOptInfos in a list
in the original PlannerInfo.

So, 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.  Once join paths have been created for all unpruned
child target relations, back in inheritance_planner, the
aforementioned paths are adjusted so that they produce the query's
top-level target list that is expanded according to a given child
relation's descriptor.

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.)

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        | 449 ++++++++++++++++++++--
 src/backend/optimizer/path/equivclass.c      | 160 ++++++++
 src/backend/optimizer/plan/planmain.c        |  41 +-
 src/backend/optimizer/plan/planner.c         | 550 ++++++++++-----------------
 src/backend/optimizer/util/appendinfo.c      |  30 +-
 src/backend/optimizer/util/inherit.c         | 127 +++++++
 src/include/nodes/pathnodes.h                |  30 ++
 src/include/optimizer/inherit.h              |   1 +
 src/include/optimizer/paths.h                |   2 +
 src/test/regress/expected/partition_join.out |   4 +-
 12 files changed, 1002 insertions(+), 411 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 8314fce78f..1da28357ef 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -4439,24 +4439,11 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
 
    <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 65302fe65b..c5c08efac1 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2203,6 +2203,7 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node)
 	WRITE_NODE_FIELD(distinct_pathkeys);
 	WRITE_NODE_FIELD(sort_pathkeys);
 	WRITE_NODE_FIELD(processed_tlist);
+	WRITE_NODE_FIELD(unexpanded_tlist);
 	WRITE_NODE_FIELD(minmax_aggs);
 	WRITE_FLOAT_FIELD(total_table_pages, "%.0f");
 	WRITE_FLOAT_FIELD(tuple_fraction, "%.4f");
@@ -2218,6 +2219,7 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node)
 	WRITE_BITMAPSET_FIELD(curOuterRels);
 	WRITE_NODE_FIELD(curOuterParams);
 	WRITE_BOOL_FIELD(partColsUpdated);
+	WRITE_NODE_FIELD(inh_target_child_rels);
 }
 
 static void
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 0debac75c6..0a4729e646 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_inherited_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_inherited_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,35 @@ 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->parse->resultRelation > 0 &&
+		root->simple_rte_array[root->parse->resultRelation]->inh)
+	{
+		/*
+		 * 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;
 }
@@ -361,6 +369,8 @@ static void
 set_rel_size(PlannerInfo *root, RelOptInfo *rel,
 			 Index rti, RangeTblEntry *rte)
 {
+	bool	inherited_update = false;
+
 	if (rel->reloptkind == RELOPT_BASEREL &&
 		relation_excluded_by_constraints(root, rel, rte))
 	{
@@ -379,8 +389,18 @@ 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)
+		{
+			inherited_update = true;
+			set_inherited_target_rel_sizes(root, rel, rti, rte);
+		}
+		else
+			set_append_rel_size(root, rel, rti, rte);
 	}
 	else
 	{
@@ -457,8 +477,9 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
 
 	/*
 	 * We insist that all non-dummy rels have a nonzero rowcount estimate.
+	 * An exception is target relations that are inheritance parents.
 	 */
-	Assert(rel->rows > 0 || IS_DUMMY_REL(rel));
+	Assert(rel->rows > 0 || IS_DUMMY_REL(rel) || inherited_update);
 }
 
 /*
@@ -469,14 +490,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 (rti == root->parse->resultRelation)
+		{
+			inherited_update = true;
+			set_inherited_target_rel_pathlists(root, rel, rti, rte);
+		}
+		else
+			set_append_rel_pathlist(root, rel, rti, rte);
 	}
 	else
 	{
@@ -530,6 +563,19 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 	}
 
 	/*
+	 * If the relation is an inheritance parent and the query's result
+	 * relation, no paths have been generated for it yet, so skip the following
+	 * steps.
+	 */
+	if (inherited_update)
+	{
+#ifdef OPTIMIZER_DEBUG
+		debug_print_rel(root, rel);
+#endif
+		return;
+	}
+
+	/*
 	 * Allow a plugin to editorialize on the set of Paths for this base
 	 * relation.  It could add new paths (such as CustomPaths) by calling
 	 * add_path(), or add_partial_path() if parallel aware.  It could also
@@ -924,6 +970,207 @@ set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 }
 
 /*
+ * set_inherited_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_inherited_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;
+
+		/* 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);
+
+		if (did_pruning && !bms_is_member(appinfo->child_relid, live_children))
+		{
+			/* This partition was pruned; skip it. */
+			set_dummy_rel_pathlist(childrel);
+			continue;
+		}
+
+		/*
+		 * 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))
+		{
+			/*
+			 * 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.
+			 */
+			set_dummy_rel_pathlist(childrel);
+			continue;
+		}
+
+		/*
+		 * Add missing Vars to child's reltarget.
+		 *
+		 * create_inherited_target_child_root() would've added only those that
+		 * are needed due to being referenced 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);
+		foreach(lc, translated_exprs)
+		{
+			Expr *expr = lfirst(lc);
+
+			if (!list_member(childrel->reltarget->exprs, expr))
+				childrel->reltarget->exprs =
+								lappend(childrel->reltarget->exprs, expr);
+		}
+
+		childrel->has_eclass_joins = rel->has_eclass_joins;
+
+		subroot = root->inh_target_child_roots[childRTindex];
+
+		/* 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 is a partitioned table it may have been marked as a
+		 * dummy rel from having all its partitions pruned.
+		 */
+		if (IS_DUMMY_REL(childrel))
+			continue;
+
+		/* We have at least one live child. */
+		has_live_children = true;
+
+		Assert(childrel->rows > 0 || childRTE->inh);
+	}
+
+	/*
+	 * 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.
+	 */
+	if (!has_live_children)
+		set_dummy_rel_pathlist(rel);
+}
+
+/*
+ * set_inherited_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_inherited_target_rel_pathlists(PlannerInfo *root, RelOptInfo *rel,
+								   Index rti, RangeTblEntry *rte)
+{
+	int			parentRTindex = rti;
+	ListCell   *l;
+
+	/* 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;
+
+		/*
+		 * 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"
  *
@@ -2566,6 +2813,146 @@ 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;
+
+	/* 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);
+
+		/*
+		 * The following implements essentially the same Assert as in
+		 * make_one_rel, our caller.
+		 */
+		Assert(bms_equal(childjoinrel->relids, subroot->all_baserels));
+
+		/* 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 this child target rel.  inheritance_planner
+		 * perform remaining steps necessary fot these paths to produce the
+		 * desired targetlist.
+		 */
+		root->inh_target_child_path_rels =
+					lappend(root->inh_target_child_path_rels, childjoinrel);
+	}
+
+	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 23792508b7..11244fc57d 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -31,6 +31,11 @@
 #include "optimizer/restrictinfo.h"
 #include "utils/lsyscache.h"
 
+typedef struct translate_eq_classes_context
+{
+	PlannerInfo	  *root;
+	AppendRelInfo *appinfo;
+} translate_eq_classes_context;
 
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 			  Expr *expr, Relids relids, Relids nullable_relids,
@@ -64,6 +69,10 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 							 bool outer_on_left);
 static bool reconsider_full_join_clause(PlannerInfo *root,
 							RestrictInfo *rinfo);
+static EquivalenceClass * adjust_inherited_eq_class(PlannerInfo *root,
+					   EquivalenceClass *new_ec, AppendRelInfo *appinfo);
+static Node *translate_eq_classes_mutator(Node *node,
+							 translate_eq_classes_context *context);
 
 
 /*
@@ -2185,6 +2194,157 @@ add_child_rel_equivalences(PlannerInfo *root,
 	}
 }
 
+/*
+ * translate_eq_classes_for_child
+ *		This creates a copy of the list of EquivalenceClasses, each of
+ *		which is translated with the provided AppendRelInfo
+ *
+ * Whereas add_child_rel_equivalences simply adds child Vars to a given
+ * EquivalenceClasses if it contains the Vars of a given parent relation,
+ * this creates a whole new EquivalenceClasses copied from the original one
+ * and if it contains Vars of the parent relation, they're *replaced* with
+ * the corresponding child Vars.
+ */
+List *
+translate_eq_classes_for_child(PlannerInfo *root, List *eq_classes,
+							   AppendRelInfo *appinfo)
+{
+	translate_eq_classes_context context;
+
+	context.root = root;
+	context.appinfo = appinfo;
+
+	return (List *) translate_eq_classes_mutator((Node *) eq_classes,
+												 &context);
+}
+
+static Node *
+translate_eq_classes_mutator(Node *node,
+							 translate_eq_classes_context *context)
+{
+	if (node == NULL)
+		return NULL;
+
+	if (IsA(node, EquivalenceClass))
+	{
+		EquivalenceClass *ec = (EquivalenceClass *) node;
+		EquivalenceClass *new_ec = makeNode(EquivalenceClass);
+
+		/*
+		 * 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 might 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.
+		 */
+		if (bms_is_member(context->appinfo->parent_relid, ec->ec_relids))
+			new_ec = adjust_inherited_eq_class(context->root, new_ec,
+											   context->appinfo);
+
+		return (Node *) new_ec;
+	}
+
+	return expression_tree_mutator(node, translate_eq_classes_mutator,
+								   (void *) context);
+}
+
+/*
+ * adjust_inherited_eq_class
+ *		This translates members of EquivalenceClass such that references to
+ *		an inheritance parent relation are replaced by the references to
+ *		a given child relation
+ *
+ * This performs in-place updates of the members of the input struct, so the
+ * callers should've made a copy of the original EC first.
+ */
+static EquivalenceClass *
+adjust_inherited_eq_class(PlannerInfo *root, EquivalenceClass *new_ec,
+						  AppendRelInfo *appinfo)
+{
+	ListCell *lc;
+	RelOptInfo *parent_rel = find_base_rel(root, appinfo->parent_relid),
+			   *child_rel = find_base_rel(root, appinfo->child_relid);
+
+	/*
+	 * 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(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);
+		}
+	}
+
+	/* Now fix up EC's relids set. */
+	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 new_ec;
+}
+
 
 /*
  * generate_implied_equalities_for_column
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 3cedd01c98..c7f2323852 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -20,6 +20,7 @@
  */
 #include "postgres.h"
 
+#include "nodes/pathnodes.h"
 #include "optimizer/appendinfo.h"
 #include "optimizer/clauses.h"
 #include "optimizer/inherit.h"
@@ -29,6 +30,7 @@
 #include "optimizer/paths.h"
 #include "optimizer/placeholder.h"
 #include "optimizer/planmain.h"
+#include "optimizer/prep.h"
 
 
 /*
@@ -60,6 +62,7 @@ query_planner(PlannerInfo *root, List *tlist,
 	Query	   *parse = root->parse;
 	List	   *joinlist;
 	RelOptInfo *final_rel;
+	Index		rti;
 
 	/*
 	 * Init planner lists to empty.
@@ -260,14 +263,42 @@ 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 during planning for individual child targets.
+	 */
+	if (parse->resultRelation &&
+		root->simple_rte_array[parse->resultRelation]->inh)
+	{
+		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 1a58d733fa..cf64688e76 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -130,8 +130,7 @@ typedef struct
 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);
+static void grouping_planner(PlannerInfo *root, 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);
@@ -241,6 +240,10 @@ static void apply_scanjoin_target_to_paths(PlannerInfo *root,
 							   List *scanjoin_targets_contain_srfs,
 							   bool scanjoin_target_parallel_safe,
 							   bool tlist_same_exprs);
+static void apply_scanjoin_target_to_paths_guts(PlannerInfo *root,
+									RelOptInfo *rel,
+									PathTarget *scanjoin_target,
+									bool tlist_same_exprs);
 static void create_partitionwise_grouping_paths(PlannerInfo *root,
 									RelOptInfo *input_rel,
 									RelOptInfo *grouped_rel,
@@ -997,7 +1000,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, tuple_fraction);
 
 	/*
 	 * Capture the set of outer-level param IDs we have access to, for use in
@@ -1173,13 +1176,38 @@ 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_inherited_target_child_roots - this creates copies of PlannerInfo for
+ *	each child after query_planner has finished processing the join tree, with
+ *	translated copies of parsetree, eq_classes, etc.
+ *
+ *	set_inherited_target_rel_sizes - this sets size estimates for child
+ *	relations
+ *
+ *	set_inherited_target_rel_pathlists - this creates Paths for scanning
+ *	individual child relations
+ *
+ *	inheritance_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 adjust the Paths of individual children do that they
+ * 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 +1219,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 +1228,52 @@ 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_inherited_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);
-
-		if (rte->rtekind == RTE_SUBQUERY)
-			subqueryRTindexes = bms_add_member(subqueryRTindexes, rti);
-		rti++;
-	}
+	root->unexpanded_tlist = list_copy(root->parse->targetList);
+	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.
 	 */
-	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);
-		}
+		/*
+		 * Result path, even if dummy, must go into outer query's FINAL
+		 * upperrel.
+		 */
+		final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);
+		final_rel->reltarget = create_pathtarget(root, tlist);
+		mark_dummy_rel(final_rel);
+		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 +1281,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,66 +1289,34 @@ 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
+	 * adjust their Paths to get desired PathTarget.
 	 */
-	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 *childbaserel = find_base_rel(root, childRTindex);
+		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;
+		ListCell   *lc;
 		Path	   *subpath;
+		List	   *tlist = subroot->processed_tlist;
+		PathTarget *scanjoin_target;
+		bool		tlist_same_exprs;
 
-		/* 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;
 
@@ -1356,24 +1328,12 @@ inheritance_planner(PlannerInfo *root)
 			(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.
+		 * Ignore a partitioned child.  Instead, the paths of its children will
+		 * be added to subpaths.
 		 */
-		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,161 +1361,57 @@ 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. */
+		scanjoin_target = create_pathtarget(subroot, tlist);
+		tlist_same_exprs = equal(scanjoin_target->exprs,
+								 childjoinrel->reltarget->exprs);
+		apply_scanjoin_target_to_paths_guts(subroot, childjoinrel,
+											scanjoin_target,
+											tlist_same_exprs);
 
 		/*
+		 * Transfer paths from childjoinrel's pathlist to sub_final_rel.
 		 * Select cheapest path in case there's more than one.  We always run
 		 * modification queries to conclusion, so we care only for the
 		 * cheapest-total path.
 		 */
 		sub_final_rel = fetch_upper_rel(subroot, UPPERREL_FINAL, NULL);
+		foreach(lc, childjoinrel->pathlist)
+			add_path(sub_final_rel, (Path *) lfirst(lc));
 		set_cheapest(sub_final_rel);
 		subpath = sub_final_rel->cheapest_total_path;
 
 		/*
-		 * If this child rel was excluded by constraint exclusion, exclude it
-		 * from the result plan.
+		 * If the childbaserel belongs to a single FDW, so does the
+		 * corresponding sub_final_rel.
 		 */
-		if (IS_DUMMY_PATH(subpath))
-			continue;
+		sub_final_rel->serverid = childbaserel->serverid;
+		sub_final_rel->userid = childbaserel->userid;
+		sub_final_rel->useridiscurrent = childbaserel->useridiscurrent;
+		sub_final_rel->fdwroutine = childbaserel->fdwroutine;
 
 		/*
-		 * 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 there is an FDW that's responsible for all baserels of the query,
+		 * let it consider adding ForeignPaths.
 		 */
-		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)));
+		if (sub_final_rel->fdwroutine &&
+			sub_final_rel->fdwroutine->GetForeignUpperPaths)
+			sub_final_rel->fdwroutine->GetForeignUpperPaths(subroot,
+															UPPERREL_FINAL,
+															childjoinrel,
+															sub_final_rel,
+															NULL);
+
+		/* Let extensions possibly add some more paths to sub_final_rel. */
+		if (create_upper_paths_hook)
+			(*create_upper_paths_hook) (subroot, UPPERREL_FINAL, childjoinrel,
+										sub_final_rel, NULL);
 
 		/*
-		 * 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.
+		 * child rel cannot be empty, or inheritance_make_rel_from_joinlist
+		 * wouldn't have put it in the list to begin with.
 		 */
-		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);
@@ -1587,36 +1443,6 @@ inheritance_planner(PlannerInfo *root)
 	 */
 
 	/*
-	 * 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
 	 * ModifyTable do that.
@@ -1651,11 +1477,6 @@ inheritance_planner(PlannerInfo *root)
  * This function adds all required top-level processing to the scan/join
  * Path(s) produced by query_planner.
  *
- * If inheritance_update is true, we're being called from inheritance_planner
- * and should not include a ModifyTable step in the resulting Path(s).
- * (inheritance_planner will create a single ModifyTable node covering all the
- * target tables.)
- *
  * 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)
@@ -1673,8 +1494,7 @@ inheritance_planner(PlannerInfo *root)
  *--------------------
  */
 static void
-grouping_planner(PlannerInfo *root, bool inheritance_update,
-				 double tuple_fraction)
+grouping_planner(PlannerInfo *root, double tuple_fraction)
 {
 	Query	   *parse = root->parse;
 	List	   *tlist;
@@ -2172,11 +1992,8 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
 											  offset_est, count_est);
 		}
 
-		/*
-		 * If this is an INSERT/UPDATE/DELETE, and we're not being called from
-		 * inheritance_planner, add the ModifyTable node.
-		 */
-		if (parse->commandType != CMD_SELECT && !inheritance_update)
+		/* If this is an INSERT/UPDATE/DELETE, add the ModifyTable node. */
+		if (parse->commandType != CMD_SELECT)
 		{
 			Index		rootRelation;
 			List	   *withCheckOptionLists;
@@ -6914,7 +6731,6 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
 							   bool scanjoin_target_parallel_safe,
 							   bool tlist_same_exprs)
 {
-	ListCell   *lc;
 	PathTarget *scanjoin_target;
 	bool		is_dummy_rel = IS_DUMMY_REL(rel);
 
@@ -6985,53 +6801,8 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
 
 	/* Extract SRF-free scan/join target. */
 	scanjoin_target = linitial_node(PathTarget, scanjoin_targets);
-
-	/*
-	 * Adjust each input path.  If the tlist exprs are the same, we can just
-	 * inject the sortgroupref information into the existing pathtarget.
-	 * Otherwise, replace each path with a projection path that generates the
-	 * SRF-free scan/join target.  This can't change the ordering of paths
-	 * within rel->pathlist, so we just modify the list in place.
-	 */
-	foreach(lc, rel->pathlist)
-	{
-		Path	   *subpath = (Path *) lfirst(lc);
-		Path	   *newpath;
-
-		Assert(subpath->param_info == NULL);
-
-		if (tlist_same_exprs)
-			subpath->pathtarget->sortgrouprefs =
-				scanjoin_target->sortgrouprefs;
-		else
-		{
-			newpath = (Path *) create_projection_path(root, rel, subpath,
-													  scanjoin_target);
-			lfirst(lc) = newpath;
-		}
-	}
-
-	/* Same for partial paths. */
-	foreach(lc, rel->partial_pathlist)
-	{
-		Path	   *subpath = (Path *) lfirst(lc);
-		Path	   *newpath;
-
-		/* Shouldn't have any parameterized paths anymore */
-		Assert(subpath->param_info == NULL);
-
-		if (tlist_same_exprs)
-			subpath->pathtarget->sortgrouprefs =
-				scanjoin_target->sortgrouprefs;
-		else
-		{
-			newpath = (Path *) create_projection_path(root,
-													  rel,
-													  subpath,
-													  scanjoin_target);
-			lfirst(lc) = newpath;
-		}
-	}
+	apply_scanjoin_target_to_paths_guts(root, rel, scanjoin_target,
+										tlist_same_exprs);
 
 	/* Now fix things up if scan/join target contains SRFs */
 	if (root->parse->hasTargetSRFs)
@@ -7059,6 +6830,10 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
 			int			nappinfos;
 			List	   *child_scanjoin_targets = NIL;
 
+			/* Skip processing pruned partitions. */
+			if (child_rel == NULL)
+				continue;
+
 			/* Translate scan/join targets for this child. */
 			appinfos = find_appinfos_by_relids(root, child_rel->relids,
 											   &nappinfos);
@@ -7112,6 +6887,67 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
 }
 
 /*
+ * apply_scanjoin_target_to_paths_guts
+ *		Portion of apply_scanjoin_target_to_paths that's useful in some places
+ *		where full powers of the former are unnecessary
+ */
+static void
+apply_scanjoin_target_to_paths_guts(PlannerInfo *root,
+									RelOptInfo *rel,
+									PathTarget *scanjoin_target,
+									bool tlist_same_exprs)
+{
+	ListCell *lc;
+
+	/*
+	 * Adjust each input path.  If the tlist exprs are the same, we can just
+	 * inject the sortgroupref information into the existing pathtarget.
+	 * Otherwise, replace each path with a projection path that generates the
+	 * SRF-free scan/join target.  This can't change the ordering of paths
+	 * within rel->pathlist, so we just modify the list in place.
+	 */
+	foreach(lc, rel->pathlist)
+	{
+		Path	   *subpath = (Path *) lfirst(lc);
+		Path	   *newpath;
+
+		Assert(subpath->param_info == NULL);
+
+		if (tlist_same_exprs)
+			subpath->pathtarget->sortgrouprefs =
+				scanjoin_target->sortgrouprefs;
+		else
+		{
+			newpath = (Path *) create_projection_path(root, rel, subpath,
+													  scanjoin_target);
+			lfirst(lc) = newpath;
+		}
+	}
+
+	/* Same for partial paths. */
+	foreach(lc, rel->partial_pathlist)
+	{
+		Path	   *subpath = (Path *) lfirst(lc);
+		Path	   *newpath;
+
+		/* Shouldn't have any parameterized paths anymore */
+		Assert(subpath->param_info == NULL);
+
+		if (tlist_same_exprs)
+			subpath->pathtarget->sortgrouprefs =
+				scanjoin_target->sortgrouprefs;
+		else
+		{
+			newpath = (Path *) create_projection_path(root,
+													  rel,
+													  subpath,
+													  scanjoin_target);
+			lfirst(lc) = newpath;
+		}
+	}
+}
+
+/*
  * create_partitionwise_grouping_paths
  *
  * If the partition keys of input relation are part of the GROUP BY clause, all
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index ca6622ece9..8914c464d3 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,34 @@ adjust_appendrel_attrs_mutator(Node *node,
 											  context->appinfos);
 		return (Node *) phv;
 	}
+
+	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;
+	}
+
 	/* 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..1d16a9f2ff 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -21,11 +21,16 @@
 #include "miscadmin.h"
 #include "optimizer/appendinfo.h"
 #include "optimizer/inherit.h"
+#include "optimizer/optimizer.h"
+#include "optimizer/paths.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 +497,125 @@ 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,
+			   *tlist_vars;
+
+
+	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);
+
+	/*
+	 * Translate ECs.  This copies the list of ECs, translating the
+	 * relevant members of individual ECs to replace the references to
+	 * the parent RTE by child RTE, including the EC members.
+	 */
+	subroot->eq_classes =
+				translate_eq_classes_for_child(root, root->eq_classes,
+											   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., using
+	 * the child's TupleDesc.
+	 */
+	tlist = preprocess_targetlist(subroot);
+	subroot->processed_tlist = tlist;
+
+	/*
+	 * Add any newly added Vars to the child RelOptInfo.  They'll be needed
+	 * at the top level, so we pass {0} for where_needed.
+	 */
+	tlist_vars = pull_var_clause((Node *) tlist,
+								 PVC_RECURSE_AGGREGATES |
+								 PVC_RECURSE_WINDOWFUNCS |
+								 PVC_INCLUDE_PLACEHOLDERS);
+	add_vars_to_targetlist(root, tlist_vars, bms_make_singleton(0), false);
+
+	/*
+	 * Adjust all_baserels to replace the original target relation with the
+	 * child target relation.
+	 */
+	subroot->all_baserels = adjust_child_relids(subroot->all_baserels,
+												1, &appinfo);
+
+	return subroot;
+}
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index a008ae07da..2dc3059f16 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -217,6 +217,17 @@ struct PlannerInfo
 	struct AppendRelInfo **append_rel_array;
 
 	/*
+	 * inh_target_child_roots is same length as above arrays, indexed by
+	 * rangetable index (entry 0 is wasted like simple_rel_array).  Only
+	 * elements corresponding to 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;
+
+	/*
 	 * all_baserels is a Relids set of all base relids (but not "other"
 	 * relids) in the query; that is, the Relids identifier of the final join
 	 * we need to form.  This is computed in make_one_rel, just before we
@@ -310,6 +321,12 @@ struct PlannerInfo
 	 */
 	List	   *processed_tlist;
 
+	/*
+	 * This stores the original version of the query's targetlist that's not
+	 * modified by the planner.  Useful for target inheritance planning.
+	 */
+	List	   *unexpanded_tlist;
+
 	/* Fields filled during create_plan() for use in setrefs.c */
 	AttrNumber *grouping_map;	/* for GroupingFunc fixup */
 	List	   *minmax_aggs;	/* List of MinMaxAggInfos */
@@ -348,9 +365,22 @@ struct PlannerInfo
 
 	/* Does this query modify any partition key columns? */
 	bool		partColsUpdated;
+
+	/* 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 040335a7c5..e8caaaf643 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -154,6 +154,8 @@ extern void add_child_rel_equivalences(PlannerInfo *root,
 						   AppendRelInfo *appinfo,
 						   RelOptInfo *parent_rel,
 						   RelOptInfo *child_rel);
+extern List *translate_eq_classes_for_child(PlannerInfo *root,
+							   List *eq_classes, AppendRelInfo *appinfo);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 									   RelOptInfo *rel,
 									   ec_matches_callback_type callback,
diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out
index bbdc373782..fd85b5ead3 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.11.0


From c83f4ec48e035ce2a9fcb2c54a251ecb900f71e0 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Fri, 8 Feb 2019 16:21:59 +0900
Subject: [PATCH 2/5] Get rid of some useless code

* plancat.c no longer needs to load partition constraints, because
even update/delete now use partprune.c's facilities

* relation_excluded_by_constraints no longer needs to worry about
fielding update/delete on partitioned tablesm, which in turn means
InheritanceKind is no longer necessary
---
 src/backend/nodes/outfuncs.c              |  1 -
 src/backend/optimizer/plan/createplan.c   | 10 -----
 src/backend/optimizer/plan/planner.c      |  8 ----
 src/backend/optimizer/prep/prepjointree.c |  1 -
 src/backend/optimizer/util/plancat.c      | 73 ++++---------------------------
 src/include/nodes/pathnodes.h             | 15 -------
 6 files changed, 9 insertions(+), 99 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index c5c08efac1..475dc68d8a 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2209,7 +2209,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);
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 236f506cfb..7383eb9dfe 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -2054,12 +2054,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;
 
@@ -2216,12 +2211,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/planner.c b/src/backend/optimizer/plan/planner.c
index cf64688e76..6a6403d7fe 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -639,7 +639,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);
@@ -1321,13 +1320,6 @@ inheritance_planner(PlannerInfo *root)
 		parent_rte->securityQuals = NIL;
 
 		/*
-		 * Mark whether we're planning a query to a partitioned table or an
-		 * inheritance parent.
-		 */
-		subroot->inhTargetKind =
-			(rootRelation != 0) ? INHKIND_PARTITIONED : INHKIND_INHERITED;
-
-		/*
 		 * Ignore a partitioned child.  Instead, the paths of its children will
 		 * be added to subpaths.
 		 */
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index aebe162713..8aef1f6535 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/plancat.c b/src/backend/optimizer/util/plancat.c
index d6dc83ca80..d8c1981b4d 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1266,36 +1266,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;
@@ -1419,40 +1389,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 2dc3059f16..b219ff8806 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -82,18 +82,6 @@ typedef enum UpperRelationKind
 	/* NB: UPPERREL_FINAL must be last enum entry; it's used to size arrays */
 } UpperRelationKind;
 
-/*
- * This enum identifies which type of relation is being planned through the
- * inheritance planner.  INHKIND_NONE indicates the inheritance planner
- * was not used.
- */
-typedef enum InheritanceKind
-{
-	INHKIND_NONE,
-	INHKIND_INHERITED,
-	INHKIND_PARTITIONED
-} InheritanceKind;
-
 /*----------
  * PlannerGlobal
  *		Global information for planning/optimization
@@ -342,9 +330,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 */
-- 
2.11.0


From 039bd48dc21a6d1b59e4818dc5bcd4b791f9b209 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Fri, 26 Oct 2018 16:45:59 +0900
Subject: [PATCH 3/5] Lazy creation of RTEs for inheritance children

Currently, expand_inherited_tables called from subquery_planner opens
all inheritance child tables and adds them to the query's range
table and PlannerInfo in the form of AppendRelInfo's initially and
later RelOptInfo's.  For partitioning, that's pretty wasteful, because
it's possible to determine only the partitions that will need to be
scanned using partition pruning, which doesn't require opening the
partitions themselves.  However, the point at which
expand_inherited_tables is called currently is too early to perform
partition pruning.

This commit rearranges things within the planner so that
expand_inherited_tables can be called from query_planner after the
latter has finished setting up the information needed to perform
partition pruning.  However that means the PlannerInfo arrays that
store RangeTblEntry's, RelOptInfo's, and AppendRelInfo's need to
be expanded if inheritance expansion leads to adding more relations
to planning.  There are also consequences around targetlist
expansion.  For example, preprocess_targetlist cannot add the junk
columns entries needed for row mark handling until all of the
inheritance children have been determined, because different children
may need different types of junk columns to be added.  So, the
approach of late inheritance expansion required some interface tweaks
for preprocess_targetlist().

For partitioning, although we don't create a RangeTblEntry and
RelOptInfo for pruned partitions at make_one_rel time, partitionwise
join code relies on the fact that even though partitions may have
been pruned, they'd still own a RelOptInfo to handle the outer join
case where the pruned partition appears on the nullable side of join.
Partitionwise join code deals with that by allocating dummy
RelOptInfos for pruned partitions that are based mostly on their
parent's properties.

There are some regression test diffs:

 1. Caused by the fact that we no longer allocate a duplicate RT
    entry for a partitioned table in its role as child, as seen in
    the partition_aggregate.out test output.

 2. Those in postgres_fdw.out are caused by the fact that junk columns
    required for row marking are added to reltarget->exprs later than
    user columns, because the row marking junk columns aren't added
    until the inheritance is expanded which as of this commit is
    later than it used to be as noted above.
---
 contrib/postgres_fdw/expected/postgres_fdw.out    |  32 +-
 src/backend/nodes/outfuncs.c                      |   1 +
 src/backend/optimizer/path/allpaths.c             | 268 +------
 src/backend/optimizer/path/joinrels.c             |  71 +-
 src/backend/optimizer/plan/initsplan.c            |  49 --
 src/backend/optimizer/plan/planmain.c             |  10 +
 src/backend/optimizer/plan/planner.c              | 199 +++--
 src/backend/optimizer/plan/setrefs.c              |   6 +
 src/backend/optimizer/prep/preptlist.c            |  15 +-
 src/backend/optimizer/prep/prepunion.c            |   3 +
 src/backend/optimizer/util/appendinfo.c           |  45 +-
 src/backend/optimizer/util/inherit.c              | 841 ++++++++++++++++------
 src/backend/optimizer/util/plancat.c              |  47 +-
 src/backend/optimizer/util/relnode.c              | 107 +--
 src/backend/partitioning/partprune.c              |  44 +-
 src/include/nodes/pathnodes.h                     |  19 +-
 src/include/optimizer/appendinfo.h                |   7 +-
 src/include/optimizer/pathnode.h                  |   1 +
 src/include/optimizer/plancat.h                   |   4 +-
 src/include/optimizer/prep.h                      |   3 +-
 src/test/regress/expected/partition_aggregate.out |   4 +-
 21 files changed, 1041 insertions(+), 735 deletions(-)

diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 42108bd3d4..4ba406af71 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -7128,15 +7128,15 @@ select * from bar where f1 in (select f1 from foo) for update;
                      Output: bar2.f1, bar2.f2, bar2.ctid, bar2.*, bar2.tableoid
                      Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR UPDATE
          ->  Hash
-               Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+               Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                ->  HashAggregate
-                     Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                     Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                      Group Key: foo.f1
                      ->  Append
                            ->  Seq Scan on public.foo
-                                 Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                                 Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                            ->  Foreign Scan on public.foo2
-                                 Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1
+                                 Output: foo2.f1, foo2.ctid, foo2.*, foo2.tableoid
                                  Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
 (23 rows)
 
@@ -7166,15 +7166,15 @@ select * from bar where f1 in (select f1 from foo) for share;
                      Output: bar2.f1, bar2.f2, bar2.ctid, bar2.*, bar2.tableoid
                      Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR SHARE
          ->  Hash
-               Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+               Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                ->  HashAggregate
-                     Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                     Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                      Group Key: foo.f1
                      ->  Append
                            ->  Seq Scan on public.foo
-                                 Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                                 Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                            ->  Foreign Scan on public.foo2
-                                 Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1
+                                 Output: foo2.f1, foo2.ctid, foo2.*, foo2.tableoid
                                  Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
 (23 rows)
 
@@ -7203,15 +7203,15 @@ update bar set f2 = f2 + 100 where f1 in (select f1 from foo);
          ->  Seq Scan on public.bar
                Output: bar.f1, bar.f2, bar.ctid
          ->  Hash
-               Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+               Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                ->  HashAggregate
-                     Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                     Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                      Group Key: foo.f1
                      ->  Append
                            ->  Seq Scan on public.foo
-                                 Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                                 Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                            ->  Foreign Scan on public.foo2
-                                 Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1
+                                 Output: foo2.f1, foo2.ctid, foo2.*, foo2.tableoid
                                  Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
    ->  Hash Join
          Output: bar2.f1, (bar2.f2 + 100), bar2.f3, bar2.ctid, foo.ctid, foo.*, foo.tableoid
@@ -7221,15 +7221,15 @@ update bar set f2 = f2 + 100 where f1 in (select f1 from foo);
                Output: bar2.f1, bar2.f2, bar2.f3, bar2.ctid
                Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR UPDATE
          ->  Hash
-               Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+               Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                ->  HashAggregate
-                     Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                     Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                      Group Key: foo.f1
                      ->  Append
                            ->  Seq Scan on public.foo
-                                 Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                                 Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                            ->  Foreign Scan on public.foo2
-                                 Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1
+                                 Output: foo2.f1, foo2.ctid, foo2.*, foo2.tableoid
                                  Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
 (39 rows)
 
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 475dc68d8a..392bc8ff01 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2270,6 +2270,7 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node)
 	WRITE_BOOL_FIELD(consider_partitionwise_join);
 	WRITE_BITMAPSET_FIELD(top_parent_relids);
 	WRITE_NODE_FIELD(partitioned_child_rels);
+	WRITE_UINT_FIELD(inh_root_parent);
 }
 
 static void
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 0a4729e646..749d173770 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -45,7 +45,6 @@
 #include "optimizer/tlist.h"
 #include "parser/parse_clause.h"
 #include "parser/parsetree.h"
-#include "partitioning/partprune.h"
 #include "rewrite/rewriteManip.h"
 #include "utils/lsyscache.h"
 
@@ -145,9 +144,6 @@ static void subquery_push_qual(Query *subquery,
 static void recurse_push_qual(Node *setOp, Query *topquery,
 				  RangeTblEntry *rte, Index rti, Node *qual);
 static void remove_unused_subquery_outputs(Query *subquery, RelOptInfo *rel);
-static bool apply_child_basequals(PlannerInfo *root, RelOptInfo *rel,
-					  RelOptInfo *childrel,
-					  RangeTblEntry *childRTE, AppendRelInfo *appinfo);
 
 
 /*
@@ -390,6 +386,14 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
 	else if (rte->inh)
 	{
 		/*
+		 * expand_inherited_tables may have proved that the relation is empty.
+		 * For example, if it's a partitioned table with 0 partitions or all
+		 * of its partitions are pruned.  In that case nothing to do here.
+		 */
+		if (IS_DUMMY_REL(rel))
+			return;
+
+		/*
 		 * 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".
@@ -986,29 +990,12 @@ set_inherited_target_rel_sizes(PlannerInfo *root, RelOptInfo *rel,
 	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);
@@ -1025,47 +1012,23 @@ set_inherited_target_rel_sizes(PlannerInfo *root, RelOptInfo *rel,
 
 		childRTindex = appinfo->child_relid;
 		childRTE = root->simple_rte_array[childRTindex];
+		Assert(childRTE != NULL);
 
 		/*
-		 * The child rel's RelOptInfo was already created during
-		 * add_base_rels_to_query.
+		 * The child rel's RelOptInfo was created during
+		 * expand_inherited_tables().
 		 */
 		childrel = find_base_rel(root, childRTindex);
+		Assert(childrel != NULL);
 		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;
-		}
-
 		/*
-		 * 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.
+		 * Child relation may have been marked dummy if build_append_child_rel
+		 * found self-contradictory quals or quals that contradict its
+		 * constraints.
 		 */
-		if (!apply_child_basequals(root, rel, childrel, childRTE, appinfo))
-		{
-			/*
-			 * Some restriction clause reduced to constant FALSE or NULL after
-			 * substitution, so this child need not be scanned.
-			 */
-			set_dummy_rel_pathlist(childrel);
+		if (IS_DUMMY_REL(childrel))
 			continue;
-		}
-
-		if (relation_excluded_by_constraints(root, childrel, childRTE))
-		{
-			/*
-			 * This child need not be scanned, so we can omit it from the
-			 * appendrel.
-			 */
-			set_dummy_rel_pathlist(childrel);
-			continue;
-		}
 
 		/*
 		 * Add missing Vars to child's reltarget.
@@ -1192,8 +1155,6 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 	double	   *parent_attrsizes;
 	int			nattrs;
 	ListCell   *l;
-	Relids		live_children = NULL;
-	bool		did_pruning = false;
 
 	/* Guard against stack overflow due to overly deep inheritance tree. */
 	check_stack_depth();
@@ -1201,32 +1162,6 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 	Assert(IS_SIMPLE_REL(rel));
 
 	/*
-	 * Initialize partitioned_child_rels to contain this RT index.
-	 *
-	 * Note that during the set_append_rel_pathlist() phase, we will bubble up
-	 * the indexes of partitioned relations that appear down in the tree, so
-	 * that when we've created Paths for all the children, the root
-	 * partitioned table's list will contain all such indexes.
-	 */
-	if (rte->relkind == RELKIND_PARTITIONED_TABLE)
-		rel->partitioned_child_rels = list_make1_int(rti);
-
-	/*
-	 * 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;
-	}
-
-	/*
 	 * If this is a partitioned baserel, set the consider_partitionwise_join
 	 * flag; currently, we only consider partitionwise joins with the baserel
 	 * if its targetlist doesn't contain a whole-row Var.
@@ -1272,50 +1207,26 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 
 		childRTindex = appinfo->child_relid;
 		childRTE = root->simple_rte_array[childRTindex];
+		Assert(childRTE != NULL);
 
 		/*
-		 * The child rel's RelOptInfo was already created during
-		 * add_base_rels_to_query.
+		 * The child rel's RelOptInfo was created during
+		 * expand_inherited_tables().
 		 */
 		childrel = find_base_rel(root, childRTindex);
+		Assert(childrel != NULL);
 		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;
-		}
-
 		/*
-		 * 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.
+		 * Child relation may have been marked dummy if build_append_child_rel
+		 * found self-contradictory quals or quals that contradict its
+		 * constraints.
 		 */
-		if (!apply_child_basequals(root, rel, childrel, childRTE, appinfo))
-		{
-			/*
-			 * Some restriction clause reduced to constant FALSE or NULL after
-			 * substitution, so this child need not be scanned.
-			 */
-			set_dummy_rel_pathlist(childrel);
+		if (IS_DUMMY_REL(childrel))
 			continue;
-		}
-
-		if (relation_excluded_by_constraints(root, childrel, childRTE))
-		{
-			/*
-			 * This child need not be scanned, so we can omit it from the
-			 * appendrel.
-			 */
-			set_dummy_rel_pathlist(childrel);
-			continue;
-		}
 
 		/*
-		 * CE failed, so finish copying/modifying targetlist and join quals.
+		 * Copy/Modify targetlist.
 		 *
 		 * NB: the resulting childrel->reltarget->exprs may contain arbitrary
 		 * expressions, which otherwise would not occur in a rel's targetlist.
@@ -2849,6 +2760,7 @@ inheritance_make_rel_from_joinlist(PlannerInfo *root, List *joinlist)
 			continue;
 
 		childrel = find_base_rel(root, appinfo->child_relid);
+		Assert(childrel != NULL);
 
 		/* Ignore excluded/pruned children. */
 		if (IS_DUMMY_REL(childrel))
@@ -3944,134 +3856,6 @@ generate_partitionwise_join_paths(PlannerInfo *root, RelOptInfo *rel)
 	list_free(live_children);
 }
 
-/*
- * apply_child_basequals
- *		Populate childrel's quals based on rel's quals, translating them using
- *		appinfo.
- *
- * If any of the resulting clauses evaluate to false or NULL, we return false
- * and don't apply any quals.  Caller can mark the relation as a dummy rel in
- * this case, since it needn't be scanned.
- *
- * If any resulting clauses evaluate to true, they're unnecessary and we don't
- * apply then.
- */
-static bool
-apply_child_basequals(PlannerInfo *root, RelOptInfo *rel,
-					  RelOptInfo *childrel, RangeTblEntry *childRTE,
-					  AppendRelInfo *appinfo)
-{
-	List	   *childquals;
-	Index		cq_min_security;
-	ListCell   *lc;
-
-	/*
-	 * The child rel's targetlist might contain non-Var expressions, which
-	 * means that substitution into the quals could produce opportunities for
-	 * const-simplification, and perhaps even pseudoconstant quals. Therefore,
-	 * transform each RestrictInfo separately to see if it reduces to a
-	 * constant or pseudoconstant.  (We must process them separately to keep
-	 * track of the security level of each qual.)
-	 */
-	childquals = NIL;
-	cq_min_security = UINT_MAX;
-	foreach(lc, rel->baserestrictinfo)
-	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
-		Node	   *childqual;
-		ListCell   *lc2;
-
-		Assert(IsA(rinfo, RestrictInfo));
-		childqual = adjust_appendrel_attrs(root,
-										   (Node *) rinfo->clause,
-										   1, &appinfo);
-		childqual = eval_const_expressions(root, childqual);
-		/* check for flat-out constant */
-		if (childqual && IsA(childqual, Const))
-		{
-			if (((Const *) childqual)->constisnull ||
-				!DatumGetBool(((Const *) childqual)->constvalue))
-			{
-				/* Restriction reduces to constant FALSE or NULL */
-				return false;
-			}
-			/* Restriction reduces to constant TRUE, so drop it */
-			continue;
-		}
-		/* might have gotten an AND clause, if so flatten it */
-		foreach(lc2, make_ands_implicit((Expr *) childqual))
-		{
-			Node	   *onecq = (Node *) lfirst(lc2);
-			bool		pseudoconstant;
-
-			/* check for pseudoconstant (no Vars or volatile functions) */
-			pseudoconstant =
-				!contain_vars_of_level(onecq, 0) &&
-				!contain_volatile_functions(onecq);
-			if (pseudoconstant)
-			{
-				/* tell createplan.c to check for gating quals */
-				root->hasPseudoConstantQuals = true;
-			}
-			/* reconstitute RestrictInfo with appropriate properties */
-			childquals = lappend(childquals,
-								 make_restrictinfo((Expr *) onecq,
-												   rinfo->is_pushed_down,
-												   rinfo->outerjoin_delayed,
-												   pseudoconstant,
-												   rinfo->security_level,
-												   NULL, NULL, NULL));
-			/* track minimum security level among child quals */
-			cq_min_security = Min(cq_min_security, rinfo->security_level);
-		}
-	}
-
-	/*
-	 * In addition to the quals inherited from the parent, we might have
-	 * securityQuals associated with this particular child node. (Currently
-	 * this can only happen in appendrels originating from UNION ALL;
-	 * inheritance child tables don't have their own securityQuals, see
-	 * expand_inherited_rtentry().)	Pull any such securityQuals up into the
-	 * baserestrictinfo for the child.  This is similar to
-	 * process_security_barrier_quals() for the parent rel, except that we
-	 * can't make any general deductions from such quals, since they don't
-	 * hold for the whole appendrel.
-	 */
-	if (childRTE->securityQuals)
-	{
-		Index		security_level = 0;
-
-		foreach(lc, childRTE->securityQuals)
-		{
-			List	   *qualset = (List *) lfirst(lc);
-			ListCell   *lc2;
-
-			foreach(lc2, qualset)
-			{
-				Expr	   *qual = (Expr *) lfirst(lc2);
-
-				/* not likely that we'd see constants here, so no check */
-				childquals = lappend(childquals,
-									 make_restrictinfo(qual,
-													   true, false, false,
-													   security_level,
-													   NULL, NULL, NULL));
-				cq_min_security = Min(cq_min_security, security_level);
-			}
-			security_level++;
-		}
-		Assert(security_level <= root->qual_security_level);
-	}
-
-	/*
-	 * OK, we've got all the baserestrictinfo quals for this child.
-	 */
-	childrel->baserestrictinfo = childquals;
-	childrel->baserestrict_min_security = cq_min_security;
-
-	return true;
-}
-
 /*****************************************************************************
  *			DEBUG SUPPORT
  *****************************************************************************/
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index dfbbfdac6d..6f77d2a0f4 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -21,6 +21,7 @@
 #include "optimizer/joininfo.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
+#include "optimizer/tlist.h"
 #include "partitioning/partbounds.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
@@ -52,6 +53,9 @@ static SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root,
 						Relids left_relids, Relids right_relids);
 static int match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel,
 							 bool strict_op);
+static RelOptInfo *build_dummy_partition_rel(PlannerInfo *root,
+									RelOptInfo *parent,
+									int partidx);
 
 
 /*
@@ -1384,6 +1388,11 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 		AppendRelInfo **appinfos;
 		int			nappinfos;
 
+		if (IS_SIMPLE_REL(rel1) && child_rel1 == NULL)
+			child_rel1 = build_dummy_partition_rel(root, rel1, cnt_parts);
+		if (IS_SIMPLE_REL(rel1) && child_rel2 == NULL)
+			child_rel2 = build_dummy_partition_rel(root, rel2, cnt_parts);
+
 		/*
 		 * If a child table has consider_partitionwise_join=false, it means
 		 * that it's a dummy relation for which we skipped setting up tlist
@@ -1462,8 +1471,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;
 }
@@ -1674,3 +1689,55 @@ match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel, bool strict_op)
 
 	return -1;
 }
+
+/*
+ * build_dummy_partition_rel
+ *		Build a RelOptInfo and AppendRelInfo for a pruned partition
+ *
+ * This does not result in opening the relation or a range table entry being
+ * created.  Also, the RelOptInfo thus created is not stored anywhere else
+ * beside the parent's part_rels array.
+ *
+ * The only reason this exists is because partition-wise join, in some cases,
+ * needs a RelOptInfo to represent an empty relation that's on the nullable
+ * side of an outer join, so that a Path representing the outer join can be
+ * created.
+ */
+static RelOptInfo *
+build_dummy_partition_rel(PlannerInfo *root, RelOptInfo *parent, int partidx)
+{
+	RangeTblEntry *parentrte = root->simple_rte_array[parent->relid];
+	RelOptInfo *rel;
+
+	Assert(parent->part_rels[partidx] == NULL);
+
+	/* Create minimally valid-looking RelOptInfo with parent's relid. */
+	rel = makeNode(RelOptInfo);
+	rel->reloptkind = RELOPT_OTHER_MEMBER_REL;
+	rel->relid = parent->relid;
+	rel->relids = bms_copy(parent->relids);
+	if (parent->top_parent_relids)
+		rel->top_parent_relids = parent->top_parent_relids;
+	else
+		rel->top_parent_relids = bms_copy(parent->relids);
+	rel->reltarget = copy_pathtarget(parent->reltarget);
+	parent->part_rels[partidx] = rel;
+	mark_dummy_rel(rel);
+
+	/*
+	 * Now we'll need a (no-op) AppendRelInfo for parent, because we're
+	 * setting the dummy partition's relid to be same as the parent's.
+	 */
+	if (root->append_rel_array[parent->relid] == NULL)
+	{
+		AppendRelInfo *appinfo = make_append_rel_info(parent, parentrte,
+													  parent->tupdesc,
+													  parentrte->relid,
+													  parent->reltype,
+													  parent->relid);
+
+		root->append_rel_array[parent->relid] = appinfo;
+	}
+
+	return rel;
+}
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 2afc3f1dfe..5e8a8b1eee 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -419,7 +419,6 @@ void
 create_lateral_join_info(PlannerInfo *root)
 {
 	bool		found_laterals = false;
-	Relids		prev_parents PG_USED_FOR_ASSERTS_ONLY = NULL;
 	Index		rti;
 	ListCell   *lc;
 
@@ -618,54 +617,6 @@ create_lateral_join_info(PlannerInfo *root)
 				bms_add_member(brel2->lateral_referencers, rti);
 		}
 	}
-
-	/*
-	 * Lastly, propagate lateral_relids and lateral_referencers from appendrel
-	 * parent rels to their child rels.  We intentionally give each child rel
-	 * the same minimum parameterization, even though it's quite possible that
-	 * some don't reference all the lateral rels.  This is because any append
-	 * path for the parent will have to have the same parameterization for
-	 * every child anyway, and there's no value in forcing extra
-	 * reparameterize_path() calls.  Similarly, a lateral reference to the
-	 * parent prevents use of otherwise-movable join rels for each child.
-	 *
-	 * It's possible for child rels to have their own children, in which case
-	 * the topmost parent's lateral info must be propagated all the way down.
-	 * This code handles that case correctly so long as append_rel_list has
-	 * entries for child relationships before grandchild relationships, which
-	 * is an okay assumption right now, but we'll need to be careful to
-	 * preserve it.  The assertions below check for incorrect ordering.
-	 */
-	foreach(lc, root->append_rel_list)
-	{
-		AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
-		RelOptInfo *parentrel = root->simple_rel_array[appinfo->parent_relid];
-		RelOptInfo *childrel = root->simple_rel_array[appinfo->child_relid];
-
-		/*
-		 * If we're processing a subquery of a query with inherited target rel
-		 * (cf. inheritance_planner), append_rel_list may contain entries for
-		 * tables that are not part of the current subquery and hence have no
-		 * RelOptInfo.  Ignore them.  We can ignore dead rels, too.
-		 */
-		if (parentrel == NULL || !IS_SIMPLE_REL(parentrel))
-			continue;
-
-		/* Verify that children are processed before grandchildren */
-#ifdef USE_ASSERT_CHECKING
-		prev_parents = bms_add_member(prev_parents, appinfo->parent_relid);
-		Assert(!bms_is_member(appinfo->child_relid, prev_parents));
-#endif
-
-		/* OK, propagate info down */
-		Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
-		Assert(childrel->direct_lateral_relids == NULL);
-		childrel->direct_lateral_relids = parentrel->direct_lateral_relids;
-		Assert(childrel->lateral_relids == NULL);
-		childrel->lateral_relids = parentrel->lateral_relids;
-		Assert(childrel->lateral_referencers == NULL);
-		childrel->lateral_referencers = parentrel->lateral_referencers;
-	}
 }
 
 
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index c7f2323852..ae570e6abe 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -284,6 +284,16 @@ query_planner(PlannerInfo *root, List *tlist,
 	}
 
 	/*
+	 * Expand RT entries that represent inherited or partitioned tables.
+	 * This will perform partition pruning on partitioned tables in the
+	 * original range table and also recursively on any child partitioned
+	 * tables that were added by the expansion of the original parent(s).
+	 * As new entries are added to the range table, various arrays in the
+	 * PlannerInfo will be expanded accordingly.
+	 */
+	expand_inherited_tables(root);
+
+	/*
 	 * Add child subroots needed during planning for individual child targets.
 	 */
 	if (parse->resultRelation &&
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 6a6403d7fe..ce0ced792f 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -25,6 +25,7 @@
 #include "access/table.h"
 #include "access/xact.h"
 #include "catalog/pg_constraint.h"
+#include "catalog/pg_inherits.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "executor/executor.h"
@@ -131,6 +132,8 @@ 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, double tuple_fraction);
+static Path *create_single_target_modifytable_path(PlannerInfo *root,
+									  RelOptInfo *final_rel, Path *subpath);
 static grouping_sets_data *preprocess_grouping_sets(PlannerInfo *root);
 static List *remap_to_groupclause_idx(List *groupClause, List *gsets,
 						 int *tleref_to_colnum_map);
@@ -719,27 +722,24 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 		}
 		if (rte->lateral)
 			root->hasLateralRTEs = true;
+
+		/*
+		 * While at it, also update the inh status.  If the relation doesn't
+		 * or can't have any children, there is no point in letting inh be set
+		 * to true.  Note that we do this before processing rowmarks, so that
+		 * the correct information for setting isParent field of PlanRowMarks.
+		 */
+		if (rte->rtekind == RTE_RELATION)
+			rte->inh = rte->inh && has_subclass(rte->relid);
 	}
 
 	/*
 	 * Preprocess RowMark information.  We need to do this after subquery
-	 * pullup (so that all non-inherited RTEs are present) and before
-	 * inheritance expansion (so that the info is available for
-	 * expand_inherited_tables to examine and modify).
+	 * pullup (so that all non-inherited RTEs are present).
 	 */
 	preprocess_rowmarks(root);
 
 	/*
-	 * Expand any rangetable entries that are inheritance sets into "append
-	 * relations".  This can add entries to the rangetable, but they must be
-	 * plain RTE_RELATION entries, so it's OK (and marginally more efficient)
-	 * to do it after checking for joins and other special RTEs.  We must do
-	 * this after pulling up subqueries, else we'd fail to handle inherited
-	 * tables in subqueries.
-	 */
-	expand_inherited_tables(root);
-
-	/*
 	 * Set hasHavingQual to remember if HAVING clause is present.  Needed
 	 * because preprocess_expression will reduce a constant-true condition to
 	 * an empty qual list ... but "HAVING TRUE" is not a semantic no-op.
@@ -1247,7 +1247,9 @@ inheritance_planner(PlannerInfo *root)
 	 * of the parent.
 	 */
 	root->unexpanded_tlist = list_copy(root->parse->targetList);
-	tlist = preprocess_targetlist(root);
+
+	/* We haven't expanded inheritance yet, so pass false. */
+	tlist = preprocess_targetlist(root, false);
 	root->processed_tlist = tlist;
 	qp_extra.tlist = tlist;
 	qp_extra.activeWindows = NIL;
@@ -1257,16 +1259,47 @@ inheritance_planner(PlannerInfo *root)
 	/*
 	 * 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.
+	 * For regular inheritance case, it's possible that we only need to modify
+	 * the parent table.
 	 */
-	if (IS_DUMMY_REL(planned_rel))
+	parent_rte = planner_rt_fetch(top_parentRTindex, root);
+	if (!parent_rte->inh || IS_DUMMY_REL(planned_rel))
 	{
+		/* Top-level query targetlit determines final_rel's PathTarget. */
+		PathTarget *scanjoin_target = create_pathtarget(root, tlist);
+
 		/*
 		 * Result path, even if dummy, must go into outer query's FINAL
 		 * upperrel.
 		 */
 		final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);
-		final_rel->reltarget = create_pathtarget(root, tlist);
-		mark_dummy_rel(final_rel);
+		final_rel->reltarget = scanjoin_target;
+
+		/* Check if we need projection to produce final PathTarget? */
+		if (!IS_DUMMY_REL(planned_rel))
+		{
+			bool	tlist_exprs_same = equal(planned_rel->reltarget->exprs,
+											 scanjoin_target->exprs);
+
+			apply_scanjoin_target_to_paths_guts(root, planned_rel,
+												scanjoin_target,
+												tlist_exprs_same);
+
+			/* Add ModifyTable on top of each path and add to final_rel. */
+			foreach(lc1, planned_rel->pathlist)
+			{
+				Path   *path = (Path *) lfirst(lc1);
+
+				path = create_single_target_modifytable_path(root, final_rel,
+															 path);
+				add_path(final_rel, path);
+			}
+		}
+		else
+		{
+			/* Empty Result path. */
+			mark_dummy_rel(final_rel);
+		}
 		return;
 	}
 
@@ -1280,7 +1313,6 @@ 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 = planner_rt_fetch(top_parentRTindex, root);
 	if (parent_rte->relkind == RELKIND_PARTITIONED_TABLE)
 	{
 		nominalRelation = top_parentRTindex;
@@ -1627,8 +1659,11 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 				parse->groupClause = preprocess_groupclause(root, NIL);
 		}
 
-		/* Preprocess targetlist */
-		tlist = preprocess_targetlist(root);
+		/*
+		 * Preprocess targetlist.  Haven't expanded inheritance yet, so pass
+		 * false.
+		 */
+		tlist = preprocess_targetlist(root, false);
 
 		/*
 		 * We are now done hacking up the query's targetlist.  Most of the
@@ -1987,60 +2022,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 		/* If this is an INSERT/UPDATE/DELETE, add the ModifyTable node. */
 		if (parse->commandType != CMD_SELECT)
 		{
-			Index		rootRelation;
-			List	   *withCheckOptionLists;
-			List	   *returningLists;
-			List	   *rowMarks;
-
-			/*
-			 * If target is a partition root table, we need to mark the
-			 * ModifyTable node appropriately for that.
-			 */
-			if (rt_fetch(parse->resultRelation, parse->rtable)->relkind ==
-				RELKIND_PARTITIONED_TABLE)
-				rootRelation = parse->resultRelation;
-			else
-				rootRelation = 0;
-
-			/*
-			 * Set up the WITH CHECK OPTION and RETURNING lists-of-lists, if
-			 * needed.
-			 */
-			if (parse->withCheckOptions)
-				withCheckOptionLists = list_make1(parse->withCheckOptions);
-			else
-				withCheckOptionLists = NIL;
-
-			if (parse->returningList)
-				returningLists = list_make1(parse->returningList);
-			else
-				returningLists = NIL;
-
-			/*
-			 * 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 ModifyTable do that.
-			 */
-			if (parse->rowMarks)
-				rowMarks = NIL;
-			else
-				rowMarks = root->rowMarks;
-
-			path = (Path *)
-				create_modifytable_path(root, final_rel,
-										parse->commandType,
-										parse->canSetTag,
-										parse->resultRelation,
-										rootRelation,
-										false,
-										list_make1_int(parse->resultRelation),
-										list_make1(path),
-										list_make1(root),
-										withCheckOptionLists,
-										returningLists,
-										rowMarks,
-										parse->onConflict,
-										assign_special_exec_param(root));
+			path = create_single_target_modifytable_path(root, final_rel,
+														 path);
 		}
 
 		/* And shove it into final_rel */
@@ -2081,6 +2064,66 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 	/* Note: currently, we leave it to callers to do set_cheapest() */
 }
 
+static Path *
+create_single_target_modifytable_path(PlannerInfo *root,
+									  RelOptInfo *final_rel,
+									  Path *subpath)
+{
+	Query	   *parse = root->parse;
+	Index		rootRelation;
+	List	   *withCheckOptionLists;
+	List	   *returningLists;
+	List	   *rowMarks;
+
+	/*
+	 * If target is a partition root table, we need to mark the ModifyTable
+	 * node appropriately for that.
+	 */
+	if (rt_fetch(parse->resultRelation, parse->rtable)->relkind ==
+		RELKIND_PARTITIONED_TABLE)
+		rootRelation = parse->resultRelation;
+	else
+		rootRelation = 0;
+
+	/*
+	 * Set up the WITH CHECK OPTION and RETURNING lists-of-lists, if needed.
+	 */
+	if (parse->withCheckOptions)
+		withCheckOptionLists = list_make1(parse->withCheckOptions);
+	else
+		withCheckOptionLists = NIL;
+
+	if (parse->returningList)
+		returningLists = list_make1(parse->returningList);
+	else
+		returningLists = NIL;
+
+	/*
+	 * 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 ModifyTable do that.
+	 */
+	if (parse->rowMarks)
+		rowMarks = NIL;
+	else
+		rowMarks = root->rowMarks;
+
+	return (Path *) create_modifytable_path(root, final_rel,
+											parse->commandType,
+											parse->canSetTag,
+											parse->resultRelation,
+											rootRelation,
+											false,
+											list_make1_int(parse->resultRelation),
+											list_make1(subpath),
+											list_make1(root),
+											withCheckOptionLists,
+											returningLists,
+											rowMarks,
+											parse->onConflict,
+											assign_special_exec_param(root));
+}
+
 /*
  * Do preprocessing for groupingSets clause and related data.  This handles the
  * preliminary steps of expanding the grouping sets, organizing them into lists
@@ -2406,7 +2449,7 @@ preprocess_rowmarks(PlannerInfo *root)
 		newrc->allMarkTypes = (1 << newrc->markType);
 		newrc->strength = rc->strength;
 		newrc->waitPolicy = rc->waitPolicy;
-		newrc->isParent = false;
+		newrc->isParent = rte->inh;
 
 		prowmarks = lappend(prowmarks, newrc);
 	}
@@ -2431,7 +2474,7 @@ preprocess_rowmarks(PlannerInfo *root)
 		newrc->allMarkTypes = (1 << newrc->markType);
 		newrc->strength = LCS_NONE;
 		newrc->waitPolicy = LockWaitBlock;	/* doesn't matter */
-		newrc->isParent = false;
+		newrc->isParent = rte->rtekind == RTE_RELATION ? rte->inh : false;
 
 		prowmarks = lappend(prowmarks, newrc);
 	}
@@ -6988,6 +7031,10 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
 		RelOptInfo *child_grouped_rel;
 		RelOptInfo *child_partially_grouped_rel;
 
+		/* Skip processing pruned partitions. */
+		if (child_input_rel == NULL)
+			continue;
+
 		/* Input child rel must have a path */
 		Assert(child_input_rel->pathlist != NIL);
 
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 0213a37670..154ccda432 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -326,6 +326,12 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing)
 													  UPPERREL_FINAL, NULL)))
 					add_rtes_to_flat_rtable(rel->subroot, true);
 			}
+			/*
+			 * A NULL rel also means an unplanned subquery rte, so apply
+			 * flatten_unplanned_rtes.
+			 */
+			else
+				flatten_unplanned_rtes(glob, rte);
 		}
 		rti++;
 	}
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 5392d1a561..41a57d16b2 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -65,9 +65,13 @@ static List *expand_targetlist(List *tlist, int command_type,
  *
  * As a side effect, if there's an ON CONFLICT UPDATE clause, its targetlist
  * is also preprocessed (and updated in-place).
+ *
+ * inheritance_expanded specifies whether inheritance tables are expanded
+ * as of calling this function.  Caller must specify it because that affects
+ * which row marking related junk columns get added to the targetlist.
  */
 List *
-preprocess_targetlist(PlannerInfo *root)
+preprocess_targetlist(PlannerInfo *root, bool inheritance_expanded)
 {
 	Query	   *parse = root->parse;
 	int			result_relation = parse->resultRelation;
@@ -134,6 +138,15 @@ preprocess_targetlist(PlannerInfo *root)
 		if (rc->rti != rc->prti)
 			continue;
 
+		/*
+		 * For inheritance parent row marks, we defer adding junk columns
+		 * until we've added child row marks, because some children might
+		 * require different row mark types which will change the parent row
+		 * mark's allMarkTypes fields.
+		 */
+		if (rc->isParent && !inheritance_expanded)
+			continue;
+
 		if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY))
 		{
 			/* Need to fetch TID */
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 55eeb5127c..7bed914f35 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -31,12 +31,15 @@
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
+#include "optimizer/clauses.h"
 #include "optimizer/cost.h"
+#include "optimizer/optimizer.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
 #include "optimizer/planner.h"
 #include "optimizer/prep.h"
+#include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
 #include "parser/parse_coerce.h"
 #include "parser/parsetree.h"
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 8914c464d3..16cd65da71 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -33,10 +33,10 @@ typedef struct
 	AppendRelInfo **appinfos;
 } adjust_appendrel_attrs_context;
 
-static void make_inh_translation_list(Relation oldrelation,
-						  Relation newrelation,
-						  Index newvarno,
-						  List **translated_vars);
+static void make_inh_translation_list(TupleDesc old_tupdesc,
+						  TupleDesc new_tupdesc,
+						  Oid from_rel, Oid to_rel,
+						  Index newvarno, List **translated_vars);
 static Node *adjust_appendrel_attrs_mutator(Node *node,
 							   adjust_appendrel_attrs_context *context);
 static List *adjust_inherited_tlist(List *tlist,
@@ -48,18 +48,20 @@ static List *adjust_inherited_tlist(List *tlist,
  *	  Build an AppendRelInfo for the parent-child pair
  */
 AppendRelInfo *
-make_append_rel_info(Relation parentrel, Relation childrel,
-					 Index parentRTindex, Index childRTindex)
+make_append_rel_info(RelOptInfo *parent, RangeTblEntry *parentrte,
+					 TupleDesc childdesc, Oid childoid, Oid childtype,
+					 Index childRTindex)
 {
 	AppendRelInfo *appinfo = makeNode(AppendRelInfo);
 
-	appinfo->parent_relid = parentRTindex;
+	appinfo->parent_relid = parent->relid;
 	appinfo->child_relid = childRTindex;
-	appinfo->parent_reltype = parentrel->rd_rel->reltype;
-	appinfo->child_reltype = childrel->rd_rel->reltype;
-	make_inh_translation_list(parentrel, childrel, childRTindex,
-							  &appinfo->translated_vars);
-	appinfo->parent_reloid = RelationGetRelid(parentrel);
+	appinfo->parent_reltype = parent->reltype;
+	appinfo->child_reltype = childtype;
+	make_inh_translation_list(parent->tupdesc, childdesc,
+							  parentrte->relid, childoid,
+							  childRTindex, &appinfo->translated_vars);
+	appinfo->parent_reloid = parentrte->relid;
 
 	return appinfo;
 }
@@ -72,14 +74,11 @@ make_append_rel_info(Relation parentrel, Relation childrel,
  * For paranoia's sake, we match type/collation as well as attribute name.
  */
 static void
-make_inh_translation_list(Relation oldrelation, Relation newrelation,
-						  Index newvarno,
-						  List **translated_vars)
+make_inh_translation_list(TupleDesc old_tupdesc, TupleDesc new_tupdesc,
+						  Oid from_rel, Oid to_rel,
+						  Index newvarno, List **translated_vars)
 {
 	List	   *vars = NIL;
-	TupleDesc	old_tupdesc = RelationGetDescr(oldrelation);
-	TupleDesc	new_tupdesc = RelationGetDescr(newrelation);
-	Oid			new_relid = RelationGetRelid(newrelation);
 	int			oldnatts = old_tupdesc->natts;
 	int			newnatts = new_tupdesc->natts;
 	int			old_attno;
@@ -109,7 +108,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
 		 * When we are generating the "translation list" for the parent table
 		 * of an inheritance set, no need to search for matches.
 		 */
-		if (oldrelation == newrelation)
+		if (from_rel == to_rel)
 		{
 			vars = lappend(vars, makeVar(newvarno,
 										 (AttrNumber) (old_attno + 1),
@@ -135,10 +134,10 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
 		{
 			HeapTuple	newtup;
 
-			newtup = SearchSysCacheAttName(new_relid, attname);
+			newtup = SearchSysCacheAttName(to_rel, attname);
 			if (!newtup)
 				elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"",
-					 attname, RelationGetRelationName(newrelation));
+					 attname, get_rel_name(to_rel));
 			new_attno = ((Form_pg_attribute) GETSTRUCT(newtup))->attnum - 1;
 			ReleaseSysCache(newtup);
 
@@ -148,10 +147,10 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
 		/* Found it, check type and collation match */
 		if (atttypid != att->atttypid || atttypmod != att->atttypmod)
 			elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type",
-				 attname, RelationGetRelationName(newrelation));
+				 attname, get_rel_name(to_rel));
 		if (attcollation != att->attcollation)
 			elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's collation",
-				 attname, RelationGetRelationName(newrelation));
+				 attname, get_rel_name(to_rel));
 
 		vars = lappend(vars, makeVar(newvarno,
 									 (AttrNumber) (new_attno + 1),
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 1d16a9f2ff..492757fd8c 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -18,35 +18,47 @@
 #include "access/table.h"
 #include "catalog/partition.h"
 #include "catalog/pg_inherits.h"
+#include "catalog/pg_type.h"
 #include "miscadmin.h"
+#include "nodes/makefuncs.h"
 #include "optimizer/appendinfo.h"
+#include "optimizer/clauses.h"
 #include "optimizer/inherit.h"
 #include "optimizer/optimizer.h"
+#include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
+#include "optimizer/plancat.h"
 #include "optimizer/planmain.h"
 #include "optimizer/planner.h"
 #include "optimizer/prep.h"
+#include "optimizer/restrictinfo.h"
+#include "partitioning/partprune.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_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel,
+					  RangeTblEntry *rte, Index rti);
+static void expand_nonpartitioned_inherited_rtentry(PlannerInfo *root,
+						 RangeTblEntry *rte, Index rti, RelOptInfo *rel);
 static void expand_partitioned_rtentry(PlannerInfo *root,
 						   RangeTblEntry *parentrte,
-						   Index parentRTindex, Relation parentrel,
-						   PlanRowMark *top_parentrc, LOCKMODE lockmode,
-						   List **appinfos);
-static void expand_single_inheritance_child(PlannerInfo *root,
-								RangeTblEntry *parentrte,
-								Index parentRTindex, Relation parentrel,
-								PlanRowMark *top_parentrc, Relation childrel,
-								List **appinfos, RangeTblEntry **childrte_p,
-								Index *childRTindex_p);
+						   Index parentRTindex, RelOptInfo *parentrel);
+static RelOptInfo *add_inheritance_child_rel(PlannerInfo *root,
+						  RangeTblEntry *parentrte,
+						  Index parentRTindex, RelOptInfo *parentrel,
+						  PlanRowMark *top_parentrc, Relation childrel,
+						  RangeTblEntry **childrte_p, Index *childRTindex_p);
 static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
 					List *translated_vars);
-
+static RelOptInfo *build_inheritance_child_rel(PlannerInfo *root,
+					   RelOptInfo *parent,
+					   Index childRTindex);
+static List *add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc);
+static bool apply_child_basequals(PlannerInfo *root, RelOptInfo *rel,
+					  RelOptInfo *childrel,
+					  RangeTblEntry *childRTE, AppendRelInfo *appinfo);
 
 /*
  * expand_inherited_tables
@@ -54,37 +66,145 @@ static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
  *		into an "append relation".  At the conclusion of this process,
  *		the "inh" flag is set in all and only those RTEs that are append
  *		relation parents.
+ *
+ * Note that although we're calling the combined output of inheritance set
+ * relations an "append relation" here, the caller may not always want to
+ * combine the relations.  For example, if the parent of the inheritance
+ * set is the query's target relation, each child relation is processed
+ * on its own as the query's target relation.
  */
 void
 expand_inherited_tables(PlannerInfo *root)
 {
-	Index		nrtes;
+	int			orig_rtable_size;
 	Index		rti;
-	ListCell   *rl;
+
+	Assert(root->simple_rel_array_size > 0);
+	orig_rtable_size = root->simple_rel_array_size;
 
 	/*
 	 * expand_inherited_rtentry may add RTEs to parse->rtable. The function is
 	 * expected to recursively handle any RTEs that it creates with inh=true.
 	 * So just scan as far as the original end of the rtable list.
 	 */
-	nrtes = list_length(root->parse->rtable);
-	rl = list_head(root->parse->rtable);
-	for (rti = 1; rti <= nrtes; rti++)
+	for (rti = 1; rti < orig_rtable_size; rti++)
 	{
-		RangeTblEntry *rte = (RangeTblEntry *) lfirst(rl);
+		RelOptInfo *brel = root->simple_rel_array[rti];
+		RangeTblEntry *rte = root->simple_rte_array[rti];
 
-		expand_inherited_rtentry(root, rte, rti);
-		rl = lnext(rl);
+		/* there may be empty slots corresponding to non-baserel RTEs */
+		if (brel == NULL)
+			continue;
+
+		if (rte->inh)
+			expand_inherited_rtentry(root, brel, rte, rti);
 	}
 }
 
 /*
  * expand_inherited_rtentry
- *		Check whether a rangetable entry represents an inheritance set.
- *		If so, add entries for all the child tables to the query's
- *		rangetable, and build AppendRelInfo nodes for all the child tables
- *		and add them to root->append_rel_list.  If not, clear the entry's
- *		"inh" flag to prevent later code from looking for AppendRelInfos.
+ *		This initializes RelOptInfos for inheritance child relations if the
+ *		passed-in relation has any
+ *
+ * 'rel' is the parent relation, whose range table entry ('rte') has been
+ * marked to require adding children.  Parent could either be a subquery (if
+ * we flattened UNION ALL query) or a table that's known to have (or once had)
+ * inheritance children.  The latter consists of both regular inheritance
+ * parents and partitioned tables.
+ *
+ * For a subquery parent, there is not much to be done here because the
+ * children's RTEs are already present in the query, so we just initialize
+ * RelOptInfos for them.  Also, the AppendRelInfos for child subqueries
+ * have already been added.
+ *
+ * For tables, we need to add the children to the range table and initialize
+ * AppendRelInfos, RelOptInfos, and PlanRowMarks (if any) for them.  For
+ * a partitioned parent, we only add the children remaining after pruning.
+ * For regular inheritance parents, we find the children using
+ * find_all_inheritors and add all of them.
+ *
+ * If it turns out that there are no children, then we set rte->inh to false
+ * to let the caller know that only the parent table needs to be scanned.  The
+ * caller can accordingly switch to a non-Append path.  For a partitioned
+ * parent, that means an empty relation because parents themselves contain no
+ * data.
+ *
+ * For the regular inheritance case, the parent also gets another RTE with
+ * inh = false to represent it as a child to be scanned as part of the
+ * inheritance set.  The original RTE is considered to represent the whole
+ * inheritance set.
+ */
+static void
+expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel,
+						 RangeTblEntry *rte, Index rti)
+{
+	Assert(rte->inh);
+	/* Inheritance parent (partitioned or not) or UNION ALL parent subquery. */
+	Assert(rte->rtekind == RTE_RELATION || rte->rtekind == RTE_SUBQUERY);
+
+	/*
+	 * UNION ALL children already got RTEs and AppendRelInfos, so just build
+	 * RelOptInfos and return.
+	 *
+	 * It might be a bit odd that this code is in this, because there is
+	 * nothing to expand really.
+	 */
+	if (rte->rtekind == RTE_SUBQUERY)
+	{
+		ListCell   *l;
+
+		/*
+		 * We don't need to use expand_planner_arrays in this case, because
+		 * no new child RTEs are created.  setup_simple_rel_arrays() and
+		 * setup_append_rel_array would've considered these child RTEs when
+		 * allocating space for various arrays.
+		 */
+		foreach(l, root->append_rel_list)
+		{
+			AppendRelInfo *appinfo = lfirst(l);
+			Index		childRTindex = appinfo->child_relid;
+
+			if (appinfo->parent_relid != rti)
+				continue;
+
+			Assert(childRTindex < root->simple_rel_array_size);
+			Assert(root->simple_rte_array[childRTindex] != NULL);
+
+			/*
+			 * We set the correct value of baserestricinfo and
+			 * baserestrict_min_security below.
+			 */
+			root->simple_rel_array[childRTindex] =
+				build_inheritance_child_rel(root, rel, appinfo->child_relid);
+		}
+	}
+	else
+	{
+		Assert(rte->rtekind == RTE_RELATION);
+		Assert(has_subclass(rte->relid));
+
+		/*
+		 * The rewriter should already have obtained an appropriate lock on
+		 * each relation named in the query.  However, for each child relation
+		 * we add to the query, we must obtain an appropriate lock, because
+		 * this will be the first use of those relations in the
+		 * parse/rewrite/plan pipeline.  Child rels should use the same
+		 * lockmode as their parent.
+		 */
+		Assert(rte->rellockmode != NoLock);
+
+		if (rte->relkind == RELKIND_PARTITIONED_TABLE)
+			expand_partitioned_rtentry(root, rte, rti, rel);
+		else
+			expand_nonpartitioned_inherited_rtentry(root, rte, rti, rel);
+	}
+}
+
+/*
+ * expand_nonpartitioned_inherited_rtentry
+ *		Add entries for all the child tables to the query's rangetable, and
+ *		build AppendRelInfo nodes for all the child tables and add them to
+ *		root->append_rel_list.
  *
  * Note that the original RTE is considered to represent the whole
  * inheritance set.  The first of the generated RTEs is an RTE for the same
@@ -95,55 +215,35 @@ expand_inherited_tables(PlannerInfo *root)
  * regular inheritance, a parent RTE must always have at least two associated
  * AppendRelInfos: one corresponding to the parent table as a simple member of
  * inheritance set and one or more corresponding to the actual children.
- * Since a partitioned table is not scanned, it might have only one associated
- * AppendRelInfo.
  */
 static void
-expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
+expand_nonpartitioned_inherited_rtentry(PlannerInfo *root,
+										RangeTblEntry *rte,
+										Index rti,
+										RelOptInfo *rel)
 {
 	Oid			parentOID;
 	PlanRowMark *oldrc;
-	Relation	oldrelation;
-	LOCKMODE	lockmode;
+	LOCKMODE	lockmode = rte->rellockmode;
 	List	   *inhOIDs;
 	ListCell   *l;
+	int			num_children;
+	int			num_children_added = 0;
 
-	/* Does RT entry allow inheritance? */
-	if (!rte->inh)
-		return;
-	/* Ignore any already-expanded UNION ALL nodes */
-	if (rte->rtekind != RTE_RELATION)
-	{
-		Assert(rte->rtekind == RTE_SUBQUERY);
-		return;
-	}
-	/* Fast path for common case of childless table */
+	Assert(rte->rtekind == RTE_RELATION);
+	Assert(lockmode != NoLock);
 	parentOID = rte->relid;
-	if (!has_subclass(parentOID))
-	{
-		/* Clear flag before returning */
-		rte->inh = false;
-		return;
-	}
-
-	/*
-	 * The rewriter should already have obtained an appropriate lock on each
-	 * relation named in the query.  However, for each child relation we add
-	 * to the query, we must obtain an appropriate lock, because this will be
-	 * the first use of those relations in the parse/rewrite/plan pipeline.
-	 * Child rels should use the same lockmode as their parent.
-	 */
-	lockmode = rte->rellockmode;
 
 	/* Scan for all members of inheritance set, acquire needed locks */
 	inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);
 
 	/*
 	 * Check that there's at least one descendant, else treat as no-child
-	 * case.  This could happen despite above has_subclass() check, if table
-	 * once had a child but no longer does.
+	 * case.  This could happen despite has_subclass() check performed by
+	 * subquery_planner, if table once had a child but no longer does.
 	 */
-	if (list_length(inhOIDs) < 2)
+	num_children = list_length(inhOIDs);
+	if (num_children < 2)
 	{
 		/* Clear flag before returning */
 		rte->inh = false;
@@ -151,216 +251,221 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
 	}
 
 	/*
-	 * If parent relation is selected FOR UPDATE/SHARE, we need to mark its
-	 * PlanRowMark as isParent = true, and generate a new PlanRowMark for each
-	 * child.
+	 * If parent relation is selected FOR UPDATE/SHARE, preprocess_rowmarks
+	 * should've set isParent = true.  We'll generate a new PlanRowMark for
+	 * each child.
 	 */
 	oldrc = get_plan_rowmark(root->rowMarks, rti);
-	if (oldrc)
-		oldrc->isParent = true;
+	Assert(oldrc == NULL || oldrc->isParent);
 
 	/*
-	 * Must open the parent relation to examine its tupdesc.  We need not lock
-	 * it; we assume the rewriter already did.
+	 * Must expand PlannerInfo arrays by num_children before we can add
+	 * children.
 	 */
-	oldrelation = table_open(parentOID, NoLock);
+	Assert(num_children > 0);
+	expand_planner_arrays(root, num_children);
 
-	/* Scan the inheritance set and expand it */
-	if (RelationGetPartitionDesc(oldrelation) != NULL)
+	foreach(l, inhOIDs)
 	{
-		Assert(rte->relkind == RELKIND_PARTITIONED_TABLE);
-
-		/*
-		 * If this table has partitions, recursively expand them in the order
-		 * in which they appear in the PartitionDesc.  While at it, also
-		 * extract the partition key columns of all the partitioned tables.
-		 */
-		expand_partitioned_rtentry(root, rte, rti, oldrelation, oldrc,
-								   lockmode, &root->append_rel_list);
-	}
-	else
-	{
-		List	   *appinfos = NIL;
+		Oid			childOID = lfirst_oid(l);
+		Relation	newrelation;
 		RangeTblEntry *childrte;
 		Index		childRTindex;
 
+		/* Already locked above. */
+		newrelation = heap_open(childOID, NoLock);
+
 		/*
-		 * This table has no partitions.  Expand any plain inheritance
-		 * children in the order the OIDs were returned by
-		 * find_all_inheritors.
+		 * It is possible that the parent table has children that are temp
+		 * tables of other backends.  We cannot safely access such tables
+		 * (because of buffering issues), and the best thing to do seems
+		 * to be to silently ignore them.
 		 */
-		foreach(l, inhOIDs)
+		if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation))
 		{
-			Oid			childOID = lfirst_oid(l);
-			Relation	newrelation;
-
-			/* Open rel if needed; we already have required locks */
-			if (childOID != parentOID)
-				newrelation = table_open(childOID, NoLock);
-			else
-				newrelation = oldrelation;
-
-			/*
-			 * It is possible that the parent table has children that are temp
-			 * tables of other backends.  We cannot safely access such tables
-			 * (because of buffering issues), and the best thing to do seems
-			 * to be to silently ignore them.
-			 */
-			if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation))
-			{
-				table_close(newrelation, lockmode);
-				continue;
-			}
-
-			expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc,
-											newrelation,
-											&appinfos, &childrte,
-											&childRTindex);
-
-			/* Close child relations, but keep locks */
-			if (childOID != parentOID)
-				table_close(newrelation, NoLock);
+			table_close(newrelation, lockmode);
+			continue;
 		}
 
-		/*
-		 * If all the children were temp tables, pretend it's a
-		 * non-inheritance situation; we don't need Append node in that case.
-		 * The duplicate RTE we added for the parent table is harmless, so we
-		 * don't bother to get rid of it; ditto for the useless PlanRowMark
-		 * node.
-		 */
-		if (list_length(appinfos) < 2)
-			rte->inh = false;
-		else
-			root->append_rel_list = list_concat(root->append_rel_list,
-												appinfos);
+		(void) add_inheritance_child_rel(root, rte, rti, rel, oldrc,
+										 newrelation, &childrte,
+										 &childRTindex);
+		Assert(childrte != NULL);
+		/* All regular inheritance children are leaf children. */
+		Assert(!childrte->inh);
+		Assert(childRTindex > 0);
 
+		/* Close child relations, but keep locks */
+		heap_close(newrelation, NoLock);
+		num_children_added++;
 	}
 
-	table_close(oldrelation, NoLock);
+	/*
+	 * If all children, including the parent (as child rel), were
+	 * excluded, mark the parent rel as empty.  If all the children were temp
+	 * tables, pretend it's a non-inheritance situation; we don't need Append
+	 * node in that case.  The duplicate RTE we added for the parent table is
+	 * harmless, so we don't bother to get rid of it; ditto for the useless
+	 * PlanRowMark node.
+	 */
+	if (num_children_added == 0)
+		mark_dummy_rel(rel);
+	else if (num_children_added == 1)
+		rte->inh = false;
+
+	/*
+	 * Add junk columns needed by the row mark if any and also add the
+	 * relevant expressions to the root parent's reltarget.
+	 */
+	if (oldrc)
+	{
+		List   *tlist = add_rowmark_junk_columns(root, oldrc);
+
+		build_base_rel_tlists(root, tlist);
+	}
 }
 
 /*
  * expand_partitioned_rtentry
- *		Recursively expand an RTE for a partitioned table.
+ *		Prunes unnecessary partitions of a partitioned table and adds
+ *		remaining ones to the Query and the PlannerInfo
+ *
+ * Partitions are added to the query in order in which they are found in
+ * the parent's PartitionDesc.
+ *
+ * Note: even though only the unpruned partitions will be added to the
+ * resulting plan, this still locks *all* partitions via find_all_inheritors
+ * when this function is called for the root partitioned table.
  */
 static void
 expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
-						   Index parentRTindex, Relation parentrel,
-						   PlanRowMark *top_parentrc, LOCKMODE lockmode,
-						   List **appinfos)
+						   Index parentRTindex, RelOptInfo *parentrel)
 {
+	LOCKMODE	lockmode = parentrte->rellockmode;
+	PlanRowMark *rootrc = NULL;
 	int			i;
-	RangeTblEntry *childrte;
-	Index		childRTindex;
-	PartitionDesc partdesc = RelationGetPartitionDesc(parentrel);
+	Bitmapset  *partindexes;
+	Index		rootParentRTindex = parentrel->inh_root_parent > 0 ?
+									parentrel->inh_root_parent :
+									parentRTindex;
 
-	check_stack_depth();
-
-	/* A partitioned table should always have a partition descriptor. */
-	Assert(partdesc);
-
-	Assert(parentrte->inh);
+	/* If root partitioned table, lock *all* partitions in the tree. */
+	if (parentRTindex == rootParentRTindex)
+		(void) find_all_inheritors(parentrte->relid, lockmode, NULL);
 
 	/*
-	 * Note down whether any partition key cols are being updated. Though it's
-	 * the root partitioned table's updatedCols we are interested in, we
-	 * instead use parentrte to get the updatedCols. This is convenient
-	 * because parentrte already has the root partrel's updatedCols translated
-	 * to match the attribute ordering of parentrel.
+	 * Initialize partitioned_child_rels to contain this RT index.
+	 *
+	 * Note that during the set_append_rel_pathlist() phase, values of the
+	 * indexes of partitioned relations that appear down in the tree will be
+	 * bubbled up into root parent's list so that when we've created Paths for
+	 * all the children, the root table's list will contain all such indexes.
 	 */
-	if (!root->partColsUpdated)
-		root->partColsUpdated =
-			has_partition_attrs(parentrel, parentrte->updatedCols, NULL);
+	parentrel->partitioned_child_rels = list_make1_int(parentRTindex);
 
-	/* First expand the partitioned table itself. */
-	expand_single_inheritance_child(root, parentrte, parentRTindex, parentrel,
-									top_parentrc, parentrel,
-									appinfos, &childrte, &childRTindex);
+	/* Perform pruning. */
+	partindexes = prune_append_rel_partitions(parentrel);
+
+	/* Must expand PlannerInfo arrays before we can add children. */
+	if (bms_num_members(partindexes) > 0)
+		expand_planner_arrays(root, bms_num_members(partindexes));
 
 	/*
-	 * If the partitioned table has no partitions, treat this as the
-	 * non-inheritance case.
+	 * For partitioned tables, we also store the partition RelOptInfo
+	 * pointers in the parent's RelOptInfo.
 	 */
-	if (partdesc->nparts == 0)
-	{
-		parentrte->inh = false;
-		return;
-	}
+	parentrel->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) *
+												   parentrel->nparts);
 
-	for (i = 0; i < partdesc->nparts; i++)
+	rootrc = get_plan_rowmark(root->rowMarks, rootParentRTindex);
+	Assert(rootrc == NULL || rootrc->isParent);
+	i = -1;
+	while ((i = bms_next_member(partindexes, i)) >= 0)
 	{
-		Oid			childOID = partdesc->oids[i];
-		Relation	childrel;
+		Oid		childOID = parentrel->part_oids[i];
+		Relation	newrelation;
+		RelOptInfo *childrel;
+		RangeTblEntry *childrte;
+		Index		childRTindex;
 
 		/* Open rel; we already have required locks */
-		childrel = table_open(childOID, NoLock);
+		newrelation = table_open(childOID, NoLock);
+		Assert(!RELATION_IS_OTHER_TEMP(newrelation));
 
 		/*
-		 * Temporary partitions belonging to other sessions should have been
-		 * disallowed at definition, but for paranoia's sake, let's double
-		 * check.
+		 * A partitioned child table with 0 children is a dummy rel, so don't
+		 * bother creating planner objects for it.
 		 */
-		if (RELATION_IS_OTHER_TEMP(childrel))
-			elog(ERROR, "temporary relation from another session found as partition");
+		if (newrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
+			RelationGetPartitionDesc(newrelation)->nparts == 0)
+		{
+			heap_close(newrelation, NoLock);
+			continue;
+		}
 
-		expand_single_inheritance_child(root, parentrte, parentRTindex,
-										parentrel, top_parentrc, childrel,
-										appinfos, &childrte, &childRTindex);
+		childrel = add_inheritance_child_rel(root, parentrte, parentRTindex,
+											 parentrel, rootrc, newrelation,
+											 &childrte, &childRTindex);
+		Assert(childrel != NULL);
+		parentrel->part_rels[i] = childrel;
 
-		/* If this child is itself partitioned, recurse */
-		if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+		/* Close child relations, but keep locks */
+		table_close(newrelation, NoLock);
+
+		/* If the child is partitioned itself, expand it too. */
+		if (childrel->part_scheme)
+		{
+			Assert(childrte->inh);
 			expand_partitioned_rtentry(root, childrte, childRTindex,
-									   childrel, top_parentrc, lockmode,
-									   appinfos);
+									   childrel);
+		}
+	}
 
-		/* Close child relation, but keep locks */
-		table_close(childrel, NoLock);
+	/*
+	 * Add junk columns needed by the row mark if any and also add the
+	 * relevant expressions to the root parent's reltarget.
+	 */
+	if (rootrc)
+	{
+		List   *tlist = add_rowmark_junk_columns(root, rootrc);
+
+		build_base_rel_tlists(root, tlist);
 	}
 }
 
 /*
- * expand_single_inheritance_child
- *		Build a RangeTblEntry and an AppendRelInfo, if appropriate, plus
- *		maybe a PlanRowMark.
+ * add_inheritance_child_rel
+ *		Build a RangeTblEntry, an AppendRelInfo, a PlanRowMark, and finally
+ *		a RelOptInfo for an inheritance child relation.
  *
- * We now expand the partition hierarchy level by level, creating a
- * corresponding hierarchy of AppendRelInfos and RelOptInfos, where each
- * partitioned descendant acts as a parent of its immediate partitions.
- * (This is a difference from what older versions of PostgreSQL did and what
- * is still done in the case of table inheritance for unpartitioned tables,
- * where the hierarchy is flattened during RTE expansion.)
+ * The return value is the RelOptInfo that's added.
  *
  * PlanRowMarks still carry the top-parent's RTI, and the top-parent's
  * allMarkTypes field still accumulates values from all descendents.
  *
  * "parentrte" and "parentRTindex" are immediate parent's RTE and
  * RTI. "top_parentrc" is top parent's PlanRowMark.
- *
- * The child RangeTblEntry and its RTI are returned in "childrte_p" and
- * "childRTindex_p" resp.
  */
-static void
-expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
-								Index parentRTindex, Relation parentrel,
-								PlanRowMark *top_parentrc, Relation childrel,
-								List **appinfos, RangeTblEntry **childrte_p,
-								Index *childRTindex_p)
+static RelOptInfo *
+add_inheritance_child_rel(PlannerInfo *root, RangeTblEntry *parentrte,
+						  Index parentRTindex, RelOptInfo *parentrel,
+						  PlanRowMark *top_parentrc, Relation childrel,
+						  RangeTblEntry **childrte_p, Index *childRTindex_p)
 {
 	Query	   *parse = root->parse;
-	Oid			parentOID = RelationGetRelid(parentrel);
 	Oid			childOID = RelationGetRelid(childrel);
 	RangeTblEntry *childrte;
 	Index		childRTindex;
 	AppendRelInfo *appinfo;
+	RelOptInfo *childrelopt;
 
 	/*
 	 * Build an RTE for the child, and attach to query's rangetable list. We
 	 * copy most fields of the parent's RTE, but replace relation OID and
-	 * relkind, and set inh = false.  Also, set requiredPerms to zero since
-	 * all required permissions checks are done on the original RTE. Likewise,
-	 * set the child's securityQuals to empty, because we only want to apply
-	 * the parent's RLS conditions regardless of what RLS properties
+	 * relkind, and set inh appropriately.  Also, set requiredPerms to zero
+	 * since all required permissions checks are done on the original RTE.
+	 * Likewise, set the child's securityQuals to empty, because we only want
+	 * to apply the parent's RLS conditions regardless of what RLS properties
 	 * individual children may have.  (This is an intentional choice to make
 	 * inherited RLS work like regular permissions checks.) The parent
 	 * securityQuals will be propagated to children along with other base
@@ -370,49 +475,42 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	*childrte_p = childrte;
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
-	/* A partitioned child will need to be expanded further. */
-	if (childOID != parentOID &&
-		childrte->relkind == RELKIND_PARTITIONED_TABLE)
-		childrte->inh = true;
-	else
-		childrte->inh = false;
+	/*
+	 * A partitioned child will need to be expanded as an append parent
+	 * itself, so set its inh to true.
+	 */
+	childrte->inh = (childrte->relkind == RELKIND_PARTITIONED_TABLE);
 	childrte->requiredPerms = 0;
 	childrte->securityQuals = NIL;
 	parse->rtable = lappend(parse->rtable, childrte);
 	childRTindex = list_length(parse->rtable);
 	*childRTindex_p = childRTindex;
 
-	/*
-	 * We need an AppendRelInfo if paths will be built for the child RTE. If
-	 * childrte->inh is true, then we'll always need to generate append paths
-	 * for it.  If childrte->inh is false, we must scan it if it's not a
-	 * partitioned table; but if it is a partitioned table, then it never has
-	 * any data of its own and need not be scanned.
-	 */
-	if (childrte->relkind != RELKIND_PARTITIONED_TABLE || childrte->inh)
-	{
-		appinfo = make_append_rel_info(parentrel, childrel,
-									   parentRTindex, childRTindex);
-		*appinfos = lappend(*appinfos, appinfo);
+	/* Create an AppendRelInfo and add it to planner's global list. */
+	appinfo = make_append_rel_info(parentrel, parentrte,
+								   RelationGetDescr(childrel),
+								   RelationGetRelid(childrel),
+								   RelationGetForm(childrel)->reltype,
+								   childRTindex);
+	root->append_rel_list = lappend(root->append_rel_list, appinfo);
 
-		/*
-		 * Translate the column permissions bitmaps to the child's attnums (we
-		 * have to build the translated_vars list before we can do this). But
-		 * if this is the parent table, leave copyObject's result alone.
-		 *
-		 * Note: we need to do this even though the executor won't run any
-		 * permissions checks on the child RTE.  The insertedCols/updatedCols
-		 * bitmaps may be examined for trigger-firing purposes.
-		 */
-		if (childOID != parentOID)
-		{
-			childrte->selectedCols = translate_col_privs(parentrte->selectedCols,
-														 appinfo->translated_vars);
-			childrte->insertedCols = translate_col_privs(parentrte->insertedCols,
-														 appinfo->translated_vars);
-			childrte->updatedCols = translate_col_privs(parentrte->updatedCols,
-														appinfo->translated_vars);
-		}
+	/*
+	 * Translate the column permissions bitmaps to the child's attnums (we
+	 * have to build the translated_vars list before we can do this). But
+	 * if this is the parent table, leave copyObject's result alone.
+	 *
+	 * Note: we need to do this even though the executor won't run any
+	 * permissions checks on the child RTE.  The insertedCols/updatedCols
+	 * bitmaps may be examined for trigger-firing purposes.
+	 */
+	if (childrte->relid != parentrte->relid)
+	{
+		childrte->selectedCols = translate_col_privs(parentrte->selectedCols,
+													 appinfo->translated_vars);
+		childrte->insertedCols = translate_col_privs(parentrte->insertedCols,
+													 appinfo->translated_vars);
+		childrte->updatedCols = translate_col_privs(parentrte->updatedCols,
+													appinfo->translated_vars);
 	}
 
 	/*
@@ -444,6 +542,147 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 
 		root->rowMarks = lappend(root->rowMarks, childrc);
 	}
+
+	/*
+	 * Add the RelOptInfo.  Even though we may not really scan this relation
+	 * for reasons such as contradictory quals, we still need to create one,
+	 * because for every RTE in the query's range table, there must be an
+	 * accompanying RelOptInfo.
+	 */
+
+	/* First, store the RTE and appinfos into planner arrays. */
+	Assert(root->simple_rte_array[childRTindex] == NULL);
+	root->simple_rte_array[childRTindex] = childrte;
+	Assert(root->append_rel_array[childRTindex] == NULL);
+	root->append_rel_array[childRTindex] = appinfo;
+
+	childrelopt = build_inheritance_child_rel(root, parentrel, childRTindex);
+	Assert(childrelopt != NULL);
+
+	return childrelopt;
+}
+
+/*
+ *	build_inheritance_child_rel
+ *		Build a RelOptInfo for child relation of an inheritance set
+ *
+ * After creating the RelOptInfo for the given child RT index, it goes on to
+ * initialize some of its fields based on the parent RelOptInfo.
+ *
+ * If the quals in baserestrictinfo turn out to be self-contradictory,
+ * RelOptInfo is marked dummy before returning.
+ */
+static RelOptInfo *
+build_inheritance_child_rel(PlannerInfo *root,
+					   RelOptInfo *parent,
+					   Index childRTindex)
+{
+	RelOptInfo *childrel;
+	RangeTblEntry *childRTE = root->simple_rte_array[childRTindex];
+	AppendRelInfo *appinfo = root->append_rel_array[childRTindex];
+
+	/* Build the RelOptInfo. */
+	childrel = build_simple_rel(root, childRTindex, parent);
+
+	/*
+	 * Propagate lateral_relids and lateral_referencers from appendrel
+	 * parent rels to their child rels.  We intentionally give each child rel
+	 * the same minimum parameterization, even though it's quite possible that
+	 * some don't reference all the lateral rels.  This is because any append
+	 * path for the parent will have to have the same parameterization for
+	 * every child anyway, and there's no value in forcing extra
+	 * reparameterize_path() calls.  Similarly, a lateral reference to the
+	 * parent prevents use of otherwise-movable join rels for each child.
+	 *
+	 * It's possible for child rels to have their own children, in which case
+	 * the topmost parent's lateral info must be propagated all the way down.
+	 * That's ensured by having childrel be expanded via this same path.
+	 */
+	childrel->direct_lateral_relids = parent->direct_lateral_relids;
+	childrel->lateral_relids = parent->lateral_relids;
+	childrel->lateral_referencers = parent->lateral_referencers;
+
+	/*
+	 * We have to copy the parent's quals to the child, with appropriate
+	 * substitution of variables.  However, only the baserestrictinfo
+	 * quals are needed before we can check for constraint exclusion; so
+	 * do that first and then check to see if we can disregard this child.
+	 */
+	if (!apply_child_basequals(root, parent, 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);
+	}
+
+	return childrel;
+}
+
+/*
+ * add_rowmark_junk_columns
+ * 		Add necessary junk columns for rowmarked inheritance parent rel.
+ *
+ * These values are needed for locking of rels selected FOR UPDATE/SHARE, and
+ * to do EvalPlanQual rechecking.  See comments for PlanRowMark in
+ * plannodes.h.
+ */
+static List *
+add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc)
+{
+	List   *tlist = root->processed_tlist;
+	Var		   *var;
+	char		resname[32];
+	TargetEntry *tle;
+
+	if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY))
+	{
+		/* Need to fetch TID */
+		var = makeVar(rc->rti,
+					  SelfItemPointerAttributeNumber,
+					  TIDOID,
+					  -1,
+					  InvalidOid,
+					  0);
+		snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId);
+		tle = makeTargetEntry((Expr *) var,
+							  list_length(tlist) + 1,
+							  pstrdup(resname),
+							  true);
+		tlist = lappend(tlist, tle);
+	}
+	if (rc->allMarkTypes & (1 << ROW_MARK_COPY))
+	{
+		/* Need the whole row as a junk var */
+		var = makeWholeRowVar(root->simple_rte_array[rc->rti],
+							  rc->rti,
+							  0,
+							  false);
+		snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);
+		tle = makeTargetEntry((Expr *) var,
+							  list_length(tlist) + 1,
+							  pstrdup(resname),
+							  true);
+		tlist = lappend(tlist, tle);
+	}
+
+	/* For inheritance cases, always fetch the tableoid too. */
+	var = makeVar(rc->rti,
+				  TableOidAttributeNumber,
+				  OIDOID,
+				  -1,
+				  InvalidOid,
+				  0);
+	snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId);
+	tle = makeTargetEntry((Expr *) var,
+						  list_length(tlist) + 1,
+						  pstrdup(resname),
+						  true);
+	tlist = lappend(tlist, tle);
+
+	return tlist;
 }
 
 /*
@@ -597,7 +836,7 @@ create_inherited_target_child_root(PlannerInfo *root, AppendRelInfo *appinfo)
 	 * column, filling placeholder entries for dropped columns, etc., using
 	 * the child's TupleDesc.
 	 */
-	tlist = preprocess_targetlist(subroot);
+	tlist = preprocess_targetlist(subroot, true);
 	subroot->processed_tlist = tlist;
 
 	/*
@@ -619,3 +858,131 @@ create_inherited_target_child_root(PlannerInfo *root, AppendRelInfo *appinfo)
 
 	return subroot;
 }
+
+/*
+ * apply_child_basequals
+ *		Populate childrel's quals based on rel's quals, translating them using
+ *		appinfo.
+ *
+ * If any of the resulting clauses evaluate to false or NULL, we return false
+ * and don't apply any quals.  Caller can mark the relation as a dummy rel in
+ * this case, since it needn't be scanned.
+ *
+ * If any resulting clauses evaluate to true, they're unnecessary and we don't
+ * apply then.
+ */
+static bool
+apply_child_basequals(PlannerInfo *root, RelOptInfo *rel,
+					  RelOptInfo *childrel, RangeTblEntry *childRTE,
+					  AppendRelInfo *appinfo)
+{
+	List	   *childquals;
+	Index		cq_min_security;
+	ListCell   *lc;
+
+	/*
+	 * The child rel's targetlist might contain non-Var expressions, which
+	 * means that substitution into the quals could produce opportunities for
+	 * const-simplification, and perhaps even pseudoconstant quals. Therefore,
+	 * transform each RestrictInfo separately to see if it reduces to a
+	 * constant or pseudoconstant.  (We must process them separately to keep
+	 * track of the security level of each qual.)
+	 */
+	childquals = NIL;
+	cq_min_security = UINT_MAX;
+	foreach(lc, rel->baserestrictinfo)
+	{
+		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		Node	   *childqual;
+		ListCell   *lc2;
+
+		Assert(IsA(rinfo, RestrictInfo));
+		childqual = adjust_appendrel_attrs(root,
+										   (Node *) rinfo->clause,
+										   1, &appinfo);
+		childqual = eval_const_expressions(root, childqual);
+		/* check for flat-out constant */
+		if (childqual && IsA(childqual, Const))
+		{
+			if (((Const *) childqual)->constisnull ||
+				!DatumGetBool(((Const *) childqual)->constvalue))
+			{
+				/* Restriction reduces to constant FALSE or NULL */
+				return false;
+			}
+			/* Restriction reduces to constant TRUE, so drop it */
+			continue;
+		}
+		/* might have gotten an AND clause, if so flatten it */
+		foreach(lc2, make_ands_implicit((Expr *) childqual))
+		{
+			Node	   *onecq = (Node *) lfirst(lc2);
+			bool		pseudoconstant;
+
+			/* check for pseudoconstant (no Vars or volatile functions) */
+			pseudoconstant =
+				!contain_vars_of_level(onecq, 0) &&
+				!contain_volatile_functions(onecq);
+			if (pseudoconstant)
+			{
+				/* tell createplan.c to check for gating quals */
+				root->hasPseudoConstantQuals = true;
+			}
+			/* reconstitute RestrictInfo with appropriate properties */
+			childquals = lappend(childquals,
+								 make_restrictinfo((Expr *) onecq,
+												   rinfo->is_pushed_down,
+												   rinfo->outerjoin_delayed,
+												   pseudoconstant,
+												   rinfo->security_level,
+												   NULL, NULL, NULL));
+			/* track minimum security level among child quals */
+			cq_min_security = Min(cq_min_security, rinfo->security_level);
+		}
+	}
+
+	/*
+	 * In addition to the quals inherited from the parent, we might have
+	 * securityQuals associated with this particular child node. (Currently
+	 * this can only happen in appendrels originating from UNION ALL;
+	 * inheritance child tables don't have their own securityQuals, see
+	 * expand_inherited_rtentry().)	Pull any such securityQuals up into the
+	 * baserestrictinfo for the child.  This is similar to
+	 * process_security_barrier_quals() for the parent rel, except that we
+	 * can't make any general deductions from such quals, since they don't
+	 * hold for the whole appendrel.
+	 */
+	if (childRTE->securityQuals)
+	{
+		Index		security_level = 0;
+
+		foreach(lc, childRTE->securityQuals)
+		{
+			List	   *qualset = (List *) lfirst(lc);
+			ListCell   *lc2;
+
+			foreach(lc2, qualset)
+			{
+				Expr	   *qual = (Expr *) lfirst(lc2);
+
+				/* not likely that we'd see constants here, so no check */
+				childquals = lappend(childquals,
+									 make_restrictinfo(qual,
+													   true, false, false,
+													   security_level,
+													   NULL, NULL, NULL));
+				cq_min_security = Min(cq_min_security, security_level);
+			}
+			security_level++;
+		}
+		Assert(security_level <= root->qual_security_level);
+	}
+
+	/*
+	 * OK, we've got all the baserestrictinfo quals for this child.
+	 */
+	childrel->baserestrictinfo = childquals;
+	childrel->baserestrict_min_security = cq_min_security;
+
+	return true;
+}
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index d8c1981b4d..83c6a956b7 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -107,20 +107,20 @@ static void set_baserel_partition_key_exprs(Relation relation,
  * important for it.
  */
 void
-get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
-				  RelOptInfo *rel)
+get_relation_info(PlannerInfo *root, RangeTblEntry *rte, RelOptInfo *rel)
 {
 	Index		varno = rel->relid;
 	Relation	relation;
 	bool		hasindex;
 	List	   *indexinfos = NIL;
+	bool		inhparent = rte->inh;
 
 	/*
 	 * We need not lock the relation since it was already locked, either by
 	 * the rewriter or when expand_inherited_rtentry() added it to the query's
 	 * rangetable.
 	 */
-	relation = table_open(relationObjectId, NoLock);
+	relation = heap_open(rte->relid, NoLock);
 
 	/* Temporary and unlogged relations are inaccessible during recovery. */
 	if (!RelationNeedsWAL(relation) && RecoveryInProgress())
@@ -446,11 +446,32 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 	get_relation_foreign_keys(root, rel, relation, inhparent);
 
 	/*
-	 * Collect info about relation's partitioning scheme, if any. Only
-	 * inheritance parents may be partitioned.
+	 * Collect some additional information for inheritance parents.
 	 */
-	if (inhparent && relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
-		set_relation_partition_info(root, rel, relation);
+	if (inhparent)
+	{
+		/*
+		 * We'll need the TupleDesc when initializing the child relation.
+		 * A copy is being made because concurrent changes might drop
+		 * the relcache entry.  That's possible because ALTER TABLE
+		 * child_table NO INHERIT parent_table only requires an
+		 * AccessShareLock on parent_table.
+		 */
+		rel->tupdesc = CreateTupleDescCopy(RelationGetDescr(relation));
+		rel->reltype = RelationGetForm(relation)->reltype;
+
+		/*
+		 * If partitioned, also save the information of partitioning scheme,
+		 * and whether the query updates any of the partition key columns.
+		 */
+		if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+		{
+			set_relation_partition_info(root, rel, relation);
+			root->partColsUpdated |= has_partition_attrs(relation,
+														 rte->updatedCols,
+														 NULL);
+		}
+	}
 
 	table_close(relation, NoLock);
 
@@ -460,7 +481,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 	 * removing an index, or adding a hypothetical index to the indexlist.
 	 */
 	if (get_relation_info_hook)
-		(*get_relation_info_hook) (root, relationObjectId, inhparent, rel);
+		(*get_relation_info_hook) (root, rte->relid, rte->inh, rel);
 }
 
 /*
@@ -2027,16 +2048,20 @@ set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel,
 							Relation relation)
 {
 	PartitionDesc partdesc;
-	PartitionKey partkey;
 
 	Assert(relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
 
 	partdesc = RelationGetPartitionDesc(relation);
-	partkey = RelationGetPartitionKey(relation);
 	rel->part_scheme = find_partition_scheme(root, relation);
 	Assert(partdesc != NULL && rel->part_scheme != NULL);
-	rel->boundinfo = partition_bounds_copy(partdesc->boundinfo, partkey);
 	rel->nparts = partdesc->nparts;
+
+	/*
+	 * Since we must've taken a lock on the table, it's okay to simply copy
+	 * the pointers to relcache data here.
+	 */
+	rel->part_oids = partdesc->oids;
+	rel->boundinfo = partdesc->boundinfo;
 	set_baserel_partition_key_exprs(relation, rel);
 	rel->partition_qual = RelationGetPartitionQual(relation);
 }
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 4130514952..f6fca75677 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -132,6 +132,50 @@ setup_append_rel_array(PlannerInfo *root)
 }
 
 /*
+ * expand_planner_arrays
+ *		Expand the PlannerInfo arrays by add_size members and initialize the
+ *		the	newly added bytes with zero
+ */
+void
+expand_planner_arrays(PlannerInfo *root, int add_size)
+{
+	int		new_size;
+
+	Assert(add_size > 0);
+
+	new_size = root->simple_rel_array_size + add_size;
+
+	/* Expand various arrays and 0-initialize added bytes. */
+	root->simple_rte_array = (RangeTblEntry **)
+							repalloc(root->simple_rte_array,
+									 sizeof(RangeTblEntry *) * new_size);
+	MemSet(root->simple_rte_array + root->simple_rel_array_size,
+		   0, sizeof(RangeTblEntry *) * add_size);
+	root->simple_rel_array = (RelOptInfo **)
+								repalloc(root->simple_rel_array,
+										 sizeof(RelOptInfo *) * new_size);
+	MemSet(root->simple_rel_array + root->simple_rel_array_size,
+		   0, sizeof(RelOptInfo *) * add_size);
+
+	if (root->append_rel_array)
+	{
+		root->append_rel_array = (AppendRelInfo **)
+									repalloc(root->append_rel_array,
+									 sizeof(AppendRelInfo *) * new_size);
+		MemSet(root->append_rel_array + root->simple_rel_array_size,
+			   0, sizeof(AppendRelInfo *) * add_size);
+	}
+	else
+	{
+		root->append_rel_array = (AppendRelInfo **)
+									palloc0(sizeof(AppendRelInfo *) *
+											new_size);
+	}
+
+	root->simple_rel_array_size = new_size;
+}
+
+/*
  * build_simple_rel
  *	  Construct a new RelOptInfo for a base relation or 'other' relation.
  */
@@ -195,6 +239,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->joininfo = NIL;
 	rel->has_eclass_joins = false;
 	rel->consider_partitionwise_join = false;	/* might get changed later */
+	rel->top_parent_relids = NULL;	/* might be changed later */
 	rel->part_scheme = NULL;
 	rel->nparts = 0;
 	rel->boundinfo = NULL;
@@ -203,6 +248,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->partexprs = NULL;
 	rel->nullable_partexprs = NULL;
 	rel->partitioned_child_rels = NIL;
+	rel->inh_root_parent = 0; /* might be changed later */
 
 	/*
 	 * Pass top parent's relids down the inheritance hierarchy. If the parent
@@ -216,16 +262,25 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 			rel->top_parent_relids = parent->top_parent_relids;
 		else
 			rel->top_parent_relids = bms_copy(parent->relids);
+
+		/*
+		 * For inheritance child relations, we also set inh_root_parent.
+		 * Note that 'parent' might itself be a child (a sub-partitioned
+		 * partition), in which case we simply use its value of
+		 * inh_root_parent.
+		 */
+		if (parent->rtekind == RTE_RELATION)
+			rel->inh_root_parent = parent->inh_root_parent > 0 ?
+										parent->inh_root_parent :
+										parent->relid;
 	}
-	else
-		rel->top_parent_relids = NULL;
 
 	/* Check type of rtable entry */
 	switch (rte->rtekind)
 	{
 		case RTE_RELATION:
 			/* Table --- retrieve statistics from the system catalogs */
-			get_relation_info(root, rte->relid, rte->inh, rel);
+			get_relation_info(root, rte, rel);
 			break;
 		case RTE_SUBQUERY:
 		case RTE_FUNCTION:
@@ -273,52 +328,6 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 		root->qual_security_level = Max(root->qual_security_level,
 										list_length(rte->securityQuals));
 
-	/*
-	 * If this rel is an appendrel parent, recurse to build "other rel"
-	 * RelOptInfos for its children.  They are "other rels" because they are
-	 * not in the main join tree, but we will need RelOptInfos to plan access
-	 * to them.
-	 */
-	if (rte->inh)
-	{
-		ListCell   *l;
-		int			nparts = rel->nparts;
-		int			cnt_parts = 0;
-
-		if (nparts > 0)
-			rel->part_rels = (RelOptInfo **)
-				palloc(sizeof(RelOptInfo *) * nparts);
-
-		foreach(l, root->append_rel_list)
-		{
-			AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
-			RelOptInfo *childrel;
-
-			/* append_rel_list contains all append rels; ignore others */
-			if (appinfo->parent_relid != relid)
-				continue;
-
-			childrel = build_simple_rel(root, appinfo->child_relid,
-										rel);
-
-			/* Nothing more to do for an unpartitioned table. */
-			if (!rel->part_scheme)
-				continue;
-
-			/*
-			 * The order of partition OIDs in append_rel_list is the same as
-			 * the order in the PartitionDesc, so the order of part_rels will
-			 * also match the PartitionDesc.  See expand_partitioned_rtentry.
-			 */
-			Assert(cnt_parts < nparts);
-			rel->part_rels[cnt_parts] = childrel;
-			cnt_parts++;
-		}
-
-		/* We should have seen all the child partitions. */
-		Assert(cnt_parts == nparts);
-	}
-
 	return rel;
 }
 
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index 8c9721935d..472a6cd331 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -45,6 +45,8 @@
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/appendinfo.h"
+#include "optimizer/clauses.h"
+#include "optimizer/cost.h"
 #include "optimizer/optimizer.h"
 #include "optimizer/pathnode.h"
 #include "partitioning/partprune.h"
@@ -433,17 +435,23 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
 		 * is, not pruned already).
 		 */
 		subplan_map = (int *) palloc(nparts * sizeof(int));
+		memset(subplan_map, -1, nparts * sizeof(int));
 		subpart_map = (int *) palloc(nparts * sizeof(int));
+		memset(subpart_map, -1, nparts * sizeof(int));
 		present_parts = NULL;
 
 		for (i = 0; i < nparts; i++)
 		{
 			RelOptInfo *partrel = subpart->part_rels[i];
-			int			subplanidx = relid_subplan_map[partrel->relid] - 1;
-			int			subpartidx = relid_subpart_map[partrel->relid] - 1;
+			int			subplanidx;
+			int			subpartidx;
 
-			subplan_map[i] = subplanidx;
-			subpart_map[i] = subpartidx;
+			/* Skip processing pruned partitions. */
+			if (partrel == NULL)
+				continue;
+
+			subplan_map[i] = subplanidx = relid_subplan_map[partrel->relid] - 1;
+			subpart_map[i] = subpartidx = relid_subpart_map[partrel->relid] - 1;
 			if (subplanidx >= 0)
 			{
 				present_parts = bms_add_member(present_parts, i);
@@ -537,23 +545,20 @@ gen_partprune_steps(RelOptInfo *rel, List *clauses, bool *contradictory)
 
 /*
  * prune_append_rel_partitions
- *		Returns RT indexes of the minimum set of child partitions which must
- *		be scanned to satisfy rel's baserestrictinfo quals.
+ *		Returns indexes into rel->part_rels of the minimum set of child
+ *		partitions which must be scanned to satisfy rel's baserestrictinfo
+ *		quals.
  *
  * Callers must ensure that 'rel' is a partitioned table.
  */
-Relids
+Bitmapset *
 prune_append_rel_partitions(RelOptInfo *rel)
 {
-	Relids		result;
 	List	   *clauses = rel->baserestrictinfo;
 	List	   *pruning_steps;
 	bool		contradictory;
 	PartitionPruneContext context;
-	Bitmapset  *partindexes;
-	int			i;
 
-	Assert(clauses != NIL);
 	Assert(rel->part_scheme != NULL);
 
 	/* If there are no partitions, return the empty set */
@@ -561,6 +566,13 @@ prune_append_rel_partitions(RelOptInfo *rel)
 		return NULL;
 
 	/*
+	 * If pruning is disabled or if there are no clauses to prune with,
+	 * return all partitions.
+	 */
+	if (!enable_partition_pruning || clauses == NIL)
+		return bms_add_range(NULL, 0, rel->nparts - 1);
+
+	/*
 	 * Process clauses.  If the clauses are found to be contradictory, we can
 	 * return the empty set.
 	 */
@@ -587,15 +599,7 @@ prune_append_rel_partitions(RelOptInfo *rel)
 	context.evalexecparams = false;
 
 	/* Actual pruning happens here. */
-	partindexes = get_matching_partitions(&context, pruning_steps);
-
-	/* Add selected partitions' RT indexes to result. */
-	i = -1;
-	result = NULL;
-	while ((i = bms_next_member(partindexes, i)) >= 0)
-		result = bms_add_member(result, rel->part_rels[i]->relid);
-
-	return result;
+	return get_matching_partitions(&context, pruning_steps);
 }
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index b219ff8806..339b786f20 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -15,6 +15,7 @@
 #define PATHNODES_H
 
 #include "access/sdir.h"
+#include "access/tupdesc.h"
 #include "fmgr.h"
 #include "lib/stringinfo.h"
 #include "nodes/params.h"
@@ -365,7 +366,6 @@ struct PlannerInfo
 };
 
 
-
 /*
  * 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
@@ -718,11 +718,28 @@ typedef struct RelOptInfo
 	int			nparts;			/* number of partitions */
 	struct PartitionBoundInfoData *boundinfo;	/* Partition bounds */
 	List	   *partition_qual; /* partition constraint */
+	Oid		   *part_oids;		/* partition OIDs */
 	struct RelOptInfo **part_rels;	/* Array of RelOptInfos of partitions,
 									 * stored in the same order of bounds */
 	List	  **partexprs;		/* Non-nullable partition key expressions. */
 	List	  **nullable_partexprs; /* Nullable partition key expressions. */
 	List	   *partitioned_child_rels; /* List of RT indexes. */
+
+	/*
+	 * For inheritance children, this is the RT index of inheritance table
+	 * mentioned in the query from which this relation originated.
+	 * top_parent_relids cannot be used for this, because if the inheritance
+	 * root table is itself under UNION ALL, top_parent_relids contains the
+	 * RT index of UNION ALL parent subquery.
+	 */
+	Index		inh_root_parent;
+
+	/*
+	 * Set only if this is an inheritance parent relation.  This information
+	 * is needed when initializing the planning info for children.
+	 */
+	TupleDesc	tupdesc;		/* A "copy" of the table's tuple desriptor */
+	Oid			reltype;		/* Table's reltype */
 } RelOptInfo;
 
 /*
diff --git a/src/include/optimizer/appendinfo.h b/src/include/optimizer/appendinfo.h
index 790914c1b0..c0b8db9a62 100644
--- a/src/include/optimizer/appendinfo.h
+++ b/src/include/optimizer/appendinfo.h
@@ -17,9 +17,10 @@
 #include "nodes/pathnodes.h"
 #include "utils/relcache.h"
 
-extern AppendRelInfo *make_append_rel_info(Relation parentrel,
-					 Relation childrel,
-					 Index parentRTindex, Index childRTindex);
+extern AppendRelInfo *make_append_rel_info(RelOptInfo *parent,
+					 RangeTblEntry *parentrte,
+					 TupleDesc childdesc, Oid childoid, Oid childtype,
+					 Index childRTindex);
 extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node,
 					   int nappinfos, AppendRelInfo **appinfos);
 extern Node *adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 574bb85b50..3a803b3fd0 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -277,6 +277,7 @@ extern Path *reparameterize_path_by_child(PlannerInfo *root, Path *path,
  */
 extern void setup_simple_rel_arrays(PlannerInfo *root);
 extern void setup_append_rel_array(PlannerInfo *root);
+extern void expand_planner_arrays(PlannerInfo *root, int add_size);
 extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid,
 				 RelOptInfo *parent);
 extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid);
diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h
index c337f047cb..04731f532f 100644
--- a/src/include/optimizer/plancat.h
+++ b/src/include/optimizer/plancat.h
@@ -25,8 +25,8 @@ typedef void (*get_relation_info_hook_type) (PlannerInfo *root,
 extern PGDLLIMPORT get_relation_info_hook_type get_relation_info_hook;
 
 
-extern void get_relation_info(PlannerInfo *root, Oid relationObjectId,
-				  bool inhparent, RelOptInfo *rel);
+extern void get_relation_info(PlannerInfo *root, RangeTblEntry *rte,
+				  RelOptInfo *rel);
 
 extern List *infer_arbiter_indexes(PlannerInfo *root);
 
diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h
index a9b2c9026c..27de05ba3e 100644
--- a/src/include/optimizer/prep.h
+++ b/src/include/optimizer/prep.h
@@ -34,7 +34,8 @@ extern Relids get_relids_for_join(Query *query, int joinrelid);
 /*
  * prototypes for preptlist.c
  */
-extern List *preprocess_targetlist(PlannerInfo *root);
+extern List *preprocess_targetlist(PlannerInfo *root,
+							bool inheritance_expanded);
 
 extern PlanRowMark *get_plan_rowmark(List *rowmarks, Index rtindex);
 
diff --git a/src/test/regress/expected/partition_aggregate.out b/src/test/regress/expected/partition_aggregate.out
index 6bc106831e..1450cef057 100644
--- a/src/test/regress/expected/partition_aggregate.out
+++ b/src/test/regress/expected/partition_aggregate.out
@@ -144,7 +144,7 @@ SELECT c, sum(a) FROM pagg_tab WHERE 1 = 2 GROUP BY c;
            QUERY PLAN           
 --------------------------------
  HashAggregate
-   Group Key: pagg_tab.c
+   Group Key: c
    ->  Result
          One-Time Filter: false
 (4 rows)
@@ -159,7 +159,7 @@ SELECT c, sum(a) FROM pagg_tab WHERE c = 'x' GROUP BY c;
            QUERY PLAN           
 --------------------------------
  GroupAggregate
-   Group Key: pagg_tab.c
+   Group Key: c
    ->  Result
          One-Time Filter: false
 (4 rows)
-- 
2.11.0


From f3d0fa8072550b66739d445d9b5fb67c8ad1e923 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Mon, 15 Oct 2018 10:59:24 +0900
Subject: [PATCH 4/5] Teach planner to only process unpruned partitions

This adds a bitmapset field live_parts to RelOptInfo and stores
the partition indexes of non-dummy partitions in it, meaning it
contains indexes of only those partitions that have a non-NULL
RelOptInfo present in its parent's RelOptInfo's part_rels array.

This speeds up processing partitioned table's partitions compared
to going through the whole part_rels array in a number of places.
---
 src/backend/optimizer/path/joinrels.c |  3 +++
 src/backend/optimizer/plan/planner.c  | 18 +++++++++---------
 src/backend/optimizer/util/inherit.c  |  1 +
 src/backend/optimizer/util/relnode.c  |  3 +++
 src/backend/partitioning/partprune.c  | 18 +++++-------------
 src/include/nodes/pathnodes.h         |  4 ++++
 6 files changed, 25 insertions(+), 22 deletions(-)

diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 6f77d2a0f4..fdbf7689ed 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -1452,6 +1452,9 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 		populate_joinrel_with_paths(root, child_rel1, child_rel2,
 									child_joinrel, child_sjinfo,
 									child_restrictlist);
+		if (!IS_DUMMY_REL(child_joinrel))
+			joinrel->live_parts = bms_add_member(joinrel->live_parts,
+												 cnt_parts);
 	}
 }
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index ce0ced792f..2feb5d5b2d 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -6857,7 +6857,9 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
 		List	   *live_children = NIL;
 
 		/* Adjust each partition. */
-		for (partition_idx = 0; partition_idx < rel->nparts; partition_idx++)
+		partition_idx = -1;
+		while ((partition_idx = bms_next_member(rel->live_parts,
+												partition_idx)) >= 0)
 		{
 			RelOptInfo *child_rel = rel->part_rels[partition_idx];
 			ListCell   *lc;
@@ -6865,9 +6867,7 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
 			int			nappinfos;
 			List	   *child_scanjoin_targets = NIL;
 
-			/* Skip processing pruned partitions. */
-			if (child_rel == NULL)
-				continue;
+			Assert(child_rel != NULL);
 
 			/* Translate scan/join targets for this child. */
 			appinfos = find_appinfos_by_relids(root, child_rel->relids,
@@ -7009,7 +7009,6 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
 									PartitionwiseAggregateType patype,
 									GroupPathExtraData *extra)
 {
-	int			nparts = input_rel->nparts;
 	int			cnt_parts;
 	List	   *grouped_live_children = NIL;
 	List	   *partially_grouped_live_children = NIL;
@@ -7021,7 +7020,9 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
 		   partially_grouped_rel != NULL);
 
 	/* Add paths for partitionwise aggregation/grouping. */
-	for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++)
+	cnt_parts = -1;
+	while ((cnt_parts = bms_next_member(input_rel->live_parts,
+										cnt_parts)) >= 0)
 	{
 		RelOptInfo *child_input_rel = input_rel->part_rels[cnt_parts];
 		PathTarget *child_target = copy_pathtarget(target);
@@ -7031,9 +7032,8 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
 		RelOptInfo *child_grouped_rel;
 		RelOptInfo *child_partially_grouped_rel;
 
-		/* Skip processing pruned partitions. */
-		if (child_input_rel == NULL)
-			continue;
+		/* A live partition must have a RelOptInfo. */
+		Assert(child_input_rel != NULL);
 
 		/* Input child rel must have a path */
 		Assert(child_input_rel->pathlist != NIL);
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 492757fd8c..6e9564d0ce 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -365,6 +365,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
 
 	/* Perform pruning. */
 	partindexes = prune_append_rel_partitions(parentrel);
+	parentrel->live_parts = partindexes;
 
 	/* Must expand PlannerInfo arrays before we can add children. */
 	if (bms_num_members(partindexes) > 0)
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index f6fca75677..03c9ded294 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -1743,6 +1743,9 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
 		joinrel->partexprs[cnt] = partexpr;
 		joinrel->nullable_partexprs[cnt] = nullable_partexpr;
 	}
+
+	/* Partitions will be added by try_partitionwise_join. */
+	joinrel->live_parts = NULL;
 }
 
 /*
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index 472a6cd331..babb7d3406 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -438,29 +438,21 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
 		memset(subplan_map, -1, nparts * sizeof(int));
 		subpart_map = (int *) palloc(nparts * sizeof(int));
 		memset(subpart_map, -1, nparts * sizeof(int));
-		present_parts = NULL;
+		present_parts = bms_copy(subpart->live_parts);
 
-		for (i = 0; i < nparts; i++)
+		i = -1;
+		while ((i = bms_next_member(present_parts, i)) >= 0)
 		{
 			RelOptInfo *partrel = subpart->part_rels[i];
 			int			subplanidx;
 			int			subpartidx;
 
-			/* Skip processing pruned partitions. */
-			if (partrel == NULL)
-				continue;
-
+			Assert(partrel != NULL);
 			subplan_map[i] = subplanidx = relid_subplan_map[partrel->relid] - 1;
 			subpart_map[i] = subpartidx = relid_subpart_map[partrel->relid] - 1;
+			/* Record finding this subplan */
 			if (subplanidx >= 0)
-			{
-				present_parts = bms_add_member(present_parts, i);
-
-				/* Record finding this subplan  */
 				subplansfound = bms_add_member(subplansfound, subplanidx);
-			}
-			else if (subpartidx >= 0)
-				present_parts = bms_add_member(present_parts, i);
 		}
 
 		pinfo = makeNode(PartitionedRelPruneInfo);
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 339b786f20..c3103370ca 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -721,6 +721,10 @@ typedef struct RelOptInfo
 	Oid		   *part_oids;		/* partition OIDs */
 	struct RelOptInfo **part_rels;	/* Array of RelOptInfos of partitions,
 									 * stored in the same order of bounds */
+	Bitmapset  *live_parts;		/* Indexes into part_rels of the non-NULL
+								 * RelOptInfos of unpruned partitions; exists
+								 * to avoid having to iterate over the entire
+								 * part_rels array to filter NULL entries. */
 	List	  **partexprs;		/* Non-nullable partition key expressions. */
 	List	  **nullable_partexprs; /* Nullable partition key expressions. */
 	List	   *partitioned_child_rels; /* List of RT indexes. */
-- 
2.11.0


From 7e56837161cc139008d9a945a89daf513267b299 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Fri, 12 Oct 2018 19:12:53 +0900
Subject: [PATCH 5/5] Do not lock all partitions at the beginning

---
 src/backend/optimizer/util/inherit.c | 15 +++++----------
 1 file changed, 5 insertions(+), 10 deletions(-)

diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 6e9564d0ce..85770226d0 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -332,10 +332,6 @@ expand_nonpartitioned_inherited_rtentry(PlannerInfo *root,
  *
  * Partitions are added to the query in order in which they are found in
  * the parent's PartitionDesc.
- *
- * Note: even though only the unpruned partitions will be added to the
- * resulting plan, this still locks *all* partitions via find_all_inheritors
- * when this function is called for the root partitioned table.
  */
 static void
 expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
@@ -349,10 +345,6 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
 									parentrel->inh_root_parent :
 									parentRTindex;
 
-	/* If root partitioned table, lock *all* partitions in the tree. */
-	if (parentRTindex == rootParentRTindex)
-		(void) find_all_inheritors(parentrte->relid, lockmode, NULL);
-
 	/*
 	 * Initialize partitioned_child_rels to contain this RT index.
 	 *
@@ -389,8 +381,11 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
 		RangeTblEntry *childrte;
 		Index		childRTindex;
 
-		/* Open rel; we already have required locks */
-		newrelation = table_open(childOID, NoLock);
+		/*
+		 * Open rel; this's the first time of opening partitions for this
+		 * query, so take the appropriate locks.
+		 */
+		newrelation = table_open(childOID, lockmode);
 		Assert(!RELATION_IS_OTHER_TEMP(newrelation));
 
 		/*
-- 
2.11.0



Attachments:

  [text/plain] 0001-Overhaul-inheritance-update-delete-planning.patch (69.4K, ../0a70234e-b2e6-debc-2fb5-e8fd927caa00@lab.ntt.co.jp/2-0001-Overhaul-inheritance-update-delete-planning.patch)
  download | inline diff:
From 81632e4088bf1b711c80ff07762ac72cd74d9327 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Fri, 8 Feb 2019 16:16:38 +0900
Subject: [PATCH 1/5] 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 child
PlannerInfo's from the informaton in the original parent PlannerInfo.
It creates them *after* it has generated enough state in the original,
PlannerInfo, 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 then generated separately for each child target
relation, after replacing the parent relation references in the
original join tree with the child relation references.  Join paths
corresponding to each child target are remembered for further
processing by stuffing stuffing the joinrel RelOptInfos in a list
in the original PlannerInfo.

So, 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.  Once join paths have been created for all unpruned
child target relations, back in inheritance_planner, the
aforementioned paths are adjusted so that they produce the query's
top-level target list that is expanded according to a given child
relation's descriptor.

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.)

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        | 449 ++++++++++++++++++++--
 src/backend/optimizer/path/equivclass.c      | 160 ++++++++
 src/backend/optimizer/plan/planmain.c        |  41 +-
 src/backend/optimizer/plan/planner.c         | 550 ++++++++++-----------------
 src/backend/optimizer/util/appendinfo.c      |  30 +-
 src/backend/optimizer/util/inherit.c         | 127 +++++++
 src/include/nodes/pathnodes.h                |  30 ++
 src/include/optimizer/inherit.h              |   1 +
 src/include/optimizer/paths.h                |   2 +
 src/test/regress/expected/partition_join.out |   4 +-
 12 files changed, 1002 insertions(+), 411 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 8314fce78f..1da28357ef 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -4439,24 +4439,11 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
 
    <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 65302fe65b..c5c08efac1 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2203,6 +2203,7 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node)
 	WRITE_NODE_FIELD(distinct_pathkeys);
 	WRITE_NODE_FIELD(sort_pathkeys);
 	WRITE_NODE_FIELD(processed_tlist);
+	WRITE_NODE_FIELD(unexpanded_tlist);
 	WRITE_NODE_FIELD(minmax_aggs);
 	WRITE_FLOAT_FIELD(total_table_pages, "%.0f");
 	WRITE_FLOAT_FIELD(tuple_fraction, "%.4f");
@@ -2218,6 +2219,7 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node)
 	WRITE_BITMAPSET_FIELD(curOuterRels);
 	WRITE_NODE_FIELD(curOuterParams);
 	WRITE_BOOL_FIELD(partColsUpdated);
+	WRITE_NODE_FIELD(inh_target_child_rels);
 }
 
 static void
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 0debac75c6..0a4729e646 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_inherited_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_inherited_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,35 @@ 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->parse->resultRelation > 0 &&
+		root->simple_rte_array[root->parse->resultRelation]->inh)
+	{
+		/*
+		 * 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;
 }
@@ -361,6 +369,8 @@ static void
 set_rel_size(PlannerInfo *root, RelOptInfo *rel,
 			 Index rti, RangeTblEntry *rte)
 {
+	bool	inherited_update = false;
+
 	if (rel->reloptkind == RELOPT_BASEREL &&
 		relation_excluded_by_constraints(root, rel, rte))
 	{
@@ -379,8 +389,18 @@ 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)
+		{
+			inherited_update = true;
+			set_inherited_target_rel_sizes(root, rel, rti, rte);
+		}
+		else
+			set_append_rel_size(root, rel, rti, rte);
 	}
 	else
 	{
@@ -457,8 +477,9 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
 
 	/*
 	 * We insist that all non-dummy rels have a nonzero rowcount estimate.
+	 * An exception is target relations that are inheritance parents.
 	 */
-	Assert(rel->rows > 0 || IS_DUMMY_REL(rel));
+	Assert(rel->rows > 0 || IS_DUMMY_REL(rel) || inherited_update);
 }
 
 /*
@@ -469,14 +490,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 (rti == root->parse->resultRelation)
+		{
+			inherited_update = true;
+			set_inherited_target_rel_pathlists(root, rel, rti, rte);
+		}
+		else
+			set_append_rel_pathlist(root, rel, rti, rte);
 	}
 	else
 	{
@@ -530,6 +563,19 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 	}
 
 	/*
+	 * If the relation is an inheritance parent and the query's result
+	 * relation, no paths have been generated for it yet, so skip the following
+	 * steps.
+	 */
+	if (inherited_update)
+	{
+#ifdef OPTIMIZER_DEBUG
+		debug_print_rel(root, rel);
+#endif
+		return;
+	}
+
+	/*
 	 * Allow a plugin to editorialize on the set of Paths for this base
 	 * relation.  It could add new paths (such as CustomPaths) by calling
 	 * add_path(), or add_partial_path() if parallel aware.  It could also
@@ -924,6 +970,207 @@ set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 }
 
 /*
+ * set_inherited_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_inherited_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;
+
+		/* 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);
+
+		if (did_pruning && !bms_is_member(appinfo->child_relid, live_children))
+		{
+			/* This partition was pruned; skip it. */
+			set_dummy_rel_pathlist(childrel);
+			continue;
+		}
+
+		/*
+		 * 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))
+		{
+			/*
+			 * 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.
+			 */
+			set_dummy_rel_pathlist(childrel);
+			continue;
+		}
+
+		/*
+		 * Add missing Vars to child's reltarget.
+		 *
+		 * create_inherited_target_child_root() would've added only those that
+		 * are needed due to being referenced 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);
+		foreach(lc, translated_exprs)
+		{
+			Expr *expr = lfirst(lc);
+
+			if (!list_member(childrel->reltarget->exprs, expr))
+				childrel->reltarget->exprs =
+								lappend(childrel->reltarget->exprs, expr);
+		}
+
+		childrel->has_eclass_joins = rel->has_eclass_joins;
+
+		subroot = root->inh_target_child_roots[childRTindex];
+
+		/* 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 is a partitioned table it may have been marked as a
+		 * dummy rel from having all its partitions pruned.
+		 */
+		if (IS_DUMMY_REL(childrel))
+			continue;
+
+		/* We have at least one live child. */
+		has_live_children = true;
+
+		Assert(childrel->rows > 0 || childRTE->inh);
+	}
+
+	/*
+	 * 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.
+	 */
+	if (!has_live_children)
+		set_dummy_rel_pathlist(rel);
+}
+
+/*
+ * set_inherited_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_inherited_target_rel_pathlists(PlannerInfo *root, RelOptInfo *rel,
+								   Index rti, RangeTblEntry *rte)
+{
+	int			parentRTindex = rti;
+	ListCell   *l;
+
+	/* 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;
+
+		/*
+		 * 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"
  *
@@ -2566,6 +2813,146 @@ 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;
+
+	/* 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);
+
+		/*
+		 * The following implements essentially the same Assert as in
+		 * make_one_rel, our caller.
+		 */
+		Assert(bms_equal(childjoinrel->relids, subroot->all_baserels));
+
+		/* 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 this child target rel.  inheritance_planner
+		 * perform remaining steps necessary fot these paths to produce the
+		 * desired targetlist.
+		 */
+		root->inh_target_child_path_rels =
+					lappend(root->inh_target_child_path_rels, childjoinrel);
+	}
+
+	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 23792508b7..11244fc57d 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -31,6 +31,11 @@
 #include "optimizer/restrictinfo.h"
 #include "utils/lsyscache.h"
 
+typedef struct translate_eq_classes_context
+{
+	PlannerInfo	  *root;
+	AppendRelInfo *appinfo;
+} translate_eq_classes_context;
 
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
 			  Expr *expr, Relids relids, Relids nullable_relids,
@@ -64,6 +69,10 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
 							 bool outer_on_left);
 static bool reconsider_full_join_clause(PlannerInfo *root,
 							RestrictInfo *rinfo);
+static EquivalenceClass * adjust_inherited_eq_class(PlannerInfo *root,
+					   EquivalenceClass *new_ec, AppendRelInfo *appinfo);
+static Node *translate_eq_classes_mutator(Node *node,
+							 translate_eq_classes_context *context);
 
 
 /*
@@ -2185,6 +2194,157 @@ add_child_rel_equivalences(PlannerInfo *root,
 	}
 }
 
+/*
+ * translate_eq_classes_for_child
+ *		This creates a copy of the list of EquivalenceClasses, each of
+ *		which is translated with the provided AppendRelInfo
+ *
+ * Whereas add_child_rel_equivalences simply adds child Vars to a given
+ * EquivalenceClasses if it contains the Vars of a given parent relation,
+ * this creates a whole new EquivalenceClasses copied from the original one
+ * and if it contains Vars of the parent relation, they're *replaced* with
+ * the corresponding child Vars.
+ */
+List *
+translate_eq_classes_for_child(PlannerInfo *root, List *eq_classes,
+							   AppendRelInfo *appinfo)
+{
+	translate_eq_classes_context context;
+
+	context.root = root;
+	context.appinfo = appinfo;
+
+	return (List *) translate_eq_classes_mutator((Node *) eq_classes,
+												 &context);
+}
+
+static Node *
+translate_eq_classes_mutator(Node *node,
+							 translate_eq_classes_context *context)
+{
+	if (node == NULL)
+		return NULL;
+
+	if (IsA(node, EquivalenceClass))
+	{
+		EquivalenceClass *ec = (EquivalenceClass *) node;
+		EquivalenceClass *new_ec = makeNode(EquivalenceClass);
+
+		/*
+		 * 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 might 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.
+		 */
+		if (bms_is_member(context->appinfo->parent_relid, ec->ec_relids))
+			new_ec = adjust_inherited_eq_class(context->root, new_ec,
+											   context->appinfo);
+
+		return (Node *) new_ec;
+	}
+
+	return expression_tree_mutator(node, translate_eq_classes_mutator,
+								   (void *) context);
+}
+
+/*
+ * adjust_inherited_eq_class
+ *		This translates members of EquivalenceClass such that references to
+ *		an inheritance parent relation are replaced by the references to
+ *		a given child relation
+ *
+ * This performs in-place updates of the members of the input struct, so the
+ * callers should've made a copy of the original EC first.
+ */
+static EquivalenceClass *
+adjust_inherited_eq_class(PlannerInfo *root, EquivalenceClass *new_ec,
+						  AppendRelInfo *appinfo)
+{
+	ListCell *lc;
+	RelOptInfo *parent_rel = find_base_rel(root, appinfo->parent_relid),
+			   *child_rel = find_base_rel(root, appinfo->child_relid);
+
+	/*
+	 * 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(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);
+		}
+	}
+
+	/* Now fix up EC's relids set. */
+	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 new_ec;
+}
+
 
 /*
  * generate_implied_equalities_for_column
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 3cedd01c98..c7f2323852 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -20,6 +20,7 @@
  */
 #include "postgres.h"
 
+#include "nodes/pathnodes.h"
 #include "optimizer/appendinfo.h"
 #include "optimizer/clauses.h"
 #include "optimizer/inherit.h"
@@ -29,6 +30,7 @@
 #include "optimizer/paths.h"
 #include "optimizer/placeholder.h"
 #include "optimizer/planmain.h"
+#include "optimizer/prep.h"
 
 
 /*
@@ -60,6 +62,7 @@ query_planner(PlannerInfo *root, List *tlist,
 	Query	   *parse = root->parse;
 	List	   *joinlist;
 	RelOptInfo *final_rel;
+	Index		rti;
 
 	/*
 	 * Init planner lists to empty.
@@ -260,14 +263,42 @@ 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 during planning for individual child targets.
+	 */
+	if (parse->resultRelation &&
+		root->simple_rte_array[parse->resultRelation]->inh)
+	{
+		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 1a58d733fa..cf64688e76 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -130,8 +130,7 @@ typedef struct
 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);
+static void grouping_planner(PlannerInfo *root, 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);
@@ -241,6 +240,10 @@ static void apply_scanjoin_target_to_paths(PlannerInfo *root,
 							   List *scanjoin_targets_contain_srfs,
 							   bool scanjoin_target_parallel_safe,
 							   bool tlist_same_exprs);
+static void apply_scanjoin_target_to_paths_guts(PlannerInfo *root,
+									RelOptInfo *rel,
+									PathTarget *scanjoin_target,
+									bool tlist_same_exprs);
 static void create_partitionwise_grouping_paths(PlannerInfo *root,
 									RelOptInfo *input_rel,
 									RelOptInfo *grouped_rel,
@@ -997,7 +1000,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, tuple_fraction);
 
 	/*
 	 * Capture the set of outer-level param IDs we have access to, for use in
@@ -1173,13 +1176,38 @@ 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_inherited_target_child_roots - this creates copies of PlannerInfo for
+ *	each child after query_planner has finished processing the join tree, with
+ *	translated copies of parsetree, eq_classes, etc.
+ *
+ *	set_inherited_target_rel_sizes - this sets size estimates for child
+ *	relations
+ *
+ *	set_inherited_target_rel_pathlists - this creates Paths for scanning
+ *	individual child relations
+ *
+ *	inheritance_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 adjust the Paths of individual children do that they
+ * 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 +1219,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 +1228,52 @@ 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_inherited_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);
-
-		if (rte->rtekind == RTE_SUBQUERY)
-			subqueryRTindexes = bms_add_member(subqueryRTindexes, rti);
-		rti++;
-	}
+	root->unexpanded_tlist = list_copy(root->parse->targetList);
+	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.
 	 */
-	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);
-		}
+		/*
+		 * Result path, even if dummy, must go into outer query's FINAL
+		 * upperrel.
+		 */
+		final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);
+		final_rel->reltarget = create_pathtarget(root, tlist);
+		mark_dummy_rel(final_rel);
+		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 +1281,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,66 +1289,34 @@ 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
+	 * adjust their Paths to get desired PathTarget.
 	 */
-	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 *childbaserel = find_base_rel(root, childRTindex);
+		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;
+		ListCell   *lc;
 		Path	   *subpath;
+		List	   *tlist = subroot->processed_tlist;
+		PathTarget *scanjoin_target;
+		bool		tlist_same_exprs;
 
-		/* 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;
 
@@ -1356,24 +1328,12 @@ inheritance_planner(PlannerInfo *root)
 			(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.
+		 * Ignore a partitioned child.  Instead, the paths of its children will
+		 * be added to subpaths.
 		 */
-		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,161 +1361,57 @@ 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. */
+		scanjoin_target = create_pathtarget(subroot, tlist);
+		tlist_same_exprs = equal(scanjoin_target->exprs,
+								 childjoinrel->reltarget->exprs);
+		apply_scanjoin_target_to_paths_guts(subroot, childjoinrel,
+											scanjoin_target,
+											tlist_same_exprs);
 
 		/*
+		 * Transfer paths from childjoinrel's pathlist to sub_final_rel.
 		 * Select cheapest path in case there's more than one.  We always run
 		 * modification queries to conclusion, so we care only for the
 		 * cheapest-total path.
 		 */
 		sub_final_rel = fetch_upper_rel(subroot, UPPERREL_FINAL, NULL);
+		foreach(lc, childjoinrel->pathlist)
+			add_path(sub_final_rel, (Path *) lfirst(lc));
 		set_cheapest(sub_final_rel);
 		subpath = sub_final_rel->cheapest_total_path;
 
 		/*
-		 * If this child rel was excluded by constraint exclusion, exclude it
-		 * from the result plan.
+		 * If the childbaserel belongs to a single FDW, so does the
+		 * corresponding sub_final_rel.
 		 */
-		if (IS_DUMMY_PATH(subpath))
-			continue;
+		sub_final_rel->serverid = childbaserel->serverid;
+		sub_final_rel->userid = childbaserel->userid;
+		sub_final_rel->useridiscurrent = childbaserel->useridiscurrent;
+		sub_final_rel->fdwroutine = childbaserel->fdwroutine;
 
 		/*
-		 * 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 there is an FDW that's responsible for all baserels of the query,
+		 * let it consider adding ForeignPaths.
 		 */
-		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)));
+		if (sub_final_rel->fdwroutine &&
+			sub_final_rel->fdwroutine->GetForeignUpperPaths)
+			sub_final_rel->fdwroutine->GetForeignUpperPaths(subroot,
+															UPPERREL_FINAL,
+															childjoinrel,
+															sub_final_rel,
+															NULL);
+
+		/* Let extensions possibly add some more paths to sub_final_rel. */
+		if (create_upper_paths_hook)
+			(*create_upper_paths_hook) (subroot, UPPERREL_FINAL, childjoinrel,
+										sub_final_rel, NULL);
 
 		/*
-		 * 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.
+		 * child rel cannot be empty, or inheritance_make_rel_from_joinlist
+		 * wouldn't have put it in the list to begin with.
 		 */
-		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);
@@ -1587,36 +1443,6 @@ inheritance_planner(PlannerInfo *root)
 	 */
 
 	/*
-	 * 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
 	 * ModifyTable do that.
@@ -1651,11 +1477,6 @@ inheritance_planner(PlannerInfo *root)
  * This function adds all required top-level processing to the scan/join
  * Path(s) produced by query_planner.
  *
- * If inheritance_update is true, we're being called from inheritance_planner
- * and should not include a ModifyTable step in the resulting Path(s).
- * (inheritance_planner will create a single ModifyTable node covering all the
- * target tables.)
- *
  * 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)
@@ -1673,8 +1494,7 @@ inheritance_planner(PlannerInfo *root)
  *--------------------
  */
 static void
-grouping_planner(PlannerInfo *root, bool inheritance_update,
-				 double tuple_fraction)
+grouping_planner(PlannerInfo *root, double tuple_fraction)
 {
 	Query	   *parse = root->parse;
 	List	   *tlist;
@@ -2172,11 +1992,8 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
 											  offset_est, count_est);
 		}
 
-		/*
-		 * If this is an INSERT/UPDATE/DELETE, and we're not being called from
-		 * inheritance_planner, add the ModifyTable node.
-		 */
-		if (parse->commandType != CMD_SELECT && !inheritance_update)
+		/* If this is an INSERT/UPDATE/DELETE, add the ModifyTable node. */
+		if (parse->commandType != CMD_SELECT)
 		{
 			Index		rootRelation;
 			List	   *withCheckOptionLists;
@@ -6914,7 +6731,6 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
 							   bool scanjoin_target_parallel_safe,
 							   bool tlist_same_exprs)
 {
-	ListCell   *lc;
 	PathTarget *scanjoin_target;
 	bool		is_dummy_rel = IS_DUMMY_REL(rel);
 
@@ -6985,53 +6801,8 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
 
 	/* Extract SRF-free scan/join target. */
 	scanjoin_target = linitial_node(PathTarget, scanjoin_targets);
-
-	/*
-	 * Adjust each input path.  If the tlist exprs are the same, we can just
-	 * inject the sortgroupref information into the existing pathtarget.
-	 * Otherwise, replace each path with a projection path that generates the
-	 * SRF-free scan/join target.  This can't change the ordering of paths
-	 * within rel->pathlist, so we just modify the list in place.
-	 */
-	foreach(lc, rel->pathlist)
-	{
-		Path	   *subpath = (Path *) lfirst(lc);
-		Path	   *newpath;
-
-		Assert(subpath->param_info == NULL);
-
-		if (tlist_same_exprs)
-			subpath->pathtarget->sortgrouprefs =
-				scanjoin_target->sortgrouprefs;
-		else
-		{
-			newpath = (Path *) create_projection_path(root, rel, subpath,
-													  scanjoin_target);
-			lfirst(lc) = newpath;
-		}
-	}
-
-	/* Same for partial paths. */
-	foreach(lc, rel->partial_pathlist)
-	{
-		Path	   *subpath = (Path *) lfirst(lc);
-		Path	   *newpath;
-
-		/* Shouldn't have any parameterized paths anymore */
-		Assert(subpath->param_info == NULL);
-
-		if (tlist_same_exprs)
-			subpath->pathtarget->sortgrouprefs =
-				scanjoin_target->sortgrouprefs;
-		else
-		{
-			newpath = (Path *) create_projection_path(root,
-													  rel,
-													  subpath,
-													  scanjoin_target);
-			lfirst(lc) = newpath;
-		}
-	}
+	apply_scanjoin_target_to_paths_guts(root, rel, scanjoin_target,
+										tlist_same_exprs);
 
 	/* Now fix things up if scan/join target contains SRFs */
 	if (root->parse->hasTargetSRFs)
@@ -7059,6 +6830,10 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
 			int			nappinfos;
 			List	   *child_scanjoin_targets = NIL;
 
+			/* Skip processing pruned partitions. */
+			if (child_rel == NULL)
+				continue;
+
 			/* Translate scan/join targets for this child. */
 			appinfos = find_appinfos_by_relids(root, child_rel->relids,
 											   &nappinfos);
@@ -7112,6 +6887,67 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
 }
 
 /*
+ * apply_scanjoin_target_to_paths_guts
+ *		Portion of apply_scanjoin_target_to_paths that's useful in some places
+ *		where full powers of the former are unnecessary
+ */
+static void
+apply_scanjoin_target_to_paths_guts(PlannerInfo *root,
+									RelOptInfo *rel,
+									PathTarget *scanjoin_target,
+									bool tlist_same_exprs)
+{
+	ListCell *lc;
+
+	/*
+	 * Adjust each input path.  If the tlist exprs are the same, we can just
+	 * inject the sortgroupref information into the existing pathtarget.
+	 * Otherwise, replace each path with a projection path that generates the
+	 * SRF-free scan/join target.  This can't change the ordering of paths
+	 * within rel->pathlist, so we just modify the list in place.
+	 */
+	foreach(lc, rel->pathlist)
+	{
+		Path	   *subpath = (Path *) lfirst(lc);
+		Path	   *newpath;
+
+		Assert(subpath->param_info == NULL);
+
+		if (tlist_same_exprs)
+			subpath->pathtarget->sortgrouprefs =
+				scanjoin_target->sortgrouprefs;
+		else
+		{
+			newpath = (Path *) create_projection_path(root, rel, subpath,
+													  scanjoin_target);
+			lfirst(lc) = newpath;
+		}
+	}
+
+	/* Same for partial paths. */
+	foreach(lc, rel->partial_pathlist)
+	{
+		Path	   *subpath = (Path *) lfirst(lc);
+		Path	   *newpath;
+
+		/* Shouldn't have any parameterized paths anymore */
+		Assert(subpath->param_info == NULL);
+
+		if (tlist_same_exprs)
+			subpath->pathtarget->sortgrouprefs =
+				scanjoin_target->sortgrouprefs;
+		else
+		{
+			newpath = (Path *) create_projection_path(root,
+													  rel,
+													  subpath,
+													  scanjoin_target);
+			lfirst(lc) = newpath;
+		}
+	}
+}
+
+/*
  * create_partitionwise_grouping_paths
  *
  * If the partition keys of input relation are part of the GROUP BY clause, all
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index ca6622ece9..8914c464d3 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,34 @@ adjust_appendrel_attrs_mutator(Node *node,
 											  context->appinfos);
 		return (Node *) phv;
 	}
+
+	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;
+	}
+
 	/* 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..1d16a9f2ff 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -21,11 +21,16 @@
 #include "miscadmin.h"
 #include "optimizer/appendinfo.h"
 #include "optimizer/inherit.h"
+#include "optimizer/optimizer.h"
+#include "optimizer/paths.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 +497,125 @@ 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,
+			   *tlist_vars;
+
+
+	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);
+
+	/*
+	 * Translate ECs.  This copies the list of ECs, translating the
+	 * relevant members of individual ECs to replace the references to
+	 * the parent RTE by child RTE, including the EC members.
+	 */
+	subroot->eq_classes =
+				translate_eq_classes_for_child(root, root->eq_classes,
+											   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., using
+	 * the child's TupleDesc.
+	 */
+	tlist = preprocess_targetlist(subroot);
+	subroot->processed_tlist = tlist;
+
+	/*
+	 * Add any newly added Vars to the child RelOptInfo.  They'll be needed
+	 * at the top level, so we pass {0} for where_needed.
+	 */
+	tlist_vars = pull_var_clause((Node *) tlist,
+								 PVC_RECURSE_AGGREGATES |
+								 PVC_RECURSE_WINDOWFUNCS |
+								 PVC_INCLUDE_PLACEHOLDERS);
+	add_vars_to_targetlist(root, tlist_vars, bms_make_singleton(0), false);
+
+	/*
+	 * Adjust all_baserels to replace the original target relation with the
+	 * child target relation.
+	 */
+	subroot->all_baserels = adjust_child_relids(subroot->all_baserels,
+												1, &appinfo);
+
+	return subroot;
+}
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index a008ae07da..2dc3059f16 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -217,6 +217,17 @@ struct PlannerInfo
 	struct AppendRelInfo **append_rel_array;
 
 	/*
+	 * inh_target_child_roots is same length as above arrays, indexed by
+	 * rangetable index (entry 0 is wasted like simple_rel_array).  Only
+	 * elements corresponding to 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;
+
+	/*
 	 * all_baserels is a Relids set of all base relids (but not "other"
 	 * relids) in the query; that is, the Relids identifier of the final join
 	 * we need to form.  This is computed in make_one_rel, just before we
@@ -310,6 +321,12 @@ struct PlannerInfo
 	 */
 	List	   *processed_tlist;
 
+	/*
+	 * This stores the original version of the query's targetlist that's not
+	 * modified by the planner.  Useful for target inheritance planning.
+	 */
+	List	   *unexpanded_tlist;
+
 	/* Fields filled during create_plan() for use in setrefs.c */
 	AttrNumber *grouping_map;	/* for GroupingFunc fixup */
 	List	   *minmax_aggs;	/* List of MinMaxAggInfos */
@@ -348,9 +365,22 @@ struct PlannerInfo
 
 	/* Does this query modify any partition key columns? */
 	bool		partColsUpdated;
+
+	/* 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 040335a7c5..e8caaaf643 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -154,6 +154,8 @@ extern void add_child_rel_equivalences(PlannerInfo *root,
 						   AppendRelInfo *appinfo,
 						   RelOptInfo *parent_rel,
 						   RelOptInfo *child_rel);
+extern List *translate_eq_classes_for_child(PlannerInfo *root,
+							   List *eq_classes, AppendRelInfo *appinfo);
 extern List *generate_implied_equalities_for_column(PlannerInfo *root,
 									   RelOptInfo *rel,
 									   ec_matches_callback_type callback,
diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out
index bbdc373782..fd85b5ead3 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.11.0



  [text/plain] 0002-Get-rid-of-some-useless-code.patch (8.5K, ../0a70234e-b2e6-debc-2fb5-e8fd927caa00@lab.ntt.co.jp/3-0002-Get-rid-of-some-useless-code.patch)
  download | inline diff:
From c83f4ec48e035ce2a9fcb2c54a251ecb900f71e0 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Fri, 8 Feb 2019 16:21:59 +0900
Subject: [PATCH 2/5] Get rid of some useless code

* plancat.c no longer needs to load partition constraints, because
even update/delete now use partprune.c's facilities

* relation_excluded_by_constraints no longer needs to worry about
fielding update/delete on partitioned tablesm, which in turn means
InheritanceKind is no longer necessary
---
 src/backend/nodes/outfuncs.c              |  1 -
 src/backend/optimizer/plan/createplan.c   | 10 -----
 src/backend/optimizer/plan/planner.c      |  8 ----
 src/backend/optimizer/prep/prepjointree.c |  1 -
 src/backend/optimizer/util/plancat.c      | 73 ++++---------------------------
 src/include/nodes/pathnodes.h             | 15 -------
 6 files changed, 9 insertions(+), 99 deletions(-)

diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index c5c08efac1..475dc68d8a 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2209,7 +2209,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);
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 236f506cfb..7383eb9dfe 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -2054,12 +2054,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;
 
@@ -2216,12 +2211,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/planner.c b/src/backend/optimizer/plan/planner.c
index cf64688e76..6a6403d7fe 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -639,7 +639,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);
@@ -1321,13 +1320,6 @@ inheritance_planner(PlannerInfo *root)
 		parent_rte->securityQuals = NIL;
 
 		/*
-		 * Mark whether we're planning a query to a partitioned table or an
-		 * inheritance parent.
-		 */
-		subroot->inhTargetKind =
-			(rootRelation != 0) ? INHKIND_PARTITIONED : INHKIND_INHERITED;
-
-		/*
 		 * Ignore a partitioned child.  Instead, the paths of its children will
 		 * be added to subpaths.
 		 */
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index aebe162713..8aef1f6535 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/plancat.c b/src/backend/optimizer/util/plancat.c
index d6dc83ca80..d8c1981b4d 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1266,36 +1266,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;
@@ -1419,40 +1389,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 2dc3059f16..b219ff8806 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -82,18 +82,6 @@ typedef enum UpperRelationKind
 	/* NB: UPPERREL_FINAL must be last enum entry; it's used to size arrays */
 } UpperRelationKind;
 
-/*
- * This enum identifies which type of relation is being planned through the
- * inheritance planner.  INHKIND_NONE indicates the inheritance planner
- * was not used.
- */
-typedef enum InheritanceKind
-{
-	INHKIND_NONE,
-	INHKIND_INHERITED,
-	INHKIND_PARTITIONED
-} InheritanceKind;
-
 /*----------
  * PlannerGlobal
  *		Global information for planning/optimization
@@ -342,9 +330,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 */
-- 
2.11.0



  [text/plain] 0003-Lazy-creation-of-RTEs-for-inheritance-children.patch (99.0K, ../0a70234e-b2e6-debc-2fb5-e8fd927caa00@lab.ntt.co.jp/4-0003-Lazy-creation-of-RTEs-for-inheritance-children.patch)
  download | inline diff:
From 039bd48dc21a6d1b59e4818dc5bcd4b791f9b209 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Fri, 26 Oct 2018 16:45:59 +0900
Subject: [PATCH 3/5] Lazy creation of RTEs for inheritance children

Currently, expand_inherited_tables called from subquery_planner opens
all inheritance child tables and adds them to the query's range
table and PlannerInfo in the form of AppendRelInfo's initially and
later RelOptInfo's.  For partitioning, that's pretty wasteful, because
it's possible to determine only the partitions that will need to be
scanned using partition pruning, which doesn't require opening the
partitions themselves.  However, the point at which
expand_inherited_tables is called currently is too early to perform
partition pruning.

This commit rearranges things within the planner so that
expand_inherited_tables can be called from query_planner after the
latter has finished setting up the information needed to perform
partition pruning.  However that means the PlannerInfo arrays that
store RangeTblEntry's, RelOptInfo's, and AppendRelInfo's need to
be expanded if inheritance expansion leads to adding more relations
to planning.  There are also consequences around targetlist
expansion.  For example, preprocess_targetlist cannot add the junk
columns entries needed for row mark handling until all of the
inheritance children have been determined, because different children
may need different types of junk columns to be added.  So, the
approach of late inheritance expansion required some interface tweaks
for preprocess_targetlist().

For partitioning, although we don't create a RangeTblEntry and
RelOptInfo for pruned partitions at make_one_rel time, partitionwise
join code relies on the fact that even though partitions may have
been pruned, they'd still own a RelOptInfo to handle the outer join
case where the pruned partition appears on the nullable side of join.
Partitionwise join code deals with that by allocating dummy
RelOptInfos for pruned partitions that are based mostly on their
parent's properties.

There are some regression test diffs:

 1. Caused by the fact that we no longer allocate a duplicate RT
    entry for a partitioned table in its role as child, as seen in
    the partition_aggregate.out test output.

 2. Those in postgres_fdw.out are caused by the fact that junk columns
    required for row marking are added to reltarget->exprs later than
    user columns, because the row marking junk columns aren't added
    until the inheritance is expanded which as of this commit is
    later than it used to be as noted above.
---
 contrib/postgres_fdw/expected/postgres_fdw.out    |  32 +-
 src/backend/nodes/outfuncs.c                      |   1 +
 src/backend/optimizer/path/allpaths.c             | 268 +------
 src/backend/optimizer/path/joinrels.c             |  71 +-
 src/backend/optimizer/plan/initsplan.c            |  49 --
 src/backend/optimizer/plan/planmain.c             |  10 +
 src/backend/optimizer/plan/planner.c              | 199 +++--
 src/backend/optimizer/plan/setrefs.c              |   6 +
 src/backend/optimizer/prep/preptlist.c            |  15 +-
 src/backend/optimizer/prep/prepunion.c            |   3 +
 src/backend/optimizer/util/appendinfo.c           |  45 +-
 src/backend/optimizer/util/inherit.c              | 841 ++++++++++++++++------
 src/backend/optimizer/util/plancat.c              |  47 +-
 src/backend/optimizer/util/relnode.c              | 107 +--
 src/backend/partitioning/partprune.c              |  44 +-
 src/include/nodes/pathnodes.h                     |  19 +-
 src/include/optimizer/appendinfo.h                |   7 +-
 src/include/optimizer/pathnode.h                  |   1 +
 src/include/optimizer/plancat.h                   |   4 +-
 src/include/optimizer/prep.h                      |   3 +-
 src/test/regress/expected/partition_aggregate.out |   4 +-
 21 files changed, 1041 insertions(+), 735 deletions(-)

diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 42108bd3d4..4ba406af71 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -7128,15 +7128,15 @@ select * from bar where f1 in (select f1 from foo) for update;
                      Output: bar2.f1, bar2.f2, bar2.ctid, bar2.*, bar2.tableoid
                      Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR UPDATE
          ->  Hash
-               Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+               Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                ->  HashAggregate
-                     Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                     Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                      Group Key: foo.f1
                      ->  Append
                            ->  Seq Scan on public.foo
-                                 Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                                 Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                            ->  Foreign Scan on public.foo2
-                                 Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1
+                                 Output: foo2.f1, foo2.ctid, foo2.*, foo2.tableoid
                                  Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
 (23 rows)
 
@@ -7166,15 +7166,15 @@ select * from bar where f1 in (select f1 from foo) for share;
                      Output: bar2.f1, bar2.f2, bar2.ctid, bar2.*, bar2.tableoid
                      Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR SHARE
          ->  Hash
-               Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+               Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                ->  HashAggregate
-                     Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                     Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                      Group Key: foo.f1
                      ->  Append
                            ->  Seq Scan on public.foo
-                                 Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                                 Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                            ->  Foreign Scan on public.foo2
-                                 Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1
+                                 Output: foo2.f1, foo2.ctid, foo2.*, foo2.tableoid
                                  Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
 (23 rows)
 
@@ -7203,15 +7203,15 @@ update bar set f2 = f2 + 100 where f1 in (select f1 from foo);
          ->  Seq Scan on public.bar
                Output: bar.f1, bar.f2, bar.ctid
          ->  Hash
-               Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+               Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                ->  HashAggregate
-                     Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                     Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                      Group Key: foo.f1
                      ->  Append
                            ->  Seq Scan on public.foo
-                                 Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                                 Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                            ->  Foreign Scan on public.foo2
-                                 Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1
+                                 Output: foo2.f1, foo2.ctid, foo2.*, foo2.tableoid
                                  Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
    ->  Hash Join
          Output: bar2.f1, (bar2.f2 + 100), bar2.f3, bar2.ctid, foo.ctid, foo.*, foo.tableoid
@@ -7221,15 +7221,15 @@ update bar set f2 = f2 + 100 where f1 in (select f1 from foo);
                Output: bar2.f1, bar2.f2, bar2.f3, bar2.ctid
                Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct2 FOR UPDATE
          ->  Hash
-               Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+               Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                ->  HashAggregate
-                     Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                     Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                      Group Key: foo.f1
                      ->  Append
                            ->  Seq Scan on public.foo
-                                 Output: foo.ctid, foo.*, foo.tableoid, foo.f1
+                                 Output: foo.f1, foo.ctid, foo.*, foo.tableoid
                            ->  Foreign Scan on public.foo2
-                                 Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1
+                                 Output: foo2.f1, foo2.ctid, foo2.*, foo2.tableoid
                                  Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1
 (39 rows)
 
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 475dc68d8a..392bc8ff01 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2270,6 +2270,7 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node)
 	WRITE_BOOL_FIELD(consider_partitionwise_join);
 	WRITE_BITMAPSET_FIELD(top_parent_relids);
 	WRITE_NODE_FIELD(partitioned_child_rels);
+	WRITE_UINT_FIELD(inh_root_parent);
 }
 
 static void
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 0a4729e646..749d173770 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -45,7 +45,6 @@
 #include "optimizer/tlist.h"
 #include "parser/parse_clause.h"
 #include "parser/parsetree.h"
-#include "partitioning/partprune.h"
 #include "rewrite/rewriteManip.h"
 #include "utils/lsyscache.h"
 
@@ -145,9 +144,6 @@ static void subquery_push_qual(Query *subquery,
 static void recurse_push_qual(Node *setOp, Query *topquery,
 				  RangeTblEntry *rte, Index rti, Node *qual);
 static void remove_unused_subquery_outputs(Query *subquery, RelOptInfo *rel);
-static bool apply_child_basequals(PlannerInfo *root, RelOptInfo *rel,
-					  RelOptInfo *childrel,
-					  RangeTblEntry *childRTE, AppendRelInfo *appinfo);
 
 
 /*
@@ -390,6 +386,14 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
 	else if (rte->inh)
 	{
 		/*
+		 * expand_inherited_tables may have proved that the relation is empty.
+		 * For example, if it's a partitioned table with 0 partitions or all
+		 * of its partitions are pruned.  In that case nothing to do here.
+		 */
+		if (IS_DUMMY_REL(rel))
+			return;
+
+		/*
 		 * 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".
@@ -986,29 +990,12 @@ set_inherited_target_rel_sizes(PlannerInfo *root, RelOptInfo *rel,
 	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);
@@ -1025,47 +1012,23 @@ set_inherited_target_rel_sizes(PlannerInfo *root, RelOptInfo *rel,
 
 		childRTindex = appinfo->child_relid;
 		childRTE = root->simple_rte_array[childRTindex];
+		Assert(childRTE != NULL);
 
 		/*
-		 * The child rel's RelOptInfo was already created during
-		 * add_base_rels_to_query.
+		 * The child rel's RelOptInfo was created during
+		 * expand_inherited_tables().
 		 */
 		childrel = find_base_rel(root, childRTindex);
+		Assert(childrel != NULL);
 		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;
-		}
-
 		/*
-		 * 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.
+		 * Child relation may have been marked dummy if build_append_child_rel
+		 * found self-contradictory quals or quals that contradict its
+		 * constraints.
 		 */
-		if (!apply_child_basequals(root, rel, childrel, childRTE, appinfo))
-		{
-			/*
-			 * Some restriction clause reduced to constant FALSE or NULL after
-			 * substitution, so this child need not be scanned.
-			 */
-			set_dummy_rel_pathlist(childrel);
+		if (IS_DUMMY_REL(childrel))
 			continue;
-		}
-
-		if (relation_excluded_by_constraints(root, childrel, childRTE))
-		{
-			/*
-			 * This child need not be scanned, so we can omit it from the
-			 * appendrel.
-			 */
-			set_dummy_rel_pathlist(childrel);
-			continue;
-		}
 
 		/*
 		 * Add missing Vars to child's reltarget.
@@ -1192,8 +1155,6 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 	double	   *parent_attrsizes;
 	int			nattrs;
 	ListCell   *l;
-	Relids		live_children = NULL;
-	bool		did_pruning = false;
 
 	/* Guard against stack overflow due to overly deep inheritance tree. */
 	check_stack_depth();
@@ -1201,32 +1162,6 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 	Assert(IS_SIMPLE_REL(rel));
 
 	/*
-	 * Initialize partitioned_child_rels to contain this RT index.
-	 *
-	 * Note that during the set_append_rel_pathlist() phase, we will bubble up
-	 * the indexes of partitioned relations that appear down in the tree, so
-	 * that when we've created Paths for all the children, the root
-	 * partitioned table's list will contain all such indexes.
-	 */
-	if (rte->relkind == RELKIND_PARTITIONED_TABLE)
-		rel->partitioned_child_rels = list_make1_int(rti);
-
-	/*
-	 * 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;
-	}
-
-	/*
 	 * If this is a partitioned baserel, set the consider_partitionwise_join
 	 * flag; currently, we only consider partitionwise joins with the baserel
 	 * if its targetlist doesn't contain a whole-row Var.
@@ -1272,50 +1207,26 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 
 		childRTindex = appinfo->child_relid;
 		childRTE = root->simple_rte_array[childRTindex];
+		Assert(childRTE != NULL);
 
 		/*
-		 * The child rel's RelOptInfo was already created during
-		 * add_base_rels_to_query.
+		 * The child rel's RelOptInfo was created during
+		 * expand_inherited_tables().
 		 */
 		childrel = find_base_rel(root, childRTindex);
+		Assert(childrel != NULL);
 		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;
-		}
-
 		/*
-		 * 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.
+		 * Child relation may have been marked dummy if build_append_child_rel
+		 * found self-contradictory quals or quals that contradict its
+		 * constraints.
 		 */
-		if (!apply_child_basequals(root, rel, childrel, childRTE, appinfo))
-		{
-			/*
-			 * Some restriction clause reduced to constant FALSE or NULL after
-			 * substitution, so this child need not be scanned.
-			 */
-			set_dummy_rel_pathlist(childrel);
+		if (IS_DUMMY_REL(childrel))
 			continue;
-		}
-
-		if (relation_excluded_by_constraints(root, childrel, childRTE))
-		{
-			/*
-			 * This child need not be scanned, so we can omit it from the
-			 * appendrel.
-			 */
-			set_dummy_rel_pathlist(childrel);
-			continue;
-		}
 
 		/*
-		 * CE failed, so finish copying/modifying targetlist and join quals.
+		 * Copy/Modify targetlist.
 		 *
 		 * NB: the resulting childrel->reltarget->exprs may contain arbitrary
 		 * expressions, which otherwise would not occur in a rel's targetlist.
@@ -2849,6 +2760,7 @@ inheritance_make_rel_from_joinlist(PlannerInfo *root, List *joinlist)
 			continue;
 
 		childrel = find_base_rel(root, appinfo->child_relid);
+		Assert(childrel != NULL);
 
 		/* Ignore excluded/pruned children. */
 		if (IS_DUMMY_REL(childrel))
@@ -3944,134 +3856,6 @@ generate_partitionwise_join_paths(PlannerInfo *root, RelOptInfo *rel)
 	list_free(live_children);
 }
 
-/*
- * apply_child_basequals
- *		Populate childrel's quals based on rel's quals, translating them using
- *		appinfo.
- *
- * If any of the resulting clauses evaluate to false or NULL, we return false
- * and don't apply any quals.  Caller can mark the relation as a dummy rel in
- * this case, since it needn't be scanned.
- *
- * If any resulting clauses evaluate to true, they're unnecessary and we don't
- * apply then.
- */
-static bool
-apply_child_basequals(PlannerInfo *root, RelOptInfo *rel,
-					  RelOptInfo *childrel, RangeTblEntry *childRTE,
-					  AppendRelInfo *appinfo)
-{
-	List	   *childquals;
-	Index		cq_min_security;
-	ListCell   *lc;
-
-	/*
-	 * The child rel's targetlist might contain non-Var expressions, which
-	 * means that substitution into the quals could produce opportunities for
-	 * const-simplification, and perhaps even pseudoconstant quals. Therefore,
-	 * transform each RestrictInfo separately to see if it reduces to a
-	 * constant or pseudoconstant.  (We must process them separately to keep
-	 * track of the security level of each qual.)
-	 */
-	childquals = NIL;
-	cq_min_security = UINT_MAX;
-	foreach(lc, rel->baserestrictinfo)
-	{
-		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
-		Node	   *childqual;
-		ListCell   *lc2;
-
-		Assert(IsA(rinfo, RestrictInfo));
-		childqual = adjust_appendrel_attrs(root,
-										   (Node *) rinfo->clause,
-										   1, &appinfo);
-		childqual = eval_const_expressions(root, childqual);
-		/* check for flat-out constant */
-		if (childqual && IsA(childqual, Const))
-		{
-			if (((Const *) childqual)->constisnull ||
-				!DatumGetBool(((Const *) childqual)->constvalue))
-			{
-				/* Restriction reduces to constant FALSE or NULL */
-				return false;
-			}
-			/* Restriction reduces to constant TRUE, so drop it */
-			continue;
-		}
-		/* might have gotten an AND clause, if so flatten it */
-		foreach(lc2, make_ands_implicit((Expr *) childqual))
-		{
-			Node	   *onecq = (Node *) lfirst(lc2);
-			bool		pseudoconstant;
-
-			/* check for pseudoconstant (no Vars or volatile functions) */
-			pseudoconstant =
-				!contain_vars_of_level(onecq, 0) &&
-				!contain_volatile_functions(onecq);
-			if (pseudoconstant)
-			{
-				/* tell createplan.c to check for gating quals */
-				root->hasPseudoConstantQuals = true;
-			}
-			/* reconstitute RestrictInfo with appropriate properties */
-			childquals = lappend(childquals,
-								 make_restrictinfo((Expr *) onecq,
-												   rinfo->is_pushed_down,
-												   rinfo->outerjoin_delayed,
-												   pseudoconstant,
-												   rinfo->security_level,
-												   NULL, NULL, NULL));
-			/* track minimum security level among child quals */
-			cq_min_security = Min(cq_min_security, rinfo->security_level);
-		}
-	}
-
-	/*
-	 * In addition to the quals inherited from the parent, we might have
-	 * securityQuals associated with this particular child node. (Currently
-	 * this can only happen in appendrels originating from UNION ALL;
-	 * inheritance child tables don't have their own securityQuals, see
-	 * expand_inherited_rtentry().)	Pull any such securityQuals up into the
-	 * baserestrictinfo for the child.  This is similar to
-	 * process_security_barrier_quals() for the parent rel, except that we
-	 * can't make any general deductions from such quals, since they don't
-	 * hold for the whole appendrel.
-	 */
-	if (childRTE->securityQuals)
-	{
-		Index		security_level = 0;
-
-		foreach(lc, childRTE->securityQuals)
-		{
-			List	   *qualset = (List *) lfirst(lc);
-			ListCell   *lc2;
-
-			foreach(lc2, qualset)
-			{
-				Expr	   *qual = (Expr *) lfirst(lc2);
-
-				/* not likely that we'd see constants here, so no check */
-				childquals = lappend(childquals,
-									 make_restrictinfo(qual,
-													   true, false, false,
-													   security_level,
-													   NULL, NULL, NULL));
-				cq_min_security = Min(cq_min_security, security_level);
-			}
-			security_level++;
-		}
-		Assert(security_level <= root->qual_security_level);
-	}
-
-	/*
-	 * OK, we've got all the baserestrictinfo quals for this child.
-	 */
-	childrel->baserestrictinfo = childquals;
-	childrel->baserestrict_min_security = cq_min_security;
-
-	return true;
-}
-
 /*****************************************************************************
  *			DEBUG SUPPORT
  *****************************************************************************/
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index dfbbfdac6d..6f77d2a0f4 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -21,6 +21,7 @@
 #include "optimizer/joininfo.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
+#include "optimizer/tlist.h"
 #include "partitioning/partbounds.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
@@ -52,6 +53,9 @@ static SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root,
 						Relids left_relids, Relids right_relids);
 static int match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel,
 							 bool strict_op);
+static RelOptInfo *build_dummy_partition_rel(PlannerInfo *root,
+									RelOptInfo *parent,
+									int partidx);
 
 
 /*
@@ -1384,6 +1388,11 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 		AppendRelInfo **appinfos;
 		int			nappinfos;
 
+		if (IS_SIMPLE_REL(rel1) && child_rel1 == NULL)
+			child_rel1 = build_dummy_partition_rel(root, rel1, cnt_parts);
+		if (IS_SIMPLE_REL(rel1) && child_rel2 == NULL)
+			child_rel2 = build_dummy_partition_rel(root, rel2, cnt_parts);
+
 		/*
 		 * If a child table has consider_partitionwise_join=false, it means
 		 * that it's a dummy relation for which we skipped setting up tlist
@@ -1462,8 +1471,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;
 }
@@ -1674,3 +1689,55 @@ match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel, bool strict_op)
 
 	return -1;
 }
+
+/*
+ * build_dummy_partition_rel
+ *		Build a RelOptInfo and AppendRelInfo for a pruned partition
+ *
+ * This does not result in opening the relation or a range table entry being
+ * created.  Also, the RelOptInfo thus created is not stored anywhere else
+ * beside the parent's part_rels array.
+ *
+ * The only reason this exists is because partition-wise join, in some cases,
+ * needs a RelOptInfo to represent an empty relation that's on the nullable
+ * side of an outer join, so that a Path representing the outer join can be
+ * created.
+ */
+static RelOptInfo *
+build_dummy_partition_rel(PlannerInfo *root, RelOptInfo *parent, int partidx)
+{
+	RangeTblEntry *parentrte = root->simple_rte_array[parent->relid];
+	RelOptInfo *rel;
+
+	Assert(parent->part_rels[partidx] == NULL);
+
+	/* Create minimally valid-looking RelOptInfo with parent's relid. */
+	rel = makeNode(RelOptInfo);
+	rel->reloptkind = RELOPT_OTHER_MEMBER_REL;
+	rel->relid = parent->relid;
+	rel->relids = bms_copy(parent->relids);
+	if (parent->top_parent_relids)
+		rel->top_parent_relids = parent->top_parent_relids;
+	else
+		rel->top_parent_relids = bms_copy(parent->relids);
+	rel->reltarget = copy_pathtarget(parent->reltarget);
+	parent->part_rels[partidx] = rel;
+	mark_dummy_rel(rel);
+
+	/*
+	 * Now we'll need a (no-op) AppendRelInfo for parent, because we're
+	 * setting the dummy partition's relid to be same as the parent's.
+	 */
+	if (root->append_rel_array[parent->relid] == NULL)
+	{
+		AppendRelInfo *appinfo = make_append_rel_info(parent, parentrte,
+													  parent->tupdesc,
+													  parentrte->relid,
+													  parent->reltype,
+													  parent->relid);
+
+		root->append_rel_array[parent->relid] = appinfo;
+	}
+
+	return rel;
+}
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 2afc3f1dfe..5e8a8b1eee 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -419,7 +419,6 @@ void
 create_lateral_join_info(PlannerInfo *root)
 {
 	bool		found_laterals = false;
-	Relids		prev_parents PG_USED_FOR_ASSERTS_ONLY = NULL;
 	Index		rti;
 	ListCell   *lc;
 
@@ -618,54 +617,6 @@ create_lateral_join_info(PlannerInfo *root)
 				bms_add_member(brel2->lateral_referencers, rti);
 		}
 	}
-
-	/*
-	 * Lastly, propagate lateral_relids and lateral_referencers from appendrel
-	 * parent rels to their child rels.  We intentionally give each child rel
-	 * the same minimum parameterization, even though it's quite possible that
-	 * some don't reference all the lateral rels.  This is because any append
-	 * path for the parent will have to have the same parameterization for
-	 * every child anyway, and there's no value in forcing extra
-	 * reparameterize_path() calls.  Similarly, a lateral reference to the
-	 * parent prevents use of otherwise-movable join rels for each child.
-	 *
-	 * It's possible for child rels to have their own children, in which case
-	 * the topmost parent's lateral info must be propagated all the way down.
-	 * This code handles that case correctly so long as append_rel_list has
-	 * entries for child relationships before grandchild relationships, which
-	 * is an okay assumption right now, but we'll need to be careful to
-	 * preserve it.  The assertions below check for incorrect ordering.
-	 */
-	foreach(lc, root->append_rel_list)
-	{
-		AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
-		RelOptInfo *parentrel = root->simple_rel_array[appinfo->parent_relid];
-		RelOptInfo *childrel = root->simple_rel_array[appinfo->child_relid];
-
-		/*
-		 * If we're processing a subquery of a query with inherited target rel
-		 * (cf. inheritance_planner), append_rel_list may contain entries for
-		 * tables that are not part of the current subquery and hence have no
-		 * RelOptInfo.  Ignore them.  We can ignore dead rels, too.
-		 */
-		if (parentrel == NULL || !IS_SIMPLE_REL(parentrel))
-			continue;
-
-		/* Verify that children are processed before grandchildren */
-#ifdef USE_ASSERT_CHECKING
-		prev_parents = bms_add_member(prev_parents, appinfo->parent_relid);
-		Assert(!bms_is_member(appinfo->child_relid, prev_parents));
-#endif
-
-		/* OK, propagate info down */
-		Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
-		Assert(childrel->direct_lateral_relids == NULL);
-		childrel->direct_lateral_relids = parentrel->direct_lateral_relids;
-		Assert(childrel->lateral_relids == NULL);
-		childrel->lateral_relids = parentrel->lateral_relids;
-		Assert(childrel->lateral_referencers == NULL);
-		childrel->lateral_referencers = parentrel->lateral_referencers;
-	}
 }
 
 
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index c7f2323852..ae570e6abe 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -284,6 +284,16 @@ query_planner(PlannerInfo *root, List *tlist,
 	}
 
 	/*
+	 * Expand RT entries that represent inherited or partitioned tables.
+	 * This will perform partition pruning on partitioned tables in the
+	 * original range table and also recursively on any child partitioned
+	 * tables that were added by the expansion of the original parent(s).
+	 * As new entries are added to the range table, various arrays in the
+	 * PlannerInfo will be expanded accordingly.
+	 */
+	expand_inherited_tables(root);
+
+	/*
 	 * Add child subroots needed during planning for individual child targets.
 	 */
 	if (parse->resultRelation &&
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 6a6403d7fe..ce0ced792f 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -25,6 +25,7 @@
 #include "access/table.h"
 #include "access/xact.h"
 #include "catalog/pg_constraint.h"
+#include "catalog/pg_inherits.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "executor/executor.h"
@@ -131,6 +132,8 @@ 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, double tuple_fraction);
+static Path *create_single_target_modifytable_path(PlannerInfo *root,
+									  RelOptInfo *final_rel, Path *subpath);
 static grouping_sets_data *preprocess_grouping_sets(PlannerInfo *root);
 static List *remap_to_groupclause_idx(List *groupClause, List *gsets,
 						 int *tleref_to_colnum_map);
@@ -719,27 +722,24 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 		}
 		if (rte->lateral)
 			root->hasLateralRTEs = true;
+
+		/*
+		 * While at it, also update the inh status.  If the relation doesn't
+		 * or can't have any children, there is no point in letting inh be set
+		 * to true.  Note that we do this before processing rowmarks, so that
+		 * the correct information for setting isParent field of PlanRowMarks.
+		 */
+		if (rte->rtekind == RTE_RELATION)
+			rte->inh = rte->inh && has_subclass(rte->relid);
 	}
 
 	/*
 	 * Preprocess RowMark information.  We need to do this after subquery
-	 * pullup (so that all non-inherited RTEs are present) and before
-	 * inheritance expansion (so that the info is available for
-	 * expand_inherited_tables to examine and modify).
+	 * pullup (so that all non-inherited RTEs are present).
 	 */
 	preprocess_rowmarks(root);
 
 	/*
-	 * Expand any rangetable entries that are inheritance sets into "append
-	 * relations".  This can add entries to the rangetable, but they must be
-	 * plain RTE_RELATION entries, so it's OK (and marginally more efficient)
-	 * to do it after checking for joins and other special RTEs.  We must do
-	 * this after pulling up subqueries, else we'd fail to handle inherited
-	 * tables in subqueries.
-	 */
-	expand_inherited_tables(root);
-
-	/*
 	 * Set hasHavingQual to remember if HAVING clause is present.  Needed
 	 * because preprocess_expression will reduce a constant-true condition to
 	 * an empty qual list ... but "HAVING TRUE" is not a semantic no-op.
@@ -1247,7 +1247,9 @@ inheritance_planner(PlannerInfo *root)
 	 * of the parent.
 	 */
 	root->unexpanded_tlist = list_copy(root->parse->targetList);
-	tlist = preprocess_targetlist(root);
+
+	/* We haven't expanded inheritance yet, so pass false. */
+	tlist = preprocess_targetlist(root, false);
 	root->processed_tlist = tlist;
 	qp_extra.tlist = tlist;
 	qp_extra.activeWindows = NIL;
@@ -1257,16 +1259,47 @@ inheritance_planner(PlannerInfo *root)
 	/*
 	 * 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.
+	 * For regular inheritance case, it's possible that we only need to modify
+	 * the parent table.
 	 */
-	if (IS_DUMMY_REL(planned_rel))
+	parent_rte = planner_rt_fetch(top_parentRTindex, root);
+	if (!parent_rte->inh || IS_DUMMY_REL(planned_rel))
 	{
+		/* Top-level query targetlit determines final_rel's PathTarget. */
+		PathTarget *scanjoin_target = create_pathtarget(root, tlist);
+
 		/*
 		 * Result path, even if dummy, must go into outer query's FINAL
 		 * upperrel.
 		 */
 		final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);
-		final_rel->reltarget = create_pathtarget(root, tlist);
-		mark_dummy_rel(final_rel);
+		final_rel->reltarget = scanjoin_target;
+
+		/* Check if we need projection to produce final PathTarget? */
+		if (!IS_DUMMY_REL(planned_rel))
+		{
+			bool	tlist_exprs_same = equal(planned_rel->reltarget->exprs,
+											 scanjoin_target->exprs);
+
+			apply_scanjoin_target_to_paths_guts(root, planned_rel,
+												scanjoin_target,
+												tlist_exprs_same);
+
+			/* Add ModifyTable on top of each path and add to final_rel. */
+			foreach(lc1, planned_rel->pathlist)
+			{
+				Path   *path = (Path *) lfirst(lc1);
+
+				path = create_single_target_modifytable_path(root, final_rel,
+															 path);
+				add_path(final_rel, path);
+			}
+		}
+		else
+		{
+			/* Empty Result path. */
+			mark_dummy_rel(final_rel);
+		}
 		return;
 	}
 
@@ -1280,7 +1313,6 @@ 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 = planner_rt_fetch(top_parentRTindex, root);
 	if (parent_rte->relkind == RELKIND_PARTITIONED_TABLE)
 	{
 		nominalRelation = top_parentRTindex;
@@ -1627,8 +1659,11 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 				parse->groupClause = preprocess_groupclause(root, NIL);
 		}
 
-		/* Preprocess targetlist */
-		tlist = preprocess_targetlist(root);
+		/*
+		 * Preprocess targetlist.  Haven't expanded inheritance yet, so pass
+		 * false.
+		 */
+		tlist = preprocess_targetlist(root, false);
 
 		/*
 		 * We are now done hacking up the query's targetlist.  Most of the
@@ -1987,60 +2022,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 		/* If this is an INSERT/UPDATE/DELETE, add the ModifyTable node. */
 		if (parse->commandType != CMD_SELECT)
 		{
-			Index		rootRelation;
-			List	   *withCheckOptionLists;
-			List	   *returningLists;
-			List	   *rowMarks;
-
-			/*
-			 * If target is a partition root table, we need to mark the
-			 * ModifyTable node appropriately for that.
-			 */
-			if (rt_fetch(parse->resultRelation, parse->rtable)->relkind ==
-				RELKIND_PARTITIONED_TABLE)
-				rootRelation = parse->resultRelation;
-			else
-				rootRelation = 0;
-
-			/*
-			 * Set up the WITH CHECK OPTION and RETURNING lists-of-lists, if
-			 * needed.
-			 */
-			if (parse->withCheckOptions)
-				withCheckOptionLists = list_make1(parse->withCheckOptions);
-			else
-				withCheckOptionLists = NIL;
-
-			if (parse->returningList)
-				returningLists = list_make1(parse->returningList);
-			else
-				returningLists = NIL;
-
-			/*
-			 * 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 ModifyTable do that.
-			 */
-			if (parse->rowMarks)
-				rowMarks = NIL;
-			else
-				rowMarks = root->rowMarks;
-
-			path = (Path *)
-				create_modifytable_path(root, final_rel,
-										parse->commandType,
-										parse->canSetTag,
-										parse->resultRelation,
-										rootRelation,
-										false,
-										list_make1_int(parse->resultRelation),
-										list_make1(path),
-										list_make1(root),
-										withCheckOptionLists,
-										returningLists,
-										rowMarks,
-										parse->onConflict,
-										assign_special_exec_param(root));
+			path = create_single_target_modifytable_path(root, final_rel,
+														 path);
 		}
 
 		/* And shove it into final_rel */
@@ -2081,6 +2064,66 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 	/* Note: currently, we leave it to callers to do set_cheapest() */
 }
 
+static Path *
+create_single_target_modifytable_path(PlannerInfo *root,
+									  RelOptInfo *final_rel,
+									  Path *subpath)
+{
+	Query	   *parse = root->parse;
+	Index		rootRelation;
+	List	   *withCheckOptionLists;
+	List	   *returningLists;
+	List	   *rowMarks;
+
+	/*
+	 * If target is a partition root table, we need to mark the ModifyTable
+	 * node appropriately for that.
+	 */
+	if (rt_fetch(parse->resultRelation, parse->rtable)->relkind ==
+		RELKIND_PARTITIONED_TABLE)
+		rootRelation = parse->resultRelation;
+	else
+		rootRelation = 0;
+
+	/*
+	 * Set up the WITH CHECK OPTION and RETURNING lists-of-lists, if needed.
+	 */
+	if (parse->withCheckOptions)
+		withCheckOptionLists = list_make1(parse->withCheckOptions);
+	else
+		withCheckOptionLists = NIL;
+
+	if (parse->returningList)
+		returningLists = list_make1(parse->returningList);
+	else
+		returningLists = NIL;
+
+	/*
+	 * 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 ModifyTable do that.
+	 */
+	if (parse->rowMarks)
+		rowMarks = NIL;
+	else
+		rowMarks = root->rowMarks;
+
+	return (Path *) create_modifytable_path(root, final_rel,
+											parse->commandType,
+											parse->canSetTag,
+											parse->resultRelation,
+											rootRelation,
+											false,
+											list_make1_int(parse->resultRelation),
+											list_make1(subpath),
+											list_make1(root),
+											withCheckOptionLists,
+											returningLists,
+											rowMarks,
+											parse->onConflict,
+											assign_special_exec_param(root));
+}
+
 /*
  * Do preprocessing for groupingSets clause and related data.  This handles the
  * preliminary steps of expanding the grouping sets, organizing them into lists
@@ -2406,7 +2449,7 @@ preprocess_rowmarks(PlannerInfo *root)
 		newrc->allMarkTypes = (1 << newrc->markType);
 		newrc->strength = rc->strength;
 		newrc->waitPolicy = rc->waitPolicy;
-		newrc->isParent = false;
+		newrc->isParent = rte->inh;
 
 		prowmarks = lappend(prowmarks, newrc);
 	}
@@ -2431,7 +2474,7 @@ preprocess_rowmarks(PlannerInfo *root)
 		newrc->allMarkTypes = (1 << newrc->markType);
 		newrc->strength = LCS_NONE;
 		newrc->waitPolicy = LockWaitBlock;	/* doesn't matter */
-		newrc->isParent = false;
+		newrc->isParent = rte->rtekind == RTE_RELATION ? rte->inh : false;
 
 		prowmarks = lappend(prowmarks, newrc);
 	}
@@ -6988,6 +7031,10 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
 		RelOptInfo *child_grouped_rel;
 		RelOptInfo *child_partially_grouped_rel;
 
+		/* Skip processing pruned partitions. */
+		if (child_input_rel == NULL)
+			continue;
+
 		/* Input child rel must have a path */
 		Assert(child_input_rel->pathlist != NIL);
 
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 0213a37670..154ccda432 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -326,6 +326,12 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing)
 													  UPPERREL_FINAL, NULL)))
 					add_rtes_to_flat_rtable(rel->subroot, true);
 			}
+			/*
+			 * A NULL rel also means an unplanned subquery rte, so apply
+			 * flatten_unplanned_rtes.
+			 */
+			else
+				flatten_unplanned_rtes(glob, rte);
 		}
 		rti++;
 	}
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 5392d1a561..41a57d16b2 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -65,9 +65,13 @@ static List *expand_targetlist(List *tlist, int command_type,
  *
  * As a side effect, if there's an ON CONFLICT UPDATE clause, its targetlist
  * is also preprocessed (and updated in-place).
+ *
+ * inheritance_expanded specifies whether inheritance tables are expanded
+ * as of calling this function.  Caller must specify it because that affects
+ * which row marking related junk columns get added to the targetlist.
  */
 List *
-preprocess_targetlist(PlannerInfo *root)
+preprocess_targetlist(PlannerInfo *root, bool inheritance_expanded)
 {
 	Query	   *parse = root->parse;
 	int			result_relation = parse->resultRelation;
@@ -134,6 +138,15 @@ preprocess_targetlist(PlannerInfo *root)
 		if (rc->rti != rc->prti)
 			continue;
 
+		/*
+		 * For inheritance parent row marks, we defer adding junk columns
+		 * until we've added child row marks, because some children might
+		 * require different row mark types which will change the parent row
+		 * mark's allMarkTypes fields.
+		 */
+		if (rc->isParent && !inheritance_expanded)
+			continue;
+
 		if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY))
 		{
 			/* Need to fetch TID */
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 55eeb5127c..7bed914f35 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -31,12 +31,15 @@
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
+#include "optimizer/clauses.h"
 #include "optimizer/cost.h"
+#include "optimizer/optimizer.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
 #include "optimizer/planner.h"
 #include "optimizer/prep.h"
+#include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
 #include "parser/parse_coerce.h"
 #include "parser/parsetree.h"
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 8914c464d3..16cd65da71 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -33,10 +33,10 @@ typedef struct
 	AppendRelInfo **appinfos;
 } adjust_appendrel_attrs_context;
 
-static void make_inh_translation_list(Relation oldrelation,
-						  Relation newrelation,
-						  Index newvarno,
-						  List **translated_vars);
+static void make_inh_translation_list(TupleDesc old_tupdesc,
+						  TupleDesc new_tupdesc,
+						  Oid from_rel, Oid to_rel,
+						  Index newvarno, List **translated_vars);
 static Node *adjust_appendrel_attrs_mutator(Node *node,
 							   adjust_appendrel_attrs_context *context);
 static List *adjust_inherited_tlist(List *tlist,
@@ -48,18 +48,20 @@ static List *adjust_inherited_tlist(List *tlist,
  *	  Build an AppendRelInfo for the parent-child pair
  */
 AppendRelInfo *
-make_append_rel_info(Relation parentrel, Relation childrel,
-					 Index parentRTindex, Index childRTindex)
+make_append_rel_info(RelOptInfo *parent, RangeTblEntry *parentrte,
+					 TupleDesc childdesc, Oid childoid, Oid childtype,
+					 Index childRTindex)
 {
 	AppendRelInfo *appinfo = makeNode(AppendRelInfo);
 
-	appinfo->parent_relid = parentRTindex;
+	appinfo->parent_relid = parent->relid;
 	appinfo->child_relid = childRTindex;
-	appinfo->parent_reltype = parentrel->rd_rel->reltype;
-	appinfo->child_reltype = childrel->rd_rel->reltype;
-	make_inh_translation_list(parentrel, childrel, childRTindex,
-							  &appinfo->translated_vars);
-	appinfo->parent_reloid = RelationGetRelid(parentrel);
+	appinfo->parent_reltype = parent->reltype;
+	appinfo->child_reltype = childtype;
+	make_inh_translation_list(parent->tupdesc, childdesc,
+							  parentrte->relid, childoid,
+							  childRTindex, &appinfo->translated_vars);
+	appinfo->parent_reloid = parentrte->relid;
 
 	return appinfo;
 }
@@ -72,14 +74,11 @@ make_append_rel_info(Relation parentrel, Relation childrel,
  * For paranoia's sake, we match type/collation as well as attribute name.
  */
 static void
-make_inh_translation_list(Relation oldrelation, Relation newrelation,
-						  Index newvarno,
-						  List **translated_vars)
+make_inh_translation_list(TupleDesc old_tupdesc, TupleDesc new_tupdesc,
+						  Oid from_rel, Oid to_rel,
+						  Index newvarno, List **translated_vars)
 {
 	List	   *vars = NIL;
-	TupleDesc	old_tupdesc = RelationGetDescr(oldrelation);
-	TupleDesc	new_tupdesc = RelationGetDescr(newrelation);
-	Oid			new_relid = RelationGetRelid(newrelation);
 	int			oldnatts = old_tupdesc->natts;
 	int			newnatts = new_tupdesc->natts;
 	int			old_attno;
@@ -109,7 +108,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
 		 * When we are generating the "translation list" for the parent table
 		 * of an inheritance set, no need to search for matches.
 		 */
-		if (oldrelation == newrelation)
+		if (from_rel == to_rel)
 		{
 			vars = lappend(vars, makeVar(newvarno,
 										 (AttrNumber) (old_attno + 1),
@@ -135,10 +134,10 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
 		{
 			HeapTuple	newtup;
 
-			newtup = SearchSysCacheAttName(new_relid, attname);
+			newtup = SearchSysCacheAttName(to_rel, attname);
 			if (!newtup)
 				elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"",
-					 attname, RelationGetRelationName(newrelation));
+					 attname, get_rel_name(to_rel));
 			new_attno = ((Form_pg_attribute) GETSTRUCT(newtup))->attnum - 1;
 			ReleaseSysCache(newtup);
 
@@ -148,10 +147,10 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
 		/* Found it, check type and collation match */
 		if (atttypid != att->atttypid || atttypmod != att->atttypmod)
 			elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type",
-				 attname, RelationGetRelationName(newrelation));
+				 attname, get_rel_name(to_rel));
 		if (attcollation != att->attcollation)
 			elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's collation",
-				 attname, RelationGetRelationName(newrelation));
+				 attname, get_rel_name(to_rel));
 
 		vars = lappend(vars, makeVar(newvarno,
 									 (AttrNumber) (new_attno + 1),
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 1d16a9f2ff..492757fd8c 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -18,35 +18,47 @@
 #include "access/table.h"
 #include "catalog/partition.h"
 #include "catalog/pg_inherits.h"
+#include "catalog/pg_type.h"
 #include "miscadmin.h"
+#include "nodes/makefuncs.h"
 #include "optimizer/appendinfo.h"
+#include "optimizer/clauses.h"
 #include "optimizer/inherit.h"
 #include "optimizer/optimizer.h"
+#include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
+#include "optimizer/plancat.h"
 #include "optimizer/planmain.h"
 #include "optimizer/planner.h"
 #include "optimizer/prep.h"
+#include "optimizer/restrictinfo.h"
+#include "partitioning/partprune.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_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel,
+					  RangeTblEntry *rte, Index rti);
+static void expand_nonpartitioned_inherited_rtentry(PlannerInfo *root,
+						 RangeTblEntry *rte, Index rti, RelOptInfo *rel);
 static void expand_partitioned_rtentry(PlannerInfo *root,
 						   RangeTblEntry *parentrte,
-						   Index parentRTindex, Relation parentrel,
-						   PlanRowMark *top_parentrc, LOCKMODE lockmode,
-						   List **appinfos);
-static void expand_single_inheritance_child(PlannerInfo *root,
-								RangeTblEntry *parentrte,
-								Index parentRTindex, Relation parentrel,
-								PlanRowMark *top_parentrc, Relation childrel,
-								List **appinfos, RangeTblEntry **childrte_p,
-								Index *childRTindex_p);
+						   Index parentRTindex, RelOptInfo *parentrel);
+static RelOptInfo *add_inheritance_child_rel(PlannerInfo *root,
+						  RangeTblEntry *parentrte,
+						  Index parentRTindex, RelOptInfo *parentrel,
+						  PlanRowMark *top_parentrc, Relation childrel,
+						  RangeTblEntry **childrte_p, Index *childRTindex_p);
 static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
 					List *translated_vars);
-
+static RelOptInfo *build_inheritance_child_rel(PlannerInfo *root,
+					   RelOptInfo *parent,
+					   Index childRTindex);
+static List *add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc);
+static bool apply_child_basequals(PlannerInfo *root, RelOptInfo *rel,
+					  RelOptInfo *childrel,
+					  RangeTblEntry *childRTE, AppendRelInfo *appinfo);
 
 /*
  * expand_inherited_tables
@@ -54,37 +66,145 @@ static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
  *		into an "append relation".  At the conclusion of this process,
  *		the "inh" flag is set in all and only those RTEs that are append
  *		relation parents.
+ *
+ * Note that although we're calling the combined output of inheritance set
+ * relations an "append relation" here, the caller may not always want to
+ * combine the relations.  For example, if the parent of the inheritance
+ * set is the query's target relation, each child relation is processed
+ * on its own as the query's target relation.
  */
 void
 expand_inherited_tables(PlannerInfo *root)
 {
-	Index		nrtes;
+	int			orig_rtable_size;
 	Index		rti;
-	ListCell   *rl;
+
+	Assert(root->simple_rel_array_size > 0);
+	orig_rtable_size = root->simple_rel_array_size;
 
 	/*
 	 * expand_inherited_rtentry may add RTEs to parse->rtable. The function is
 	 * expected to recursively handle any RTEs that it creates with inh=true.
 	 * So just scan as far as the original end of the rtable list.
 	 */
-	nrtes = list_length(root->parse->rtable);
-	rl = list_head(root->parse->rtable);
-	for (rti = 1; rti <= nrtes; rti++)
+	for (rti = 1; rti < orig_rtable_size; rti++)
 	{
-		RangeTblEntry *rte = (RangeTblEntry *) lfirst(rl);
+		RelOptInfo *brel = root->simple_rel_array[rti];
+		RangeTblEntry *rte = root->simple_rte_array[rti];
 
-		expand_inherited_rtentry(root, rte, rti);
-		rl = lnext(rl);
+		/* there may be empty slots corresponding to non-baserel RTEs */
+		if (brel == NULL)
+			continue;
+
+		if (rte->inh)
+			expand_inherited_rtentry(root, brel, rte, rti);
 	}
 }
 
 /*
  * expand_inherited_rtentry
- *		Check whether a rangetable entry represents an inheritance set.
- *		If so, add entries for all the child tables to the query's
- *		rangetable, and build AppendRelInfo nodes for all the child tables
- *		and add them to root->append_rel_list.  If not, clear the entry's
- *		"inh" flag to prevent later code from looking for AppendRelInfos.
+ *		This initializes RelOptInfos for inheritance child relations if the
+ *		passed-in relation has any
+ *
+ * 'rel' is the parent relation, whose range table entry ('rte') has been
+ * marked to require adding children.  Parent could either be a subquery (if
+ * we flattened UNION ALL query) or a table that's known to have (or once had)
+ * inheritance children.  The latter consists of both regular inheritance
+ * parents and partitioned tables.
+ *
+ * For a subquery parent, there is not much to be done here because the
+ * children's RTEs are already present in the query, so we just initialize
+ * RelOptInfos for them.  Also, the AppendRelInfos for child subqueries
+ * have already been added.
+ *
+ * For tables, we need to add the children to the range table and initialize
+ * AppendRelInfos, RelOptInfos, and PlanRowMarks (if any) for them.  For
+ * a partitioned parent, we only add the children remaining after pruning.
+ * For regular inheritance parents, we find the children using
+ * find_all_inheritors and add all of them.
+ *
+ * If it turns out that there are no children, then we set rte->inh to false
+ * to let the caller know that only the parent table needs to be scanned.  The
+ * caller can accordingly switch to a non-Append path.  For a partitioned
+ * parent, that means an empty relation because parents themselves contain no
+ * data.
+ *
+ * For the regular inheritance case, the parent also gets another RTE with
+ * inh = false to represent it as a child to be scanned as part of the
+ * inheritance set.  The original RTE is considered to represent the whole
+ * inheritance set.
+ */
+static void
+expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel,
+						 RangeTblEntry *rte, Index rti)
+{
+	Assert(rte->inh);
+	/* Inheritance parent (partitioned or not) or UNION ALL parent subquery. */
+	Assert(rte->rtekind == RTE_RELATION || rte->rtekind == RTE_SUBQUERY);
+
+	/*
+	 * UNION ALL children already got RTEs and AppendRelInfos, so just build
+	 * RelOptInfos and return.
+	 *
+	 * It might be a bit odd that this code is in this, because there is
+	 * nothing to expand really.
+	 */
+	if (rte->rtekind == RTE_SUBQUERY)
+	{
+		ListCell   *l;
+
+		/*
+		 * We don't need to use expand_planner_arrays in this case, because
+		 * no new child RTEs are created.  setup_simple_rel_arrays() and
+		 * setup_append_rel_array would've considered these child RTEs when
+		 * allocating space for various arrays.
+		 */
+		foreach(l, root->append_rel_list)
+		{
+			AppendRelInfo *appinfo = lfirst(l);
+			Index		childRTindex = appinfo->child_relid;
+
+			if (appinfo->parent_relid != rti)
+				continue;
+
+			Assert(childRTindex < root->simple_rel_array_size);
+			Assert(root->simple_rte_array[childRTindex] != NULL);
+
+			/*
+			 * We set the correct value of baserestricinfo and
+			 * baserestrict_min_security below.
+			 */
+			root->simple_rel_array[childRTindex] =
+				build_inheritance_child_rel(root, rel, appinfo->child_relid);
+		}
+	}
+	else
+	{
+		Assert(rte->rtekind == RTE_RELATION);
+		Assert(has_subclass(rte->relid));
+
+		/*
+		 * The rewriter should already have obtained an appropriate lock on
+		 * each relation named in the query.  However, for each child relation
+		 * we add to the query, we must obtain an appropriate lock, because
+		 * this will be the first use of those relations in the
+		 * parse/rewrite/plan pipeline.  Child rels should use the same
+		 * lockmode as their parent.
+		 */
+		Assert(rte->rellockmode != NoLock);
+
+		if (rte->relkind == RELKIND_PARTITIONED_TABLE)
+			expand_partitioned_rtentry(root, rte, rti, rel);
+		else
+			expand_nonpartitioned_inherited_rtentry(root, rte, rti, rel);
+	}
+}
+
+/*
+ * expand_nonpartitioned_inherited_rtentry
+ *		Add entries for all the child tables to the query's rangetable, and
+ *		build AppendRelInfo nodes for all the child tables and add them to
+ *		root->append_rel_list.
  *
  * Note that the original RTE is considered to represent the whole
  * inheritance set.  The first of the generated RTEs is an RTE for the same
@@ -95,55 +215,35 @@ expand_inherited_tables(PlannerInfo *root)
  * regular inheritance, a parent RTE must always have at least two associated
  * AppendRelInfos: one corresponding to the parent table as a simple member of
  * inheritance set and one or more corresponding to the actual children.
- * Since a partitioned table is not scanned, it might have only one associated
- * AppendRelInfo.
  */
 static void
-expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
+expand_nonpartitioned_inherited_rtentry(PlannerInfo *root,
+										RangeTblEntry *rte,
+										Index rti,
+										RelOptInfo *rel)
 {
 	Oid			parentOID;
 	PlanRowMark *oldrc;
-	Relation	oldrelation;
-	LOCKMODE	lockmode;
+	LOCKMODE	lockmode = rte->rellockmode;
 	List	   *inhOIDs;
 	ListCell   *l;
+	int			num_children;
+	int			num_children_added = 0;
 
-	/* Does RT entry allow inheritance? */
-	if (!rte->inh)
-		return;
-	/* Ignore any already-expanded UNION ALL nodes */
-	if (rte->rtekind != RTE_RELATION)
-	{
-		Assert(rte->rtekind == RTE_SUBQUERY);
-		return;
-	}
-	/* Fast path for common case of childless table */
+	Assert(rte->rtekind == RTE_RELATION);
+	Assert(lockmode != NoLock);
 	parentOID = rte->relid;
-	if (!has_subclass(parentOID))
-	{
-		/* Clear flag before returning */
-		rte->inh = false;
-		return;
-	}
-
-	/*
-	 * The rewriter should already have obtained an appropriate lock on each
-	 * relation named in the query.  However, for each child relation we add
-	 * to the query, we must obtain an appropriate lock, because this will be
-	 * the first use of those relations in the parse/rewrite/plan pipeline.
-	 * Child rels should use the same lockmode as their parent.
-	 */
-	lockmode = rte->rellockmode;
 
 	/* Scan for all members of inheritance set, acquire needed locks */
 	inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);
 
 	/*
 	 * Check that there's at least one descendant, else treat as no-child
-	 * case.  This could happen despite above has_subclass() check, if table
-	 * once had a child but no longer does.
+	 * case.  This could happen despite has_subclass() check performed by
+	 * subquery_planner, if table once had a child but no longer does.
 	 */
-	if (list_length(inhOIDs) < 2)
+	num_children = list_length(inhOIDs);
+	if (num_children < 2)
 	{
 		/* Clear flag before returning */
 		rte->inh = false;
@@ -151,216 +251,221 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
 	}
 
 	/*
-	 * If parent relation is selected FOR UPDATE/SHARE, we need to mark its
-	 * PlanRowMark as isParent = true, and generate a new PlanRowMark for each
-	 * child.
+	 * If parent relation is selected FOR UPDATE/SHARE, preprocess_rowmarks
+	 * should've set isParent = true.  We'll generate a new PlanRowMark for
+	 * each child.
 	 */
 	oldrc = get_plan_rowmark(root->rowMarks, rti);
-	if (oldrc)
-		oldrc->isParent = true;
+	Assert(oldrc == NULL || oldrc->isParent);
 
 	/*
-	 * Must open the parent relation to examine its tupdesc.  We need not lock
-	 * it; we assume the rewriter already did.
+	 * Must expand PlannerInfo arrays by num_children before we can add
+	 * children.
 	 */
-	oldrelation = table_open(parentOID, NoLock);
+	Assert(num_children > 0);
+	expand_planner_arrays(root, num_children);
 
-	/* Scan the inheritance set and expand it */
-	if (RelationGetPartitionDesc(oldrelation) != NULL)
+	foreach(l, inhOIDs)
 	{
-		Assert(rte->relkind == RELKIND_PARTITIONED_TABLE);
-
-		/*
-		 * If this table has partitions, recursively expand them in the order
-		 * in which they appear in the PartitionDesc.  While at it, also
-		 * extract the partition key columns of all the partitioned tables.
-		 */
-		expand_partitioned_rtentry(root, rte, rti, oldrelation, oldrc,
-								   lockmode, &root->append_rel_list);
-	}
-	else
-	{
-		List	   *appinfos = NIL;
+		Oid			childOID = lfirst_oid(l);
+		Relation	newrelation;
 		RangeTblEntry *childrte;
 		Index		childRTindex;
 
+		/* Already locked above. */
+		newrelation = heap_open(childOID, NoLock);
+
 		/*
-		 * This table has no partitions.  Expand any plain inheritance
-		 * children in the order the OIDs were returned by
-		 * find_all_inheritors.
+		 * It is possible that the parent table has children that are temp
+		 * tables of other backends.  We cannot safely access such tables
+		 * (because of buffering issues), and the best thing to do seems
+		 * to be to silently ignore them.
 		 */
-		foreach(l, inhOIDs)
+		if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation))
 		{
-			Oid			childOID = lfirst_oid(l);
-			Relation	newrelation;
-
-			/* Open rel if needed; we already have required locks */
-			if (childOID != parentOID)
-				newrelation = table_open(childOID, NoLock);
-			else
-				newrelation = oldrelation;
-
-			/*
-			 * It is possible that the parent table has children that are temp
-			 * tables of other backends.  We cannot safely access such tables
-			 * (because of buffering issues), and the best thing to do seems
-			 * to be to silently ignore them.
-			 */
-			if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation))
-			{
-				table_close(newrelation, lockmode);
-				continue;
-			}
-
-			expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc,
-											newrelation,
-											&appinfos, &childrte,
-											&childRTindex);
-
-			/* Close child relations, but keep locks */
-			if (childOID != parentOID)
-				table_close(newrelation, NoLock);
+			table_close(newrelation, lockmode);
+			continue;
 		}
 
-		/*
-		 * If all the children were temp tables, pretend it's a
-		 * non-inheritance situation; we don't need Append node in that case.
-		 * The duplicate RTE we added for the parent table is harmless, so we
-		 * don't bother to get rid of it; ditto for the useless PlanRowMark
-		 * node.
-		 */
-		if (list_length(appinfos) < 2)
-			rte->inh = false;
-		else
-			root->append_rel_list = list_concat(root->append_rel_list,
-												appinfos);
+		(void) add_inheritance_child_rel(root, rte, rti, rel, oldrc,
+										 newrelation, &childrte,
+										 &childRTindex);
+		Assert(childrte != NULL);
+		/* All regular inheritance children are leaf children. */
+		Assert(!childrte->inh);
+		Assert(childRTindex > 0);
 
+		/* Close child relations, but keep locks */
+		heap_close(newrelation, NoLock);
+		num_children_added++;
 	}
 
-	table_close(oldrelation, NoLock);
+	/*
+	 * If all children, including the parent (as child rel), were
+	 * excluded, mark the parent rel as empty.  If all the children were temp
+	 * tables, pretend it's a non-inheritance situation; we don't need Append
+	 * node in that case.  The duplicate RTE we added for the parent table is
+	 * harmless, so we don't bother to get rid of it; ditto for the useless
+	 * PlanRowMark node.
+	 */
+	if (num_children_added == 0)
+		mark_dummy_rel(rel);
+	else if (num_children_added == 1)
+		rte->inh = false;
+
+	/*
+	 * Add junk columns needed by the row mark if any and also add the
+	 * relevant expressions to the root parent's reltarget.
+	 */
+	if (oldrc)
+	{
+		List   *tlist = add_rowmark_junk_columns(root, oldrc);
+
+		build_base_rel_tlists(root, tlist);
+	}
 }
 
 /*
  * expand_partitioned_rtentry
- *		Recursively expand an RTE for a partitioned table.
+ *		Prunes unnecessary partitions of a partitioned table and adds
+ *		remaining ones to the Query and the PlannerInfo
+ *
+ * Partitions are added to the query in order in which they are found in
+ * the parent's PartitionDesc.
+ *
+ * Note: even though only the unpruned partitions will be added to the
+ * resulting plan, this still locks *all* partitions via find_all_inheritors
+ * when this function is called for the root partitioned table.
  */
 static void
 expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
-						   Index parentRTindex, Relation parentrel,
-						   PlanRowMark *top_parentrc, LOCKMODE lockmode,
-						   List **appinfos)
+						   Index parentRTindex, RelOptInfo *parentrel)
 {
+	LOCKMODE	lockmode = parentrte->rellockmode;
+	PlanRowMark *rootrc = NULL;
 	int			i;
-	RangeTblEntry *childrte;
-	Index		childRTindex;
-	PartitionDesc partdesc = RelationGetPartitionDesc(parentrel);
+	Bitmapset  *partindexes;
+	Index		rootParentRTindex = parentrel->inh_root_parent > 0 ?
+									parentrel->inh_root_parent :
+									parentRTindex;
 
-	check_stack_depth();
-
-	/* A partitioned table should always have a partition descriptor. */
-	Assert(partdesc);
-
-	Assert(parentrte->inh);
+	/* If root partitioned table, lock *all* partitions in the tree. */
+	if (parentRTindex == rootParentRTindex)
+		(void) find_all_inheritors(parentrte->relid, lockmode, NULL);
 
 	/*
-	 * Note down whether any partition key cols are being updated. Though it's
-	 * the root partitioned table's updatedCols we are interested in, we
-	 * instead use parentrte to get the updatedCols. This is convenient
-	 * because parentrte already has the root partrel's updatedCols translated
-	 * to match the attribute ordering of parentrel.
+	 * Initialize partitioned_child_rels to contain this RT index.
+	 *
+	 * Note that during the set_append_rel_pathlist() phase, values of the
+	 * indexes of partitioned relations that appear down in the tree will be
+	 * bubbled up into root parent's list so that when we've created Paths for
+	 * all the children, the root table's list will contain all such indexes.
 	 */
-	if (!root->partColsUpdated)
-		root->partColsUpdated =
-			has_partition_attrs(parentrel, parentrte->updatedCols, NULL);
+	parentrel->partitioned_child_rels = list_make1_int(parentRTindex);
 
-	/* First expand the partitioned table itself. */
-	expand_single_inheritance_child(root, parentrte, parentRTindex, parentrel,
-									top_parentrc, parentrel,
-									appinfos, &childrte, &childRTindex);
+	/* Perform pruning. */
+	partindexes = prune_append_rel_partitions(parentrel);
+
+	/* Must expand PlannerInfo arrays before we can add children. */
+	if (bms_num_members(partindexes) > 0)
+		expand_planner_arrays(root, bms_num_members(partindexes));
 
 	/*
-	 * If the partitioned table has no partitions, treat this as the
-	 * non-inheritance case.
+	 * For partitioned tables, we also store the partition RelOptInfo
+	 * pointers in the parent's RelOptInfo.
 	 */
-	if (partdesc->nparts == 0)
-	{
-		parentrte->inh = false;
-		return;
-	}
+	parentrel->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) *
+												   parentrel->nparts);
 
-	for (i = 0; i < partdesc->nparts; i++)
+	rootrc = get_plan_rowmark(root->rowMarks, rootParentRTindex);
+	Assert(rootrc == NULL || rootrc->isParent);
+	i = -1;
+	while ((i = bms_next_member(partindexes, i)) >= 0)
 	{
-		Oid			childOID = partdesc->oids[i];
-		Relation	childrel;
+		Oid		childOID = parentrel->part_oids[i];
+		Relation	newrelation;
+		RelOptInfo *childrel;
+		RangeTblEntry *childrte;
+		Index		childRTindex;
 
 		/* Open rel; we already have required locks */
-		childrel = table_open(childOID, NoLock);
+		newrelation = table_open(childOID, NoLock);
+		Assert(!RELATION_IS_OTHER_TEMP(newrelation));
 
 		/*
-		 * Temporary partitions belonging to other sessions should have been
-		 * disallowed at definition, but for paranoia's sake, let's double
-		 * check.
+		 * A partitioned child table with 0 children is a dummy rel, so don't
+		 * bother creating planner objects for it.
 		 */
-		if (RELATION_IS_OTHER_TEMP(childrel))
-			elog(ERROR, "temporary relation from another session found as partition");
+		if (newrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
+			RelationGetPartitionDesc(newrelation)->nparts == 0)
+		{
+			heap_close(newrelation, NoLock);
+			continue;
+		}
 
-		expand_single_inheritance_child(root, parentrte, parentRTindex,
-										parentrel, top_parentrc, childrel,
-										appinfos, &childrte, &childRTindex);
+		childrel = add_inheritance_child_rel(root, parentrte, parentRTindex,
+											 parentrel, rootrc, newrelation,
+											 &childrte, &childRTindex);
+		Assert(childrel != NULL);
+		parentrel->part_rels[i] = childrel;
 
-		/* If this child is itself partitioned, recurse */
-		if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+		/* Close child relations, but keep locks */
+		table_close(newrelation, NoLock);
+
+		/* If the child is partitioned itself, expand it too. */
+		if (childrel->part_scheme)
+		{
+			Assert(childrte->inh);
 			expand_partitioned_rtentry(root, childrte, childRTindex,
-									   childrel, top_parentrc, lockmode,
-									   appinfos);
+									   childrel);
+		}
+	}
 
-		/* Close child relation, but keep locks */
-		table_close(childrel, NoLock);
+	/*
+	 * Add junk columns needed by the row mark if any and also add the
+	 * relevant expressions to the root parent's reltarget.
+	 */
+	if (rootrc)
+	{
+		List   *tlist = add_rowmark_junk_columns(root, rootrc);
+
+		build_base_rel_tlists(root, tlist);
 	}
 }
 
 /*
- * expand_single_inheritance_child
- *		Build a RangeTblEntry and an AppendRelInfo, if appropriate, plus
- *		maybe a PlanRowMark.
+ * add_inheritance_child_rel
+ *		Build a RangeTblEntry, an AppendRelInfo, a PlanRowMark, and finally
+ *		a RelOptInfo for an inheritance child relation.
  *
- * We now expand the partition hierarchy level by level, creating a
- * corresponding hierarchy of AppendRelInfos and RelOptInfos, where each
- * partitioned descendant acts as a parent of its immediate partitions.
- * (This is a difference from what older versions of PostgreSQL did and what
- * is still done in the case of table inheritance for unpartitioned tables,
- * where the hierarchy is flattened during RTE expansion.)
+ * The return value is the RelOptInfo that's added.
  *
  * PlanRowMarks still carry the top-parent's RTI, and the top-parent's
  * allMarkTypes field still accumulates values from all descendents.
  *
  * "parentrte" and "parentRTindex" are immediate parent's RTE and
  * RTI. "top_parentrc" is top parent's PlanRowMark.
- *
- * The child RangeTblEntry and its RTI are returned in "childrte_p" and
- * "childRTindex_p" resp.
  */
-static void
-expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
-								Index parentRTindex, Relation parentrel,
-								PlanRowMark *top_parentrc, Relation childrel,
-								List **appinfos, RangeTblEntry **childrte_p,
-								Index *childRTindex_p)
+static RelOptInfo *
+add_inheritance_child_rel(PlannerInfo *root, RangeTblEntry *parentrte,
+						  Index parentRTindex, RelOptInfo *parentrel,
+						  PlanRowMark *top_parentrc, Relation childrel,
+						  RangeTblEntry **childrte_p, Index *childRTindex_p)
 {
 	Query	   *parse = root->parse;
-	Oid			parentOID = RelationGetRelid(parentrel);
 	Oid			childOID = RelationGetRelid(childrel);
 	RangeTblEntry *childrte;
 	Index		childRTindex;
 	AppendRelInfo *appinfo;
+	RelOptInfo *childrelopt;
 
 	/*
 	 * Build an RTE for the child, and attach to query's rangetable list. We
 	 * copy most fields of the parent's RTE, but replace relation OID and
-	 * relkind, and set inh = false.  Also, set requiredPerms to zero since
-	 * all required permissions checks are done on the original RTE. Likewise,
-	 * set the child's securityQuals to empty, because we only want to apply
-	 * the parent's RLS conditions regardless of what RLS properties
+	 * relkind, and set inh appropriately.  Also, set requiredPerms to zero
+	 * since all required permissions checks are done on the original RTE.
+	 * Likewise, set the child's securityQuals to empty, because we only want
+	 * to apply the parent's RLS conditions regardless of what RLS properties
 	 * individual children may have.  (This is an intentional choice to make
 	 * inherited RLS work like regular permissions checks.) The parent
 	 * securityQuals will be propagated to children along with other base
@@ -370,49 +475,42 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	*childrte_p = childrte;
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
-	/* A partitioned child will need to be expanded further. */
-	if (childOID != parentOID &&
-		childrte->relkind == RELKIND_PARTITIONED_TABLE)
-		childrte->inh = true;
-	else
-		childrte->inh = false;
+	/*
+	 * A partitioned child will need to be expanded as an append parent
+	 * itself, so set its inh to true.
+	 */
+	childrte->inh = (childrte->relkind == RELKIND_PARTITIONED_TABLE);
 	childrte->requiredPerms = 0;
 	childrte->securityQuals = NIL;
 	parse->rtable = lappend(parse->rtable, childrte);
 	childRTindex = list_length(parse->rtable);
 	*childRTindex_p = childRTindex;
 
-	/*
-	 * We need an AppendRelInfo if paths will be built for the child RTE. If
-	 * childrte->inh is true, then we'll always need to generate append paths
-	 * for it.  If childrte->inh is false, we must scan it if it's not a
-	 * partitioned table; but if it is a partitioned table, then it never has
-	 * any data of its own and need not be scanned.
-	 */
-	if (childrte->relkind != RELKIND_PARTITIONED_TABLE || childrte->inh)
-	{
-		appinfo = make_append_rel_info(parentrel, childrel,
-									   parentRTindex, childRTindex);
-		*appinfos = lappend(*appinfos, appinfo);
+	/* Create an AppendRelInfo and add it to planner's global list. */
+	appinfo = make_append_rel_info(parentrel, parentrte,
+								   RelationGetDescr(childrel),
+								   RelationGetRelid(childrel),
+								   RelationGetForm(childrel)->reltype,
+								   childRTindex);
+	root->append_rel_list = lappend(root->append_rel_list, appinfo);
 
-		/*
-		 * Translate the column permissions bitmaps to the child's attnums (we
-		 * have to build the translated_vars list before we can do this). But
-		 * if this is the parent table, leave copyObject's result alone.
-		 *
-		 * Note: we need to do this even though the executor won't run any
-		 * permissions checks on the child RTE.  The insertedCols/updatedCols
-		 * bitmaps may be examined for trigger-firing purposes.
-		 */
-		if (childOID != parentOID)
-		{
-			childrte->selectedCols = translate_col_privs(parentrte->selectedCols,
-														 appinfo->translated_vars);
-			childrte->insertedCols = translate_col_privs(parentrte->insertedCols,
-														 appinfo->translated_vars);
-			childrte->updatedCols = translate_col_privs(parentrte->updatedCols,
-														appinfo->translated_vars);
-		}
+	/*
+	 * Translate the column permissions bitmaps to the child's attnums (we
+	 * have to build the translated_vars list before we can do this). But
+	 * if this is the parent table, leave copyObject's result alone.
+	 *
+	 * Note: we need to do this even though the executor won't run any
+	 * permissions checks on the child RTE.  The insertedCols/updatedCols
+	 * bitmaps may be examined for trigger-firing purposes.
+	 */
+	if (childrte->relid != parentrte->relid)
+	{
+		childrte->selectedCols = translate_col_privs(parentrte->selectedCols,
+													 appinfo->translated_vars);
+		childrte->insertedCols = translate_col_privs(parentrte->insertedCols,
+													 appinfo->translated_vars);
+		childrte->updatedCols = translate_col_privs(parentrte->updatedCols,
+													appinfo->translated_vars);
 	}
 
 	/*
@@ -444,6 +542,147 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 
 		root->rowMarks = lappend(root->rowMarks, childrc);
 	}
+
+	/*
+	 * Add the RelOptInfo.  Even though we may not really scan this relation
+	 * for reasons such as contradictory quals, we still need to create one,
+	 * because for every RTE in the query's range table, there must be an
+	 * accompanying RelOptInfo.
+	 */
+
+	/* First, store the RTE and appinfos into planner arrays. */
+	Assert(root->simple_rte_array[childRTindex] == NULL);
+	root->simple_rte_array[childRTindex] = childrte;
+	Assert(root->append_rel_array[childRTindex] == NULL);
+	root->append_rel_array[childRTindex] = appinfo;
+
+	childrelopt = build_inheritance_child_rel(root, parentrel, childRTindex);
+	Assert(childrelopt != NULL);
+
+	return childrelopt;
+}
+
+/*
+ *	build_inheritance_child_rel
+ *		Build a RelOptInfo for child relation of an inheritance set
+ *
+ * After creating the RelOptInfo for the given child RT index, it goes on to
+ * initialize some of its fields based on the parent RelOptInfo.
+ *
+ * If the quals in baserestrictinfo turn out to be self-contradictory,
+ * RelOptInfo is marked dummy before returning.
+ */
+static RelOptInfo *
+build_inheritance_child_rel(PlannerInfo *root,
+					   RelOptInfo *parent,
+					   Index childRTindex)
+{
+	RelOptInfo *childrel;
+	RangeTblEntry *childRTE = root->simple_rte_array[childRTindex];
+	AppendRelInfo *appinfo = root->append_rel_array[childRTindex];
+
+	/* Build the RelOptInfo. */
+	childrel = build_simple_rel(root, childRTindex, parent);
+
+	/*
+	 * Propagate lateral_relids and lateral_referencers from appendrel
+	 * parent rels to their child rels.  We intentionally give each child rel
+	 * the same minimum parameterization, even though it's quite possible that
+	 * some don't reference all the lateral rels.  This is because any append
+	 * path for the parent will have to have the same parameterization for
+	 * every child anyway, and there's no value in forcing extra
+	 * reparameterize_path() calls.  Similarly, a lateral reference to the
+	 * parent prevents use of otherwise-movable join rels for each child.
+	 *
+	 * It's possible for child rels to have their own children, in which case
+	 * the topmost parent's lateral info must be propagated all the way down.
+	 * That's ensured by having childrel be expanded via this same path.
+	 */
+	childrel->direct_lateral_relids = parent->direct_lateral_relids;
+	childrel->lateral_relids = parent->lateral_relids;
+	childrel->lateral_referencers = parent->lateral_referencers;
+
+	/*
+	 * We have to copy the parent's quals to the child, with appropriate
+	 * substitution of variables.  However, only the baserestrictinfo
+	 * quals are needed before we can check for constraint exclusion; so
+	 * do that first and then check to see if we can disregard this child.
+	 */
+	if (!apply_child_basequals(root, parent, 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);
+	}
+
+	return childrel;
+}
+
+/*
+ * add_rowmark_junk_columns
+ * 		Add necessary junk columns for rowmarked inheritance parent rel.
+ *
+ * These values are needed for locking of rels selected FOR UPDATE/SHARE, and
+ * to do EvalPlanQual rechecking.  See comments for PlanRowMark in
+ * plannodes.h.
+ */
+static List *
+add_rowmark_junk_columns(PlannerInfo *root, PlanRowMark *rc)
+{
+	List   *tlist = root->processed_tlist;
+	Var		   *var;
+	char		resname[32];
+	TargetEntry *tle;
+
+	if (rc->allMarkTypes & ~(1 << ROW_MARK_COPY))
+	{
+		/* Need to fetch TID */
+		var = makeVar(rc->rti,
+					  SelfItemPointerAttributeNumber,
+					  TIDOID,
+					  -1,
+					  InvalidOid,
+					  0);
+		snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId);
+		tle = makeTargetEntry((Expr *) var,
+							  list_length(tlist) + 1,
+							  pstrdup(resname),
+							  true);
+		tlist = lappend(tlist, tle);
+	}
+	if (rc->allMarkTypes & (1 << ROW_MARK_COPY))
+	{
+		/* Need the whole row as a junk var */
+		var = makeWholeRowVar(root->simple_rte_array[rc->rti],
+							  rc->rti,
+							  0,
+							  false);
+		snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);
+		tle = makeTargetEntry((Expr *) var,
+							  list_length(tlist) + 1,
+							  pstrdup(resname),
+							  true);
+		tlist = lappend(tlist, tle);
+	}
+
+	/* For inheritance cases, always fetch the tableoid too. */
+	var = makeVar(rc->rti,
+				  TableOidAttributeNumber,
+				  OIDOID,
+				  -1,
+				  InvalidOid,
+				  0);
+	snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId);
+	tle = makeTargetEntry((Expr *) var,
+						  list_length(tlist) + 1,
+						  pstrdup(resname),
+						  true);
+	tlist = lappend(tlist, tle);
+
+	return tlist;
 }
 
 /*
@@ -597,7 +836,7 @@ create_inherited_target_child_root(PlannerInfo *root, AppendRelInfo *appinfo)
 	 * column, filling placeholder entries for dropped columns, etc., using
 	 * the child's TupleDesc.
 	 */
-	tlist = preprocess_targetlist(subroot);
+	tlist = preprocess_targetlist(subroot, true);
 	subroot->processed_tlist = tlist;
 
 	/*
@@ -619,3 +858,131 @@ create_inherited_target_child_root(PlannerInfo *root, AppendRelInfo *appinfo)
 
 	return subroot;
 }
+
+/*
+ * apply_child_basequals
+ *		Populate childrel's quals based on rel's quals, translating them using
+ *		appinfo.
+ *
+ * If any of the resulting clauses evaluate to false or NULL, we return false
+ * and don't apply any quals.  Caller can mark the relation as a dummy rel in
+ * this case, since it needn't be scanned.
+ *
+ * If any resulting clauses evaluate to true, they're unnecessary and we don't
+ * apply then.
+ */
+static bool
+apply_child_basequals(PlannerInfo *root, RelOptInfo *rel,
+					  RelOptInfo *childrel, RangeTblEntry *childRTE,
+					  AppendRelInfo *appinfo)
+{
+	List	   *childquals;
+	Index		cq_min_security;
+	ListCell   *lc;
+
+	/*
+	 * The child rel's targetlist might contain non-Var expressions, which
+	 * means that substitution into the quals could produce opportunities for
+	 * const-simplification, and perhaps even pseudoconstant quals. Therefore,
+	 * transform each RestrictInfo separately to see if it reduces to a
+	 * constant or pseudoconstant.  (We must process them separately to keep
+	 * track of the security level of each qual.)
+	 */
+	childquals = NIL;
+	cq_min_security = UINT_MAX;
+	foreach(lc, rel->baserestrictinfo)
+	{
+		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+		Node	   *childqual;
+		ListCell   *lc2;
+
+		Assert(IsA(rinfo, RestrictInfo));
+		childqual = adjust_appendrel_attrs(root,
+										   (Node *) rinfo->clause,
+										   1, &appinfo);
+		childqual = eval_const_expressions(root, childqual);
+		/* check for flat-out constant */
+		if (childqual && IsA(childqual, Const))
+		{
+			if (((Const *) childqual)->constisnull ||
+				!DatumGetBool(((Const *) childqual)->constvalue))
+			{
+				/* Restriction reduces to constant FALSE or NULL */
+				return false;
+			}
+			/* Restriction reduces to constant TRUE, so drop it */
+			continue;
+		}
+		/* might have gotten an AND clause, if so flatten it */
+		foreach(lc2, make_ands_implicit((Expr *) childqual))
+		{
+			Node	   *onecq = (Node *) lfirst(lc2);
+			bool		pseudoconstant;
+
+			/* check for pseudoconstant (no Vars or volatile functions) */
+			pseudoconstant =
+				!contain_vars_of_level(onecq, 0) &&
+				!contain_volatile_functions(onecq);
+			if (pseudoconstant)
+			{
+				/* tell createplan.c to check for gating quals */
+				root->hasPseudoConstantQuals = true;
+			}
+			/* reconstitute RestrictInfo with appropriate properties */
+			childquals = lappend(childquals,
+								 make_restrictinfo((Expr *) onecq,
+												   rinfo->is_pushed_down,
+												   rinfo->outerjoin_delayed,
+												   pseudoconstant,
+												   rinfo->security_level,
+												   NULL, NULL, NULL));
+			/* track minimum security level among child quals */
+			cq_min_security = Min(cq_min_security, rinfo->security_level);
+		}
+	}
+
+	/*
+	 * In addition to the quals inherited from the parent, we might have
+	 * securityQuals associated with this particular child node. (Currently
+	 * this can only happen in appendrels originating from UNION ALL;
+	 * inheritance child tables don't have their own securityQuals, see
+	 * expand_inherited_rtentry().)	Pull any such securityQuals up into the
+	 * baserestrictinfo for the child.  This is similar to
+	 * process_security_barrier_quals() for the parent rel, except that we
+	 * can't make any general deductions from such quals, since they don't
+	 * hold for the whole appendrel.
+	 */
+	if (childRTE->securityQuals)
+	{
+		Index		security_level = 0;
+
+		foreach(lc, childRTE->securityQuals)
+		{
+			List	   *qualset = (List *) lfirst(lc);
+			ListCell   *lc2;
+
+			foreach(lc2, qualset)
+			{
+				Expr	   *qual = (Expr *) lfirst(lc2);
+
+				/* not likely that we'd see constants here, so no check */
+				childquals = lappend(childquals,
+									 make_restrictinfo(qual,
+													   true, false, false,
+													   security_level,
+													   NULL, NULL, NULL));
+				cq_min_security = Min(cq_min_security, security_level);
+			}
+			security_level++;
+		}
+		Assert(security_level <= root->qual_security_level);
+	}
+
+	/*
+	 * OK, we've got all the baserestrictinfo quals for this child.
+	 */
+	childrel->baserestrictinfo = childquals;
+	childrel->baserestrict_min_security = cq_min_security;
+
+	return true;
+}
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index d8c1981b4d..83c6a956b7 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -107,20 +107,20 @@ static void set_baserel_partition_key_exprs(Relation relation,
  * important for it.
  */
 void
-get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
-				  RelOptInfo *rel)
+get_relation_info(PlannerInfo *root, RangeTblEntry *rte, RelOptInfo *rel)
 {
 	Index		varno = rel->relid;
 	Relation	relation;
 	bool		hasindex;
 	List	   *indexinfos = NIL;
+	bool		inhparent = rte->inh;
 
 	/*
 	 * We need not lock the relation since it was already locked, either by
 	 * the rewriter or when expand_inherited_rtentry() added it to the query's
 	 * rangetable.
 	 */
-	relation = table_open(relationObjectId, NoLock);
+	relation = heap_open(rte->relid, NoLock);
 
 	/* Temporary and unlogged relations are inaccessible during recovery. */
 	if (!RelationNeedsWAL(relation) && RecoveryInProgress())
@@ -446,11 +446,32 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 	get_relation_foreign_keys(root, rel, relation, inhparent);
 
 	/*
-	 * Collect info about relation's partitioning scheme, if any. Only
-	 * inheritance parents may be partitioned.
+	 * Collect some additional information for inheritance parents.
 	 */
-	if (inhparent && relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
-		set_relation_partition_info(root, rel, relation);
+	if (inhparent)
+	{
+		/*
+		 * We'll need the TupleDesc when initializing the child relation.
+		 * A copy is being made because concurrent changes might drop
+		 * the relcache entry.  That's possible because ALTER TABLE
+		 * child_table NO INHERIT parent_table only requires an
+		 * AccessShareLock on parent_table.
+		 */
+		rel->tupdesc = CreateTupleDescCopy(RelationGetDescr(relation));
+		rel->reltype = RelationGetForm(relation)->reltype;
+
+		/*
+		 * If partitioned, also save the information of partitioning scheme,
+		 * and whether the query updates any of the partition key columns.
+		 */
+		if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+		{
+			set_relation_partition_info(root, rel, relation);
+			root->partColsUpdated |= has_partition_attrs(relation,
+														 rte->updatedCols,
+														 NULL);
+		}
+	}
 
 	table_close(relation, NoLock);
 
@@ -460,7 +481,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 	 * removing an index, or adding a hypothetical index to the indexlist.
 	 */
 	if (get_relation_info_hook)
-		(*get_relation_info_hook) (root, relationObjectId, inhparent, rel);
+		(*get_relation_info_hook) (root, rte->relid, rte->inh, rel);
 }
 
 /*
@@ -2027,16 +2048,20 @@ set_relation_partition_info(PlannerInfo *root, RelOptInfo *rel,
 							Relation relation)
 {
 	PartitionDesc partdesc;
-	PartitionKey partkey;
 
 	Assert(relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
 
 	partdesc = RelationGetPartitionDesc(relation);
-	partkey = RelationGetPartitionKey(relation);
 	rel->part_scheme = find_partition_scheme(root, relation);
 	Assert(partdesc != NULL && rel->part_scheme != NULL);
-	rel->boundinfo = partition_bounds_copy(partdesc->boundinfo, partkey);
 	rel->nparts = partdesc->nparts;
+
+	/*
+	 * Since we must've taken a lock on the table, it's okay to simply copy
+	 * the pointers to relcache data here.
+	 */
+	rel->part_oids = partdesc->oids;
+	rel->boundinfo = partdesc->boundinfo;
 	set_baserel_partition_key_exprs(relation, rel);
 	rel->partition_qual = RelationGetPartitionQual(relation);
 }
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 4130514952..f6fca75677 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -132,6 +132,50 @@ setup_append_rel_array(PlannerInfo *root)
 }
 
 /*
+ * expand_planner_arrays
+ *		Expand the PlannerInfo arrays by add_size members and initialize the
+ *		the	newly added bytes with zero
+ */
+void
+expand_planner_arrays(PlannerInfo *root, int add_size)
+{
+	int		new_size;
+
+	Assert(add_size > 0);
+
+	new_size = root->simple_rel_array_size + add_size;
+
+	/* Expand various arrays and 0-initialize added bytes. */
+	root->simple_rte_array = (RangeTblEntry **)
+							repalloc(root->simple_rte_array,
+									 sizeof(RangeTblEntry *) * new_size);
+	MemSet(root->simple_rte_array + root->simple_rel_array_size,
+		   0, sizeof(RangeTblEntry *) * add_size);
+	root->simple_rel_array = (RelOptInfo **)
+								repalloc(root->simple_rel_array,
+										 sizeof(RelOptInfo *) * new_size);
+	MemSet(root->simple_rel_array + root->simple_rel_array_size,
+		   0, sizeof(RelOptInfo *) * add_size);
+
+	if (root->append_rel_array)
+	{
+		root->append_rel_array = (AppendRelInfo **)
+									repalloc(root->append_rel_array,
+									 sizeof(AppendRelInfo *) * new_size);
+		MemSet(root->append_rel_array + root->simple_rel_array_size,
+			   0, sizeof(AppendRelInfo *) * add_size);
+	}
+	else
+	{
+		root->append_rel_array = (AppendRelInfo **)
+									palloc0(sizeof(AppendRelInfo *) *
+											new_size);
+	}
+
+	root->simple_rel_array_size = new_size;
+}
+
+/*
  * build_simple_rel
  *	  Construct a new RelOptInfo for a base relation or 'other' relation.
  */
@@ -195,6 +239,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->joininfo = NIL;
 	rel->has_eclass_joins = false;
 	rel->consider_partitionwise_join = false;	/* might get changed later */
+	rel->top_parent_relids = NULL;	/* might be changed later */
 	rel->part_scheme = NULL;
 	rel->nparts = 0;
 	rel->boundinfo = NULL;
@@ -203,6 +248,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	rel->partexprs = NULL;
 	rel->nullable_partexprs = NULL;
 	rel->partitioned_child_rels = NIL;
+	rel->inh_root_parent = 0; /* might be changed later */
 
 	/*
 	 * Pass top parent's relids down the inheritance hierarchy. If the parent
@@ -216,16 +262,25 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 			rel->top_parent_relids = parent->top_parent_relids;
 		else
 			rel->top_parent_relids = bms_copy(parent->relids);
+
+		/*
+		 * For inheritance child relations, we also set inh_root_parent.
+		 * Note that 'parent' might itself be a child (a sub-partitioned
+		 * partition), in which case we simply use its value of
+		 * inh_root_parent.
+		 */
+		if (parent->rtekind == RTE_RELATION)
+			rel->inh_root_parent = parent->inh_root_parent > 0 ?
+										parent->inh_root_parent :
+										parent->relid;
 	}
-	else
-		rel->top_parent_relids = NULL;
 
 	/* Check type of rtable entry */
 	switch (rte->rtekind)
 	{
 		case RTE_RELATION:
 			/* Table --- retrieve statistics from the system catalogs */
-			get_relation_info(root, rte->relid, rte->inh, rel);
+			get_relation_info(root, rte, rel);
 			break;
 		case RTE_SUBQUERY:
 		case RTE_FUNCTION:
@@ -273,52 +328,6 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 		root->qual_security_level = Max(root->qual_security_level,
 										list_length(rte->securityQuals));
 
-	/*
-	 * If this rel is an appendrel parent, recurse to build "other rel"
-	 * RelOptInfos for its children.  They are "other rels" because they are
-	 * not in the main join tree, but we will need RelOptInfos to plan access
-	 * to them.
-	 */
-	if (rte->inh)
-	{
-		ListCell   *l;
-		int			nparts = rel->nparts;
-		int			cnt_parts = 0;
-
-		if (nparts > 0)
-			rel->part_rels = (RelOptInfo **)
-				palloc(sizeof(RelOptInfo *) * nparts);
-
-		foreach(l, root->append_rel_list)
-		{
-			AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
-			RelOptInfo *childrel;
-
-			/* append_rel_list contains all append rels; ignore others */
-			if (appinfo->parent_relid != relid)
-				continue;
-
-			childrel = build_simple_rel(root, appinfo->child_relid,
-										rel);
-
-			/* Nothing more to do for an unpartitioned table. */
-			if (!rel->part_scheme)
-				continue;
-
-			/*
-			 * The order of partition OIDs in append_rel_list is the same as
-			 * the order in the PartitionDesc, so the order of part_rels will
-			 * also match the PartitionDesc.  See expand_partitioned_rtentry.
-			 */
-			Assert(cnt_parts < nparts);
-			rel->part_rels[cnt_parts] = childrel;
-			cnt_parts++;
-		}
-
-		/* We should have seen all the child partitions. */
-		Assert(cnt_parts == nparts);
-	}
-
 	return rel;
 }
 
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index 8c9721935d..472a6cd331 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -45,6 +45,8 @@
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/appendinfo.h"
+#include "optimizer/clauses.h"
+#include "optimizer/cost.h"
 #include "optimizer/optimizer.h"
 #include "optimizer/pathnode.h"
 #include "partitioning/partprune.h"
@@ -433,17 +435,23 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
 		 * is, not pruned already).
 		 */
 		subplan_map = (int *) palloc(nparts * sizeof(int));
+		memset(subplan_map, -1, nparts * sizeof(int));
 		subpart_map = (int *) palloc(nparts * sizeof(int));
+		memset(subpart_map, -1, nparts * sizeof(int));
 		present_parts = NULL;
 
 		for (i = 0; i < nparts; i++)
 		{
 			RelOptInfo *partrel = subpart->part_rels[i];
-			int			subplanidx = relid_subplan_map[partrel->relid] - 1;
-			int			subpartidx = relid_subpart_map[partrel->relid] - 1;
+			int			subplanidx;
+			int			subpartidx;
 
-			subplan_map[i] = subplanidx;
-			subpart_map[i] = subpartidx;
+			/* Skip processing pruned partitions. */
+			if (partrel == NULL)
+				continue;
+
+			subplan_map[i] = subplanidx = relid_subplan_map[partrel->relid] - 1;
+			subpart_map[i] = subpartidx = relid_subpart_map[partrel->relid] - 1;
 			if (subplanidx >= 0)
 			{
 				present_parts = bms_add_member(present_parts, i);
@@ -537,23 +545,20 @@ gen_partprune_steps(RelOptInfo *rel, List *clauses, bool *contradictory)
 
 /*
  * prune_append_rel_partitions
- *		Returns RT indexes of the minimum set of child partitions which must
- *		be scanned to satisfy rel's baserestrictinfo quals.
+ *		Returns indexes into rel->part_rels of the minimum set of child
+ *		partitions which must be scanned to satisfy rel's baserestrictinfo
+ *		quals.
  *
  * Callers must ensure that 'rel' is a partitioned table.
  */
-Relids
+Bitmapset *
 prune_append_rel_partitions(RelOptInfo *rel)
 {
-	Relids		result;
 	List	   *clauses = rel->baserestrictinfo;
 	List	   *pruning_steps;
 	bool		contradictory;
 	PartitionPruneContext context;
-	Bitmapset  *partindexes;
-	int			i;
 
-	Assert(clauses != NIL);
 	Assert(rel->part_scheme != NULL);
 
 	/* If there are no partitions, return the empty set */
@@ -561,6 +566,13 @@ prune_append_rel_partitions(RelOptInfo *rel)
 		return NULL;
 
 	/*
+	 * If pruning is disabled or if there are no clauses to prune with,
+	 * return all partitions.
+	 */
+	if (!enable_partition_pruning || clauses == NIL)
+		return bms_add_range(NULL, 0, rel->nparts - 1);
+
+	/*
 	 * Process clauses.  If the clauses are found to be contradictory, we can
 	 * return the empty set.
 	 */
@@ -587,15 +599,7 @@ prune_append_rel_partitions(RelOptInfo *rel)
 	context.evalexecparams = false;
 
 	/* Actual pruning happens here. */
-	partindexes = get_matching_partitions(&context, pruning_steps);
-
-	/* Add selected partitions' RT indexes to result. */
-	i = -1;
-	result = NULL;
-	while ((i = bms_next_member(partindexes, i)) >= 0)
-		result = bms_add_member(result, rel->part_rels[i]->relid);
-
-	return result;
+	return get_matching_partitions(&context, pruning_steps);
 }
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index b219ff8806..339b786f20 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -15,6 +15,7 @@
 #define PATHNODES_H
 
 #include "access/sdir.h"
+#include "access/tupdesc.h"
 #include "fmgr.h"
 #include "lib/stringinfo.h"
 #include "nodes/params.h"
@@ -365,7 +366,6 @@ struct PlannerInfo
 };
 
 
-
 /*
  * 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
@@ -718,11 +718,28 @@ typedef struct RelOptInfo
 	int			nparts;			/* number of partitions */
 	struct PartitionBoundInfoData *boundinfo;	/* Partition bounds */
 	List	   *partition_qual; /* partition constraint */
+	Oid		   *part_oids;		/* partition OIDs */
 	struct RelOptInfo **part_rels;	/* Array of RelOptInfos of partitions,
 									 * stored in the same order of bounds */
 	List	  **partexprs;		/* Non-nullable partition key expressions. */
 	List	  **nullable_partexprs; /* Nullable partition key expressions. */
 	List	   *partitioned_child_rels; /* List of RT indexes. */
+
+	/*
+	 * For inheritance children, this is the RT index of inheritance table
+	 * mentioned in the query from which this relation originated.
+	 * top_parent_relids cannot be used for this, because if the inheritance
+	 * root table is itself under UNION ALL, top_parent_relids contains the
+	 * RT index of UNION ALL parent subquery.
+	 */
+	Index		inh_root_parent;
+
+	/*
+	 * Set only if this is an inheritance parent relation.  This information
+	 * is needed when initializing the planning info for children.
+	 */
+	TupleDesc	tupdesc;		/* A "copy" of the table's tuple desriptor */
+	Oid			reltype;		/* Table's reltype */
 } RelOptInfo;
 
 /*
diff --git a/src/include/optimizer/appendinfo.h b/src/include/optimizer/appendinfo.h
index 790914c1b0..c0b8db9a62 100644
--- a/src/include/optimizer/appendinfo.h
+++ b/src/include/optimizer/appendinfo.h
@@ -17,9 +17,10 @@
 #include "nodes/pathnodes.h"
 #include "utils/relcache.h"
 
-extern AppendRelInfo *make_append_rel_info(Relation parentrel,
-					 Relation childrel,
-					 Index parentRTindex, Index childRTindex);
+extern AppendRelInfo *make_append_rel_info(RelOptInfo *parent,
+					 RangeTblEntry *parentrte,
+					 TupleDesc childdesc, Oid childoid, Oid childtype,
+					 Index childRTindex);
 extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node,
 					   int nappinfos, AppendRelInfo **appinfos);
 extern Node *adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 574bb85b50..3a803b3fd0 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -277,6 +277,7 @@ extern Path *reparameterize_path_by_child(PlannerInfo *root, Path *path,
  */
 extern void setup_simple_rel_arrays(PlannerInfo *root);
 extern void setup_append_rel_array(PlannerInfo *root);
+extern void expand_planner_arrays(PlannerInfo *root, int add_size);
 extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid,
 				 RelOptInfo *parent);
 extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid);
diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h
index c337f047cb..04731f532f 100644
--- a/src/include/optimizer/plancat.h
+++ b/src/include/optimizer/plancat.h
@@ -25,8 +25,8 @@ typedef void (*get_relation_info_hook_type) (PlannerInfo *root,
 extern PGDLLIMPORT get_relation_info_hook_type get_relation_info_hook;
 
 
-extern void get_relation_info(PlannerInfo *root, Oid relationObjectId,
-				  bool inhparent, RelOptInfo *rel);
+extern void get_relation_info(PlannerInfo *root, RangeTblEntry *rte,
+				  RelOptInfo *rel);
 
 extern List *infer_arbiter_indexes(PlannerInfo *root);
 
diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h
index a9b2c9026c..27de05ba3e 100644
--- a/src/include/optimizer/prep.h
+++ b/src/include/optimizer/prep.h
@@ -34,7 +34,8 @@ extern Relids get_relids_for_join(Query *query, int joinrelid);
 /*
  * prototypes for preptlist.c
  */
-extern List *preprocess_targetlist(PlannerInfo *root);
+extern List *preprocess_targetlist(PlannerInfo *root,
+							bool inheritance_expanded);
 
 extern PlanRowMark *get_plan_rowmark(List *rowmarks, Index rtindex);
 
diff --git a/src/test/regress/expected/partition_aggregate.out b/src/test/regress/expected/partition_aggregate.out
index 6bc106831e..1450cef057 100644
--- a/src/test/regress/expected/partition_aggregate.out
+++ b/src/test/regress/expected/partition_aggregate.out
@@ -144,7 +144,7 @@ SELECT c, sum(a) FROM pagg_tab WHERE 1 = 2 GROUP BY c;
            QUERY PLAN           
 --------------------------------
  HashAggregate
-   Group Key: pagg_tab.c
+   Group Key: c
    ->  Result
          One-Time Filter: false
 (4 rows)
@@ -159,7 +159,7 @@ SELECT c, sum(a) FROM pagg_tab WHERE c = 'x' GROUP BY c;
            QUERY PLAN           
 --------------------------------
  GroupAggregate
-   Group Key: pagg_tab.c
+   Group Key: c
    ->  Result
          One-Time Filter: false
 (4 rows)
-- 
2.11.0



  [text/plain] 0004-Teach-planner-to-only-process-unpruned-partitions.patch (6.8K, ../0a70234e-b2e6-debc-2fb5-e8fd927caa00@lab.ntt.co.jp/5-0004-Teach-planner-to-only-process-unpruned-partitions.patch)
  download | inline diff:
From f3d0fa8072550b66739d445d9b5fb67c8ad1e923 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Mon, 15 Oct 2018 10:59:24 +0900
Subject: [PATCH 4/5] Teach planner to only process unpruned partitions

This adds a bitmapset field live_parts to RelOptInfo and stores
the partition indexes of non-dummy partitions in it, meaning it
contains indexes of only those partitions that have a non-NULL
RelOptInfo present in its parent's RelOptInfo's part_rels array.

This speeds up processing partitioned table's partitions compared
to going through the whole part_rels array in a number of places.
---
 src/backend/optimizer/path/joinrels.c |  3 +++
 src/backend/optimizer/plan/planner.c  | 18 +++++++++---------
 src/backend/optimizer/util/inherit.c  |  1 +
 src/backend/optimizer/util/relnode.c  |  3 +++
 src/backend/partitioning/partprune.c  | 18 +++++-------------
 src/include/nodes/pathnodes.h         |  4 ++++
 6 files changed, 25 insertions(+), 22 deletions(-)

diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 6f77d2a0f4..fdbf7689ed 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -1452,6 +1452,9 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 		populate_joinrel_with_paths(root, child_rel1, child_rel2,
 									child_joinrel, child_sjinfo,
 									child_restrictlist);
+		if (!IS_DUMMY_REL(child_joinrel))
+			joinrel->live_parts = bms_add_member(joinrel->live_parts,
+												 cnt_parts);
 	}
 }
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index ce0ced792f..2feb5d5b2d 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -6857,7 +6857,9 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
 		List	   *live_children = NIL;
 
 		/* Adjust each partition. */
-		for (partition_idx = 0; partition_idx < rel->nparts; partition_idx++)
+		partition_idx = -1;
+		while ((partition_idx = bms_next_member(rel->live_parts,
+												partition_idx)) >= 0)
 		{
 			RelOptInfo *child_rel = rel->part_rels[partition_idx];
 			ListCell   *lc;
@@ -6865,9 +6867,7 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
 			int			nappinfos;
 			List	   *child_scanjoin_targets = NIL;
 
-			/* Skip processing pruned partitions. */
-			if (child_rel == NULL)
-				continue;
+			Assert(child_rel != NULL);
 
 			/* Translate scan/join targets for this child. */
 			appinfos = find_appinfos_by_relids(root, child_rel->relids,
@@ -7009,7 +7009,6 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
 									PartitionwiseAggregateType patype,
 									GroupPathExtraData *extra)
 {
-	int			nparts = input_rel->nparts;
 	int			cnt_parts;
 	List	   *grouped_live_children = NIL;
 	List	   *partially_grouped_live_children = NIL;
@@ -7021,7 +7020,9 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
 		   partially_grouped_rel != NULL);
 
 	/* Add paths for partitionwise aggregation/grouping. */
-	for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++)
+	cnt_parts = -1;
+	while ((cnt_parts = bms_next_member(input_rel->live_parts,
+										cnt_parts)) >= 0)
 	{
 		RelOptInfo *child_input_rel = input_rel->part_rels[cnt_parts];
 		PathTarget *child_target = copy_pathtarget(target);
@@ -7031,9 +7032,8 @@ create_partitionwise_grouping_paths(PlannerInfo *root,
 		RelOptInfo *child_grouped_rel;
 		RelOptInfo *child_partially_grouped_rel;
 
-		/* Skip processing pruned partitions. */
-		if (child_input_rel == NULL)
-			continue;
+		/* A live partition must have a RelOptInfo. */
+		Assert(child_input_rel != NULL);
 
 		/* Input child rel must have a path */
 		Assert(child_input_rel->pathlist != NIL);
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 492757fd8c..6e9564d0ce 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -365,6 +365,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
 
 	/* Perform pruning. */
 	partindexes = prune_append_rel_partitions(parentrel);
+	parentrel->live_parts = partindexes;
 
 	/* Must expand PlannerInfo arrays before we can add children. */
 	if (bms_num_members(partindexes) > 0)
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index f6fca75677..03c9ded294 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -1743,6 +1743,9 @@ build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel,
 		joinrel->partexprs[cnt] = partexpr;
 		joinrel->nullable_partexprs[cnt] = nullable_partexpr;
 	}
+
+	/* Partitions will be added by try_partitionwise_join. */
+	joinrel->live_parts = NULL;
 }
 
 /*
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index 472a6cd331..babb7d3406 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -438,29 +438,21 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
 		memset(subplan_map, -1, nparts * sizeof(int));
 		subpart_map = (int *) palloc(nparts * sizeof(int));
 		memset(subpart_map, -1, nparts * sizeof(int));
-		present_parts = NULL;
+		present_parts = bms_copy(subpart->live_parts);
 
-		for (i = 0; i < nparts; i++)
+		i = -1;
+		while ((i = bms_next_member(present_parts, i)) >= 0)
 		{
 			RelOptInfo *partrel = subpart->part_rels[i];
 			int			subplanidx;
 			int			subpartidx;
 
-			/* Skip processing pruned partitions. */
-			if (partrel == NULL)
-				continue;
-
+			Assert(partrel != NULL);
 			subplan_map[i] = subplanidx = relid_subplan_map[partrel->relid] - 1;
 			subpart_map[i] = subpartidx = relid_subpart_map[partrel->relid] - 1;
+			/* Record finding this subplan */
 			if (subplanidx >= 0)
-			{
-				present_parts = bms_add_member(present_parts, i);
-
-				/* Record finding this subplan  */
 				subplansfound = bms_add_member(subplansfound, subplanidx);
-			}
-			else if (subpartidx >= 0)
-				present_parts = bms_add_member(present_parts, i);
 		}
 
 		pinfo = makeNode(PartitionedRelPruneInfo);
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 339b786f20..c3103370ca 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -721,6 +721,10 @@ typedef struct RelOptInfo
 	Oid		   *part_oids;		/* partition OIDs */
 	struct RelOptInfo **part_rels;	/* Array of RelOptInfos of partitions,
 									 * stored in the same order of bounds */
+	Bitmapset  *live_parts;		/* Indexes into part_rels of the non-NULL
+								 * RelOptInfos of unpruned partitions; exists
+								 * to avoid having to iterate over the entire
+								 * part_rels array to filter NULL entries. */
 	List	  **partexprs;		/* Non-nullable partition key expressions. */
 	List	  **nullable_partexprs; /* Nullable partition key expressions. */
 	List	   *partitioned_child_rels; /* List of RT indexes. */
-- 
2.11.0



  [text/plain] 0005-Do-not-lock-all-partitions-at-the-beginning.patch (1.9K, ../0a70234e-b2e6-debc-2fb5-e8fd927caa00@lab.ntt.co.jp/6-0005-Do-not-lock-all-partitions-at-the-beginning.patch)
  download | inline diff:
From 7e56837161cc139008d9a945a89daf513267b299 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Fri, 12 Oct 2018 19:12:53 +0900
Subject: [PATCH 5/5] Do not lock all partitions at the beginning

---
 src/backend/optimizer/util/inherit.c | 15 +++++----------
 1 file changed, 5 insertions(+), 10 deletions(-)

diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 6e9564d0ce..85770226d0 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -332,10 +332,6 @@ expand_nonpartitioned_inherited_rtentry(PlannerInfo *root,
  *
  * Partitions are added to the query in order in which they are found in
  * the parent's PartitionDesc.
- *
- * Note: even though only the unpruned partitions will be added to the
- * resulting plan, this still locks *all* partitions via find_all_inheritors
- * when this function is called for the root partitioned table.
  */
 static void
 expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
@@ -349,10 +345,6 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
 									parentrel->inh_root_parent :
 									parentRTindex;
 
-	/* If root partitioned table, lock *all* partitions in the tree. */
-	if (parentRTindex == rootParentRTindex)
-		(void) find_all_inheritors(parentrte->relid, lockmode, NULL);
-
 	/*
 	 * Initialize partitioned_child_rels to contain this RT index.
 	 *
@@ -389,8 +381,11 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
 		RangeTblEntry *childrte;
 		Index		childRTindex;
 
-		/* Open rel; we already have required locks */
-		newrelation = table_open(childOID, NoLock);
+		/*
+		 * Open rel; this's the first time of opening partitions for this
+		 * query, so take the appropriate locks.
+		 */
+		newrelation = table_open(childOID, lockmode);
 		Assert(!RELATION_IS_OTHER_TEMP(newrelation));
 
 		/*
-- 
2.11.0



view thread (265+ messages)  latest in thread

Message-ID: <0a70234e-b2e6-debc-2fb5-e8fd927caa00@lab.ntt.co.jp>
Permalink:  ../0a70234e-b2e6-debc-2fb5-e8fd927caa00@lab.ntt.co.jp/
Also on:    postgresql.org/message-id/0a70234e-b2e6-debc-2fb5-e8fd927caa00@lab.ntt.co.jp

 ·  · 

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: pgsql-hackers@postgresql.org
  Cc: Langote_Amit_f8@lab.ntt.co.jp, tgl@sss.pgh.pa.us, david.rowley@2ndquadrant.com, imai.yoshikazu@jp.fujitsu.com, amitlangote09@gmail.com, alvherre@2ndquadrant.com
  Subject: Re: speeding up planning with partitions
  In-Reply-To: <0a70234e-b2e6-debc-2fb5-e8fd927caa00@lab.ntt.co.jp>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox