commit 5be335129bedbd5fe648f71ea801a0e1b0677fc5
Author: Tom Lane <tgl@sss.pgh.pa.us>
Date:   Fri Dec 23 10:55:27 2022 -0500

    Remove RestrictInfo.nullable_relids and associated infrastructure.
    
    There is no more code using this field, only code computing it,
    so just delete all that.  We can likewise get rid of
    EquivalenceMember.em_nullable_relids and
    PlannerInfo.nullable_baserels.

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 4028c5692e..12e940a2e6 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -6308,7 +6308,6 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
 									  false,
 									  root->qual_security_level,
 									  grouped_rel->relids,
-									  NULL,
 									  NULL);
 			if (is_foreign_expr(root, grouped_rel, expr))
 				fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index d81b09add5..05f24b01b1 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -2745,7 +2745,6 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 		if (var)
 			pathkeys = build_expression_pathkey(root,
 												(Expr *) var,
-												NULL,	/* below outer joins */
 												Int8LessOperator,
 												rel->relids,
 												false);
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index d1e1965479..a7c84daa74 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -34,7 +34,7 @@
 
 
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-										Expr *expr, Relids relids, Relids nullable_relids,
+										Expr *expr, Relids relids,
 										EquivalenceMember *parent,
 										Oid datatype);
 static bool is_exprlist_member(Expr *node, List *exprs);
@@ -131,9 +131,7 @@ process_equivalence(PlannerInfo *root,
 	Expr	   *item1;
 	Expr	   *item2;
 	Relids		item1_relids,
-				item2_relids,
-				item1_nullable_relids,
-				item2_nullable_relids;
+				item2_relids;
 	List	   *opfamilies;
 	EquivalenceClass *ec1,
 			   *ec2;
@@ -206,8 +204,7 @@ process_equivalence(PlannerInfo *root,
 								  restrictinfo->pseudoconstant,
 								  restrictinfo->security_level,
 								  NULL,
-								  restrictinfo->outer_relids,
-								  restrictinfo->nullable_relids);
+								  restrictinfo->outer_relids);
 		}
 		return false;
 	}
@@ -225,12 +222,6 @@ process_equivalence(PlannerInfo *root,
 			return false;		/* RHS is non-strict but not constant */
 	}
 
-	/* Calculate nullable-relid sets for each side of the clause */
-	item1_nullable_relids = bms_intersect(item1_relids,
-										  restrictinfo->nullable_relids);
-	item2_nullable_relids = bms_intersect(item2_relids,
-										  restrictinfo->nullable_relids);
-
 	/*
 	 * We use the declared input types of the operator, not exprType() of the
 	 * inputs, as the nominal datatypes for opfamily lookup.  This presumes
@@ -400,7 +391,7 @@ process_equivalence(PlannerInfo *root,
 	else if (ec1)
 	{
 		/* Case 3: add item2 to ec1 */
-		em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids,
+		em2 = add_eq_member(ec1, item2, item2_relids,
 							NULL, item2_type);
 		ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
 		ec1->ec_below_outer_join |= below_outer_join;
