mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-04 00:02:52 -05:00 
			
		
		
		
	Get rid of the planner's LateralJoinInfo data structure.
I originally modeled this data structure on SpecialJoinInfo, but after commit acfcd45cacb6df23 that looks like a pretty poor decision. All we really need is relid sets identifying laterally-referenced rels; and most of the time, what we want to know about includes indirect lateral references, a case the LateralJoinInfo data was unsuited to compute with any efficiency. The previous commit redefined RelOptInfo.lateral_relids as the transitive closure of lateral references, so that it easily supports checking indirect references. For the places where we really do want just direct references, add a new RelOptInfo field direct_lateral_relids, which is easily set up as a copy of lateral_relids before we perform the transitive closure calculation. Then we can just drop lateral_info_list and LateralJoinInfo and the supporting code. This makes the planner's handling of lateral references noticeably more efficient, and shorter too. Such a change can't be back-patched into stable branches for fear of breaking extensions that might be looking at the planner's data structures; but it seems not too late to push it into 9.5, so I've done so.
This commit is contained in:
		
							parent
							
								
									ed8bec915e
								
							
						
					
					
						commit
						4fcf48450d
					
				@ -2066,20 +2066,6 @@ _copySpecialJoinInfo(const SpecialJoinInfo *from)
 | 
			
		||||
	return newnode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * _copyLateralJoinInfo
 | 
			
		||||
 */
 | 
			
		||||
static LateralJoinInfo *
 | 
			
		||||
_copyLateralJoinInfo(const LateralJoinInfo *from)
 | 
			
		||||
{
 | 
			
		||||
	LateralJoinInfo *newnode = makeNode(LateralJoinInfo);
 | 
			
		||||
 | 
			
		||||
	COPY_BITMAPSET_FIELD(lateral_lhs);
 | 
			
		||||
	COPY_BITMAPSET_FIELD(lateral_rhs);
 | 
			
		||||
 | 
			
		||||
	return newnode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * _copyAppendRelInfo
 | 
			
		||||
 */
 | 
			
		||||
@ -4519,9 +4505,6 @@ copyObject(const void *from)
 | 
			
		||||
		case T_SpecialJoinInfo:
 | 
			
		||||
			retval = _copySpecialJoinInfo(from);
 | 
			
		||||
			break;
 | 
			
		||||
		case T_LateralJoinInfo:
 | 
			
		||||
			retval = _copyLateralJoinInfo(from);
 | 
			
		||||
			break;
 | 
			
		||||
		case T_AppendRelInfo:
 | 
			
		||||
			retval = _copyAppendRelInfo(from);
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
@ -845,15 +845,6 @@ _equalSpecialJoinInfo(const SpecialJoinInfo *a, const SpecialJoinInfo *b)
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
@ -2860,9 +2851,6 @@ equal(const void *a, const void *b)
 | 
			
		||||
		case T_SpecialJoinInfo:
 | 
			
		||||
			retval = _equalSpecialJoinInfo(a, b);
 | 
			
		||||
			break;
 | 
			
		||||
		case T_LateralJoinInfo:
 | 
			
		||||
			retval = _equalLateralJoinInfo(a, b);
 | 
			
		||||
			break;
 | 
			
		||||
		case T_AppendRelInfo:
 | 
			
		||||
			retval = _equalAppendRelInfo(a, b);
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
@ -1847,7 +1847,6 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node)
 | 
			
		||||
	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);
 | 
			
		||||
@ -1892,6 +1891,7 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node)
 | 
			
		||||
	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);
 | 
			
		||||
@ -2056,15 +2056,6 @@ _outSpecialJoinInfo(StringInfo str, const SpecialJoinInfo *node)
 | 
			
		||||
	WRITE_NODE_FIELD(semi_rhs_exprs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
@ -3355,9 +3346,6 @@ _outNode(StringInfo str, const void *obj)
 | 
			
		||||
			case T_SpecialJoinInfo:
 | 
			
		||||
				_outSpecialJoinInfo(str, obj);
 | 
			
		||||
				break;
 | 
			
		||||
			case T_LateralJoinInfo:
 | 
			
		||||
				_outLateralJoinInfo(str, obj);
 | 
			
		||||
				break;
 | 
			
		||||
			case T_AppendRelInfo:
 | 
			
		||||
				_outAppendRelInfo(str, obj);
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
@ -231,7 +231,7 @@ join_search_one_level(PlannerInfo *root, int level)
 | 
			
		||||
		 */
 | 
			
		||||
		if (joinrels[level] == NIL &&
 | 
			
		||||
			root->join_info_list == NIL &&
 | 
			
		||||
			root->lateral_info_list == NIL)
 | 
			
		||||
			!root->hasLateralRTEs)
 | 
			
		||||
			elog(ERROR, "failed to build any %d-way joins", level);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -559,15 +559,7 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 | 
			
		||||
				 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)
 | 
			
		||||
			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))
 | 
			
		||||
