diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 77e6ff5376..e14a2e51c2 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -1378,7 +1378,7 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 	Assert(joinrel->consider_partitionwise_join);
 
 	/*
-	 * We can not perform partition-wise join if either of the joining
+	 * We can not perform partitionwise join if either of the joining
 	 * relations is not partitioned.
 	 */
 	if (!IS_PARTITIONED_REL(rel1) || !IS_PARTITIONED_REL(rel2))
@@ -1399,8 +1399,9 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 
 	/*
 	 * If we don't have the partition bounds for the join rel yet, try to
-	 * create it along with pairs of partitions to be joined; else generate
-	 * those using the partitioning info for the join rel we already have.
+	 * compute those along with pairs of partitions to be joined; else generate
+	 * the pairs using the partitioning info of the join relation we already
+	 * have.
 	 */
 	if (joinrel->nparts == -1)
 	{
@@ -1412,16 +1413,17 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 		Assert(joinrel->part_rels == NULL);
 
 		/*
-		 * See if the partition bounds for inputs are exactly the same, in
-		 * which case we don't need to work hard: partitions with the same
-		 * partition indexes will form join pairs, and the join rel will have
-		 * the same partition bounds as inputs; otherwise try to merge the
-		 * partition bounds along with generating join pairs.
+		 * See if the partition bounds of the joining relations are exactly the
+		 * same, in which case we don't need to work hard: partitions with the
+		 * same partition indexes will form join pairs, and the join relation
+		 * will have the same partition bounds as the joining relations;
+		 * otherwise try to merge the partition bounds along with generating
+		 * join pairs.
 		 *
-		 * Even if one or both inputs have merged partition bounds, it'd be
-		 * possible for the partition bounds to be exactly the same, but it
-		 * seems unlikely to be worth the cycles to check; do this check only
-		 * if both inputs have non-merged partition bounds.
+		 * Even if one or both the joining relations have merged partition
+		 * bounds, it'd be possible for the partition bounds to be exactly the
+		 * same, but it seems unlikely to be worth the cycles to check; do this
+		 * check only if both inputs have non-merged partition bounds.
 		 */
 		if (!rel1->merged &&
 			!rel2->merged &&
@@ -1467,10 +1469,11 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 		Assert(joinrel->part_rels);
 
 		/*
-		 * If the partition bounds for the join rel are not merged ones,
-		 * inputs are guaranteed to have the same partition bounds, so
-		 * partitions with the same partition indexes will form join pairs;
-		 * else let get_matching_part_pairs() do the work.
+		 * If the partition bounds of the join relation were computed by
+		 * merging the bounds of the joining relations, generate the pairs of
+		 * joining partitions by matching their relids. Nothing to do otherwise
+		 * since the partitions at same cardinal positions form the joining
+		 * pairs.
 		 */
 		if (joinrel->merged)
 		{
@@ -1847,8 +1850,6 @@ get_matching_part_pairs(PlannerInfo *root, RelOptInfo *joinrel,
 						RelOptInfo *rel1, RelOptInfo *rel2,
 						List **parts1, List **parts2)
 {
-	bool		rel1_is_simple = IS_SIMPLE_REL(rel1);
-	bool		rel2_is_simple = IS_SIMPLE_REL(rel2);
 	int 		cnt_parts;
 
 	*parts1 = NIL;
@@ -1863,7 +1864,7 @@ get_matching_part_pairs(PlannerInfo *root, RelOptInfo *joinrel,
 		Relids		child_relids2;
 
 		/*
-		 * If this segment of the join is empty, it means that this segment
+		 * If the current partition of the join is empty, it means that this segment
 		 * was ignored when previously creating child-join paths for it in
 		 * try_partitionwise_join() as it would not contribute to the join
 		 * result, due to one or both inputs being empty; add NULL to each of
@@ -1893,7 +1894,7 @@ get_matching_part_pairs(PlannerInfo *root, RelOptInfo *joinrel,
 		 * joined even when planning partitionwise joins of rel1, meaning that
 		 * the child rel would have been built by the time we get here.
 		 */
-		if (rel1_is_simple)
+		if (IS_SIMPLE_REL(rel1))
 		{
 			int			varno = bms_singleton_member(child_relids1);
 
@@ -1914,7 +1915,7 @@ get_matching_part_pairs(PlannerInfo *root, RelOptInfo *joinrel,
 		/*
 		 * Get a child rel for rel2 with the relids.  See above comments.
 		 */
-		if (rel2_is_simple)
+		if (IS_SIMPLE_REL(rel2))
 		{
 			int			varno = bms_singleton_member(child_relids2);
 
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 3c7b6030c0..f639e2c69b 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -3156,6 +3156,16 @@ partition_bounds_merge(int partnatts,
 	switch (strategy)
 	{
 		case PARTITION_STRATEGY_HASH:
+			/*
+			 * When the modulo of used for partition mapping (i.e. maximum
+			 * number of has partitions) is same, their partition bounds are
+			 * same and do not need merging. If the maximum number of
+			 * partitions is different for both the joining relations, there's
+			 * high probability that one partition on one side will join with
+			 * multiple partitions on the other side. So exact partition bounds
+			 * match will work in most of the cases. The cases otherwise are
+			 * not so common to spend the effort in coding and planning.
+			 */
 			merged_bounds = NULL;
 
 			break;
@@ -3324,7 +3334,7 @@ compare_range_partitions(int partnatts, FmgrInfo *partsupfuncs,
 
 /*
  * get_merged_range_bounds
- *		Given the bounds of range partitions to be join, determine the range
+ *		Given the bounds of range partitions to be joined, determine the range
  *		bounds of the merged partition produced from the range partitions
  *
  * *merged_lb and *merged_ub are set to the lower and upper bounds of the
@@ -3534,7 +3544,7 @@ partition_range_bounds_merge(int partnatts, FmgrInfo *partsupfuncs,
 		 * the side which finishes earlier has an extra partition with lower
 		 * and upper bounds higher than any other partition of the unfinished
 		 * side. That way we advance the partitions on that side till all of
-		 * them are  exhausted.
+		 * them are exhausted.
 		 */
 		if (outer_part == -1)
 		{
@@ -4093,8 +4103,13 @@ map_and_merge_partitions(PartitionMap *outer_map, PartitionMap *inner_map,
 		if (!outer_merged && !inner_merged)
 		{
 			/*
-			 * Note that we will fix the larger index that have been added to
-			 * the merged_indexes list so far in fix_merged_indexes().
+			 * Both the inner and outer partitions have an empty partition on
+			 * the other side as their joining partner. But now that each of
+			 * them has found a non-empty joining partner we should re-map
+			 * those to a single partition in the join. We use lower of the
+			 * two indexes to avoid any holes being created by re-mapping.
+			 * Also, it keeps the partition index array in partition bounds
+			 * roughly sorted.
 			 */
 			if (outer_merged_index < inner_merged_index)
 			{
@@ -4165,6 +4180,12 @@ map_and_merge_partitions(PartitionMap *outer_map, PartitionMap *inner_map,
 /*
  * merge_partition_with_dummy
  *
+ * The caller thinks that the partition at the given index does not have a
+ * partition in the other relation or the joining partition is empty. In such a
+ * case we assign a temporary index (indicated by merged flag in the map) for
+ * the resulting partition in the join. In case the given partition finds a
+ * non-empty partner latter we will adjust the mapping again.
+ *
  * *next_index is incremented.
  */
 static int
@@ -4206,8 +4227,8 @@ process_outer_partition(PartitionMap *outer_map,
 
 	/*
 	 * If the inner side has the default partition, the outer partition has to
-	 * be joined with the default partition; try merging them.  Otherwise, we
-	 * should in an outer join, in which case the outer partition has to be
+	 * be joined with the default partition; try merging them.  Otherwise
+	 * it's an outer join, in which case the outer partition has to be
 	 * scanned all the way anyway; if the outer partition is already mapped to
 	 * a merged partition, get it, otherwise create a new merged partition by
 	 * merging the outer partition with a dummy partition.
@@ -4217,9 +4238,11 @@ process_outer_partition(PartitionMap *outer_map,
 		Assert(inner_default >= 0);
 
 		/*
-		 * If the outer side has the default partition as well, we need to
-		 * merge the default partitions (see merge_default_partitions()); give
-		 * up on it.
+		 * If the outer side has the default partition as well, the default
+		 * partition from inner side will have two matching partitions on the
+		 * outer side: the default partition on the outer side and the given
+		 * outer partition. Partitionwise join doesn't handle this scenario
+		 * yet.
 		 */
 		if (outer_has_default)
 			return -1;
@@ -4231,9 +4254,13 @@ process_outer_partition(PartitionMap *outer_map,
 			return -1;
 
 		/*
-		 * If this is a FULL join, the merged partition would act as the
-		 * default partition of the join; record the index in *default_index
-		 * if not done yet.
+		 * If this is a FULL join, both the sides act as outer side. Since the
+		 * inner partition is a default partition, it will have partition key
+		 * values which are not covered by any other partition. In join result
+		 * as well, the resulting partition will hold partition key values that
+		 * no other partition holds. Thus the merged partition would act as the
+		 * default partition of the join; record the index in *default_index if
+		 * not done yet.
 		 */
 		if (jointype == JOIN_FULL)
 		{
@@ -4293,8 +4320,11 @@ process_inner_partition(PartitionMap *outer_map,
 
 		/*
 		 * If the inner side has the default partition as well, we need to
-		 * merge the default partitions (see merge_default_partitions()); give
-		 * up on it.
+		 * merge the default partitions (see merge_default_partitions()). So
+		 * there will two inner partitions, given inner partition and the
+		 * default inner partition, that will map to the default outer
+		 * partition. Partitionwise join does not support this case, so give up
+		 * on it.
 		 */
 		if (inner_has_default)
 			return -1;
@@ -4590,6 +4620,13 @@ merge_default_partitions(PartitionMap *outer_map,
 	}
 	else
 	{
+		/*
+		 * We should have already given up if we found that both the inner and
+		 * outer relations have default partitions and either of them had a
+		 * partition without a matching non-default partition on the other
+		 * side. See process_outer_partition() and process_inner_partition()
+		 * for details.
+		 */
 		Assert(outer_has_default && inner_has_default);
 		Assert(outer_merged_index == -1);
 		Assert(inner_merged_index == -1);
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 1545877d8c..bdf8ac4bce 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -720,7 +720,12 @@ typedef struct RelOptInfo
 
 	/* used for partitioned relations */
 	PartitionScheme part_scheme;	/* Partitioning scheme. */
-	int			nparts;			/* number of partitions */
+	int			nparts;			/* number of partitions.
+								 * 0 for a relation with no partitions,
+								 * > 0 indicates actual number of partitions
+								 * -1 for a relation whose number of partitions
+								 *    is not yet known.
+								 */
 	struct PartitionBoundInfoData *boundinfo;	/* Partition bounds */
 	bool		merged;			/* true if partition bounds were created by
 								 * partition_bounds_merge() */
