From c6df0d6e55b3aed79ffd23670656a991e8a243c9 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 16 May 2023 19:33:01 -0400
Subject: [PATCH v6 3/5] Examine the transitive closure in
 add_outer_joins_to_relids.

We also have to check outer joins that commute with commutators
of the one we just completed.

Richard Guo

Discussion: https://postgr.es/m/0b819232-4b50-f245-1c7d-c8c61bf41827@postgrespro.ru
---
 src/backend/optimizer/path/joinrels.c | 29 +++++++++++++++++++++------
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 262d3aec65..e8beaca196 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -804,12 +804,17 @@ 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.)
+	 * There will not be any higher OJs that can be added either, so we're
+	 * done.
 	 */
-	if (bms_is_subset(sjinfo->commute_below_l, input_relids))
-		input_relids = bms_add_member(input_relids, sjinfo->ojrelid);
+	if (!bms_is_subset(sjinfo->commute_below_l, input_relids))
+		return input_relids;
+
+	/* OK to add OJ's own relid */
+	input_relids = bms_add_member(input_relids, sjinfo->ojrelid);
 
 	/*
 	 * Contrariwise, if we are now forming the final result of such a commuted
@@ -818,6 +823,7 @@ add_outer_joins_to_relids(PlannerInfo *root, Relids input_relids,
 	 */
 	if (sjinfo->commute_above_l)
 	{
+		Relids		commute_above_rels = bms_copy(sjinfo->commute_above_l);
 		ListCell   *lc;
 
 		/*
@@ -835,15 +841,26 @@ add_outer_joins_to_relids(PlannerInfo *root, Relids input_relids,
 				othersj->ojrelid == 0 || othersj->jointype != JOIN_LEFT)
 				continue;		/* definitely not interesting */
 
-			if (othersj->commute_below_l == NULL)
-				continue;		/* was never a candidate to be pushed down */
+			if (!bms_is_member(othersj->ojrelid, commute_above_rels))
+				continue;
 
 			/* 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) &&
 				bms_is_subset(othersj->commute_below_l, input_relids))
+			{
 				input_relids = bms_add_member(input_relids, othersj->ojrelid);
+
+				/*
+				 * We must also check any joins that othersj potentially
+				 * commutes with.  They likewise must appear later in
+				 * join_info_list than othersj itself, so we can visit them
+				 * later in this loop.
+				 */
+				commute_above_rels = bms_add_members(commute_above_rels,
+													 othersj->commute_above_l);
+			}
 		}
 	}
 
-- 
2.31.1

