diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index d08739127b..9215f566b7 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -156,8 +156,8 @@ static PartitionPruneStep *gen_prune_step_op(GeneratePruningStepsContext *contex
 static PartitionPruneStep *gen_prune_step_combine(GeneratePruningStepsContext *context,
 												  List *source_stepids,
 												  PartitionPruneCombineOp combineOp);
-static PartitionPruneStep *gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
-													   List **keyclauses, Bitmapset *nullkeys);
+static List *gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
+							List **keyclauses, Bitmapset *nullkeys);
 static PartClauseMatchStatus match_clause_to_partition_key(GeneratePruningStepsContext *context,
 														   Expr *clause, Expr *partkey, int partkeyidx,
 														   bool *clause_is_not_null,
@@ -926,24 +926,23 @@ get_matching_partitions(PartitionPruneContext *context, List *pruning_steps)
  * gen_partprune_steps_internal
  *		Processes 'clauses' to generate partition pruning steps.
  *
- * From OpExpr clauses that are mutually AND'd, we find combinations of those
- * that match to the partition key columns and for every such combination,
- * we emit a PartitionPruneStepOp containing a vector of expressions whose
- * values are used as a look up key to search partitions by comparing the
- * values with partition bounds.  Relevant details of the operator and a
- * vector of (possibly cross-type) comparison functions is also included with
- * each step.
+ * From OpExpr clauses that are mutually ANDed, we find combinations of those
+ * that match the partition key columns and make one or more "op" steps
+ * (PartitionPruneStepOp) from those clause combinations; details of how the
+ * combinations can be found in gen_prune_steps_from_opexps(). Each step thus
+ * created is added to context->steps
  *
- * For BoolExpr clauses, we recursively generate steps for each argument, and
- * return a PartitionPruneStepCombine of their results.
+ * For BoolExpr clauses, each argument is processed recursively and to
+ * mix the results of the steps resulting from each, a "combine" step
+ * (PartitionPruneStepCombine) is added to context->steps.
  *
- * The return value is a list of the steps generated, which are also added to
- * the context's steps list.  Each step is assigned a step identifier, unique
- * even across recursive calls.
+ * The return value is the list of steps to evaluate to get the partition set
+ * satisfying the provided clauses.  Note that all of those steps would also
+ * have been added to context->steps.
  *
  * If we find clauses that are mutually contradictory, or contradictory with
  * the partitioning constraint, or a pseudoconstant clause that contains
- * false, we set context->contradictory to true and return NIL (that is, no
+ * false, we set context->contradictory to true and return NULL (that is, no
  * pruning steps).  Caller should consider all partitions as pruned in that
  * case.
  */
@@ -975,7 +974,7 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
 		predicate_refuted_by(context->rel->partition_qual, clauses, false))
 	{
 		context->contradictory = true;
-		return NIL;
+		return NULL;
 	}
 
 	memset(keyclauses, 0, sizeof(keyclauses));
@@ -994,7 +993,7 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
 			 !DatumGetBool(((Const *) clause)->constvalue)))
 		{
 			context->contradictory = true;
-			return NIL;
+			return NULL;
 		}
 
 		/* Get the BoolExpr's out of the way. */
@@ -1046,11 +1045,14 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
 
 					if (argsteps != NIL)
 					{
-						PartitionPruneStep *step;
+						/*
+						 * Because of the way gen_partprune_steps_internal()
+						 * generates its result list, it suffices to only add
+						 * the last of the steps we got from it.
+						 */
+						PartitionPruneStep *last = llast(argsteps);
 
-						Assert(list_length(argsteps) == 1);
-						step = (PartitionPruneStep *) linitial(argsteps);
-						arg_stepids = lappend_int(arg_stepids, step->step_id);
+						arg_stepids = lappend_int(arg_stepids, last->step_id);
 					}
 					else
 					{
@@ -1073,7 +1075,7 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
 				if (all_args_contradictory)
 				{
 					context->contradictory = true;
-					return NIL;
+					return NULL;
 				}
 
 				if (arg_stepids != NIL)
@@ -1089,9 +1091,7 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
 			else if (is_andclause(clause))
 			{
 				List	   *args = ((BoolExpr *) clause)->args;
-				List	   *argsteps,
-						   *arg_stepids = NIL;
-				ListCell   *lc1;
+				List	   *argsteps;
 
 				/*
 				 * args may itself contain clauses of arbitrary type, so just
@@ -1102,23 +1102,16 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
 
 				/* If any AND arm is contradictory, we can stop immediately */
 				if (context->contradictory)
-					return NIL;
-
-				foreach(lc1, argsteps)
-				{
-					PartitionPruneStep *step = lfirst(lc1);
-
-					arg_stepids = lappend_int(arg_stepids, step->step_id);
-				}
+					return NULL;
 
-				if (arg_stepids != NIL)
-				{
-					PartitionPruneStep *step;
+				/*
+				 * Because of the way gen_partprune_steps_internal() generates
+				 * its result list, it suffices to only add the last of the
+				 * steps we got from it.
+				 */
+				if (argsteps != NIL)
+					result = lappend(result, llast(argsteps));
 
-					step = gen_prune_step_combine(context, arg_stepids,
-												  PARTPRUNE_COMBINE_INTERSECT);
-					result = lappend(result, step);
-				}
 				continue;
 			}
 
@@ -1155,7 +1148,7 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
 					if (bms_is_member(i, nullkeys))
 					{
 						context->contradictory = true;
-						return NIL;
+						return NULL;
 					}
 					generate_opsteps = true;
 					keyclauses[i] = lappend(keyclauses[i], pc);
@@ -1172,7 +1165,7 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
 							keyclauses[i] != NIL)
 						{
 							context->contradictory = true;
-							return NIL;
+							return NULL;
 						}
 						nullkeys = bms_add_member(nullkeys, i);
 					}
