From 2e2ae4a76cb3e19fde4ba6f2bc45461dbcb92387 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Mon, 4 Mar 2019 11:43:38 +0900
Subject: [PATCH v31 6/8] Perform pruning in expand_partitioned_rtentry

This allows to avoid opening/locking partitions that won't be
scanned at all.  Since expand_partitioned_rtentry recursivly processes
sub-partitioned tables, translated quals must be added right when the
child RelOptInfo is built for the sub-partitioned tables.  So,
build_simple_rel itself performs apply_child_basequals.

Code for partitioning optimizations that access partition RelOptInfos
from part_rels array of the parent's RelOptInfo must now be made
aware that some entries might be NULL due partition pruning.  In the
case of partitionwise join, even a pruned partition may need to be
joined if it falls on the nullable side of an outer join.  A dummy
RelOptInfo containing a dummy path is built in that case and put
into its parent's part_rels array.
---
 src/backend/executor/execPartition.c  |  17 ++-
 src/backend/optimizer/path/allpaths.c | 176 +-----------------------
 src/backend/optimizer/path/joinrels.c |  92 ++++++++++++-
 src/backend/optimizer/plan/planner.c  |   8 ++
 src/backend/optimizer/util/inherit.c  |  21 ++-
 src/backend/optimizer/util/relnode.c  | 189 ++++++++++++++++++++++++--
 src/backend/partitioning/partprune.c  |  45 +++---
 7 files changed, 336 insertions(+), 212 deletions(-)

diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 37e96a6013..5b7c386144 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -1653,9 +1653,20 @@ ExecCreatePartitionPruneState(PlanState *planstate,
 				memcpy(pprune->subplan_map, pinfo->subplan_map,
 					   sizeof(int) * pinfo->nparts);
 
-				/* Double-check that list of relations has not changed. */
-				Assert(memcmp(partdesc->oids, pinfo->relid_map,
-					   pinfo->nparts * sizeof(Oid)) == 0);
+#ifdef USE_ASSERT_CHECKING
+				/*
+				 * Double-check that list of unpruned relations has not
+				 * changed.
+				 */
+				{
+					int		k;
+
+					/* Pruned partitions are not added to relid_map. */
+					for (k = 0; k < pinfo->nparts; k++)
+						Assert(partdesc->oids[k] == pinfo->relid_map[k] ||
+							   pinfo->subplan_map[k] == -1);
+				}
+#endif
 			}
 			else
 			{
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 892032dbe4..8716dd9690 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -139,9 +139,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);
 
 
 /*
@@ -946,8 +943,6 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 	double	   *parent_attrsizes;
 	int			nattrs;
 	ListCell   *l;
-	Relids		live_children = NULL;
-	bool		did_pruning = false;
 
 	/* Guard against stack overflow due to overly deep inheritance tree. */
 	check_stack_depth();
@@ -965,21 +960,6 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 	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
@@ -1034,30 +1014,11 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 		childrel = find_base_rel(root, childRTindex);
 		Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
 
-		if (did_pruning && !bms_is_member(appinfo->child_relid, live_children))
-		{
-			/* This partition was pruned; skip it. */
-			set_dummy_rel_pathlist(childrel);
+		/* build_simple_rel may have already proven the child to be dummy. */
+		if (IS_DUMMY_REL(childrel))
 			continue;
-		}
-
-		/*
-		 * We have to copy the parent's targetlist and quals to the child,
-		 * with appropriate substitution of variables.  If any constant false
-		 * or NULL clauses turn up, we can disregard the child right away.
-		 * If not, we can apply constraint exclusion with just the
-		 * baserestrictinfo quals.
-		 */
-		if (!apply_child_basequals(root, rel, childrel, childRTE, appinfo))
-		{
-			/*
-			 * Some restriction clause reduced to constant FALSE or NULL after
-			 * substitution, so this child need not be scanned.
-			 */
-			set_dummy_rel_pathlist(childrel);
-			continue;
-		}
 
