From 3f2682d8c7b8116e5853e121f843d4c3ad8eacbe Mon Sep 17 00:00:00 2001
From: Richard Guo <guofenglinux@gmail.com>
Date: Wed, 26 Jul 2023 16:16:49 +0800
Subject: [PATCH v4] 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     | 15 +++---
 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   |  3 ++
 src/backend/optimizer/plan/initsplan.c    | 63 ++++-------------------
 src/backend/optimizer/plan/subselect.c    | 28 +++++++++-
 src/backend/optimizer/util/inherit.c      |  2 -
 src/backend/optimizer/util/joininfo.c     |  1 -
 src/backend/optimizer/util/orclauses.c    |  1 -
 src/backend/optimizer/util/pathnode.c     |  3 ++
 src/backend/optimizer/util/relnode.c      | 23 +++++++--
 src/backend/optimizer/util/restrictinfo.c | 31 ++++-------
 src/include/nodes/pathnodes.h             | 57 ++++++++++----------
 src/include/optimizer/cost.h              |  2 +-
 src/include/optimizer/restrictinfo.h      |  4 +-
 18 files changed, 148 insertions(+), 144 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 4053cd641c..635b5c7c95 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -5847,7 +5847,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;
@@ -6609,7 +6609,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 ee23ed7835..625e500feb 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -4998,7 +4998,7 @@ get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel,
  *	sjinfo: SpecialJoinInfo relevant to this join
  *	restrictlist: join quals
  * Output parameters:
- *	*semifactors is filled in (see pathnodes.h for field definitions)
+ *	extra->semifactors is filled in (see pathnodes.h for field definitions)
  */
 void
 compute_semi_anti_join_factors(PlannerInfo *root,
@@ -5008,7 +5008,7 @@ compute_semi_anti_join_factors(PlannerInfo *root,
 							   JoinType jointype,
 							   SpecialJoinInfo *sjinfo,
 							   List *restrictlist,
-							   SemiAntiJoinFactors *semifactors)
+							   JoinPathExtraData *extra)
 {
 	Selectivity jselec;
 	Selectivity nselec;
@@ -5031,7 +5031,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);
 		}
 	}
