I wrote:
> Ah-hah --- the new check I added in join_is_legal understood about
> chains of LATERAL references, but it forgot that we could also have chains
> of outer-join ordering constraints.  When we're looking to see if joining
> on the basis of a LATERAL reference would break some later outer join, we
> have to look at outer joins to the outer joins' inner relations, too.

> Fixed in the attached.  I also stuck all of join_is_legal's
> lateral-related checks inside an "if (root->hasLateralRTEs)" block,
> which will save some time in typical queries with no LATERAL.  That
> makes that section of the patch a bit bigger than before, but it's
> mostly just reindentation.

As threatened, here's a patch on top of that that gets rid of
LateralJoinInfo.  I'm pretty happy with this, although I have an
itchy feeling that we could dispense with the lateral_vars lists too.

                        regards, tom lane

diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 26264cb..ba04b72 100644
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copySpecialJoinInfo(const SpecialJoinIn
*** 2067,2086 ****
  }
  
  /*
-  * _copyLateralJoinInfo
-  */
- static LateralJoinInfo *
- _copyLateralJoinInfo(const LateralJoinInfo *from)
- {
- 	LateralJoinInfo *newnode = makeNode(LateralJoinInfo);
- 
- 	COPY_BITMAPSET_FIELD(lateral_lhs);
- 	COPY_BITMAPSET_FIELD(lateral_rhs);
- 
- 	return newnode;
- }
- 
- /*
   * _copyAppendRelInfo
   */
  static AppendRelInfo *
--- 2067,2072 ----
*************** copyObject(const void *from)
*** 4519,4527 ****
  		case T_SpecialJoinInfo:
  			retval = _copySpecialJoinInfo(from);
  			break;
- 		case T_LateralJoinInfo:
- 			retval = _copyLateralJoinInfo(from);
- 			break;
  		case T_AppendRelInfo:
  			retval = _copyAppendRelInfo(from);
  			break;
--- 4505,4510 ----
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index aa6e102..356fcaf 100644
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalSpecialJoinInfo(const SpecialJoinI
*** 846,860 ****
  }
  
  static bool
- _equalLateralJoinInfo(const LateralJoinInfo *a, const LateralJoinInfo *b)
- {
- 	COMPARE_BITMAPSET_FIELD(lateral_lhs);
- 	COMPARE_BITMAPSET_FIELD(lateral_rhs);
- 
- 	return true;
- }
- 
- static bool
  _equalAppendRelInfo(const AppendRelInfo *a, const AppendRelInfo *b)
  {
  	COMPARE_SCALAR_FIELD(parent_relid);
--- 846,851 ----
*************** equal(const void *a, const void *b)
*** 2860,2868 ****
  		case T_SpecialJoinInfo:
  			retval = _equalSpecialJoinInfo(a, b);
  			break;
- 		case T_LateralJoinInfo:
- 			retval = _equalLateralJoinInfo(a, b);
- 			break;
  		case T_AppendRelInfo:
  			retval = _equalAppendRelInfo(a, b);
  			break;
--- 2851,2856 ----
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index f07c793..63fae82 100644
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
*************** _outPlannerInfo(StringInfo str, const Pl
*** 1847,1853 ****
  	WRITE_NODE_FIELD(right_join_clauses);
  	WRITE_NODE_FIELD(full_join_clauses);
  	WRITE_NODE_FIELD(join_info_list);
- 	WRITE_NODE_FIELD(lateral_info_list);
  	WRITE_NODE_FIELD(append_rel_list);
  	WRITE_NODE_FIELD(rowMarks);
  	WRITE_NODE_FIELD(placeholder_list);
--- 1847,1852 ----
*************** _outRelOptInfo(StringInfo str, const Rel
*** 1892,1897 ****
--- 1891,1897 ----
  	WRITE_NODE_FIELD(cheapest_total_path);
  	WRITE_NODE_FIELD(cheapest_unique_path);
  	WRITE_NODE_FIELD(cheapest_parameterized_paths);
+ 	WRITE_BITMAPSET_FIELD(direct_lateral_relids);
  	WRITE_BITMAPSET_FIELD(lateral_relids);
  	WRITE_UINT_FIELD(relid);
  	WRITE_OID_FIELD(reltablespace);
*************** _outSpecialJoinInfo(StringInfo str, cons
*** 2057,2071 ****
  }
  
  static void
- _outLateralJoinInfo(StringInfo str, const LateralJoinInfo *node)
- {
- 	WRITE_NODE_TYPE("LATERALJOININFO");
- 
- 	WRITE_BITMAPSET_FIELD(lateral_lhs);
- 	WRITE_BITMAPSET_FIELD(lateral_rhs);
- }
- 
- static void
  _outAppendRelInfo(StringInfo str, const AppendRelInfo *node)
  {
  	WRITE_NODE_TYPE("APPENDRELINFO");
--- 2057,2062 ----
*************** _outNode(StringInfo str, const void *obj
*** 3355,3363 ****
  			case T_SpecialJoinInfo:
  				_outSpecialJoinInfo(str, obj);
  				break;
- 			case T_LateralJoinInfo:
- 				_outLateralJoinInfo(str, obj);
- 				break;
  			case T_AppendRelInfo:
  				_outAppendRelInfo(str, obj);
  				break;
--- 3346,3351 ----
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index e57b8e2..2ab650c 100644
*** a/src/backend/optimizer/path/joinrels.c
--- b/src/backend/optimizer/path/joinrels.c
*************** join_search_one_level(PlannerInfo *root,
*** 231,237 ****
  		 */
  		if (joinrels[level] == NIL &&
  			root->join_info_list == NIL &&
! 			root->lateral_info_list == NIL)
  			elog(ERROR, "failed to build any %d-way joins", level);
  	}
  }
--- 231,237 ----
  		 */
  		if (joinrels[level] == NIL &&
  			root->join_info_list == NIL &&
! 			!root->hasLateralRTEs)
  			elog(ERROR, "failed to build any %d-way joins", level);
  	}
  }
*************** join_is_legal(PlannerInfo *root, RelOptI
*** 554,568 ****
  				 match_sjinfo->jointype == JOIN_FULL))
  				return false;	/* not implementable as nestloop */
  			/* check there is a direct reference from rel2 to rel1 */
! 			foreach(l, root->lateral_info_list)
! 			{
! 				LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(l);
! 
! 				if (bms_is_subset(ljinfo->lateral_rhs, rel2->relids) &&
! 					bms_is_subset(ljinfo->lateral_lhs, rel1->relids))
! 					break;
! 			}
! 			if (l == NULL)
  				return false;	/* only indirect refs, so reject */
  			/* check we won't have a dangerous PHV */
  			if (have_dangerous_phv(root, rel1->relids, rel2->lateral_relids))
--- 554,560 ----
  				 match_sjinfo->jointype == JOIN_FULL))
  				return false;	/* not implementable as nestloop */
  			/* check there is a direct reference from rel2 to rel1 */