+		/* Apply constraint exclusion. */
 		if (relation_excluded_by_constraints(root, childrel, childRTE))
 		{
 			/*
@@ -1069,7 +1030,8 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 		}
 
 		/*
-		 * CE failed, so finish copying/modifying targetlist and join quals.
+		 * Constraint exclusion failed, so finish copying/modifying targetlist
+		 * and join quals.
 		 *
 		 * NB: the resulting childrel->reltarget->exprs may contain arbitrary
 		 * expressions, which otherwise would not occur in a rel's targetlist.
@@ -3561,134 +3523,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 699a34d6cf..55a7a34061 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -14,6 +14,7 @@
  */
 #include "postgres.h"
 
+#include "access/table.h"
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/appendinfo.h"
@@ -21,6 +22,8 @@
 #include "optimizer/joininfo.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
+#include "optimizer/tlist.h"
+#include "parser/parsetree.h"
 #include "partitioning/partbounds.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
@@ -51,6 +54,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, Relation parentrel,
+									int partidx);
 
 
 /*
@@ -1345,6 +1351,8 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 					   RelOptInfo *joinrel, SpecialJoinInfo *parent_sjinfo,
 					   List *parent_restrictlist)
 {
+	Relation	baserel1 = NULL,
+				baserel2 = NULL;
 	bool		rel1_is_simple = IS_SIMPLE_REL(rel1);
 	bool		rel2_is_simple = IS_SIMPLE_REL(rel2);
 	int			nparts;
@@ -1395,6 +1403,18 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 
 	nparts = joinrel->nparts;
 
+	if (rel1_is_simple)
+	{
+		RangeTblEntry *rte = planner_rt_fetch(rel1->relid, root);
+
+		baserel1 = table_open(rte->relid, NoLock);
+	}
+	if (rel2_is_simple)
+	{
+		RangeTblEntry *rte = planner_rt_fetch(rel2->relid, root);
+
+		baserel2 = table_open(rte->relid, NoLock);
+	}
 	/*
 	 * Create child-join relations for this partitioned join, if those don't
 	 * exist. Add paths to child-joins for a pair of child relations
@@ -1411,6 +1431,13 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 		AppendRelInfo **appinfos;
 		int			nappinfos;
 
+		if (rel1_is_simple && child_rel1 == NULL)
+			child_rel1 = build_dummy_partition_rel(root, rel1, baserel1,
+												   cnt_parts);
+		if (rel2_is_simple && child_rel2 == NULL)
+			child_rel2 = build_dummy_partition_rel(root, rel2, baserel2,
+												   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
@@ -1471,6 +1498,11 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 									child_joinrel, child_sjinfo,
 									child_restrictlist);
 	}
+
+	if (baserel1)
+		table_close(baserel1, NoLock);
+	if (baserel2)
+		table_close(baserel2, NoLock);
 }
 
 /*
@@ -1489,8 +1521,14 @@ update_child_rel_info(PlannerInfo *root,
 							   (Node *) rel->reltarget->exprs,
 							   1, &appinfo);
 
-	/* Make child entries in the EquivalenceClass as well */
-	if (rel->has_eclass_joins || has_useful_pathkeys(root, rel))
+	/*
+	 * Make child entries in the EquivalenceClass as well.  If the childrel
+	 * appears to be a dummy one (one built by build_dummy_partition_rel()),
+	 * no need to make any new entries, because anything that would need those
+	 * can instead use the parent's (rel).
+	 */
+	if (childrel->relid != rel->relid &&
+		(rel->has_eclass_joins || has_useful_pathkeys(root, rel)))
 		add_child_rel_equivalences(root, appinfo, rel, childrel);
 	childrel->has_eclass_joins = rel->has_eclass_joins;
 }
