diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 1b68ab1b82..cf396856a9 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -1790,6 +1790,8 @@ perform_pruning_base_step(PartitionPruneContext *context,
 	Datum		values[PARTITION_MAX_KEYS];
 	FmgrInfo	partsupfunc[PARTITION_MAX_KEYS];
 
+	Assert(list_length(opstep->exprs) == list_length(opstep->cmpfns));
+
 	nvalues = 0;
 	lc1 = list_head(opstep->exprs);
 	lc2 = list_head(opstep->cmpfns);
@@ -1830,8 +1832,6 @@ perform_pruning_base_step(PartitionPruneContext *context,
 				 * than the one cached in the PartitionKey, we'll need to
 				 * look up the FmgrInfo.
 				 */
-				if (lc2 == NULL)
-					elog(ERROR, "incomplete cmpfnids list in pruning step");
 				cmpfn = lfirst_oid(lc2);
 				Assert(OidIsValid(cmpfn));
 				if (cmpfn != context->partsupfunc[keyno].fn_oid)
@@ -1879,6 +1879,8 @@ perform_pruning_base_step(PartitionPruneContext *context,
 /*
  * perform_pruning_base_step_ne
  *		Returns indexes of partitions obtained by executing 'nestep'.
+ *
+ * Note this pruning method is only supported by LIST partitioning.
  */
 static Bitmapset *
 perform_pruning_base_step_ne(PartitionPruneContext *context,
@@ -1893,6 +1895,9 @@ perform_pruning_base_step_ne(PartitionPruneContext *context,
 	int			n_ne_datums = list_length(nestep->exprs),
 				i;
 
+	Assert(context->strategy == PARTITION_STRATEGY_LIST);
+	Assert(list_length(nestep->exprs) == list_length(nestep->cmpfns));
+
 	/*
 	 * Apply not-equal clauses.  This only applies in the list
 	 * partitioning case as this is the only partition type where
@@ -1905,8 +1910,7 @@ perform_pruning_base_step_ne(PartitionPruneContext *context,
 	 * Some datums may require different comparison function than
 	 * the default partitioning-specific one.
 	 */
-	partsupfunc = (FmgrInfo **)
-	palloc(n_ne_datums * sizeof(FmgrInfo *));
+	partsupfunc = (FmgrInfo **) palloc(n_ne_datums * sizeof(FmgrInfo *));
 
 	i = 0;
 	forboth(lc1, nestep->exprs, lc2, nestep->cmpfns)
@@ -1933,14 +1937,15 @@ perform_pruning_base_step_ne(PartitionPruneContext *context,
 			}
 			else
 				partsupfunc[i] = &context->partsupfunc[0];
-		}
 
-		ne_datums[i++] = datum;
+			ne_datums[i++] = datum;
+		}
 	}
 
 	excluded_parts = get_partitions_excluded_by_ne_datums(context, ne_datums, i,
 													  partsupfunc);
 	pfree(ne_datums);
+	pfree(partsupfunc);
 
 	/* All partitions apart from those in excluded_parts match */
 	result = bms_add_range(NULL, 0, context->nparts - 1);
@@ -2280,7 +2285,7 @@ get_partitions_for_keys_range(PartitionPruneContext *context,
 			{
 				if (nvalues == partnatts)
 				{
-					/* There can only be one partition. */
+					/* There can only be zero or one matching partitions. */
 					if (partindices[off + 1] >= 0)
 						return bms_make_singleton(partindices[off + 1]);
 					else if (partition_bound_has_default(boundinfo))
@@ -2535,7 +2540,8 @@ get_partitions_excluded_by_ne_datums(PartitionPruneContext *context,
 
 	/*
 	 * We can only do this exclusion for list partitions because that's the
-	 * only case where we require all values to explicitly specified.
+	 * only case where we require all values to explicitly specified in the
+	 * partition boundinfo.
 	 */
 	Assert(context->strategy == PARTITION_STRATEGY_LIST);
 	Assert(context->partnatts == 1);
@@ -2569,6 +2575,12 @@ get_partitions_excluded_by_ne_datums(PartitionPruneContext *context,
 	 * counted all this, we can eliminate any partition where the number of
 	 * datums found in the query matches the number of datums allowed in the
 	 * partition.
+	 *
+	 * It might seem like we can skip attempting pruning any partitions here
+	 * if we found no not-equal clauses which match any partitions above, but
+	 * because we previously ensured these clauses are strict we may be able
+	 * to at least eliminate the NULL partition, providing that partition does
+	 * not also allow any non-NULL values.
 	 */
 	if (!bms_is_empty(foundoffsets))
 	{
@@ -2579,6 +2591,13 @@ get_partitions_excluded_by_ne_datums(PartitionPruneContext *context,
 			datums_in_query[boundinfo->indexes[i]]++;
 	}
 
+	/*
+	 * We can't prune anything if there's no foundoffsets and no NULL
+	 * partition.
+	 */
+	else if (!partition_bound_accepts_nulls(boundinfo))
+		return NULL;
+
 	/*
 	 * Now, in a single pass over all the datums, count the number of datums
 	 * permitted in each partition.
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6dd525323c..26c5281385 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2158,6 +2158,7 @@ _copyPartitionPruneStepOpNe(const PartitionPruneStepOpNe *from)
 	PartitionPruneStepOpNe *newnode = makeNode(PartitionPruneStepOpNe);
 
 	COPY_NODE_FIELD(exprs);
+	COPY_NODE_FIELD(cmpfns);
 
 	return newnode;
 }
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 00d6252552..2cb7f086b7 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1483,8 +1483,9 @@ inheritance_planner(PlannerInfo *root)
 			continue;
 
 		/* Add the current parent's RT index to the partitioned rels set. */
-		partitioned_relids = bms_add_member(partitioned_relids,
-											appinfo->parent_relid);
+		if (partitioned_relids)
+			partitioned_relids = bms_add_member(partitioned_relids,
+												appinfo->parent_relid);
 
 		/*
 		 * If this is the first non-excluded child, its post-planning rtable
diff --git a/src/backend/optimizer/util/partprune.c b/src/backend/optimizer/util/partprune.c
index 7dfd8fdcaf..e2d8fdb2a2 100644
--- a/src/backend/optimizer/util/partprune.c
+++ b/src/backend/optimizer/util/partprune.c
@@ -758,8 +758,8 @@ generate_pruning_steps_from_opexprs(PartitionScheme part_scheme,
 		}
 
 		/*
-		 * If we've decided that clauses for subsequent partition keys would't
-		 * be useful for pruning, don't look.
+		 * If we've decided that clauses for subsequent partition keys
+		 * wouldn't be useful for pruning, don't look.
 		 */
 		if (!consider_next_key)
 			break;
@@ -895,9 +895,9 @@ generate_pruning_steps_from_opexprs(PartitionScheme part_scheme,
 					ListCell *lc1;
 
 					/*
-					 * Locate the clause for the greatest column (whic may not
-					 * be the last partition key column).  Actually, the last
-					 * element of eq_clauses must give us what we need.
+					 * Locate the clause for the greatest column (which may
+					 * not be the last partition key column).  Actually, the
+					 * last element of eq_clauses must give us what we need.
 					 */
 					pc = llast(eq_clauses);
 
@@ -994,7 +994,7 @@ generate_pruning_steps_from_opexprs(PartitionScheme part_scheme,
  *
  * Return value:
  *
- *	one of PARTCLAUSE_MATCH_* enum values if the clause is successfully
+ *	One of PARTCLAUSE_MATCH_* enum values if the clause is successfully
  *	matched to the partition key.  If it is PARTCLAUSE_MATCH_CONTRADICT, then
  *	this means the clause is self-contradictory (which can happen only if it's
  *	a BoolExpr whose arguments may be self-contradictory)
@@ -1065,6 +1065,7 @@ match_clause_to_partition_key(RelOptInfo *rel,
 		Oid			commutator = InvalidOid,
 					negator = InvalidOid;
 		Oid			cmpfn;
+		Oid			exprtype;
 
 		leftop = (Expr *) get_leftop(clause);
 		if (IsA(leftop, RelabelType))
@@ -1152,7 +1153,8 @@ match_clause_to_partition_key(RelOptInfo *rel,
 		}
 
 		/* Check if we're going to need a cross-type comparison function. */
-		if (exprType((Node *) expr) != part_scheme->partopcintype[partkeyidx])
+		exprtype = exprType((Node *) expr);
+		if (exprtype != part_scheme->partopcintype[partkeyidx])
 		{
 			int	procnum = (part_scheme->strategy == PARTITION_STRATEGY_HASH)
 							? HASHEXTENDED_PROC
@@ -1160,7 +1162,7 @@ match_clause_to_partition_key(RelOptInfo *rel,
 
 			cmpfn = get_opfamily_proc(part_scheme->partopfamily[partkeyidx],
 									  part_scheme->partopcintype[partkeyidx],
-									  exprType((Node *) expr), procnum);
+									  exprtype, procnum);
 			/* If we couldn't find one, we cannot use this expression. */
 			if (!OidIsValid(cmpfn))
 				return PARTCLAUSE_UNSUPPORTED;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 959964ba13..a0345a0abf 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1528,10 +1528,10 @@ typedef struct PartitionPruneStep
  * where partnatts is the number of partition key columns.  'opstrategy' is the
  * strategy of the operator in the clause matched to the last partition key.
  * 'exprs' contains expressions which comprise the look-up key to be passed to
- * the partition bound search function.  'cmpfnids' contains the OIDs of
+ * the partition bound search function.  'cmpfns' contains the OIDs of
  * comparison function used to compare aforementioned expressions with
- * partition bounds.  Both 'exprs' and 'cmpfns' contain up to partnatts
- * elements.
+ * partition bounds.  Both 'exprs' and 'cmpfns' contain the same number of
+ * items up to partnatts items.
  *
  * Once we find the offset of a partition bound using the look-up key, we
  * determine which partitions to include in the result based on the value of
@@ -1563,9 +1563,9 @@ typedef struct PartitionPruneStepOp
  *							OpExpr clauses each containing a <> operator
  *
  * This is a special form of PartitionPruneStepOp, where each of the
- * expressions in 'expr' is compared using a <> operator.  To prune a given
+ * expressions in 'exprs' is compared using a <> operator.  To prune a given
  * partition, we must check if each of the values it allows matches the value
- * of one of the expressions in 'expr' using the corresponding comparison
+ * of one of the expressions in 'exprs' using the corresponding comparison
  * function in 'cmpfns'.
  *
  * Note: Since we must consider every possible value of the partition key a