@ -582,15 +574,7 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
 | 
			
		||||
				 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)
 | 
			
		||||
			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))
 | 
			
		||||
@ -922,17 +906,9 @@ have_join_order_restriction(PlannerInfo *root,
 | 
			
		||||
	 * 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))
 | 
			
		||||
	if (bms_overlap(rel1->relids, rel2->direct_lateral_relids) ||
 | 
			
		||||
		bms_overlap(rel2->relids, rel1->direct_lateral_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,
 | 
			
		||||
 | 
			
		||||
@ -439,9 +439,6 @@ remove_rel_from_query(PlannerInfo *root, int relid, Relids joinrelids)
 | 
			
		||||
		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.
 | 
			
		||||
 | 
			
		||||
@ -47,7 +47,6 @@ typedef struct PostponedQual
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
@ -382,11 +381,8 @@ extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 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).
 | 
			
		||||
 *	  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.
 | 
			
		||||
@ -394,6 +390,7 @@ extract_lateral_references(PlannerInfo *root, RelOptInfo *brel, Index rtindex)
 | 
			
		||||
void
 | 
			
		||||
create_lateral_join_info(PlannerInfo *root)
 | 
			
		||||
{
 | 
			
		||||
	bool		found_laterals = false;
 | 
			
		||||
	Index		rti;
 | 
			
		||||
	ListCell   *lc;
 | 
			
		||||
 | 
			
		||||
@ -430,8 +427,7 @@ create_lateral_join_info(PlannerInfo *root)
 | 
			
		||||
			{
 | 
			
		||||
				Var		   *var = (Var *) node;
 | 
			
		||||
 | 
			
		||||
				add_lateral_info(root, bms_make_singleton(var->varno),
 | 
			
		||||
								 brel->relids);
 | 
			
		||||
				found_laterals = true;
 | 
			
		||||
				lateral_relids = bms_add_member(lateral_relids,
 | 
			
		||||
												var->varno);
 | 
			
		||||
			}
 | 
			
		||||
@ -441,7 +437,7 @@ create_lateral_join_info(PlannerInfo *root)
 | 
			
		||||
				PlaceHolderInfo *phinfo = find_placeholder_info(root, phv,
 | 
			
		||||
																false);
 | 
			
		||||
 | 
			
		||||
				add_lateral_info(root, phinfo->ph_eval_at, brel->relids);
 | 
			
		||||
				found_laterals = true;
 | 
			
		||||
				lateral_relids = bms_add_members(lateral_relids,
 | 
			
		||||
												 phinfo->ph_eval_at);
 | 
			
		||||
			}
 | 
			
		||||
@ -449,69 +445,54 @@ create_lateral_join_info(PlannerInfo *root)
 | 
			
		||||
				Assert(false);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* We now have all the direct lateral refs from this rel */
 | 
			
		||||
		brel->lateral_relids = lateral_relids;
 | 
			
		||||
		/* 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 make
 | 
			
		||||
	 * LateralJoinInfos describing each such reference.  Unlike references in
 | 
			
		||||
	 * unflattened LATERAL RTEs, the referencing location could be a join.
 | 
			
		||||
	 * 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 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.
 | 
			
		||||
	 * 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)
 | 
			
		||||
		if (phinfo->ph_lateral == NULL)
 | 
			
		||||
			continue;			/* PHV is uninteresting if no lateral refs */
 | 
			
		||||
 | 
			
		||||
		found_laterals = true;
 | 
			
		||||
 | 
			
		||||
		if (bms_get_singleton_member(eval_at, &varno))
 | 
			
		||||
		{
 | 
			
		||||
			List	   *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
 | 
			
		||||
											   PVC_RECURSE_AGGREGATES,
 | 
			
		||||
											   PVC_INCLUDE_PLACEHOLDERS);
 | 
			
		||||
			ListCell   *lc2;
 | 
			
		||||
			int			ev_at;
 | 
			
		||||
			/* Evaluation site is a baserel */
 | 
			
		||||
			RelOptInfo *brel = find_base_rel(root, varno);
 | 
			
		||||
 | 
			
		||||
			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);
 | 
			
		||||
			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
 | 
			
		||||
					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);
 | 
			
		||||
			/* 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);
 | 
			
		||||
@ -519,17 +500,22 @@ create_lateral_join_info(PlannerInfo *root)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* If we found no lateral references, we're done. */
 | 
			
		||||
	if (root->lateral_info_list == NIL)
 | 
			
		||||
	/*
 | 
			
		||||
	 * 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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * 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.
 | 
			
		||||
	 * 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
 | 
			
		||||
@ -632,6 +618,8 @@ create_lateral_join_info(PlannerInfo *root)
 | 
			
		||||
					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);
 | 
			
		||||
@ -641,46 +629,6 @@ create_lateral_join_info(PlannerInfo *root)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*****************************************************************************
 | 
			
		||||
 *
 | 
			
		||||
 | 
			
		||||
@ -433,9 +433,8 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
 | 
			
		||||
	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 */
 | 
			
		||||
	/* There shouldn't be any OJ 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);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -114,7 +114,6 @@ query_planner(PlannerInfo *root, List *tlist,
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
@ -201,9 +200,8 @@ query_planner(PlannerInfo *root, List *tlist,
 | 
			
		||||
	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.
 | 
			
		||||
	 * Construct the lateral reference sets now that we have finalized
 | 
			
		||||
	 * PlaceHolderVar eval levels.
 | 
			
		||||
	 */
 | 
			
		||||
	create_lateral_join_info(root);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1132,9 +1132,8 @@ inheritance_planner(PlannerInfo *root)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* There shouldn't be any OJ or LATERAL info to translate, as yet */
 | 
			
		||||
		/* There shouldn't be any OJ 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 */
 | 
			
		||||
 | 
			
		||||