@@ -1701,3 +1739,53 @@ 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,
+						  Relation parentrel, int partidx)
+{
+	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(parentrel, parentrel,
+													  parent->relid,
+													  parent->relid);
+
+		root->append_rel_array[parent->relid] = appinfo;
+	}
+
+	return rel;
+}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 7a218f5b1b..8dd5df8cc1 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -7117,6 +7117,10 @@ apply_scanjoin_target_to_paths(PlannerInfo *root,
 			List	   *child_scanjoin_targets = NIL;
 			ListCell   *lc;
 
+			/* 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);
@@ -7217,6 +7221,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/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 24111c46e9..5d7005de96 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -26,6 +26,7 @@
 #include "optimizer/planner.h"
 #include "optimizer/prep.h"
 #include "partitioning/partdesc.h"
+#include "partitioning/partprune.h"
 #include "utils/rel.h"
 
 
@@ -233,6 +234,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
 						   PlanRowMark *top_parentrc, LOCKMODE lockmode,
 						   List **appinfos)
 {
+	Bitmapset  *live_parts = 0;
 	int			i;
 	RangeTblEntry *childrte;
 	Index		childRTindex;
@@ -272,23 +274,35 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
 	{
 		rel = find_base_rel(root, parentRTindex);
 
+		/*
+		 * Perform partition pruning using restriction clauses assigned to
+		 * parent relation.  live_parts will contain PartitionDesc indexes
+		 * of partitions that survive pruning.  Below, we will initialize
+		 * child objects for the surviving partitions.
+		 */
+		if (partdesc->nparts > 0)
+			live_parts = prune_append_rel_partitions(rel);
+
 		/*
 		 * Expand simple_rel_array and friends to hold child objects.
 		 *
 		 * We'll need nparts + 1 new slots, because we also consider parent
 		 * as a child relation. FIXME: no longer add the parent as child
 		 */
-		expand_planner_arrays(root, partdesc->nparts + 1);
+		expand_planner_arrays(root, bms_num_members(live_parts) + 1);
 
 		/*
 		 * We also store partition RelOptInfo pointers in the parent
-		 * relation.
+		 * relation.  Since we're palloc0'ing, slots corresponding to
+		 * pruned partitions will contain NULL.
 		 */
 		Assert(rel->part_rels == NULL);
 		if (partdesc->nparts > 0)
 			rel->part_rels = (RelOptInfo **) palloc0(rel->nparts *
 													 sizeof(RelOptInfo *));
 	}
