aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2014-01-30 14:51:19 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2014-01-30 14:51:19 -0500
commita4aa854cadc9a8272c08940c93cbaeb3d1c89945 (patch)
treea2666c320f58bb6335aa741ebb88c9ab5725a61e
parente3ec8015d073e5aef10c2618904b20c07bddd904 (diff)
downloadpostgresql-a4aa854cadc9a8272c08940c93cbaeb3d1c89945.tar.gz
postgresql-a4aa854cadc9a8272c08940c93cbaeb3d1c89945.zip
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.
-rw-r--r--src/backend/optimizer/plan/initsplan.c56
-rw-r--r--src/test/regress/expected/join.out22
-rw-r--r--src/test/regress/sql/join.sql8
3 files changed, 59 insertions, 27 deletions
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 04a399ee13c..69226f6f3bd 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -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;
@@ -896,6 +897,32 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
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
* we mustn't add it to join_info_list just yet, because we don't want
@@ -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);
diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out
index e38e8b6d76e..b2722c39f55 100644
--- a/src/test/regress/expected/join.out
+++ b/src/test/regress/expected/join.out
@@ -4012,6 +4012,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
diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql
index 49d50a68721..7ad7cac90cd 100644
--- a/src/test/regress/sql/join.sql
+++ b/src/test/regress/sql/join.sql
@@ -1120,6 +1120,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;