]> granicus.if.org Git - postgresql/commitdiff
Fix bogus handling of "postponed" lateral quals.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 30 Jan 2014 19:51:19 +0000 (14:51 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 30 Jan 2014 19:51:19 +0000 (14:51 -0500)
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.

src/backend/optimizer/plan/initsplan.c
src/test/regress/expected/join.out
src/test/regress/sql/join.sql

index 04a399ee13cd549a1b3dec3a44f0f09bbefdc99c..69226f6f3bd75a2a53a93a2f8c97959460b04583 100644 (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);
 
index e38e8b6d76e9193e0b0104eab343824ee33237fa..b2722c39f555b7e2d3b3913233359c42a24756a6 100644 (file)
@@ -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
index 49d50a68721ff2047dd3b095b7216ed25d6aa34b..7ad7cac90cdf6cbf868e0cade3054d0c6964f6bd 100644 (file)
@@ -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;