From e448cde136542f7afc34d3e6bf47f66e992aa34b Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Fri, 26 Oct 2018 16:45:59 +0900
Subject: [PATCH v19 2/4] 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.
---
 .../postgres_fdw/expected/postgres_fdw.out    |  32 +-
 src/backend/nodes/outfuncs.c                  |   1 +
 src/backend/optimizer/path/allpaths.c         | 239 +----
 src/backend/optimizer/path/joinrels.c         |  61 ++
 src/backend/optimizer/plan/initsplan.c        |  58 --
 src/backend/optimizer/plan/planmain.c         |  10 +
 src/backend/optimizer/plan/planner.c          |  72 +-
 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          | 839 +++++++++++++-----
 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 +-
 .../regress/expected/partition_aggregate.out  |   4 +-
 21 files changed, 950 insertions(+), 667 deletions(-)

diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index b3894d0760..fde1a9cdca 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -7072,15 +7072,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)
 
@@ -7110,15 +7110,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)
 
@@ -7147,15 +7147,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
@@ -7165,15 +7165,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 e7a65985cf..a58ec885d8 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2271,6 +2271,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 f07db81a80..994cca7110 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);
 
 
 /*
@@ -387,6 +383,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
@@ -968,29 +972,12 @@ set_inherit_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);
@@ -1001,7 +988,6 @@ set_inherit_target_rel_sizes(PlannerInfo *root, RelOptInfo *rel,
 		ListCell   *lc;
 		List	   *translated_exprs,
 				   *child_target_exprs;
-		bool		childpruned;
 
 		/* append_rel_list contains all append rels; ignore others */
 		if (appinfo->parent_relid != parentRTindex)
@@ -1009,32 +995,23 @@ set_inherit_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);
 
-		childpruned = (did_pruning &&
-					   !bms_is_member(appinfo->child_relid, live_children));
-
 		/*
-		 * Unless the child is pruned, we have to copy the parent's targetlist
-		 * and quals to the child, with appropriate substitution of variables.
-		 * If any constant false or NULL clauses turn up, we can disregard the
-		 * child right away.  If not, we can apply constraint exclusion with
-		 * just the baserestrictinfo quals.
+		 * Child relation may have been marked dummy if build_append_child_rel
+		 * found self-contradictory quals or quals that contradict its
+		 * constraints.
 		 */
-		if (childpruned ||
-			!apply_child_basequals(root, rel, childrel, childRTE, appinfo) ||
-			relation_excluded_by_constraints(root, childrel, childRTE))
-		{
-			/* This partition needn't be scanned; skip it. */
-			set_dummy_rel_pathlist(childrel);
+		if (IS_DUMMY_REL(childrel))
 			continue;
-		}
 
 		/*
 		 * Add missing Vars to child's reltarget.
@@ -1192,40 +1169,12 @@ 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();
 
 	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
@@ -1265,7 +1214,6 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 		RelOptInfo *childrel;
 		ListCell   *parentvars;
 		ListCell   *childvars;
-		bool		childpruned;
 
 		/* append_rel_list contains all append rels; ignore others */
 		if (appinfo->parent_relid != parentRTindex)
@@ -1273,34 +1221,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);
 
-		childpruned = (did_pruning &&
-					   !bms_is_member(appinfo->child_relid, live_children));
 		/*
-		 * Unless the child is pruned, we have to copy the parent's targetlist
-		 * and quals to the child, with appropriate substitution of variables.
-		 * If any constant false or NULL clauses turn up, we can disregard the
-		 * child right away.  If not, we can apply constraint exclusion with
-		 * just the baserestrictinfo quals.
+		 * Child relation may have been marked dummy if build_append_child_rel
+		 * found self-contradictory quals or quals that contradict its
+		 * constraints.
 		 */
-		if (childpruned ||
-			!apply_child_basequals(root, rel, childrel, childRTE, appinfo) ||
-			relation_excluded_by_constraints(root, childrel, childRTE))
-		{
-			/* This partition needn't be scanned; skip it. */
-			set_dummy_rel_pathlist(childrel);
+		if (IS_DUMMY_REL(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.
@@ -2837,6 +2777,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))
@@ -3941,134 +3882,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 a02321486f..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
@@ -1680,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 d6ffa7869d..5e8a8b1eee 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -617,64 +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.
-	 */
-	for (rti = 1; rti < root->simple_rel_array_size; rti++)
-	{
-		RelOptInfo *brel = root->simple_rel_array[rti];
-		RangeTblEntry *brte = root->simple_rte_array[rti];
-
-		/*
-		 * Skip empty slots. Also skip non-simple relations i.e. dead
-		 * relations.
-		 */
-		if (brel == NULL || !IS_SIMPLE_REL(brel))
-			continue;
-
-		/*
-		 * In the case of table inheritance, the parent RTE is directly linked
-		 * to every child table via an AppendRelInfo.  In the case of table
-		 * partitioning, the inheritance hierarchy is expanded one level at a
-		 * time rather than flattened.  Therefore, an other member rel that is
-		 * a partitioned table may have children of its own, and must
-		 * therefore be marked with the appropriate lateral info so that those
-		 * children eventually get marked also.
-		 */
-		Assert(brte);
-		if (brel->reloptkind == RELOPT_OTHER_MEMBER_REL &&
-			(brte->rtekind != RTE_RELATION ||
-			 brte->relkind != RELKIND_PARTITIONED_TABLE))
-			continue;
-
-		if (brte->inh)
-		{
-			foreach(lc, root->append_rel_list)
-			{
-				AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
-				RelOptInfo *childrel;
-
-				if (appinfo->parent_relid != rti)
-					continue;
-				childrel = root->simple_rel_array[appinfo->child_relid];
-				Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
-				Assert(childrel->direct_lateral_relids == NULL);
-				childrel->direct_lateral_relids = brel->direct_lateral_relids;
-				Assert(childrel->lateral_relids == NULL);
-				childrel->lateral_relids = brel->lateral_relids;
-				Assert(childrel->lateral_referencers == NULL);
-				childrel->lateral_referencers = brel->lateral_referencers;
-			}
-		}
-	}
 }
 
 
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index acb1461624..7a3b8171c5 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -281,6 +281,16 @@ query_planner(PlannerInfo *root, List *tlist,
 		root->all_baserels = bms_add_member(root->all_baserels, brel->relid);
 	}
 