@ -1150,14 +1150,11 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 | 
			
		||||
										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.
 | 
			
		||||
	 * 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->lateral_info_list == NIL);
 | 
			
		||||
	Assert(subroot->lateral_info_list == NIL);
 | 
			
		||||
	Assert(root->placeholder_list == NIL);
 | 
			
		||||
	Assert(subroot->placeholder_list == NIL);
 | 
			
		||||
 | 
			
		||||
@ -1642,7 +1639,6 @@ pull_up_simple_values(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
@ -2839,7 +2835,6 @@ substitute_multiple_relids_walker(Node *node,
 | 
			
		||||
	}
 | 
			
		||||
	/* 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,7 +1786,6 @@ adjust_appendrel_attrs_mutator(Node *node,
 | 
			
		||||
	}
 | 
			
		||||
	/* 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));
 | 
			
		||||
 | 
			
		||||
@ -363,12 +363,10 @@ fix_placeholder_input_needed_levels(PlannerInfo *root)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * add_placeholders_to_base_rels
 | 
			
		||||
 *		Add any required PlaceHolderVars to base rels' targetlists, and
 | 
			
		||||
 *		update lateral_vars lists for lateral references contained in them.
 | 
			
		||||
 *		Add any required PlaceHolderVars to base rels' targetlists.
 | 
			
		||||
 *
 | 
			
		||||
 * If any placeholder can be computed at a base rel and is needed above it,
 | 
			
		||||
 * add it to that rel's targetlist, and add any lateral references it requires
 | 
			
		||||
 * to the rel's lateral_vars list.  This might look like it could be merged
 | 
			
		||||
 * add it to that rel's targetlist.  This might look like it could be merged
 | 
			
		||||
 * with fix_placeholder_input_needed_levels, but it must be separate because
 | 
			
		||||
 * join removal happens in between, and can change the ph_eval_at sets.  There
 | 
			
		||||
 * is essentially the same logic in add_placeholders_to_joinrel, but we can't
 | 
			
		||||
@ -385,58 +383,22 @@ add_placeholders_to_base_rels(PlannerInfo *root)
 | 
			
		||||
		Relids		eval_at = phinfo->ph_eval_at;
 | 
			
		||||
		int			varno;
 | 
			
		||||
 | 
			
		||||
		if (bms_get_singleton_member(eval_at, &varno))
 | 
			
		||||
		if (bms_get_singleton_member(eval_at, &varno) &&
 | 
			
		||||
			bms_nonempty_difference(phinfo->ph_needed, eval_at))
 | 
			
		||||
		{
 | 
			
		||||
			RelOptInfo *rel = find_base_rel(root, varno);
 | 
			
		||||
 | 
			
		||||
			/* add it to reltargetlist if needed above the rel scan level */
 | 
			
		||||
			if (bms_nonempty_difference(phinfo->ph_needed, eval_at))
 | 
			
		||||
			rel->reltargetlist = lappend(rel->reltargetlist,
 | 
			
		||||
										 copyObject(phinfo->ph_var));
 | 
			
		||||
			/* if there are lateral refs in it, add them to lateral_vars */
 | 
			
		||||
			if (phinfo->ph_lateral != NULL)
 | 
			
		||||
			{
 | 
			
		||||
				List	   *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr,
 | 
			
		||||
												   PVC_RECURSE_AGGREGATES,
 | 
			
		||||
												   PVC_INCLUDE_PLACEHOLDERS);
 | 
			
		||||
				ListCell   *lc2;
 | 
			
		||||
 | 
			
		||||
				foreach(lc2, vars)
 | 
			
		||||
				{
 | 
			
		||||
					Node	   *node = (Node *) lfirst(lc2);
 | 
			
		||||
 | 
			
		||||
					if (IsA(node, Var))
 | 
			
		||||
					{
 | 
			
		||||
						Var		   *var = (Var *) node;
 | 
			
		||||
 | 
			
		||||
						if (var->varno != varno)
 | 
			
		||||
							rel->lateral_vars = lappend(rel->lateral_vars,
 | 
			
		||||
														var);
 | 
			
		||||
					}
 | 
			
		||||
					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))
 | 
			
		||||
							rel->lateral_vars = lappend(rel->lateral_vars,
 | 
			
		||||
														other_phv);
 | 
			
		||||
					}
 | 
			
		||||
					else
 | 
			
		||||
						Assert(false);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				list_free(vars);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * add_placeholders_to_joinrel
 | 
			
		||||
 *		Add any required PlaceHolderVars to a join rel's targetlist.
 | 
			
		||||
 *		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.
 | 
			
		||||
