diff options
Diffstat (limited to 'src/backend/optimizer/prep/prepjointree.c')
| -rw-r--r-- | src/backend/optimizer/prep/prepjointree.c | 44 | 
1 files changed, 34 insertions, 10 deletions
| diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index afeb6ddb8c4..875baefbd39 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -41,6 +41,8 @@ typedef struct pullup_replace_vars_context  	PlannerInfo *root;  	List	   *targetlist;		/* tlist of subquery being pulled up */  	RangeTblEntry *target_rte;	/* RTE of subquery */ +	Relids		relids;			/* relids within subquery, as numbered after +								 * pullup (set only if target_rte->lateral) */  	bool	   *outer_hasSubLinks;		/* -> outer query's hasSubLinks */  	int			varno;			/* varno of subquery */  	bool		need_phvs;		/* do we need PlaceHolderVars? */ @@ -884,14 +886,19 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,  	/*  	 * The subquery's targetlist items are now in the appropriate form to  	 * insert into the top query, but if we are under an outer join then -	 * non-nullable items may have to be turned into PlaceHolderVars.  If we -	 * are dealing with an appendrel member then anything that's not a simple -	 * Var has to be turned into a PlaceHolderVar.	Set up appropriate context -	 * data for pullup_replace_vars. +	 * non-nullable items and lateral references may have to be turned into +	 * PlaceHolderVars.  If we are dealing with an appendrel member then +	 * anything that's not a simple Var has to be turned into a +	 * PlaceHolderVar.	Set up required context data for pullup_replace_vars.  	 */  	rvcontext.root = root;  	rvcontext.targetlist = subquery->targetList;  	rvcontext.target_rte = rte; +	if (rte->lateral) +		rvcontext.relids = get_relids_in_jointree((Node *) subquery->jointree, +												  true); +	else	/* won't need relids */ +		rvcontext.relids = NULL;  	rvcontext.outer_hasSubLinks = &parse->hasSubLinks;  	rvcontext.varno = varno;  	rvcontext.need_phvs = (lowest_nulling_outer_join != NULL || @@ -1675,8 +1682,18 @@ pullup_replace_vars_callback(Var *var,  			if (newnode && IsA(newnode, Var) &&  				((Var *) newnode)->varlevelsup == 0)  			{ -				/* Simple Vars always escape being wrapped */ -				wrap = false; +				/* +				 * Simple Vars always escape being wrapped, unless they are +				 * lateral references to something outside the subquery being +				 * pulled up.  (Even then, we could omit the PlaceHolderVar if +				 * the referenced rel is under the same lowest outer join, but +				 * it doesn't seem worth the trouble to check that.) +				 */ +				if (rcon->target_rte->lateral && +					!bms_is_member(((Var *) newnode)->varno, rcon->relids)) +					wrap = true; +				else +					wrap = false;  			}  			else if (newnode && IsA(newnode, PlaceHolderVar) &&  					 ((PlaceHolderVar *) newnode)->phlevelsup == 0) @@ -1692,9 +1709,10 @@ pullup_replace_vars_callback(Var *var,  			else  			{  				/* -				 * If it contains a Var of current level, and does not contain -				 * any non-strict constructs, then it's certainly nullable so -				 * we don't need to insert a PlaceHolderVar. +				 * If it contains a Var of the subquery being pulled up, and +				 * does not contain any non-strict constructs, then it's +				 * certainly nullable so we don't need to insert a +				 * PlaceHolderVar.  				 *  				 * This analysis could be tighter: in particular, a non-strict  				 * construct hidden within a lower-level PlaceHolderVar is not @@ -1703,8 +1721,14 @@ pullup_replace_vars_callback(Var *var,  				 *  				 * Note: in future maybe we should insert a PlaceHolderVar  				 * anyway, if the tlist item is expensive to evaluate? +				 * +				 * For a LATERAL subquery, we have to check the actual var +				 * membership of the node, but if it's non-lateral then any +				 * level-zero var must belong to the subquery.  				 */ -				if (contain_vars_of_level((Node *) newnode, 0) && +				if ((rcon->target_rte->lateral ? +				   bms_overlap(pull_varnos((Node *) newnode), rcon->relids) : +					 contain_vars_of_level((Node *) newnode, 0)) &&  					!contain_nonstrict_functions((Node *) newnode))  				{  					/* No wrap needed */ | 
