diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 2a088b416b..5a5f6b0f5f 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -153,13 +153,6 @@ typedef struct PruneStepResult
 	 */
 	Bitmapset  *bound_offsets;
 
-	/*
-	 * Set if we need to scan all partitions that contain non-null data; if
-	 * this is set, bound_offsets should be NULL and its value should not be
-	 * relied upon.
-	 */
-	bool		scan_all_nonnull;
-
 	/* Set if we need to scan the default and/or the null partition, resp. */
 	bool		scan_default;
 	bool		scan_null;
@@ -230,19 +223,16 @@ static PruneStepResult *perform_pruning_combine_step(PartitionPruneContext *cont
 							 PruneStepResult **step_results);
 static bool partkey_datum_from_expr(PartitionPruneContext *context,
 						Expr *expr, Datum *value);
-static Bitmapset *get_matching_hash_bound(PartitionPruneContext *context,
-							int opstrategy, Datum *values, int nvalues,
-							FmgrInfo *partsupfunc, Bitmapset *nullkeys,
-							bool *scan_all_nonnull);
-static Bitmapset *get_matching_list_bounds(PartitionPruneContext *context,
+static PruneStepResult *get_matching_hash_bounds(PartitionPruneContext *context,
+							 int opstrategy, Datum *values, int nvalues,
+							 FmgrInfo *partsupfunc, Bitmapset *nullkeys);
+static PruneStepResult *get_matching_list_bounds(PartitionPruneContext *context,
 							int opstrategy, Datum value, int nvalues,
 							FmgrInfo *partsupfunc, Bitmapset *nullkeys,
-							bool *scan_null, bool *scan_default,
-							bool *scan_all_nonnull);
-static Bitmapset *get_matching_range_bounds(PartitionPruneContext *context,
+							Bitmapset *notnullkeys);
+static PruneStepResult *get_matching_range_bounds(PartitionPruneContext *context,
 							int opstrategy, Datum *values, int nvalues,
-							FmgrInfo *partsupfunc, Bitmapset *nullkeys,
-							bool *scan_default, bool *scan_all_nonnull);
+							FmgrInfo *partsupfunc, Bitmapset *nullkeys);
 
 /*
  * RelationBuildPartitionDesc
@@ -1677,12 +1667,11 @@ Bitmapset *
 get_matching_partitions(PartitionPruneContext *context,
 						List *pruning_steps)
 {
-	Bitmapset  *result;
+	Bitmapset  *partitions;
 	int			num_steps = list_length(pruning_steps),
 				i;
-	PruneStepResult **step_results,
-					 *last_step_result;
-	Bitmapset  *bound_offsets;
+	PruneStepResult **results,
+					 *final_result;
 	ListCell   *lc;
 
 	/* If there are no pruning steps then all partitions match. */
