From 7c4f5b4625a23e69f650cfd206ad6b33adced460 Mon Sep 17 00:00:00 2001
From: pgsql-guo <richard.guo@openpie.com>
Date: Thu, 23 Mar 2023 11:53:40 +0800
Subject: [PATCH v1] Retiring is_pushed_down

When forming an outer join's joinrel, we have the is_pushed_down flag in
RestrictInfo nodes to distinguish those quals that are in that join's
JOIN/ON condition from those that were pushed down to the joinrel and
thus act as filter quals.  Since now we have the outer-join-aware-Var
infrastructure, we can check to see whether a qual clause's
required_relids reference the outer join(s) being formed, in order to
tell if it's a join or filter clause.  This seems like a more principled
way.

This patch is an attempt to retire the is_pushed_down flag.
---
 contrib/postgres_fdw/postgres_fdw.c       |  3 +-
 src/backend/optimizer/path/costsize.c     | 13 +++--
 src/backend/optimizer/path/equivclass.c   |  4 --
 src/backend/optimizer/path/joinpath.c     | 14 +++--
 src/backend/optimizer/path/joinrels.c     | 25 +++++----
 src/backend/optimizer/plan/analyzejoins.c | 13 +++--
 src/backend/optimizer/plan/createplan.c   | 18 +++++++
 src/backend/optimizer/plan/initsplan.c    | 62 ++++-------------------
 src/backend/optimizer/plan/subselect.c    | 28 +++++++++-
 src/backend/optimizer/util/inherit.c      |  2 -
 src/backend/optimizer/util/orclauses.c    |  1 -
 src/backend/optimizer/util/relnode.c      | 23 +++++++--
 src/backend/optimizer/util/restrictinfo.c | 31 ++++--------
 src/include/nodes/pathnodes.h             | 53 +++++++++----------
 src/include/optimizer/cost.h              |  2 +-
 src/include/optimizer/restrictinfo.h      |  4 +-
 16 files changed, 155 insertions(+), 141 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c5cada55fb..60159e58d0 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -5798,7 +5798,7 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
 													   rinfo->clause);
 
 		if (IS_OUTER_JOIN(jointype) &&
-			!RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
+			!RINFO_IS_PUSHED_DOWN(rinfo, extra->ojrelids, joinrel->relids))
 		{
 			if (!is_remote_clause)
 				return false;
@@ -6519,7 +6519,6 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 			Assert(!IsA(expr, RestrictInfo));
 			rinfo = make_restrictinfo(root,
 									  expr,
-									  true,
 									  false,
 									  false,
 									  false,
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index ef475d95a1..be80a8b327 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -4738,7 +4738,7 @@ compute_semi_anti_join_factors(PlannerInfo *root,
 							   JoinType jointype,
 							   SpecialJoinInfo *sjinfo,
 							   List *restrictlist,
-							   SemiAntiJoinFactors *semifactors)
+							   JoinPathExtraData *extra)
 {
 	Selectivity jselec;
 	Selectivity nselec;
@@ -4761,7 +4761,7 @@ compute_semi_anti_join_factors(PlannerInfo *root,
 		{
 			RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
 
-			if (!RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
+			if (!RINFO_IS_PUSHED_DOWN(rinfo, extra->ojrelids, joinrel->relids))
 				joinquals = lappend(joinquals, rinfo);
 		}
 	}
@@ -4829,8 +4829,8 @@ compute_semi_anti_join_factors(PlannerInfo *root,
 	else
 		avgmatch = 1.0;
 
-	semifactors->outer_match_frac = jselec;
-	semifactors->match_count = avgmatch;
+	extra->semifactors.outer_match_frac = jselec;
+	extra->semifactors.match_count = avgmatch;
 }
 
 /*
@@ -5195,13 +5195,16 @@ calc_joinrel_size_estimate(PlannerInfo *root,
 		List	   *joinquals = NIL;
 		List	   *pushedquals = NIL;
 		ListCell   *l;
+		Relids		ojrelids = CALC_OUTER_JOIN_RELIDS(joinrel->relids,
+													  outer_rel->relids,
+													  inner_rel->relids);
 
 		/* Grovel through the clauses to separate into two lists */
 		foreach(l, restrictlist)
 		{
 			RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
 
-			if (RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
+			if (RINFO_IS_PUSHED_DOWN(rinfo, ojrelids, joinrel->relids))
 				pushedquals = lappend(pushedquals, rinfo);
 			else
 				joinquals = lappend(joinquals, rinfo);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7fa502d6e2..96a22f1389 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -196,7 +196,6 @@ process_equivalence(PlannerInfo *root,
 			*p_restrictinfo =
 				make_restrictinfo(root,
 								  (Expr *) ntest,
-								  restrictinfo->is_pushed_down,
 								  restrictinfo->has_clone,
 								  restrictinfo->is_clone,
 								  restrictinfo->pseudoconstant,
@@ -2005,7 +2004,6 @@ reconsider_outer_join_clauses(PlannerInfo *root)
 				/* throw back a dummy replacement clause (see notes above) */
 				rinfo = make_restrictinfo(root,
 										  (Expr *) makeBoolConst(true, false),
-										  rinfo->is_pushed_down,
 										  rinfo->has_clone,
 										  rinfo->is_clone,
 										  false,	/* pseudoconstant */
@@ -2033,7 +2031,6 @@ reconsider_outer_join_clauses(PlannerInfo *root)
 				/* throw back a dummy replacement clause (see notes above) */
 				rinfo = make_restrictinfo(root,
 										  (Expr *) makeBoolConst(true, false),
-										  rinfo->is_pushed_down,
 										  rinfo->has_clone,
 										  rinfo->is_clone,
 										  false,	/* pseudoconstant */
@@ -2061,7 +2058,6 @@ reconsider_outer_join_clauses(PlannerInfo *root)
 				/* throw back a dummy replacement clause (see notes above) */
 				rinfo = make_restrictinfo(root,
 										  (Expr *) makeBoolConst(true, false),
-										  rinfo->is_pushed_down,
 										  rinfo->has_clone,
 										  rinfo->is_clone,
 										  false,	/* pseudoconstant */
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index f047ad9ba4..01f2e4fbb2 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -83,6 +83,7 @@ static List *select_mergejoin_clauses(PlannerInfo *root,
 									  RelOptInfo *innerrel,
 									  List *restrictlist,
 									  JoinType jointype,
+									  JoinPathExtraData *extra,
 									  bool *mergejoin_allowed);
 static void generate_mergejoin_paths(PlannerInfo *root,
 									 RelOptInfo *joinrel,
@@ -149,6 +150,9 @@ add_paths_to_joinrel(PlannerInfo *root,
 	extra.mergeclause_list = NIL;
 	extra.sjinfo = sjinfo;
 	extra.param_source_rels = NULL;
+	extra.ojrelids = CALC_OUTER_JOIN_RELIDS(joinrel->relids,
+											outerrel->relids,
+											innerrel->relids);
 
 	/*
 	 * See if the inner relation is provably unique for this outer rel.
@@ -213,6 +217,7 @@ add_paths_to_joinrel(PlannerInfo *root,
 														  innerrel,
 														  restrictlist,
 														  jointype,
+														  &extra,
 														  &mergejoin_allowed);
 
 	/*
@@ -222,7 +227,7 @@ add_paths_to_joinrel(PlannerInfo *root,
 	if (jointype == JOIN_SEMI || jointype == JOIN_ANTI || extra.inner_unique)
 		compute_semi_anti_join_factors(root, joinrel, outerrel, innerrel,
 									   jointype, sjinfo, restrictlist,
-									   &extra.semifactors);
+									   &extra);
 
 	/*
 	 * Decide whether it's sensible to generate parameterized paths for this
@@ -2090,7 +2095,8 @@ hash_inner_and_outer(PlannerInfo *root,
 		 * If processing an outer join, only use its own join clauses for
 		 * hashing.  For inner joins we need not be so picky.
 		 */
-		if (isouterjoin && RINFO_IS_PUSHED_DOWN(restrictinfo, joinrel->relids))
+		if (isouterjoin &&
+			RINFO_IS_PUSHED_DOWN(restrictinfo, extra->ojrelids, joinrel->relids))
 			continue;
 
 		if (!restrictinfo->can_join ||
@@ -2322,6 +2328,7 @@ select_mergejoin_clauses(PlannerInfo *root,
 						 RelOptInfo *innerrel,
 						 List *restrictlist,
 						 JoinType jointype,
+						 JoinPathExtraData *extra,
 						 bool *mergejoin_allowed)
 {
 	List	   *result_list = NIL;
@@ -2339,7 +2346,8 @@ select_mergejoin_clauses(PlannerInfo *root,
 		 * we don't set have_nonmergeable_joinclause here because pushed-down
 		 * clauses will become otherquals not joinquals.)
 		 */
-		if (isouterjoin && RINFO_IS_PUSHED_DOWN(restrictinfo, joinrel->relids))
+		if (isouterjoin &&
+			RINFO_IS_PUSHED_DOWN(restrictinfo, extra->ojrelids, joinrel->relids))
 			continue;
 
 		/* Check that clause is a mergeable operator clause */
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 015a0b3cbe..b283d13bf0 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -34,6 +34,7 @@ static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
 static bool has_legal_joinclause(PlannerInfo *root, RelOptInfo *rel);
 static bool restriction_is_constant_false(List *restrictlist,
 										  RelOptInfo *joinrel,
+										  Relids ojrelids,
 										  bool only_pushed_down);
 static void populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
 										RelOptInfo *rel2, RelOptInfo *joinrel,
@@ -895,6 +896,10 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
 							RelOptInfo *rel2, RelOptInfo *joinrel,
 							SpecialJoinInfo *sjinfo, List *restrictlist)
 {
+	Relids		ojrelids = CALC_OUTER_JOIN_RELIDS(joinrel->relids,
+												  rel1->relids,
+												  rel2->relids);
+
 	/*
 	 * Consider paths using each rel as both outer and inner.  Depending on
 	 * the join type, a provably empty outer or inner rel might mean the join
@@ -917,7 +922,7 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
 	{
 		case JOIN_INNER:
 			if (is_dummy_rel(rel1) || is_dummy_rel(rel2) ||
-				restriction_is_constant_false(restrictlist, joinrel, false))
+				restriction_is_constant_false(restrictlist, joinrel, ojrelids, false))
 			{
 				mark_dummy_rel(joinrel);
 				break;
@@ -931,12 +936,12 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
 			break;
 		case JOIN_LEFT:
 			if (is_dummy_rel(rel1) ||
-				restriction_is_constant_false(restrictlist, joinrel, true))
+				restriction_is_constant_false(restrictlist, joinrel, ojrelids, true))
 			{
 				mark_dummy_rel(joinrel);
 				break;
 			}
-			if (restriction_is_constant_false(restrictlist, joinrel, false) &&
+			if (restriction_is_constant_false(restrictlist, joinrel, ojrelids, false) &&
 				bms_is_subset(rel2->relids, sjinfo->syn_righthand))
 				mark_dummy_rel(rel2);
 			add_paths_to_joinrel(root, joinrel, rel1, rel2,
@@ -948,7 +953,7 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
 			break;
 		case JOIN_FULL:
 			if ((is_dummy_rel(rel1) && is_dummy_rel(rel2)) ||
-				restriction_is_constant_false(restrictlist, joinrel, true))
+				restriction_is_constant_false(restrictlist, joinrel, ojrelids, true))
 			{
 				mark_dummy_rel(joinrel);
 				break;
@@ -984,7 +989,7 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
 				bms_is_subset(sjinfo->min_righthand, rel2->relids))
 			{
 				if (is_dummy_rel(rel1) || is_dummy_rel(rel2) ||
-					restriction_is_constant_false(restrictlist, joinrel, false))
+					restriction_is_constant_false(restrictlist, joinrel, ojrelids, false))
 				{
 					mark_dummy_rel(joinrel);
 					break;
@@ -1007,7 +1012,7 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
 								   sjinfo) != NULL)
 			{
 				if (is_dummy_rel(rel1) || is_dummy_rel(rel2) ||
-					restriction_is_constant_false(restrictlist, joinrel, false))
+					restriction_is_constant_false(restrictlist, joinrel, ojrelids, false))
 				{
 					mark_dummy_rel(joinrel);
 					break;
@@ -1022,12 +1027,12 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
 			break;
 		case JOIN_ANTI:
 			if (is_dummy_rel(rel1) ||
-				restriction_is_constant_false(restrictlist, joinrel, true))
+				restriction_is_constant_false(restrictlist, joinrel, ojrelids, true))
 			{
 				mark_dummy_rel(joinrel);
 				break;
 			}
-			if (restriction_is_constant_false(restrictlist, joinrel, false) &&
+			if (restriction_is_constant_false(restrictlist, joinrel, ojrelids, false) &&
 				bms_is_subset(rel2->relids, sjinfo->syn_righthand))
 				mark_dummy_rel(rel2);
 			add_paths_to_joinrel(root, joinrel, rel1, rel2,
@@ -1424,6 +1429,7 @@ mark_dummy_rel(RelOptInfo *rel)
 static bool
 restriction_is_constant_false(List *restrictlist,
 							  RelOptInfo *joinrel,
+							  Relids ojrelids,
 							  bool only_pushed_down)
 {
 	ListCell   *lc;
@@ -1438,7 +1444,8 @@ restriction_is_constant_false(List *restrictlist,
 	{
 		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
 
-		if (only_pushed_down && !RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
+		if (only_pushed_down &&
+			!RINFO_IS_PUSHED_DOWN(rinfo, ojrelids, joinrel->relids))
 			continue;
 
 		if (rinfo->clause && IsA(rinfo->clause, Const))
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 5f3cce873a..4c8c5d672b 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -284,7 +284,9 @@ join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo)
 		 * above the outer join, even if it references no other rels (it might
 		 * be from WHERE, for example).
 		 */
-		if (RINFO_IS_PUSHED_DOWN(restrictinfo, joinrelids))
+		if (RINFO_IS_PUSHED_DOWN(restrictinfo,
+								 bms_make_singleton(sjinfo->ojrelid),
+								 joinrelids))
 			continue;			/* ignore; not useful here */
 
 		/* Ignore if it's not a mergejoinable clause */
@@ -484,7 +486,9 @@ remove_rel_from_query(PlannerInfo *root, int relid, SpecialJoinInfo *sjinfo)
 
 		remove_join_clause_from_rels(root, rinfo, rinfo->required_relids);
 
-		if (RINFO_IS_PUSHED_DOWN(rinfo, join_plus_commute))
+		if (RINFO_IS_PUSHED_DOWN(rinfo,
+								 bms_make_singleton(sjinfo->ojrelid),
+								 join_plus_commute))
 		{
 			/*
 			 * There might be references to relid or ojrelid in the
@@ -1286,6 +1290,9 @@ is_innerrel_unique_for(PlannerInfo *root,
 {
 	List	   *clause_list = NIL;
 	ListCell   *lc;
+	Relids		ojrelids = CALC_OUTER_JOIN_RELIDS(joinrelids,
+												  outerrelids,
+												  innerrel->relids);
 
 	/*
 	 * Search for mergejoinable clauses that constrain the inner rel against
@@ -1303,7 +1310,7 @@ is_innerrel_unique_for(PlannerInfo *root,
 		 * join, we can't use it.
 		 */
 		if (IS_OUTER_JOIN(jointype) &&
-			RINFO_IS_PUSHED_DOWN(restrictinfo, joinrelids))
+			RINFO_IS_PUSHED_DOWN(restrictinfo, ojrelids, joinrelids))
 			continue;
 
 		/* Ignore if it's not a mergejoinable clause */
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index af48109058..13ab1d3eeb 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -4347,8 +4347,14 @@ create_nestloop_plan(PlannerInfo *root,
 	/* Any pseudoconstant clauses are ignored here */
 	if (IS_OUTER_JOIN(best_path->jpath.jointype))
 	{
+		Relids	ojrelids =
+			CALC_OUTER_JOIN_RELIDS(best_path->jpath.path.parent->relids,
+								   best_path->jpath.outerjoinpath->parent->relids,
+								   best_path->jpath.innerjoinpath->parent->relids);
+
 		extract_actual_join_clauses(joinrestrictclauses,
 									best_path->jpath.path.parent->relids,
+									ojrelids,
 									&joinclauses, &otherclauses);
 	}
 	else
@@ -4435,8 +4441,14 @@ create_mergejoin_plan(PlannerInfo *root,
 	/* Any pseudoconstant clauses are ignored here */
 	if (IS_OUTER_JOIN(best_path->jpath.jointype))
 	{
+		Relids	ojrelids =
+			CALC_OUTER_JOIN_RELIDS(best_path->jpath.path.parent->relids,
+								   best_path->jpath.outerjoinpath->parent->relids,
+								   best_path->jpath.innerjoinpath->parent->relids);
+
 		extract_actual_join_clauses(joinclauses,
 									best_path->jpath.path.parent->relids,
+									ojrelids,
 									&joinclauses, &otherclauses);
 	}
 	else
@@ -4737,8 +4749,14 @@ create_hashjoin_plan(PlannerInfo *root,
 	/* Any pseudoconstant clauses are ignored here */
 	if (IS_OUTER_JOIN(best_path->jpath.jointype))
 	{
+		Relids	ojrelids =
+			CALC_OUTER_JOIN_RELIDS(best_path->jpath.path.parent->relids,
+								   best_path->jpath.outerjoinpath->parent->relids,
+								   best_path->jpath.innerjoinpath->parent->relids);
+
 		extract_actual_join_clauses(joinclauses,
 									best_path->jpath.path.parent->relids,
+									ojrelids,
 									&joinclauses, &otherclauses);
 	}
 	else
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index b31d892121..c7e0369905 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -963,19 +963,16 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode,
 									child_domain->jd_relids);
 				jtitem->qualscope = bms_union(left_item->qualscope,
 											  right_item->qualscope);
-				/* caution: ANTI join derived from SEMI will lack rtindex */
-				if (j->rtindex != 0)
-				{
-					parent_domain->jd_relids =
-						bms_add_member(parent_domain->jd_relids,
-									   j->rtindex);
-					jtitem->qualscope = bms_add_member(jtitem->qualscope,
+				Assert(j->rtindex != 0);
+				parent_domain->jd_relids =
+					bms_add_member(parent_domain->jd_relids,
+								   j->rtindex);
+				jtitem->qualscope = bms_add_member(jtitem->qualscope,
+												   j->rtindex);
+				root->outer_join_rels = bms_add_member(root->outer_join_rels,
 													   j->rtindex);
-					root->outer_join_rels = bms_add_member(root->outer_join_rels,
-														   j->rtindex);
-					mark_rels_nulled_by_join(root, j->rtindex,
-											 right_item->qualscope);
-				}
+				mark_rels_nulled_by_join(root, j->rtindex,
+										 right_item->qualscope);
 				jtitem->inner_join_rels = bms_union(left_item->inner_join_rels,
 													right_item->inner_join_rels);
 				jtitem->left_rels = left_item->qualscope;
@@ -2209,7 +2206,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
 						List **postponed_oj_qual_list)
 {
 	Relids		relids;
-	bool		is_pushed_down;
 	bool		pseudoconstant = false;
 	bool		maybe_equivalence;
 	bool		maybe_outer_join;
@@ -2319,37 +2315,9 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
 		}
 	}
 
-	/*----------
+	/*
 	 * Check to see if clause application must be delayed by outer-join
 	 * considerations.
-	 *
-	 * A word about is_pushed_down: we mark the qual as "pushed down" if
-	 * it is (potentially) applicable at a level different from its original
-	 * syntactic level.  This flag is used to distinguish OUTER JOIN ON quals
-	 * from other quals pushed down to the same joinrel.  The rules are:
-	 *		WHERE quals and INNER JOIN quals: is_pushed_down = true.
-	 *		Non-degenerate OUTER JOIN quals: is_pushed_down = false.
-	 *		Degenerate OUTER JOIN quals: is_pushed_down = true.
-	 * A "degenerate" OUTER JOIN qual is one that doesn't mention the
-	 * non-nullable side, and hence can be pushed down into the nullable side
-	 * without changing the join result.  It is correct to treat it as a
-	 * regular filter condition at the level where it is evaluated.
-	 *
-	 * Note: it is not immediately obvious that a simple boolean is enough
-	 * for this: if for some reason we were to attach a degenerate qual to
-	 * its original join level, it would need to be treated as an outer join
-	 * qual there.  However, this cannot happen, because all the rels the
-	 * clause mentions must be in the outer join's min_righthand, therefore
-	 * the join it needs must be formed before the outer join; and we always
-	 * attach quals to the lowest level where they can be evaluated.  But
-	 * if we were ever to re-introduce a mechanism for delaying evaluation
-	 * of "expensive" quals, this area would need work.
-	 *
-	 * Note: generally, use of is_pushed_down has to go through the macro
-	 * RINFO_IS_PUSHED_DOWN, because that flag alone is not always sufficient
-	 * to tell whether a clause must be treated as pushed-down in context.
-	 * This seems like another reason why it should perhaps be rethought.
-	 *----------
 	 */
 	if (bms_overlap(relids, outerjoin_nonnullable))
 	{
@@ -2373,7 +2341,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
 		 * deductions, if it is mergejoinable.  So consider adding it to the
 		 * lists of set-aside outer-join clauses.
 		 */
-		is_pushed_down = false;
 		maybe_equivalence = false;
 		maybe_outer_join = true;
 
@@ -2390,12 +2357,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
 	}
 	else
 	{
-		/*
-		 * Normal qual clause or degenerate outer-join clause.  Either way, we
-		 * can mark it as pushed-down.
-		 */
-		is_pushed_down = true;
-
 		/*
 		 * It's possible that this is an IS NULL clause that's redundant with
 		 * a lower antijoin; if so we can just discard it.  We need not test
@@ -2420,7 +2381,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
 	 */
 	restrictinfo = make_restrictinfo(root,
 									 (Expr *) clause,
-									 is_pushed_down,
 									 has_clone,
 									 is_clone,
 									 pseudoconstant,
@@ -2792,7 +2752,6 @@ process_implied_equality(PlannerInfo *root,
 	 */
 	restrictinfo = make_restrictinfo(root,
 									 (Expr *) clause,
-									 true,	/* is_pushed_down */
 									 false, /* !has_clone */
 									 false, /* !is_clone */
 									 pseudoconstant,
@@ -2886,7 +2845,6 @@ build_implied_join_equality(PlannerInfo *root,
 	 */
 	restrictinfo = make_restrictinfo(root,
 									 clause,
-									 true,	/* is_pushed_down */
 									 false, /* !has_clone */
 									 false, /* !is_clone */
 									 false, /* pseudoconstant */
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 7a9fe88fec..f63afec871 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -1519,7 +1519,33 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
 	result->join_using_alias = NULL;
 	result->quals = whereClause;
 	result->alias = NULL;
-	result->rtindex = 0;		/* we don't need an RTE for it */
+	result->rtindex = 0;		/* we don't need an RTE for JOIN_SEMI */
+
+	/*
+	 * Add a RTE for JOIN_ANTI
+	 */
+	if (result->jointype == JOIN_ANTI)
+	{
+		ParseNamespaceItem *jnsitem;
+		ParseState *pstate;
+
+		/* Create a dummy ParseState for addRangeTableEntryForJoin */
+		pstate = make_parsestate(NULL);
+
+		jnsitem = addRangeTableEntryForJoin(pstate,
+											NULL,
+											NULL,
+											JOIN_ANTI,
+											0,
+											NULL,
+											NIL,
+											NIL,
+											NULL,
+											NULL,
+											false);
+		parse->rtable = lappend(parse->rtable, jnsitem->p_rte);
+		result->rtindex = list_length(parse->rtable);
+	}
 
 	return result;
 }
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 94de855a22..46ce5692cf 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -893,7 +893,6 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel,
 			childquals = lappend(childquals,
 								 make_restrictinfo(root,
 												   (Expr *) onecq,
-												   rinfo->is_pushed_down,
 												   rinfo->has_clone,
 												   rinfo->is_clone,
 												   pseudoconstant,
@@ -931,7 +930,6 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel,
 				/* not likely that we'd see constants here, so no check */
 				childquals = lappend(childquals,
 									 make_restrictinfo(root, qual,
-													   true,
 													   false, false,
 													   false,
 													   security_level,
diff --git a/src/backend/optimizer/util/orclauses.c b/src/backend/optimizer/util/orclauses.c
index 6ef9d14b90..bc0ce171cc 100644
--- a/src/backend/optimizer/util/orclauses.c
+++ b/src/backend/optimizer/util/orclauses.c
@@ -265,7 +265,6 @@ consider_new_or_clause(PlannerInfo *root, RelOptInfo *rel,
 	 */
 	or_rinfo = make_restrictinfo(root,
 								 orclause,
-								 true,
 								 false,
 								 false,
 								 false,
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 76dad17e33..9542954f0c 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -57,6 +57,7 @@ static List *subbuild_joinrel_restrictlist(PlannerInfo *root,
 										   RelOptInfo *joinrel,
 										   RelOptInfo *input_rel,
 										   Relids both_input_relids,
+										   SpecialJoinInfo *sjinfo,
 										   List *new_restrictlist);
 static List *subbuild_joinrel_joinlist(RelOptInfo *joinrel,
 									   List *joininfo_list,
@@ -1286,9 +1287,9 @@ build_joinrel_restrictlist(PlannerInfo *root,
 	 * same clauses arriving from both input relations).
 	 */
 	result = subbuild_joinrel_restrictlist(root, joinrel, outer_rel,
-										   both_input_relids, NIL);
+										   both_input_relids, sjinfo, NIL);
 	result = subbuild_joinrel_restrictlist(root, joinrel, inner_rel,
-										   both_input_relids, result);
+										   both_input_relids, sjinfo, result);
 
 	/*
 	 * Add on any clauses derived from EquivalenceClasses.  These cannot be
@@ -1328,6 +1329,7 @@ subbuild_joinrel_restrictlist(PlannerInfo *root,
 							  RelOptInfo *joinrel,
 							  RelOptInfo *input_rel,
 							  Relids both_input_relids,
+							  SpecialJoinInfo *sjinfo,
 							  List *new_restrictlist)
 {
 	ListCell   *l;
@@ -1349,11 +1351,15 @@ subbuild_joinrel_restrictlist(PlannerInfo *root,
 			 */
 			if (rinfo->has_clone || rinfo->is_clone)
 			{
-				Assert(!RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids));
 				if (!bms_is_subset(rinfo->required_relids, both_input_relids))
 					continue;
 				if (bms_overlap(rinfo->incompatible_relids, both_input_relids))
 					continue;
+
+				Assert(!RINFO_IS_PUSHED_DOWN(rinfo,
+											 bms_difference(joinrel->relids,
+															both_input_relids),
+											 joinrel->relids));
 			}
 			else
 			{
@@ -1364,7 +1370,11 @@ subbuild_joinrel_restrictlist(PlannerInfo *root,
 				 * (There is little point in checking incompatible_relids,
 				 * because it'll be NULL.)
 				 */
-				Assert(RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids) ||
+				Assert(sjinfo->ojrelid == 0 ||
+					   RINFO_IS_PUSHED_DOWN(rinfo,
+											bms_difference(joinrel->relids,
+														   both_input_relids),
+											joinrel->relids) ||
 					   bms_is_subset(rinfo->required_relids,
 									 both_input_relids));
 			}
@@ -2059,6 +2069,9 @@ have_partkey_equi_join(PlannerInfo *root, RelOptInfo *joinrel,
 	int			cnt_pks;
 	bool		pk_has_clause[PARTITION_MAX_KEYS];
 	bool		strict_op;
+	Relids		ojrelids = CALC_OUTER_JOIN_RELIDS(joinrel->relids,
+												  rel1->relids,
+												  rel2->relids);
 
 	/*
 	 * This function must only be called when the joined relations have same
@@ -2079,7 +2092,7 @@ have_partkey_equi_join(PlannerInfo *root, RelOptInfo *joinrel,
 
 		/* If processing an outer join, only use its own join clauses. */
 		if (IS_OUTER_JOIN(jointype) &&
-			RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
+			RINFO_IS_PUSHED_DOWN(rinfo, ojrelids, joinrel->relids))
 			continue;
 
 		/* Skip clauses which can not be used for a join. */
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index d6d26a2b51..75945c3ab9 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -24,7 +24,6 @@
 static RestrictInfo *make_restrictinfo_internal(PlannerInfo *root,
 												Expr *clause,
 												Expr *orclause,
-												bool is_pushed_down,
 												bool has_clone,
 												bool is_clone,
 												bool pseudoconstant,
@@ -34,7 +33,6 @@ static RestrictInfo *make_restrictinfo_internal(PlannerInfo *root,
 												Relids outer_relids);
 static Expr *make_sub_restrictinfos(PlannerInfo *root,
 									Expr *clause,
-									bool is_pushed_down,
 									bool has_clone,
 									bool is_clone,
 									bool pseudoconstant,
@@ -49,11 +47,11 @@ static Expr *make_sub_restrictinfos(PlannerInfo *root,
  *
  * Build a RestrictInfo node containing the given subexpression.
  *
- * The is_pushed_down, has_clone, is_clone, and pseudoconstant flags for the
- * RestrictInfo must be supplied by the caller, as well as the correct values
- * for security_level, incompatible_relids, and outer_relids.
- * required_relids can be NULL, in which case it defaults to the actual clause
- * contents (i.e., clause_relids).
+ * The has_clone, is_clone, and pseudoconstant flags for the RestrictInfo
+ * must be supplied by the caller, as well as the correct values for
+ * security_level, incompatible_relids, and outer_relids.  required_relids
+ * can be NULL, in which case it defaults to the actual clause contents
+ * (i.e., clause_relids).
  *
  * We initialize fields that depend only on the given subexpression, leaving
  * others that depend on context (or may never be needed at all) to be filled
@@ -62,7 +60,6 @@ static Expr *make_sub_restrictinfos(PlannerInfo *root,
 RestrictInfo *
 make_restrictinfo(PlannerInfo *root,
 				  Expr *clause,
-				  bool is_pushed_down,
 				  bool has_clone,
 				  bool is_clone,
 				  bool pseudoconstant,
@@ -78,7 +75,6 @@ make_restrictinfo(PlannerInfo *root,
 	if (is_orclause(clause))
 		return (RestrictInfo *) make_sub_restrictinfos(root,
 													   clause,
-													   is_pushed_down,
 													   has_clone,
 													   is_clone,
 													   pseudoconstant,
@@ -93,7 +89,6 @@ make_restrictinfo(PlannerInfo *root,
 	return make_restrictinfo_internal(root,
 									  clause,
 									  NULL,
-									  is_pushed_down,
 									  has_clone,
 									  is_clone,
 									  pseudoconstant,
@@ -112,7 +107,6 @@ static RestrictInfo *
 make_restrictinfo_internal(PlannerInfo *root,
 						   Expr *clause,
 						   Expr *orclause,
-						   bool is_pushed_down,
 						   bool has_clone,
 						   bool is_clone,
 						   bool pseudoconstant,
@@ -126,7 +120,6 @@ make_restrictinfo_internal(PlannerInfo *root,
 
 	restrictinfo->clause = clause;
 	restrictinfo->orclause = orclause;
-	restrictinfo->is_pushed_down = is_pushed_down;
 	restrictinfo->pseudoconstant = pseudoconstant;
 	restrictinfo->has_clone = has_clone;
 	restrictinfo->is_clone = is_clone;
@@ -259,9 +252,9 @@ make_restrictinfo_internal(PlannerInfo *root,
  * implicit-AND lists at top level of RestrictInfo lists.  Only ORs and
  * simple clauses are valid RestrictInfos.
  *
- * The same is_pushed_down, has_clone, is_clone, and pseudoconstant flag
- * values can be applied to all RestrictInfo nodes in the result.  Likewise
- * for security_level, incompatible_relids, and outer_relids.
+ * The same has_clone, is_clone, and pseudoconstant flag values can be
+ * applied to all RestrictInfo nodes in the result.  Likewise for
+ * security_level, incompatible_relids, and outer_relids.
  *
  * The given required_relids are attached to our top-level output,
  * but any OR-clause constituents are allowed to default to just the
@@ -270,7 +263,6 @@ make_restrictinfo_internal(PlannerInfo *root,
 static Expr *
 make_sub_restrictinfos(PlannerInfo *root,
 					   Expr *clause,
-					   bool is_pushed_down,
 					   bool has_clone,
 					   bool is_clone,
 					   bool pseudoconstant,
@@ -288,7 +280,6 @@ make_sub_restrictinfos(PlannerInfo *root,
 			orlist = lappend(orlist,
 							 make_sub_restrictinfos(root,
 													lfirst(temp),
-													is_pushed_down,
 													has_clone,
 													is_clone,
 													pseudoconstant,
@@ -299,7 +290,6 @@ make_sub_restrictinfos(PlannerInfo *root,
 		return (Expr *) make_restrictinfo_internal(root,
 												   clause,
 												   make_orclause(orlist),
-												   is_pushed_down,
 												   has_clone,
 												   is_clone,
 												   pseudoconstant,
@@ -317,7 +307,6 @@ make_sub_restrictinfos(PlannerInfo *root,
 			andlist = lappend(andlist,
 							  make_sub_restrictinfos(root,
 													 lfirst(temp),
-													 is_pushed_down,
 													 has_clone,
 													 is_clone,
 													 pseudoconstant,
@@ -331,7 +320,6 @@ make_sub_restrictinfos(PlannerInfo *root,
 		return (Expr *) make_restrictinfo_internal(root,
 												   clause,
 												   NULL,
-												   is_pushed_down,
 												   has_clone,
 												   is_clone,
 												   pseudoconstant,
@@ -521,6 +509,7 @@ extract_actual_clauses(List *restrictinfo_list,
 void
 extract_actual_join_clauses(List *restrictinfo_list,
 							Relids joinrelids,
+							Relids ojrelids,
 							List **joinquals,
 							List **otherquals)
 {
@@ -533,7 +522,7 @@ extract_actual_join_clauses(List *restrictinfo_list,
 	{
 		RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
 
-		if (RINFO_IS_PUSHED_DOWN(rinfo, joinrelids))
+		if (RINFO_IS_PUSHED_DOWN(rinfo, ojrelids, joinrelids))
 		{
 			if (!rinfo->pseudoconstant &&
 				!rinfo_is_constant_true(rinfo))
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index c17b53f7ad..d1dc27be6f 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -2408,27 +2408,16 @@ typedef struct LimitPath
  * level of the outer join, which is true except when it is a "degenerate"
  * condition that references only Vars from the nullable side of the join.
  *
- * RestrictInfo nodes contain a flag to indicate whether a qual has been
- * pushed down to a lower level than its original syntactic placement in the
- * join tree would suggest.  If an outer join prevents us from pushing a qual
- * down to its "natural" semantic level (the level associated with just the
- * base rels used in the qual) then we mark the qual with a "required_relids"
- * value including more than just the base rels it actually uses.  By
- * pretending that the qual references all the rels required to form the outer
- * join, we prevent it from being evaluated below the outer join's joinrel.
- * When we do form the outer join's joinrel, we still need to distinguish
- * those quals that are actually in that join's JOIN/ON condition from those
- * that appeared elsewhere in the tree and were pushed down to the join rel
- * because they used no other rels.  That's what the is_pushed_down flag is
- * for; it tells us that a qual is not an OUTER JOIN qual for the set of base
- * rels listed in required_relids.  A clause that originally came from WHERE
- * or an INNER JOIN condition will *always* have its is_pushed_down flag set.
- * It's possible for an OUTER JOIN clause to be marked is_pushed_down too,
- * if we decide that it can be pushed down into the nullable side of the join.
- * In that case it acts as a plain filter qual for wherever it gets evaluated.
- * (In short, is_pushed_down is only false for non-degenerate outer join
- * conditions.  Possibly we should rename it to reflect that meaning?  But
- * see also the comments for RINFO_IS_PUSHED_DOWN, below.)
+ * If an outer join prevents us from pushing a qual down to its "natural"
+ * semantic level (the level associated with just the base rels used in the
+ * qual) then we mark the qual with a "required_relids" value including more
+ * than just the base rels it actually uses.  By pretending that the qual
+ * references all the rels required to form the outer join, we prevent it from
+ * being evaluated below the outer join's joinrel.  When we do form the outer
+ * join's joinrel, we still need to distinguish those quals that are actually
+ * in that join's JOIN/ON condition from those that appeared elsewhere in the
+ * tree and were pushed down to the join rel because they used no other rels.
+ * See the comments for RINFO_IS_PUSHED_DOWN for how we do that.
  *
  * There is also an incompatible_relids field, which is a set of outer-join
  * relids above which we cannot evaluate the clause (because they might null
@@ -2515,9 +2504,6 @@ typedef struct RestrictInfo
 	/* the represented clause of WHERE or JOIN */
 	Expr	   *clause;
 
-	/* true if clause was pushed down in level */
-	bool		is_pushed_down;
-
 	/* see comment above */
 	bool		can_join pg_node_attr(equal_ignore);
 
@@ -2661,16 +2647,20 @@ typedef struct RestrictInfo
  * This macro embodies the correct way to test whether a RestrictInfo is
  * "pushed down" to a given outer join, that is, should be treated as a filter
  * clause rather than a join clause at that outer join.  This is certainly so
- * if is_pushed_down is true; but examining that is not sufficient anymore,
- * because outer-join clauses will get pushed down to lower outer joins when
- * we generate a path for the lower outer join that is parameterized by the
- * LHS of the upper one.  We can detect such a clause by noting that its
+ * if the outer join clause references that outer join in any varnullingrels or
+ * phnullingrels set; but examining that is not sufficient anymore, because
+ * outer-join clauses will get pushed down to lower outer joins when we
+ * generate a path for the lower outer join that is parameterized by the LHS of
+ * the upper one.  We can detect such a clause by noting that its
  * required_relids exceed the scope of the join.
  */
-#define RINFO_IS_PUSHED_DOWN(rinfo, joinrelids) \
-	((rinfo)->is_pushed_down || \
+#define RINFO_IS_PUSHED_DOWN(rinfo, ojrelids, joinrelids) \
+	(bms_overlap((rinfo)->required_relids, (ojrelids)) || \
 	 !bms_is_subset((rinfo)->required_relids, joinrelids))
 
+#define CALC_OUTER_JOIN_RELIDS(joinrelids, outerrelids, innerrelids) \
+	(bms_difference((joinrelids), bms_union((outerrelids), (innerrelids))))
+
 /*
  * Since mergejoinscansel() is a relatively expensive function, and would
  * otherwise be invoked many times while planning a large join tree,
@@ -3170,6 +3160,8 @@ typedef struct SemiAntiJoinFactors
  * sjinfo is extra info about special joins for selectivity estimation
  * semifactors is as shown above (only valid for SEMI/ANTI/inner_unique joins)
  * param_source_rels are OK targets for parameterization of result paths
+ * ojrelids is the relid set of any outer joins that will be calculated at this
+ *		join, or NULL for inner join
  */
 typedef struct JoinPathExtraData
 {
@@ -3179,6 +3171,7 @@ typedef struct JoinPathExtraData
 	SpecialJoinInfo *sjinfo;
 	SemiAntiJoinFactors semifactors;
 	Relids		param_source_rels;
+	Relids		ojrelids;
 } JoinPathExtraData;
 
 /*
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index 6cf49705d3..11bdd120c2 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -183,7 +183,7 @@ extern void compute_semi_anti_join_factors(PlannerInfo *root,
 										   JoinType jointype,
 										   SpecialJoinInfo *sjinfo,
 										   List *restrictlist,
-										   SemiAntiJoinFactors *semifactors);
+										   JoinPathExtraData *extra);
 extern void set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel);
 extern double get_parameterized_baserel_size(PlannerInfo *root,
 											 RelOptInfo *rel,
diff --git a/src/include/optimizer/restrictinfo.h b/src/include/optimizer/restrictinfo.h
index e140e619ac..04658f0067 100644
--- a/src/include/optimizer/restrictinfo.h
+++ b/src/include/optimizer/restrictinfo.h
@@ -19,12 +19,11 @@
 
 /* Convenience macro for the common case of a valid-everywhere qual */
 #define make_simple_restrictinfo(root, clause)  \
-	make_restrictinfo(root, clause, true, false, false, false, 0, \
+	make_restrictinfo(root, clause, false, false, false, 0, \
 		NULL, NULL, NULL)
 
 extern RestrictInfo *make_restrictinfo(PlannerInfo *root,
 									   Expr *clause,
-									   bool is_pushed_down,
 									   bool has_clone,
 									   bool is_clone,
 									   bool pseudoconstant,
@@ -41,6 +40,7 @@ extern List *extract_actual_clauses(List *restrictinfo_list,
 									bool pseudoconstant);
 extern void extract_actual_join_clauses(List *restrictinfo_list,
 										Relids joinrelids,
+										Relids ojrelids,
 										List **joinquals,
 										List **otherquals);
 extern bool join_clause_is_movable_to(RestrictInfo *rinfo, RelOptInfo *baserel);
-- 
2.32.0

