Fix bogus handling of "postponed" lateral quals.

When pulling a "postponed" qual from a LATERAL subquery up into the quals
of an outer join, we must make sure that the postponed qual is included
in those seen by make_outerjoininfo().  Otherwise we might compute a
too-small min_lefthand or min_righthand for the outer join, leading to
"JOIN qualification cannot refer to other relations" failures from
distribute_qual_to_rels.  Subtler errors in the created plan seem possible,
too, if the extra qual would only affect join ordering constraints.

Per bug #9041 from David Leverton.  Back-patch to 9.3.
This commit is contained in:
Tom Lane 2014-01-30 14:51:16 -05:00
parent c29a6dd548
commit 043f6ff05d
3 changed files with 59 additions and 27 deletions

View File

@ -797,6 +797,7 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
ojscope;
List *leftjoinlist,
*rightjoinlist;
List *my_quals;
SpecialJoinInfo *sjinfo;
ListCell *l;
@ -895,6 +896,32 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
root->nullable_baserels = bms_add_members(root->nullable_baserels,
nullable_rels);
/*
* Try to process any quals postponed by children. If they need
* further postponement, add them to my output postponed_qual_list.
* Quals that can be processed now must be included in my_quals, so
* that they'll be handled properly in make_outerjoininfo.
*/
my_quals = NIL;
foreach(l, child_postponed_quals)
{
PostponedQual *pq = (PostponedQual *) lfirst(l);
if (bms_is_subset(pq->relids, *qualscope))
my_quals = lappend(my_quals, pq->qual);
else
{
/*
* We should not be postponing any quals past an outer join.
* If this Assert fires, pull_up_subqueries() messed up.
*/
Assert(j->jointype == JOIN_INNER);
*postponed_qual_list = lappend(*postponed_qual_list, pq);
}
}
/* list_concat is nondestructive of its second argument */
my_quals = list_concat(my_quals, (List *) j->quals);
/*
* For an OJ, form the SpecialJoinInfo now, because we need the OJ's
* semantic scope (ojscope) to pass to distribute_qual_to_rels. But
@ -910,7 +937,7 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
leftids, rightids,
*inner_join_rels,
j->jointype,
(List *) j->quals);
my_quals);
if (j->jointype == JOIN_SEMI)
ojscope = NULL;
else
@ -923,33 +950,8 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
ojscope = NULL;
}
/*
* Try to process any quals postponed by children. If they need
* further postponement, add them to my output postponed_qual_list.
*/
foreach(l, child_postponed_quals)
{
PostponedQual *pq = (PostponedQual *) lfirst(l);
if (bms_is_subset(pq->relids, *qualscope))
distribute_qual_to_rels(root, pq->qual,
false, below_outer_join, j->jointype,
*qualscope,
ojscope, nonnullable_rels, NULL,
NULL);
else
{
/*
* We should not be postponing any quals past an outer join.
* If this Assert fires, pull_up_subqueries() messed up.
*/
Assert(j->jointype == JOIN_INNER);
*postponed_qual_list = lappend(*postponed_qual_list, pq);
}
}
/* Process the JOIN's qual clauses */
foreach(l, (List *) j->quals)
foreach(l, my_quals)
{
Node *qual = (Node *) lfirst(l);

View File

@ -4060,6 +4060,28 @@ select c.*,a.*,ss1.q1,ss2.q1,ss3.* from
Output: i.f1
(34 rows)
-- check processing of postponed quals (bug #9041)
explain (verbose, costs off)
select * from
(select 1 as x) x cross join (select 2 as y) y
left join lateral (
select * from (select 3 as z) z where z.z = x.x
) zz on zz.z = y.y;
QUERY PLAN
----------------------------------------------
Nested Loop Left Join
Output: (1), (2), (3)
Join Filter: (((3) = (1)) AND ((3) = (2)))
-> Nested Loop
Output: (1), (2)
-> Result
Output: 1
-> Result
Output: 2
-> Result
Output: 3
(11 rows)
-- test some error cases where LATERAL should have been used but wasn't
select f1,g from int4_tbl a, (select f1 as g) ss;
ERROR: column "f1" does not exist

View File

@ -1134,6 +1134,14 @@ select c.*,a.*,ss1.q1,ss2.q1,ss3.* from
) on c.q2 = ss2.q1,
lateral (select * from int4_tbl i where ss2.y > f1) ss3;
-- check processing of postponed quals (bug #9041)
explain (verbose, costs off)
select * from
(select 1 as x) x cross join (select 2 as y) y
left join lateral (
select * from (select 3 as z) z where z.z = x.x
) zz on zz.z = y.y;
-- test some error cases where LATERAL should have been used but wasn't
select f1,g from int4_tbl a, (select f1 as g) ss;
select f1,g from int4_tbl a, (select a.f1 as g) ss;