+	else
+		live_parts = bms_add_range(NULL, 0, partdesc->nparts - 1);
 
 	/* First expand the partitioned table itself. */
 	expand_single_inheritance_child(root, parentrte, parentRTindex, parentrel,
@@ -305,7 +319,8 @@ expand_partitioned_rtentry(PlannerInfo *root, RangeTblEntry *parentrte,
 		return;
 	}
 
-	for (i = 0; i < partdesc->nparts; i++)
+	i = -1;
+	while ((i = bms_next_member(live_parts, i)) >= 0)
 	{
 		Oid			childOID = partdesc->oids[i];
 		Relation	childrel;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 2d475e3297..649f6c8481 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -70,6 +70,9 @@ static void build_child_join_reltarget(PlannerInfo *root,
 						   RelOptInfo *childrel,
 						   int nappinfos,
 						   AppendRelInfo **appinfos);
+static bool apply_child_basequals(PlannerInfo *root, RelOptInfo *rel,
+					  RelOptInfo *childrel,
+					  RangeTblEntry *childRTE, AppendRelInfo *appinfo);
 
 
 /*
@@ -324,9 +327,161 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 		root->qual_security_level = Max(root->qual_security_level,
 										list_length(rte->securityQuals));
 
+	/*
+	 * Copy the parent's quals to the child, with appropriate substitution of
+	 * variables.  If any constant false or NULL clauses turn up, we can
+	 * disregard the child as dummy right away.
+	 *
+	 * We must copy the quals now, because if the child rel happens to be a
+	 * partitioned table, expand_partitioned_rtentry (our caller) can
+	 * immediate perform partition prunning using the translated quals.
+	 */
+	if (parent)
+	{
+		AppendRelInfo *appinfo = root->append_rel_array[relid];
+
+		Assert(appinfo != NULL);
+		if (!apply_child_basequals(root, parent, rel, rte, appinfo))
+		{
+			/*
+			 * Some restriction clause reduced to constant FALSE or NULL after
+			 * substitution, so this child need not be scanned.
+			 */
+			mark_dummy_rel(rel);
+		}
+	}
+
 	return rel;
 }
 
+/*
+ * 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 doesn't need to 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;
+}
+
 /*
  * add_appendrel_other_rels
  *		This adds the "other rel" RelOptInfos a given "appendrel" baserel
@@ -339,6 +494,8 @@ add_appendrel_other_rels(PlannerInfo *root, RangeTblEntry *rte, Index rti)
 {
 	ListCell	   *l;
 	RelOptInfo	   *rel;
+	Relation		relation = NULL;
+	PartitionDesc	partdesc = NULL;
 	int				i;
 
 	/*
@@ -362,16 +519,20 @@ add_appendrel_other_rels(PlannerInfo *root, RangeTblEntry *rte, Index rti)
 
 	/*
 	 * For partitioned tables, we need to store the child RelOptInfos in the
-	 * rel->part_rels array too.
+	 * rel->part_rels array too.  Some elements of this array might remain set
+	 * to NULL if the corresponding partitions were pruned and hence not in
+	 * append_rel_list.
 	 */
 	if (rel->part_scheme)
 	{
 		Assert(rel->nparts > 0);
 		rel->part_rels = (RelOptInfo **)
 				palloc0(sizeof(RelOptInfo *) * rel->nparts);
+		relation = table_open(rte->relid, NoLock);
+		partdesc = PartitionDirectoryLookup(root->glob->partition_directory,
+											relation);
 	}
 
-	i = 0;
 	foreach(l, root->append_rel_list)
 	{
 		AppendRelInfo  *appinfo = lfirst(l);
@@ -391,26 +552,30 @@ add_appendrel_other_rels(PlannerInfo *root, RangeTblEntry *rte, Index rti)
 		 * For partitioned parents, we also need to add childrel to its
 		 * part_rels array.  The order in which child tables appear in
 		 * append_rel_list is same as the order in which they appear in the
-		 * parent's PartitionDesc, so assigning partitions like this works.
+		 * parent's PartitionDesc.  But considering that some partitions may
+		 * have been pruned, we need to look up the correct position to
+		 * assign this childrel by scanning the PartitionDesc.
 		 */
 		if (rel->part_scheme != NULL)
 		{
-			Assert(i < rel->nparts);
-			rel->part_rels[i] = childrel;
+			Assert(partdesc != NULL);
+			for (i = 0; i < partdesc->nparts; i++)
+			{
+				if (childrte->relid == partdesc->oids[i])
+				{
+					rel->part_rels[i] = childrel;
+					break;
+				}
+			}
 		}
 
-		i++;
-
 		/* Child may itself be an inherited relation. */
 		if (childrte->inh)
 			add_appendrel_other_rels(root, childrte, childRTindex);
 	}
 
-	/*
-	 * For a partitioned table with non-zero number of partitions, we must
-	 * have assigned all elements of its part_rels array.
-	 */
-	Assert(rel->nparts == 0 || i == rel->nparts);
+	if (relation)
+		table_close(relation, NoLock);
 }
 
 /*
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index b5c0889935..9ebded67a9 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -45,6 +45,7 @@
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/appendinfo.h"
+#include "optimizer/cost.h"
 #include "optimizer/optimizer.h"
 #include "optimizer/pathnode.h"
 #include "parser/parsetree.h"
@@ -435,18 +436,24 @@ 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));
-		relid_map = (Oid *) palloc(nparts * sizeof(int));
+		memset(subpart_map, -1, nparts * sizeof(int));
+		relid_map = (Oid *) palloc0(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;
 			relid_map[i] = planner_rt_fetch(partrel->relid, root)->relid;
 			if (subplanidx >= 0)
 			{
@@ -542,29 +549,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.
@@ -592,15 +603,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);
 }
 
 /*
-- 
2.17.2 (Apple Git-113)

