From e0cb996147a3c7bb2faaebc31706a4d17542870c Mon Sep 17 00:00:00 2001
From: Richard Guo <guofenglinux@gmail.com>
Date: Thu, 4 May 2023 18:28:27 +0800
Subject: [PATCH v3 4/4] Adjust outer join's target list

Since outer join's ojrelid might be postponed to be added to relids, we
need to do two kinds of adjustments to outer join's target list.

* check whether the outer join has been completely performed before we
add its relid to the nulling bitmap.

* for all the pushed down outer joins that have been completely
performed just by now, we need to add their relids to the nulling bitmap
if they can null the Var or PHV.

This patch does the adjustments.
---
 src/backend/optimizer/util/relnode.c | 61 ++++++++++++++++++++++++----
 1 file changed, 54 insertions(+), 7 deletions(-)

diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index fb35f23737..48bbf3090c 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -42,6 +42,7 @@ typedef struct JoinHashEntry
 
 static void build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
 								RelOptInfo *input_rel,
+								Relids pushed_down_ojrelids,
 								SpecialJoinInfo *sjinfo,
 								bool can_null);
 static List *build_joinrel_restrictlist(PlannerInfo *root,
@@ -656,6 +657,7 @@ build_join_rel(PlannerInfo *root,
 {
 	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));
@@ -764,9 +766,13 @@ build_join_rel(PlannerInfo *root,
 	 * and inner rels we first try to build it from.  But the contents should
 	 * be the same regardless.
 	 */
-	build_joinrel_tlist(root, joinrel, outer_rel, sjinfo,
+	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,
 						(sjinfo->jointype == JOIN_FULL));
-	build_joinrel_tlist(root, joinrel, inner_rel, sjinfo,
+	build_joinrel_tlist(root, joinrel, inner_rel, pushed_down_ojrelids, sjinfo,
 						(sjinfo->jointype != JOIN_INNER));
 	add_placeholders_to_joinrel(root, joinrel, outer_rel, inner_rel, sjinfo);
 
@@ -1053,15 +1059,17 @@ min_join_parameterization(PlannerInfo *root,
  * identity 3 (see optimizer/README).  We must take steps to ensure that
  * the output Vars have the same nulling bitmaps that they would if the
  * two joins had been done in syntactic order; else they won't match Vars
- * appearing higher in the query tree.  We need to do two things:
+ * appearing higher in the query tree.  We need to do three things:
  *
- * First, we add the outer join's relid to the nulling bitmap only if the Var
- * or PHV actually comes from within the syntactically nullable side(s) of the
- * outer join.  This takes care of the possibility that we have transformed
+ * First, we add the outer join's relid to the nulling bitmap only if the
+ * outer join has been completely performed and the Var or PHV actually
+ * comes from within the syntactically nullable side(s) of the outer join.
+ * This takes care of the possibility that we have transformed
  *		(A leftjoin B on (Pab)) leftjoin C on (Pbc)
  * to
  *		A leftjoin (B leftjoin C on (Pbc)) on (Pab)
- * Here the now-upper A/B join must not mark C columns as nulled by itself.
+ * Here the pushed-down B/C join cannot mark C columns as nulled by itself,
+ * and the now-upper A/B join must not mark C columns as nulled by itself.
  *
  * Second, any relid in sjinfo->commute_above_r that is already part of
  * the joinrel is added to the nulling bitmaps of nullable Vars and PHVs.
@@ -1072,10 +1080,21 @@ min_join_parameterization(PlannerInfo *root,
  * The C columns emitted by the B/C join need to be shown as nulled by both
  * the B/C and A/B joins, even though they've not physically traversed the
  * A/B join.
+ *
+ * Third, for each of the pushed down outer joins that have been completely
+ * performed just by now, we add its relid to the nulling bitmap if the Var
+ * or PHV actually comes from within its syntactically nullable side(s).
+ * This takes care of the possibility that we have transformed
+ *		(A leftjoin B on (Pab)) leftjoin C on (Pbc)
+ * to
+ *		A leftjoin (B leftjoin C on (Pbc)) on (Pab)
+ * Here the now-upper A/B join needs to mark C columns as nulled by the
+ * pushed-down B/C join.
  */
 static void
 build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
 					RelOptInfo *input_rel,
+					Relids pushed_down_ojrelids,
 					SpecialJoinInfo *sjinfo,
 					bool can_null)
 {
@@ -1104,9 +1123,12 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
 				 */
 				if (can_null)
 				{
+					int		i;
+
 					phv = copyObject(phv);
 					/* See comments above to understand this logic */
 					if (sjinfo->ojrelid != 0 &&
+						bms_is_member(sjinfo->ojrelid, relids) &&
 						(bms_is_subset(phv->phrels, sjinfo->syn_righthand) ||
 						 (sjinfo->jointype == JOIN_FULL &&
 						  bms_is_subset(phv->phrels, sjinfo->syn_lefthand))))
@@ -1116,6 +1138,17 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
 						bms_join(phv->phnullingrels,
 								 bms_intersect(sjinfo->commute_above_r,
 											   relids));
+					i = -1;
+					while ((i = bms_next_member(pushed_down_ojrelids, i)) >= 0)
+					{
+						SpecialJoinInfo *othersj = root->join_info_array[i];
+
+						Assert(othersj->ojrelid != 0 && othersj->jointype == JOIN_LEFT);
+
+						if (bms_is_subset(phv->phrels, othersj->syn_righthand))
+							phv->phnullingrels = bms_add_member(phv->phnullingrels,
+																othersj->ojrelid);
+					}
 				}
 
 				joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs,
@@ -1169,9 +1202,12 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
 		 */
 		if (can_null && var->varno != ROWID_VAR)
 		{
+			int		i;
+
 			var = copyObject(var);
 			/* See comments above to understand this logic */
 			if (sjinfo->ojrelid != 0 &&
+				bms_is_member(sjinfo->ojrelid, relids) &&
 				(bms_is_member(var->varno, sjinfo->syn_righthand) ||
 				 (sjinfo->jointype == JOIN_FULL &&
 				  bms_is_member(var->varno, sjinfo->syn_lefthand))))
@@ -1181,6 +1217,17 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
 				bms_join(var->varnullingrels,
 						 bms_intersect(sjinfo->commute_above_r,
 									   relids));
+			i = -1;
+			while ((i = bms_next_member(pushed_down_ojrelids, i)) >= 0)
+			{
+				SpecialJoinInfo *othersj = root->join_info_array[i];
+
+				Assert(othersj->ojrelid != 0 && othersj->jointype == JOIN_LEFT);
+
+				if (bms_is_member(var->varno, othersj->syn_righthand))
+					var->varnullingrels = bms_add_member(var->varnullingrels,
+														 othersj->ojrelid);
+			}
 		}
 
 		joinrel->reltarget->exprs = lappend(joinrel->reltarget->exprs,
-- 
2.31.0