@@ -5083,8 +5083,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;
 }
 
 /*
@@ -5434,13 +5434,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 1d6bedb399..bb51bd97aa 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,
@@ -2031,7 +2030,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 */
@@ -2059,7 +2057,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 */
@@ -2087,7 +2084,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 5be8da9e09..9809260d18 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -84,6 +84,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,
@@ -150,6 +151,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.
@@ -214,6 +218,7 @@ add_paths_to_joinrel(PlannerInfo *root,
 														  innerrel,
 														  restrictlist,
 														  jointype,
+														  &extra,
 														  &mergejoin_allowed);
 
 	/*
@@ -223,7 +228,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
@@ -2118,7 +2123,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 ||
@@ -2350,6 +2356,7 @@ select_mergejoin_clauses(PlannerInfo *root,
 						 RelOptInfo *innerrel,
 						 List *restrictlist,
 						 JoinType jointype,
+						 JoinPathExtraData *extra,
 						 bool *mergejoin_allowed)
 {
 	List	   *result_list = NIL;
@@ -2367,7 +2374,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 f3a9412d18..b27ad767c9 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,
@@ -894,6 +895,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
@@ -916,7 +921,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;
@@ -930,12 +935,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,
@@ -947,7 +952,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;
@@ -983,7 +988,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;
@@ -1006,7 +1011,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;
@@ -1021,12 +1026,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,
@@ -1423,6 +1428,7 @@ mark_dummy_rel(RelOptInfo *rel)
 static bool
 restriction_is_constant_false(List *restrictlist,
 							  RelOptInfo *joinrel,
+							  Relids ojrelids,
 							  bool only_pushed_down)
 {
 	ListCell   *lc;
@@ -1437,7 +1443,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 506fccd20c..91831ea3ae 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -299,7 +299,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 */
@@ -530,7 +532,9 @@ remove_leftjoinrel_from_query(PlannerInfo *root, int relid,
 
 		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
@@ -1381,6 +1385,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
@@ -1398,7 +1405,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 3b77886567..4de49cacdc 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -4396,6 +4396,7 @@ create_nestloop_plan(PlannerInfo *root,
 	{
 		extract_actual_join_clauses(joinrestrictclauses,
 									best_path->jpath.path.parent->relids,
+									best_path->jpath.ojrelids,
 									&joinclauses, &otherclauses);
 	}
 	else
@@ -4484,6 +4485,7 @@ create_mergejoin_plan(PlannerInfo *root,
 	{
 		extract_actual_join_clauses(joinclauses,
 									best_path->jpath.path.parent->relids,
+									best_path->jpath.ojrelids,
 									&joinclauses, &otherclauses);
 	}
 	else
@@ -4786,6 +4788,7 @@ create_hashjoin_plan(PlannerInfo *root,
 	{
 		extract_actual_join_clauses(joinclauses,
 									best_path->jpath.path.parent->relids,
+									best_path->jpath.ojrelids,
 									&joinclauses, &otherclauses);
 	}
 	else
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index e2c68fe6f9..d4353ff56a 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -962,19 +962,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;
@@ -2208,7 +2205,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;
@@ -2318,37 +2314,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))
 	{
@@ -2372,7 +2340,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;
 
@@ -2389,12 +2356,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
@@ -2419,7 +2380,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
 	 */
 	restrictinfo = make_restrictinfo(root,
 									 (Expr *) clause,
-									 is_pushed_down,
 									 has_clone,
 									 is_clone,
 									 pseudoconstant,
@@ -2665,7 +2625,6 @@ add_base_clause_to_rel(PlannerInfo *root, Index relid,
 
 			restrictinfo = make_restrictinfo(root,
 											 (Expr *) makeBoolConst(false, false),
-											 restrictinfo->is_pushed_down,
 											 restrictinfo->has_clone,
 											 restrictinfo->is_clone,
 											 restrictinfo->pseudoconstant,
@@ -2991,7 +2950,6 @@ process_implied_equality(PlannerInfo *root,
 	 */
 	restrictinfo = make_restrictinfo(root,
 									 (Expr *) clause,
-									 true,	/* is_pushed_down */
 									 false, /* !has_clone */
 									 false, /* !is_clone */
 									 pseudoconstant,
@@ -3085,7 +3043,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 e35ebea8b4..496d362bfd 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -1515,7 +1515,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 4797312ae5..3050bf5e37 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -891,7 +891,6 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel,
 			/* reconstitute RestrictInfo with appropriate properties */
 			childrinfo = make_restrictinfo(root,
 										   (Expr *) onecq,
-										   rinfo->is_pushed_down,
 										   rinfo->has_clone,
 										   rinfo->is_clone,
 										   pseudoconstant,
@@ -938,7 +937,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/joininfo.c b/src/backend/optimizer/util/joininfo.c
index 5fb0c17630..173aff761b 100644
--- a/src/backend/optimizer/util/joininfo.c
+++ b/src/backend/optimizer/util/joininfo.c
@@ -115,7 +115,6 @@ add_join_clause_to_rels(PlannerInfo *root,
 
 		restrictinfo = make_restrictinfo(root,
 										 (Expr *) makeBoolConst(false, false),
-										 restrictinfo->is_pushed_down,
 										 restrictinfo->has_clone,
 										 restrictinfo->is_clone,
 										 restrictinfo->pseudoconstant,
diff --git a/src/backend/optimizer/util/orclauses.c b/src/backend/optimizer/util/orclauses.c
index 5e2bf26ec4..35500e40b9 100644
--- a/src/backend/optimizer/util/orclauses.c
+++ b/src/backend/optimizer/util/orclauses.c
@@ -264,7 +264,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/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 3cf1dac087..0b392cba4f 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -2524,6 +2524,7 @@ create_nestloop_path(PlannerInfo *root,
 	pathnode->jpath.outerjoinpath = outer_path;
 	pathnode->jpath.innerjoinpath = inner_path;
 	pathnode->jpath.joinrestrictinfo = restrict_clauses;
+	pathnode->jpath.ojrelids = extra->ojrelids;
 
 	final_cost_nestloop(root, pathnode, workspace, extra);
 
@@ -2588,6 +2589,7 @@ create_mergejoin_path(PlannerInfo *root,
 	pathnode->jpath.outerjoinpath = outer_path;
 	pathnode->jpath.innerjoinpath = inner_path;
 	pathnode->jpath.joinrestrictinfo = restrict_clauses;
+	pathnode->jpath.ojrelids = extra->ojrelids;
 	pathnode->path_mergeclauses = mergeclauses;
 	pathnode->outersortkeys = outersortkeys;
 	pathnode->innersortkeys = innersortkeys;
@@ -2665,6 +2667,7 @@ create_hashjoin_path(PlannerInfo *root,
 	pathnode->jpath.outerjoinpath = outer_path;
 	pathnode->jpath.innerjoinpath = inner_path;
 	pathnode->jpath.joinrestrictinfo = restrict_clauses;
+	pathnode->jpath.ojrelids = extra->ojrelids;
 	pathnode->path_hashclauses = hashclauses;
 	/* final_cost_hashjoin will fill in pathnode->num_batches */
 
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index e05b21c884..f03c8c2fa2 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -58,6 +58,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,
@@ -1311,9 +1312,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
@@ -1353,6 +1354,7 @@ subbuild_joinrel_restrictlist(PlannerInfo *root,
 							  RelOptInfo *joinrel,
 							  RelOptInfo *input_rel,
 							  Relids both_input_relids,
+							  SpecialJoinInfo *sjinfo,
 							  List *new_restrictlist)
 {
 	ListCell   *l;
@@ -1374,11 +1376,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
 			{
@@ -1389,7 +1395,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));
 			}
@@ -2096,6 +2106,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
@@ -2116,7 +2129,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 0b406e9334..5d05287bf3 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 6c71098f2d..5778cfa345 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -2070,6 +2070,10 @@ typedef struct JoinPath
 	 * joinrestrictinfo is needed in JoinPath, and can't be merged into the
 	 * parent RelOptInfo.
 	 */
+
+	Relids		ojrelids;	/* the relid set of any outer joins that will be
+							 * calculated at this join for outer join, or NULL
+							 * for inner join */
 } JoinPath;
 
 /*
@@ -2444,27 +2448,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
@@ -2551,9 +2544,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);
 
@@ -2700,16 +2690,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,
@@ -3212,6 +3206,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
 {
@@ -3221,6 +3217,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 b1c51a4e70..6b83499c62 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 1b42c832c5..295ce764d3 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.31.0