! 			if (!bms_overlap(rel1->relids, rel2->direct_lateral_relids))
  				return false;	/* only indirect refs, so reject */
  			/* check we won't have a dangerous PHV */
  			if (have_dangerous_phv(root, rel1->relids, rel2->lateral_relids))
*************** join_is_legal(PlannerInfo *root, RelOptI
*** 577,591 ****
  				 match_sjinfo->jointype == JOIN_FULL))
  				return false;	/* not implementable as nestloop */
  			/* check there is a direct reference from rel1 to rel2 */
! 			foreach(l, root->lateral_info_list)
! 			{
! 				LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(l);
! 
! 				if (bms_is_subset(ljinfo->lateral_rhs, rel1->relids) &&
! 					bms_is_subset(ljinfo->lateral_lhs, rel2->relids))
! 					break;
! 			}
! 			if (l == NULL)
  				return false;	/* only indirect refs, so reject */
  			/* check we won't have a dangerous PHV */
  			if (have_dangerous_phv(root, rel2->relids, rel1->lateral_relids))
--- 569,575 ----
  				 match_sjinfo->jointype == JOIN_FULL))
  				return false;	/* not implementable as nestloop */
  			/* check there is a direct reference from rel1 to rel2 */
! 			if (!bms_overlap(rel2->relids, rel1->direct_lateral_relids))
  				return false;	/* only indirect refs, so reject */
  			/* check we won't have a dangerous PHV */
  			if (have_dangerous_phv(root, rel2->relids, rel1->lateral_relids))
