mirror of
https://github.com/postgres/postgres.git
synced 2025-04-21 12:05:57 +03:00
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:
parent
e3ec8015d0
commit
a4aa854cad
@ -797,6 +797,7 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
|
|||||||
ojscope;
|
ojscope;
|
||||||
List *leftjoinlist,
|
List *leftjoinlist,
|
||||||
*rightjoinlist;
|
*rightjoinlist;
|
||||||
|
List *my_quals;
|
||||||
SpecialJoinInfo *sjinfo;
|
SpecialJoinInfo *sjinfo;
|
||||||
ListCell *l;
|
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,
|
root->nullable_baserels = bms_add_members(root->nullable_baserels,
|
||||||
nullable_rels);
|
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
|
* For an OJ, form the SpecialJoinInfo now, because we need the OJ's
|
||||||
* semantic scope (ojscope) to pass to distribute_qual_to_rels. But
|
* 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,
|
leftids, rightids,
|
||||||
*inner_join_rels,
|
*inner_join_rels,
|
||||||
j->jointype,
|
j->jointype,
|
||||||
(List *) j->quals);
|
my_quals);
|
||||||
if (j->jointype == JOIN_SEMI)
|
if (j->jointype == JOIN_SEMI)
|
||||||
ojscope = NULL;
|
ojscope = NULL;
|
||||||
else
|
else
|
||||||
@ -923,33 +950,8 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
|
|||||||
ojscope = NULL;
|
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 */
|
/* Process the JOIN's qual clauses */
|
||||||
foreach(l, (List *) j->quals)
|
foreach(l, my_quals)
|
||||||
{
|
{
|
||||||
Node *qual = (Node *) lfirst(l);
|
Node *qual = (Node *) lfirst(l);
|
||||||
|
|
||||||
|
@ -4012,6 +4012,28 @@ select c.*,a.*,ss1.q1,ss2.q1,ss3.* from
|
|||||||
Output: i.f1
|
Output: i.f1
|
||||||
(34 rows)
|
(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
|
-- 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 f1 as g) ss;
|
||||||
ERROR: column "f1" does not exist
|
ERROR: column "f1" does not exist
|
||||||
|
@ -1120,6 +1120,14 @@ select c.*,a.*,ss1.q1,ss2.q1,ss3.* from
|
|||||||
) on c.q2 = ss2.q1,
|
) on c.q2 = ss2.q1,
|
||||||
lateral (select * from int4_tbl i where ss2.y > f1) ss3;
|
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
|
-- 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 f1 as g) ss;
|
||||||
select f1,g from int4_tbl a, (select a.f1 as g) ss;
|
select f1,g from int4_tbl a, (select a.f1 as g) ss;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user