Fix a bug in 8.2.x that was exposed while investigating Kevin Grittner's

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.
This commit is contained in:
Tom Lane 2008-01-09 20:50:12 +00:00
parent 0a1e67bab3
commit 02b3f326b2
4 changed files with 117 additions and 33 deletions

View 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 =

View 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)

View 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)

View 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;