@ -2175,6 +2175,17 @@ select c.id, ss.a from c
left join ( select d . a from onerow , d left join b on d . a = b . id ) ss
on c . id = ss . a ;
-- check the case when the placeholder relates to an outer join and its
-- inner in the press field but actually uses only the outer side of the join
explain ( costs off )
SELECT q . val FROM b LEFT JOIN (
SELECT ( q1 . z IS NOT NULL ) AS val
FROM b LEFT JOIN (
SELECT ( t1 . b_id IS NOT NULL ) AS z FROM a t1 LEFT JOIN a t2 USING ( id )
) AS q1
ON true
) AS q ON true ;
CREATE TEMP TABLE parted_b ( id int PRIMARY KEY ) partition by range ( id ) ;
CREATE TEMP TABLE parted_b1 partition of parted_b for values from ( 0 ) to ( 10 ) ;
@ -2409,6 +2420,489 @@ select * from
select * from
int8_tbl x join ( int4_tbl x cross join int4_tbl y ( ff ) ) j on q1 = f1 ; -- ok
- -
-- test that semi- or inner self-joins on a unique column are removed
- -
-- enable only nestloop to get more predictable plans
set enable_hashjoin to off ;
set enable_mergejoin to off ;
create table sj ( a int unique , b int , c int unique ) ;
insert into sj values ( 1 , null , 2 ) , ( null , 2 , null ) , ( 2 , 1 , 1 ) ;
analyze sj ;
-- Trivial self-join case.
explain ( costs off )
select p . * from sj p , sj q where q . a = p . a and q . b = q . a - 1 ;
select p . * from sj p , sj q where q . a = p . a and q . b = q . a - 1 ;
-- Self-join removal performs after a subquery pull-up process and could remove
-- such kind of self-join too. Check this option.
explain ( costs off )
select * from sj p
where exists ( select * from sj q
where q . a = p . a and q . b < 10 ) ;
select * from sj p
where exists ( select * from sj q
where q . a = p . a and q . b < 10 ) ;
-- Don't remove self-join for the case of equality of two different unique columns.
explain ( costs off )
select * from sj t1 , sj t2 where t1 . a = t2 . c and t1 . b is not null ;
-- Ensure that relations with TABLESAMPLE clauses are not considered as
-- candidates to be removed
explain ( costs off )
select * from sj t1
join lateral
( select * from sj tablesample system ( t1 . b ) ) s
on t1 . a = s . a ;
-- Ensure that SJE does not form a self-referential lateral dependency
explain ( costs off )
select * from sj t1
left join lateral
( select t1 . a as t1a , * from sj t2 ) s
on true
where t1 . a = s . a ;
-- Degenerated case.
explain ( costs off )
select * from
( select a as x from sj where false ) as q1 ,
( select a as y from sj where false ) as q2
where q1 . x = q2 . y ;
-- We can't use a cross-EC generated self join qual because of current logic of
-- the generate_join_implied_equalities routine.
explain ( costs off )
select * from sj t1 , sj t2 where t1 . a = t1 . b and t1 . b = t2 . b and t2 . b = t2 . a ;
explain ( costs off )
select * from sj t1 , sj t2 , sj t3
where t1 . a = t1 . b and t1 . b = t2 . b and t2 . b = t2 . a and
t1 . b = t3 . b and t3 . b = t3 . a ;
-- Double self-join removal.
-- Use a condition on "b + 1", not on "b", for the second join, so that
-- the equivalence class is different from the first one, and we can
-- test the non-ec code path.
explain ( costs off )
select *
from sj t1
join sj t2 on t1 . a = t2 . a and t1 . b = t2 . b
join sj t3 on t2 . a = t3 . a and t2 . b + 1 = t3 . b + 1 ;
-- subselect that references the removed relation
explain ( costs off )
select t1 . a , ( select a from sj where a = t2 . a and a = t1 . a )
from sj t1 , sj t2
where t1 . a = t2 . a ;
-- self-join under outer join
explain ( costs off )
select * from sj x join sj y on x . a = y . a
left join int8_tbl z on x . a = z . q1 ;
explain ( costs off )
select * from sj x join sj y on x . a = y . a
left join int8_tbl z on y . a = z . q1 ;
explain ( costs off )
select * from (
select t1 . * , t2 . a as ax from sj t1 join sj t2
on ( t1 . a = t2 . a and t1 . c * t1 . c = t2 . c + 2 and t2 . b is null )
) as q1
left join
( select t3 . * from sj t3 , sj t4 where t3 . c = t4 . c ) as q2
on q1 . ax = q2 . a ;
-- Test that placeholders are updated correctly after join removal
explain ( costs off )
select * from ( values ( 1 ) ) x
left join ( select coalesce ( y . q1 , 1 ) from int8_tbl y
right join sj j1 inner join sj j2 on j1 . a = j2 . a
on true ) z
on true ;
-- Test that references to the removed rel in lateral subqueries are replaced
-- correctly after join removal
explain ( verbose , costs off )
select t3 . a from sj t1
join sj t2 on t1 . a = t2 . a
join lateral ( select t1 . a offset 0 ) t3 on true ;
explain ( verbose , costs off )
select t3 . a from sj t1
join sj t2 on t1 . a = t2 . a
join lateral ( select * from ( select t1 . a offset 0 ) offset 0 ) t3 on true ;
explain ( verbose , costs off )
select t4 . a from sj t1
join sj t2 on t1 . a = t2 . a
join lateral ( select t3 . a from sj t3 , ( select t1 . a ) offset 0 ) t4 on true ;
-- Check updating of semi_rhs_exprs links from upper-level semi join to
-- the removing relation
explain ( verbose , costs off )
select t1 . a from sj t1 where t1 . b in (
select t2 . b from sj t2 join sj t3 on t2 . c = t3 . c ) ;
- -
-- SJE corner case: uniqueness of an inner is [partially] derived from
-- baserestrictinfo clauses.
-- XXX: We really should allow SJE for these corner cases?
- -
INSERT INTO sj VALUES ( 3 , 1 , 3 ) ;
-- Don't remove SJ
EXPLAIN ( COSTS OFF )
SELECT * FROM sj j1 , sj j2 WHERE j1 . b = j2 . b AND j1 . a = 2 AND j2 . a = 3 ;
-- Return one row
SELECT * FROM sj j1 , sj j2 WHERE j1 . b = j2 . b AND j1 . a = 2 AND j2 . a = 3 ;
-- Remove SJ, define uniqueness by a constant
EXPLAIN ( COSTS OFF )
SELECT * FROM sj j1 , sj j2 WHERE j1 . b = j2 . b AND j1 . a = 2 AND j2 . a = 2 ;
-- Return one row
SELECT * FROM sj j1 , sj j2 WHERE j1 . b = j2 . b AND j1 . a = 2 AND j2 . a = 2 ;
-- Remove SJ, define uniqueness by a constant expression
EXPLAIN ( COSTS OFF )
SELECT * FROM sj j1 , sj j2
WHERE j1 . b = j2 . b
AND j1 . a = ( EXTRACT ( DOW FROM current_timestamp ( 0 ) ) / 15 + 3 ) : : int
AND ( EXTRACT ( DOW FROM current_timestamp ( 0 ) ) / 15 + 3 ) : : int = j2 . a ;
-- Return one row
SELECT * FROM sj j1 , sj j2
WHERE j1 . b = j2 . b
AND j1 . a = ( EXTRACT ( DOW FROM current_timestamp ( 0 ) ) / 15 + 3 ) : : int
AND ( EXTRACT ( DOW FROM current_timestamp ( 0 ) ) / 15 + 3 ) : : int = j2 . a ;
-- Remove SJ
EXPLAIN ( COSTS OFF )
SELECT * FROM sj j1 , sj j2 WHERE j1 . b = j2 . b AND j1 . a = 1 AND j2 . a = 1 ;
-- Return no rows
SELECT * FROM sj j1 , sj j2 WHERE j1 . b = j2 . b AND j1 . a = 1 AND j2 . a = 1 ;
-- Shuffle a clause. Remove SJ
EXPLAIN ( COSTS OFF )
SELECT * FROM sj j1 , sj j2 WHERE j1 . b = j2 . b AND 1 = j1 . a AND j2 . a = 1 ;
-- Return no rows
SELECT * FROM sj j1 , sj j2 WHERE j1 . b = j2 . b AND 1 = j1 . a AND j2 . a = 1 ;
-- SJE Corner case: a 'a.x=a.x' clause, have replaced with 'a.x IS NOT NULL'
-- after SJ elimination it shouldn't be a mergejoinable clause.
EXPLAIN ( COSTS OFF )
SELECT t4 . *
FROM ( SELECT t1 . * , t2 . a AS a1 FROM sj t1 , sj t2 WHERE t1 . b = t2 . b ) AS t3
JOIN sj t4 ON ( t4 . a = t3 . a ) WHERE t3 . a1 = 42 ;
SELECT t4 . *
FROM ( SELECT t1 . * , t2 . a AS a1 FROM sj t1 , sj t2 WHERE t1 . b = t2 . b ) AS t3
JOIN sj t4 ON ( t4 . a = t3 . a ) WHERE t3 . a1 = 42 ;
-- Functional index
CREATE UNIQUE INDEX sj_fn_idx ON sj ( ( a * a ) ) ;
-- Remove SJ
EXPLAIN ( COSTS OFF )
SELECT * FROM sj j1 , sj j2
WHERE j1 . b = j2 . b AND j1 . a * j1 . a = 1 AND j2 . a * j2 . a = 1 ;
-- Don't remove SJ
EXPLAIN ( COSTS OFF )
SELECT * FROM sj j1 , sj j2
WHERE j1 . b = j2 . b AND j1 . a * j1 . a = 1 AND j2 . a * j2 . a = 2 ;
-- Restriction contains expressions in both sides, Remove SJ.
EXPLAIN ( COSTS OFF )
SELECT * FROM sj j1 , sj j2
WHERE j1 . b = j2 . b
AND ( j1 . a * j1 . a ) = ( EXTRACT ( DOW FROM current_timestamp ( 0 ) ) / 15 + 3 ) : : int
AND ( EXTRACT ( DOW FROM current_timestamp ( 0 ) ) / 15 + 3 ) : : int = ( j2 . a * j2 . a ) ;
-- Empty set of rows should be returned
SELECT * FROM sj j1 , sj j2
WHERE j1 . b = j2 . b
AND ( j1 . a * j1 . a ) = ( EXTRACT ( DOW FROM current_timestamp ( 0 ) ) / 15 + 3 ) : : int
AND ( EXTRACT ( DOW FROM current_timestamp ( 0 ) ) / 15 + 3 ) : : int = ( j2 . a * j2 . a ) ;
-- Restriction contains volatile function - disable SJE feature.
EXPLAIN ( COSTS OFF )
SELECT * FROM sj j1 , sj j2
WHERE j1 . b = j2 . b
AND ( j1 . a * j1 . c / 3 ) = ( random ( ) / 3 + 3 ) : : int
AND ( random ( ) / 3 + 3 ) : : int = ( j2 . a * j2 . c / 3 ) ;
-- Return one row
SELECT * FROM sj j1 , sj j2
WHERE j1 . b = j2 . b
AND ( j1 . a * j1 . c / 3 ) = ( random ( ) / 3 + 3 ) : : int
AND ( random ( ) / 3 + 3 ) : : int = ( j2 . a * j2 . c / 3 ) ;
-- Multiple filters
CREATE UNIQUE INDEX sj_temp_idx1 ON sj ( a , b , c ) ;
-- Remove SJ
EXPLAIN ( COSTS OFF )
SELECT * FROM sj j1 , sj j2
WHERE j1 . b = j2 . b AND j1 . a = 2 AND j1 . c = 3 AND j2 . a = 2 AND 3 = j2 . c ;
-- Don't remove SJ
EXPLAIN ( COSTS OFF )
SELECT * FROM sj j1 , sj j2
WHERE j1 . b = j2 . b AND 2 = j1 . a AND j1 . c = 3 AND j2 . a = 1 AND 3 = j2 . c ;
CREATE UNIQUE INDEX sj_temp_idx ON sj ( a , b ) ;
-- Don't remove SJ
EXPLAIN ( COSTS OFF )
SELECT * FROM sj j1 , sj j2 WHERE j1 . b = j2 . b AND j1 . a = 2 ;
-- Don't remove SJ
EXPLAIN ( COSTS OFF )
SELECT * FROM sj j1 , sj j2 WHERE j1 . b = j2 . b AND 2 = j2 . a ;
-- Don't remove SJ
EXPLAIN ( COSTS OFF )
SELECT * FROM sj j1 , sj j2 WHERE j1 . b = j2 . b AND ( j1 . a = 1 OR j2 . a = 1 ) ;
DROP INDEX sj_fn_idx , sj_temp_idx1 , sj_temp_idx ;
-- Test that OR predicated are updated correctly after join removal
CREATE TABLE tab_with_flag ( id INT PRIMARY KEY , is_flag SMALLINT ) ;
CREATE INDEX idx_test_is_flag ON tab_with_flag ( is_flag ) ;
EXPLAIN ( COSTS OFF )
SELECT COUNT ( * ) FROM tab_with_flag
WHERE
( is_flag IS NULL OR is_flag = 0 )
AND id IN ( SELECT id FROM tab_with_flag WHERE id IN ( 2 , 3 ) ) ;
DROP TABLE tab_with_flag ;
-- HAVING clause
explain ( costs off )
select p . b from sj p join sj q on p . a = q . a group by p . b having sum ( p . a ) = 1 ;
-- update lateral references and range table entry reference
explain ( verbose , costs off )
select 1 from ( select x . * from sj x , sj y where x . a = y . a ) q ,
lateral generate_series ( 1 , q . a ) gs ( i ) ;
explain ( verbose , costs off )
select 1 from ( select y . * from sj x , sj y where x . a = y . a ) q ,
lateral generate_series ( 1 , q . a ) gs ( i ) ;
-- Test that a non-EC-derived join clause is processed correctly. Use an
-- outer join so that we can't form an EC.
explain ( costs off ) select * from sj p join sj q on p . a = q . a
left join sj r on p . a + q . a = r . a ;
-- FIXME this constant false filter doesn't look good. Should we merge
-- equivalence classes?
explain ( costs off )
select * from sj p , sj q where p . a = q . a and p . b = 1 and q . b = 2 ;
-- Check that attr_needed is updated correctly after self-join removal. In this
-- test, the join of j1 with j2 is removed. k1.b is required at either j1 or j2.
-- If this info is lost, join targetlist for (k1, k2) will not contain k1.b.
-- Use index scan for k1 so that we don't get 'b' from physical tlist used for
-- seqscan. Also disable reordering of joins because this test depends on a
-- particular join tree.
create table sk ( a int , b int ) ;
create index on sk ( a ) ;
set join_collapse_limit to 1 ;
set enable_seqscan to off ;
explain ( costs off ) select 1 from
( sk k1 join sk k2 on k1 . a = k2 . a )
join ( sj j1 join sj j2 on j1 . a = j2 . a ) on j1 . b = k1 . b ;
explain ( costs off ) select 1 from
( sk k1 join sk k2 on k1 . a = k2 . a )
join ( sj j1 join sj j2 on j1 . a = j2 . a ) on j2 . b = k1 . b ;
reset join_collapse_limit ;
reset enable_seqscan ;
-- Check that clauses from the join filter list is not lost on the self-join removal
CREATE TABLE emp1 ( id SERIAL PRIMARY KEY NOT NULL , code int ) ;
EXPLAIN ( VERBOSE , COSTS OFF )
SELECT * FROM emp1 e1 , emp1 e2 WHERE e1 . id = e2 . id AND e2 . code < > e1 . code ;
-- Shuffle self-joined relations. Only in the case of iterative deletion
-- attempts explains of these queries will be identical.
CREATE UNIQUE INDEX ON emp1 ( ( id * id ) ) ;
EXPLAIN ( COSTS OFF )
SELECT count ( * ) FROM emp1 c1 , emp1 c2 , emp1 c3
WHERE c1 . id = c2 . id AND c1 . id * c2 . id = c3 . id * c3 . id ;
EXPLAIN ( COSTS OFF )
SELECT count ( * ) FROM emp1 c1 , emp1 c2 , emp1 c3
WHERE c1 . id = c3 . id AND c1 . id * c3 . id = c2 . id * c2 . id ;
EXPLAIN ( COSTS OFF )
SELECT count ( * ) FROM emp1 c1 , emp1 c2 , emp1 c3
WHERE c3 . id = c2 . id AND c3 . id * c2 . id = c1 . id * c1 . id ;
-- Check the usage of a parse tree by the set operations (bug #18170)
EXPLAIN ( COSTS OFF )
SELECT c1 . code FROM emp1 c1 LEFT JOIN emp1 c2 ON c1 . id = c2 . id
WHERE c2 . id IS NOT NULL
EXCEPT ALL
SELECT c3 . code FROM emp1 c3 ;
-- Check that SJE removes references from PHVs correctly
explain ( costs off )
select * from emp1 t1 left join
( select coalesce ( t3 . code , 1 ) from emp1 t2
left join ( emp1 t3 join emp1 t4 on t3 . id = t4 . id )
on true )
on true ;
-- Check that SJE removes the whole PHVs correctly
explain ( verbose , costs off )
select 1 from emp1 t1 left join
( ( select 1 as x , * from emp1 t2 ) s1 inner join
( select * from emp1 t3 ) s2 on s1 . id = s2 . id )
on true
where s1 . x = 1 ;
-- Check that PHVs do not impose any constraints on removing self joins
explain ( verbose , costs off )
select * from emp1 t1 join emp1 t2 on t1 . id = t2 . id left join
lateral ( select t1 . id as t1id , * from generate_series ( 1 , 1 ) t3 ) s on true ;
explain ( verbose , costs off )
select * from generate_series ( 1 , 10 ) t1 ( id ) left join
lateral ( select t1 . id as t1id , t2 . id from emp1 t2 join emp1 t3 on t2 . id = t3 . id )
on true ;
-- Check that SJE replaces join clauses involving the removed rel correctly
explain ( costs off )
select * from emp1 t1
inner join emp1 t2 on t1 . id = t2 . id
left join emp1 t3 on t1 . id > 1 and t1 . id < 2 ;
-- Check that SJE doesn't replace the target relation
EXPLAIN ( COSTS OFF )
WITH t1 AS ( SELECT * FROM emp1 )
UPDATE emp1 SET code = t1 . code + 1 FROM t1
WHERE t1 . id = emp1 . id RETURNING emp1 . id , emp1 . code , t1 . code ;
INSERT INTO emp1 VALUES ( 1 , 1 ) , ( 2 , 1 ) ;
WITH t1 AS ( SELECT * FROM emp1 )
UPDATE emp1 SET code = t1 . code + 1 FROM t1
WHERE t1 . id = emp1 . id RETURNING emp1 . id , emp1 . code , t1 . code ;
TRUNCATE emp1 ;
EXPLAIN ( COSTS OFF )
UPDATE sj sq SET b = 1 FROM sj as sz WHERE sq . a = sz . a ;
CREATE RULE sj_del_rule AS ON DELETE TO sj
DO INSTEAD
UPDATE sj SET a = 1 WHERE a = old . a ;
EXPLAIN ( COSTS OFF ) DELETE FROM sj ;
DROP RULE sj_del_rule ON sj CASCADE ;
-- Check that SJE does not mistakenly omit qual clauses (bug #18187)
insert into emp1 values ( 1 , 1 ) ;
explain ( costs off )
select 1 from emp1 full join
( select * from emp1 t1 join
emp1 t2 join emp1 t3 on t2 . id = t3 . id
on true
where false ) s on true
where false ;
select 1 from emp1 full join
( select * from emp1 t1 join
emp1 t2 join emp1 t3 on t2 . id = t3 . id
on true
where false ) s on true
where false ;
-- Check that SJE does not mistakenly re-use knowledge of relation uniqueness
-- made with different set of quals
insert into emp1 values ( 2 , 1 ) ;
explain ( costs off )
select * from emp1 t1 where exists ( select * from emp1 t2
where t2 . id = t1 . code and t2 . code > 0 ) ;
select * from emp1 t1 where exists ( select * from emp1 t2
where t2 . id = t1 . code and t2 . code > 0 ) ;
-- We can remove the join even if we find the join can't duplicate rows and
-- the base quals of each side are different. In the following case we end up
-- moving quals over to s1 to make it so it can't match any rows.
create table sl ( a int , b int , c int ) ;
create unique index on sl ( a , b ) ;
vacuum analyze sl ;
-- Both sides are unique, but base quals are different
explain ( costs off )
select * from sl t1 , sl t2 where t1 . a = t2 . a and t1 . b = 1 and t2 . b = 2 ;
-- Check NullTest in baserestrictinfo list
explain ( costs off )
select * from sl t1 , sl t2
where t1 . a = t2 . a and t1 . b = 1 and t2 . b = 2
and t1 . c IS NOT NULL and t2 . c IS NOT NULL
and t2 . b IS NOT NULL and t1 . b IS NOT NULL
and t1 . a IS NOT NULL and t2 . a IS NOT NULL ;
explain ( verbose , costs off )
select * from sl t1 , sl t2
where t1 . b = t2 . b and t2 . a = 3 and t1 . a = 3
and t1 . c IS NOT NULL and t2 . c IS NOT NULL
and t2 . b IS NOT NULL and t1 . b IS NOT NULL
and t1 . a IS NOT NULL and t2 . a IS NOT NULL ;
-- Join qual isn't mergejoinable, but inner is unique.
EXPLAIN ( COSTS OFF )
SELECT n2 . a FROM sj n1 , sj n2 WHERE n1 . a < > n2 . a AND n2 . a = 1 ;
EXPLAIN ( COSTS OFF )
SELECT * FROM
( SELECT n2 . a FROM sj n1 , sj n2 WHERE n1 . a < > n2 . a ) q0 , sl
WHERE q0 . a = 1 ;
-- Check optimization disabling if it will violate special join conditions.
-- Two identical joined relations satisfies self join removal conditions but
-- stay in different special join infos.
CREATE TABLE sj_t1 ( id serial , a int ) ;
CREATE TABLE sj_t2 ( id serial , a int ) ;
CREATE TABLE sj_t3 ( id serial , a int ) ;
CREATE TABLE sj_t4 ( id serial , a int ) ;
CREATE UNIQUE INDEX ON sj_t3 USING btree ( a , id ) ;
CREATE UNIQUE INDEX ON sj_t2 USING btree ( id ) ;
EXPLAIN ( COSTS OFF )
SELECT * FROM sj_t1
JOIN (
SELECT sj_t2 . id AS id FROM sj_t2
WHERE EXISTS
(
SELECT TRUE FROM sj_t3 , sj_t4 WHERE sj_t3 . a = 1 AND sj_t3 . id = sj_t2 . id
)
) t2t3t4
ON sj_t1 . id = t2t3t4 . id
JOIN (
SELECT sj_t2 . id AS id FROM sj_t2
WHERE EXISTS
(
SELECT TRUE FROM sj_t3 , sj_t4 WHERE sj_t3 . a = 1 AND sj_t3 . id = sj_t2 . id
)
) _t2t3t4
ON sj_t1 . id = _t2t3t4 . id ;
- -
-- Test RowMarks-related code
- -
-- Both sides have explicit LockRows marks
EXPLAIN ( COSTS OFF )
SELECT a1 . a FROM sj a1 , sj a2 WHERE ( a1 . a = a2 . a ) FOR UPDATE ;
reset enable_hashjoin ;
reset enable_mergejoin ;
- -
-- Test hints given on incorrect column references are useful
- -