*************** have_join_order_restriction(PlannerInfo 
*** 917,933 ****
  	 * If either side has a direct lateral reference to the other, attempt the
  	 * join regardless of outer-join considerations.
  	 */
! 	foreach(l, root->lateral_info_list)
! 	{
! 		LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(l);
! 
! 		if (bms_is_subset(ljinfo->lateral_rhs, rel2->relids) &&
! 			bms_overlap(ljinfo->lateral_lhs, rel1->relids))
! 			return true;
! 		if (bms_is_subset(ljinfo->lateral_rhs, rel1->relids) &&
! 			bms_overlap(ljinfo->lateral_lhs, rel2->relids))
! 			return true;
! 	}
  
  	/*
  	 * Likewise, if both rels are needed to compute some PlaceHolderVar,
--- 901,909 ----
  	 * If either side has a direct lateral reference to the other, attempt the
  	 * join regardless of outer-join considerations.
  	 */
! 	if (bms_overlap(rel1->relids, rel2->direct_lateral_relids) ||
! 		bms_overlap(rel2->relids, rel1->direct_lateral_relids))
! 		return true;
  
  	/*
  	 * Likewise, if both rels are needed to compute some PlaceHolderVar,
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 7912b15..d188d97 100644
*** a/src/backend/optimizer/plan/analyzejoins.c
--- b/src/backend/optimizer/plan/analyzejoins.c
*************** remove_rel_from_query(PlannerInfo *root,
*** 439,447 ****
  		sjinfo->syn_righthand = bms_del_member(sjinfo->syn_righthand, relid);
  	}
  
- 	/* There shouldn't be any LATERAL info to translate, as yet */
- 	Assert(root->lateral_info_list == NIL);
- 
  	/*
  	 * Likewise remove references from PlaceHolderVar data structures,
  	 * removing any no-longer-needed placeholders entirely.
--- 439,444 ----
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index e5688ef..a3f79b6 100644
*** a/src/backend/optimizer/plan/initsplan.c
--- b/src/backend/optimizer/plan/initsplan.c
*************** typedef struct PostponedQual
*** 47,53 ****
  
  static void extract_lateral_references(PlannerInfo *root, RelOptInfo *brel,
  						   Index rtindex);
- static void add_lateral_info(PlannerInfo *root, Relids lhs, Relids rhs);
  static List *deconstruct_recurse(PlannerInfo *root, Node *jtnode,
  					bool below_outer_join,
  					Relids *qualscope, Relids *inner_join_rels,
--- 47,52 ----
*************** extract_lateral_references(PlannerInfo *
*** 382,392 ****
  
  /*
   * create_lateral_join_info
!  *	  For each unflattened LATERAL subquery, create LateralJoinInfo(s) and add
!  *	  them to root->lateral_info_list, and fill in the per-rel lateral_relids
!  *	  and lateral_referencers sets.  Also generate LateralJoinInfo(s) to
!  *	  represent any lateral references within PlaceHolderVars (this part deals
!  *	  with the effects of flattened LATERAL subqueries).
   *
   * This has to run after deconstruct_jointree, because we need to know the
   * final ph_eval_at values for PlaceHolderVars.
--- 381,388 ----
  
  /*
   * create_lateral_join_info
!  *	  Fill in the per-base-relation direct_lateral_relids, lateral_relids
!  *	  and lateral_referencers sets.
   *
   * This has to run after deconstruct_jointree, because we need to know the
   * final ph_eval_at values for PlaceHolderVars.
*************** extract_lateral_references(PlannerInfo *
*** 394,399 ****
--- 390,396 ----
  void
  create_lateral_join_info(PlannerInfo *root)
  {
+ 	bool		found_laterals = false;
  	Index		rti;
  	ListCell   *lc;
  
*************** create_lateral_join_info(PlannerInfo *ro
*** 430,437 ****
  			{
  				Var		   *var = (Var *) node;
  
! 				add_lateral_info(root, bms_make_singleton(var->varno),
! 								 brel->relids);
  				lateral_relids = bms_add_member(lateral_relids,
  												var->varno);
  			}
--- 427,433 ----
  			{
  				Var		   *var = (Var *) node;
  
! 				found_laterals = true;
  				lateral_relids = bms_add_member(lateral_relids,
  												var->varno);
  			}
*************** create_lateral_join_info(PlannerInfo *ro
*** 441,447 ****
  				PlaceHolderInfo *phinfo = find_placeholder_info(root, phv,
  																false);
  
! 				add_lateral_info(root, phinfo->ph_eval_at, brel->relids);
  				lateral_relids = bms_add_members(lateral_relids,
  												 phinfo->ph_eval_at);
  			}
--- 437,443 ----
  				PlaceHolderInfo *phinfo = find_placeholder_info(root, phv,
  																false);
  
! 				found_laterals = true;
  				lateral_relids = bms_add_members(lateral_relids,
  												 phinfo->ph_eval_at);
  			}
*************** create_lateral_join_info(PlannerInfo *ro
*** 449,517 ****
  				Assert(false);
  		}
  
! 		/* We now have all the direct lateral refs from this rel */
! 		brel->lateral_relids = lateral_relids;
  	}
  
  	/*
! 	 * Now check for lateral references within PlaceHolderVars, and make
! 	 * LateralJoinInfos describing each such reference.  Unlike references in
! 	 * unflattened LATERAL RTEs, the referencing location could be a join.
  	 *
! 	 * For a PHV that is due to be evaluated at a join, we mark each of the
! 	 * join's member baserels as having the PHV's lateral references too. Even
! 	 * though the baserels could be scanned without considering those lateral
! 	 * refs, we will never be able to form the join except as a path
! 	 * parameterized by the lateral refs, so there is no point in considering
! 	 * unparameterized paths for the baserels; and we mustn't try to join any
! 	 * of those baserels to the lateral refs too soon, either.
  	 */
  	foreach(lc, root->placeholder_list)
  	{
  		PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
  		Relids		eval_at = phinfo->ph_eval_at;
  
! 		if (phinfo->ph_lateral != NULL)
! 		{
! 			List	   *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
! 											   PVC_RECURSE_AGGREGATES,
! 											   PVC_INCLUDE_PLACEHOLDERS);
! 			ListCell   *lc2;
! 			int			ev_at;
! 
! 			foreach(lc2, vars)
! 			{
! 				Node	   *node = (Node *) lfirst(lc2);
! 
! 				if (IsA(node, Var))
! 				{
! 					Var		   *var = (Var *) node;
! 
! 					if (!bms_is_member(var->varno, eval_at))
! 						add_lateral_info(root,
! 										 bms_make_singleton(var->varno),
! 										 eval_at);
! 				}
! 				else if (IsA(node, PlaceHolderVar))
! 				{
! 					PlaceHolderVar *other_phv = (PlaceHolderVar *) node;
! 					PlaceHolderInfo *other_phi;
  
! 					other_phi = find_placeholder_info(root, other_phv,
! 													  false);
! 					if (!bms_is_subset(other_phi->ph_eval_at, eval_at))
! 						add_lateral_info(root, other_phi->ph_eval_at, eval_at);
! 				}
! 				else
! 					Assert(false);
! 			}
  
! 			list_free(vars);
  
! 			ev_at = -1;
! 			while ((ev_at = bms_next_member(eval_at, ev_at)) >= 0)
  			{
! 				RelOptInfo *brel = find_base_rel(root, ev_at);
  
  				brel->lateral_relids = bms_add_members(brel->lateral_relids,
  													   phinfo->ph_lateral);
--- 445,498 ----
  				Assert(false);
  		}
  
! 		/* We now have all the simple lateral refs from this rel */
! 		brel->direct_lateral_relids = lateral_relids;
! 		brel->lateral_relids = bms_copy(lateral_relids);
  	}
  
  	/*
! 	 * Now check for lateral references within PlaceHolderVars, and mark their
! 	 * eval_at rels as having lateral references to the source rels.
  	 *
! 	 * For a PHV that is due to be evaluated at a baserel, mark its source(s)
! 	 * as direct lateral dependencies of the baserel (adding onto the ones
! 	 * recorded above).  If it's due to be evaluated at a join, mark its
! 	 * source(s) as indirect lateral dependencies of each baserel in the join,
! 	 * ie put them into lateral_relids but not direct_lateral_relids.  This is
! 	 * appropriate because we can't put any such baserel on the outside of a
! 	 * join to one of the PHV's lateral dependencies, but on the other hand we
! 	 * also can't yet join it directly to the dependency.
  	 */
  	foreach(lc, root->placeholder_list)
  	{
  		PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
  		Relids		eval_at = phinfo->ph_eval_at;
+ 		int			varno;
  
! 		if (phinfo->ph_lateral == NULL)
! 			continue;			/* PHV is uninteresting if no lateral refs */
  
! 		found_laterals = true;
  
! 		if (bms_get_singleton_member(eval_at, &varno))
! 		{
! 			/* Evaluation site is a baserel */
! 			RelOptInfo *brel = find_base_rel(root, varno);
  
! 			brel->direct_lateral_relids =
! 				bms_add_members(brel->direct_lateral_relids,
! 								phinfo->ph_lateral);
! 			brel->lateral_relids =
! 				bms_add_members(brel->lateral_relids,
! 								phinfo->ph_lateral);
! 		}
! 		else
! 		{
! 			/* Evaluation site is a join */
! 			varno = -1;
! 			while ((varno = bms_next_member(eval_at, varno)) >= 0)
  			{
! 				RelOptInfo *brel = find_base_rel(root, varno);
  
  				brel->lateral_relids = bms_add_members(brel->lateral_relids,
  													   phinfo->ph_lateral);
*************** create_lateral_join_info(PlannerInfo *ro
*** 519,535 ****
  		}
  	}
  
! 	/* If we found no lateral references, we're done. */
! 	if (root->lateral_info_list == NIL)
  		return;
  
  	/*
! 	 * At this point the lateral_relids sets represent only direct lateral
! 	 * references.  Replace them by their transitive closure, so that they
! 	 * describe both direct and indirect lateral references.  If relation X
! 	 * references Y laterally, and Y references Z laterally, then we will have
! 	 * to scan X on the inside of a nestloop with Z, so for all intents and
! 	 * purposes X is laterally dependent on Z too.
  	 *
  	 * This code is essentially Warshall's algorithm for transitive closure.
  	 * The outer loop considers each baserel, and propagates its lateral
--- 500,521 ----
  		}
  	}
  
! 	/*
! 	 * If we found no actual lateral references, we're done; but reset the
! 	 * hasLateralRTEs flag to avoid useless work later.
! 	 */
! 	if (!found_laterals)
! 	{
! 		root->hasLateralRTEs = false;
  		return;
+ 	}
  
  	/*
! 	 * Calculate the transitive closure of the lateral_relids sets, so that
! 	 * they describe both direct and indirect lateral references.  If relation
! 	 * X references Y laterally, and Y references Z laterally, then we will
! 	 * have to scan X on the inside of a nestloop with Z, so for all intents
! 	 * and purposes X is laterally dependent on Z too.
  	 *
  	 * This code is essentially Warshall's algorithm for transitive closure.
  	 * The outer loop considers each baserel, and propagates its lateral
*************** create_lateral_join_info(PlannerInfo *ro
*** 623,632 ****
  		if (brel == NULL || brel->reloptkind != RELOPT_BASEREL)
  			continue;
  
- 		/*
- 		 * If it's an appendrel parent, copy its lateral_relids and
- 		 * lateral_referencers to each child rel.
- 		 */
  		if (root->simple_rte_array[rti]->inh)
  		{
  			foreach(lc, root->append_rel_list)
--- 609,614 ----
*************** create_lateral_join_info(PlannerInfo *ro
*** 638,643 ****
--- 620,627 ----
  					continue;
  				childrel = root->simple_rel_array[appinfo->child_relid];
  				Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
+ 				Assert(childrel->direct_lateral_relids == NULL);
+ 				childrel->direct_lateral_relids = brel->direct_lateral_relids;
  				Assert(childrel->lateral_relids == NULL);
  				childrel->lateral_relids = brel->lateral_relids;
  				Assert(childrel->lateral_referencers == NULL);
*************** create_lateral_join_info(PlannerInfo *ro
*** 647,692 ****
  	}
  }
  
- /*
-  * add_lateral_info
-  *		Add a LateralJoinInfo to root->lateral_info_list, if needed
-  *
-  * We suppress redundant list entries.  The passed Relids are copied if saved.
-  */
- static void
- add_lateral_info(PlannerInfo *root, Relids lhs, Relids rhs)
- {
- 	LateralJoinInfo *ljinfo;
- 	ListCell   *lc;
- 
- 	/* Sanity-check the input */
- 	Assert(!bms_is_empty(lhs));
- 	Assert(!bms_is_empty(rhs));
- 	Assert(!bms_overlap(lhs, rhs));
- 
- 	/*
- 	 * The input is redundant if it has the same RHS and an LHS that is a
- 	 * subset of an existing entry's.  If an existing entry has the same RHS
- 	 * and an LHS that is a subset of the new one, it's redundant, but we
- 	 * don't trouble to get rid of it.  The only case that is really worth
- 	 * worrying about is identical entries, and we handle that well enough
- 	 * with this simple logic.
- 	 */
- 	foreach(lc, root->lateral_info_list)
- 	{
- 		ljinfo = (LateralJoinInfo *) lfirst(lc);
- 		if (bms_equal(rhs, ljinfo->lateral_rhs) &&
- 			bms_is_subset(lhs, ljinfo->lateral_lhs))
- 			return;
- 	}
- 
- 	/* Not there, so make a new entry */
- 	ljinfo = makeNode(LateralJoinInfo);
- 	ljinfo->lateral_lhs = bms_copy(lhs);
- 	ljinfo->lateral_rhs = bms_copy(rhs);
- 	root->lateral_info_list = lappend(root->lateral_info_list, ljinfo);
- }
- 
  
  /*****************************************************************************
   *
--- 631,636 ----
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index a761cfd..02d38f1 100644
*** a/src/backend/optimizer/plan/planagg.c
--- b/src/backend/optimizer/plan/planagg.c
*************** build_minmax_path(PlannerInfo *root, Min
*** 433,441 ****
  	subroot->plan_params = NIL;
  	subroot->outer_params = NULL;
  	subroot->init_plans = NIL;
! 	/* There shouldn't be any OJ or LATERAL info to translate, as yet */
  	Assert(subroot->join_info_list == NIL);
- 	Assert(subroot->lateral_info_list == NIL);
  	/* and we haven't created PlaceHolderInfos, either */
  	Assert(subroot->placeholder_list == NIL);
  
--- 433,440 ----
  	subroot->plan_params = NIL;
  	subroot->outer_params = NULL;
  	subroot->init_plans = NIL;
! 	/* There shouldn't be any OJ info to translate, as yet */
  	Assert(subroot->join_info_list == NIL);
  	/* and we haven't created PlaceHolderInfos, either */
  	Assert(subroot->placeholder_list == NIL);
  
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index d73e7c0..91873d3 100644
*** a/src/backend/optimizer/plan/planmain.c
--- b/src/backend/optimizer/plan/planmain.c
*************** query_planner(PlannerInfo *root, List *t
*** 114,120 ****
  	root->right_join_clauses = NIL;
  	root->full_join_clauses = NIL;
  	root->join_info_list = NIL;
- 	root->lateral_info_list = NIL;
  	root->placeholder_list = NIL;
  	root->initial_rels = NIL;
  
--- 114,119 ----
*************** query_planner(PlannerInfo *root, List *t
*** 201,207 ****
  	add_placeholders_to_base_rels(root);
  
  	/*
! 	 * Create the LateralJoinInfo list now that we have finalized
  	 * PlaceHolderVar eval levels and made any necessary additions to the
  	 * lateral_vars lists for lateral references within PlaceHolderVars.
  	 */
--- 200,206 ----
  	add_placeholders_to_base_rels(root);
  
  	/*
! 	 * Construct the lateral reference sets now that we have finalized
  	 * PlaceHolderVar eval levels and made any necessary additions to the
  	 * lateral_vars lists for lateral references within PlaceHolderVars.
  	 */
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index a9cccee..797df31 100644
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
*************** inheritance_planner(PlannerInfo *root)
*** 1132,1140 ****
  			}
  		}
  
! 		/* There shouldn't be any OJ or LATERAL info to translate, as yet */
  		Assert(subroot.join_info_list == NIL);
- 		Assert(subroot.lateral_info_list == NIL);
  		/* and we haven't created PlaceHolderInfos, either */
  		Assert(subroot.placeholder_list == NIL);
  		/* hack to mark target relation as an inheritance partition */
--- 1132,1139 ----
  			}
  		}
  
! 		/* There shouldn't be any OJ info to translate, as yet */
  		Assert(subroot.join_info_list == NIL);
  		/* and we haven't created PlaceHolderInfos, either */
  		Assert(subroot.placeholder_list == NIL);
  		/* hack to mark target relation as an inheritance partition */
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 401ba5b..5d3de14 100644
*** a/src/backend/optimizer/prep/prepjointree.c
--- b/src/backend/optimizer/prep/prepjointree.c
*************** pull_up_simple_subquery(PlannerInfo *roo
*** 1150,1163 ****
  										subroot->append_rel_list);
  
  	/*
! 	 * We don't have to do the equivalent bookkeeping for outer-join or
! 	 * LATERAL info, because that hasn't been set up yet.  placeholder_list
! 	 * likewise.
  	 */
  	Assert(root->join_info_list == NIL);
  	Assert(subroot->join_info_list == NIL);
- 	Assert(root->lateral_info_list == NIL);
- 	Assert(subroot->lateral_info_list == NIL);
  	Assert(root->placeholder_list == NIL);
  	Assert(subroot->placeholder_list == NIL);
  
--- 1150,1160 ----
  										subroot->append_rel_list);
  
  	/*
! 	 * We don't have to do the equivalent bookkeeping for outer-join info,
! 	 * because that hasn't been set up yet.  placeholder_list likewise.
  	 */
  	Assert(root->join_info_list == NIL);
  	Assert(subroot->join_info_list == NIL);
  	Assert(root->placeholder_list == NIL);
  	Assert(subroot->placeholder_list == NIL);
  