@@ -418,7 +409,7 @@ process_equivalence(PlannerInfo *root,
 	else if (ec2)
 	{
 		/* Case 3: add item1 to ec2 */
-		em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids,
+		em1 = add_eq_member(ec2, item1, item1_relids,
 							NULL, item1_type);
 		ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
 		ec2->ec_below_outer_join |= below_outer_join;
@@ -452,9 +443,9 @@ process_equivalence(PlannerInfo *root,
 		ec->ec_min_security = restrictinfo->security_level;
 		ec->ec_max_security = restrictinfo->security_level;
 		ec->ec_merged = NULL;
-		em1 = add_eq_member(ec, item1, item1_relids, item1_nullable_relids,
+		em1 = add_eq_member(ec, item1, item1_relids,
 							NULL, item1_type);
-		em2 = add_eq_member(ec, item2, item2_relids, item2_nullable_relids,
+		em2 = add_eq_member(ec, item2, item2_relids,
 							NULL, item2_type);
 
 		root->eq_classes = lappend(root->eq_classes, ec);
@@ -545,13 +536,12 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
  */
 static EquivalenceMember *
 add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-			  Relids nullable_relids, EquivalenceMember *parent, Oid datatype)
+			  EquivalenceMember *parent, Oid datatype)
 {
 	EquivalenceMember *em = makeNode(EquivalenceMember);
 
 	em->em_expr = expr;
 	em->em_relids = relids;
-	em->em_nullable_relids = nullable_relids;
 	em->em_is_const = false;
 	em->em_is_child = (parent != NULL);
 	em->em_datatype = datatype;
@@ -588,13 +578,6 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
  *	  equivalence class it is a member of; if none, optionally build a new
  *	  single-member EquivalenceClass for it.
  *
- * expr is the expression, and nullable_relids is the set of base relids
- * that are potentially nullable below it.  We actually only care about
- * the set of such relids that are used in the expression; but for caller
- * convenience, we perform that intersection step here.  The caller need
- * only be sure that nullable_relids doesn't omit any nullable rels that
- * might appear in the expr.
- *
  * sortref is the SortGroupRef of the originating SortGroupClause, if any,
  * or zero if not.  (It should never be zero if the expression is volatile!)
  *
@@ -623,7 +606,6 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 EquivalenceClass *
 get_eclass_for_sort_expr(PlannerInfo *root,
 						 Expr *expr,
-						 Relids nullable_relids,
 						 List *opfamilies,
 						 Oid opcintype,
 						 Oid collation,
@@ -719,13 +701,12 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 		elog(ERROR, "volatile EquivalenceClass has no sortref");
 
 	/*
-	 * Get the precise set of nullable relids appearing in the expression.
+	 * Get the precise set of relids appearing in the expression.
 	 */
 	expr_relids = pull_varnos(root, (Node *) expr);
-	nullable_relids = bms_intersect(nullable_relids, expr_relids);
 
 	newem = add_eq_member(newec, copyObject(expr), expr_relids,
-						  nullable_relids, NULL, opcintype);
+						  NULL, opcintype);
 
 	/*
 	 * add_eq_member doesn't check for volatile functions, set-returning
@@ -1211,8 +1192,6 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 		rinfo = process_implied_equality(root, eq_op, ec->ec_collation,
 										 cur_em->em_expr, const_em->em_expr,
 										 bms_copy(ec->ec_relids),
-										 bms_union(cur_em->em_nullable_relids,
-												   const_em->em_nullable_relids),
 										 ec->ec_min_security,
 										 ec->ec_below_outer_join,
 										 cur_em->em_is_const);
@@ -1285,8 +1264,6 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 			rinfo = process_implied_equality(root, eq_op, ec->ec_collation,
 											 prev_em->em_expr, cur_em->em_expr,
 											 bms_copy(ec->ec_relids),
-											 bms_union(prev_em->em_nullable_relids,
-													   cur_em->em_nullable_relids),
 											 ec->ec_min_security,
 											 ec->ec_below_outer_join,
 											 false);
@@ -1889,8 +1866,6 @@ create_join_clause(PlannerInfo *root,
 										rightem->em_expr,
 										bms_union(leftem->em_relids,
 												  rightem->em_relids),
-										bms_union(leftem->em_nullable_relids,
-												  rightem->em_nullable_relids),
 										ec->ec_min_security);
 
 	/* If it's a child clause, copy the parent's rinfo_serial */
@@ -2110,8 +2085,7 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 				left_type,
 				right_type,
 				inner_datatype;
-	Relids		inner_relids,
-				inner_nullable_relids;
+	Relids		inner_relids;
 	ListCell   *lc1;
 
 	Assert(is_opclause(rinfo->clause));
@@ -2138,8 +2112,6 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 		inner_datatype = left_type;
 		inner_relids = rinfo->left_relids;
 	}
-	inner_nullable_relids = bms_intersect(inner_relids,
-										  rinfo->nullable_relids);
 
 	/* Scan EquivalenceClasses for a match to outervar */
 	foreach(lc1, root->eq_classes)
@@ -2200,7 +2172,6 @@ reconsider_outer_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo,
 												   innervar,
 												   cur_em->em_expr,
 												   bms_copy(inner_relids),
-												   bms_copy(inner_nullable_relids),
 												   cur_ec->ec_min_security);
 			if (process_equivalence(root, &newrinfo, true))
 				match = true;
@@ -2238,9 +2209,7 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 				left_type,
 				right_type;
 	Relids		left_relids,
-				right_relids,
-				left_nullable_relids,
-				right_nullable_relids;
+				right_relids;
 	ListCell   *lc1;
 
 	/* Can't use an outerjoin_delayed clause here */
@@ -2256,10 +2225,6 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 	rightvar = (Expr *) get_rightop(rinfo->clause);
 	left_relids = rinfo->left_relids;
 	right_relids = rinfo->right_relids;
-	left_nullable_relids = bms_intersect(left_relids,
-										 rinfo->nullable_relids);
-	right_nullable_relids = bms_intersect(right_relids,
-										  rinfo->nullable_relids);
 
 	foreach(lc1, root->eq_classes)
 	{
@@ -2361,7 +2326,6 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 													   leftvar,
 													   cur_em->em_expr,
 													   bms_copy(left_relids),
-													   bms_copy(left_nullable_relids),
 													   cur_ec->ec_min_security);
 				if (process_equivalence(root, &newrinfo, true))
 					matchleft = true;
@@ -2377,7 +2341,6 @@ reconsider_full_join_clause(PlannerInfo *root, OuterJoinClauseInfo *ojcinfo)
 													   rightvar,
 													   cur_em->em_expr,
 													   bms_copy(right_relids),
-													   bms_copy(right_nullable_relids),
 													   cur_ec->ec_min_security);
 				if (process_equivalence(root, &newrinfo, true))
 					matchright = true;
@@ -2667,7 +2630,6 @@ add_child_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
-				Relids		new_nullable_relids;
 
 				if (parent_rel->reloptkind == RELOPT_BASEREL)
 				{
@@ -2697,21 +2659,7 @@ add_child_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				/*
-				 * And likewise for nullable_relids.  Note this code assumes
-				 * parent and child relids are singletons.
-				 */
-				new_nullable_relids = cur_em->em_nullable_relids;
-				if (bms_overlap(new_nullable_relids, top_parent_relids))
-				{
-					new_nullable_relids = bms_difference(new_nullable_relids,
-														 top_parent_relids);
-					new_nullable_relids = bms_add_members(new_nullable_relids,
-														  child_relids);
-				}
-
-				(void) add_eq_member(cur_ec, child_expr,
-									 new_relids, new_nullable_relids,
+				(void) add_eq_member(cur_ec, child_expr, new_relids,
 									 cur_em, cur_em->em_datatype);
 
 				/* Record this EC index for the child rel */
@@ -2808,7 +2756,6 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 				/* Yes, generate transformed child version */
 				Expr	   *child_expr;
 				Relids		new_relids;
-				Relids		new_nullable_relids;
 
 				if (parent_joinrel->reloptkind == RELOPT_JOINREL)
 				{
@@ -2839,20 +2786,7 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 											top_parent_relids);
 				new_relids = bms_add_members(new_relids, child_relids);
 
-				/*
-				 * For nullable_relids, we must selectively replace parent
-				 * nullable relids with child ones.
-				 */
-				new_nullable_relids = cur_em->em_nullable_relids;
-				if (bms_overlap(new_nullable_relids, top_parent_relids))
-					new_nullable_relids =
-						adjust_child_relids_multilevel(root,
-													   new_nullable_relids,
-													   child_joinrel,
-													   child_joinrel->top_parent);
-
-				(void) add_eq_member(cur_ec, child_expr,
-									 new_relids, new_nullable_relids,
+				(void) add_eq_member(cur_ec, child_expr, new_relids,
 									 cur_em, cur_em->em_datatype);
 			}
 		}
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index a9943cd6e0..bf919ca97f 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -180,9 +180,6 @@ pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys)
  *	  Given an expression and sort-order information, create a PathKey.
  *	  The result is always a "canonical" PathKey, but it might be redundant.
  *
- * expr is the expression, and nullable_relids is the set of base relids
- * that are potentially nullable below it.
- *
  * If the PathKey is being generated from a SortGroupClause, sortref should be
  * the SortGroupClause's SortGroupRef; otherwise zero.
  *
@@ -198,7 +195,6 @@ pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys)
 static PathKey *
 make_pathkey_from_sortinfo(PlannerInfo *root,
 						   Expr *expr,
-						   Relids nullable_relids,
 						   Oid opfamily,
 						   Oid opcintype,
 						   Oid collation,
@@ -234,7 +230,7 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
 			 equality_op);
 
 	/* Now find or (optionally) create a matching EquivalenceClass */