@@ -1182,7 +1175,7 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
 						if (bms_is_member(i, nullkeys))
 						{
 							context->contradictory = true;
-							return NIL;
+							return NULL;
 						}
 						notnullkeys = bms_add_member(notnullkeys, i);
 					}
@@ -1196,7 +1189,7 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
 				case PARTCLAUSE_MATCH_CONTRADICT:
 					/* We've nothing more to do if a contradiction was found. */
 					context->contradictory = true;
-					return NIL;
+					return NULL;
 
 				case PARTCLAUSE_NOMATCH:
 
@@ -1253,12 +1246,11 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
 	}
 	else if (generate_opsteps)
 	{
-		PartitionPruneStep *step;
+		List   *opsteps;
 
 		/* Strategy 2 */
-		step = gen_prune_steps_from_opexps(context, keyclauses, nullkeys);
-		if (step != NULL)
-			result = lappend(result, step);
+		opsteps = gen_prune_steps_from_opexps(context, keyclauses, nullkeys);
+		result = list_concat(result, opsteps);
 	}
 	else if (bms_num_members(notnullkeys) == part_scheme->partnatts)
 	{
@@ -1271,12 +1263,14 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
 	}
 
 	/*
-	 * Finally, results from all entries appearing in result should be
-	 * combined using an INTERSECT combine step, if more than one.
+	 * Finally, if there are multiple steps, add an INTERSECT step to combine
+	 * the partition sets resulting from them and append it to the result
+	 * list.
 	 */
 	if (list_length(result) > 1)
 	{
 		List	   *step_ids = NIL;
+		PartitionPruneStep *final;
 
 		foreach(lc, result)
 		{
@@ -1285,14 +1279,9 @@ gen_partprune_steps_internal(GeneratePruningStepsContext *context,
 			step_ids = lappend_int(step_ids, step->step_id);
 		}
 
-		if (step_ids != NIL)
-		{
-			PartitionPruneStep *step;
-
-			step = gen_prune_step_combine(context, step_ids,
-										  PARTPRUNE_COMBINE_INTERSECT);
-			result = lappend(result, step);
-		}
+		final = gen_prune_step_combine(context, step_ids,
+									   PARTPRUNE_COMBINE_INTERSECT);
+		result = lappend(result, final);
 	}
 
 	return result;
@@ -1356,15 +1345,28 @@ gen_prune_step_combine(GeneratePruningStepsContext *context,
 
 /*
  * gen_prune_steps_from_opexps
- *		Generate pruning steps based on clauses for partition keys
+ *		Generate pruning "op" steps (PartitionPruneStepOp) based on OpExpr
+ *		clauses that have been matched to partition keys
  *
- * 'keyclauses' contains one list of clauses per partition key.  We check here
- * if we have found clauses for a valid subset of the partition key. In some
- * cases, (depending on the type of partitioning being used) if we didn't
- * find clauses for a given key, we discard clauses that may have been
+ * 'keyclauses' contains a list of clauses for each partition key.  We check
+ * here if we have found clauses for a valid subset of the partition keys. In
+ * some cases, depending on the type of partitioning being used, if we don't
+ * see clauses for a given key, we discard clauses that may have been
  * found for any subsequent keys; see specific notes below.
+ *
+ * Each "op" step generated here will contain a vector of expressions whose
+ * values will constitute a tuple to be used as the look up key to search
+ * partitions by comparing it with partition bound tuples in the
+ * PartitionBoundInfo of the parent table, along with the effective operator
+ * strategy to use in those comparisons (=, >, <, etc.), and a vector of
+ * possibly cross-type comparison functions.
+ *
+ * Note that if multiple steps are returned, the partition set satisfying all
+ * the clauses is the one obtained by intersecting the partition sets of those
+ * individual steps, which the caller should do by adding a relevant "combine"
+ * step.
  */
-static PartitionPruneStep *
+static List *
 gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
 							List **keyclauses, Bitmapset *nullkeys)
 {
@@ -1397,7 +1399,7 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
 		 */
 		if (part_scheme->strategy == PARTITION_STRATEGY_HASH &&
 			clauselist == NIL && !bms_is_member(i, nullkeys))
-			return NULL;
+			return NIL;
 
 		foreach(lc, clauselist)
 		{
@@ -1728,27 +1730,7 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
 			break;
 	}
 
-	/* Lastly, add a combine step to mutually AND these op steps, if needed */
-	if (list_length(opsteps) > 1)
-	{
-		List	   *opstep_ids = NIL;
-
-		foreach(lc, opsteps)
-		{
-			PartitionPruneStep *step = lfirst(lc);
-
-			opstep_ids = lappend_int(opstep_ids, step->step_id);
-		}
-
-		if (opstep_ids != NIL)
-			return gen_prune_step_combine(context, opstep_ids,
-										  PARTPRUNE_COMBINE_INTERSECT);
-		return NULL;
-	}
-	else if (opsteps != NIL)
-		return linitial(opsteps);
-
-	return NULL;
+	return opsteps;
 }
 
 /*
@@ -1782,8 +1764,8 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
  *   true otherwise.
  *
  * * PARTCLAUSE_MATCH_STEPS if there is a match.
- *   Output arguments: *clause_steps is set to a list of PartitionPruneStep
- *   generated for the clause.
+ *   Output arguments: *clause_steps is set to the list of recursively
+ *   generated steps for the clause.
  *
  * * PARTCLAUSE_MATCH_CONTRADICT if the clause is self-contradictory, ie
  *   it provably returns FALSE or NULL.