*************** pull_up_simple_values(PlannerInfo *root,
*** 1642,1648 ****
  	Assert(root->append_rel_list == NIL);
  	Assert(list_length(parse->rtable) == 1);
  	Assert(root->join_info_list == NIL);
- 	Assert(root->lateral_info_list == NIL);
  	Assert(root->placeholder_list == NIL);
  
  	/*
--- 1639,1644 ----
*************** substitute_multiple_relids_walker(Node *
*** 2839,2845 ****
  	}
  	/* Shouldn't need to handle planner auxiliary nodes here */
  	Assert(!IsA(node, SpecialJoinInfo));
- 	Assert(!IsA(node, LateralJoinInfo));
  	Assert(!IsA(node, AppendRelInfo));
  	Assert(!IsA(node, PlaceHolderInfo));
  	Assert(!IsA(node, MinMaxAggInfo));
--- 2835,2840 ----
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 8884fb1..2e55131 100644
*** a/src/backend/optimizer/prep/prepunion.c
--- b/src/backend/optimizer/prep/prepunion.c
*************** adjust_appendrel_attrs_mutator(Node *nod
*** 1786,1792 ****
  	}
  	/* Shouldn't need to handle planner auxiliary nodes here */
  	Assert(!IsA(node, SpecialJoinInfo));
- 	Assert(!IsA(node, LateralJoinInfo));
  	Assert(!IsA(node, AppendRelInfo));
  	Assert(!IsA(node, PlaceHolderInfo));
  	Assert(!IsA(node, MinMaxAggInfo));
--- 1786,1791 ----
diff --git a/src/backend/optimizer/util/placeholder.c b/src/backend/optimizer/util/placeholder.c
index 2870315..009ba69 100644
*** a/src/backend/optimizer/util/placeholder.c
--- b/src/backend/optimizer/util/placeholder.c
*************** add_placeholders_to_base_rels(PlannerInf
*** 436,442 ****
  
  /*
   * add_placeholders_to_joinrel
!  *		Add any required PlaceHolderVars to a join rel's targetlist.
   *
   * A join rel should emit a PlaceHolderVar if (a) the PHV is needed above
   * this join level and (b) the PHV can be computed at or below this level.
--- 436,444 ----
  
  /*
   * add_placeholders_to_joinrel
!  *		Add any required PlaceHolderVars to a join rel's targetlist;
!  *		and if they contain lateral references, add those references to the
!  *		joinrel's direct_lateral_relids.
   *
   * A join rel should emit a PlaceHolderVar if (a) the PHV is needed above
   * this join level and (b) the PHV can be computed at or below this level.
*************** add_placeholders_to_joinrel(PlannerInfo 
*** 463,468 ****
--- 465,474 ----
  				joinrel->reltargetlist = lappend(joinrel->reltargetlist,
  												 phinfo->ph_var);
  				joinrel->width += phinfo->ph_width;
+ 				/* Adjust joinrel's direct_lateral_relids as needed */
+ 				joinrel->direct_lateral_relids =
+ 					bms_add_members(joinrel->direct_lateral_relids,
+ 									phinfo->ph_lateral);
  			}
  		}
  	}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index a2be2ed..f2bdfcc 100644
