|
|
|
|
@ -2321,476 +2321,6 @@ 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; |
|
|
|
|
|
|
|
|
|
-- 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 Lateral links from top-level query to the removing relation |
|
|
|
|
explain (COSTS OFF) |
|
|
|
|
SELECT * FROM pg_am am WHERE am.amname IN ( |
|
|
|
|
SELECT c1.relname AS relname |
|
|
|
|
FROM pg_class c1 |
|
|
|
|
JOIN pg_class c2 |
|
|
|
|
ON c1.oid=c2.oid AND c1.oid < 10 |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
-- |
|
|
|
|
-- 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 |
|
|
|
|
-- |
|
|
|
|
|