]> granicus.if.org Git - postgresql/commitdiff
Fix a bug in 8.2.x that was exposed while investigating Kevin Grittner's
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 9 Jan 2008 20:50:12 +0000 (20:50 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 9 Jan 2008 20:50:12 +0000 (20:50 +0000)
report of poor planning in 8.3: it's unsafe to push a constant across an
outer join when the outer-join condition is delayed by lower outer joins,
unless we recheck the outer-join condition at the upper outer join.
8.2.x doesn't really have the ability to tell whether this is the case
or not, but fortunately it doesn't matter --- it seems most desirable to
keep the join condition whether it's entirely redundant or not.  However,
it's usually mostly redundant, so force its selectivity to 1.0.

It might be a good idea to back-patch this into 8.1 as well, but I'll
refrain until/unless there's evidence that 8.1 actually fails on any
cases that this would fix.

src/backend/optimizer/path/pathkeys.c
src/test/regress/expected/join.out
src/test/regress/expected/join_1.out
src/test/regress/sql/join.sql

index b254598fcf34a92996a0c498637812186836d6f6..abc50ef14d07bc2bad6cdf460982d8c685c89c69 100644 (file)
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/path/pathkeys.c,v 1.79 2006/10/04 00:29:54 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/path/pathkeys.c,v 1.79.2.1 2008/01/09 20:50:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -423,15 +423,16 @@ sub_generate_join_implications(PlannerInfo *root,
                                                                         false);
 
                        /*
-                        * We can remove explicit tests of this outer-join qual, too,
-                        * since we now have tests forcing each of its sides to the same
-                        * value.
+                        * We used to think we could remove explicit tests of this
+                        * outer-join qual, too, since we now have tests forcing each of
+                        * its sides to the same value.  However, that fails in some
+                        * corner cases where lower outer joins could cause one of the
+                        * variables to go to NULL.  (BUG in 8.2 through 8.2.6.)
+                        * So now we just leave it in place, but mark it with selectivity
+                        * 1.0 so that we don't underestimate the join size output ---
+                        * it's mostly redundant with the constant constraints.
                         */
-                       process_implied_equality(root,
-                                                                        leftop, rightop,
-                                                                        rinfo->left_sortop, rinfo->right_sortop,
-                                                                        rinfo->left_relids, rinfo->right_relids,
-                                                                        true);
+                       rinfo->this_selec = 1.0;
 
                        /*
                         * And recurse to see if we can deduce anything from INNERVAR =
@@ -465,15 +466,16 @@ sub_generate_join_implications(PlannerInfo *root,
                                                                         false);
 
                        /*
-                        * We can remove explicit tests of this outer-join qual, too,
-                        * since we now have tests forcing each of its sides to the same
-                        * value.
+                        * We used to think we could remove explicit tests of this
+                        * outer-join qual, too, since we now have tests forcing each of
+                        * its sides to the same value.  However, that fails in some
+                        * corner cases where lower outer joins could cause one of the
+                        * variables to go to NULL.  (BUG in 8.2 through 8.2.6.)
+                        * So now we just leave it in place, but mark it with selectivity
+                        * 1.0 so that we don't underestimate the join size output ---
+                        * it's mostly redundant with the constant constraints.
                         */
-                       process_implied_equality(root,
-                                                                        leftop, rightop,
-                                                                        rinfo->left_sortop, rinfo->right_sortop,
-                                                                        rinfo->left_relids, rinfo->right_relids,
-                                                                        true);
+                       rinfo->this_selec = 1.0;
 
                        /*
                         * And recurse to see if we can deduce anything from INNERVAR =
@@ -542,25 +544,22 @@ sub_generate_join_implications(PlannerInfo *root,
                                                                                 rinfo->right_sortop,
                                                                                 rinfo->right_relids,
                                                                                 false);
-                               /* ... and remove COALESCE() = CONSTANT */
-                               process_implied_const_eq(root, equi_key_set, relids,
-                                                                                item1,
-                                                                                sortop1,
-                                                                                item1_relids,
-                                                                                true);
 
                                /*
-                                * We can remove explicit tests of this outer-join qual, too,
-                                * since we now have tests forcing each of its sides to the
-                                * same value.
+                                * We used to think we could remove explicit tests of this
+                                * outer-join qual, too, since we now have tests forcing each
+                                * of its sides to the same value.  However, that fails in
+                                * some corner cases where lower outer joins could cause one
+                                * of the variables to go to NULL.  (BUG in 8.2 through
+                                * 8.2.6.)  So now we just leave it in place, but mark it with
+                                * selectivity 1.0 so that we don't underestimate the join
+                                * size output --- it's mostly redundant with the constant
+                                * constraints.
+                                *
+                                * Ideally we'd do that for the COALESCE() = CONSTANT rinfo,
+                                * too, but we don't have easy access to that here.
                                 */
-                               process_implied_equality(root,
-                                                                                leftop, rightop,
-                                                                                rinfo->left_sortop,
-                                                                                rinfo->right_sortop,
-                                                                                rinfo->left_relids,
-                                                                                rinfo->right_relids,
-                                                                                true);
+                               rinfo->this_selec = 1.0;
 
                                /*
                                 * And recurse to see if we can deduce anything from LEFTVAR =
index 3dd4f5ed7d01218181c764e5cd1c67969f0de514..bd33cb5c12646b7df4d4e54a9f5b045c4f0a8879 100644 (file)
@@ -2290,3 +2290,34 @@ from yy
      301 |         |          |          |        1
 (3 rows)
 
+--
+-- regression test for improper pushing of constants across outer-join clauses
+-- (as seen in early 8.2.x releases)
+--
+create temp table zt1 (f1 int primary key);
+NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "zt1_pkey" for table "zt1"
+create temp table zt2 (f2 int primary key);
+NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "zt2_pkey" for table "zt2"
+create temp table zt3 (f3 int primary key);
+NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "zt3_pkey" for table "zt3"
+insert into zt1 values(53);
+insert into zt2 values(53);
+select * from
+  zt2 left join zt3 on (f2 = f3)
+      left join zt1 on (f3 = f1)
+where f2 = 53;
+ f2 | f3 | f1 
+----+----+----
+ 53 |    |   
+(1 row)
+
+create temp view zv1 as select *,'dummy'::text AS junk from zt1;
+select * from
+  zt2 left join zt3 on (f2 = f3)
+      left join zv1 on (f3 = f1)
+where f2 = 53;
+ f2 | f3 | f1 | junk 
+----+----+----+------
+ 53 |    |    | 
+(1 row)
+
index 9e10c73acb7b96b1b9353d60b185fa63e3142464..4690b711402199bb7c5bf395010a560e10854ebe 100644 (file)
@@ -2290,3 +2290,34 @@ from yy
      301 |         |          |          |        1
 (3 rows)
 
+--
+-- regression test for improper pushing of constants across outer-join clauses
+-- (as seen in early 8.2.x releases)
+--
+create temp table zt1 (f1 int primary key);
+NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "zt1_pkey" for table "zt1"
+create temp table zt2 (f2 int primary key);
+NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "zt2_pkey" for table "zt2"
+create temp table zt3 (f3 int primary key);
+NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "zt3_pkey" for table "zt3"
+insert into zt1 values(53);
+insert into zt2 values(53);
+select * from
+  zt2 left join zt3 on (f2 = f3)
+      left join zt1 on (f3 = f1)
+where f2 = 53;
+ f2 | f3 | f1 
+----+----+----
+ 53 |    |   
+(1 row)
+
+create temp view zv1 as select *,'dummy'::text AS junk from zt1;
+select * from
+  zt2 left join zt3 on (f2 = f3)
+      left join zv1 on (f3 = f1)
+where f2 = 53;
+ f2 | f3 | f1 | junk 
+----+----+----+------
+ 53 |    |    | 
+(1 row)
+
index 4efb3c7283626153d9555667eb83a0e93fc8614c..96e15b526c2370a4ac46cb5adc090db59cdc6489 100644 (file)
@@ -462,3 +462,26 @@ from yy
      left join (SELECT * FROM yy where pkyy = 101) as yya ON yy.pkyy = yya.pkyy
      left join xx xxa on yya.pkxx = xxa.pkxx
      left join xx xxb on coalesce (xxa.pkxx, 1) = xxb.pkxx;
+
+--
+-- regression test for improper pushing of constants across outer-join clauses
+-- (as seen in early 8.2.x releases)
+--
+
+create temp table zt1 (f1 int primary key);
+create temp table zt2 (f2 int primary key);
+create temp table zt3 (f3 int primary key);
+insert into zt1 values(53);
+insert into zt2 values(53);
+
+select * from
+  zt2 left join zt3 on (f2 = f3)
+      left join zt1 on (f3 = f1)
+where f2 = 53;
+
+create temp view zv1 as select *,'dummy'::text AS junk from zt1;
+
+select * from
+  zt2 left join zt3 on (f2 = f3)
+      left join zv1 on (f3 = f1)
+where f2 = 53;