@ -463,6 +425,10 @@ add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel)
 | 
			
		||||
				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);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -111,6 +111,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
 | 
			
		||||
	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;
 | 
			
		||||
@ -373,6 +374,10 @@ build_join_rel(PlannerInfo *root,
 | 
			
		||||
	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 */
 | 
			
		||||
@ -422,6 +427,18 @@ build_join_rel(PlannerInfo *root,
 | 
			
		||||
	build_joinrel_tlist(root, joinrel, inner_rel);
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
@ -782,7 +782,6 @@ flatten_join_alias_vars_mutator(Node *node,
 | 
			
		||||
	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));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -402,7 +402,6 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
 | 
			
		||||
	/* 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));
 | 
			
		||||
 | 
			
		||||
@ -586,7 +585,6 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
 | 
			
		||||
	}
 | 
			
		||||
	/* 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));
 | 
			
		||||
 | 
			
		||||
@ -868,7 +866,6 @@ rangeTableEntry_used_walker(Node *node,
 | 
			
		||||
	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));
 | 
			
		||||
 | 
			
		||||
@ -247,7 +247,6 @@ typedef enum NodeTag
 | 
			
		||||
	T_RestrictInfo,
 | 
			
		||||
	T_PlaceHolderVar,
 | 
			
		||||
	T_SpecialJoinInfo,
 | 
			
		||||
	T_LateralJoinInfo,
 | 
			
		||||
	T_AppendRelInfo,
 | 
			
		||||
	T_PlaceHolderInfo,
 | 
			
		||||
	T_MinMaxAggInfo,
 | 
			
		||||
 | 
			
		||||
@ -226,8 +226,6 @@ typedef struct PlannerInfo
 | 
			
		||||
 | 
			
		||||
	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 */
 | 
			
		||||
@ -357,6 +355,7 @@ typedef struct PlannerInfo
 | 
			
		||||
 *			(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)
 | 
			
		||||
 *
 | 
			
		||||
@ -466,6 +465,7 @@ typedef struct RelOptInfo
 | 
			
		||||
 | 
			
		||||
	/* 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!) */
 | 
			
		||||
@ -1461,43 +1461,6 @@ typedef struct SpecialJoinInfo
 | 
			
		||||
	List	   *semi_rhs_exprs; /* righthand-side expressions of these ops */
 | 
			
		||||
} 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.
 | 
			
		||||
 *
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user