From 2c07fe29e255ce374d3bac009236c0331eab04c5 Mon Sep 17 00:00:00 2001
From: Richard Guo <guofenglinux@gmail.com>
Date: Wed, 17 May 2023 15:43:18 +0800
Subject: [PATCH v6 6/6] Collect pushed down outer joins in
 add_outer_joins_to_relids.

---
 src/backend/optimizer/path/equivclass.c |  3 +-
 src/backend/optimizer/path/joinrels.c   | 17 ++++++--
 src/backend/optimizer/util/relnode.c    | 58 ++++++++++---------------
 src/include/optimizer/pathnode.h        |  1 +
 src/include/optimizer/paths.h           |  3 +-
 5 files changed, 42 insertions(+), 40 deletions(-)

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index ecb1343d1a..2db1bf6448 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -1399,7 +1399,8 @@ generate_join_implied_equalities(PlannerInfo *root,
 		nominal_join_relids = bms_union(outer_relids, nominal_inner_relids);
 		nominal_join_relids = add_outer_joins_to_relids(root,
 														nominal_join_relids,
-														sjinfo);
+														sjinfo,
+														NULL);
 	}
 	else
 	{
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index e8beaca196..ce55a5337f 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -694,6 +694,7 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
 	SpecialJoinInfo sjinfo_data;
 	RelOptInfo *joinrel;
 	List	   *restrictlist;
+	List	   *pushed_down_joins = NULL;
 
 	/* We should never try to join two overlapping sets of rels. */
 	Assert(!bms_overlap(rel1->relids, rel2->relids));
@@ -711,7 +712,8 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
 	}
 
 	/* Add outer join relid(s) to form the canonical relids. */
-	joinrelids = add_outer_joins_to_relids(root, joinrelids, sjinfo);
+	joinrelids = add_outer_joins_to_relids(root, joinrelids, sjinfo,
+										   &pushed_down_joins);
 
 	/* Swap rels if needed to match the join info. */
 	if (reversed)
@@ -754,7 +756,7 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
 	 * goes with this particular joining.
 	 */
 	joinrel = build_join_rel(root, joinrelids, rel1, rel2, sjinfo,
-							 &restrictlist);
+							 pushed_down_joins, &restrictlist);
 
 	/*
 	 * If we've already proven this join is empty, we needn't consider any
@@ -785,10 +787,13 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
  * it first, if a separate value is desired.
  *
  * sjinfo represents the join being performed.
+ *
+ * pushed_down_joins if not null is used to collect all the pushed down
+ * outer joins.
  */
 Relids
 add_outer_joins_to_relids(PlannerInfo *root, Relids input_relids,
-						  SpecialJoinInfo *sjinfo)
+						  SpecialJoinInfo *sjinfo, List **pushed_down_joins)
 {
 	/* Nothing to do if this isn't an outer join with an assigned relid. */
 	if (sjinfo == NULL || sjinfo->ojrelid == 0)
@@ -860,6 +865,10 @@ add_outer_joins_to_relids(PlannerInfo *root, Relids input_relids,
 				 */
 				commute_above_rels = bms_add_members(commute_above_rels,
 													 othersj->commute_above_l);
+
+				/* collect the pushed down outer joins */
+				if (pushed_down_joins != NULL)
+					*pushed_down_joins = lappend(*pushed_down_joins, othersj);
 			}
 		}
 	}
@@ -1627,7 +1636,7 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 		/* Build correct join relids for child join */
 		child_joinrelids = bms_union(child_rel1->relids, child_rel2->relids);
 		child_joinrelids = add_outer_joins_to_relids(root, child_joinrelids,
-													 child_sjinfo);
+													 child_sjinfo, NULL);
 
 		/* Find the AppendRelInfo structures */
 		appinfos = find_appinfos_by_relids(root, child_joinrelids, &nappinfos);
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index e6d2d7a353..71036bb99d 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -42,7 +42,7 @@ typedef struct JoinHashEntry
 
 static void build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
 								RelOptInfo *input_rel,