@@ -1696,8 +1685,8 @@ get_matching_partitions(PartitionPruneContext *context,
 	 * result of applying all pruning steps is the value contained in the slot
 	 * of the last pruning step.
 	 */
-	step_results = (PruneStepResult **)
-							palloc0(num_steps * sizeof(PruneStepResult *));
+	results = (PruneStepResult **)
+						palloc0(num_steps * sizeof(PruneStepResult *));
 	foreach(lc, pruning_steps)
 	{
 		PartitionPruneStep *step = lfirst(lc);
@@ -1705,18 +1694,14 @@ get_matching_partitions(PartitionPruneContext *context,
 		switch (nodeTag(step))
 		{
 			case T_PartitionPruneStepOp:
-				step_results[step->step_id] =
-					perform_pruning_base_step(context,
+				results[step->step_id] = perform_pruning_base_step(context,
 											  (PartitionPruneStepOp *) step);
-				Assert(!step_results[step->step_id]->scan_all_nonnull ||
-					   step_results[step->step_id]->bound_offsets == NULL);
 				break;
 
 			case T_PartitionPruneStepCombine:
-				step_results[step->step_id] =
-					perform_pruning_combine_step(context,
-											(PartitionPruneStepCombine *) step,
-												 step_results);
+				results[step->step_id] = perform_pruning_combine_step(context,
+										(PartitionPruneStepCombine *) step,
+																	 results);
 				break;
 
 			default:
@@ -1730,16 +1715,10 @@ get_matching_partitions(PartitionPruneContext *context,
 	 * partitions need to be in the result, including special null-accepting
 	 * and default partitions.  Collect the actual partition indexes now.
 	 */
-	last_step_result = step_results[num_steps - 1];
-	Assert(last_step_result != NULL);
-	if (last_step_result->scan_all_nonnull)
-		bound_offsets = bms_add_range(NULL, 0,
-									  context->boundinfo->ndatums - 1);
-	else
-		bound_offsets = last_step_result->bound_offsets;
 	i = -1;
-	result = NULL;
-	while ((i = bms_next_member(bound_offsets, i)) >= 0)
+	partitions = NULL;
+	final_result = results[num_steps - 1];
+	while ((i = bms_next_member(final_result->bound_offsets, i)) >= 0)
 	{
 		int		partindex = context->boundinfo->indexes[i];
 
@@ -1752,26 +1731,28 @@ get_matching_partitions(PartitionPruneContext *context,
 		 * the query's conditions.
 		 */
 		if (partindex >= 0)
-			result = bms_add_member(result, partindex);
+			partitions = bms_add_member(partitions, partindex);
 	}
 
 	/* Add the null and/or default partition if needed and if present. */
-	if (last_step_result->scan_null)
+	if (final_result->scan_null)
 	{
 		Assert(context->strategy == PARTITION_STRATEGY_LIST);
-		if (partition_bound_accepts_nulls(context->boundinfo))
-			result = bms_add_member(result, context->boundinfo->null_index);
+		Assert(partition_bound_accepts_nulls(context->boundinfo));
+		partitions = bms_add_member(partitions,
+									context->boundinfo->null_index);
 	}
-	if (last_step_result->scan_default)
+	if (final_result->scan_default)
 	{
 		Assert(context->strategy == PARTITION_STRATEGY_LIST ||
 			   context->strategy == PARTITION_STRATEGY_RANGE);
-		if (partition_bound_has_default(context->boundinfo))
-			result = bms_add_member(result,
+		Assert(partition_bound_has_default(context->boundinfo));
+
+		partitions = bms_add_member(partitions,
 									context->boundinfo->default_index);
 	}
 
-	return result;
+	return partitions;
 }
 
 /* Module-local functions */
@@ -1788,7 +1769,6 @@ static PruneStepResult *
 perform_pruning_base_step(PartitionPruneContext *context,
 						  PartitionPruneStepOp *opstep)
 {
-	PruneStepResult *result;
 	ListCell   *lc1,
 			   *lc2;
 	int			keyno,
@@ -1859,55 +1839,35 @@ perform_pruning_base_step(PartitionPruneContext *context,
 		}
 	}
 
-	result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
-
 	switch (context->strategy)
 	{
 		case PARTITION_STRATEGY_HASH:
-			result->bound_offsets = get_matching_hash_bound(context,
-												opstep->opstrategy,
-												values, nvalues,
-												partsupfunc,
-												opstep->nullkeys,
-												&result->scan_all_nonnull);
-			/*
-			 * Since there are neither of the special partitions (null and
-			 * default) in case of hash partitioning, scan_null and
-			 * scan_default are not set.
-			 */
-			result->scan_null = result->scan_default = false;
-			break;
+			return get_matching_hash_bounds(context,
+											opstep->opstrategy,
+											values, nvalues,
+											partsupfunc,
+											opstep->nullkeys);
 
 		case PARTITION_STRATEGY_LIST:
-			result->bound_offsets = get_matching_list_bounds(context,
-													opstep->opstrategy,
-													values[0], nvalues,
-													&partsupfunc[0],
-													opstep->nullkeys,
-													&result->scan_null,
-													&result->scan_default,
-													&result->scan_all_nonnull);
-			break;
+			return get_matching_list_bounds(context,
+											opstep->opstrategy,
+											values[0], nvalues,
+											&partsupfunc[0],
+											opstep->nullkeys,
+											opstep->notnullkeys);
 
 		case PARTITION_STRATEGY_RANGE:
-			result->bound_offsets = get_matching_range_bounds(context,
-													opstep->opstrategy,
-													values, nvalues,
-													partsupfunc,
-													opstep->nullkeys,
-													&result->scan_default,
-													&result->scan_all_nonnull);
-			/* There is no special null-accepting range partition. */
-			result->scan_null = false;
-			break;
+			return get_matching_range_bounds(context,
+											 opstep->opstrategy,
+											 values, nvalues,
+											 partsupfunc,
+											 opstep->nullkeys);
 
 		default:
 			elog(ERROR, "unexpected partition strategy: %d",
 				 (int) context->strategy);
-			break;
+			return NULL;			/* keep compiler quiet */
 	}
-
-	return result;
 }
 
 /*
@@ -1938,8 +1898,7 @@ perform_pruning_combine_step(PartitionPruneContext *context,
 	{
 		PartitionBoundInfo boundinfo = context->boundinfo;
 
-		result->bound_offsets = NULL;
-		result->scan_all_nonnull = true;
+		result->bound_offsets = bms_add_range(NULL, 0, boundinfo->ndatums - 1);
 		result->scan_default = partition_bound_has_default(boundinfo);
 		result->scan_null = partition_bound_accepts_nulls(boundinfo);
 		return result;
@@ -1968,22 +1927,13 @@ perform_pruning_combine_step(PartitionPruneContext *context,
 					step_result = step_results[step_id];
 					Assert(step_result != NULL);
 
-					result->scan_all_nonnull = step_result->scan_all_nonnull;
-
-					/*
-					 * Set bound_offsets if required.  A bound's offset will
-					 * be added to the results if it is present in either the
-					 * source or the target.
-					 */
-					if (!result->scan_all_nonnull)
-						result->bound_offsets =
-								bms_add_members(result->bound_offsets,
+					/* Record any additional datum indexes from this step */
+					result->bound_offsets =
+										bms_add_members(result->bound_offsets,
 												step_result->bound_offsets);
 					/* Update whether to scan null and default partitions. */
-					if (!result->scan_null)
-						result->scan_null = step_result->scan_null;
-					if (!result->scan_default)
-						result->scan_default = step_result->scan_default;
+					result->scan_null |= step_result->scan_null;
+					result->scan_default |= step_result->scan_default;
 				}
 				break;
 
@@ -2005,19 +1955,12 @@ perform_pruning_combine_step(PartitionPruneContext *context,
 						result->bound_offsets = step_result->bound_offsets;
 						result->scan_null = step_result->scan_null;
 						result->scan_default = step_result->scan_default;
-						result->scan_all_nonnull =
-											step_result->scan_all_nonnull;
 						firststep = false;
 					}
 					else
 					{
-						/*
-						 * Set bound_offsets if required.  A bound's offset
-						 * will be added to the results only if it is present
-						 * in both the source and the target.
-						 */
-						if (!step_result->scan_all_nonnull)
-							result->bound_offsets =
+						/* Record datum indexes common to both steps */
+						result->bound_offsets =
 								bms_int_members(result->bound_offsets,
 												step_result->bound_offsets);
 						/*
@@ -2049,32 +1992,20 @@ perform_pruning_combine_step(PartitionPruneContext *context,
 					source = step_results[source_step_id];
 					Assert(source != NULL);
 
-					result->scan_all_nonnull = source->scan_all_nonnull;
-
-					/*
-					 * Set bound_offsets if required.  A bound's offset
-					 * will be added to the results only if it is present
-					 * in target but not the source.
-					 */
-					if (!result->scan_all_nonnull)
-					{
-						/* First add all possible datum offsets. */
-						result->bound_offsets =
-										bms_add_range(NULL, 0,
-													  boundinfo->ndatums - 1);
-						/* Remove from it the members present in source. */
-						result->bound_offsets =
+					/* Generate bitwise-NOT of members. */
+					result->bound_offsets = bms_add_range(NULL, 0,
+													boundinfo->ndatums - 1);
+					result->bound_offsets =
 									bms_del_members(result->bound_offsets,
 													source->bound_offsets);
-					}
 
 					/*
-					 * Revert whether to scan the null partition as the source
-					 * steps would've determined it.
+					 * Invert whether to scan the null partition as the source
+					 * steps would've determined it.  We need not set this flag
+					 * if there's no NULL partition.
 					 */
-					Assert(!source->scan_null ||
-							partition_bound_accepts_nulls(boundinfo));
-					result->scan_null = !source->scan_null;
+					result->scan_null = !source->scan_null &&
+										partition_bound_accepts_nulls(boundinfo);
 
 					/*
 					 * Unlike other partitions, the set of values contained in
@@ -2089,14 +2020,9 @@ perform_pruning_combine_step(PartitionPruneContext *context,
 					 * non-default partition defined, would still contain
 					 * datums of the partition key's type that could only be
 					 * in the default partition.
-					 *
-					 * XXX - the above reasoing only seems to apply if the
-					 * table is list partitioned.  Maybe we should Assert that
-					 * it is.  Currently, we generate a combine step with
-					 * the inversion op only for a case that's supported for
-					 * list partitioning.
 					 */
-					result->scan_default = true;
+					result->scan_default =
+									partition_bound_has_default(boundinfo);
 				}
 				break;
 
@@ -2131,7 +2057,7 @@ partkey_datum_from_expr(PartitionPruneContext *context,
 }
 
 /*
- * get_matching_hash_bound
+ * get_matching_hash_bounds
  *		Determine offset of the hash bound matching the specified value,
  *		considering that all the non-null values come from clauses containing
  *		a compatible hash eqaulity operator and any keys that are null come
@@ -2152,16 +2078,13 @@ partkey_datum_from_expr(PartitionPruneContext *context,
  * hash for the type of the values contained in 'values'
 
  * 'nullkeys' is the set of partition keys that are null.
- *
- * '*scan_all_nonnull' is set if all partitions containing non-null datums
- * should be scanned
  */
-static Bitmapset *
-get_matching_hash_bound(PartitionPruneContext *context,
-						int opstrategy, Datum *values, int nvalues,
-						FmgrInfo *partsupfunc, Bitmapset *nullkeys,
-						bool *scan_all_nonnull)
+static PruneStepResult *
+get_matching_hash_bounds(PartitionPruneContext *context,
+						 int opstrategy, Datum *values, int nvalues,
+						 FmgrInfo *partsupfunc, Bitmapset *nullkeys)
 {
+	PruneStepResult *result;
 	PartitionBoundInfo boundinfo = context->boundinfo;
 	int		   *partindices = boundinfo->indexes;
 	int			partnatts = context->partnatts;
@@ -2172,6 +2095,8 @@ get_matching_hash_bound(PartitionPruneContext *context,
 
 	Assert(context->strategy == PARTITION_STRATEGY_HASH);
 
+	result = palloc0(sizeof(PruneStepResult));
+
 	/*
 	 * For hash partitioning we can only perform pruning based on equality
 	 * clauses to the partition key or IS NULL clauses.  We also can only
@@ -2179,7 +2104,6 @@ get_matching_hash_bound(PartitionPruneContext *context,
 	 */
 	if (nvalues + bms_num_members(nullkeys) == partnatts)
 	{
-		*scan_all_nonnull = false;
 		/*
 		 * If there are any values, they must have come from clauses
 		 * containing an equality operator compatible with hash partitioning.
@@ -2193,12 +2117,13 @@ get_matching_hash_bound(PartitionPruneContext *context,
 		rowHash = compute_hash_value(partnatts, partsupfunc, values, isnull);
 
 		if (partindices[rowHash % greatest_modulus] >= 0)
-			return bms_make_singleton(rowHash % greatest_modulus);
+			result->bound_offsets = bms_make_singleton(rowHash % greatest_modulus);
 	}
 	else
-		*scan_all_nonnull = true;
+		result->bound_offsets = bms_add_range(NULL, 0,
+											  boundinfo->ndatums - 1);
 
-	return NULL;
+	return result;
 }
 
 /*
@@ -2216,22 +2141,14 @@ get_matching_hash_bound(PartitionPruneContext *context,
  * to perform partition_list_bsearch
 
  * 'nullkeys' is the set of partition keys that are null.
- *
- * '*scan_null' is set if the special null-accepting partition should be
- * scanned
- *
- * '*scan_default' is set if the special default partition should be scanned
- *
- * '*scan_all_nonnull' is set if all partitions containing non-null datums
- * should be scanned
  */
-static Bitmapset *
+static PruneStepResult *
 get_matching_list_bounds(PartitionPruneContext *context,
 						 int opstrategy, Datum value, int nvalues,
 						 FmgrInfo *partsupfunc, Bitmapset *nullkeys,
-						 bool *scan_null, bool *scan_default,
-						 bool *scan_all_nonnull)
+						 Bitmapset *notnullkeys)
 {
+	PruneStepResult *result;
 	PartitionBoundInfo boundinfo = context->boundinfo;
 	int			off,
 				minoff,
@@ -2243,7 +2160,7 @@ get_matching_list_bounds(PartitionPruneContext *context,
 	Assert(context->strategy == PARTITION_STRATEGY_LIST);
 	Assert(context->partnatts == 1);
 
-	*scan_null = *scan_default = *scan_all_nonnull = false;
+	result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
 
 	if (!bms_is_empty(nullkeys))
 	{
@@ -2253,10 +2170,21 @@ get_matching_list_bounds(PartitionPruneContext *context,
 		 * the former doesn't exist.
 		 */
 		if (partition_bound_accepts_nulls(boundinfo))
-			*scan_null = true;
-		else if (partition_bound_has_default(boundinfo))
-			*scan_default = true;
-		return NULL;
+			result->scan_null = true;
+		else
+			result->scan_default = partition_bound_has_default(boundinfo);
+		return result;
+	}
+
+	/*
+	 * Handle IS NOT NULL clauses.  Here we just include everything apart from
+	 * the NULL partition.
+	 */
+	if (!bms_is_empty(notnullkeys))
+	{
+		result->bound_offsets = bms_add_range(NULL, 0, boundinfo->ndatums - 1);
+		result->scan_default = partition_bound_has_default(boundinfo);
+		return result;
 	}
 
 	/*
@@ -2265,9 +2193,8 @@ get_matching_list_bounds(PartitionPruneContext *context,
 	 */
 	if (boundinfo->ndatums == 0)
 	{
-		if (partition_bound_has_default(boundinfo))
-			*scan_default = true;
-		return NULL;
+		result->scan_default = partition_bound_has_default(boundinfo);
+		return result;
 	}
 
 	minoff = 0;
@@ -2283,21 +2210,29 @@ get_matching_list_bounds(PartitionPruneContext *context,
 	 */
 	if (opstrategy != BTEqualStrategyNumber &&
 		partition_bound_has_default(boundinfo))
-		*scan_default = true;
+		result->scan_default = true;
 
 	/*
-	 * If there are no values to compare with the datums in boundinfo, it
-	 * means the caller asked for partitions for all non-null datums.  Add
-	 * indexes of *all* partitions.
+	 * If there are no values to compare with the datums in boundinfo then
+	 * handle this according to what the opstrategy is set to.  Normally this
+	 * will mean we must include all non-null datums, however, in the case
+	 * that we're performing partition elimination for not-equal clauses, lack
+	 * of any values means we want to return only the NULL partition. The
+	 * result of this will be inverted to become all partitions apart from the
+	 * NULL.
 	 */
 	if (nvalues == 0)
 	{
-		*scan_all_nonnull = true;
-		return NULL;
+		if (opstrategy != InvalidStrategy)
+			result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
+		else
+			result->scan_null = partition_bound_accepts_nulls(boundinfo);
+		return result;
 	}
 
 	switch (opstrategy)
 	{
+		case InvalidStrategy:		/* inverted not-equal case */
 		case BTEqualStrategyNumber:
 			off = partition_list_bsearch(partsupfunc,
 										 partcollation,
@@ -2306,11 +2241,11 @@ get_matching_list_bounds(PartitionPruneContext *context,
 			if (off >= 0 && is_equal)
 			{
 				Assert(boundinfo->indexes[off] >= 0);
-				return bms_make_singleton(off);
+				result->bound_offsets = bms_make_singleton(off);
+				return result;
 			}
-			else if (partition_bound_has_default(boundinfo))
-				*scan_default = true;
-			return NULL;
+			result->scan_default = partition_bound_has_default(boundinfo);
+			return result;
 
 		case BTGreaterEqualStrategyNumber:
 			inclusive = true;
@@ -2342,7 +2277,7 @@ get_matching_list_bounds(PartitionPruneContext *context,
 			 * above anyway if one exists.
 			 */
 			if (off > boundinfo->ndatums - 1)
-				return NULL;
+				return result;
 
 			minoff = off;
 			break;
@@ -2365,7 +2300,7 @@ get_matching_list_bounds(PartitionPruneContext *context,
 			 * above anyway if one exists.
 			 */
 			if (off < 0)
-				return NULL;
+				return result;
 
 			maxoff = off;
 			break;
@@ -2375,7 +2310,8 @@ get_matching_list_bounds(PartitionPruneContext *context,
 			break;
 	}
 
-	return bms_add_range(NULL, minoff, maxoff);
+	result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
+	return result;
 }
 
 /*
@@ -2402,12 +2338,12 @@ get_matching_list_bounds(PartitionPruneContext *context,
  *
  * 'nullkeys' is the set of partition keys that are null.
  */
-static Bitmapset *
+static PruneStepResult *
 get_matching_range_bounds(PartitionPruneContext *context,
 						  int opstrategy, Datum *values, int nvalues,
-						  FmgrInfo *partsupfunc, Bitmapset *nullkeys,
-						  bool *scan_default, bool *scan_all_nonnull)
+						  FmgrInfo *partsupfunc, Bitmapset *nullkeys)
 {
+	PruneStepResult *result;
 	PartitionBoundInfo boundinfo = context->boundinfo;
 	Oid		   *partcollation = context->partcollation;
 	int			partnatts = context->partnatts;
@@ -2422,7 +2358,7 @@ get_matching_range_bounds(PartitionPruneContext *context,
 	Assert(context->strategy == PARTITION_STRATEGY_RANGE);
 	Assert(nvalues <= partnatts);
 
-	*scan_default = *scan_all_nonnull = false;
+	result = (PruneStepResult *) palloc0(sizeof(PruneStepResult));
 
 	/*
 	 * If there are no datums to compare keys with, or if we got an IS NULL
@@ -2430,9 +2366,8 @@ get_matching_range_bounds(PartitionPruneContext *context,
 	 */
 	if (boundinfo->ndatums == 0 || !bms_is_empty(nullkeys))
 	{
-		if (partition_bound_has_default(boundinfo))
-			*scan_default = true;
-		return NULL;
+		result->scan_default = partition_bound_has_default(boundinfo);
+		return result;
 	}
 
 	minoff = 0;
@@ -2451,10 +2386,10 @@ get_matching_range_bounds(PartitionPruneContext *context,
 		if (partindices[maxoff] < 0)
 			maxoff--;
 
-		if (partition_bound_has_default(boundinfo))
-			*scan_default = true;
+		result->scan_default = partition_bound_has_default(boundinfo);
 
-		return bms_add_range(NULL, minoff, maxoff);
+		result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
+		return result;
 	}
 
 	/*
@@ -2462,7 +2397,7 @@ get_matching_range_bounds(PartitionPruneContext *context,
 	 * the default partition, if any.
 	 */
 	if (nvalues < partnatts && partition_bound_has_default(boundinfo))
-		*scan_default = true;
+		result->scan_default = true;
 
 	switch (opstrategy)
 	{
@@ -2482,10 +2417,12 @@ get_matching_range_bounds(PartitionPruneContext *context,
 				{
 					/* There can only be zero or one matching partition. */
 					if (partindices[off + 1] >= 0)
-						return bms_make_singleton(off + 1);
-					else if (partition_bound_has_default(boundinfo))
-						*scan_default = true;
-					return NULL;
+					{
+						result->bound_offsets = bms_make_singleton(off + 1);
+						return result;
+					}
+					result->scan_default = partition_bound_has_default(boundinfo);
+					return result;
 				}
 				else
 				{
@@ -2591,14 +2528,15 @@ get_matching_range_bounds(PartitionPruneContext *context,
 					{
 						if (partindices[i] < 0)
 						{
-							*scan_default = true;
+							result->scan_default = true;
 							break;
 						}
 					}
 				}
 
 				Assert(minoff >= 0 && maxoff >= 0);
-				return bms_add_range(NULL, minoff, maxoff);
+				result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
+				return result;
 			}
 			else if (off >= 0)	/* !is_equal */
 			{
@@ -2610,18 +2548,17 @@ get_matching_range_bounds(PartitionPruneContext *context,
 				 * only partition that may contain the look-up value.
 				 */
 				if (partindices[off + 1] >= 0)
-					return bms_make_singleton(off + 1);
-				else if (partition_bound_has_default(boundinfo))
-					*scan_default = true;
-				return NULL;
+					result->bound_offsets = bms_make_singleton(off + 1);
+				else
+					result->scan_default = partition_bound_has_default(boundinfo);
+				return result;
 			}
 			/*
 			 * off < 0, meaning the look-up value is smaller that all bounds,
 			 * so only the default partition, if any, qualifies.
 			 */
-			else if (partition_bound_has_default(boundinfo))
-				*scan_default = true;
-			return NULL;
+			result->scan_default = partition_bound_has_default(boundinfo);
+			return result;
 
 		case BTGreaterEqualStrategyNumber:
 			inclusive = true;
@@ -2719,9 +2656,8 @@ get_matching_range_bounds(PartitionPruneContext *context,
 				 * All bounds are greater than the key, so we could only
 				 * expect to find the look-up key in the default partition.
 				 */
-				if (partition_bound_has_default(boundinfo))
-					*scan_default = true;
-				return NULL;
+				result->scan_default = partition_bound_has_default(boundinfo);
+				return result;
 			}
 			else
 			{
@@ -2794,7 +2730,7 @@ get_matching_range_bounds(PartitionPruneContext *context,
 			PARTITION_RANGE_DATUM_VALUE &&
 			partition_bound_has_default(boundinfo))
 		{
-			*scan_default = true;
+			result->scan_default = true;
 		}
 
 		minoff++;
@@ -2815,7 +2751,7 @@ get_matching_range_bounds(PartitionPruneContext *context,
 			PARTITION_RANGE_DATUM_VALUE &&
 			partition_bound_has_default(boundinfo))
 		{
-			*scan_default = true;
+			result->scan_default = true;
 		}
 
 		maxoff--;
@@ -2832,7 +2768,7 @@ get_matching_range_bounds(PartitionPruneContext *context,
 		{
 			if (partindices[i] < 0)
 			{
-				*scan_default = true;
+				result->scan_default = true;
 				break;
 			}
 		}
@@ -2840,8 +2776,9 @@ get_matching_range_bounds(PartitionPruneContext *context,
 
 	Assert(minoff >= 0 && maxoff >= 0);
 	if (minoff > maxoff)
-		return NULL;
-	return bms_add_range(NULL, minoff, maxoff);
+		return result;
+	result->bound_offsets = bms_add_range(NULL, minoff, maxoff);
+	return result;
 }
 
 /*
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index f5634274a9..51f1baa42e 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2141,10 +2141,12 @@ _copyPartitionPruneStepOp(const PartitionPruneStepOp *from)
 {
 	PartitionPruneStepOp *newnode = makeNode(PartitionPruneStepOp);
 
+	COPY_SCALAR_FIELD(step.step_id);
 	COPY_SCALAR_FIELD(opstrategy);
 	COPY_NODE_FIELD(exprs);
 	COPY_NODE_FIELD(cmpfns);
 	COPY_BITMAPSET_FIELD(nullkeys);
+	COPY_BITMAPSET_FIELD(notnullkeys);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 3f9e2585c7..de0b3def2c 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1703,6 +1703,7 @@ _outPartitionPruneStepOp(StringInfo str, const PartitionPruneStepOp *node)
 	WRITE_NODE_FIELD(exprs);
 	WRITE_NODE_FIELD(cmpfns);
 	WRITE_BITMAPSET_FIELD(nullkeys);
+	WRITE_BITMAPSET_FIELD(notnullkeys);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 8348933151..00eb43823f 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1338,6 +1338,7 @@ _readPartitionPruneStepOp(void)
 	READ_NODE_FIELD(exprs);
 	READ_NODE_FIELD(cmpfns);
 	READ_BITMAPSET_FIELD(nullkeys);
+	READ_BITMAPSET_FIELD(notnullkeys);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/util/partprune.c b/src/backend/optimizer/util/partprune.c
index e5e6d7530b..f793d7d266 100644
--- a/src/backend/optimizer/util/partprune.c
+++ b/src/backend/optimizer/util/partprune.c
@@ -119,8 +119,8 @@ static List *get_steps_using_prefix_recurse(GeneratePruningStepsContext *context
 							   List *step_exprs,
 							   List *step_cmpfns);
 static Node *generate_pruning_step_op(GeneratePruningStepsContext *context,
-							int opstrategy,
-							List *exprs, List *cmpfns, Bitmapset *nullkeys);
+							int opstrategy, List *exprs, List *cmpfns,
+							Bitmapset *nullkeys, Bitmapset *notnullkeys);
 static Node *generate_pruning_step_combine(GeneratePruningStepsContext *context,
 							List *source_stepids,
 							PartitionPruneCombineOp combineOp);
@@ -434,7 +434,6 @@ generate_partition_pruning_steps_internal(RelOptInfo *rel,
 														  COMBINE_UNION));
 				continue;
 			}
-
 			/*
 			 * Fall-through for a NOT clause, which is handled in
 			 * match_clause_to_partition_key().
@@ -573,14 +572,21 @@ generate_partition_pruning_steps_internal(RelOptInfo *rel,
 			PartitionPruneStep *step;
 
 			/*
-			 * Generate a opstep using what must be a btree = operator, that
-			 * is, the negator of <> originally appearing in the clause.
+			 * Generate an opstep using what must be a btree = operator, that
+			 * is, the negator of <> originally appearing in the clause. We
+			 * pass InvalidStrategy as the opstrategy rather than
+			 * BTEqualStrategyNumber to signal to the step execution code to
+			 * handle the case when no values are available for pruning.
+			 * Normally when no values are available we'd return all non-null
+			 * partitions, but in this case we want to return NULL, so that
+			 * when the result is inverted it becomes all non-null partitions.
 			 */
 			step = (PartitionPruneStep *)
 						generate_pruning_step_op(context,
-												 BTEqualStrategyNumber,
+												 InvalidStrategy,
 												 list_make1(pc->expr),
 												 list_make1_oid(pc->cmpfn),
+												 NULL,
 												 NULL);
 			step_ids = lappend_int(step_ids, step->step_id);
 		}
@@ -594,8 +600,8 @@ generate_partition_pruning_steps_internal(RelOptInfo *rel,
 		 */
 		Assert(part_scheme->partnatts == 1);
 		nullpartStep = (PartitionPruneStep *)
-						   generate_pruning_step_op(context, 0, NIL, NIL,
-													bms_make_singleton(0));
+						   generate_pruning_step_op(context, InvalidStrategy, NIL, NIL,
+													bms_make_singleton(0), NULL);
 		step_ids = lappend_int(step_ids, nullpartStep->step_id);
 
 		/* Combine all opsteps above using a UNION combine step first. */
@@ -626,8 +632,9 @@ generate_partition_pruning_steps_internal(RelOptInfo *rel,
 			(part_scheme->strategy != PARTITION_STRATEGY_HASH ||
 			 bms_num_members(nullkeys) == part_scheme->partnatts))
 			result = lappend(result,
-							 generate_pruning_step_op(context, 0, NIL, NIL,
-													  nullkeys));
+							 generate_pruning_step_op(context,
+													  InvalidStrategy, NIL,
+													  NIL, nullkeys, NULL));
 
 		/*
 		 * Note that for IS NOT NULL clauses, simply having step suffices;
@@ -638,8 +645,9 @@ generate_partition_pruning_steps_internal(RelOptInfo *rel,
 		if (!bms_is_empty(notnullkeys) &&
 			part_scheme->strategy != PARTITION_STRATEGY_HASH)
 			result = lappend(result,
-							 generate_pruning_step_op(context, 0, NIL, NIL,
-													  NULL));
+							 generate_pruning_step_op(context,
+													  InvalidStrategy, NIL,
+													  NIL, NULL, notnullkeys));
 	}
 	else
 	{
@@ -1535,7 +1543,7 @@ get_steps_using_prefix(GeneratePruningStepsContext *context,
 		return list_make1(generate_pruning_step_op(context, step_opstrategy,
 											   list_make1(step_lastexpr),
 											   list_make1_oid(step_lastcmpfn),
-											   step_nullkeys));
+											   step_nullkeys, NULL));
 
 	/* Recurse to generate steps for various combinations. */
 	return get_steps_using_prefix_recurse(context,
@@ -1669,7 +1677,8 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context,
 													  step_opstrategy,
 													  step_exprs1,
 													  step_cmpfns1,
-													  step_nullkeys));
+													  step_nullkeys,
+													  NULL));
 		}
 	}
 
@@ -1685,7 +1694,8 @@ get_steps_using_prefix_recurse(GeneratePruningStepsContext *context,
 static Node *
 generate_pruning_step_op(GeneratePruningStepsContext *context,
 						 int opstrategy, List *exprs, List *cmpfns,
-						 Bitmapset *nullkeys)
+						 Bitmapset *nullkeys,
+						 Bitmapset *notnullkeys)
 {
 	PartitionPruneStepOp *opstep = makeNode(PartitionPruneStepOp);
 
@@ -1694,6 +1704,7 @@ generate_pruning_step_op(GeneratePruningStepsContext *context,
 	opstep->exprs = exprs;
 	opstep->cmpfns = cmpfns;
 	opstep->nullkeys = nullkeys;
+	opstep->notnullkeys = notnullkeys;
 
 	context->steps = lappend(context->steps, opstep);
 
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index a71d729e72..6f28cd4d56 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1546,6 +1546,9 @@ typedef struct PartitionPruneStep
  * to the hash partition bound search function.  It is never possible to
  * have an expression be present in 'exprs' for a given partition key and
  * the corresponding bit set in 'nullkeys'.
+ *
+ * 'notnullkeys' is similar to 'nullkeys' but this can be set not just for
+ * IS NOT NULL clauses, but also for strict clauses in general.
  *----------
  */
 typedef struct PartitionPruneStepOp
@@ -1556,6 +1559,7 @@ typedef struct PartitionPruneStepOp
 	List	   *exprs;
 	List	   *cmpfns;
 	Bitmapset  *nullkeys;
+	Bitmapset  *notnullkeys;
 } PartitionPruneStepOp;
 
 /*----------