+	/*
+	 * 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 to use during planning for individual child
 	 * targets
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 71aa8dc72a..c90b62bea1 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"
@@ -717,26 +718,17 @@ 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).
-	 */
-	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);
-
 	/*
 	 * Now that we have figured out "actual" inheritance situation of the
 	 * relations, set whether the query is an inherited UPDATE/DELETE.
@@ -744,6 +736,12 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	root->inherited_update = (parse->resultRelation &&
 					  rt_fetch(parse->resultRelation, parse->rtable)->inh);
 
+	/*
+	 * Preprocess RowMark information.  We need to do this after subquery
+	 * pullup (so that all non-inherited RTEs are present).
+	 */
+	preprocess_rowmarks(root);
+
 	/*
 	 * Set hasHavingQual to remember if HAVING clause is present.  Needed
 	 * because preprocess_expression will reduce a constant-true condition to
@@ -1257,7 +1255,7 @@ inheritance_planner(PlannerInfo *root)
 	root->unexpanded_tlist = list_copy(root->parse->targetList);
 
 	/* We haven't expanded inheritance yet, so pass false. */
-	tlist = preprocess_targetlist(root);
+	tlist = preprocess_targetlist(root, false);
 	root->processed_tlist = tlist;
 	qp_extra.tlist = tlist;
 	qp_extra.activeWindows = NIL;
@@ -1267,9 +1265,11 @@ 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.
-	 * Let grouping_planner finish up the final path.
+	 * For regular inheritance case, it's possible that we only need to modify
+	 * the parent table.  Let grouping_planner finish up the final path.
 	 */
-	if (IS_DUMMY_REL(planned_rel))
+	parent_rte = planner_rt_fetch(top_parentRTindex, root);
+	if (!parent_rte->inh || IS_DUMMY_REL(planned_rel))
 	{
 		grouping_planner(root, false, planned_rel, 0.0);
 		return;
@@ -1285,7 +1285,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;
@@ -1614,14 +1613,19 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
 		 */
 		if (planned_rel == NULL)
 		{
-			tlist = preprocess_targetlist(root);
+			/* We 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
-			 * remaining planning work will be done with the PathTarget
-			 * representation of tlists, but save aside the full representation
-			 * so that we can transfer its decoration (resnames etc) to the
-			 * topmost tlist of the finished Plan.
+			 * We are now mostly done hacking up the query's targetlist.  More
+			 * columns might be added during query_planner after inheritance
+			 * expansion in make_one_rel, because some children may require
+			 * different column than the parent for row mark handling; see
+			 * add_rowmark_junk_columns().  Most of the remaining planning
+			 * work will be done with the PathTarget representation of tlists,
+			 * but save aside the full representation so that we can transfer
+			 * its decoration (resnames etc) to the topmost tlist of the
+			 * finished Plan.
 			 */
 			root->processed_tlist = tlist;
 		}
@@ -2397,7 +2401,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);
 	}
@@ -2422,7 +2426,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);
 	}
@@ -6859,6 +6863,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);
@@ -6960,6 +6968,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 7447853db9..c72251aea2 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 68ec4aab6a..363892466b 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -18,33 +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
@@ -52,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
@@ -93,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;
@@ -149,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
@@ -368,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);
 	}
 
 	/*
@@ -442,6 +542,143 @@ 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.
+	 */
+	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;
 }
 
 /*
@@ -584,7 +821,7 @@ create_inherited_target_child_root(PlannerInfo *root, AppendRelInfo *appinfo)
 	 * column, filling placeholder entries for dropped columns, etc., all of
 	 * which occurs with the child's TupleDesc.
 	 */
-	tlist = preprocess_targetlist(subroot);
+	tlist = preprocess_targetlist(subroot, true);
 	subroot->processed_tlist = tlist;
 
 	/* Add any newly added Vars to the child RelOptInfo. */
@@ -599,3 +836,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 f2ee83c81b..f3f7bb29c6 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -105,20 +105,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())
@@ -444,11 +444,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);
 
@@ -458,7 +479,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);
 }
 
 /*
@@ -1845,16 +1866,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 f04c6b76f4..09b1b8075a 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -131,6 +131,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,29 +545,33 @@ 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 */
 	if (rel->nparts == 0)
 		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 b46947ffe9..1d36bda039 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"
@@ -383,7 +384,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
@@ -736,11 +736,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 d0c8f99d0a..2e9ebdc94c 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -265,6 +265,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 40f70f9f2b..fb370ed80e 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.17.2 (Apple Git-113)