-								Relids pushed_down_ojrelids,
+								List *pushed_down_joins,
 								SpecialJoinInfo *sjinfo,
 								bool can_null);
 static List *build_joinrel_restrictlist(PlannerInfo *root,
@@ -633,6 +633,7 @@ add_join_rel(PlannerInfo *root, RelOptInfo *joinrel)
  * 'outer_rel' and 'inner_rel' are relation nodes for the relations to be
  *		joined
  * 'sjinfo': join context info
+ * 'pushed_down_joins': if not NULL, all the pushed down outer joins
  * 'restrictlist_ptr': result variable.  If not NULL, *restrictlist_ptr
  *		receives the list of RestrictInfo nodes that apply to this
  *		particular pair of joinable relations.
@@ -646,11 +647,11 @@ build_join_rel(PlannerInfo *root,
 			   RelOptInfo *outer_rel,
 			   RelOptInfo *inner_rel,
 			   SpecialJoinInfo *sjinfo,
+			   List *pushed_down_joins,
 			   List **restrictlist_ptr)
 {
 	RelOptInfo *joinrel;
 	List	   *restrictlist;
-	Relids		pushed_down_ojrelids;
 
 	/* This function should be used only for join between parents. */
 	Assert(!IS_OTHER_REL(outer_rel) && !IS_OTHER_REL(inner_rel));
@@ -759,13 +760,9 @@ build_join_rel(PlannerInfo *root,
 	 * and inner rels we first try to build it from.  But the contents should
 	 * be the same regardless.
 	 */
-	pushed_down_ojrelids = bms_difference(joinrel->relids,
-										  bms_union(outer_rel->relids,
-													inner_rel->relids));
-	pushed_down_ojrelids = bms_del_member(pushed_down_ojrelids, sjinfo->ojrelid);
-	build_joinrel_tlist(root, joinrel, outer_rel, pushed_down_ojrelids, sjinfo,
+	build_joinrel_tlist(root, joinrel, outer_rel, pushed_down_joins, sjinfo,
 						(sjinfo->jointype == JOIN_FULL));
-	build_joinrel_tlist(root, joinrel, inner_rel, pushed_down_ojrelids, sjinfo,
+	build_joinrel_tlist(root, joinrel, inner_rel, pushed_down_joins, sjinfo,
 						(sjinfo->jointype != JOIN_INNER));
 	add_placeholders_to_joinrel(root, joinrel, outer_rel, inner_rel, sjinfo);
 
@@ -876,7 +873,8 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 
 	joinrel->reloptkind = RELOPT_OTHER_JOINREL;
 	joinrel->relids = bms_union(outer_rel->relids, inner_rel->relids);
-	joinrel->relids = add_outer_joins_to_relids(root, joinrel->relids, sjinfo);
+	joinrel->relids = add_outer_joins_to_relids(root, joinrel->relids, sjinfo,
+												NULL);
 	joinrel->rows = 0;
 	/* cheap startup cost is interesting iff not all tuples to be retrieved */
 	joinrel->consider_startup = (root->tuple_fraction > 0);
@@ -1064,8 +1062,8 @@ min_join_parameterization(PlannerInfo *root,
  * Here the pushed-down B/C join cannot mark C columns as nulled yet,
  * while the now-upper A/B join must not mark C columns as nulled by itself.
  *
- * Second, perform the same operation for each outer-join relid listed in
- * pushed_down_ojrelids (which, in this example, would be "C" when we are
+ * Second, perform the same operation for each outer-join listed in
+ * pushed_down_joins (which, in this example, would be B/C join when we are
  * at the now-upper A/B join).  This allows the now-upper join to complete
  * the marking of "C" Vars that now have fully valid values.
  *
@@ -1082,7 +1080,7 @@ min_join_parameterization(PlannerInfo *root,
 static void
 build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
 					RelOptInfo *input_rel,
-					Relids pushed_down_ojrelids,
+					List *pushed_down_joins,
 					SpecialJoinInfo *sjinfo,
 					bool can_null)
 {
@@ -1111,6 +1109,8 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
 				 */
 				if (can_null)
 				{
+					ListCell   *lc;
+
 					phv = copyObject(phv);
 					/* See comments above to understand this logic */
 					if (sjinfo->ojrelid != 0 &&
@@ -1120,19 +1120,13 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
 						  bms_is_subset(phv->phrels, sjinfo->syn_lefthand))))
 						phv->phnullingrels = bms_add_member(phv->phnullingrels,
 															sjinfo->ojrelid);
-					if (pushed_down_ojrelids)
+					foreach(lc, pushed_down_joins)
 					{
-						ListCell   *lc;
+						SpecialJoinInfo *othersj = (SpecialJoinInfo *) lfirst(lc);
 
-						foreach(lc, root->join_info_list)
-						{
-							SpecialJoinInfo *othersj = (SpecialJoinInfo *) lfirst(lc);
-
-							if (bms_is_member(othersj->ojrelid, pushed_down_ojrelids) &&
-								bms_is_subset(phv->phrels, othersj->syn_righthand))
-								phv->phnullingrels = bms_add_member(phv->phnullingrels,
-																	othersj->ojrelid);
-						}
+						if (bms_is_subset(phv->phrels, othersj->syn_righthand))
+							phv->phnullingrels = bms_add_member(phv->phnullingrels,
+																othersj->ojrelid);
 					}
 					phv->phnullingrels =
 						bms_join(phv->phnullingrels,
@@ -1191,6 +1185,8 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
 		 */
 		if (can_null && var->varno != ROWID_VAR)
 		{
+			ListCell   *lc;
+
 			var = copyObject(var);
 			/* See comments above to understand this logic */
 			if (sjinfo->ojrelid != 0 &&
@@ -1200,19 +1196,13 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
 				  bms_is_member(var->varno, sjinfo->syn_lefthand))))
 				var->varnullingrels = bms_add_member(var->varnullingrels,
 													 sjinfo->ojrelid);
-			if (pushed_down_ojrelids)
+			foreach(lc, pushed_down_joins)
 			{
-				ListCell   *lc;
+				SpecialJoinInfo *othersj = (SpecialJoinInfo *) lfirst(lc);
 
-				foreach(lc, root->join_info_list)
-				{
-					SpecialJoinInfo *othersj = (SpecialJoinInfo *) lfirst(lc);
-
-					if (bms_is_member(othersj->ojrelid, pushed_down_ojrelids) &&
-						bms_is_member(var->varno, othersj->syn_righthand))
-						var->varnullingrels = bms_add_member(var->varnullingrels,
-															 othersj->ojrelid);
-				}
+				if (bms_is_member(var->varno, othersj->syn_righthand))
+					var->varnullingrels = bms_add_member(var->varnullingrels,
+														 othersj->ojrelid);
 			}
 			var->varnullingrels =
 				bms_join(var->varnullingrels,
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 69be701b16..001e75b5b7 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -311,6 +311,7 @@ extern RelOptInfo *build_join_rel(PlannerInfo *root,
 								  RelOptInfo *outer_rel,
 								  RelOptInfo *inner_rel,
 								  SpecialJoinInfo *sjinfo,
+								  List *pushed_down_joins,
 								  List **restrictlist_ptr);
 extern Relids min_join_parameterization(PlannerInfo *root,
 										Relids joinrelids,
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index d9e1623274..50bc3b503a 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -105,7 +105,8 @@ extern void join_search_one_level(PlannerInfo *root, int level);
 extern RelOptInfo *make_join_rel(PlannerInfo *root,
 								 RelOptInfo *rel1, RelOptInfo *rel2);
 extern Relids add_outer_joins_to_relids(PlannerInfo *root, Relids input_relids,
-										SpecialJoinInfo *sjinfo);
+										SpecialJoinInfo *sjinfo,
+										List **pushed_down_joins);
 extern bool have_join_order_restriction(PlannerInfo *root,
 										RelOptInfo *rel1, RelOptInfo *rel2);
 extern bool have_dangerous_phv(PlannerInfo *root,
-- 
2.31.0

