From 4a0ef2907e3dfd6cfe778020877068996ef749d4 Mon Sep 17 00:00:00 2001
From: Richard Guo <guofenglinux@gmail.com>
Date: Thu, 4 May 2023 15:57:17 +0800
Subject: [PATCH v3 3/4] Improve add_outer_joins_to_relids

When we've completely formed a pushed up outer join, actually we only
need to consider the pushed-down joins that are in its commute_above_l.
But note that this process should be recursive, meaning that if an outer
join in its commute_above_l is also considered qualified to be added to
the relid set, we need to consider that outer join's commute_above_l
too.  By this way we only need to check the relevant SpecialJoinInfos,
rather than scan the whole join_info_list.
---
 src/backend/optimizer/path/joinrels.c | 55 ++++++++++++++-------------
 1 file changed, 28 insertions(+), 27 deletions(-)

diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index f2010bda71..b782474b5d 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -810,6 +810,8 @@ Relids
 add_outer_joins_to_relids(PlannerInfo *root, Relids input_relids,
 						  SpecialJoinInfo *sjinfo)
 {
+	Relids		commute_above_relids;
+
 	/* Nothing to do if this isn't an outer join with an assigned relid. */
 	if (sjinfo == NULL || sjinfo->ojrelid == 0)
 		return input_relids;
@@ -824,46 +826,45 @@ add_outer_joins_to_relids(PlannerInfo *root, Relids input_relids,
 		return bms_add_member(input_relids, sjinfo->ojrelid);
 
 	/*
-	 * Add the OJ relid unless this join has been pushed into the RHS of a
-	 * syntactically-lower left join per OJ identity 3.  (If it has, then we
+	 * We cannot add the OJ relid if this join has been pushed into the RHS of
+	 * a syntactically-lower left join per OJ identity 3.  (If it has, then we
 	 * cannot claim that its outputs represent the final state of its RHS.)
+	 *
+	 * Also in this case there should be no pushed-down outer joins that can be
+	 * added.
 	 */
-	if (commute_below_l_is_subset(sjinfo, input_relids))
-		input_relids = bms_add_member(input_relids, sjinfo->ojrelid);
+	if (!commute_below_l_is_subset(sjinfo, input_relids))
+		return input_relids;
 
 	/*
 	 * Contrariwise, if we are now forming the final result of such a commuted
 	 * pair of OJs, it's time to add the relid(s) of the pushed-down join(s).
 	 * We can skip this if this join was never a candidate to be pushed up.
 	 */
-	if (sjinfo->commute_above_l)
+	input_relids = bms_add_member(input_relids, sjinfo->ojrelid);
+	commute_above_relids = bms_copy(sjinfo->commute_above_l);
+	while (!bms_is_empty(commute_above_relids))
 	{
-		ListCell   *lc;
+		int		above_relid = bms_next_member(commute_above_relids, -1);
+		SpecialJoinInfo *othersj = root->join_info_array[above_relid];
 
-		/*
-		 * The current join could complete the nulling of more than one
-		 * pushed-down join, so we have to examine all the SpecialJoinInfos.
-		 * Because join_info_list was built in bottom-up order, it's
-		 * sufficient to traverse it once: an ojrelid we add in one loop
-		 * iteration would not have affected decisions of earlier iterations.
-		 */
-		foreach(lc, root->join_info_list)
-		{
-			SpecialJoinInfo *othersj = (SpecialJoinInfo *) lfirst(lc);
+		commute_above_relids = bms_del_member(commute_above_relids, above_relid);
 
-			if (othersj == sjinfo ||
-				othersj->ojrelid == 0 || othersj->jointype != JOIN_LEFT)
-				continue;		/* definitely not interesting */
+		Assert(othersj != sjinfo);
+		Assert(othersj->ojrelid != 0);
+		Assert(othersj->jointype == JOIN_LEFT);
+		Assert(othersj->commute_below != NULL);
 
-			if (othersj->commute_below == NULL)
-				continue;		/* was never a candidate to be pushed down */
+		/* Add it if not already present but conditions now satisfied */
+		if (!bms_is_member(othersj->ojrelid, input_relids) &&
+			bms_is_subset(othersj->min_lefthand, input_relids) &&
+			bms_is_subset(othersj->min_righthand, input_relids) &&
+			commute_below_l_is_subset(othersj, input_relids))
+		{
+			input_relids = bms_add_member(input_relids, othersj->ojrelid);
 
-			/* Add it if not already present but conditions now satisfied */
-			if (!bms_is_member(othersj->ojrelid, input_relids) &&
-				bms_is_subset(othersj->min_lefthand, input_relids) &&
-				bms_is_subset(othersj->min_righthand, input_relids) &&
-				commute_below_l_is_subset(othersj, input_relids))
-				input_relids = bms_add_member(input_relids, othersj->ojrelid);
+			commute_above_relids = bms_add_members(commute_above_relids,
+												   othersj->commute_above_l);
 		}
 	}
 
-- 
2.31.0

