Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Fix improper interaction of FULL JOINs with lateral references.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 8 Apr 2019 20:09:07 +0000 (16:09 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 8 Apr 2019 20:09:07 +0000 (16:09 -0400)
join_is_legal() needs to reject forming certain outer joins in cases
where that would lead the planner down a blind alley.  However, it
mistakenly supposed that the way to handle full joins was to treat them
as applying the same constraints as for left joins, only to both sides.
That doesn't work, as shown in bug #15741 from Anthony Skorski: given
a lateral reference out of a join that's fully enclosed by a full join,
the code would fail to believe that any join ordering is legal, resulting
in errors like "failed to build any N-way joins".

However, we don't really need to consider full joins at all for this
purpose, because we effectively force them to be evaluated in syntactic
order, and that order is always legal for lateral references.  Hence,
get rid of this broken logic for full joins and just ignore them instead.

This seems to have been an oversight in commit 7e19db0c0.
Back-patch to all supported branches, as that was.

Discussion: https://postgr.es/m/15741-276f1f464b3f40eb@postgresql.org

src/backend/optimizer/path/joinrels.c
src/test/regress/expected/rangefuncs.out
src/test/regress/sql/rangefuncs.sql

index 2cd222073e9981beda301a07284c5bfe0104822b..d70e70e78694300932b883dbc5ac0237f1c3dc41 100644 (file)
@@ -609,6 +609,10 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
                {
                    SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(l);
 
+                   /* ignore full joins --- their ordering is predetermined */
+                   if (sjinfo->jointype == JOIN_FULL)
+                       continue;
+
                    if (bms_overlap(sjinfo->min_lefthand, join_plus_rhs) &&
                        !bms_is_subset(sjinfo->min_righthand, join_plus_rhs))
                    {
@@ -616,15 +620,6 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
                                                      sjinfo->min_righthand);
                        more = true;
                    }
-                   /* full joins constrain both sides symmetrically */
-                   if (sjinfo->jointype == JOIN_FULL &&
-                       bms_overlap(sjinfo->min_righthand, join_plus_rhs) &&
-                       !bms_is_subset(sjinfo->min_lefthand, join_plus_rhs))
-                   {
-                       join_plus_rhs = bms_add_members(join_plus_rhs,
-                                                       sjinfo->min_lefthand);
-                       more = true;
-                   }
                }
            } while (more);
            if (bms_overlap(join_plus_rhs, join_lateral_rels))
index f06cfa4b21dc470a3ae1dc86857e4167bc3f736f..57eccdfc96dc3b345cba87be6add74c47dc91aca 100644 (file)
@@ -1407,6 +1407,31 @@ SELECT * FROM (VALUES (1),(2),(3)) v1(r1),
   3 |  3 | 30 | 8
 (45 rows)
 
+-- check handling of FULL JOIN with multiple lateral references (bug #15741)
+SELECT *
+FROM (VALUES (1),(2)) v1(r1)
+    LEFT JOIN LATERAL (
+        SELECT *
+        FROM generate_series(1, v1.r1) AS gs1
+        LEFT JOIN LATERAL (
+            SELECT *
+            FROM generate_series(1, gs1) AS gs2
+            LEFT JOIN generate_series(1, gs2) AS gs3 ON TRUE
+        ) AS ss1 ON TRUE
+        FULL JOIN generate_series(1, v1.r1) AS gs4 ON FALSE
+    ) AS ss0 ON TRUE;
+ r1 | gs1 | gs2 | gs3 | gs4 
+----+-----+-----+-----+-----
+  1 |     |     |     |   1
+  1 |   1 |   1 |   1 |    
+  2 |     |     |     |   1
+  2 |     |     |     |   2
+  2 |   1 |   1 |   1 |    
+  2 |   2 |   1 |   1 |    
+  2 |   2 |   2 |   1 |    
+  2 |   2 |   2 |   2 |    
+(8 rows)
+
 DROP FUNCTION foo_sql(int,int);
 DROP FUNCTION foo_mat(int,int);
 DROP SEQUENCE foo_rescan_seq1;
index c8edc553bc39c7b64d6521b86c4ce038348b776b..ce1445568087c8efae489e5272dc1cdff187221f 100644 (file)
@@ -321,6 +321,21 @@ SELECT * FROM (VALUES (1),(2),(3)) v1(r1),
               LATERAL (SELECT r1, * FROM (VALUES (10),(20),(30)) v2(r2)
                                          LEFT JOIN generate_series(r1,2+r2/5) f(i) ON ((r2+i)<100) OFFSET 0) s1;
 
+-- check handling of FULL JOIN with multiple lateral references (bug #15741)
+
+SELECT *
+FROM (VALUES (1),(2)) v1(r1)
+    LEFT JOIN LATERAL (
+        SELECT *
+        FROM generate_series(1, v1.r1) AS gs1
+        LEFT JOIN LATERAL (
+            SELECT *
+            FROM generate_series(1, gs1) AS gs2
+            LEFT JOIN generate_series(1, gs2) AS gs3 ON TRUE
+        ) AS ss1 ON TRUE
+        FULL JOIN generate_series(1, v1.r1) AS gs4 ON FALSE
+    ) AS ss0 ON TRUE;
+
 DROP FUNCTION foo_sql(int,int);
 DROP FUNCTION foo_mat(int,int);
 DROP SEQUENCE foo_rescan_seq1;