*** a/src/backend/optimizer/util/relnode.c
--- b/src/backend/optimizer/util/relnode.c
*************** build_simple_rel(PlannerInfo *root, int 
*** 111,116 ****
--- 111,117 ----
  	rel->cheapest_total_path = NULL;
  	rel->cheapest_unique_path = NULL;
  	rel->cheapest_parameterized_paths = NIL;
+ 	rel->direct_lateral_relids = NULL;
  	rel->lateral_relids = NULL;
  	rel->relid = relid;
  	rel->rtekind = rte->rtekind;
*************** build_join_rel(PlannerInfo *root,
*** 373,378 ****
--- 374,383 ----
  	joinrel->cheapest_total_path = NULL;
  	joinrel->cheapest_unique_path = NULL;
  	joinrel->cheapest_parameterized_paths = NIL;
+ 	/* init direct_lateral_relids from children; we'll finish it up below */
+ 	joinrel->direct_lateral_relids =
+ 		bms_union(outer_rel->direct_lateral_relids,
+ 				  inner_rel->direct_lateral_relids);
  	joinrel->lateral_relids = min_join_parameterization(root, joinrel->relids,
  														outer_rel, inner_rel);
  	joinrel->relid = 0;			/* indicates not a baserel */
*************** build_join_rel(PlannerInfo *root,
*** 423,428 ****
--- 428,445 ----
  	add_placeholders_to_joinrel(root, joinrel);
  
  	/*
+ 	 * add_placeholders_to_joinrel also took care of adding the ph_lateral
+ 	 * sets of any PlaceHolderVars computed here to direct_lateral_relids, so
+ 	 * now we can finish computing that.  This is much like the computation of
+ 	 * the transitively-closed lateral_relids in min_join_parameterization,
+ 	 * except that here we *do* have to consider the added PHVs.
+ 	 */
+ 	joinrel->direct_lateral_relids =
+ 		bms_del_members(joinrel->direct_lateral_relids, joinrel->relids);
+ 	if (bms_is_empty(joinrel->direct_lateral_relids))
+ 		joinrel->direct_lateral_relids = NULL;
+ 
+ 	/*
  	 * Construct restrict and join clause lists for the new joinrel. (The
  	 * caller might or might not need the restrictlist, but I need it anyway
  	 * for set_joinrel_size_estimates().)
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index 773e7b2..32038ce 100644
*** a/src/backend/optimizer/util/var.c
--- b/src/backend/optimizer/util/var.c
*************** flatten_join_alias_vars_mutator(Node *no
*** 782,788 ****
  	Assert(!IsA(node, SubPlan));
  	/* Shouldn't need to handle these planner auxiliary nodes here */
  	Assert(!IsA(node, SpecialJoinInfo));
- 	Assert(!IsA(node, LateralJoinInfo));
  	Assert(!IsA(node, PlaceHolderInfo));
  	Assert(!IsA(node, MinMaxAggInfo));
  
--- 782,787 ----
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 1da90ff..6f22168 100644
*** a/src/backend/rewrite/rewriteManip.c
--- b/src/backend/rewrite/rewriteManip.c
*************** OffsetVarNodes_walker(Node *node, Offset
*** 402,408 ****
  	/* Shouldn't need to handle other planner auxiliary nodes here */
  	Assert(!IsA(node, PlanRowMark));
  	Assert(!IsA(node, SpecialJoinInfo));
- 	Assert(!IsA(node, LateralJoinInfo));
  	Assert(!IsA(node, PlaceHolderInfo));
  	Assert(!IsA(node, MinMaxAggInfo));
  
--- 402,407 ----
*************** ChangeVarNodes_walker(Node *node, Change
*** 586,592 ****
  	}
  	/* Shouldn't need to handle other planner auxiliary nodes here */
  	Assert(!IsA(node, SpecialJoinInfo));
- 	Assert(!IsA(node, LateralJoinInfo));
  	Assert(!IsA(node, PlaceHolderInfo));
  	Assert(!IsA(node, MinMaxAggInfo));
  
--- 585,590 ----
*************** rangeTableEntry_used_walker(Node *node,
*** 868,874 ****
  	Assert(!IsA(node, PlaceHolderVar));
  	Assert(!IsA(node, PlanRowMark));
  	Assert(!IsA(node, SpecialJoinInfo));
- 	Assert(!IsA(node, LateralJoinInfo));
  	Assert(!IsA(node, AppendRelInfo));
  	Assert(!IsA(node, PlaceHolderInfo));
  	Assert(!IsA(node, MinMaxAggInfo));
--- 866,871 ----
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 94bdb7c..603edd3 100644
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 247,253 ****
  	T_RestrictInfo,
  	T_PlaceHolderVar,
  	T_SpecialJoinInfo,
- 	T_LateralJoinInfo,
  	T_AppendRelInfo,
  	T_PlaceHolderInfo,
  	T_MinMaxAggInfo,
--- 247,252 ----
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 74f4daf..5393005 100644
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
*************** typedef struct PlannerInfo
*** 226,233 ****
  
  	List	   *join_info_list; /* list of SpecialJoinInfos */
  
- 	List	   *lateral_info_list;		/* list of LateralJoinInfos */
- 
  	List	   *append_rel_list;	/* list of AppendRelInfos */
  
  	List	   *rowMarks;		/* list of PlanRowMarks */
--- 226,231 ----
*************** typedef struct PlannerInfo
*** 357,362 ****
--- 355,361 ----
   *			(no duplicates) output from relation; NULL if not yet requested
   *		cheapest_parameterized_paths - best paths for their parameterizations;
   *			always includes cheapest_total_path, even if that's unparameterized
+  *		direct_lateral_relids - rels this rel has direct LATERAL references to
   *		lateral_relids - required outer rels for LATERAL, as a Relids set
   *			(includes both direct and indirect lateral references)
   *
*************** typedef struct PlannerInfo
*** 374,379 ****
--- 373,379 ----
   *		lateral_vars - lateral cross-references of rel, if any (list of
   *					   Vars and PlaceHolderVars)
   *		lateral_referencers - relids of rels that reference this one laterally
+  *				(includes both direct and indirect lateral references)
   *		indexlist - list of IndexOptInfo nodes for relation's indexes
   *					(always NIL if it's not a table)
   *		pages - number of disk pages in relation (zero if not a table)
*************** typedef struct RelOptInfo
*** 465,470 ****
--- 465,471 ----
  
  	/* parameterization information needed for both base rels and join rels */
  	/* (see also lateral_vars and lateral_referencers) */
+ 	Relids		direct_lateral_relids;	/* rels directly laterally referenced */
  	Relids		lateral_relids; /* minimum parameterization of rel */
  
  	/* information about a base rel (not set for join rels!) */
*************** typedef struct SpecialJoinInfo
*** 1461,1503 ****
  } SpecialJoinInfo;
  
  /*
-  * "Lateral join" info.
-  *
-  * Lateral references constrain the join order in a way that's somewhat like
-  * outer joins, though different in detail.  We construct a LateralJoinInfo
-  * for each lateral cross-reference, placing them in the PlannerInfo node's
-  * lateral_info_list.
-  *
-  * For unflattened LATERAL RTEs, we generate LateralJoinInfo(s) in which
-  * lateral_rhs is the relid of the LATERAL baserel, and lateral_lhs is a set
-  * of relids of baserels it references, all of which must be present on the
-  * LHS to compute a parameter needed by the RHS.  Typically, lateral_lhs is
-  * a singleton, but it can include multiple rels if the RHS references a
-  * PlaceHolderVar with a multi-rel ph_eval_at level.  We disallow joining to
-  * only part of the LHS in such cases, since that would result in a join tree
-  * with no convenient place to compute the PHV.
-  *
-  * When an appendrel contains lateral references (eg "LATERAL (SELECT x.col1
-  * UNION ALL SELECT y.col2)"), the LateralJoinInfos reference the parent
-  * baserel not the member otherrels, since it is the parent relid that is
-  * considered for joining purposes.
-  *
-  * If any LATERAL RTEs were flattened into the parent query, it is possible
-  * that the query now contains PlaceHolderVars containing lateral references,
-  * representing expressions that need to be evaluated at particular spots in
-  * the jointree but contain lateral references to Vars from elsewhere.  These
-  * give rise to LateralJoinInfos in which lateral_rhs is the evaluation point
-  * of a PlaceHolderVar and lateral_lhs is the set of lateral rels it needs.
-  */
- 
- typedef struct LateralJoinInfo
- {
- 	NodeTag		type;
- 	Relids		lateral_lhs;	/* rels needed to compute a lateral value */
- 	Relids		lateral_rhs;	/* rel where lateral value is needed */
- } LateralJoinInfo;
- 
- /*
   * Append-relation info.
   *
   * When we expand an inheritable table or a UNION-ALL subselect into an
--- 1462,1467 ----
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to