-	eclass = get_eclass_for_sort_expr(root, expr, nullable_relids,
+	eclass = get_eclass_for_sort_expr(root, expr,
 									  opfamilies, opcintype, collation,
 									  sortref, rel, create_it);
 
@@ -257,7 +253,6 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
 static PathKey *
 make_pathkey_from_sortop(PlannerInfo *root,
 						 Expr *expr,
-						 Relids nullable_relids,
 						 Oid ordering_op,
 						 bool nulls_first,
 						 Index sortref,
@@ -279,7 +274,6 @@ make_pathkey_from_sortop(PlannerInfo *root,
 
 	return make_pathkey_from_sortinfo(root,
 									  expr,
-									  nullable_relids,
 									  opfamily,
 									  opcintype,
 									  collation,
@@ -584,12 +578,10 @@ build_index_pathkeys(PlannerInfo *root,
 		}
 
 		/*
-		 * OK, try to make a canonical pathkey for this sort key.  Note we're
-		 * underneath any outer joins, so nullable_relids should be NULL.
+		 * OK, try to make a canonical pathkey for this sort key.
 		 */
 		cpathkey = make_pathkey_from_sortinfo(root,
 											  indexkey,
-											  NULL,
 											  index->sortopfamily[i],
 											  index->opcintype[i],
 											  index->indexcollations[i],
@@ -743,14 +735,12 @@ build_partition_pathkeys(PlannerInfo *root, RelOptInfo *partrel,
 		/*
 		 * Try to make a canonical pathkey for this partkey.
 		 *
-		 * We're considering a baserel scan, so nullable_relids should be
-		 * NULL.  Also, we assume the PartitionDesc lists any NULL partition
-		 * last, so we treat the scan like a NULLS LAST index: we have
-		 * nulls_first for backwards scan only.
+		 * We assume the PartitionDesc lists any NULL partition last, so we
+		 * treat the scan like a NULLS LAST index: we have nulls_first for
+		 * backwards scan only.
 		 */
 		cpathkey = make_pathkey_from_sortinfo(root,
 											  keyCol,
-											  NULL,
 											  partscheme->partopfamily[i],
 											  partscheme->partopcintype[i],
 											  partscheme->partcollation[i],
@@ -799,7 +789,7 @@ build_partition_pathkeys(PlannerInfo *root, RelOptInfo *partrel,
  *	  Build a pathkeys list that describes an ordering by a single expression
  *	  using the given sort operator.
  *
- * expr, nullable_relids, and rel are as for make_pathkey_from_sortinfo.
+ * expr and rel are as for make_pathkey_from_sortinfo.
  * We induce the other arguments assuming default sort order for the operator.
  *
  * Similarly to make_pathkey_from_sortinfo, the result is NIL if create_it
@@ -808,7 +798,6 @@ build_partition_pathkeys(PlannerInfo *root, RelOptInfo *partrel,
 List *
 build_expression_pathkey(PlannerInfo *root,
 						 Expr *expr,
-						 Relids nullable_relids,
 						 Oid opno,
 						 Relids rel,
 						 bool create_it)
@@ -827,7 +816,6 @@ build_expression_pathkey(PlannerInfo *root,
 
 	cpathkey = make_pathkey_from_sortinfo(root,
 										  expr,
-										  nullable_relids,
 										  opfamily,
 										  opcintype,
 										  exprCollation((Node *) expr),
@@ -908,14 +896,11 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 				 * expression is *not* volatile in the outer query: it's just
 				 * a Var referencing whatever the subquery emitted. (IOW, the
 				 * outer query isn't going to re-execute the volatile
-				 * expression itself.)	So this is okay.  Likewise, it's
-				 * correct to pass nullable_relids = NULL, because we're
-				 * underneath any outer joins appearing in the outer query.
+				 * expression itself.)	So this is okay.
 				 */
 				outer_ec =
 					get_eclass_for_sort_expr(root,
 											 (Expr *) outer_var,
-											 NULL,
 											 sub_eclass->ec_opfamilies,
 											 sub_member->em_datatype,
 											 sub_eclass->ec_collation,
@@ -997,7 +982,6 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 					/* See if we have a matching EC for the TLE */
 					outer_ec = get_eclass_for_sort_expr(root,
 														(Expr *) outer_var,
-														NULL,
 														sub_eclass->ec_opfamilies,
 														sub_expr_type,
 														sub_expr_coll,
@@ -1138,13 +1122,6 @@ build_join_pathkeys(PlannerInfo *root,
  * The resulting PathKeys are always in canonical form.  (Actually, there
  * is no longer any code anywhere that creates non-canonical PathKeys.)
  *
- * We assume that root->nullable_baserels is the set of base relids that could
- * have gone to NULL below the SortGroupClause expressions.  This is okay if
- * the expressions came from the query's top level (ORDER BY, DISTINCT, etc)
- * and if this function is only invoked after deconstruct_jointree.  In the
- * future we might have to make callers pass in the appropriate
- * nullable-relids set, but for now it seems unnecessary.
- *
  * 'sortclauses' is a list of SortGroupClause nodes
  * 'tlist' is the targetlist to find the referenced tlist entries in
  */
@@ -1166,7 +1143,6 @@ make_pathkeys_for_sortclauses(PlannerInfo *root,
 		Assert(OidIsValid(sortcl->sortop));
 		pathkey = make_pathkey_from_sortop(root,
 										   sortkey,
-										   root->nullable_baserels,
 										   sortcl->sortop,
 										   sortcl->nulls_first,
 										   sortcl->tleSortGroupRef,
@@ -1222,7 +1198,6 @@ initialize_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo)
 	restrictinfo->left_ec =
 		get_eclass_for_sort_expr(root,
 								 (Expr *) get_leftop(clause),
-								 restrictinfo->nullable_relids,
 								 restrictinfo->mergeopfamilies,
 								 lefttype,
 								 ((OpExpr *) clause)->inputcollid,
@@ -1232,7 +1207,6 @@ initialize_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo)
 	restrictinfo->right_ec =
 		get_eclass_for_sort_expr(root,
 								 (Expr *) get_rightop(clause),
-								 restrictinfo->nullable_relids,
 								 restrictinfo->mergeopfamilies,
 								 righttype,
 								 ((OpExpr *) clause)->inputcollid,
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 325dae3d45..0432df29fe 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -131,7 +131,7 @@ static void distribute_qual_to_rels(PlannerInfo *root, Node *clause,
 									List **postponed_qual_list,
 									List **postponed_oj_qual_list);
 static bool check_outerjoin_delay(PlannerInfo *root, Relids *relids_p,
-								  Relids *nullable_relids_p, bool is_pushed_down);
+								  bool is_pushed_down);
 static bool check_equivalence_delay(PlannerInfo *root,
 									RestrictInfo *restrictinfo);
 static bool check_redundant_nullability_qual(PlannerInfo *root, Node *clause);
@@ -772,7 +772,6 @@ deconstruct_jointree(PlannerInfo *root)
 	/* These are filled as we scan the jointree */
 	root->all_baserels = NULL;
 	root->outer_join_rels = NULL;
-	root->nullable_baserels = NULL;
 
 	/* Perform the initial scan of the jointree */
 	result = deconstruct_recurse(root, (Node *) root->parse->jointree,
@@ -928,7 +927,6 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode,
 	else if (IsA(jtnode, JoinExpr))
 	{
 		JoinExpr   *j = (JoinExpr *) jtnode;
-		Relids		nullable_rels;
 		JoinTreeItem *left_item,
 				   *right_item;
 		List	   *leftjoinlist,
@@ -954,8 +952,6 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode,
 				jtitem->right_rels = right_item->qualscope;
 				/* Inner join adds no restrictions for quals */
 				jtitem->nonnullable_rels = NULL;
-				/* and it doesn't force anything to null, either */
-				nullable_rels = NULL;
 				break;
 			case JOIN_LEFT:
 			case JOIN_ANTI:
@@ -986,7 +982,6 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode,
 				jtitem->left_rels = left_item->qualscope;
 				jtitem->right_rels = right_item->qualscope;
 				jtitem->nonnullable_rels = left_item->qualscope;
-				nullable_rels = right_item->qualscope;
 				break;
 			case JOIN_SEMI:
 				/* Recurse */
@@ -1009,13 +1004,6 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode,
 				jtitem->right_rels = right_item->qualscope;
 				/* Semi join adds no restrictions for quals */
 				jtitem->nonnullable_rels = NULL;
-
-				/*
-				 * Theoretically, a semijoin would null the RHS; but since the
-				 * RHS can't be accessed above the join, this is immaterial
-				 * and we needn't account for it.
-				 */
-				nullable_rels = NULL;
 				break;
 			case JOIN_FULL:
 				/* Recurse */
@@ -1045,21 +1033,15 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode,
 				jtitem->right_rels = right_item->qualscope;
 				/* each side is both outer and inner */
 				jtitem->nonnullable_rels = jtitem->qualscope;
-				nullable_rels = jtitem->qualscope;
 				break;
 			default:
 				/* JOIN_RIGHT was eliminated during reduce_outer_joins() */
 				elog(ERROR, "unrecognized join type: %d",
 					 (int) j->jointype);
 				leftjoinlist = rightjoinlist = NIL; /* keep compiler quiet */
-				nullable_rels = NULL;
 				break;
 		}
 
-		/* Report all rels that will be nulled anywhere in the jointree */
-		root->nullable_baserels = bms_add_members(root->nullable_baserels,
-												  nullable_rels);
-
 		/*
 		 * Compute the output joinlist.  We fold subproblems together except
 		 * at a FULL JOIN or where join_collapse_limit would be exceeded.
@@ -2210,7 +2192,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
 	bool		pseudoconstant = false;
 	bool		maybe_equivalence;
 	bool		maybe_outer_join;
-	Relids		nullable_relids;
 	RestrictInfo *restrictinfo;
 
 	/*
@@ -2364,7 +2345,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
 		/* Check to see if must be delayed by lower outer join */
 		outerjoin_delayed = check_outerjoin_delay(root,
 												  &relids,
-												  &nullable_relids,
 												  false);
 
 		/*
@@ -2392,7 +2372,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
 		/* Check to see if must be delayed by lower outer join */
 		outerjoin_delayed = check_outerjoin_delay(root,
 												  &relids,
-												  &nullable_relids,
 												  true);
 
 		if (outerjoin_delayed)
@@ -2452,8 +2431,7 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
 									 pseudoconstant,
 									 security_level,
 									 relids,
-									 outerjoin_nonnullable,
-									 nullable_relids);
+									 outerjoin_nonnullable);
 
 	/* Apply appropriate clone marking, too */
 	restrictinfo->has_clone = has_clone;
@@ -2611,9 +2589,7 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
  * If the qual must be delayed, add relids to *relids_p to reflect the lowest
  * safe level for evaluating the qual, and return true.  Any extra delay for
  * higher-level joins is reflected by setting delay_upper_joins to true in
- * SpecialJoinInfo structs.  We also compute nullable_relids, the set of
- * referenced relids that are nullable by lower outer joins (note that this
- * can be nonempty even for a non-delayed qual).
+ * SpecialJoinInfo structs.
  *
  * For an is_pushed_down qual, we can evaluate the qual as soon as (1) we have
  * all the rels it mentions, and (2) we are at or above any outer joins that
@@ -2636,8 +2612,8 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
  * mentioning only C cannot be applied below the join to A.
  *
  * For a non-pushed-down qual, this isn't going to determine where we place the
- * qual, but we need to determine outerjoin_delayed and nullable_relids anyway
- * for use later in the planning process.
+ * qual, but we need to determine outerjoin_delayed anyway for use later in
+ * the planning process.
  *
  * Lastly, a pushed-down qual that references the nullable side of any current
  * join_info_list member and has to be evaluated above that OJ (because its
@@ -2655,24 +2631,18 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
 static bool
 check_outerjoin_delay(PlannerInfo *root,
 					  Relids *relids_p, /* in/out parameter */
-					  Relids *nullable_relids_p,	/* output parameter */
 					  bool is_pushed_down)
 {
 	Relids		relids;
-	Relids		nullable_relids;
 	bool		outerjoin_delayed;
 	bool		found_some;
 
 	/* fast path if no special joins */
 	if (root->join_info_list == NIL)
-	{
-		*nullable_relids_p = NULL;
 		return false;
-	}
 
 	/* must copy relids because we need the original value at the end */
 	relids = bms_copy(*relids_p);
-	nullable_relids = NULL;
 	outerjoin_delayed = false;
 	do
 	{
@@ -2699,12 +2669,6 @@ check_outerjoin_delay(PlannerInfo *root,
 					/* we'll need another iteration */
 					found_some = true;
 				}
-				/* track all the nullable rels of relevant OJs */
-				nullable_relids = bms_add_members(nullable_relids,
-												  sjinfo->min_righthand);
-				if (sjinfo->jointype == JOIN_FULL)
-					nullable_relids = bms_add_members(nullable_relids,
-													  sjinfo->min_lefthand);
 				/* set delay_upper_joins if needed */
 				if (is_pushed_down && sjinfo->jointype != JOIN_FULL &&
 					bms_overlap(relids, sjinfo->min_lefthand))
@@ -2713,13 +2677,9 @@ check_outerjoin_delay(PlannerInfo *root,
 		}
 	} while (found_some);
 
-	/* identify just the actually-referenced nullable rels */
-	nullable_relids = bms_int_members(nullable_relids, *relids_p);
-
-	/* replace *relids_p, and return nullable_relids */
+	/* replace *relids_p */
 	bms_free(*relids_p);
 	*relids_p = relids;
-	*nullable_relids_p = nullable_relids;
 	return outerjoin_delayed;
 }
 
@@ -2741,7 +2701,6 @@ check_equivalence_delay(PlannerInfo *root,
 						RestrictInfo *restrictinfo)
 {
 	Relids		relids;
-	Relids		nullable_relids;
 
 	/* fast path if no special joins */
 	if (root->join_info_list == NIL)
@@ -2750,12 +2709,12 @@ check_equivalence_delay(PlannerInfo *root,
 	/* must copy restrictinfo's relids to avoid changing it */
 	relids = bms_copy(restrictinfo->left_relids);
 	/* check left side does not need delay */
-	if (check_outerjoin_delay(root, &relids, &nullable_relids, true))
+	if (check_outerjoin_delay(root, &relids, true))
 		return false;
 
 	/* and similarly for the right side */
 	relids = bms_copy(restrictinfo->right_relids);
-	if (check_outerjoin_delay(root, &relids, &nullable_relids, true))
+	if (check_outerjoin_delay(root, &relids, true))
 		return false;
 
 	return true;
@@ -2881,11 +2840,6 @@ distribute_restrictinfo_to_rels(PlannerInfo *root,
  * variable-free.  Otherwise the qual is applied at the lowest join level
  * that provides all its variables.
  *
- * "nullable_relids" is the set of relids used in the expressions that are
- * potentially nullable below the expressions.  (This has to be supplied by
- * caller because this function is used after deconstruct_jointree, so we
- * don't have knowledge of where the clause items came from.)
- *
  * "security_level" is the security level to assign to the new restrictinfo.
  *
  * "both_const" indicates whether both items are known pseudo-constant;
@@ -2911,7 +2865,6 @@ process_implied_equality(PlannerInfo *root,
 						 Expr *item1,
 						 Expr *item2,
 						 Relids qualscope,
-						 Relids nullable_relids,
 						 Index security_level,
 						 bool below_outer_join,
 						 bool both_const)
@@ -2995,8 +2948,7 @@ process_implied_equality(PlannerInfo *root,
 									 pseudoconstant,
 									 security_level,
 									 relids,
-									 NULL,	/* outer_relids */
-									 nullable_relids);
+									 NULL); /* outer_relids */
 
 	/*
 	 * If it's a join clause, add vars used in the clause to targetlists of
@@ -3061,7 +3013,6 @@ build_implied_join_equality(PlannerInfo *root,
 							Expr *item1,
 							Expr *item2,
 							Relids qualscope,
-							Relids nullable_relids,
 							Index security_level)
 {
 	RestrictInfo *restrictinfo;
@@ -3089,8 +3040,7 @@ build_implied_join_equality(PlannerInfo *root,
 									 false, /* pseudoconstant */
 									 security_level,	/* security_level */
 									 qualscope, /* required_relids */
-									 NULL,	/* outer_relids */
-									 nullable_relids);	/* nullable_relids */
+									 NULL); /* outer_relids */
 
 	/* Set mergejoinability/hashjoinability flags */
 	check_mergejoinable(restrictinfo);
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index e18d64b6dc..662d2c1f17 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -448,9 +448,6 @@ adjust_appendrel_attrs_mutator(Node *node,
 		newinfo->outer_relids = adjust_child_relids(oldinfo->outer_relids,
 													context->nappinfos,
 													context->appinfos);
-		newinfo->nullable_relids = adjust_child_relids(oldinfo->nullable_relids,
-													   context->nappinfos,
-													   context->appinfos);
 		newinfo->left_relids = adjust_child_relids(oldinfo->left_relids,
 												   context->nappinfos,
 												   context->appinfos);
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index f51ce45cd3..6814377599 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -869,7 +869,7 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel,
 												   rinfo->outerjoin_delayed,
 												   pseudoconstant,
 												   rinfo->security_level,
-												   NULL, NULL, NULL));
+												   NULL, NULL));
 			/* track minimum security level among child quals */
 			cq_min_security = Min(cq_min_security, rinfo->security_level);
 		}
@@ -904,7 +904,7 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel,
 									 make_restrictinfo(root, qual,
 													   true, false, false,
 													   security_level,
-													   NULL, NULL, NULL));
+													   NULL, NULL));
 				cq_min_security = Min(cq_min_security, security_level);
 			}
 			security_level++;
diff --git a/src/backend/optimizer/util/orclauses.c b/src/backend/optimizer/util/orclauses.c
index e96ef176ad..17ca0073c5 100644
--- a/src/backend/optimizer/util/orclauses.c
+++ b/src/backend/optimizer/util/orclauses.c
@@ -275,7 +275,6 @@ consider_new_or_clause(PlannerInfo *root, RelOptInfo *rel,
 								 false,
 								 join_or_rinfo->security_level,
 								 NULL,
-								 NULL,
 								 NULL);
 
 	/*
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index 15f410cf36..1d8912608b 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -29,8 +29,7 @@ static RestrictInfo *make_restrictinfo_internal(PlannerInfo *root,
 												bool pseudoconstant,
 												Index security_level,
 												Relids required_relids,
-												Relids outer_relids,
-												Relids nullable_relids);
+												Relids outer_relids);
 static Expr *make_sub_restrictinfos(PlannerInfo *root,
 									Expr *clause,
 									bool is_pushed_down,
@@ -38,8 +37,7 @@ static Expr *make_sub_restrictinfos(PlannerInfo *root,
 									bool pseudoconstant,
 									Index security_level,
 									Relids required_relids,
-									Relids outer_relids,
-									Relids nullable_relids);
+									Relids outer_relids);
 
 
 /*
@@ -49,7 +47,7 @@ static Expr *make_sub_restrictinfos(PlannerInfo *root,
  *
  * The is_pushed_down, outerjoin_delayed, and pseudoconstant flags for the
  * RestrictInfo must be supplied by the caller, as well as the correct values
- * for security_level, outer_relids, and nullable_relids.
+ * for security_level and outer_relids.
  * required_relids can be NULL, in which case it defaults to the actual clause
  * contents (i.e., clause_relids).
  *
@@ -69,8 +67,7 @@ make_restrictinfo(PlannerInfo *root,
 				  bool pseudoconstant,
 				  Index security_level,
 				  Relids required_relids,
-				  Relids outer_relids,
-				  Relids nullable_relids)
+				  Relids outer_relids)
 {
 	/*
 	 * If it's an OR clause, build a modified copy with RestrictInfos inserted
@@ -84,8 +81,7 @@ make_restrictinfo(PlannerInfo *root,
 													   pseudoconstant,
 													   security_level,
 													   required_relids,
-													   outer_relids,
-													   nullable_relids);
+													   outer_relids);
 
 	/* Shouldn't be an AND clause, else AND/OR flattening messed up */
 	Assert(!is_andclause(clause));
@@ -98,8 +94,7 @@ make_restrictinfo(PlannerInfo *root,
 									  pseudoconstant,
 									  security_level,
 									  required_relids,
-									  outer_relids,
-									  nullable_relids);
+									  outer_relids);
 }
 
 /*
@@ -116,8 +111,7 @@ make_restrictinfo_internal(PlannerInfo *root,
 						   bool pseudoconstant,
 						   Index security_level,
 						   Relids required_relids,
-						   Relids outer_relids,
-						   Relids nullable_relids)
+						   Relids outer_relids)
 {
 	RestrictInfo *restrictinfo = makeNode(RestrictInfo);
 	Relids		baserels;
@@ -132,7 +126,6 @@ make_restrictinfo_internal(PlannerInfo *root,
 	restrictinfo->can_join = false; /* may get set below */
 	restrictinfo->security_level = security_level;
 	restrictinfo->outer_relids = outer_relids;
-	restrictinfo->nullable_relids = nullable_relids;
 
 	/*
 	 * If it's potentially delayable by lower-level security quals, figure out
@@ -260,7 +253,7 @@ make_restrictinfo_internal(PlannerInfo *root,
  *
  * The same is_pushed_down, outerjoin_delayed, and pseudoconstant flag
  * values can be applied to all RestrictInfo nodes in the result.  Likewise
- * for security_level, outer_relids, and nullable_relids.
+ * for security_level 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
@@ -274,8 +267,7 @@ make_sub_restrictinfos(PlannerInfo *root,
 					   bool pseudoconstant,
 					   Index security_level,
 					   Relids required_relids,
-					   Relids outer_relids,
-					   Relids nullable_relids)
+					   Relids outer_relids)
 {
 	if (is_orclause(clause))
 	{
@@ -291,8 +283,7 @@ make_sub_restrictinfos(PlannerInfo *root,
 													pseudoconstant,
 													security_level,
 													NULL,
-													outer_relids,
-													nullable_relids));
+													outer_relids));
 		return (Expr *) make_restrictinfo_internal(root,
 												   clause,
 												   make_orclause(orlist),
@@ -301,8 +292,7 @@ make_sub_restrictinfos(PlannerInfo *root,
 												   pseudoconstant,
 												   security_level,
 												   required_relids,
-												   outer_relids,
-												   nullable_relids);
+												   outer_relids);
 	}
 	else if (is_andclause(clause))
 	{
@@ -318,8 +308,7 @@ make_sub_restrictinfos(PlannerInfo *root,
 													 pseudoconstant,
 													 security_level,
 													 required_relids,
-													 outer_relids,
-													 nullable_relids));
+													 outer_relids));
 		return make_andclause(andlist);
 	}
 	else
@@ -331,8 +320,7 @@ make_sub_restrictinfos(PlannerInfo *root,
 												   pseudoconstant,
 												   security_level,
 												   required_relids,
-												   outer_relids,
-												   nullable_relids);
+												   outer_relids);
 }
 
 /*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index b36cb41edf..40880a44f0 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -268,14 +268,6 @@ struct PlannerInfo
 	 */
 	Relids		all_query_rels;
 
-	/*
-	 * nullable_baserels is a Relids set of base relids that are nullable by
-	 * some outer join in the jointree; these are rels that are potentially
-	 * nullable below the WHERE clause, SELECT targetlist, etc.  This is
-	 * computed in deconstruct_jointree.
-	 */
-	Relids		nullable_baserels;
-
 	/*
 	 * join_rel_list is a list of all join-relation RelOptInfos we have
 	 * considered in this planning run.  For small problems we just scan the
@@ -1364,7 +1356,6 @@ typedef struct EquivalenceMember
 
 	Expr	   *em_expr;		/* the expression represented */
 	Relids		em_relids;		/* all relids appearing in em_expr */
-	Relids		em_nullable_relids; /* nullable by lower outer joins */
 	bool		em_is_const;	/* expression is pseudoconstant? */
 	bool		em_is_child;	/* derived version for a child relation? */
 	Oid			em_datatype;	/* the "nominal type" used by the opfamily */
@@ -2390,9 +2381,7 @@ typedef struct LimitPath
  * in parameterized scans, since pushing it into the join's outer side would
  * lead to wrong answers.)
  *
- * There is also a nullable_relids field, which is the set of rels the clause
- * references that can be forced null by some outer join below the clause.
- *
+ * XXX this comment needs work, if we don't remove it completely:
  * outerjoin_delayed = true is subtly different from nullable_relids != NULL:
  * a clause might reference some nullable rels and yet not be
  * outerjoin_delayed because it also references all the other rels of the
@@ -2506,9 +2495,6 @@ typedef struct RestrictInfo
 	/* If an outer-join clause, the outer-side relations, else NULL: */
 	Relids		outer_relids;
 
-	/* The relids used in the clause that are nullable by lower outer joins: */
-	Relids		nullable_relids;
-
 	/*
 	 * Relids in the left/right side of the clause.  These fields are set for
 	 * any binary opclause.
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 41f765d342..03866de136 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -128,7 +128,6 @@ extern Expr *canonicalize_ec_expression(Expr *expr,
 extern void reconsider_outer_join_clauses(PlannerInfo *root);
 extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 												  Expr *expr,
-												  Relids nullable_relids,
 												  List *opfamilies,
 												  Oid opcintype,
 												  Oid collation,
@@ -216,7 +215,7 @@ extern List *build_index_pathkeys(PlannerInfo *root, IndexOptInfo *index,
 extern List *build_partition_pathkeys(PlannerInfo *root, RelOptInfo *partrel,
 									  ScanDirection scandir, bool *partialkeys);
 extern List *build_expression_pathkey(PlannerInfo *root, Expr *expr,
-									  Relids nullable_relids, Oid opno,
+									  Oid opno,
 									  Relids rel, bool create_it);
 extern List *convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 									   List *subquery_pathkeys,
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 9dffdcfd1e..57b963c0f7 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -83,7 +83,6 @@ extern RestrictInfo *process_implied_equality(PlannerInfo *root,
 											  Expr *item1,
 											  Expr *item2,
 											  Relids qualscope,
-											  Relids nullable_relids,
 											  Index security_level,
 											  bool below_outer_join,
 											  bool both_const);
@@ -93,7 +92,6 @@ extern RestrictInfo *build_implied_join_equality(PlannerInfo *root,
 												 Expr *item1,
 												 Expr *item2,
 												 Relids qualscope,
-												 Relids nullable_relids,
 												 Index security_level);
 extern void match_foreign_keys_to_quals(PlannerInfo *root);
 
diff --git a/src/include/optimizer/restrictinfo.h b/src/include/optimizer/restrictinfo.h
index 17d3b4ab05..1f092371ea 100644
--- a/src/include/optimizer/restrictinfo.h
+++ b/src/include/optimizer/restrictinfo.h
@@ -19,7 +19,7 @@
 
 /* Convenience macro for the common case of a valid-everywhere qual */
 #define make_simple_restrictinfo(root, clause)  \
-	make_restrictinfo(root, clause, true, false, false, 0, NULL, NULL, NULL)
+	make_restrictinfo(root, clause, true, false, false, 0, NULL, NULL)
 
 extern RestrictInfo *make_restrictinfo(PlannerInfo *root,
 									   Expr *clause,
@@ -28,8 +28,7 @@ extern RestrictInfo *make_restrictinfo(PlannerInfo *root,
 									   bool pseudoconstant,
 									   Index security_level,
 									   Relids required_relids,
-									   Relids outer_relids,
-									   Relids nullable_relids);
+									   Relids outer_relids);
 extern RestrictInfo *commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op);
 extern bool restriction_is_or_clause(RestrictInfo *restrictinfo);
 extern bool restriction_is_securely_promotable(RestrictInfo *restrictinfo,
