mirror of
https://github.com/zebrajr/postgres.git
synced 2026-01-15 12:15:21 +00:00
When looking up statistical data about an expression, we failed to look through PlaceHolderVar nodes, treating them as opaque. This could prevent us from matching an expression to base columns, index expressions, or extended statistics, as examine_variable() relies on strict structural matching. As a result, queries involving PlaceHolderVar nodes often fell back to default selectivity estimates, potentially leading to poor plan choices. This patch updates examine_variable() to strip PlaceHolderVars before analysis. This is safe during estimation because PlaceHolderVars are transparent for the purpose of statistics lookup: they do not alter the value distribution of the underlying expression. To minimize performance overhead on this hot path, a lightweight walker first checks for the presence of PlaceHolderVars. The more expensive mutator is invoked only when necessary. There is one ensuing plan change in the regression tests, which is expected and demonstrates the fix: the rowcount estimate becomes much more accurate with this patch. Back-patch to v18. Although this issue exists before that, changes in this version made it common enough to notice. Given the lack of field reports for older versions, I am not back-patching further. Reported-by: Haowu Ge <gehaowu@bitmoe.com> Author: Richard Guo <guofenglinux@gmail.com> Discussion: https://postgr.es/m/62af586c-c270-44f3-9c5e-02c81d537e3d.gehaowu@bitmoe.com Backpatch-through: 18
3793 lines
111 KiB
PL/PgSQL
3793 lines
111 KiB
PL/PgSQL
--
|
|
-- JOIN
|
|
-- Test JOIN clauses
|
|
--
|
|
|
|
CREATE TABLE J1_TBL (
|
|
i integer,
|
|
j integer,
|
|
t text
|
|
);
|
|
|
|
CREATE TABLE J2_TBL (
|
|
i integer,
|
|
k integer
|
|
);
|
|
|
|
|
|
INSERT INTO J1_TBL VALUES (1, 4, 'one');
|
|
INSERT INTO J1_TBL VALUES (2, 3, 'two');
|
|
INSERT INTO J1_TBL VALUES (3, 2, 'three');
|
|
INSERT INTO J1_TBL VALUES (4, 1, 'four');
|
|
INSERT INTO J1_TBL VALUES (5, 0, 'five');
|
|
INSERT INTO J1_TBL VALUES (6, 6, 'six');
|
|
INSERT INTO J1_TBL VALUES (7, 7, 'seven');
|
|
INSERT INTO J1_TBL VALUES (8, 8, 'eight');
|
|
INSERT INTO J1_TBL VALUES (0, NULL, 'zero');
|
|
INSERT INTO J1_TBL VALUES (NULL, NULL, 'null');
|
|
INSERT INTO J1_TBL VALUES (NULL, 0, 'zero');
|
|
|
|
INSERT INTO J2_TBL VALUES (1, -1);
|
|
INSERT INTO J2_TBL VALUES (2, 2);
|
|
INSERT INTO J2_TBL VALUES (3, -3);
|
|
INSERT INTO J2_TBL VALUES (2, 4);
|
|
INSERT INTO J2_TBL VALUES (5, -5);
|
|
INSERT INTO J2_TBL VALUES (5, -5);
|
|
INSERT INTO J2_TBL VALUES (0, NULL);
|
|
INSERT INTO J2_TBL VALUES (NULL, NULL);
|
|
INSERT INTO J2_TBL VALUES (NULL, 0);
|
|
|
|
-- useful in some tests below
|
|
create temp table onerow();
|
|
insert into onerow default values;
|
|
analyze onerow;
|
|
|
|
|
|
--
|
|
-- CORRELATION NAMES
|
|
-- Make sure that table/column aliases are supported
|
|
-- before diving into more complex join syntax.
|
|
--
|
|
|
|
SELECT *
|
|
FROM J1_TBL AS tx;
|
|
|
|
SELECT *
|
|
FROM J1_TBL tx;
|
|
|
|
SELECT *
|
|
FROM J1_TBL AS t1 (a, b, c);
|
|
|
|
SELECT *
|
|
FROM J1_TBL t1 (a, b, c);
|
|
|
|
SELECT *
|
|
FROM J1_TBL t1 (a, b, c), J2_TBL t2 (d, e);
|
|
|
|
SELECT t1.a, t2.e
|
|
FROM J1_TBL t1 (a, b, c), J2_TBL t2 (d, e)
|
|
WHERE t1.a = t2.d;
|
|
|
|
|
|
--
|
|
-- CROSS JOIN
|
|
-- Qualifications are not allowed on cross joins,
|
|
-- which degenerate into a standard unqualified inner join.
|
|
--
|
|
|
|
SELECT *
|
|
FROM J1_TBL CROSS JOIN J2_TBL;
|
|
|
|
-- ambiguous column
|
|
SELECT i, k, t
|
|
FROM J1_TBL CROSS JOIN J2_TBL;
|
|
|
|
-- resolve previous ambiguity by specifying the table name
|
|
SELECT t1.i, k, t
|
|
FROM J1_TBL t1 CROSS JOIN J2_TBL t2;
|
|
|
|
SELECT ii, tt, kk
|
|
FROM (J1_TBL CROSS JOIN J2_TBL)
|
|
AS tx (ii, jj, tt, ii2, kk);
|
|
|
|
SELECT tx.ii, tx.jj, tx.kk
|
|
FROM (J1_TBL t1 (a, b, c) CROSS JOIN J2_TBL t2 (d, e))
|
|
AS tx (ii, jj, tt, ii2, kk);
|
|
|
|
SELECT *
|
|
FROM J1_TBL CROSS JOIN J2_TBL a CROSS JOIN J2_TBL b;
|
|
|
|
|
|
--
|
|
--
|
|
-- Inner joins (equi-joins)
|
|
--
|
|
--
|
|
|
|
--
|
|
-- Inner joins (equi-joins) with USING clause
|
|
-- The USING syntax changes the shape of the resulting table
|
|
-- by including a column in the USING clause only once in the result.
|
|
--
|
|
|
|
-- Inner equi-join on specified column
|
|
SELECT *
|
|
FROM J1_TBL INNER JOIN J2_TBL USING (i);
|
|
|
|
-- Same as above, slightly different syntax
|
|
SELECT *
|
|
FROM J1_TBL JOIN J2_TBL USING (i);
|
|
|
|
SELECT *
|
|
FROM J1_TBL t1 (a, b, c) JOIN J2_TBL t2 (a, d) USING (a)
|
|
ORDER BY a, d;
|
|
|
|
SELECT *
|
|
FROM J1_TBL t1 (a, b, c) JOIN J2_TBL t2 (a, b) USING (b)
|
|
ORDER BY b, t1.a;
|
|
|
|
-- test join using aliases
|
|
SELECT * FROM J1_TBL JOIN J2_TBL USING (i) WHERE J1_TBL.t = 'one'; -- ok
|
|
SELECT * FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE J1_TBL.t = 'one'; -- ok
|
|
SELECT * FROM (J1_TBL JOIN J2_TBL USING (i)) AS x WHERE J1_TBL.t = 'one'; -- error
|
|
SELECT * FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE x.i = 1; -- ok
|
|
SELECT * FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE x.t = 'one'; -- error
|
|
SELECT * FROM (J1_TBL JOIN J2_TBL USING (i) AS x) AS xx WHERE x.i = 1; -- error (XXX could use better hint)
|
|
SELECT * FROM J1_TBL a1 JOIN J2_TBL a2 USING (i) AS a1; -- error
|
|
SELECT x.* FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE J1_TBL.t = 'one';
|
|
SELECT ROW(x.*) FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE J1_TBL.t = 'one';
|
|
SELECT row_to_json(x.*) FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE J1_TBL.t = 'one';
|
|
|
|
--
|
|
-- NATURAL JOIN
|
|
-- Inner equi-join on all columns with the same name
|
|
--
|
|
|
|
SELECT *
|
|
FROM J1_TBL NATURAL JOIN J2_TBL;
|
|
|
|
SELECT *
|
|
FROM J1_TBL t1 (a, b, c) NATURAL JOIN J2_TBL t2 (a, d);
|
|
|
|
SELECT *
|
|
FROM J1_TBL t1 (a, b, c) NATURAL JOIN J2_TBL t2 (d, a);
|
|
|
|
-- mismatch number of columns
|
|
-- currently, Postgres will fill in with underlying names
|
|
SELECT *
|
|
FROM J1_TBL t1 (a, b) NATURAL JOIN J2_TBL t2 (a);
|
|
|
|
|
|
--
|
|
-- Inner joins (equi-joins)
|
|
--
|
|
|
|
SELECT *
|
|
FROM J1_TBL JOIN J2_TBL ON (J1_TBL.i = J2_TBL.i);
|
|
|
|
SELECT *
|
|
FROM J1_TBL JOIN J2_TBL ON (J1_TBL.i = J2_TBL.k);
|
|
|
|
|
|
--
|
|
-- Non-equi-joins
|
|
--
|
|
|
|
SELECT *
|
|
FROM J1_TBL JOIN J2_TBL ON (J1_TBL.i <= J2_TBL.k);
|
|
|
|
|
|
--
|
|
-- Outer joins
|
|
-- Note that OUTER is a noise word
|
|
--
|
|
|
|
SELECT *
|
|
FROM J1_TBL LEFT OUTER JOIN J2_TBL USING (i)
|
|
ORDER BY i, k, t;
|
|
|
|
SELECT *
|
|
FROM J1_TBL LEFT JOIN J2_TBL USING (i)
|
|
ORDER BY i, k, t;
|
|
|
|
SELECT *
|
|
FROM J1_TBL RIGHT OUTER JOIN J2_TBL USING (i);
|
|
|
|
SELECT *
|
|
FROM J1_TBL RIGHT JOIN J2_TBL USING (i);
|
|
|
|
SELECT *
|
|
FROM J1_TBL FULL OUTER JOIN J2_TBL USING (i)
|
|
ORDER BY i, k, t;
|
|
|
|
SELECT *
|
|
FROM J1_TBL FULL JOIN J2_TBL USING (i)
|
|
ORDER BY i, k, t;
|
|
|
|
SELECT *
|
|
FROM J1_TBL LEFT JOIN J2_TBL USING (i) WHERE (k = 1);
|
|
|
|
SELECT *
|
|
FROM J1_TBL LEFT JOIN J2_TBL USING (i) WHERE (i = 1);
|
|
|
|
--
|
|
-- semijoin selectivity for <>
|
|
--
|
|
explain (costs off)
|
|
select * from tenk1 a, tenk1 b
|
|
where exists(select * from tenk1 c
|
|
where b.twothousand = c.twothousand and b.fivethous <> c.fivethous)
|
|
and a.tenthous = b.tenthous and a.tenthous < 5000;
|
|
|
|
|
|
--
|
|
-- More complicated constructs
|
|
--
|
|
|
|
--
|
|
-- Multiway full join
|
|
--
|
|
|
|
CREATE TABLE t1 (name TEXT, n INTEGER);
|
|
CREATE TABLE t2 (name TEXT, n INTEGER);
|
|
CREATE TABLE t3 (name TEXT, n INTEGER);
|
|
|
|
INSERT INTO t1 VALUES ( 'bb', 11 );
|
|
INSERT INTO t2 VALUES ( 'bb', 12 );
|
|
INSERT INTO t2 VALUES ( 'cc', 22 );
|
|
INSERT INTO t2 VALUES ( 'ee', 42 );
|
|
INSERT INTO t3 VALUES ( 'bb', 13 );
|
|
INSERT INTO t3 VALUES ( 'cc', 23 );
|
|
INSERT INTO t3 VALUES ( 'dd', 33 );
|
|
|
|
SELECT * FROM t1 FULL JOIN t2 USING (name) FULL JOIN t3 USING (name);
|
|
|
|
--
|
|
-- Test interactions of join syntax and subqueries
|
|
--
|
|
|
|
-- Basic cases (we expect planner to pull up the subquery here)
|
|
SELECT * FROM
|
|
(SELECT * FROM t2) as s2
|
|
INNER JOIN
|
|
(SELECT * FROM t3) s3
|
|
USING (name);
|
|
|
|
SELECT * FROM
|
|
(SELECT * FROM t2) as s2
|
|
LEFT JOIN
|
|
(SELECT * FROM t3) s3
|
|
USING (name);
|
|
|
|
SELECT * FROM
|
|
(SELECT * FROM t2) as s2
|
|
FULL JOIN
|
|
(SELECT * FROM t3) s3
|
|
USING (name);
|
|
|
|
-- Cases with non-nullable expressions in subquery results;
|
|
-- make sure these go to null as expected
|
|
SELECT * FROM
|
|
(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2
|
|
NATURAL INNER JOIN
|
|
(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3;
|
|
|
|
SELECT * FROM
|
|
(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2
|
|
NATURAL LEFT JOIN
|
|
(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3;
|
|
|
|
SELECT * FROM
|
|
(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2
|
|
NATURAL FULL JOIN
|
|
(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3;
|
|
|
|
SELECT * FROM
|
|
(SELECT name, n as s1_n, 1 as s1_1 FROM t1) as s1
|
|
NATURAL INNER JOIN
|
|
(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2
|
|
NATURAL INNER JOIN
|
|
(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3;
|
|
|
|
SELECT * FROM
|
|
(SELECT name, n as s1_n, 1 as s1_1 FROM t1) as s1
|
|
NATURAL FULL JOIN
|
|
(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2
|
|
NATURAL FULL JOIN
|
|
(SELECT name, n as s3_n, 3 as s3_2 FROM t3) s3;
|
|
|
|
SELECT * FROM
|
|
(SELECT name, n as s1_n FROM t1) as s1
|
|
NATURAL FULL JOIN
|
|
(SELECT * FROM
|
|
(SELECT name, n as s2_n FROM t2) as s2
|
|
NATURAL FULL JOIN
|
|
(SELECT name, n as s3_n FROM t3) as s3
|
|
) ss2;
|
|
|
|
SELECT * FROM
|
|
(SELECT name, n as s1_n FROM t1) as s1
|
|
NATURAL FULL JOIN
|
|
(SELECT * FROM
|
|
(SELECT name, n as s2_n, 2 as s2_2 FROM t2) as s2
|
|
NATURAL FULL JOIN
|
|
(SELECT name, n as s3_n FROM t3) as s3
|
|
) ss2;
|
|
|
|
-- Constants as join keys can also be problematic
|
|
SELECT * FROM
|
|
(SELECT name, n as s1_n FROM t1) as s1
|
|
FULL JOIN
|
|
(SELECT name, 2 as s2_n FROM t2) as s2
|
|
ON (s1_n = s2_n);
|
|
|
|
|
|
-- Test for propagation of nullability constraints into sub-joins
|
|
|
|
create temp table x (x1 int, x2 int);
|
|
insert into x values (1,11);
|
|
insert into x values (2,22);
|
|
insert into x values (3,null);
|
|
insert into x values (4,44);
|
|
insert into x values (5,null);
|
|
|
|
create temp table y (y1 int, y2 int);
|
|
insert into y values (1,111);
|
|
insert into y values (2,222);
|
|
insert into y values (3,333);
|
|
insert into y values (4,null);
|
|
|
|
select * from x;
|
|
select * from y;
|
|
|
|
select * from x left join y on (x1 = y1 and x2 is not null);
|
|
select * from x left join y on (x1 = y1 and y2 is not null);
|
|
|
|
select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2)
|
|
on (x1 = xx1);
|
|
select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2)
|
|
on (x1 = xx1 and x2 is not null);
|
|
select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2)
|
|
on (x1 = xx1 and y2 is not null);
|
|
select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2)
|
|
on (x1 = xx1 and xx2 is not null);
|
|
-- these should NOT give the same answers as above
|
|
select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2)
|
|
on (x1 = xx1) where (x2 is not null);
|
|
select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2)
|
|
on (x1 = xx1) where (y2 is not null);
|
|
select * from (x left join y on (x1 = y1)) left join x xx(xx1,xx2)
|
|
on (x1 = xx1) where (xx2 is not null);
|
|
|
|
--
|
|
-- regression test: check for bug with propagation of implied equality
|
|
-- to outside an IN
|
|
--
|
|
select count(*) from tenk1 a where unique1 in
|
|
(select unique1 from tenk1 b join tenk1 c using (unique1)
|
|
where b.unique2 = 42);
|
|
|
|
--
|
|
-- regression test: check for failure to generate a plan with multiple
|
|
-- degenerate IN clauses
|
|
--
|
|
select count(*) from tenk1 x where
|
|
x.unique1 in (select a.f1 from int4_tbl a,float8_tbl b where a.f1=b.f1) and
|
|
x.unique1 = 0 and
|
|
x.unique1 in (select aa.f1 from int4_tbl aa,float8_tbl bb where aa.f1=bb.f1);
|
|
|
|
-- try that with GEQO too
|
|
begin;
|
|
set geqo = on;
|
|
set geqo_threshold = 2;
|
|
select count(*) from tenk1 x where
|
|
x.unique1 in (select a.f1 from int4_tbl a,float8_tbl b where a.f1=b.f1) and
|
|
x.unique1 = 0 and
|
|
x.unique1 in (select aa.f1 from int4_tbl aa,float8_tbl bb where aa.f1=bb.f1);
|
|
rollback;
|
|
|
|
--
|
|
-- regression test: be sure we cope with proven-dummy append rels
|
|
--
|
|
explain (costs off)
|
|
select aa, bb, unique1, unique1
|
|
from tenk1 right join b_star on aa = unique1
|
|
where bb < bb and bb is null;
|
|
|
|
select aa, bb, unique1, unique1
|
|
from tenk1 right join b_star on aa = unique1
|
|
where bb < bb and bb is null;
|
|
|
|
--
|
|
-- regression test: check handling of empty-FROM subquery underneath outer join
|
|
--
|
|
explain (costs off)
|
|
select * from int8_tbl i1 left join (int8_tbl i2 join
|
|
(select 123 as x) ss on i2.q1 = x) on i1.q2 = i2.q2
|
|
order by 1, 2;
|
|
|
|
select * from int8_tbl i1 left join (int8_tbl i2 join
|
|
(select 123 as x) ss on i2.q1 = x) on i1.q2 = i2.q2
|
|
order by 1, 2;
|
|
|
|
--
|
|
-- regression test: check a case where join_clause_is_movable_into()
|
|
-- used to give an imprecise result, causing an assertion failure
|
|
--
|
|
select count(*)
|
|
from
|
|
(select t3.tenthous as x1, coalesce(t1.stringu1, t2.stringu1) as x2
|
|
from tenk1 t1
|
|
left join tenk1 t2 on t1.unique1 = t2.unique1
|
|
join tenk1 t3 on t1.unique2 = t3.unique2) ss,
|
|
tenk1 t4,
|
|
tenk1 t5
|
|
where t4.thousand = t5.unique1 and ss.x1 = t4.tenthous and ss.x2 = t5.stringu1;
|
|
|
|
--
|
|
-- regression test: check a case where we formerly missed including an EC
|
|
-- enforcement clause because it was expected to be handled at scan level
|
|
--
|
|
explain (costs off)
|
|
select a.f1, b.f1, t.thousand, t.tenthous from
|
|
tenk1 t,
|
|
(select sum(f1)+1 as f1 from int4_tbl i4a) a,
|
|
(select sum(f1) as f1 from int4_tbl i4b) b
|
|
where b.f1 = t.thousand and a.f1 = b.f1 and (a.f1+b.f1+999) = t.tenthous;
|
|
|
|
select a.f1, b.f1, t.thousand, t.tenthous from
|
|
tenk1 t,
|
|
(select sum(f1)+1 as f1 from int4_tbl i4a) a,
|
|
(select sum(f1) as f1 from int4_tbl i4b) b
|
|
where b.f1 = t.thousand and a.f1 = b.f1 and (a.f1+b.f1+999) = t.tenthous;
|
|
|
|
--
|
|
-- Test hash joins with multiple hash keys and subplans.
|
|
--
|
|
|
|
-- First ensure we get a hash join with multiple hash keys.
|
|
explain (costs off)
|
|
select t1.unique1,t2.unique1 from tenk1 t1
|
|
inner join tenk1 t2 on t1.two = t2.two
|
|
and t1.unique1 = (select min(unique1) from tenk1
|
|
where t2.unique1=unique1)
|
|
where t1.unique1 < 10 and t2.unique1 < 10
|
|
order by t1.unique1;
|
|
|
|
-- Ensure we get the expected result
|
|
select t1.unique1,t2.unique1 from tenk1 t1
|
|
inner join tenk1 t2 on t1.two = t2.two
|
|
and t1.unique1 = (select min(unique1) from tenk1
|
|
where t2.unique1=unique1)
|
|
where t1.unique1 < 10 and t2.unique1 < 10
|
|
order by t1.unique1;
|
|
|
|
--
|
|
-- checks for correct handling of quals in multiway outer joins
|
|
--
|
|
explain (costs off)
|
|
select t1.f1
|
|
from int4_tbl t1, int4_tbl t2
|
|
left join int4_tbl t3 on t3.f1 > 0
|
|
left join int4_tbl t4 on t3.f1 > 1
|
|
where t4.f1 is null;
|
|
|
|
select t1.f1
|
|
from int4_tbl t1, int4_tbl t2
|
|
left join int4_tbl t3 on t3.f1 > 0
|
|
left join int4_tbl t4 on t3.f1 > 1
|
|
where t4.f1 is null;
|
|
|
|
explain (costs off)
|
|
select *
|
|
from int4_tbl t1 left join int4_tbl t2 on true
|
|
left join int4_tbl t3 on t2.f1 > 0
|
|
left join int4_tbl t4 on t3.f1 > 0;
|
|
|
|
explain (costs off)
|
|
select * from onek t1
|
|
left join onek t2 on t1.unique1 = t2.unique1
|
|
left join onek t3 on t2.unique1 != t3.unique1
|
|
left join onek t4 on t3.unique1 = t4.unique1;
|
|
|
|
explain (costs off)
|
|
select * from int4_tbl t1
|
|
left join (select now() from int4_tbl t2
|
|
left join int4_tbl t3 on t2.f1 = t3.f1
|
|
left join int4_tbl t4 on t3.f1 = t4.f1) s on true
|
|
inner join int4_tbl t5 on true;
|
|
|
|
explain (costs off)
|
|
select * from int4_tbl t1
|
|
left join int4_tbl t2 on true
|
|
left join int4_tbl t3 on true
|
|
left join int4_tbl t4 on t2.f1 = t3.f1;
|
|
|
|
explain (costs off)
|
|
select * from int4_tbl t1
|
|
left join int4_tbl t2 on true
|
|
left join int4_tbl t3 on t2.f1 = t3.f1
|
|
left join int4_tbl t4 on t3.f1 != t4.f1;
|
|
|
|
explain (costs off)
|
|
select * from int4_tbl t1
|
|
left join (int4_tbl t2 left join int4_tbl t3 on t2.f1 > 0) on t2.f1 > 1
|
|
left join int4_tbl t4 on t2.f1 > 2 and t3.f1 > 3
|
|
where t1.f1 = coalesce(t2.f1, 1);
|
|
|
|
explain (costs off)
|
|
select * from int4_tbl t1
|
|
left join ((select t2.f1 from int4_tbl t2
|
|
left join int4_tbl t3 on t2.f1 > 0
|
|
where t3.f1 is null) s
|
|
left join tenk1 t4 on s.f1 > 1)
|
|
on s.f1 = t1.f1;
|
|
|
|
explain (costs off)
|
|
select * from int4_tbl t1
|
|
left join ((select t2.f1 from int4_tbl t2
|
|
left join int4_tbl t3 on t2.f1 > 0
|
|
where t2.f1 <> coalesce(t3.f1, -1)) s
|
|
left join tenk1 t4 on s.f1 > 1)
|
|
on s.f1 = t1.f1;
|
|
|
|
explain (costs off)
|
|
select * from onek t1
|
|
left join onek t2 on t1.unique1 = t2.unique1
|
|
left join onek t3 on t2.unique1 = t3.unique1
|
|
left join onek t4 on t3.unique1 = t4.unique1 and t2.unique2 = t4.unique2;
|
|
|
|
explain (costs off)
|
|
select * from int8_tbl t1 left join
|
|
(int8_tbl t2 left join int8_tbl t3 full join int8_tbl t4 on false on false)
|
|
left join int8_tbl t5 on t2.q1 = t5.q1
|
|
on t2.q2 = 123;
|
|
|
|
explain (costs off)
|
|
select * from int8_tbl t1
|
|
left join int8_tbl t2 on true
|
|
left join lateral
|
|
(select * from int8_tbl t3 where t3.q1 = t2.q1 offset 0) s
|
|
on t2.q1 = 1;
|
|
|
|
explain (costs off)
|
|
select * from int8_tbl t1
|
|
left join int8_tbl t2 on true
|
|
left join lateral
|
|
(select * from generate_series(t2.q1, 100)) s
|
|
on t2.q1 = 1;
|
|
|
|
explain (costs off)
|
|
select * from int8_tbl t1
|
|
left join int8_tbl t2 on true
|
|
left join lateral
|
|
(select t2.q1 from int8_tbl t3) s
|
|
on t2.q1 = 1;
|
|
|
|
explain (costs off)
|
|
select * from onek t1
|
|
left join onek t2 on true
|
|
left join lateral
|
|
(select * from onek t3 where t3.two = t2.two offset 0) s
|
|
on t2.unique1 = 1;
|
|
|
|
--
|
|
-- check a case where we formerly got confused by conflicting sort orders
|
|
-- in redundant merge join path keys
|
|
--
|
|
explain (costs off)
|
|
select * from
|
|
j1_tbl full join
|
|
(select * from j2_tbl order by j2_tbl.i desc, j2_tbl.k asc) j2_tbl
|
|
on j1_tbl.i = j2_tbl.i and j1_tbl.i = j2_tbl.k;
|
|
|
|
select * from
|
|
j1_tbl full join
|
|
(select * from j2_tbl order by j2_tbl.i desc, j2_tbl.k asc) j2_tbl
|
|
on j1_tbl.i = j2_tbl.i and j1_tbl.i = j2_tbl.k;
|
|
|
|
--
|
|
-- a different check for handling of redundant sort keys in merge joins
|
|
--
|
|
explain (costs off)
|
|
select count(*) from
|
|
(select * from tenk1 x order by x.thousand, x.twothousand, x.fivethous) x
|
|
left join
|
|
(select * from tenk1 y order by y.unique2) y
|
|
on x.thousand = y.unique2 and x.twothousand = y.hundred and x.fivethous = y.unique2;
|
|
|
|
select count(*) from
|
|
(select * from tenk1 x order by x.thousand, x.twothousand, x.fivethous) x
|
|
left join
|
|
(select * from tenk1 y order by y.unique2) y
|
|
on x.thousand = y.unique2 and x.twothousand = y.hundred and x.fivethous = y.unique2;
|
|
|
|
set enable_hashjoin = 0;
|
|
set enable_nestloop = 0;
|
|
set enable_hashagg = 0;
|
|
|
|
--
|
|
-- Check that we use the pathkeys from a prefix of the group by / order by
|
|
-- clause for the join pathkeys when that prefix covers all join quals. We
|
|
-- expect this to lead to an incremental sort for the group by / order by.
|
|
--
|
|
explain (costs off)
|
|
select x.thousand, x.twothousand, count(*)
|
|
from tenk1 x inner join tenk1 y on x.thousand = y.thousand
|
|
group by x.thousand, x.twothousand
|
|
order by x.thousand desc, x.twothousand;
|
|
|
|
reset enable_hashagg;
|
|
reset enable_nestloop;
|
|
reset enable_hashjoin;
|
|
|
|
--
|
|
-- Clean up
|
|
--
|
|
|
|
DROP TABLE t1;
|
|
DROP TABLE t2;
|
|
DROP TABLE t3;
|
|
|
|
DROP TABLE J1_TBL;
|
|
DROP TABLE J2_TBL;
|
|
|
|
-- Both DELETE and UPDATE allow the specification of additional tables
|
|
-- to "join" against to determine which rows should be modified.
|
|
|
|
CREATE TEMP TABLE t1 (a int, b int);
|
|
CREATE TEMP TABLE t2 (a int, b int);
|
|
CREATE TEMP TABLE t3 (x int, y int);
|
|
|
|
INSERT INTO t1 VALUES (5, 10);
|
|
INSERT INTO t1 VALUES (15, 20);
|
|
INSERT INTO t1 VALUES (100, 100);
|
|
INSERT INTO t1 VALUES (200, 1000);
|
|
INSERT INTO t2 VALUES (200, 2000);
|
|
INSERT INTO t3 VALUES (5, 20);
|
|
INSERT INTO t3 VALUES (6, 7);
|
|
INSERT INTO t3 VALUES (7, 8);
|
|
INSERT INTO t3 VALUES (500, 100);
|
|
|
|
DELETE FROM t3 USING t1 table1 WHERE t3.x = table1.a;
|
|
SELECT * FROM t3;
|
|
DELETE FROM t3 USING t1 JOIN t2 USING (a) WHERE t3.x > t1.a;
|
|
SELECT * FROM t3;
|
|
DELETE FROM t3 USING t3 t3_other WHERE t3.x = t3_other.x AND t3.y = t3_other.y;
|
|
SELECT * FROM t3;
|
|
|
|
-- Test join against inheritance tree
|
|
|
|
create temp table t2a () inherits (t2);
|
|
|
|
insert into t2a values (200, 2001);
|
|
|
|
select * from t1 left join t2 on (t1.a = t2.a);
|
|
|
|
-- Test matching of column name with wrong alias
|
|
|
|
select t1.x from t1 join t3 on (t1.a = t3.x);
|
|
|
|
-- Test matching of locking clause with wrong alias
|
|
|
|
select t1.*, t2.*, unnamed_join.* from
|
|
t1 join t2 on (t1.a = t2.a), t3 as unnamed_join
|
|
for update of unnamed_join;
|
|
|
|
select foo.*, unnamed_join.* from
|
|
t1 join t2 using (a) as foo, t3 as unnamed_join
|
|
for update of unnamed_join;
|
|
|
|
select foo.*, unnamed_join.* from
|
|
t1 join t2 using (a) as foo, t3 as unnamed_join
|
|
for update of foo;
|
|
|
|
select bar.*, unnamed_join.* from
|
|
(t1 join t2 using (a) as foo) as bar, t3 as unnamed_join
|
|
for update of foo;
|
|
|
|
select bar.*, unnamed_join.* from
|
|
(t1 join t2 using (a) as foo) as bar, t3 as unnamed_join
|
|
for update of bar;
|
|
|
|
--
|
|
-- regression test for 8.1 merge right join bug
|
|
--
|
|
|
|
CREATE TEMP TABLE tt1 ( tt1_id int4, joincol int4 );
|
|
INSERT INTO tt1 VALUES (1, 11);
|
|
INSERT INTO tt1 VALUES (2, NULL);
|
|
|
|
CREATE TEMP TABLE tt2 ( tt2_id int4, joincol int4 );
|
|
INSERT INTO tt2 VALUES (21, 11);
|
|
INSERT INTO tt2 VALUES (22, 11);
|
|
|
|
set enable_hashjoin to off;
|
|
set enable_nestloop to off;
|
|
|
|
-- these should give the same results
|
|
|
|
select tt1.*, tt2.* from tt1 left join tt2 on tt1.joincol = tt2.joincol;
|
|
|
|
select tt1.*, tt2.* from tt2 right join tt1 on tt1.joincol = tt2.joincol;
|
|
|
|
reset enable_hashjoin;
|
|
reset enable_nestloop;
|
|
|
|
--
|
|
-- regression test for bug #18522 (merge-right-anti-join in inner_unique cases)
|
|
--
|
|
|
|
create temp table tbl_ra(a int unique, b int);
|
|
insert into tbl_ra select i, i%100 from generate_series(1,1000)i;
|
|
create index on tbl_ra (b);
|
|
analyze tbl_ra;
|
|
|
|
set enable_hashjoin to off;
|
|
set enable_nestloop to off;
|
|
|
|
-- ensure we get a merge right anti join
|
|
explain (costs off)
|
|
select * from tbl_ra t1
|
|
where not exists (select 1 from tbl_ra t2 where t2.b = t1.a) and t1.b < 2;
|
|
|
|
-- and check we get the expected results
|
|
select * from tbl_ra t1
|
|
where not exists (select 1 from tbl_ra t2 where t2.b = t1.a) and t1.b < 2;
|
|
|
|
reset enable_hashjoin;
|
|
reset enable_nestloop;
|
|
|
|
--
|
|
-- regression test for bug with hash-right-semi join
|
|
--
|
|
|
|
create temp table tbl_rs(a int, b int);
|
|
insert into tbl_rs select i, i from generate_series(1,10)i;
|
|
analyze tbl_rs;
|
|
|
|
-- ensure we get a hash right semi join
|
|
explain (costs off)
|
|
select * from tbl_rs t1 join
|
|
lateral (select * from tbl_rs t2 where t2.a in
|
|
(select t1.a+t3.a from tbl_rs t3) and t2.a < 5)
|
|
on true;
|
|
|
|
-- and check we get the expected results
|
|
select * from tbl_rs t1 join
|
|
lateral (select * from tbl_rs t2 where t2.a in
|
|
(select t1.a+t3.a from tbl_rs t3) and t2.a < 5)
|
|
on true;
|
|
|
|
--
|
|
-- regression test for bug with parallel-hash-right-semi join
|
|
--
|
|
|
|
begin;
|
|
|
|
-- encourage use of parallel plans
|
|
set local parallel_setup_cost=0;
|
|
set local parallel_tuple_cost=0;
|
|
set local min_parallel_table_scan_size=0;
|
|
set local max_parallel_workers_per_gather=4;
|
|
|
|
-- ensure we don't get parallel hash right semi join
|
|
explain (costs off)
|
|
select * from tenk1 t1
|
|
where exists (select 1 from tenk1 t2 where fivethous = t1.fivethous)
|
|
and t1.fivethous < 5;
|
|
|
|
rollback;
|
|
|
|
--
|
|
-- regression test for bug #13908 (hash join with skew tuples & nbatch increase)
|
|
--
|
|
|
|
set work_mem to '64kB';
|
|
set enable_mergejoin to off;
|
|
set enable_memoize to off;
|
|
|
|
explain (costs off)
|
|
select count(*) from tenk1 a, tenk1 b
|
|
where a.hundred = b.thousand and (b.fivethous % 10) < 10;
|
|
select count(*) from tenk1 a, tenk1 b
|
|
where a.hundred = b.thousand and (b.fivethous % 10) < 10;
|
|
|
|
reset work_mem;
|
|
reset enable_mergejoin;
|
|
reset enable_memoize;
|
|
|
|
--
|
|
-- regression test for 8.2 bug with improper re-ordering of left joins
|
|
--
|
|
|
|
create temp table tt3(f1 int, f2 text);
|
|
insert into tt3 select x, repeat('xyzzy', 100) from generate_series(1,10000) x;
|
|
analyze tt3;
|
|
|
|
create temp table tt4(f1 int);
|
|
insert into tt4 values (0),(1),(9999);
|
|
analyze tt4;
|
|
|
|
set enable_nestloop to off;
|
|
|
|
EXPLAIN (COSTS OFF)
|
|
SELECT a.f1
|
|
FROM tt4 a
|
|
LEFT JOIN (
|
|
SELECT b.f1
|
|
FROM tt3 b LEFT JOIN tt3 c ON (b.f1 = c.f1)
|
|
WHERE COALESCE(c.f1, 0) = 0
|
|
) AS d ON (a.f1 = d.f1)
|
|
WHERE COALESCE(d.f1, 0) = 0
|
|
ORDER BY 1;
|
|
|
|
SELECT a.f1
|
|
FROM tt4 a
|
|
LEFT JOIN (
|
|
SELECT b.f1
|
|
FROM tt3 b LEFT JOIN tt3 c ON (b.f1 = c.f1)
|
|
WHERE COALESCE(c.f1, 0) = 0
|
|
) AS d ON (a.f1 = d.f1)
|
|
WHERE COALESCE(d.f1, 0) = 0
|
|
ORDER BY 1;
|
|
|
|
reset enable_nestloop;
|
|
|
|
--
|
|
-- basic semijoin and antijoin recognition tests
|
|
--
|
|
|
|
explain (costs off)
|
|
select a.* from tenk1 a
|
|
where unique1 in (select unique2 from tenk1 b);
|
|
|
|
-- sadly, this is not an antijoin
|
|
explain (costs off)
|
|
select a.* from tenk1 a
|
|
where unique1 not in (select unique2 from tenk1 b);
|
|
|
|
explain (costs off)
|
|
select a.* from tenk1 a
|
|
where exists (select 1 from tenk1 b where a.unique1 = b.unique2);
|
|
|
|
explain (costs off)
|
|
select a.* from tenk1 a
|
|
where not exists (select 1 from tenk1 b where a.unique1 = b.unique2);
|
|
|
|
explain (costs off)
|
|
select a.* from tenk1 a left join tenk1 b on a.unique1 = b.unique2
|
|
where b.unique2 is null;
|
|
|
|
-- check that we avoid de-duplicating columns redundantly
|
|
set enable_memoize to off;
|
|
explain (costs off)
|
|
select 1 from tenk1
|
|
where (hundred, thousand) in (select twothousand, twothousand from onek);
|
|
reset enable_memoize;
|
|
|
|
--
|
|
-- regression test for bogus RTE_GROUP entries
|
|
--
|
|
|
|
explain (costs off)
|
|
select a.* from tenk1 a
|
|
where exists (select 1 from tenk1 b where a.unique1 = b.unique2 group by b.unique1);
|
|
|
|
--
|
|
-- regression test for proper handling of outer joins within antijoins
|
|
--
|
|
|
|
create temp table tt4x(c1 int, c2 int, c3 int);
|
|
|
|
explain (costs off)
|
|
select * from tt4x t1
|
|
where not exists (
|
|
select 1 from tt4x t2
|
|
left join tt4x t3 on t2.c3 = t3.c1
|
|
left join ( select t5.c1 as c1
|
|
from tt4x t4 left join tt4x t5 on t4.c2 = t5.c1
|
|
) a1 on t3.c2 = a1.c1
|
|
where t1.c1 = t2.c2
|
|
);
|
|
|
|
--
|
|
-- regression test for problems of the sort depicted in bug #3494
|
|
--
|
|
|
|
create temp table tt5(f1 int, f2 int);
|
|
create temp table tt6(f1 int, f2 int);
|
|
|
|
insert into tt5 values(1, 10);
|
|
insert into tt5 values(1, 11);
|
|
|
|
insert into tt6 values(1, 9);
|
|
insert into tt6 values(1, 2);
|
|
insert into tt6 values(2, 9);
|
|
|
|
select * from tt5,tt6 where tt5.f1 = tt6.f1 and tt5.f1 = tt5.f2 - tt6.f2;
|
|
|
|
--
|
|
-- regression test for problems of the sort depicted in bug #3588
|
|
--
|
|
|
|
create temp table xx (pkxx int);
|
|
create temp table yy (pkyy int, pkxx int);
|
|
|
|
insert into xx values (1);
|
|
insert into xx values (2);
|
|
insert into xx values (3);
|
|
|
|
insert into yy values (101, 1);
|
|
insert into yy values (201, 2);
|
|
insert into yy values (301, NULL);
|
|
|
|
select yy.pkyy as yy_pkyy, yy.pkxx as yy_pkxx, yya.pkyy as yya_pkyy,
|
|
xxa.pkxx as xxa_pkxx, xxb.pkxx as xxb_pkxx
|
|
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;
|
|
|
|
--
|
|
-- regression test for improper extraction of OR indexqual conditions
|
|
-- (as seen in early 8.3.x releases)
|
|
--
|
|
|
|
select a.unique2, a.ten, b.tenthous, b.unique2, b.hundred
|
|
from tenk1 a left join tenk1 b on a.unique2 = b.tenthous
|
|
where a.unique1 = 42 and
|
|
((b.unique2 is null and a.ten = 2) or b.hundred = 3);
|
|
|
|
--
|
|
-- test proper positioning of one-time quals in EXISTS (8.4devel bug)
|
|
--
|
|
prepare foo(bool) as
|
|
select count(*) from tenk1 a left join tenk1 b
|
|
on (a.unique2 = b.unique1 and exists
|
|
(select 1 from tenk1 c where c.thousand = b.unique2 and $1));
|
|
execute foo(true);
|
|
execute foo(false);
|
|
|
|
--
|
|
-- test for sane behavior with noncanonical merge clauses, per bug #4926
|
|
--
|
|
|
|
begin;
|
|
|
|
set enable_mergejoin = 1;
|
|
set enable_hashjoin = 0;
|
|
set enable_nestloop = 0;
|
|
|
|
create temp table a (i integer);
|
|
create temp table b (x integer, y integer);
|
|
|
|
select * from a left join b on i = x and i = y and x = i;
|
|
|
|
rollback;
|
|
|
|
--
|
|
-- test handling of merge clauses using record_ops
|
|
--
|
|
begin;
|
|
|
|
create type mycomptype as (id int, v bigint);
|
|
|
|
create temp table tidv (idv mycomptype);
|
|
create index on tidv (idv);
|
|
|
|
explain (costs off)
|
|
select a.idv, b.idv from tidv a, tidv b where a.idv = b.idv;
|
|
|
|
set enable_mergejoin = 0;
|
|
set enable_hashjoin = 0;
|
|
|
|
explain (costs off)
|
|
select a.idv, b.idv from tidv a, tidv b where a.idv = b.idv;
|
|
|
|
rollback;
|
|
|
|
--
|
|
-- test NULL behavior of whole-row Vars, per bug #5025
|
|
--
|
|
select t1.q2, count(t2.*)
|
|
from int8_tbl t1 left join int8_tbl t2 on (t1.q2 = t2.q1)
|
|
group by t1.q2 order by 1;
|
|
|
|
select t1.q2, count(t2.*)
|
|
from int8_tbl t1 left join (select * from int8_tbl) t2 on (t1.q2 = t2.q1)
|
|
group by t1.q2 order by 1;
|
|
|
|
select t1.q2, count(t2.*)
|
|
from int8_tbl t1 left join (select * from int8_tbl offset 0) t2 on (t1.q2 = t2.q1)
|
|
group by t1.q2 order by 1;
|
|
|
|
select t1.q2, count(t2.*)
|
|
from int8_tbl t1 left join
|
|
(select q1, case when q2=1 then 1 else q2 end as q2 from int8_tbl) t2
|
|
on (t1.q2 = t2.q1)
|
|
group by t1.q2 order by 1;
|
|
|
|
--
|
|
-- test incorrect failure to NULL pulled-up subexpressions
|
|
--
|
|
begin;
|
|
|
|
create temp table a (
|
|
code char not null,
|
|
constraint a_pk primary key (code)
|
|
);
|
|
create temp table b (
|
|
a char not null,
|
|
num integer not null,
|
|
constraint b_pk primary key (a, num)
|
|
);
|
|
create temp table c (
|
|
name char not null,
|
|
a char,
|
|
constraint c_pk primary key (name)
|
|
);
|
|
|
|
insert into a (code) values ('p');
|
|
insert into a (code) values ('q');
|
|
insert into b (a, num) values ('p', 1);
|
|
insert into b (a, num) values ('p', 2);
|
|
insert into c (name, a) values ('A', 'p');
|
|
insert into c (name, a) values ('B', 'q');
|
|
insert into c (name, a) values ('C', null);
|
|
|
|
select c.name, ss.code, ss.b_cnt, ss.const
|
|
from c left join
|
|
(select a.code, coalesce(b_grp.cnt, 0) as b_cnt, -1 as const
|
|
from a left join
|
|
(select count(1) as cnt, b.a from b group by b.a) as b_grp
|
|
on a.code = b_grp.a
|
|
) as ss
|
|
on (c.a = ss.code)
|
|
order by c.name;
|
|
|
|
rollback;
|
|
|
|
--
|
|
-- test incorrect handling of placeholders that only appear in targetlists,
|
|
-- per bug #6154
|
|
--
|
|
SELECT * FROM
|
|
( SELECT 1 as key1 ) sub1
|
|
LEFT JOIN
|
|
( SELECT sub3.key3, sub4.value2, COALESCE(sub4.value2, 66) as value3 FROM
|
|
( SELECT 1 as key3 ) sub3
|
|
LEFT JOIN
|
|
( SELECT sub5.key5, COALESCE(sub6.value1, 1) as value2 FROM
|
|
( SELECT 1 as key5 ) sub5
|
|
LEFT JOIN
|
|
( SELECT 2 as key6, 42 as value1 ) sub6
|
|
ON sub5.key5 = sub6.key6
|
|
) sub4
|
|
ON sub4.key5 = sub3.key3
|
|
) sub2
|
|
ON sub1.key1 = sub2.key3;
|
|
|
|
-- test the path using join aliases, too
|
|
SELECT * FROM
|
|
( SELECT 1 as key1 ) sub1
|
|
LEFT JOIN
|
|
( SELECT sub3.key3, value2, COALESCE(value2, 66) as value3 FROM
|
|
( SELECT 1 as key3 ) sub3
|
|
LEFT JOIN
|
|
( SELECT sub5.key5, COALESCE(sub6.value1, 1) as value2 FROM
|
|
( SELECT 1 as key5 ) sub5
|
|
LEFT JOIN
|
|
( SELECT 2 as key6, 42 as value1 ) sub6
|
|
ON sub5.key5 = sub6.key6
|
|
) sub4
|
|
ON sub4.key5 = sub3.key3
|
|
) sub2
|
|
ON sub1.key1 = sub2.key3;
|
|
|
|
--
|
|
-- test case where a PlaceHolderVar is used as a nestloop parameter
|
|
--
|
|
|
|
EXPLAIN (COSTS OFF)
|
|
SELECT qq, unique1
|
|
FROM
|
|
( SELECT COALESCE(q1, 0) AS qq FROM int8_tbl a ) AS ss1
|
|
FULL OUTER JOIN
|
|
( SELECT COALESCE(q2, -1) AS qq FROM int8_tbl b ) AS ss2
|
|
USING (qq)
|
|
INNER JOIN tenk1 c ON qq = unique2;
|
|
|
|
SELECT qq, unique1
|
|
FROM
|
|
( SELECT COALESCE(q1, 0) AS qq FROM int8_tbl a ) AS ss1
|
|
FULL OUTER JOIN
|
|
( SELECT COALESCE(q2, -1) AS qq FROM int8_tbl b ) AS ss2
|
|
USING (qq)
|
|
INNER JOIN tenk1 c ON qq = unique2;
|
|
|
|
--
|
|
-- nested nestloops can require nested PlaceHolderVars
|
|
--
|
|
|
|
create temp table nt1 (
|
|
id int primary key,
|
|
a1 boolean,
|
|
a2 boolean
|
|
);
|
|
create temp table nt2 (
|
|
id int primary key,
|
|
nt1_id int,
|
|
b1 boolean,
|
|
b2 boolean,
|
|
foreign key (nt1_id) references nt1(id)
|
|
);
|
|
create temp table nt3 (
|
|
id int primary key,
|
|
nt2_id int,
|
|
c1 boolean,
|
|
foreign key (nt2_id) references nt2(id)
|
|
);
|
|
|
|
insert into nt1 values (1,true,true);
|
|
insert into nt1 values (2,true,false);
|
|
insert into nt1 values (3,false,false);
|
|
insert into nt2 values (1,1,true,true);
|
|
insert into nt2 values (2,2,true,false);
|
|
insert into nt2 values (3,3,false,false);
|
|
insert into nt3 values (1,1,true);
|
|
insert into nt3 values (2,2,false);
|
|
insert into nt3 values (3,3,true);
|
|
|
|
explain (costs off)
|
|
select nt3.id
|
|
from nt3 as nt3
|
|
left join
|
|
(select nt2.*, (nt2.b1 and ss1.a3) AS b3
|
|
from nt2 as nt2
|
|
left join
|
|
(select nt1.*, (nt1.id is not null) as a3 from nt1) as ss1
|
|
on ss1.id = nt2.nt1_id
|
|
) as ss2
|
|
on ss2.id = nt3.nt2_id
|
|
where nt3.id = 1 and ss2.b3;
|
|
|
|
select nt3.id
|
|
from nt3 as nt3
|
|
left join
|
|
(select nt2.*, (nt2.b1 and ss1.a3) AS b3
|
|
from nt2 as nt2
|
|
left join
|
|
(select nt1.*, (nt1.id is not null) as a3 from nt1) as ss1
|
|
on ss1.id = nt2.nt1_id
|
|
) as ss2
|
|
on ss2.id = nt3.nt2_id
|
|
where nt3.id = 1 and ss2.b3;
|
|
|
|
--
|
|
-- test case where a PlaceHolderVar is propagated into a subquery
|
|
--
|
|
|
|
explain (costs off)
|
|
select * from
|
|
int8_tbl t1 left join
|
|
(select q1 as x, 42 as y from int8_tbl t2) ss
|
|
on t1.q2 = ss.x
|
|
where
|
|
1 = (select 1 from int8_tbl t3 where ss.y is not null limit 1)
|
|
order by 1,2;
|
|
|
|
select * from
|
|
int8_tbl t1 left join
|
|
(select q1 as x, 42 as y from int8_tbl t2) ss
|
|
on t1.q2 = ss.x
|
|
where
|
|
1 = (select 1 from int8_tbl t3 where ss.y is not null limit 1)
|
|
order by 1,2;
|
|
|
|
--
|
|
-- variant where a PlaceHolderVar is needed at a join, but not above the join
|
|
--
|
|
|
|
explain (costs off)
|
|
select * from
|
|
int4_tbl as i41,
|
|
lateral
|
|
(select 1 as x from
|
|
(select i41.f1 as lat,
|
|
i42.f1 as loc from
|
|
int8_tbl as i81, int4_tbl as i42) as ss1
|
|
right join int4_tbl as i43 on (i43.f1 > 1)
|
|
where ss1.loc = ss1.lat) as ss2
|
|
where i41.f1 > 0;
|
|
|
|
select * from
|
|
int4_tbl as i41,
|
|
lateral
|
|
(select 1 as x from
|
|
(select i41.f1 as lat,
|
|
i42.f1 as loc from
|
|
int8_tbl as i81, int4_tbl as i42) as ss1
|
|
right join int4_tbl as i43 on (i43.f1 > 1)
|
|
where ss1.loc = ss1.lat) as ss2
|
|
where i41.f1 > 0;
|
|
|
|
--
|
|
-- test the corner cases FULL JOIN ON TRUE and FULL JOIN ON FALSE
|
|
--
|
|
select * from int4_tbl a full join int4_tbl b on true;
|
|
select * from int4_tbl a full join int4_tbl b on false;
|
|
|
|
--
|
|
-- test for ability to use a cartesian join when necessary
|
|
--
|
|
|
|
create temp table q1 as select 1 as q1;
|
|
create temp table q2 as select 0 as q2;
|
|
analyze q1;
|
|
analyze q2;
|
|
|
|
explain (costs off)
|
|
select * from
|
|
tenk1 join int4_tbl on f1 = twothousand,
|
|
q1, q2
|
|
where q1 = thousand or q2 = thousand;
|
|
|
|
explain (costs off)
|
|
select * from
|
|
tenk1 join int4_tbl on f1 = twothousand,
|
|
q1, q2
|
|
where thousand = (q1 + q2);
|
|
|
|
--
|
|
-- test ability to generate a suitable plan for a star-schema query
|
|
--
|
|
|
|
explain (costs off)
|
|
select * from
|
|
tenk1, int8_tbl a, int8_tbl b
|
|
where thousand = a.q1 and tenthous = b.q1 and a.q2 = 1 and b.q2 = 2;
|
|
|
|
--
|
|
-- test a corner case in which we shouldn't apply the star-schema optimization
|
|
--
|
|
|
|
explain (costs off)
|
|
select t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 from
|
|
tenk1 t1
|
|
inner join int4_tbl i1
|
|
left join (select v1.x2, v2.y1, 11 AS d1
|
|
from (select 1,0 from onerow) v1(x1,x2)
|
|
left join (select 3,1 from onerow) v2(y1,y2)
|
|
on v1.x1 = v2.y2) subq1
|
|
on (i1.f1 = subq1.x2)
|
|
on (t1.unique2 = subq1.d1)
|
|
left join tenk1 t2
|
|
on (subq1.y1 = t2.unique1)
|
|
where t1.unique2 < 42 and t1.stringu1 > t2.stringu2;
|
|
|
|
select t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 from
|
|
tenk1 t1
|
|
inner join int4_tbl i1
|
|
left join (select v1.x2, v2.y1, 11 AS d1
|
|
from (select 1,0 from onerow) v1(x1,x2)
|
|
left join (select 3,1 from onerow) v2(y1,y2)
|
|
on v1.x1 = v2.y2) subq1
|
|
on (i1.f1 = subq1.x2)
|
|
on (t1.unique2 = subq1.d1)
|
|
left join tenk1 t2
|
|
on (subq1.y1 = t2.unique1)
|
|
where t1.unique2 < 42 and t1.stringu1 > t2.stringu2;
|
|
|
|
-- variant that isn't quite a star-schema case
|
|
|
|
explain (verbose, costs off)
|
|
select ss1.d1 from
|
|
tenk1 as t1
|
|
inner join tenk1 as t2
|
|
on t1.tenthous = t2.ten
|
|
inner join
|
|
int8_tbl as i8
|
|
left join int4_tbl as i4
|
|
inner join (select 64::information_schema.cardinal_number as d1
|
|
from tenk1 t3,
|
|
lateral (select abs(t3.unique1) + random()) ss0(x)
|
|
where t3.fivethous < 0) as ss1
|
|
on i4.f1 = ss1.d1
|
|
on i8.q1 = i4.f1
|
|
on t1.tenthous = ss1.d1
|
|
where t1.unique1 < i4.f1;
|
|
|
|
select ss1.d1 from
|
|
tenk1 as t1
|
|
inner join tenk1 as t2
|
|
on t1.tenthous = t2.ten
|
|
inner join
|
|
int8_tbl as i8
|
|
left join int4_tbl as i4
|
|
inner join (select 64::information_schema.cardinal_number as d1
|
|
from tenk1 t3,
|
|
lateral (select abs(t3.unique1) + random()) ss0(x)
|
|
where t3.fivethous < 0) as ss1
|
|
on i4.f1 = ss1.d1
|
|
on i8.q1 = i4.f1
|
|
on t1.tenthous = ss1.d1
|
|
where t1.unique1 < i4.f1;
|
|
|
|
-- this variant is foldable by the remove-useless-RESULT-RTEs code
|
|
|
|
explain (costs off)
|
|
select t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 from
|
|
tenk1 t1
|
|
inner join int4_tbl i1
|
|
left join (select v1.x2, v2.y1, 11 AS d1
|
|
from (values(1,0)) v1(x1,x2)
|
|
left join (values(3,1)) v2(y1,y2)
|
|
on v1.x1 = v2.y2) subq1
|
|
on (i1.f1 = subq1.x2)
|
|
on (t1.unique2 = subq1.d1)
|
|
left join tenk1 t2
|
|
on (subq1.y1 = t2.unique1)
|
|
where t1.unique2 < 42 and t1.stringu1 > t2.stringu2;
|
|
|
|
select t1.unique2, t1.stringu1, t2.unique1, t2.stringu2 from
|
|
tenk1 t1
|
|
inner join int4_tbl i1
|
|
left join (select v1.x2, v2.y1, 11 AS d1
|
|
from (values(1,0)) v1(x1,x2)
|
|
left join (values(3,1)) v2(y1,y2)
|
|
on v1.x1 = v2.y2) subq1
|
|
on (i1.f1 = subq1.x2)
|
|
on (t1.unique2 = subq1.d1)
|
|
left join tenk1 t2
|
|
on (subq1.y1 = t2.unique1)
|
|
where t1.unique2 < 42 and t1.stringu1 > t2.stringu2;
|
|
|
|
-- Here's a variant that we can't fold too aggressively, though,
|
|
-- or we end up with noplace to evaluate the lateral PHV
|
|
explain (verbose, costs off)
|
|
select * from
|
|
(select 1 as x) ss1 left join (select 2 as y) ss2 on (true),
|
|
lateral (select ss2.y as z limit 1) ss3;
|
|
select * from
|
|
(select 1 as x) ss1 left join (select 2 as y) ss2 on (true),
|
|
lateral (select ss2.y as z limit 1) ss3;
|
|
|
|
-- This example demonstrates the folly of our old "have_dangerous_phv" logic
|
|
begin;
|
|
set local from_collapse_limit to 2;
|
|
explain (verbose, costs off)
|
|
select * from int8_tbl t1
|
|
left join
|
|
(select coalesce(t2.q1 + x, 0) from int8_tbl t2,
|
|
lateral (select t3.q1 as x from int8_tbl t3,
|
|
lateral (select t2.q1, t3.q1 offset 0) s))
|
|
on true;
|
|
rollback;
|
|
|
|
-- ... not that the initial replacement didn't have some bugs too
|
|
begin;
|
|
create temp table t(i int primary key);
|
|
|
|
explain (verbose, costs off)
|
|
select * from t t1
|
|
left join (select 1 as x, * from t t2(i2)) t2ss on t1.i = t2ss.i2
|
|
left join t t3(i3) on false
|
|
left join t t4(i4) on t4.i4 > t2ss.x;
|
|
|
|
explain (verbose, costs off)
|
|
select * from
|
|
(select k from
|
|
(select i, coalesce(i, j) as k from
|
|
(select i from t union all select 0)
|
|
join (select 1 as j limit 1) on i = j)
|
|
right join (select 2 as x) on true
|
|
join (select 3 as y) on i is not null
|
|
),
|
|
lateral (select k as kl limit 1);
|
|
|
|
rollback;
|
|
|
|
-- PHVs containing SubLinks are quite tricky to get right
|
|
explain (verbose, costs off)
|
|
select *
|
|
from int8_tbl i8
|
|
inner join
|
|
(select (select true) as x
|
|
from int4_tbl i4, lateral (select i4.f1 as y limit 1) ss1
|
|
where i4.f1 = 0) ss2 on true
|
|
right join (select false as z) ss3 on true,
|
|
lateral (select i8.q2 as q2l where x limit 1) ss4
|
|
where i8.q2 = 123;
|
|
|
|
explain (verbose, costs off)
|
|
select *
|
|
from int8_tbl i8
|
|
inner join
|
|
(select (select true) as x
|
|
from int4_tbl i4, lateral (select 1 as y limit 1) ss1
|
|
where i4.f1 = 0) ss2 on true
|
|
right join (select false as z) ss3 on true,
|
|
lateral (select i8.q2 as q2l where x limit 1) ss4
|
|
where i8.q2 = 123;
|
|
|
|
-- Test proper handling of appendrel PHVs during useless-RTE removal
|
|
explain (costs off)
|
|
select * from
|
|
(select 0 as z) as t1
|
|
left join
|
|
(select true as a) as t2
|
|
on true,
|
|
lateral (select true as b
|
|
union all
|
|
select a as b) as t3
|
|
where b;
|
|
|
|
select * from
|
|
(select 0 as z) as t1
|
|
left join
|
|
(select true as a) as t2
|
|
on true,
|
|
lateral (select true as b
|
|
union all
|
|
select a as b) as t3
|
|
where b;
|
|
|
|
-- Test PHV in a semijoin qual, which confused useless-RTE removal (bug #17700)
|
|
explain (verbose, costs off)
|
|
with ctetable as not materialized ( select 1 as f1 )
|
|
select * from ctetable c1
|
|
where f1 in ( select c3.f1 from ctetable c2 full join ctetable c3 on true );
|
|
|
|
with ctetable as not materialized ( select 1 as f1 )
|
|
select * from ctetable c1
|
|
where f1 in ( select c3.f1 from ctetable c2 full join ctetable c3 on true );
|
|
|
|
-- Test PHV that winds up in a Result node, despite having nonempty nullingrels
|
|
explain (verbose, costs off)
|
|
select table_catalog, table_name
|
|
from int4_tbl t1
|
|
inner join (int8_tbl t2
|
|
left join information_schema.column_udt_usage on null)
|
|
on null;
|
|
|
|
-- Test handling of qual pushdown to appendrel members with non-Var outputs
|
|
explain (verbose, costs off)
|
|
select * from int4_tbl left join (
|
|
select text 'foo' union all select text 'bar'
|
|
) ss(x) on true
|
|
where ss.x is null;
|
|
|
|
--
|
|
-- test inlining of immutable functions
|
|
--
|
|
create function f_immutable_int4(i integer) returns integer as
|
|
$$ begin return i; end; $$ language plpgsql immutable;
|
|
|
|
-- check optimization of function scan with join
|
|
explain (costs off)
|
|
select unique1 from tenk1, (select * from f_immutable_int4(1) x) x
|
|
where x = unique1;
|
|
|
|
explain (verbose, costs off)
|
|
select unique1, x.*
|
|
from tenk1, (select *, random() from f_immutable_int4(1) x) x
|
|
where x = unique1;
|
|
|
|
explain (costs off)
|
|
select unique1 from tenk1, f_immutable_int4(1) x where x = unique1;
|
|
|
|
explain (costs off)
|
|
select unique1 from tenk1, lateral f_immutable_int4(1) x where x = unique1;
|
|
|
|
explain (costs off)
|
|
select unique1 from tenk1, lateral f_immutable_int4(1) x where x in (select 17);
|
|
|
|
explain (costs off)
|
|
select unique1, x from tenk1 join f_immutable_int4(1) x on unique1 = x;
|
|
|
|
explain (costs off)
|
|
select unique1, x from tenk1 left join f_immutable_int4(1) x on unique1 = x;
|
|
|
|
explain (costs off)
|
|
select unique1, x from tenk1 right join f_immutable_int4(1) x on unique1 = x;
|
|
|
|
explain (costs off)
|
|
select unique1, x from tenk1 full join f_immutable_int4(1) x on unique1 = x;
|
|
|
|
-- check that pullup of a const function allows further const-folding
|
|
explain (costs off)
|
|
select unique1 from tenk1, f_immutable_int4(1) x where x = 42;
|
|
|
|
-- test inlining of immutable functions with PlaceHolderVars
|
|
explain (costs off)
|
|
select nt3.id
|
|
from nt3 as nt3
|
|
left join
|
|
(select nt2.*, (nt2.b1 or i4 = 42) AS b3
|
|
from nt2 as nt2
|
|
left join
|
|
f_immutable_int4(0) i4
|
|
on i4 = nt2.nt1_id
|
|
) as ss2
|
|
on ss2.id = nt3.nt2_id
|
|
where nt3.id = 1 and ss2.b3;
|
|
|
|
drop function f_immutable_int4(int);
|
|
|
|
-- test inlining when function returns composite
|
|
|
|
create function mki8(bigint, bigint) returns int8_tbl as
|
|
$$select row($1,$2)::int8_tbl$$ language sql;
|
|
|
|
create function mki4(int) returns int4_tbl as
|
|
$$select row($1)::int4_tbl$$ language sql;
|
|
|
|
explain (verbose, costs off)
|
|
select * from mki8(1,2);
|
|
select * from mki8(1,2);
|
|
|
|
explain (verbose, costs off)
|
|
select * from mki4(42);
|
|
select * from mki4(42);
|
|
|
|
drop function mki8(bigint, bigint);
|
|
drop function mki4(int);
|
|
|
|
-- test const-folding of a whole-row Var into a per-field Var
|
|
-- (need to inline a function to reach this case, else parser does it)
|
|
create function f_field_select(t onek) returns int4 as
|
|
$$ select t.unique2; $$ language sql immutable;
|
|
|
|
explain (verbose, costs off)
|
|
select (t2.*).unique1, f_field_select(t2) from tenk1 t1
|
|
left join onek t2 on t1.unique1 = t2.unique1
|
|
left join int8_tbl t3 on true;
|
|
|
|
drop function f_field_select(t onek);
|
|
|
|
--
|
|
-- test extraction of restriction OR clauses from join OR clause
|
|
-- (we used to only do this for indexable clauses)
|
|
--
|
|
|
|
explain (costs off)
|
|
select * from tenk1 a join tenk1 b on
|
|
(a.unique1 = 1 and b.unique1 = 2) or (a.unique2 = 3 and b.hundred = 4);
|
|
explain (costs off)
|
|
select * from tenk1 a join tenk1 b on
|
|
(a.unique1 = 1 and b.unique1 = 2) or (a.unique2 = 3 and b.ten = 4);
|
|
explain (costs off)
|
|
select * from tenk1 a join tenk1 b on
|
|
(a.unique1 = 1 and b.unique1 = 2) or
|
|
((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4);
|
|
|
|
explain (costs off)
|
|
select * from tenk1 a join tenk1 b on
|
|
(a.unique1 = 1 and b.unique1 = 2) or
|
|
((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4);
|
|
explain (costs off)
|
|
select * from tenk1 a join tenk1 b on
|
|
(a.unique1 < 20 or a.unique1 = 3 or a.unique1 = 1 and b.unique1 = 2) or
|
|
((a.unique2 = 3 or a.unique2 = 7) and b.hundred = 4);
|
|
|
|
--
|
|
-- test placement of movable quals in a parameterized join tree
|
|
--
|
|
|
|
explain (costs off)
|
|
select * from tenk1 t1 left join
|
|
(tenk1 t2 join tenk1 t3 on t2.thousand = t3.unique2)
|
|
on t1.hundred = t2.hundred and t1.ten = t3.ten
|
|
where t1.unique1 = 1;
|
|
|
|
explain (costs off)
|
|
select * from tenk1 t1 left join
|
|
(tenk1 t2 join tenk1 t3 on t2.thousand = t3.unique2)
|
|
on t1.hundred = t2.hundred and t1.ten + t2.ten = t3.ten
|
|
where t1.unique1 = 1;
|
|
|
|
explain (costs off)
|
|
select count(*) from
|
|
tenk1 a join tenk1 b on a.unique1 = b.unique2
|
|
left join tenk1 c on a.unique2 = b.unique1 and c.thousand = a.thousand
|
|
join int4_tbl on b.thousand = f1;
|
|
|
|
select count(*) from
|
|
tenk1 a join tenk1 b on a.unique1 = b.unique2
|
|
left join tenk1 c on a.unique2 = b.unique1 and c.thousand = a.thousand
|
|
join int4_tbl on b.thousand = f1;
|
|
|
|
explain (costs off)
|
|
select b.unique1 from
|
|
tenk1 a join tenk1 b on a.unique1 = b.unique2
|
|
left join tenk1 c on b.unique1 = 42 and c.thousand = a.thousand
|
|
join int4_tbl i1 on b.thousand = f1
|
|
right join int4_tbl i2 on i2.f1 = b.tenthous
|
|
order by 1;
|
|
|
|
select b.unique1 from
|
|
tenk1 a join tenk1 b on a.unique1 = b.unique2
|
|
left join tenk1 c on b.unique1 = 42 and c.thousand = a.thousand
|
|
join int4_tbl i1 on b.thousand = f1
|
|
right join int4_tbl i2 on i2.f1 = b.tenthous
|
|
order by 1;
|
|
|
|
explain (costs off)
|
|
select * from
|
|
(
|
|
select unique1, q1, coalesce(unique1, -1) + q1 as fault
|
|
from int8_tbl left join tenk1 on (q2 = unique2)
|
|
) ss
|
|
where fault = 122
|
|
order by fault;
|
|
|
|
select * from
|
|
(
|
|
select unique1, q1, coalesce(unique1, -1) + q1 as fault
|
|
from int8_tbl left join tenk1 on (q2 = unique2)
|
|
) ss
|
|
where fault = 122
|
|
order by fault;
|
|
|
|
explain (costs off)
|
|
select * from
|
|
(values (1, array[10,20]), (2, array[20,30])) as v1(v1x,v1ys)
|
|
left join (values (1, 10), (2, 20)) as v2(v2x,v2y) on v2x = v1x
|
|
left join unnest(v1ys) as u1(u1y) on u1y = v2y;
|
|
|
|
select * from
|
|
(values (1, array[10,20]), (2, array[20,30])) as v1(v1x,v1ys)
|
|
left join (values (1, 10), (2, 20)) as v2(v2x,v2y) on v2x = v1x
|
|
left join unnest(v1ys) as u1(u1y) on u1y = v2y;
|
|
|
|
--
|
|
-- test handling of potential equivalence clauses above outer joins
|
|
--
|
|
|
|
explain (costs off)
|
|
select q1, unique2, thousand, hundred
|
|
from int8_tbl a left join tenk1 b on q1 = unique2
|
|
where coalesce(thousand,123) = q1 and q1 = coalesce(hundred,123);
|
|
|
|
select q1, unique2, thousand, hundred
|
|
from int8_tbl a left join tenk1 b on q1 = unique2
|
|
where coalesce(thousand,123) = q1 and q1 = coalesce(hundred,123);
|
|
|
|
explain (costs off)
|
|
select f1, unique2, case when unique2 is null then f1 else 0 end
|
|
from int4_tbl a left join tenk1 b on f1 = unique2
|
|
where (case when unique2 is null then f1 else 0 end) = 0;
|
|
|
|
select f1, unique2, case when unique2 is null then f1 else 0 end
|
|
from int4_tbl a left join tenk1 b on f1 = unique2
|
|
where (case when unique2 is null then f1 else 0 end) = 0;
|
|
|
|
--
|
|
-- another case with equivalence clauses above outer joins (bug #8591)
|
|
--
|
|
|
|
explain (costs off)
|
|
select a.unique1, b.unique1, c.unique1, coalesce(b.twothousand, a.twothousand)
|
|
from tenk1 a left join tenk1 b on b.thousand = a.unique1 left join tenk1 c on c.unique2 = coalesce(b.twothousand, a.twothousand)
|
|
where a.unique2 < 10 and coalesce(b.twothousand, a.twothousand) = 44;
|
|
|
|
select a.unique1, b.unique1, c.unique1, coalesce(b.twothousand, a.twothousand)
|
|
from tenk1 a left join tenk1 b on b.thousand = a.unique1 left join tenk1 c on c.unique2 = coalesce(b.twothousand, a.twothousand)
|
|
where a.unique2 < 10 and coalesce(b.twothousand, a.twothousand) = 44;
|
|
|
|
-- related case
|
|
|
|
explain (costs off)
|
|
select * from int8_tbl t1 left join int8_tbl t2 on t1.q2 = t2.q1,
|
|
lateral (select * from int8_tbl t3 where t2.q1 = t2.q2) ss;
|
|
|
|
select * from int8_tbl t1 left join int8_tbl t2 on t1.q2 = t2.q1,
|
|
lateral (select * from int8_tbl t3 where t2.q1 = t2.q2) ss;
|
|
|
|
--
|
|
-- check handling of join aliases when flattening multiple levels of subquery
|
|
--
|
|
|
|
explain (verbose, costs off)
|
|
select foo1.join_key as foo1_id, foo3.join_key AS foo3_id, bug_field from
|
|
(values (0),(1)) foo1(join_key)
|
|
left join
|
|
(select join_key, bug_field from
|
|
(select ss1.join_key, ss1.bug_field from
|
|
(select f1 as join_key, 666 as bug_field from int4_tbl i1) ss1
|
|
) foo2
|
|
left join
|
|
(select unique2 as join_key from tenk1 i2) ss2
|
|
using (join_key)
|
|
) foo3
|
|
using (join_key);
|
|
|
|
select foo1.join_key as foo1_id, foo3.join_key AS foo3_id, bug_field from
|
|
(values (0),(1)) foo1(join_key)
|
|
left join
|
|
(select join_key, bug_field from
|
|
(select ss1.join_key, ss1.bug_field from
|
|
(select f1 as join_key, 666 as bug_field from int4_tbl i1) ss1
|
|
) foo2
|
|
left join
|
|
(select unique2 as join_key from tenk1 i2) ss2
|
|
using (join_key)
|
|
) foo3
|
|
using (join_key);
|
|
|
|
--
|
|
-- check handling of a variable-free join alias
|
|
--
|
|
explain (verbose, costs off)
|
|
select * from
|
|
int4_tbl i0 left join
|
|
( (select *, 123 as x from int4_tbl i1) ss1
|
|
left join
|
|
(select *, q2 as x from int8_tbl i2) ss2
|
|
using (x)
|
|
) ss0
|
|
on (i0.f1 = ss0.f1)
|
|
order by i0.f1, x;
|
|
|
|
select * from
|
|
int4_tbl i0 left join
|
|
( (select *, 123 as x from int4_tbl i1) ss1
|
|
left join
|
|
(select *, q2 as x from int8_tbl i2) ss2
|
|
using (x)
|
|
) ss0
|
|
on (i0.f1 = ss0.f1)
|
|
order by i0.f1, x;
|
|
|
|
--
|
|
-- test successful handling of nested outer joins with degenerate join quals
|
|
--
|
|
|
|
explain (verbose, costs off)
|
|
select t1.* from
|
|
text_tbl t1
|
|
left join (select *, '***'::text as d1 from int8_tbl i8b1) b1
|
|
left join int8_tbl i8
|
|
left join (select *, null::int as d2 from int8_tbl i8b2) b2
|
|
on (i8.q1 = b2.q1)
|
|
on (b2.d2 = b1.q2)
|
|
on (t1.f1 = b1.d1)
|
|
left join int4_tbl i4
|
|
on (i8.q2 = i4.f1);
|
|
|
|
select t1.* from
|
|
text_tbl t1
|
|
left join (select *, '***'::text as d1 from int8_tbl i8b1) b1
|
|
left join int8_tbl i8
|
|
left join (select *, null::int as d2 from int8_tbl i8b2) b2
|
|
on (i8.q1 = b2.q1)
|
|
on (b2.d2 = b1.q2)
|
|
on (t1.f1 = b1.d1)
|
|
left join int4_tbl i4
|
|
on (i8.q2 = i4.f1);
|
|
|
|
explain (verbose, costs off)
|
|
select t1.* from
|
|
text_tbl t1
|
|
left join (select *, '***'::text as d1 from int8_tbl i8b1) b1
|
|
left join int8_tbl i8
|
|
left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2) b2
|
|
on (i8.q1 = b2.q1)
|
|
on (b2.d2 = b1.q2)
|
|
on (t1.f1 = b1.d1)
|
|
left join int4_tbl i4
|
|
on (i8.q2 = i4.f1);
|
|
|
|
select t1.* from
|
|
text_tbl t1
|
|
left join (select *, '***'::text as d1 from int8_tbl i8b1) b1
|
|
left join int8_tbl i8
|
|
left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2) b2
|
|
on (i8.q1 = b2.q1)
|
|
on (b2.d2 = b1.q2)
|
|
on (t1.f1 = b1.d1)
|
|
left join int4_tbl i4
|
|
on (i8.q2 = i4.f1);
|
|
|
|
explain (verbose, costs off)
|
|
select t1.* from
|
|
text_tbl t1
|
|
left join (select *, '***'::text as d1 from int8_tbl i8b1) b1
|
|
left join int8_tbl i8
|
|
left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2
|
|
where q1 = f1) b2
|
|
on (i8.q1 = b2.q1)
|
|
on (b2.d2 = b1.q2)
|
|
on (t1.f1 = b1.d1)
|
|
left join int4_tbl i4
|
|
on (i8.q2 = i4.f1);
|
|
|
|
select t1.* from
|
|
text_tbl t1
|
|
left join (select *, '***'::text as d1 from int8_tbl i8b1) b1
|
|
left join int8_tbl i8
|
|
left join (select *, null::int as d2 from int8_tbl i8b2, int4_tbl i4b2
|
|
where q1 = f1) b2
|
|
on (i8.q1 = b2.q1)
|
|
on (b2.d2 = b1.q2)
|
|
on (t1.f1 = b1.d1)
|
|
left join int4_tbl i4
|
|
on (i8.q2 = i4.f1);
|
|
|
|
explain (verbose, costs off)
|
|
select * from
|
|
text_tbl t1
|
|
inner join int8_tbl i8
|
|
on i8.q2 = 456
|
|
right join text_tbl t2
|
|
on t1.f1 = 'doh!'
|
|
left join int4_tbl i4
|
|
on i8.q1 = i4.f1;
|
|
|
|
select * from
|
|
text_tbl t1
|
|
inner join int8_tbl i8
|
|
on i8.q2 = 456
|
|
right join text_tbl t2
|
|
on t1.f1 = 'doh!'
|
|
left join int4_tbl i4
|
|
on i8.q1 = i4.f1;
|
|
|
|
-- check handling of a variable-free qual for a non-commutable outer join
|
|
explain (costs off)
|
|
select nspname
|
|
from (select 1 as x) ss1
|
|
left join
|
|
( select n.nspname, c.relname
|
|
from pg_class c left join pg_namespace n on n.oid = c.relnamespace
|
|
where c.relkind = 'r'
|
|
) ss2 on false;
|
|
|
|
-- check handling of apparently-commutable outer joins with non-commutable
|
|
-- joins between them
|
|
explain (costs off)
|
|
select 1 from
|
|
int4_tbl i4
|
|
left join int8_tbl i8 on i4.f1 is not null
|
|
left join (select 1 as a) ss1 on null
|
|
join int4_tbl i42 on ss1.a is null or i8.q1 <> i8.q2
|
|
right join (select 2 as b) ss2
|
|
on ss2.b < i4.f1;
|
|
|
|
--
|
|
-- test for appropriate join order in the presence of lateral references
|
|
--
|
|
|
|
explain (verbose, costs off)
|
|
select * from
|
|
text_tbl t1
|
|
left join int8_tbl i8
|
|
on i8.q2 = 123,
|
|
lateral (select i8.q1, t2.f1 from text_tbl t2 limit 1) as ss
|
|
where t1.f1 = ss.f1;
|
|
|
|
select * from
|
|
text_tbl t1
|
|
left join int8_tbl i8
|
|
on i8.q2 = 123,
|
|
lateral (select i8.q1, t2.f1 from text_tbl t2 limit 1) as ss
|
|
where t1.f1 = ss.f1;
|
|
|
|
explain (verbose, costs off)
|
|
select * from
|
|
text_tbl t1
|
|
left join int8_tbl i8
|
|
on i8.q2 = 123,
|
|
lateral (select i8.q1, t2.f1 from text_tbl t2 limit 1) as ss1,
|
|
lateral (select ss1.* from text_tbl t3 limit 1) as ss2
|
|
where t1.f1 = ss2.f1;
|
|
|
|
select * from
|
|
text_tbl t1
|
|
left join int8_tbl i8
|
|
on i8.q2 = 123,
|
|
lateral (select i8.q1, t2.f1 from text_tbl t2 limit 1) as ss1,
|
|
lateral (select ss1.* from text_tbl t3 limit 1) as ss2
|
|
where t1.f1 = ss2.f1;
|
|
|
|
explain (verbose, costs off)
|
|
select 1 from
|
|
text_tbl as tt1
|
|
inner join text_tbl as tt2 on (tt1.f1 = 'foo')
|
|
left join text_tbl as tt3 on (tt3.f1 = 'foo')
|
|
left join text_tbl as tt4 on (tt3.f1 = tt4.f1),
|
|
lateral (select tt4.f1 as c0 from text_tbl as tt5 limit 1) as ss1
|
|
where tt1.f1 = ss1.c0;
|
|
|
|
select 1 from
|
|
text_tbl as tt1
|
|
inner join text_tbl as tt2 on (tt1.f1 = 'foo')
|
|
left join text_tbl as tt3 on (tt3.f1 = 'foo')
|
|
left join text_tbl as tt4 on (tt3.f1 = tt4.f1),
|
|
lateral (select tt4.f1 as c0 from text_tbl as tt5 limit 1) as ss1
|
|
where tt1.f1 = ss1.c0;
|
|
|
|
explain (verbose, costs off)
|
|
select 1 from
|
|
int4_tbl as i4
|
|
inner join
|
|
((select 42 as n from int4_tbl x1 left join int8_tbl x2 on f1 = q1) as ss1
|
|
right join (select 1 as z) as ss2 on true)
|
|
on false,
|
|
lateral (select i4.f1, ss1.n from int8_tbl as i8 limit 1) as ss3;
|
|
|
|
select 1 from
|
|
int4_tbl as i4
|
|
inner join
|
|
((select 42 as n from int4_tbl x1 left join int8_tbl x2 on f1 = q1) as ss1
|
|
right join (select 1 as z) as ss2 on true)
|
|
on false,
|
|
lateral (select i4.f1, ss1.n from int8_tbl as i8 limit 1) as ss3;
|
|
|
|
--
|
|
-- check a case where we formerly generated invalid parameterized paths
|
|
--
|
|
|
|
begin;
|
|
|
|
create temp table t (a int unique);
|
|
|
|
explain (costs off)
|
|
select 1 from t t1
|
|
join lateral (select t1.a from (select 1) foo offset 0) as s1 on true
|
|
join
|
|
(select 1 from t t2
|
|
inner join (t t3
|
|
left join (t t4 left join t t5 on t4.a = 1)
|
|
on t3.a = t4.a)
|
|
on false
|
|
where t3.a = coalesce(t5.a,1)) as s2
|
|
on true;
|
|
|
|
rollback;
|
|
|
|
--
|
|
-- check a case in which a PlaceHolderVar forces join order
|
|
--
|
|
|
|
explain (verbose, costs off)
|
|
select ss2.* from
|
|
int4_tbl i41
|
|
left join int8_tbl i8
|
|
join (select i42.f1 as c1, i43.f1 as c2, 42 as c3
|
|
from int4_tbl i42, int4_tbl i43) ss1
|
|
on i8.q1 = ss1.c2
|
|
on i41.f1 = ss1.c1,
|
|
lateral (select i41.*, i8.*, ss1.* from text_tbl limit 1) ss2
|
|
where ss1.c2 = 0;
|
|
|
|
select ss2.* from
|
|
int4_tbl i41
|
|
left join int8_tbl i8
|
|
join (select i42.f1 as c1, i43.f1 as c2, 42 as c3
|
|
from int4_tbl i42, int4_tbl i43) ss1
|
|
on i8.q1 = ss1.c2
|
|
on i41.f1 = ss1.c1,
|
|
lateral (select i41.*, i8.*, ss1.* from text_tbl limit 1) ss2
|
|
where ss1.c2 = 0;
|
|
|
|
--
|
|
-- test successful handling of full join underneath left join (bug #14105)
|
|
--
|
|
|
|
explain (costs off)
|
|
select * from
|
|
(select 1 as id) as xx
|
|
left join
|
|
(tenk1 as a1 full join (select 1 as id) as yy on (a1.unique1 = yy.id))
|
|
on (xx.id = coalesce(yy.id, yy.id));
|
|
|
|
select * from
|
|
(select 1 as id) as xx
|
|
left join
|
|
(tenk1 as a1 full join (select 1 as id) as yy on (a1.unique1 = yy.id))
|
|
on (xx.id = coalesce(yy.id, yy.id));
|
|
|
|
--
|
|
-- test ability to push constants through outer join clauses
|
|
--
|
|
|
|
explain (costs off)
|
|
select * from int4_tbl a left join tenk1 b on f1 = unique2 where f1 = 0;
|
|
|
|
explain (costs off)
|
|
select * from tenk1 a full join tenk1 b using(unique2) where unique2 = 42;
|
|
|
|
--
|
|
-- test that quals attached to an outer join have correct semantics,
|
|
-- specifically that they don't re-use expressions computed below the join;
|
|
-- we force a mergejoin so that coalesce(b.q1, 1) appears as a join input
|
|
--
|
|
|
|
set enable_hashjoin to off;
|
|
set enable_nestloop to off;
|
|
|
|
explain (verbose, costs off)
|
|
select a.q2, b.q1
|
|
from int8_tbl a left join int8_tbl b on a.q2 = coalesce(b.q1, 1)
|
|
where coalesce(b.q1, 1) > 0;
|
|
select a.q2, b.q1
|
|
from int8_tbl a left join int8_tbl b on a.q2 = coalesce(b.q1, 1)
|
|
where coalesce(b.q1, 1) > 0;
|
|
|
|
reset enable_hashjoin;
|
|
reset enable_nestloop;
|
|
|
|
--
|
|
-- test join strength reduction with a SubPlan providing the proof
|
|
--
|
|
|
|
explain (costs off)
|
|
select a.unique1, b.unique2
|
|
from onek a left join onek b on a.unique1 = b.unique2
|
|
where (b.unique2, random() > 0) = any (select q1, random() > 0 from int8_tbl c where c.q1 < b.unique1);
|
|
|
|
select a.unique1, b.unique2
|
|
from onek a left join onek b on a.unique1 = b.unique2
|
|
where (b.unique2, random() > 0) = any (select q1, random() > 0 from int8_tbl c where c.q1 < b.unique1);
|
|
|
|
--
|
|
-- test full-join strength reduction
|
|
--
|
|
|
|
explain (costs off)
|
|
select a.unique1, b.unique2
|
|
from onek a full join onek b on a.unique1 = b.unique2
|
|
where a.unique1 = 42;
|
|
|
|
select a.unique1, b.unique2
|
|
from onek a full join onek b on a.unique1 = b.unique2
|
|
where a.unique1 = 42;
|
|
|
|
explain (costs off)
|
|
select a.unique1, b.unique2
|
|
from onek a full join onek b on a.unique1 = b.unique2
|
|
where b.unique2 = 43;
|
|
|
|
select a.unique1, b.unique2
|
|
from onek a full join onek b on a.unique1 = b.unique2
|
|
where b.unique2 = 43;
|
|
|
|
explain (costs off)
|
|
select a.unique1, b.unique2
|
|
from onek a full join onek b on a.unique1 = b.unique2
|
|
where a.unique1 = 42 and b.unique2 = 42;
|
|
|
|
select a.unique1, b.unique2
|
|
from onek a full join onek b on a.unique1 = b.unique2
|
|
where a.unique1 = 42 and b.unique2 = 42;
|
|
|
|
--
|
|
-- test result-RTE removal underneath a full join
|
|
--
|
|
|
|
explain (costs off)
|
|
select * from
|
|
(select * from int8_tbl i81 join (values(123,2)) v(v1,v2) on q2=v1) ss1
|
|
full join
|
|
(select * from (values(456,2)) w(v1,v2) join int8_tbl i82 on q2=v1) ss2
|
|
on true;
|
|
|
|
select * from
|
|
(select * from int8_tbl i81 join (values(123,2)) v(v1,v2) on q2=v1) ss1
|
|
full join
|
|
(select * from (values(456,2)) w(v1,v2) join int8_tbl i82 on q2=v1) ss2
|
|
on true;
|
|
|
|
--
|
|
-- test join removal
|
|
--
|
|
|
|
begin;
|
|
|
|
CREATE TEMP TABLE a (id int PRIMARY KEY, b_id int);
|
|
CREATE TEMP TABLE b (id int PRIMARY KEY, c_id int);
|
|
CREATE TEMP TABLE c (id int PRIMARY KEY);
|
|
CREATE TEMP TABLE d (a int, b int);
|
|
CREATE TEMP TABLE e (id1 int, id2 int, PRIMARY KEY(id1, id2));
|
|
INSERT INTO a VALUES (0, 0), (1, NULL);
|
|
INSERT INTO b VALUES (0, 0), (1, NULL);
|
|
INSERT INTO c VALUES (0), (1);
|
|
INSERT INTO d VALUES (1,3), (2,2), (3,1);
|
|
INSERT INTO e VALUES (0,0), (2,2), (3,1);
|
|
|
|
-- all these cases should be optimizable into a simple seqscan
|
|
explain (costs off) SELECT a.* FROM a LEFT JOIN b ON a.b_id = b.id;
|
|
explain (costs off) SELECT b.* FROM b LEFT JOIN c ON b.c_id = c.id;
|
|
explain (costs off)
|
|
SELECT a.* FROM a LEFT JOIN (b left join c on b.c_id = c.id)
|
|
ON (a.b_id = b.id);
|
|
explain (costs off)
|
|
SELECT a.* FROM a LEFT JOIN b ON a.id = b.id
|
|
LEFT JOIN e ON e.id1 = a.b_id AND b.c_id = e.id2;
|
|
|
|
-- check optimization of outer join within another special join
|
|
explain (costs off)
|
|
select id from a where id in (
|
|
select b.id from b left join c on b.id = c.id
|
|
);
|
|
|
|
-- check optimization with oddly-nested outer joins
|
|
explain (costs off)
|
|
select a1.id from
|
|
(a a1 left join a a2 on true)
|
|
left join
|
|
(a a3 left join a a4 on a3.id = a4.id)
|
|
on a2.id = a3.id;
|
|
|
|
explain (costs off)
|
|
select a1.id from
|
|
(a a1 left join a a2 on a1.id = a2.id)
|
|
left join
|
|
(a a3 left join a a4 on a3.id = a4.id)
|
|
on a2.id = a3.id;
|
|
|
|
explain (costs off)
|
|
select 1 from a t1
|
|
left join a t2 on true
|
|
inner join a t3 on true
|
|
left join a t4 on t2.id = t4.id and t2.id = t3.id;
|
|
|
|
-- another example (bug #17781)
|
|
explain (costs off)
|
|
select ss1.f1
|
|
from int4_tbl as t1
|
|
left join (int4_tbl as t2
|
|
right join int4_tbl as t3 on null
|
|
left join (int4_tbl as t4
|
|
right join int8_tbl as t5 on null)
|
|
on t2.f1 = t4.f1
|
|
left join ((select null as f1 from int4_tbl as t6) as ss1
|
|
inner join int8_tbl as t7 on null)
|
|
on t5.q1 = t7.q2)
|
|
on false;
|
|
|
|
-- variant with Var rather than PHV coming from t6
|
|
explain (costs off)
|
|
select ss1.f1
|
|
from int4_tbl as t1
|
|
left join (int4_tbl as t2
|
|
right join int4_tbl as t3 on null
|
|
left join (int4_tbl as t4
|
|
right join int8_tbl as t5 on null)
|
|
on t2.f1 = t4.f1
|
|
left join ((select f1 from int4_tbl as t6) as ss1
|
|
inner join int8_tbl as t7 on null)
|
|
on t5.q1 = t7.q2)
|
|
on false;
|
|
|
|
-- per further discussion of bug #17781
|
|
explain (costs off)
|
|
select ss1.x
|
|
from (select f1/2 as x from int4_tbl i4 left join a on a.id = i4.f1) ss1
|
|
right join int8_tbl i8 on true
|
|
where current_user is not null; -- this is to add a Result node
|
|
|
|
-- and further discussion of bug #17781
|
|
explain (costs off)
|
|
select *
|
|
from int8_tbl t1
|
|
left join (int8_tbl t2 left join onek t3 on t2.q1 > t3.unique1)
|
|
on t1.q2 = t2.q2
|
|
left join onek t4
|
|
on t2.q2 < t3.unique2;
|
|
|
|
-- More tests of correct placement of pseudoconstant quals
|
|
|
|
-- simple constant-false condition
|
|
explain (costs off)
|
|
select * from int8_tbl t1 left join
|
|
(int8_tbl t2 inner join int8_tbl t3 on false
|
|
left join int8_tbl t4 on t2.q2 = t4.q2)
|
|
on t1.q1 = t2.q1;
|
|
|
|
-- deduce constant-false from an EquivalenceClass
|
|
explain (costs off)
|
|
select * from int8_tbl t1 left join
|
|
(int8_tbl t2 inner join int8_tbl t3 on (t2.q1-t3.q2) = 0 and (t2.q1-t3.q2) = 1
|
|
left join int8_tbl t4 on t2.q2 = t4.q2)
|
|
on t1.q1 = t2.q1;
|
|
|
|
-- pseudoconstant based on an outer-level Param
|
|
explain (costs off)
|
|
select exists(
|
|
select * from int8_tbl t1 left join
|
|
(int8_tbl t2 inner join int8_tbl t3 on x0.f1 = 1
|
|
left join int8_tbl t4 on t2.q2 = t4.q2)
|
|
on t1.q1 = t2.q1
|
|
) from int4_tbl x0;
|
|
|
|
-- check that join removal works for a left join when joining a subquery
|
|
-- that is guaranteed to be unique by its GROUP BY clause
|
|
explain (costs off)
|
|
select d.* from d left join (select * from b group by b.id, b.c_id) s
|
|
on d.a = s.id and d.b = s.c_id;
|
|
|
|
-- check that join removal works for a left join when joining a subquery
|
|
-- that is guaranteed to be unique by GROUPING SETS
|
|
explain (costs off)
|
|
select d.* from d left join (select 1 as x from b group by ()) s
|
|
on d.a = s.x;
|
|
|
|
explain (costs off)
|
|
select d.* from d left join (select 1 as x from b group by grouping sets(())) s
|
|
on d.a = s.x;
|
|
|
|
explain (costs off)
|
|
select d.* from d left join (select 1 as x from b group by grouping sets(()), grouping sets(())) s
|
|
on d.a = s.x;
|
|
|
|
explain (costs off)
|
|
select d.* from d left join (select 1 as x from b group by distinct grouping sets((), ())) s
|
|
on d.a = s.x;
|
|
|
|
-- similarly, but keying off a DISTINCT clause
|
|
explain (costs off)
|
|
select d.* from d left join (select distinct * from b) s
|
|
on d.a = s.id and d.b = s.c_id;
|
|
|
|
-- join removal is not possible when the GROUP BY contains a column that is
|
|
-- not in the join condition. (Note: as of 9.6, we notice that b.id is a
|
|
-- primary key and so drop b.c_id from the GROUP BY of the resulting plan;
|
|
-- but this happens too late for join removal in the outer plan level.)
|
|
explain (costs off)
|
|
select d.* from d left join (select * from b group by b.id, b.c_id) s
|
|
on d.a = s.id;
|
|
|
|
-- join removal is not possible when the GROUP BY contains non-empty grouping
|
|
-- sets or multiple empty grouping sets
|
|
explain (costs off)
|
|
select d.* from d left join (select 1 as x from b group by rollup(x)) s
|
|
on d.a = s.x;
|
|
|
|
explain (costs off)
|
|
select d.* from d left join (select 1 as x from b group by grouping sets((), ())) s
|
|
on d.a = s.x;
|
|
|
|
explain (costs off)
|
|
select d.* from d left join (select 1 as x from b group by grouping sets((), grouping sets(()))) s
|
|
on d.a = s.x;
|
|
|
|
-- similarly, but keying off a DISTINCT clause
|
|
explain (costs off)
|
|
select d.* from d left join (select distinct * from b) s
|
|
on d.a = s.id;
|
|
|
|
-- join removal is not possible here
|
|
explain (costs off)
|
|
select 1 from a t1
|
|
left join (a t2 left join a t3 on t2.id = 1) on t2.id = 1;
|
|
|
|
-- check join removal works when uniqueness of the join condition is enforced
|
|
-- by a UNION
|
|
explain (costs off)
|
|
select d.* from d left join (select id from a union select id from b) s
|
|
on d.a = s.id;
|
|
|
|
-- check join removal with a cross-type comparison operator
|
|
explain (costs off)
|
|
select i8.* from int8_tbl i8 left join (select f1 from int4_tbl group by f1) i4
|
|
on i8.q1 = i4.f1;
|
|
|
|
-- check join removal with lateral references
|
|
explain (costs off)
|
|
select 1 from (select a.id FROM a left join b on a.b_id = b.id) q,
|
|
lateral generate_series(1, q.id) gs(i) where q.id = gs.i;
|
|
|
|
-- check join removal within RHS of an outer join
|
|
explain (costs off)
|
|
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);
|
|
|
|
-- test join removals on a partitioned table
|
|
explain (costs off)
|
|
select a.* from a left join parted_b pb on a.b_id = pb.id;
|
|
|
|
rollback;
|
|
|
|
create temp table parent (k int primary key, pd int);
|
|
create temp table child (k int unique, cd int);
|
|
insert into parent values (1, 10), (2, 20), (3, 30);
|
|
insert into child values (1, 100), (4, 400);
|
|
|
|
-- this case is optimizable
|
|
select p.* from parent p left join child c on (p.k = c.k);
|
|
explain (costs off)
|
|
select p.* from parent p left join child c on (p.k = c.k);
|
|
|
|
-- this case is not
|
|
select p.*, linked from parent p
|
|
left join (select c.*, true as linked from child c) as ss
|
|
on (p.k = ss.k);
|
|
explain (costs off)
|
|
select p.*, linked from parent p
|
|
left join (select c.*, true as linked from child c) as ss
|
|
on (p.k = ss.k);
|
|
|
|
-- check for a 9.0rc1 bug: join removal breaks pseudoconstant qual handling
|
|
select p.* from
|
|
parent p left join child c on (p.k = c.k)
|
|
where p.k = 1 and p.k = 2;
|
|
explain (costs off)
|
|
select p.* from
|
|
parent p left join child c on (p.k = c.k)
|
|
where p.k = 1 and p.k = 2;
|
|
|
|
select p.* from
|
|
(parent p left join child c on (p.k = c.k)) join parent x on p.k = x.k
|
|
where p.k = 1 and p.k = 2;
|
|
explain (costs off)
|
|
select p.* from
|
|
(parent p left join child c on (p.k = c.k)) join parent x on p.k = x.k
|
|
where p.k = 1 and p.k = 2;
|
|
|
|
-- bug 5255: this is not optimizable by join removal
|
|
begin;
|
|
|
|
CREATE TEMP TABLE a (id int PRIMARY KEY);
|
|
CREATE TEMP TABLE b (id int PRIMARY KEY, a_id int);
|
|
INSERT INTO a VALUES (0), (1);
|
|
INSERT INTO b VALUES (0, 0), (1, NULL);
|
|
|
|
SELECT * FROM b LEFT JOIN a ON (b.a_id = a.id) WHERE (a.id IS NULL OR a.id > 0);
|
|
SELECT b.* FROM b LEFT JOIN a ON (b.a_id = a.id) WHERE (a.id IS NULL OR a.id > 0);
|
|
|
|
rollback;
|
|
|
|
-- another join removal bug: this is not optimizable, either
|
|
begin;
|
|
|
|
create temp table innertab (id int8 primary key, dat1 int8);
|
|
insert into innertab values(123, 42);
|
|
|
|
SELECT * FROM
|
|
(SELECT 1 AS x) ss1
|
|
LEFT JOIN
|
|
(SELECT q1, q2, COALESCE(dat1, q1) AS y
|
|
FROM int8_tbl LEFT JOIN innertab ON q2 = id) ss2
|
|
ON true;
|
|
|
|
-- join removal bug #17769: can't remove if there's a pushed-down reference
|
|
EXPLAIN (COSTS OFF)
|
|
SELECT q2 FROM
|
|
(SELECT *
|
|
FROM int8_tbl LEFT JOIN innertab ON q2 = id) ss
|
|
WHERE COALESCE(dat1, 0) = q1;
|
|
|
|
-- join removal bug #17773: otherwise-removable PHV appears in a qual condition
|
|
EXPLAIN (VERBOSE, COSTS OFF)
|
|
SELECT q2 FROM
|
|
(SELECT q2, 'constant'::text AS x
|
|
FROM int8_tbl LEFT JOIN innertab ON q2 = id) ss
|
|
RIGHT JOIN int4_tbl ON NULL
|
|
WHERE x >= x;
|
|
|
|
-- join removal bug #17786: check that OR conditions are cleaned up
|
|
EXPLAIN (COSTS OFF)
|
|
SELECT f1, x
|
|
FROM int4_tbl
|
|
JOIN ((SELECT 42 AS x FROM int8_tbl LEFT JOIN innertab ON q1 = id) AS ss1
|
|
RIGHT JOIN tenk1 ON NULL)
|
|
ON tenk1.unique1 = ss1.x OR tenk1.unique2 = ss1.x;
|
|
|
|
rollback;
|
|
|
|
-- another join removal bug: we must clean up correctly when removing a PHV
|
|
begin;
|
|
|
|
create temp table uniquetbl (f1 text unique);
|
|
|
|
explain (costs off)
|
|
select t1.* from
|
|
uniquetbl as t1
|
|
left join (select *, '***'::text as d1 from uniquetbl) t2
|
|
on t1.f1 = t2.f1
|
|
left join uniquetbl t3
|
|
on t2.d1 = t3.f1;
|
|
|
|
explain (costs off)
|
|
select t0.*
|
|
from
|
|
text_tbl t0
|
|
left join
|
|
(select case t1.ten when 0 then 'doh!'::text else null::text end as case1,
|
|
t1.stringu2
|
|
from tenk1 t1
|
|
join int4_tbl i4 ON i4.f1 = t1.unique2
|
|
left join uniquetbl u1 ON u1.f1 = t1.string4) ss
|
|
on t0.f1 = ss.case1
|
|
where ss.stringu2 !~* ss.case1;
|
|
|
|
select t0.*
|
|
from
|
|
text_tbl t0
|
|
left join
|
|
(select case t1.ten when 0 then 'doh!'::text else null::text end as case1,
|
|
t1.stringu2
|
|
from tenk1 t1
|
|
join int4_tbl i4 ON i4.f1 = t1.unique2
|
|
left join uniquetbl u1 ON u1.f1 = t1.string4) ss
|
|
on t0.f1 = ss.case1
|
|
where ss.stringu2 !~* ss.case1;
|
|
|
|
rollback;
|
|
|
|
-- another join removal bug: we must clean up EquivalenceClasses too
|
|
begin;
|
|
|
|
create temp table t (a int unique);
|
|
insert into t values (1);
|
|
|
|
explain (costs off)
|
|
select 1
|
|
from t t1
|
|
left join (select 2 as c
|
|
from t t2 left join t t3 on t2.a = t3.a) s
|
|
on true
|
|
where t1.a = s.c;
|
|
|
|
select 1
|
|
from t t1
|
|
left join (select 2 as c
|
|
from t t2 left join t t3 on t2.a = t3.a) s
|
|
on true
|
|
where t1.a = s.c;
|
|
|
|
rollback;
|
|
|
|
-- check handling of semijoins after join removal: we must suppress
|
|
-- unique-ification of known-constant values
|
|
begin;
|
|
|
|
create temp table t (a int unique, b int);
|
|
insert into t values (1, 2);
|
|
|
|
explain (verbose, costs off)
|
|
select t1.a from t t1
|
|
left join t t2 on t1.a = t2.a
|
|
join t t3 on true
|
|
where exists (select 1 from t t4
|
|
join t t5 on t4.b = t5.b
|
|
join t t6 on t5.b = t6.b
|
|
where t1.a = t4.a and t3.a = t5.a and t4.a = 1);
|
|
|
|
select t1.a from t t1
|
|
left join t t2 on t1.a = t2.a
|
|
join t t3 on true
|
|
where exists (select 1 from t t4
|
|
join t t5 on t4.b = t5.b
|
|
join t t6 on t5.b = t6.b
|
|
where t1.a = t4.a and t3.a = t5.a and t4.a = 1);
|
|
|
|
rollback;
|
|
|
|
-- check handling of semijoins if all RHS columns are equated to constants: we
|
|
-- should suppress unique-ification in this case.
|
|
begin;
|
|
|
|
create temp table t (a int, b int);
|
|
insert into t values (1, 2);
|
|
|
|
explain (costs off)
|
|
select * from t t1, t t2 where exists
|
|
(select 1 from t t3 where t1.a = t3.a and t2.b = t3.b and t3.a = 1 and t3.b = 2);
|
|
|
|
select * from t t1, t t2 where exists
|
|
(select 1 from t t3 where t1.a = t3.a and t2.b = t3.b and t3.a = 1 and t3.b = 2);
|
|
|
|
rollback;
|
|
|
|
-- check handling of semijoin unique-ification for child relations if all RHS
|
|
-- columns are equated to constants.
|
|
begin;
|
|
|
|
create temp table p (a int, b int) partition by range (a);
|
|
create temp table p1 partition of p for values from (0) to (10);
|
|
create temp table p2 partition of p for values from (10) to (20);
|
|
insert into p values (1, 2);
|
|
insert into p values (10, 20);
|
|
|
|
set enable_partitionwise_join to on;
|
|
|
|
explain (costs off)
|
|
select * from p t1 where exists
|
|
(select 1 from p t2 where t1.a = t2.a and t1.a = 1);
|
|
|
|
select * from p t1 where exists
|
|
(select 1 from p t2 where t1.a = t2.a and t1.a = 1);
|
|
|
|
rollback;
|
|
|
|
-- test cases where we can remove a join, but not a PHV computed at it
|
|
begin;
|
|
|
|
create temp table t (a int unique, b int);
|
|
insert into t values (1,1), (2,2);
|
|
|
|
explain (costs off)
|
|
select 1
|
|
from t t1
|
|
left join (select t2.a, 1 as c
|
|
from t t2 left join t t3 on t2.a = t3.a) s
|
|
on true
|
|
left join t t4 on true
|
|
where s.a < s.c;
|
|
|
|
explain (costs off)
|
|
select t1.a, s.*
|
|
from t t1
|
|
left join lateral (select t2.a, coalesce(t1.a, 1) as c
|
|
from t t2 left join t t3 on t2.a = t3.a) s
|
|
on true
|
|
left join t t4 on true
|
|
where s.a < s.c;
|
|
|
|
select t1.a, s.*
|
|
from t t1
|
|
left join lateral (select t2.a, coalesce(t1.a, 1) as c
|
|
from t t2 left join t t3 on t2.a = t3.a) s
|
|
on true
|
|
left join t t4 on true
|
|
where s.a < s.c;
|
|
|
|
rollback;
|
|
|
|
-- test case to expose miscomputation of required relid set for a PHV
|
|
explain (verbose, costs off)
|
|
select i8.*, ss.v, t.unique2
|
|
from int8_tbl i8
|
|
left join int4_tbl i4 on i4.f1 = 1
|
|
left join lateral (select i4.f1 + 1 as v) as ss on true
|
|
left join tenk1 t on t.unique2 = ss.v
|
|
where q2 = 456;
|
|
|
|
select i8.*, ss.v, t.unique2
|
|
from int8_tbl i8
|
|
left join int4_tbl i4 on i4.f1 = 1
|
|
left join lateral (select i4.f1 + 1 as v) as ss on true
|
|
left join tenk1 t on t.unique2 = ss.v
|
|
where q2 = 456;
|
|
|
|
-- and check a related issue where we miscompute required relids for
|
|
-- a PHV that's been translated to a child rel
|
|
create temp table parttbl (a integer primary key) partition by range (a);
|
|
create temp table parttbl1 partition of parttbl for values from (1) to (100);
|
|
insert into parttbl values (11), (12);
|
|
explain (costs off)
|
|
select * from
|
|
(select *, 12 as phv from parttbl) as ss
|
|
right join int4_tbl on true
|
|
where ss.a = ss.phv and f1 = 0;
|
|
|
|
select * from
|
|
(select *, 12 as phv from parttbl) as ss
|
|
right join int4_tbl on true
|
|
where ss.a = ss.phv and f1 = 0;
|
|
|
|
-- bug #8444: we've historically allowed duplicate aliases within aliased JOINs
|
|
|
|
select * from
|
|
int8_tbl x join (int4_tbl x cross join int4_tbl y) j on q1 = f1; -- error
|
|
select * from
|
|
int8_tbl x join (int4_tbl x cross join int4_tbl y) j on q1 = y.f1; -- error
|
|
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;
|
|
|
|
-- Try PHV, which could potentially be removed completely by SJE, but that's
|
|
-- not implemented yet.
|
|
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;
|
|
|
|
-- This is a degenerate case of PHV usage: it is evaluated and needed inside
|
|
-- a baserel scan operation that the SJE removes. The PHV in this test should
|
|
-- be in the filter of parameterized Index Scan: the replace_nestloop_params()
|
|
-- code will detect if the placeholder list doesn't have a reference to this
|
|
-- parameter.
|
|
--
|
|
-- NOTE: enable_hashjoin and enable_mergejoin must be disabled.
|
|
CREATE TABLE tbl_phv(x int, y int PRIMARY KEY);
|
|
CREATE INDEX tbl_phv_idx ON tbl_phv(x);
|
|
INSERT INTO tbl_phv (x, y)
|
|
SELECT gs, gs FROM generate_series(1,100) AS gs;
|
|
VACUUM ANALYZE tbl_phv;
|
|
EXPLAIN (COSTS OFF, VERBOSE)
|
|
SELECT 1 FROM tbl_phv t1 LEFT JOIN
|
|
(SELECT 1 extra, x, y FROM tbl_phv tl) t3 JOIN
|
|
(SELECT y FROM tbl_phv tr) t4
|
|
ON t4.y = t3.y
|
|
ON true WHERE t3.extra IS NOT NULL AND t3.x = t1.x % 2;
|
|
DROP TABLE IF EXISTS tbl_phv;
|
|
|
|
-- 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;
|
|
|
|
-- Check that SJE correctly replaces relations in OR-clauses
|
|
EXPLAIN (COSTS OFF)
|
|
SELECT * FROM emp1 t1
|
|
INNER JOIN emp1 t2 ON t1.id = t2.id
|
|
LEFT JOIN emp1 t3 ON t1.code = 1 AND (t2.code = t3.code OR t2.code = 1);
|
|
|
|
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
|
|
--
|
|
|
|
select t1.uunique1 from
|
|
tenk1 t1 join tenk2 t2 on t1.two = t2.two; -- error, prefer "t1" suggestion
|
|
select t2.uunique1 from
|
|
tenk1 t1 join tenk2 t2 on t1.two = t2.two; -- error, prefer "t2" suggestion
|
|
select uunique1 from
|
|
tenk1 t1 join tenk2 t2 on t1.two = t2.two; -- error, suggest both at once
|
|
select ctid from
|
|
tenk1 t1 join tenk2 t2 on t1.two = t2.two; -- error, need qualification
|
|
|
|
--
|
|
-- Take care to reference the correct RTE
|
|
--
|
|
|
|
select atts.relid::regclass, s.* from pg_stats s join
|
|
pg_attribute a on s.attname = a.attname and s.tablename =
|
|
a.attrelid::regclass::text join (select unnest(indkey) attnum,
|
|
indexrelid from pg_index i) atts on atts.attnum = a.attnum where
|
|
schemaname != 'pg_catalog';
|
|
|
|
-- Test bug in rangetable flattening
|
|
explain (verbose, costs off)
|
|
select 1 from
|
|
(select * from int8_tbl where q1 <> (select 42) offset 0) ss
|
|
where false;
|
|
|
|
--
|
|
-- Test LATERAL
|
|
--
|
|
|
|
select unique2, x.*
|
|
from tenk1 a, lateral (select * from int4_tbl b where f1 = a.unique1) x;
|
|
explain (costs off)
|
|
select unique2, x.*
|
|
from tenk1 a, lateral (select * from int4_tbl b where f1 = a.unique1) x;
|
|
select unique2, x.*
|
|
from int4_tbl x, lateral (select unique2 from tenk1 where f1 = unique1) ss;
|
|
explain (costs off)
|
|
select unique2, x.*
|
|
from int4_tbl x, lateral (select unique2 from tenk1 where f1 = unique1) ss;
|
|
explain (costs off)
|
|
select unique2, x.*
|
|
from int4_tbl x cross join lateral (select unique2 from tenk1 where f1 = unique1) ss;
|
|
select unique2, x.*
|
|
from int4_tbl x left join lateral (select unique1, unique2 from tenk1 where f1 = unique1) ss on true;
|
|
explain (costs off)
|
|
select unique2, x.*
|
|
from int4_tbl x left join lateral (select unique1, unique2 from tenk1 where f1 = unique1) ss on true;
|
|
|
|
-- check scoping of lateral versus parent references
|
|
-- the first of these should return int8_tbl.q2, the second int8_tbl.q1
|
|
select *, (select r from (select q1 as q2) x, (select q2 as r) y) from int8_tbl;
|
|
select *, (select r from (select q1 as q2) x, lateral (select q2 as r) y) from int8_tbl;
|
|
|
|
-- lateral with function in FROM
|
|
select count(*) from tenk1 a, lateral generate_series(1,two) g;
|
|
explain (costs off)
|
|
select count(*) from tenk1 a, lateral generate_series(1,two) g;
|
|
explain (costs off)
|
|
select count(*) from tenk1 a cross join lateral generate_series(1,two) g;
|
|
-- don't need the explicit LATERAL keyword for functions
|
|
explain (costs off)
|
|
select count(*) from tenk1 a, generate_series(1,two) g;
|
|
|
|
-- lateral with UNION ALL subselect
|
|
explain (costs off)
|
|
select * from generate_series(100,200) g,
|
|
lateral (select * from int8_tbl a where g = q1 union all
|
|
select * from int8_tbl b where g = q2) ss;
|
|
select * from generate_series(100,200) g,
|
|
lateral (select * from int8_tbl a where g = q1 union all
|
|
select * from int8_tbl b where g = q2) ss;
|
|
|
|
-- lateral with VALUES
|
|
explain (costs off)
|
|
select count(*) from tenk1 a,
|
|
tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x;
|
|
select count(*) from tenk1 a,
|
|
tenk1 b join lateral (values(a.unique1)) ss(x) on b.unique2 = ss.x;
|
|
|
|
-- lateral with VALUES, no flattening possible
|
|
explain (costs off)
|
|
select count(*) from tenk1 a,
|
|
tenk1 b join lateral (values(a.unique1),(-1)) ss(x) on b.unique2 = ss.x;
|
|
select count(*) from tenk1 a,
|
|
tenk1 b join lateral (values(a.unique1),(-1)) ss(x) on b.unique2 = ss.x;
|
|
|
|
-- lateral injecting a strange outer join condition
|
|
explain (costs off)
|
|
select * from int8_tbl a,
|
|
int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z)
|
|
on x.q2 = ss.z
|
|
order by a.q1, a.q2, x.q1, x.q2, ss.z;
|
|
select * from int8_tbl a,
|
|
int8_tbl x left join lateral (select a.q1 from int4_tbl y) ss(z)
|
|
on x.q2 = ss.z
|
|
order by a.q1, a.q2, x.q1, x.q2, ss.z;
|
|
|
|
-- lateral reference to a join alias variable
|
|
select * from (select f1/2 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1,
|
|
lateral (select x) ss2(y);
|
|
select * from (select f1 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1,
|
|
lateral (values(x)) ss2(y);
|
|
select * from ((select f1/2 as x from int4_tbl) ss1 join int4_tbl i4 on x = f1) j,
|
|
lateral (select x) ss2(y);
|
|
|
|
-- lateral references requiring pullup
|
|
select * from (values(1)) x(lb),
|
|
lateral generate_series(lb,4) x4;
|
|
select * from (select f1/1000000000 from int4_tbl) x(lb),
|
|
lateral generate_series(lb,4) x4;
|
|
select * from (values(1)) x(lb),
|
|
lateral (values(lb)) y(lbcopy);
|
|
select * from (values(1)) x(lb),
|
|
lateral (select lb from int4_tbl) y(lbcopy);
|
|
select * from
|
|
int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1,
|
|
lateral (values(x.q1,y.q1,y.q2)) v(xq1,yq1,yq2);
|
|
select * from
|
|
int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1,
|
|
lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2);
|
|
select x.* from
|
|
int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1,
|
|
lateral (select x.q1,y.q1,y.q2) v(xq1,yq1,yq2);
|
|
select v.* from
|
|
(int8_tbl x left join (select q1,coalesce(q2,0) q2 from int8_tbl) y on x.q2 = y.q1)
|
|
left join int4_tbl z on z.f1 = x.q2,
|
|
lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy);
|
|
select v.* from
|
|
(int8_tbl x left join (select q1,(select coalesce(q2,0)) q2 from int8_tbl) y on x.q2 = y.q1)
|
|
left join int4_tbl z on z.f1 = x.q2,
|
|
lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy);
|
|
select v.* from
|
|
(int8_tbl x left join (select q1,(select coalesce(q2,0)) q2 from int8_tbl) y on x.q2 = y.q1)
|
|
left join int4_tbl z on z.f1 = x.q2,
|
|
lateral (select x.q1,y.q1 from onerow union all select x.q2,y.q2 from onerow) v(vx,vy);
|
|
|
|
explain (verbose, costs off)
|
|
select * from
|
|
int8_tbl a left join
|
|
lateral (select *, a.q2 as x from int8_tbl b) ss on a.q2 = ss.q1;
|
|
select * from
|
|
int8_tbl a left join
|
|
lateral (select *, a.q2 as x from int8_tbl b) ss on a.q2 = ss.q1;
|
|
explain (verbose, costs off)
|
|
select * from
|
|
int8_tbl a left join
|
|
lateral (select *, coalesce(a.q2, 42) as x from int8_tbl b) ss on a.q2 = ss.q1;
|
|
select * from
|
|
int8_tbl a left join
|
|
lateral (select *, coalesce(a.q2, 42) as x from int8_tbl b) ss on a.q2 = ss.q1;
|
|
|
|
-- lateral can result in join conditions appearing below their
|
|
-- real semantic level
|
|
explain (verbose, costs off)
|
|
select * from int4_tbl i left join
|
|
lateral (select * from int2_tbl j where i.f1 = j.f1) k on true;
|
|
select * from int4_tbl i left join
|
|
lateral (select * from int2_tbl j where i.f1 = j.f1) k on true;
|
|
explain (verbose, costs off)
|
|
select * from int4_tbl i left join
|
|
lateral (select coalesce(i, i) from int2_tbl j where i.f1 = j.f1) k on true;
|
|
select * from int4_tbl i left join
|
|
lateral (select coalesce(i, i) from int2_tbl j where i.f1 = j.f1) k on true;
|
|
explain (verbose, costs off)
|
|
select * from int4_tbl a,
|
|
lateral (
|
|
select * from int4_tbl b left join int8_tbl c on (b.f1 = q1 and a.f1 = q2)
|
|
) ss;
|
|
select * from int4_tbl a,
|
|
lateral (
|
|
select * from int4_tbl b left join int8_tbl c on (b.f1 = q1 and a.f1 = q2)
|
|
) ss;
|
|
|
|
-- lateral reference in a PlaceHolderVar evaluated at join level
|
|
explain (verbose, costs off)
|
|
select * from
|
|
int8_tbl a left join lateral
|
|
(select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from
|
|
int8_tbl b cross join int8_tbl c) ss
|
|
on a.q2 = ss.bq1;
|
|
select * from
|
|
int8_tbl a left join lateral
|
|
(select b.q1 as bq1, c.q1 as cq1, least(a.q1,b.q1,c.q1) from
|
|
int8_tbl b cross join int8_tbl c) ss
|
|
on a.q2 = ss.bq1;
|
|
|
|
-- case requiring nested PlaceHolderVars
|
|
explain (verbose, costs off)
|
|
select * from
|
|
int8_tbl c left join (
|
|
int8_tbl a left join (select q1, coalesce(q2,42) as x from int8_tbl b) ss1
|
|
on a.q2 = ss1.q1
|
|
cross join
|
|
lateral (select q1, coalesce(ss1.x,q2) as y from int8_tbl d) ss2
|
|
) on c.q2 = ss2.q1,
|
|
lateral (select ss2.y offset 0) ss3;
|
|
|
|
-- another case requiring nested PlaceHolderVars
|
|
explain (verbose, costs off)
|
|
select * from
|
|
(select 0 as val0) as ss0
|
|
left join (select 1 as val) as ss1 on true
|
|
left join lateral (select ss1.val as val_filtered where false) as ss2 on true;
|
|
|
|
select * from
|
|
(select 0 as val0) as ss0
|
|
left join (select 1 as val) as ss1 on true
|
|
left join lateral (select ss1.val as val_filtered where false) as ss2 on true;
|
|
|
|
-- case that breaks the old ph_may_need optimization
|
|
explain (verbose, costs off)
|
|
select c.*,a.*,ss1.q1,ss2.q1,ss3.* from
|
|
int8_tbl c left join (
|
|
int8_tbl a left join
|
|
(select q1, coalesce(q2,f1) as x from int8_tbl b, int4_tbl b2
|
|
where q1 < f1) ss1
|
|
on a.q2 = ss1.q1
|
|
cross join
|
|
lateral (select q1, coalesce(ss1.x,q2) as y from int8_tbl d) ss2
|
|
) on c.q2 = ss2.q1,
|
|
lateral (select * from int4_tbl i where ss2.y > f1) ss3;
|
|
|
|
-- check processing of postponed quals (bug #9041)
|
|
explain (verbose, costs off)
|
|
select * from
|
|
(select 1 as x offset 0) x cross join (select 2 as y offset 0) y
|
|
left join lateral (
|
|
select * from (select 3 as z offset 0) z where z.z = x.x
|
|
) zz on zz.z = y.y;
|
|
|
|
-- a new postponed-quals issue (bug #17768)
|
|
explain (costs off)
|
|
select * from int4_tbl t1,
|
|
lateral (select * from int4_tbl t2 inner join int4_tbl t3 on t1.f1 = 1
|
|
inner join (int4_tbl t4 left join int4_tbl t5 on true) on true) ss;
|
|
|
|
-- check dummy rels with lateral references (bug #15694)
|
|
explain (verbose, costs off)
|
|
select * from int8_tbl i8 left join lateral
|
|
(select *, i8.q2 from int4_tbl where false) ss on true;
|
|
explain (verbose, costs off)
|
|
select * from int8_tbl i8 left join lateral
|
|
(select *, i8.q2 from int4_tbl i1, int4_tbl i2 where false) ss on true;
|
|
|
|
-- check handling of nested appendrels inside LATERAL
|
|
select * from
|
|
((select 2 as v) union all (select 3 as v)) as q1
|
|
cross join lateral
|
|
((select * from
|
|
((select 4 as v) union all (select 5 as v)) as q3)
|
|
union all
|
|
(select q1.v)
|
|
) as q2;
|
|
|
|
-- check the number of columns specified
|
|
SELECT * FROM (int8_tbl i cross join int4_tbl j) ss(a,b,c,d);
|
|
|
|
-- check we don't try to do a unique-ified semijoin with LATERAL
|
|
explain (verbose, costs off)
|
|
select * from
|
|
(values (0,9998), (1,1000)) v(id,x),
|
|
lateral (select f1 from int4_tbl
|
|
where f1 = any (select unique1 from tenk1
|
|
where unique2 = v.x offset 0)) ss;
|
|
select * from
|
|
(values (0,9998), (1,1000)) v(id,x),
|
|
lateral (select f1 from int4_tbl
|
|
where f1 = any (select unique1 from tenk1
|
|
where unique2 = v.x offset 0)) ss;
|
|
|
|
-- check proper extParam/allParam handling (this isn't exactly a LATERAL issue,
|
|
-- but we can make the test case much more compact with LATERAL)
|
|
explain (verbose, costs off)
|
|
select * from (values (0), (1)) v(id),
|
|
lateral (select * from int8_tbl t1,
|
|
lateral (select * from
|
|
(select * from int8_tbl t2
|
|
where (q1, random() > 0) = any (select q2, random() > 0 from int8_tbl t3
|
|
where q2 = (select greatest(t1.q1,t2.q2))
|
|
and (select v.id=0)) offset 0) ss2) ss
|
|
where t1.q1 = ss.q2) ss0;
|
|
|
|
select * from (values (0), (1)) v(id),
|
|
lateral (select * from int8_tbl t1,
|
|
lateral (select * from
|
|
(select * from int8_tbl t2
|
|
where (q1, random() > 0) = any (select q2, random() > 0 from int8_tbl t3
|
|
where q2 = (select greatest(t1.q1,t2.q2))
|
|
and (select v.id=0)) offset 0) ss2) ss
|
|
where t1.q1 = ss.q2) ss0;
|
|
|
|
-- test some error cases where LATERAL should have been used but wasn't
|
|
select f1,g from int4_tbl a, (select f1 as g) ss;
|
|
select f1,g from int4_tbl a, (select a.f1 as g) ss;
|
|
select f1,g from int4_tbl a cross join (select f1 as g) ss;
|
|
select f1,g from int4_tbl a cross join (select a.f1 as g) ss;
|
|
-- SQL:2008 says the left table is in scope but illegal to access here
|
|
select f1,g from int4_tbl a right join lateral generate_series(0, a.f1) g on true;
|
|
select f1,g from int4_tbl a full join lateral generate_series(0, a.f1) g on true;
|
|
-- check we complain about ambiguous table references
|
|
select * from
|
|
int8_tbl x cross join (int4_tbl x cross join lateral (select x.f1) ss);
|
|
-- LATERAL can be used to put an aggregate into the FROM clause of its query
|
|
select 1 from tenk1 a, lateral (select max(a.unique1) from int4_tbl b) ss;
|
|
|
|
-- check behavior of LATERAL in UPDATE/DELETE
|
|
|
|
create temp table xx1 as select f1 as x1, -f1 as x2 from int4_tbl;
|
|
|
|
-- error, can't do this:
|
|
update xx1 set x2 = f1 from (select * from int4_tbl where f1 = x1) ss;
|
|
update xx1 set x2 = f1 from (select * from int4_tbl where f1 = xx1.x1) ss;
|
|
-- can't do it even with LATERAL:
|
|
update xx1 set x2 = f1 from lateral (select * from int4_tbl where f1 = x1) ss;
|
|
-- we might in future allow something like this, but for now it's an error:
|
|
update xx1 set x2 = f1 from xx1, lateral (select * from int4_tbl where f1 = x1) ss;
|
|
|
|
-- also errors:
|
|
delete from xx1 using (select * from int4_tbl where f1 = x1) ss;
|
|
delete from xx1 using (select * from int4_tbl where f1 = xx1.x1) ss;
|
|
delete from xx1 using lateral (select * from int4_tbl where f1 = x1) ss;
|
|
|
|
--
|
|
-- test LATERAL reference propagation down a multi-level inheritance hierarchy
|
|
-- produced for a multi-level partitioned table hierarchy.
|
|
--
|
|
create table join_pt1 (a int, b int, c varchar) partition by range(a);
|
|
create table join_pt1p1 partition of join_pt1 for values from (0) to (100) partition by range(b);
|
|
create table join_pt1p2 partition of join_pt1 for values from (100) to (200);
|
|
create table join_pt1p1p1 partition of join_pt1p1 for values from (0) to (100);
|
|
insert into join_pt1 values (1, 1, 'x'), (101, 101, 'y');
|
|
create table join_ut1 (a int, b int, c varchar);
|
|
insert into join_ut1 values (101, 101, 'y'), (2, 2, 'z');
|
|
explain (verbose, costs off)
|
|
select t1.b, ss.phv from join_ut1 t1 left join lateral
|
|
(select t2.a as t2a, t3.a t3a, least(t1.a, t2.a, t3.a) phv
|
|
from join_pt1 t2 join join_ut1 t3 on t2.a = t3.b) ss
|
|
on t1.a = ss.t2a order by t1.a;
|
|
select t1.b, ss.phv from join_ut1 t1 left join lateral
|
|
(select t2.a as t2a, t3.a t3a, least(t1.a, t2.a, t3.a) phv
|
|
from join_pt1 t2 join join_ut1 t3 on t2.a = t3.b) ss
|
|
on t1.a = ss.t2a order by t1.a;
|
|
|
|
drop table join_pt1;
|
|
drop table join_ut1;
|
|
|
|
--
|
|
-- test estimation behavior with multi-column foreign key and constant qual
|
|
--
|
|
|
|
begin;
|
|
|
|
create table fkest (x integer, x10 integer, x10b integer, x100 integer);
|
|
insert into fkest select x, x/10, x/10, x/100 from generate_series(1,1000) x;
|
|
create unique index on fkest(x, x10, x100);
|
|
analyze fkest;
|
|
|
|
explain (costs off)
|
|
select * from fkest f1
|
|
join fkest f2 on (f1.x = f2.x and f1.x10 = f2.x10b and f1.x100 = f2.x100)
|
|
join fkest f3 on f1.x = f3.x
|
|
where f1.x100 = 2;
|
|
|
|
alter table fkest add constraint fk
|
|
foreign key (x, x10b, x100) references fkest (x, x10, x100);
|
|
|
|
explain (costs off)
|
|
select * from fkest f1
|
|
join fkest f2 on (f1.x = f2.x and f1.x10 = f2.x10b and f1.x100 = f2.x100)
|
|
join fkest f3 on f1.x = f3.x
|
|
where f1.x100 = 2;
|
|
|
|
rollback;
|
|
|
|
--
|
|
-- test that foreign key join estimation performs sanely for outer joins
|
|
--
|
|
|
|
begin;
|
|
|
|
create table fkest (a int, b int, c int unique, primary key(a,b));
|
|
create table fkest1 (a int, b int, primary key(a,b));
|
|
|
|
insert into fkest select x/10, x%10, x from generate_series(1,1000) x;
|
|
insert into fkest1 select x/10, x%10 from generate_series(1,1000) x;
|
|
|
|
alter table fkest1
|
|
add constraint fkest1_a_b_fkey foreign key (a,b) references fkest;
|
|
|
|
analyze fkest;
|
|
analyze fkest1;
|
|
|
|
explain (costs off)
|
|
select *
|
|
from fkest f
|
|
left join fkest1 f1 on f.a = f1.a and f.b = f1.b
|
|
left join fkest1 f2 on f.a = f2.a and f.b = f2.b
|
|
left join fkest1 f3 on f.a = f3.a and f.b = f3.b
|
|
where f.c = 1;
|
|
|
|
rollback;
|
|
|
|
--
|
|
-- test planner's ability to mark joins as unique
|
|
--
|
|
|
|
create table j1 (id int primary key);
|
|
create table j2 (id int primary key);
|
|
create table j3 (id int);
|
|
|
|
insert into j1 values(1),(2),(3);
|
|
insert into j2 values(1),(2),(3);
|
|
insert into j3 values(1),(1);
|
|
|
|
analyze j1;
|
|
analyze j2;
|
|
analyze j3;
|
|
|
|
-- ensure join is properly marked as unique
|
|
explain (verbose, costs off)
|
|
select * from j1 inner join j2 on j1.id = j2.id;
|
|
|
|
-- ensure join is not unique when not an equi-join
|
|
explain (verbose, costs off)
|
|
select * from j1 inner join j2 on j1.id > j2.id;
|
|
|
|
-- ensure non-unique rel is not chosen as inner
|
|
explain (verbose, costs off)
|
|
select * from j1 inner join j3 on j1.id = j3.id;
|
|
|
|
-- ensure left join is marked as unique
|
|
explain (verbose, costs off)
|
|
select * from j1 left join j2 on j1.id = j2.id;
|
|
|
|
-- ensure right join is marked as unique
|
|
explain (verbose, costs off)
|
|
select * from j1 right join j2 on j1.id = j2.id;
|
|
|
|
-- ensure full join is marked as unique
|
|
explain (verbose, costs off)
|
|
select * from j1 full join j2 on j1.id = j2.id;
|
|
|
|
-- a clauseless (cross) join can't be unique
|
|
explain (verbose, costs off)
|
|
select * from j1 cross join j2;
|
|
|
|
-- ensure a natural join is marked as unique
|
|
explain (verbose, costs off)
|
|
select * from j1 natural join j2;
|
|
|
|
-- ensure a distinct clause allows the inner to become unique
|
|
explain (verbose, costs off)
|
|
select * from j1
|
|
inner join (select distinct id from j3) j3 on j1.id = j3.id;
|
|
|
|
-- ensure group by clause allows the inner to become unique
|
|
explain (verbose, costs off)
|
|
select * from j1
|
|
inner join (select id from j3 group by id) j3 on j1.id = j3.id;
|
|
|
|
drop table j1;
|
|
drop table j2;
|
|
drop table j3;
|
|
|
|
-- test more complex permutations of unique joins
|
|
|
|
create table j1 (id1 int, id2 int, primary key(id1,id2));
|
|
create table j2 (id1 int, id2 int, primary key(id1,id2));
|
|
create table j3 (id1 int, id2 int, primary key(id1,id2));
|
|
|
|
insert into j1 values(1,1),(1,2);
|
|
insert into j2 values(1,1);
|
|
insert into j3 values(1,1);
|
|
|
|
analyze j1;
|
|
analyze j2;
|
|
analyze j3;
|
|
|
|
-- ensure there's no unique join when not all columns which are part of the
|
|
-- unique index are seen in the join clause
|
|
explain (verbose, costs off)
|
|
select * from j1
|
|
inner join j2 on j1.id1 = j2.id1;
|
|
|
|
-- ensure proper unique detection with multiple join quals
|
|
explain (verbose, costs off)
|
|
select * from j1
|
|
inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2;
|
|
|
|
-- ensure we don't detect the join to be unique when quals are not part of the
|
|
-- join condition
|
|
explain (verbose, costs off)
|
|
select * from j1
|
|
inner join j2 on j1.id1 = j2.id1 where j1.id2 = 1;
|
|
|
|
-- as above, but for left joins.
|
|
explain (verbose, costs off)
|
|
select * from j1
|
|
left join j2 on j1.id1 = j2.id1 where j1.id2 = 1;
|
|
|
|
create unique index j1_id2_idx on j1(id2) where id2 > 0;
|
|
|
|
-- ensure we don't use a partial unique index as unique proofs
|
|
explain (verbose, costs off)
|
|
select * from j1
|
|
inner join j2 on j1.id2 = j2.id2;
|
|
|
|
drop index j1_id2_idx;
|
|
|
|
-- validate logic in merge joins which skips mark and restore.
|
|
-- it should only do this if all quals which were used to detect the unique
|
|
-- are present as join quals, and not plain quals.
|
|
set enable_nestloop to 0;
|
|
set enable_hashjoin to 0;
|
|
set enable_sort to 0;
|
|
|
|
-- create indexes that will be preferred over the PKs to perform the join
|
|
create index j1_id1_idx on j1 (id1) where id1 % 1000 = 1;
|
|
create index j2_id1_idx on j2 (id1) where id1 % 1000 = 1;
|
|
|
|
-- need an additional row in j2, if we want j2_id1_idx to be preferred
|
|
insert into j2 values(1,2);
|
|
analyze j2;
|
|
|
|
explain (costs off) select * from j1
|
|
inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2
|
|
where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1;
|
|
|
|
select * from j1
|
|
inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2
|
|
where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1;
|
|
|
|
-- Exercise array keys mark/restore B-Tree code
|
|
explain (costs off) select * from j1
|
|
inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2
|
|
where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1 and j2.id1 = any (array[1]);
|
|
|
|
select * from j1
|
|
inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2
|
|
where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1 and j2.id1 = any (array[1]);
|
|
|
|
-- Exercise array keys "find extreme element" B-Tree code
|
|
explain (costs off) select * from j1
|
|
inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2
|
|
where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1 and j2.id1 >= any (array[1,5]);
|
|
|
|
select * from j1
|
|
inner join j2 on j1.id1 = j2.id1 and j1.id2 = j2.id2
|
|
where j1.id1 % 1000 = 1 and j2.id1 % 1000 = 1 and j2.id1 >= any (array[1,5]);
|
|
|
|
reset enable_nestloop;
|
|
reset enable_hashjoin;
|
|
reset enable_sort;
|
|
|
|
drop table j1;
|
|
drop table j2;
|
|
drop table j3;
|
|
|
|
-- check that semijoin inner is not seen as unique for a portion of the outerrel
|
|
explain (verbose, costs off)
|
|
select t1.unique1, t2.hundred
|
|
from onek t1, tenk1 t2
|
|
where exists (select 1 from tenk1 t3
|
|
where t3.thousand = t1.unique1 and t3.tenthous = t2.hundred)
|
|
and t1.unique1 < 1;
|
|
|
|
-- ... unless it actually is unique
|
|
create table j3 as select unique1, tenthous from onek;
|
|
vacuum analyze j3;
|
|
create unique index on j3(unique1, tenthous);
|
|
|
|
explain (verbose, costs off)
|
|
select t1.unique1, t2.hundred
|
|
from onek t1, tenk1 t2
|
|
where exists (select 1 from j3
|
|
where j3.unique1 = t1.unique1 and j3.tenthous = t2.hundred)
|
|
and t1.unique1 < 1;
|
|
|
|
drop table j3;
|
|
|
|
-- Exercise the "skip fetch" Bitmap Heap Scan optimization when candidate
|
|
-- tuples are discarded. This may occur when:
|
|
-- 1. A join doesn't require all inner tuples to be scanned for each outer
|
|
-- tuple, and
|
|
-- 2. The inner side is scanned using a bitmap heap scan, and
|
|
-- 3. The bitmap heap scan is eligible for the "skip fetch" optimization.
|
|
-- This optimization is usable when no data from the underlying table is
|
|
-- needed. Use a temp table so it is only visible to this backend and
|
|
-- vacuum may reliably mark all blocks in the table all visible in the
|
|
-- visibility map.
|
|
CREATE TEMP TABLE skip_fetch (a INT, b INT) WITH (fillfactor=10);
|
|
INSERT INTO skip_fetch SELECT i % 3, i FROM generate_series(0,30) i;
|
|
CREATE INDEX ON skip_fetch(a);
|
|
VACUUM (ANALYZE) skip_fetch;
|
|
|
|
SET enable_indexonlyscan = off;
|
|
SET enable_seqscan = off;
|
|
EXPLAIN (COSTS OFF)
|
|
SELECT t1.a FROM skip_fetch t1 LEFT JOIN skip_fetch t2 ON t2.a = 1 WHERE t2.a IS NULL;
|
|
SELECT t1.a FROM skip_fetch t1 LEFT JOIN skip_fetch t2 ON t2.a = 1 WHERE t2.a IS NULL;
|
|
|
|
RESET enable_indexonlyscan;
|
|
RESET enable_seqscan;
|
|
-- Test BitmapHeapScan with a rescan releases resources correctly
|
|
SET enable_seqscan = off;
|
|
SET enable_indexscan = off;
|
|
|
|
CREATE TEMP TABLE rescan_bhs (a INT);
|
|
INSERT INTO rescan_bhs VALUES (1), (2);
|
|
CREATE INDEX ON rescan_bhs (a);
|
|
|
|
EXPLAIN (COSTS OFF)
|
|
SELECT * FROM rescan_bhs t1 LEFT JOIN rescan_bhs t2 ON t1.a IN
|
|
(SELECT a FROM rescan_bhs t3 WHERE t2.a > 1);
|
|
SELECT * FROM rescan_bhs t1 LEFT JOIN rescan_bhs t2 ON t1.a IN
|
|
(SELECT a FROM rescan_bhs t3 WHERE t2.a > 1);
|
|
|
|
RESET enable_seqscan;
|
|
RESET enable_indexscan;
|
|
|
|
-- Test that we do not account for nullingrels when looking up statistics
|
|
CREATE TABLE group_tbl (a INT, b INT);
|
|
INSERT INTO group_tbl SELECT 1, 1;
|
|
CREATE STATISTICS group_tbl_stat (ndistinct) ON a, b FROM group_tbl;
|
|
ANALYZE group_tbl;
|
|
|
|
EXPLAIN (COSTS OFF)
|
|
SELECT 1 FROM group_tbl t1
|
|
LEFT JOIN (SELECT a c1, COALESCE(a, a) c2 FROM group_tbl t2) s ON TRUE
|
|
GROUP BY s.c1, s.c2;
|
|
|
|
DROP TABLE group_tbl;
|
|
|
|
-- Test that we ignore PlaceHolderVars when looking up statistics
|
|
EXPLAIN (COSTS OFF)
|
|
SELECT t1.unique1 FROM tenk1 t1 LEFT JOIN
|
|
(SELECT *, 42 AS phv FROM tenk1 t2) ss ON t1.unique2 = ss.unique2
|
|
WHERE ss.unique1 = ss.phv AND t1.unique1 < 100;
|
|
|
|
SELECT t1.unique1 FROM tenk1 t1 LEFT JOIN
|
|
(SELECT *, 42 AS phv FROM tenk1 t2) ss ON t1.unique2 = ss.unique2
|
|
WHERE ss.unique1 = ss.phv AND t1.unique1 < 100;
|
|
|
|
--
|
|
-- Test for a nested loop join involving index scan, transforming OR-clauses
|
|
-- to SAOP.
|
|
--
|
|
|
|
EXPLAIN (COSTS OFF)
|
|
SELECT COUNT(*) FROM tenk1 t1, tenk1 t2
|
|
WHERE t2.thousand = t1.tenthous OR t2.thousand = t1.unique1 OR t2.thousand = t1.unique2;
|
|
SELECT COUNT(*) FROM tenk1 t1, tenk1 t2
|
|
WHERE t2.thousand = t1.tenthous OR t2.thousand = t1.unique1 OR t2.thousand = t1.unique2;
|
|
|
|
EXPLAIN (COSTS OFF)
|
|
SELECT COUNT(*) FROM onek t1 LEFT JOIN tenk1 t2
|
|
ON (t2.thousand = t1.tenthous OR t2.thousand = t1.thousand);
|
|
SELECT COUNT(*) FROM onek t1 LEFT JOIN tenk1 t2
|
|
ON (t2.thousand = t1.tenthous OR t2.thousand = t1.thousand);
|