diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 0a003d9935..cbce6fda8e 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -1605,9 +1605,13 @@ ExecFindInitialMatchingSubPlans(PartitionPruneState *prunestate, int nsubplans)
 
 		/*
 		 * Now we can re-sequence each PartitionPruneInfo's subplan_map so
-		 * that they point to the new index of the subplan.
+		 * that they point to the new index of the subplan.  We perform this
+		 * loop in a back-to-front order so that we determine present_parts
+		 * for the lowest-level partitioned tables first.  This way we can
+		 * determine if a sub-partitioned table's partitions were entirely
+		 * pruned so we can exclude that from 'present_parts'.
 		 */
-		for (i = 0; i < prunestate->num_partprunedata; i++)
+		for (i = prunestate->num_partprunedata - 1; i >= 0 ; i--)
 		{
 			int			nparts;
 			int			j;
@@ -1627,7 +1631,7 @@ ExecFindInitialMatchingSubPlans(PartitionPruneState *prunestate, int nsubplans)
 			for (j = 0; j < nparts; j++)
 			{
 				int			oldidx = pprune->subplan_map[j];
-
+				int			subidx;
 				/*
 				 * If this partition existed as a subplan then change the old
 				 * subplan index to the new subplan index.  The new index may
@@ -1643,41 +1647,17 @@ ExecFindInitialMatchingSubPlans(PartitionPruneState *prunestate, int nsubplans)
 						pprune->present_parts =
 							bms_add_member(pprune->present_parts, j);
 				}
-			}
-		}
-
-		/*
-		 * Now we must determine which sub-partitioned tables still have
-		 * unpruned partitions.  The easiest way to do this is to simply loop
-		 * over each PartitionPruningData again checking if there are any
-		 * 'present_parts' in the sub-partitioned table.  We needn't bother
-		 * doing this if there are no sub-partitioned tables.
-		 */
-		if (prunestate->num_partprunedata > 1)
-		{
-			for (i = 0; i < prunestate->num_partprunedata; i++)
-			{
-				int			nparts;
-				int			j;
-
-				pprune = &prunestate->partprunedata[i];
-				nparts = pprune->context.nparts;
-
-				for (j = 0; j < nparts; j++)
+				else if ((subidx = pprune->subpart_map[j]) >= 0)
 				{
-					int			subidx = pprune->subpart_map[j];
-
-					if (subidx >= 0)
-					{
-						PartitionPruningData *subprune;
+					PartitionPruningData *subprune;
 
-						subprune = &prunestate->partprunedata[subidx];
+					subprune = &prunestate->partprunedata[subidx];
 
-						if (!bms_is_empty(subprune->present_parts))
-							pprune->present_parts =
-								bms_add_member(pprune->present_parts, j);
-					}
+					if (!bms_is_empty(subprune->present_parts))
+						pprune->present_parts =
+							bms_add_member(pprune->present_parts, j);
 				}
+
 			}
 		}
 
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 477b9f7fb8..146298828d 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -1451,7 +1451,8 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 
 		/*
 		 * If we need to build partitioned_rels, accumulate the partitioned
-		 * rels for this child.
+		 * rels for this child.  We must ensure that parents are always listed
+		 * before their child partitioned tables.
 		 */
 		if (build_partitioned_rels)
 		{
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index fc0388e621..a09464dede 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -170,7 +170,7 @@ static PruneStepResult *perform_pruning_combine_step(PartitionPruneContext *cont
 static bool match_boolean_partition_clause(Oid partopfamily, Expr *clause,
 							   Expr *partkey, Expr **outconst);
 static bool partkey_datum_from_expr(PartitionPruneContext *context,
-						Expr *expr, int stateidx, Datum *value);
+						Expr *expr, int stateidx, Datum *value, bool *isnull);
 
 
 /*
@@ -183,9 +183,15 @@ static bool partkey_datum_from_expr(PartitionPruneContext *context,
  * data structure which allows mapping of partition indexes into 'subpaths'
  * indexes.
  *
+ * 'partition_rels' must list all RT index of all parent partitioned tables
+ * for all partitions seen in 'subpaths'.  Heigher level parent partitioned
+ * tables must come before their child partitioned table, meaning the top-most
+ * parent must be first in the list.
+ *
  * If no non-Const expressions are being compared to the partition key in any
- * of the 'partitioned_rels', then we return NIL.  In such a case run-time
- * partition pruning would be useless, since the planner did it already.
+ * of the 'partitioned_rels', then we return NIL to indicate no run-time
+ * pruning should be performed.  Run-time pruning would be useless, since the
+ * pruning done during planning will have pruned everything that can be.
  */
 List *
 make_partition_pruneinfo(PlannerInfo *root, List *partition_rels,
@@ -2835,21 +2841,40 @@ perform_pruning_base_step(PartitionPruneContext *context,
 			Expr	   *expr;
 			int			stateidx;
 			Datum		datum;
+			bool		isnull;
 
 			expr = lfirst(lc1);
 			stateidx = PruneCxtStateIdx(context->partnatts,
 										opstep->step.step_id, keyno);
-			if (partkey_datum_from_expr(context, expr, stateidx, &datum))
+			if (partkey_datum_from_expr(context, expr, stateidx, &datum,
+										&isnull))
 			{
 				Oid			cmpfn;
 
+				/*
+				 * Since we only allow strict operators in pruning steps, any
+				 * NULL valued datum cannot possibily match any partitions.
+				 */
+				if (isnull)
+				{
+					PruneStepResult *result;
+
+					result = (PruneStepResult *) palloc(sizeof(PruneStepResult));
+					result->bound_offsets = NULL;
+					result->scan_default = false;
+					result->scan_null = false;
+
+					return result;
+				}
+
+				cmpfn = lfirst_oid(lc2);
+				Assert(OidIsValid(cmpfn));
+
 				/*
 				 * If we're going to need a different comparison function than
 				 * the one cached in the PartitionKey, we'll need to look up
 				 * the FmgrInfo.
 				 */
-				cmpfn = lfirst_oid(lc2);
-				Assert(OidIsValid(cmpfn));
 				if (cmpfn != context->partsupfunc[keyno].fn_oid)
 					fmgr_info(cmpfn, &partsupfunc[keyno]);
 				else
@@ -3073,7 +3098,8 @@ match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey,
  *
  * Evaluate 'expr', whose ExprState is stateidx of the context exprstate
  * array; set *value to the resulting Datum.  Return true if evaluation was
- * possible, otherwise false.
+ * possible, otherwise false. 'isnull' is set to indicate if the 'value' datum
+ * is SQL NULL or not.
  *
  * Note that the evaluated result may be in the per-tuple memory context of
  * context->planstate->ps_ExprContext, and we may have leaked other memory
@@ -3082,11 +3108,14 @@ match_boolean_partition_clause(Oid partopfamily, Expr *clause, Expr *partkey,
  */
 static bool
 partkey_datum_from_expr(PartitionPruneContext *context,
-						Expr *expr, int stateidx, Datum *value)
+						Expr *expr, int stateidx, Datum *value, bool *isnull)
 {
 	if (IsA(expr, Const))
 	{
-		*value = ((Const *) expr)->constvalue;
+		Const *con = (Const *) expr;
+
+		*value = con->constvalue;
+		*isnull = con->constisnull;
 		return true;
 	}
 	else
@@ -3105,14 +3134,10 @@ partkey_datum_from_expr(PartitionPruneContext *context,
 		{
 			ExprState  *exprstate;
 			ExprContext *ectx;
-			bool		isNull;
 
 			exprstate = context->exprstates[stateidx];
 			ectx = context->planstate->ps_ExprContext;
-			*value = ExecEvalExprSwitchContext(exprstate, ectx, &isNull);
-			if (isNull)
-				return false;
-
+			*value = ExecEvalExprSwitchContext(exprstate, ectx, isnull);
 			return true;
 		}
 	}
diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h
index 9f0b817c54..59176139e7 100644
--- a/src/include/executor/execPartition.h
+++ b/src/include/executor/execPartition.h
@@ -121,12 +121,21 @@ typedef struct PartitionTupleRouting
  * subplan when we can prove that no tuple matching the 'pruning_steps' will
  * be found within.
  *
- * subplan_map					An array containing the subplan index which
- *								matches this partition index, or -1 if the
- *								subplan has been pruned already.
- * subpart_map					An array containing the index into the
- *								partprunedata array in PartitionPruneState, or
- *								-1 if there is no such element in that array.
+ * subplan_map					An array indexed by the partitioned table's
+ *								partition index as returned by the pruning
+ *								code.  Contains the index of the subplan for
+ *								that partition, or -1 if that subplan does not
+ *								exists due to having been pruned away already
+ *								or the index belongs to a subpartitioned
+ *								table.
+ * subpart_map					An array indexed by the partitioned table's
+ *								partition index as returned by the pruning
+ *								code.  Contains the index of the
+ *								PartitionPruneState 'partprunedata' element
+ *								which stores the details for this
+ *								subpartitioned table, or -1 if that table was
+ *								pruned away already, or if the index belongs
+ *								to a leaf partition.
  * present_parts				A Bitmapset of the partition indexes that we
  *								have subplans mapped for.
  * context						Contains the context details required to call
@@ -161,7 +170,9 @@ typedef struct PartitionPruningData
  *
  * partprunedata		Array of PartitionPruningData for the plan's target
  *						partitioned relation. First element contains the
- *						details for the target partitioned table.
+ *						details for the target partitioned table the remaining
+ *						elements are in flattened hierarchical order with the
+ *						parents coming before their children.
  * num_partprunedata	Number of items in 'partprunedata' array.
  * do_initial_prune		true if pruning should be performed during executor
  *						startup (at any hierarchy level).
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index dacc50edc2..42343de210 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -220,7 +220,10 @@ typedef struct ModifyTable
 	CmdType		operation;		/* INSERT, UPDATE, or DELETE */
 	bool		canSetTag;		/* do we set the command tag/es_processed? */
 	Index		nominalRelation;	/* Parent RT index for use of EXPLAIN */
-	/* RT indexes of non-leaf tables in a partition tree */
+	/*
+	 * A flattened hierarchical list of RT indexes of partitioned tables in
+	 * parent to child order having the top-most parent first.
+	 */
 	List	   *partitioned_rels;
 	bool		partColsUpdated;	/* some part key in hierarchy updated */
 	List	   *resultRelations;	/* integer list of RT indexes */
@@ -257,7 +260,10 @@ typedef struct Append
 	 */
 	int			first_partial_plan;
 
-	/* RT indexes of non-leaf tables in a partition tree */
+	/*
+	 * A flattened hierarchical list of RT indexes of partitioned tables in
+	 * parent to child order having the top-most parent first.
+	 */
 	List	   *partitioned_rels;
 
 	/* Info for run-time subplan pruning, one entry per partitioned_rels */
@@ -272,7 +278,10 @@ typedef struct Append
 typedef struct MergeAppend
 {
 	Plan		plan;
-	/* RT indexes of non-leaf tables in a partition tree */
+	/*
+	 * A flattened hierarchical list of RT indexes of partitioned tables in
+	 * parent to child order having the top-most parent first.
+	 */
 	List	   *partitioned_rels;
 	List	   *mergeplans;
 	/* remaining fields are just like the sort-key info in struct Sort */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 3b28d1994f..78177aa687 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -689,7 +689,9 @@ typedef struct RelOptInfo
 									 * 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. */
+	List	   *partitioned_child_rels; /* Flattened hierarchical list of
+										 * partitioned table of RT indexes
+										 * in parent to child order. */
 } RelOptInfo;
 
 /*
diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
index ab32c7d67e..47c6aa4817 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -2731,6 +2731,52 @@ explain (analyze, costs off, summary off, timing off)  execute q1 (1,2,2,1);
          Filter: ((b = ANY (ARRAY[$1, $2])) AND ($3 <> b) AND ($4 <> b))
 (4 rows)
 
+drop table listp_2;
+-- Ensure Params that evaulate to NULL properly prune away all partitions
+explain (analyze, costs off, summary off, timing off) select * from listp where a = (select null::int);
+                  QUERY PLAN                  
+----------------------------------------------
+ Append (actual rows=0 loops=1)
+   InitPlan 1 (returns $0)
+     ->  Result (actual rows=1 loops=1)
+   ->  Seq Scan on listp_1_1 (never executed)
+         Filter: (a = $0)
+(5 rows)
+
+prepare q2 (int) as select * from listp where a = $1;
+execute q2 (1);
+ a | b 
+---+---
+(0 rows)
+
+execute q2 (1);
+ a | b 
+---+---
+(0 rows)
+
+execute q2 (1);
+ a | b 
+---+---
+(0 rows)
+
+execute q2 (1);
+ a | b 
+---+---
+(0 rows)
+
+execute q2 (1);
+ a | b 
+---+---
+(0 rows)
+
+explain (analyze, costs off, summary off, timing off)  execute q2 (null);
+                  QUERY PLAN                  
+----------------------------------------------
+ Append (actual rows=0 loops=1)
+   ->  Seq Scan on listp_1_1 (never executed)
+         Filter: (a = $1)
+(3 rows)
+
 drop table listp;
 -- Ensure runtime pruning works with initplans params with boolean types
 create table boolvalues (value bool not null);
diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql
index 609fe09aeb..8d62b19a3d 100644
--- a/src/test/regress/sql/partition_prune.sql
+++ b/src/test/regress/sql/partition_prune.sql
@@ -685,6 +685,21 @@ explain (analyze, costs off, summary off, timing off)  execute q1 (1,2,2,0);
 -- One subplan will remain in this case, but it should not be executed.
 explain (analyze, costs off, summary off, timing off)  execute q1 (1,2,2,1);
 
+drop table listp_2;
+
+-- Ensure Params that evaulate to NULL properly prune away all partitions
+explain (analyze, costs off, summary off, timing off) select * from listp where a = (select null::int);
+
+prepare q2 (int) as select * from listp where a = $1;
+
+execute q2 (1);
+execute q2 (1);
+execute q2 (1);
+execute q2 (1);
+execute q2 (1);
+
+explain (analyze, costs off, summary off, timing off)  execute q2 (null);
+
 drop table listp;
 
 -- Ensure runtime pruning works with initplans params with boolean types
