From bdabe000d7f00430bc6a7b9afb8d766b81c3022f Mon Sep 17 00:00:00 2001 From: Peter Toth Date: Tue, 11 Jun 2019 10:29:48 +0200 Subject: [PATCH 1/8] [SPARK-28034][SQL] Add with.sql --- .../resources/sql-tests/inputs/pgSQL/with.sql | 1048 ++++++++ .../sql-tests/results/pgSQL/with.sql.out | 2291 +++++++++++++++++ 2 files changed, 3339 insertions(+) create mode 100644 sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql create mode 100644 sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out diff --git a/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql b/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql new file mode 100644 index 000000000000..ed2d6cacf0dc --- /dev/null +++ b/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql @@ -0,0 +1,1048 @@ +-- +-- Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group +-- +-- +-- WITH +-- https://github.com/postgres/postgres/blob/REL_12_BETA1/src/test/regress/sql/with.sql + +-- +-- Tests for common table expressions (WITH query, ... SELECT ...) +-- + +-- Basic WITH +WITH q1(x,y) AS (SELECT 1,2) +SELECT * FROM q1, q1 AS q2; + +-- Multiple uses are evaluated only once +SELECT count(*) FROM ( + WITH q1(x) AS (SELECT random() FROM generate_series(1, 5)) + SELECT * FROM q1 + UNION + SELECT * FROM q1 +) ss; + +-- WITH RECURSIVE + +-- sum of 1..100 +WITH RECURSIVE t(n) AS ( + VALUES (1) +UNION ALL + SELECT n+1 FROM t WHERE n < 100 +) +SELECT sum(n) FROM t; + +WITH RECURSIVE t(n) AS ( + SELECT (VALUES(1)) +UNION ALL + SELECT n+1 FROM t WHERE n < 5 +) +SELECT * FROM t; + +-- recursive view +CREATE RECURSIVE VIEW nums (n) AS + VALUES (1) +UNION ALL + SELECT n+1 FROM nums WHERE n < 5; + +SELECT * FROM nums; + +CREATE OR REPLACE RECURSIVE VIEW nums (n) AS + VALUES (1) +UNION ALL + SELECT n+1 FROM nums WHERE n < 6; + +SELECT * FROM nums; + +-- This is an infinite loop with UNION ALL, but not with UNION +WITH RECURSIVE t(n) AS ( + SELECT 1 +UNION + SELECT 10-n FROM t) +SELECT * FROM t; + +-- This'd be an infinite loop, but outside query reads only as much as needed +WITH RECURSIVE t(n) AS ( + VALUES (1) +UNION ALL + SELECT n+1 FROM t) +SELECT * FROM t LIMIT 10; + +-- UNION case should have same property +WITH RECURSIVE t(n) AS ( + SELECT 1 +UNION + SELECT n+1 FROM t) +SELECT * FROM t LIMIT 10; + +-- Test behavior with an unknown-type literal in the WITH +WITH q AS (SELECT 'foo' AS x) +SELECT x, x IS OF (text) AS is_text FROM q; + +WITH RECURSIVE t(n) AS ( + SELECT 'foo' +UNION ALL + SELECT n || ' bar' FROM t WHERE length(n) < 20 +) +SELECT n, n IS OF (text) AS is_text FROM t; + +-- In a perfect world, this would work and resolve the literal as int ... +-- but for now, we have to be content with resolving to text too soon. +WITH RECURSIVE t(n) AS ( + SELECT '7' +UNION ALL + SELECT n+1 FROM t WHERE n < 10 +) +SELECT n, n IS OF (int) AS is_int FROM t; + +-- +-- Some examples with a tree +-- +-- department structure represented here is as follows: +-- +-- ROOT-+->A-+->B-+->C +-- | | +-- | +->D-+->F +-- +->E-+->G + +CREATE TEMP TABLE department ( + id INTEGER PRIMARY KEY, -- department ID + parent_department INTEGER REFERENCES department, -- upper department ID + name TEXT -- department name +); + +INSERT INTO department VALUES (0, NULL, 'ROOT'); +INSERT INTO department VALUES (1, 0, 'A'); +INSERT INTO department VALUES (2, 1, 'B'); +INSERT INTO department VALUES (3, 2, 'C'); +INSERT INTO department VALUES (4, 2, 'D'); +INSERT INTO department VALUES (5, 0, 'E'); +INSERT INTO department VALUES (6, 4, 'F'); +INSERT INTO department VALUES (7, 5, 'G'); + + +-- extract all departments under 'A'. Result should be A, B, C, D and F +WITH RECURSIVE subdepartment AS +( + -- non recursive term + SELECT name as root_name, * FROM department WHERE name = 'A' + + UNION ALL + + -- recursive term + SELECT sd.root_name, d.* FROM department AS d, subdepartment AS sd + WHERE d.parent_department = sd.id +) +SELECT * FROM subdepartment ORDER BY name; + +-- extract all departments under 'A' with "level" number +WITH RECURSIVE subdepartment(level, id, parent_department, name) AS +( + -- non recursive term + SELECT 1, * FROM department WHERE name = 'A' + + UNION ALL + + -- recursive term + SELECT sd.level + 1, d.* FROM department AS d, subdepartment AS sd + WHERE d.parent_department = sd.id +) +SELECT * FROM subdepartment ORDER BY name; + +-- extract all departments under 'A' with "level" number. +-- Only shows level 2 or more +WITH RECURSIVE subdepartment(level, id, parent_department, name) AS +( + -- non recursive term + SELECT 1, * FROM department WHERE name = 'A' + + UNION ALL + + -- recursive term + SELECT sd.level + 1, d.* FROM department AS d, subdepartment AS sd + WHERE d.parent_department = sd.id +) +SELECT * FROM subdepartment WHERE level >= 2 ORDER BY name; + +-- "RECURSIVE" is ignored if the query has no self-reference +WITH RECURSIVE subdepartment AS +( + -- note lack of recursive UNION structure + SELECT * FROM department WHERE name = 'A' +) +SELECT * FROM subdepartment ORDER BY name; + +-- inside subqueries +SELECT count(*) FROM ( + WITH RECURSIVE t(n) AS ( + SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 500 + ) + SELECT * FROM t) AS t WHERE n < ( + SELECT count(*) FROM ( + WITH RECURSIVE t(n) AS ( + SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 100 + ) + SELECT * FROM t WHERE n < 50000 + ) AS t WHERE n < 100); + +-- use same CTE twice at different subquery levels +WITH q1(x,y) AS ( + SELECT hundred, sum(ten) FROM tenk1 GROUP BY hundred + ) +SELECT count(*) FROM q1 WHERE y > (SELECT sum(y)/100 FROM q1 qsub); + +-- via a VIEW +CREATE TEMPORARY VIEW vsubdepartment AS + WITH RECURSIVE subdepartment AS + ( + -- non recursive term + SELECT * FROM department WHERE name = 'A' + UNION ALL + -- recursive term + SELECT d.* FROM department AS d, subdepartment AS sd + WHERE d.parent_department = sd.id + ) + SELECT * FROM subdepartment; + +SELECT * FROM vsubdepartment ORDER BY name; + +-- Check reverse listing +SELECT pg_get_viewdef('vsubdepartment'::regclass); +SELECT pg_get_viewdef('vsubdepartment'::regclass, true); + +-- Another reverse-listing example +CREATE VIEW sums_1_100 AS +WITH RECURSIVE t(n) AS ( + VALUES (1) +UNION ALL + SELECT n+1 FROM t WHERE n < 100 +) +SELECT sum(n) FROM t; + +\d+ sums_1_100 + +-- corner case in which sub-WITH gets initialized first +with recursive q as ( + select * from department + union all + (with x as (select * from q) + select * from x) + ) +select * from q limit 24; + +with recursive q as ( + select * from department + union all + (with recursive x as ( + select * from department + union all + (select * from q union all select * from x) + ) + select * from x) + ) +select * from q limit 32; + +-- recursive term has sub-UNION +WITH RECURSIVE t(i,j) AS ( + VALUES (1,2) + UNION ALL + SELECT t2.i, t.j+1 FROM + (SELECT 2 AS i UNION ALL SELECT 3 AS i) AS t2 + JOIN t ON (t2.i = t.i+1)) + + SELECT * FROM t; + +-- +-- different tree example +-- +CREATE TEMPORARY TABLE tree( + id INTEGER PRIMARY KEY, + parent_id INTEGER REFERENCES tree(id) +); + +INSERT INTO tree +VALUES (1, NULL), (2, 1), (3,1), (4,2), (5,2), (6,2), (7,3), (8,3), + (9,4), (10,4), (11,7), (12,7), (13,7), (14, 9), (15,11), (16,11); + +-- +-- get all paths from "second level" nodes to leaf nodes +-- +WITH RECURSIVE t(id, path) AS ( + VALUES(1,ARRAY[]::integer[]) +UNION ALL + SELECT tree.id, t.path || tree.id + FROM tree JOIN t ON (tree.parent_id = t.id) +) +SELECT t1.*, t2.* FROM t AS t1 JOIN t AS t2 ON + (t1.path[1] = t2.path[1] AND + array_upper(t1.path,1) = 1 AND + array_upper(t2.path,1) > 1) + ORDER BY t1.id, t2.id; + +-- just count 'em +WITH RECURSIVE t(id, path) AS ( + VALUES(1,ARRAY[]::integer[]) +UNION ALL + SELECT tree.id, t.path || tree.id + FROM tree JOIN t ON (tree.parent_id = t.id) +) +SELECT t1.id, count(t2.*) FROM t AS t1 JOIN t AS t2 ON + (t1.path[1] = t2.path[1] AND + array_upper(t1.path,1) = 1 AND + array_upper(t2.path,1) > 1) + GROUP BY t1.id + ORDER BY t1.id; + +-- this variant tickled a whole-row-variable bug in 8.4devel +WITH RECURSIVE t(id, path) AS ( + VALUES(1,ARRAY[]::integer[]) +UNION ALL + SELECT tree.id, t.path || tree.id + FROM tree JOIN t ON (tree.parent_id = t.id) +) +SELECT t1.id, t2.path, t2 FROM t AS t1 JOIN t AS t2 ON +(t1.id=t2.id); + +-- +-- test cycle detection +-- +create temp table graph( f int, t int, label text ); + +insert into graph values + (1, 2, 'arc 1 -> 2'), + (1, 3, 'arc 1 -> 3'), + (2, 3, 'arc 2 -> 3'), + (1, 4, 'arc 1 -> 4'), + (4, 5, 'arc 4 -> 5'), + (5, 1, 'arc 5 -> 1'); + +with recursive search_graph(f, t, label, path, cycle) as ( + select *, array[row(g.f, g.t)], false from graph g + union all + select g.*, path || row(g.f, g.t), row(g.f, g.t) = any(path) + from graph g, search_graph sg + where g.f = sg.t and not cycle +) +select * from search_graph; + +-- ordering by the path column has same effect as SEARCH DEPTH FIRST +with recursive search_graph(f, t, label, path, cycle) as ( + select *, array[row(g.f, g.t)], false from graph g + union all + select g.*, path || row(g.f, g.t), row(g.f, g.t) = any(path) + from graph g, search_graph sg + where g.f = sg.t and not cycle +) +select * from search_graph order by path; + +-- +-- test multiple WITH queries +-- +WITH RECURSIVE + y (id) AS (VALUES (1)), + x (id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5) +SELECT * FROM x; + +-- forward reference OK +WITH RECURSIVE + x(id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5), + y(id) AS (values (1)) + SELECT * FROM x; + +WITH RECURSIVE + x(id) AS + (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5), + y(id) AS + (VALUES (1) UNION ALL SELECT id+1 FROM y WHERE id < 10) + SELECT y.*, x.* FROM y LEFT JOIN x USING (id); + +WITH RECURSIVE + x(id) AS + (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5), + y(id) AS + (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 10) + SELECT y.*, x.* FROM y LEFT JOIN x USING (id); + +WITH RECURSIVE + x(id) AS + (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ), + y(id) AS + (SELECT * FROM x UNION ALL SELECT * FROM x), + z(id) AS + (SELECT * FROM x UNION ALL SELECT id+1 FROM z WHERE id < 10) + SELECT * FROM z; + +WITH RECURSIVE + x(id) AS + (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ), + y(id) AS + (SELECT * FROM x UNION ALL SELECT * FROM x), + z(id) AS + (SELECT * FROM y UNION ALL SELECT id+1 FROM z WHERE id < 10) + SELECT * FROM z; + +-- +-- Test WITH attached to a data-modifying statement +-- + +CREATE TEMPORARY TABLE y (a INTEGER); +INSERT INTO y SELECT generate_series(1, 10); + +WITH t AS ( + SELECT a FROM y +) +INSERT INTO y +SELECT a+20 FROM t RETURNING *; + +SELECT * FROM y; + +WITH t AS ( + SELECT a FROM y +) +UPDATE y SET a = y.a-10 FROM t WHERE y.a > 20 AND t.a = y.a RETURNING y.a; + +SELECT * FROM y; + +WITH RECURSIVE t(a) AS ( + SELECT 11 + UNION ALL + SELECT a+1 FROM t WHERE a < 50 +) +DELETE FROM y USING t WHERE t.a = y.a RETURNING y.a; + +SELECT * FROM y; + +DROP TABLE y; + +-- +-- error cases +-- + +-- INTERSECT +WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT SELECT n+1 FROM x) + SELECT * FROM x; + +WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT ALL SELECT n+1 FROM x) + SELECT * FROM x; + +-- EXCEPT +WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT SELECT n+1 FROM x) + SELECT * FROM x; + +WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT ALL SELECT n+1 FROM x) + SELECT * FROM x; + +-- no non-recursive term +WITH RECURSIVE x(n) AS (SELECT n FROM x) + SELECT * FROM x; + +-- recursive term in the left hand side (strictly speaking, should allow this) +WITH RECURSIVE x(n) AS (SELECT n FROM x UNION ALL SELECT 1) + SELECT * FROM x; + +CREATE TEMPORARY TABLE y (a INTEGER); +INSERT INTO y SELECT generate_series(1, 10); + +-- LEFT JOIN + +WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 + UNION ALL + SELECT x.n+1 FROM y LEFT JOIN x ON x.n = y.a WHERE n < 10) +SELECT * FROM x; + +-- RIGHT JOIN +WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 + UNION ALL + SELECT x.n+1 FROM x RIGHT JOIN y ON x.n = y.a WHERE n < 10) +SELECT * FROM x; + +-- FULL JOIN +WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 + UNION ALL + SELECT x.n+1 FROM x FULL JOIN y ON x.n = y.a WHERE n < 10) +SELECT * FROM x; + +-- subquery +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x + WHERE n IN (SELECT * FROM x)) + SELECT * FROM x; + +-- aggregate functions +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT count(*) FROM x) + SELECT * FROM x; + +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT sum(n) FROM x) + SELECT * FROM x; + +-- ORDER BY +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x ORDER BY 1) + SELECT * FROM x; + +-- LIMIT/OFFSET +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x LIMIT 10 OFFSET 1) + SELECT * FROM x; + +-- FOR UPDATE +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x FOR UPDATE) + SELECT * FROM x; + +-- target list has a recursive query name +WITH RECURSIVE x(id) AS (values (1) + UNION ALL + SELECT (SELECT * FROM x) FROM x WHERE id < 5 +) SELECT * FROM x; + +-- mutual recursive query (not implemented) +WITH RECURSIVE + x (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM y WHERE id < 5), + y (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 5) +SELECT * FROM x; + +-- non-linear recursion is not allowed +WITH RECURSIVE foo(i) AS + (values (1) + UNION ALL + (SELECT i+1 FROM foo WHERE i < 10 + UNION ALL + SELECT i+1 FROM foo WHERE i < 5) +) SELECT * FROM foo; + +WITH RECURSIVE foo(i) AS + (values (1) + UNION ALL + SELECT * FROM + (SELECT i+1 FROM foo WHERE i < 10 + UNION ALL + SELECT i+1 FROM foo WHERE i < 5) AS t +) SELECT * FROM foo; + +WITH RECURSIVE foo(i) AS + (values (1) + UNION ALL + (SELECT i+1 FROM foo WHERE i < 10 + EXCEPT + SELECT i+1 FROM foo WHERE i < 5) +) SELECT * FROM foo; + +WITH RECURSIVE foo(i) AS + (values (1) + UNION ALL + (SELECT i+1 FROM foo WHERE i < 10 + INTERSECT + SELECT i+1 FROM foo WHERE i < 5) +) SELECT * FROM foo; + +-- Wrong type induced from non-recursive term +WITH RECURSIVE foo(i) AS + (SELECT i FROM (VALUES(1),(2)) t(i) + UNION ALL + SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10) +SELECT * FROM foo; + +-- rejects different typmod, too (should we allow this?) +WITH RECURSIVE foo(i) AS + (SELECT i::numeric(3,0) FROM (VALUES(1),(2)) t(i) + UNION ALL + SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10) +SELECT * FROM foo; + +-- disallow OLD/NEW reference in CTE +CREATE TEMPORARY TABLE x (n integer); +CREATE RULE r2 AS ON UPDATE TO x DO INSTEAD + WITH t AS (SELECT OLD.*) UPDATE y SET a = t.n FROM t; + +-- +-- test for bug #4902 +-- +with cte(foo) as ( values(42) ) values((select foo from cte)); +with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q; + +-- test CTE referencing an outer-level variable (to see that changed-parameter +-- signaling still works properly after fixing this bug) +select ( with cte(foo) as ( values(f1) ) + select (select foo from cte) ) +from int4_tbl; + +select ( with cte(foo) as ( values(f1) ) + values((select foo from cte)) ) +from int4_tbl; + +-- +-- test for nested-recursive-WITH bug +-- +WITH RECURSIVE t(j) AS ( + WITH RECURSIVE s(i) AS ( + VALUES (1) + UNION ALL + SELECT i+1 FROM s WHERE i < 10 + ) + SELECT i FROM s + UNION ALL + SELECT j+1 FROM t WHERE j < 10 +) +SELECT * FROM t; + +-- +-- test WITH attached to intermediate-level set operation +-- + +WITH outermost(x) AS ( + SELECT 1 + UNION (WITH innermost as (SELECT 2) + SELECT * FROM innermost + UNION SELECT 3) +) +SELECT * FROM outermost ORDER BY 1; + +WITH outermost(x) AS ( + SELECT 1 + UNION (WITH innermost as (SELECT 2) + SELECT * FROM outermost -- fail + UNION SELECT * FROM innermost) +) +SELECT * FROM outermost ORDER BY 1; + +WITH RECURSIVE outermost(x) AS ( + SELECT 1 + UNION (WITH innermost as (SELECT 2) + SELECT * FROM outermost + UNION SELECT * FROM innermost) +) +SELECT * FROM outermost ORDER BY 1; + +WITH RECURSIVE outermost(x) AS ( + WITH innermost as (SELECT 2 FROM outermost) -- fail + SELECT * FROM innermost + UNION SELECT * from outermost +) +SELECT * FROM outermost ORDER BY 1; + +-- +-- This test will fail with the old implementation of PARAM_EXEC parameter +-- assignment, because the "q1" Var passed down to A's targetlist subselect +-- looks exactly like the "A.id" Var passed down to C's subselect, causing +-- the old code to give them the same runtime PARAM_EXEC slot. But the +-- lifespans of the two parameters overlap, thanks to B also reading A. +-- + +with +A as ( select q2 as id, (select q1) as x from int8_tbl ), +B as ( select id, row_number() over (partition by id) as r from A ), +C as ( select A.id, array(select B.id from B where B.id = A.id) from A ) +select * from C; + +-- +-- Test CTEs read in non-initialization orders +-- + +WITH RECURSIVE + tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)), + iter (id_key, row_type, link) AS ( + SELECT 0, 'base', 17 + UNION ALL ( + WITH remaining(id_key, row_type, link, min) AS ( + SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER () + FROM tab INNER JOIN iter USING (link) + WHERE tab.id_key > iter.id_key + ), + first_remaining AS ( + SELECT id_key, row_type, link + FROM remaining + WHERE id_key=min + ), + effect AS ( + SELECT tab.id_key, 'new'::text, tab.link + FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key + WHERE e.row_type = 'false' + ) + SELECT * FROM first_remaining + UNION ALL SELECT * FROM effect + ) + ) +SELECT * FROM iter; + +WITH RECURSIVE + tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)), + iter (id_key, row_type, link) AS ( + SELECT 0, 'base', 17 + UNION ( + WITH remaining(id_key, row_type, link, min) AS ( + SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER () + FROM tab INNER JOIN iter USING (link) + WHERE tab.id_key > iter.id_key + ), + first_remaining AS ( + SELECT id_key, row_type, link + FROM remaining + WHERE id_key=min + ), + effect AS ( + SELECT tab.id_key, 'new'::text, tab.link + FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key + WHERE e.row_type = 'false' + ) + SELECT * FROM first_remaining + UNION ALL SELECT * FROM effect + ) + ) +SELECT * FROM iter; + +-- +-- Data-modifying statements in WITH +-- + +-- INSERT ... RETURNING +WITH t AS ( + INSERT INTO y + VALUES + (11), + (12), + (13), + (14), + (15), + (16), + (17), + (18), + (19), + (20) + RETURNING * +) +SELECT * FROM t; + +SELECT * FROM y; + +-- UPDATE ... RETURNING +WITH t AS ( + UPDATE y + SET a=a+1 + RETURNING * +) +SELECT * FROM t; + +SELECT * FROM y; + +-- DELETE ... RETURNING +WITH t AS ( + DELETE FROM y + WHERE a <= 10 + RETURNING * +) +SELECT * FROM t; + +SELECT * FROM y; + +-- forward reference +WITH RECURSIVE t AS ( + INSERT INTO y + SELECT a+5 FROM t2 WHERE a > 5 + RETURNING * +), t2 AS ( + UPDATE y SET a=a-11 RETURNING * +) +SELECT * FROM t +UNION ALL +SELECT * FROM t2; + +SELECT * FROM y; + +-- unconditional DO INSTEAD rule +CREATE RULE y_rule AS ON DELETE TO y DO INSTEAD + INSERT INTO y VALUES(42) RETURNING *; + +WITH t AS ( + DELETE FROM y RETURNING * +) +SELECT * FROM t; + +SELECT * FROM y; + +DROP RULE y_rule ON y; + +-- check merging of outer CTE with CTE in a rule action +CREATE TEMP TABLE bug6051 AS + select i from generate_series(1,3) as t(i); + +SELECT * FROM bug6051; + +WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) +INSERT INTO bug6051 SELECT * FROM t1; + +SELECT * FROM bug6051; + +CREATE TEMP TABLE bug6051_2 (i int); + +CREATE RULE bug6051_ins AS ON INSERT TO bug6051 DO INSTEAD + INSERT INTO bug6051_2 + SELECT NEW.i; + +WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) +INSERT INTO bug6051 SELECT * FROM t1; + +SELECT * FROM bug6051; +SELECT * FROM bug6051_2; + +-- a truly recursive CTE in the same list +WITH RECURSIVE t(a) AS ( + SELECT 0 + UNION ALL + SELECT a+1 FROM t WHERE a+1 < 5 +), t2 as ( + INSERT INTO y + SELECT * FROM t RETURNING * +) +SELECT * FROM t2 JOIN y USING (a) ORDER BY a; + +SELECT * FROM y; + +-- data-modifying WITH in a modifying statement +WITH t AS ( + DELETE FROM y + WHERE a <= 10 + RETURNING * +) +INSERT INTO y SELECT -a FROM t RETURNING *; + +SELECT * FROM y; + +-- check that WITH query is run to completion even if outer query isn't +WITH t AS ( + UPDATE y SET a = a * 100 RETURNING * +) +SELECT * FROM t LIMIT 10; + +SELECT * FROM y; + +-- data-modifying WITH containing INSERT...ON CONFLICT DO UPDATE +CREATE TABLE withz AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i; +ALTER TABLE withz ADD UNIQUE (k); + +WITH t AS ( + INSERT INTO withz SELECT i, 'insert' + FROM generate_series(0, 16) i + ON CONFLICT (k) DO UPDATE SET v = withz.v || ', now update' + RETURNING * +) +SELECT * FROM t JOIN y ON t.k = y.a ORDER BY a, k; + +-- Test EXCLUDED.* reference within CTE +WITH aa AS ( + INSERT INTO withz VALUES(1, 5) ON CONFLICT (k) DO UPDATE SET v = EXCLUDED.v + WHERE withz.k != EXCLUDED.k + RETURNING * +) +SELECT * FROM aa; + +-- New query/snapshot demonstrates side-effects of previous query. +SELECT * FROM withz ORDER BY k; + +-- +-- Ensure subqueries within the update clause work, even if they +-- reference outside values +-- +WITH aa AS (SELECT 1 a, 2 b) +INSERT INTO withz VALUES(1, 'insert') +ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); +WITH aa AS (SELECT 1 a, 2 b) +INSERT INTO withz VALUES(1, 'insert') +ON CONFLICT (k) DO UPDATE SET v = ' update' WHERE withz.k = (SELECT a FROM aa); +WITH aa AS (SELECT 1 a, 2 b) +INSERT INTO withz VALUES(1, 'insert') +ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); +WITH aa AS (SELECT 'a' a, 'b' b UNION ALL SELECT 'a' a, 'b' b) +INSERT INTO withz VALUES(1, 'insert') +ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 'a' LIMIT 1); +WITH aa AS (SELECT 1 a, 2 b) +INSERT INTO withz VALUES(1, (SELECT b || ' insert' FROM aa WHERE a = 1 )) +ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); + +-- Update a row more than once, in different parts of a wCTE. That is +-- an allowed, presumably very rare, edge case, but since it was +-- broken in the past, having a test seems worthwhile. +WITH simpletup AS ( + SELECT 2 k, 'Green' v), +upsert_cte AS ( + INSERT INTO withz VALUES(2, 'Blue') ON CONFLICT (k) DO + UPDATE SET (k, v) = (SELECT k, v FROM simpletup WHERE simpletup.k = withz.k) + RETURNING k, v) +INSERT INTO withz VALUES(2, 'Red') ON CONFLICT (k) DO +UPDATE SET (k, v) = (SELECT k, v FROM upsert_cte WHERE upsert_cte.k = withz.k) +RETURNING k, v; + +DROP TABLE withz; + +-- check that run to completion happens in proper ordering + +TRUNCATE TABLE y; +INSERT INTO y SELECT generate_series(1, 3); +CREATE TEMPORARY TABLE yy (a INTEGER); + +WITH RECURSIVE t1 AS ( + INSERT INTO y SELECT * FROM y RETURNING * +), t2 AS ( + INSERT INTO yy SELECT * FROM t1 RETURNING * +) +SELECT 1; + +SELECT * FROM y; +SELECT * FROM yy; + +WITH RECURSIVE t1 AS ( + INSERT INTO yy SELECT * FROM t2 RETURNING * +), t2 AS ( + INSERT INTO y SELECT * FROM y RETURNING * +) +SELECT 1; + +SELECT * FROM y; +SELECT * FROM yy; + +-- triggers + +TRUNCATE TABLE y; +INSERT INTO y SELECT generate_series(1, 10); + +CREATE FUNCTION y_trigger() RETURNS trigger AS $$ +begin + raise notice 'y_trigger: a = %', new.a; + return new; +end; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER y_trig BEFORE INSERT ON y FOR EACH ROW + EXECUTE PROCEDURE y_trigger(); + +WITH t AS ( + INSERT INTO y + VALUES + (21), + (22), + (23) + RETURNING * +) +SELECT * FROM t; + +SELECT * FROM y; + +DROP TRIGGER y_trig ON y; + +CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH ROW + EXECUTE PROCEDURE y_trigger(); + +WITH t AS ( + INSERT INTO y + VALUES + (31), + (32), + (33) + RETURNING * +) +SELECT * FROM t LIMIT 1; + +SELECT * FROM y; + +DROP TRIGGER y_trig ON y; + +CREATE OR REPLACE FUNCTION y_trigger() RETURNS trigger AS $$ +begin + raise notice 'y_trigger'; + return null; +end; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH STATEMENT + EXECUTE PROCEDURE y_trigger(); + +WITH t AS ( + INSERT INTO y + VALUES + (41), + (42), + (43) + RETURNING * +) +SELECT * FROM t; + +SELECT * FROM y; + +DROP TRIGGER y_trig ON y; +DROP FUNCTION y_trigger(); + +-- WITH attached to inherited UPDATE or DELETE + +CREATE TEMP TABLE parent ( id int, val text ); +CREATE TEMP TABLE child1 ( ) INHERITS ( parent ); +CREATE TEMP TABLE child2 ( ) INHERITS ( parent ); + +INSERT INTO parent VALUES ( 1, 'p1' ); +INSERT INTO child1 VALUES ( 11, 'c11' ),( 12, 'c12' ); +INSERT INTO child2 VALUES ( 23, 'c21' ),( 24, 'c22' ); + +WITH rcte AS ( SELECT sum(id) AS totalid FROM parent ) +UPDATE parent SET id = id + totalid FROM rcte; + +SELECT * FROM parent; + +WITH wcte AS ( INSERT INTO child1 VALUES ( 42, 'new' ) RETURNING id AS newid ) +UPDATE parent SET id = id + newid FROM wcte; + +SELECT * FROM parent; + +WITH rcte AS ( SELECT max(id) AS maxid FROM parent ) +DELETE FROM parent USING rcte WHERE id = maxid; + +SELECT * FROM parent; + +WITH wcte AS ( INSERT INTO child2 VALUES ( 42, 'new2' ) RETURNING id AS newid ) +DELETE FROM parent USING wcte WHERE id = newid; + +SELECT * FROM parent; + +-- check EXPLAIN VERBOSE for a wCTE with RETURNING + +EXPLAIN (VERBOSE, COSTS OFF) +WITH wcte AS ( INSERT INTO int8_tbl VALUES ( 42, 47 ) RETURNING q2 ) +DELETE FROM a USING wcte WHERE aa = q2; + +-- error cases + +-- data-modifying WITH tries to use its own output +WITH RECURSIVE t AS ( + INSERT INTO y + SELECT * FROM t +) +VALUES(FALSE); + +-- no RETURNING in a referenced data-modifying WITH +WITH t AS ( + INSERT INTO y VALUES(0) +) +SELECT * FROM t; + +-- data-modifying WITH allowed only at the top level +SELECT * FROM ( + WITH t AS (UPDATE y SET a=a+1 RETURNING *) + SELECT * FROM t +) ss; + +-- most variants of rules aren't allowed +CREATE RULE y_rule AS ON INSERT TO y WHERE a=0 DO INSTEAD DELETE FROM y; +WITH t AS ( + INSERT INTO y VALUES(0) +) +VALUES(FALSE); +DROP RULE y_rule ON y; + +-- check that parser lookahead for WITH doesn't cause any odd behavior +create table foo (with baz); -- fail, WITH is a reserved word +create table foo (with ordinality); -- fail, WITH is a reserved word +with ordinality as (select 1 as x) select * from ordinality; + +-- check sane response to attempt to modify CTE relation +WITH test AS (SELECT 42) INSERT INTO test VALUES (1); + +-- check response to attempt to modify table with same name as a CTE (perhaps +-- surprisingly it works, because CTEs don't hide tables from data-modifying +-- statements) +create temp table test (i int); +with test as (select 42) insert into test select * from test; +select * from test; +drop table test; diff --git a/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out b/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out new file mode 100644 index 000000000000..2a2085556bb2 --- /dev/null +++ b/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out @@ -0,0 +1,2291 @@ +-- +-- Tests for common table expressions (WITH query, ... SELECT ...) +-- +-- Basic WITH +WITH q1(x,y) AS (SELECT 1,2) +SELECT * FROM q1, q1 AS q2; + x | y | x | y +---+---+---+--- + 1 | 2 | 1 | 2 +(1 row) + +-- Multiple uses are evaluated only once +SELECT count(*) FROM ( + WITH q1(x) AS (SELECT random() FROM generate_series(1, 5)) + SELECT * FROM q1 + UNION + SELECT * FROM q1 +) ss; + count +------- + 5 +(1 row) + +-- WITH RECURSIVE +-- sum of 1..100 +WITH RECURSIVE t(n) AS ( + VALUES (1) +UNION ALL + SELECT n+1 FROM t WHERE n < 100 +) +SELECT sum(n) FROM t; + sum +------ + 5050 +(1 row) + +WITH RECURSIVE t(n) AS ( + SELECT (VALUES(1)) +UNION ALL + SELECT n+1 FROM t WHERE n < 5 +) +SELECT * FROM t; + n +--- + 1 + 2 + 3 + 4 + 5 +(5 rows) + +-- recursive view +CREATE RECURSIVE VIEW nums (n) AS + VALUES (1) +UNION ALL + SELECT n+1 FROM nums WHERE n < 5; +SELECT * FROM nums; + n +--- + 1 + 2 + 3 + 4 + 5 +(5 rows) + +CREATE OR REPLACE RECURSIVE VIEW nums (n) AS + VALUES (1) +UNION ALL + SELECT n+1 FROM nums WHERE n < 6; +SELECT * FROM nums; + n +--- + 1 + 2 + 3 + 4 + 5 + 6 +(6 rows) + +-- This is an infinite loop with UNION ALL, but not with UNION +WITH RECURSIVE t(n) AS ( + SELECT 1 +UNION + SELECT 10-n FROM t) +SELECT * FROM t; + n +--- + 1 + 9 +(2 rows) + +-- This'd be an infinite loop, but outside query reads only as much as needed +WITH RECURSIVE t(n) AS ( + VALUES (1) +UNION ALL + SELECT n+1 FROM t) +SELECT * FROM t LIMIT 10; + n +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 +(10 rows) + +-- UNION case should have same property +WITH RECURSIVE t(n) AS ( + SELECT 1 +UNION + SELECT n+1 FROM t) +SELECT * FROM t LIMIT 10; + n +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 +(10 rows) + +-- Test behavior with an unknown-type literal in the WITH +WITH q AS (SELECT 'foo' AS x) +SELECT x, x IS OF (text) AS is_text FROM q; + x | is_text +-----+--------- + foo | t +(1 row) + +WITH RECURSIVE t(n) AS ( + SELECT 'foo' +UNION ALL + SELECT n || ' bar' FROM t WHERE length(n) < 20 +) +SELECT n, n IS OF (text) AS is_text FROM t; + n | is_text +-------------------------+--------- + foo | t + foo bar | t + foo bar bar | t + foo bar bar bar | t + foo bar bar bar bar | t + foo bar bar bar bar bar | t +(6 rows) + +-- In a perfect world, this would work and resolve the literal as int ... +-- but for now, we have to be content with resolving to text too soon. +WITH RECURSIVE t(n) AS ( + SELECT '7' +UNION ALL + SELECT n+1 FROM t WHERE n < 10 +) +SELECT n, n IS OF (int) AS is_int FROM t; +ERROR: operator does not exist: text + integer +LINE 4: SELECT n+1 FROM t WHERE n < 10 + ^ +HINT: No operator matches the given name and argument types. You might need to add explicit type casts. +-- +-- Some examples with a tree +-- +-- department structure represented here is as follows: +-- +-- ROOT-+->A-+->B-+->C +-- | | +-- | +->D-+->F +-- +->E-+->G +CREATE TEMP TABLE department ( + id INTEGER PRIMARY KEY, -- department ID + parent_department INTEGER REFERENCES department, -- upper department ID + name TEXT -- department name +); +INSERT INTO department VALUES (0, NULL, 'ROOT'); +INSERT INTO department VALUES (1, 0, 'A'); +INSERT INTO department VALUES (2, 1, 'B'); +INSERT INTO department VALUES (3, 2, 'C'); +INSERT INTO department VALUES (4, 2, 'D'); +INSERT INTO department VALUES (5, 0, 'E'); +INSERT INTO department VALUES (6, 4, 'F'); +INSERT INTO department VALUES (7, 5, 'G'); +-- extract all departments under 'A'. Result should be A, B, C, D and F +WITH RECURSIVE subdepartment AS +( + -- non recursive term + SELECT name as root_name, * FROM department WHERE name = 'A' + UNION ALL + -- recursive term + SELECT sd.root_name, d.* FROM department AS d, subdepartment AS sd + WHERE d.parent_department = sd.id +) +SELECT * FROM subdepartment ORDER BY name; + root_name | id | parent_department | name +-----------+----+-------------------+------ + A | 1 | 0 | A + A | 2 | 1 | B + A | 3 | 2 | C + A | 4 | 2 | D + A | 6 | 4 | F +(5 rows) + +-- extract all departments under 'A' with "level" number +WITH RECURSIVE subdepartment(level, id, parent_department, name) AS +( + -- non recursive term + SELECT 1, * FROM department WHERE name = 'A' + UNION ALL + -- recursive term + SELECT sd.level + 1, d.* FROM department AS d, subdepartment AS sd + WHERE d.parent_department = sd.id +) +SELECT * FROM subdepartment ORDER BY name; + level | id | parent_department | name +-------+----+-------------------+------ + 1 | 1 | 0 | A + 2 | 2 | 1 | B + 3 | 3 | 2 | C + 3 | 4 | 2 | D + 4 | 6 | 4 | F +(5 rows) + +-- extract all departments under 'A' with "level" number. +-- Only shows level 2 or more +WITH RECURSIVE subdepartment(level, id, parent_department, name) AS +( + -- non recursive term + SELECT 1, * FROM department WHERE name = 'A' + UNION ALL + -- recursive term + SELECT sd.level + 1, d.* FROM department AS d, subdepartment AS sd + WHERE d.parent_department = sd.id +) +SELECT * FROM subdepartment WHERE level >= 2 ORDER BY name; + level | id | parent_department | name +-------+----+-------------------+------ + 2 | 2 | 1 | B + 3 | 3 | 2 | C + 3 | 4 | 2 | D + 4 | 6 | 4 | F +(4 rows) + +-- "RECURSIVE" is ignored if the query has no self-reference +WITH RECURSIVE subdepartment AS +( + -- note lack of recursive UNION structure + SELECT * FROM department WHERE name = 'A' +) +SELECT * FROM subdepartment ORDER BY name; + id | parent_department | name +----+-------------------+------ + 1 | 0 | A +(1 row) + +-- inside subqueries +SELECT count(*) FROM ( + WITH RECURSIVE t(n) AS ( + SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 500 + ) + SELECT * FROM t) AS t WHERE n < ( + SELECT count(*) FROM ( + WITH RECURSIVE t(n) AS ( + SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 100 + ) + SELECT * FROM t WHERE n < 50000 + ) AS t WHERE n < 100); + count +------- + 98 +(1 row) + +-- use same CTE twice at different subquery levels +WITH q1(x,y) AS ( + SELECT hundred, sum(ten) FROM tenk1 GROUP BY hundred + ) +SELECT count(*) FROM q1 WHERE y > (SELECT sum(y)/100 FROM q1 qsub); + count +------- + 50 +(1 row) + +-- via a VIEW +CREATE TEMPORARY VIEW vsubdepartment AS + WITH RECURSIVE subdepartment AS + ( + -- non recursive term + SELECT * FROM department WHERE name = 'A' + UNION ALL + -- recursive term + SELECT d.* FROM department AS d, subdepartment AS sd + WHERE d.parent_department = sd.id + ) + SELECT * FROM subdepartment; +SELECT * FROM vsubdepartment ORDER BY name; + id | parent_department | name +----+-------------------+------ + 1 | 0 | A + 2 | 1 | B + 3 | 2 | C + 4 | 2 | D + 6 | 4 | F +(5 rows) + +-- Check reverse listing +SELECT pg_get_viewdef('vsubdepartment'::regclass); + pg_get_viewdef +----------------------------------------------- + WITH RECURSIVE subdepartment AS ( + + SELECT department.id, + + department.parent_department, + + department.name + + FROM department + + WHERE (department.name = 'A'::text)+ + UNION ALL + + SELECT d.id, + + d.parent_department, + + d.name + + FROM department d, + + subdepartment sd + + WHERE (d.parent_department = sd.id)+ + ) + + SELECT subdepartment.id, + + subdepartment.parent_department, + + subdepartment.name + + FROM subdepartment; +(1 row) + +SELECT pg_get_viewdef('vsubdepartment'::regclass, true); + pg_get_viewdef +--------------------------------------------- + WITH RECURSIVE subdepartment AS ( + + SELECT department.id, + + department.parent_department, + + department.name + + FROM department + + WHERE department.name = 'A'::text+ + UNION ALL + + SELECT d.id, + + d.parent_department, + + d.name + + FROM department d, + + subdepartment sd + + WHERE d.parent_department = sd.id+ + ) + + SELECT subdepartment.id, + + subdepartment.parent_department, + + subdepartment.name + + FROM subdepartment; +(1 row) + +-- Another reverse-listing example +CREATE VIEW sums_1_100 AS +WITH RECURSIVE t(n) AS ( + VALUES (1) +UNION ALL + SELECT n+1 FROM t WHERE n < 100 +) +SELECT sum(n) FROM t; +\d+ sums_1_100 + View "public.sums_1_100" + Column | Type | Collation | Nullable | Default | Storage | Description +--------+--------+-----------+----------+---------+---------+------------- + sum | bigint | | | | plain | +View definition: + WITH RECURSIVE t(n) AS ( + VALUES (1) + UNION ALL + SELECT t_1.n + 1 + FROM t t_1 + WHERE t_1.n < 100 + ) + SELECT sum(t.n) AS sum + FROM t; + +-- corner case in which sub-WITH gets initialized first +with recursive q as ( + select * from department + union all + (with x as (select * from q) + select * from x) + ) +select * from q limit 24; + id | parent_department | name +----+-------------------+------ + 0 | | ROOT + 1 | 0 | A + 2 | 1 | B + 3 | 2 | C + 4 | 2 | D + 5 | 0 | E + 6 | 4 | F + 7 | 5 | G + 0 | | ROOT + 1 | 0 | A + 2 | 1 | B + 3 | 2 | C + 4 | 2 | D + 5 | 0 | E + 6 | 4 | F + 7 | 5 | G + 0 | | ROOT + 1 | 0 | A + 2 | 1 | B + 3 | 2 | C + 4 | 2 | D + 5 | 0 | E + 6 | 4 | F + 7 | 5 | G +(24 rows) + +with recursive q as ( + select * from department + union all + (with recursive x as ( + select * from department + union all + (select * from q union all select * from x) + ) + select * from x) + ) +select * from q limit 32; + id | parent_department | name +----+-------------------+------ + 0 | | ROOT + 1 | 0 | A + 2 | 1 | B + 3 | 2 | C + 4 | 2 | D + 5 | 0 | E + 6 | 4 | F + 7 | 5 | G + 0 | | ROOT + 1 | 0 | A + 2 | 1 | B + 3 | 2 | C + 4 | 2 | D + 5 | 0 | E + 6 | 4 | F + 7 | 5 | G + 0 | | ROOT + 1 | 0 | A + 2 | 1 | B + 3 | 2 | C + 4 | 2 | D + 5 | 0 | E + 6 | 4 | F + 7 | 5 | G + 0 | | ROOT + 1 | 0 | A + 2 | 1 | B + 3 | 2 | C + 4 | 2 | D + 5 | 0 | E + 6 | 4 | F + 7 | 5 | G +(32 rows) + +-- recursive term has sub-UNION +WITH RECURSIVE t(i,j) AS ( + VALUES (1,2) + UNION ALL + SELECT t2.i, t.j+1 FROM + (SELECT 2 AS i UNION ALL SELECT 3 AS i) AS t2 + JOIN t ON (t2.i = t.i+1)) + SELECT * FROM t; + i | j +---+--- + 1 | 2 + 2 | 3 + 3 | 4 +(3 rows) + +-- +-- different tree example +-- +CREATE TEMPORARY TABLE tree( + id INTEGER PRIMARY KEY, + parent_id INTEGER REFERENCES tree(id) +); +INSERT INTO tree +VALUES (1, NULL), (2, 1), (3,1), (4,2), (5,2), (6,2), (7,3), (8,3), + (9,4), (10,4), (11,7), (12,7), (13,7), (14, 9), (15,11), (16,11); +-- +-- get all paths from "second level" nodes to leaf nodes +-- +WITH RECURSIVE t(id, path) AS ( + VALUES(1,ARRAY[]::integer[]) +UNION ALL + SELECT tree.id, t.path || tree.id + FROM tree JOIN t ON (tree.parent_id = t.id) +) +SELECT t1.*, t2.* FROM t AS t1 JOIN t AS t2 ON + (t1.path[1] = t2.path[1] AND + array_upper(t1.path,1) = 1 AND + array_upper(t2.path,1) > 1) + ORDER BY t1.id, t2.id; + id | path | id | path +----+------+----+------------- + 2 | {2} | 4 | {2,4} + 2 | {2} | 5 | {2,5} + 2 | {2} | 6 | {2,6} + 2 | {2} | 9 | {2,4,9} + 2 | {2} | 10 | {2,4,10} + 2 | {2} | 14 | {2,4,9,14} + 3 | {3} | 7 | {3,7} + 3 | {3} | 8 | {3,8} + 3 | {3} | 11 | {3,7,11} + 3 | {3} | 12 | {3,7,12} + 3 | {3} | 13 | {3,7,13} + 3 | {3} | 15 | {3,7,11,15} + 3 | {3} | 16 | {3,7,11,16} +(13 rows) + +-- just count 'em +WITH RECURSIVE t(id, path) AS ( + VALUES(1,ARRAY[]::integer[]) +UNION ALL + SELECT tree.id, t.path || tree.id + FROM tree JOIN t ON (tree.parent_id = t.id) +) +SELECT t1.id, count(t2.*) FROM t AS t1 JOIN t AS t2 ON + (t1.path[1] = t2.path[1] AND + array_upper(t1.path,1) = 1 AND + array_upper(t2.path,1) > 1) + GROUP BY t1.id + ORDER BY t1.id; + id | count +----+------- + 2 | 6 + 3 | 7 +(2 rows) + +-- this variant tickled a whole-row-variable bug in 8.4devel +WITH RECURSIVE t(id, path) AS ( + VALUES(1,ARRAY[]::integer[]) +UNION ALL + SELECT tree.id, t.path || tree.id + FROM tree JOIN t ON (tree.parent_id = t.id) +) +SELECT t1.id, t2.path, t2 FROM t AS t1 JOIN t AS t2 ON +(t1.id=t2.id); + id | path | t2 +----+-------------+-------------------- + 1 | {} | (1,{}) + 2 | {2} | (2,{2}) + 3 | {3} | (3,{3}) + 4 | {2,4} | (4,"{2,4}") + 5 | {2,5} | (5,"{2,5}") + 6 | {2,6} | (6,"{2,6}") + 7 | {3,7} | (7,"{3,7}") + 8 | {3,8} | (8,"{3,8}") + 9 | {2,4,9} | (9,"{2,4,9}") + 10 | {2,4,10} | (10,"{2,4,10}") + 11 | {3,7,11} | (11,"{3,7,11}") + 12 | {3,7,12} | (12,"{3,7,12}") + 13 | {3,7,13} | (13,"{3,7,13}") + 14 | {2,4,9,14} | (14,"{2,4,9,14}") + 15 | {3,7,11,15} | (15,"{3,7,11,15}") + 16 | {3,7,11,16} | (16,"{3,7,11,16}") +(16 rows) + +-- +-- test cycle detection +-- +create temp table graph( f int, t int, label text ); +insert into graph values + (1, 2, 'arc 1 -> 2'), + (1, 3, 'arc 1 -> 3'), + (2, 3, 'arc 2 -> 3'), + (1, 4, 'arc 1 -> 4'), + (4, 5, 'arc 4 -> 5'), + (5, 1, 'arc 5 -> 1'); +with recursive search_graph(f, t, label, path, cycle) as ( + select *, array[row(g.f, g.t)], false from graph g + union all + select g.*, path || row(g.f, g.t), row(g.f, g.t) = any(path) + from graph g, search_graph sg + where g.f = sg.t and not cycle +) +select * from search_graph; + f | t | label | path | cycle +---+---+------------+-------------------------------------------+------- + 1 | 2 | arc 1 -> 2 | {"(1,2)"} | f + 1 | 3 | arc 1 -> 3 | {"(1,3)"} | f + 2 | 3 | arc 2 -> 3 | {"(2,3)"} | f + 1 | 4 | arc 1 -> 4 | {"(1,4)"} | f + 4 | 5 | arc 4 -> 5 | {"(4,5)"} | f + 5 | 1 | arc 5 -> 1 | {"(5,1)"} | f + 1 | 2 | arc 1 -> 2 | {"(5,1)","(1,2)"} | f + 1 | 3 | arc 1 -> 3 | {"(5,1)","(1,3)"} | f + 1 | 4 | arc 1 -> 4 | {"(5,1)","(1,4)"} | f + 2 | 3 | arc 2 -> 3 | {"(1,2)","(2,3)"} | f + 4 | 5 | arc 4 -> 5 | {"(1,4)","(4,5)"} | f + 5 | 1 | arc 5 -> 1 | {"(4,5)","(5,1)"} | f + 1 | 2 | arc 1 -> 2 | {"(4,5)","(5,1)","(1,2)"} | f + 1 | 3 | arc 1 -> 3 | {"(4,5)","(5,1)","(1,3)"} | f + 1 | 4 | arc 1 -> 4 | {"(4,5)","(5,1)","(1,4)"} | f + 2 | 3 | arc 2 -> 3 | {"(5,1)","(1,2)","(2,3)"} | f + 4 | 5 | arc 4 -> 5 | {"(5,1)","(1,4)","(4,5)"} | f + 5 | 1 | arc 5 -> 1 | {"(1,4)","(4,5)","(5,1)"} | f + 1 | 2 | arc 1 -> 2 | {"(1,4)","(4,5)","(5,1)","(1,2)"} | f + 1 | 3 | arc 1 -> 3 | {"(1,4)","(4,5)","(5,1)","(1,3)"} | f + 1 | 4 | arc 1 -> 4 | {"(1,4)","(4,5)","(5,1)","(1,4)"} | t + 2 | 3 | arc 2 -> 3 | {"(4,5)","(5,1)","(1,2)","(2,3)"} | f + 4 | 5 | arc 4 -> 5 | {"(4,5)","(5,1)","(1,4)","(4,5)"} | t + 5 | 1 | arc 5 -> 1 | {"(5,1)","(1,4)","(4,5)","(5,1)"} | t + 2 | 3 | arc 2 -> 3 | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} | f +(25 rows) + +-- ordering by the path column has same effect as SEARCH DEPTH FIRST +with recursive search_graph(f, t, label, path, cycle) as ( + select *, array[row(g.f, g.t)], false from graph g + union all + select g.*, path || row(g.f, g.t), row(g.f, g.t) = any(path) + from graph g, search_graph sg + where g.f = sg.t and not cycle +) +select * from search_graph order by path; + f | t | label | path | cycle +---+---+------------+-------------------------------------------+------- + 1 | 2 | arc 1 -> 2 | {"(1,2)"} | f + 2 | 3 | arc 2 -> 3 | {"(1,2)","(2,3)"} | f + 1 | 3 | arc 1 -> 3 | {"(1,3)"} | f + 1 | 4 | arc 1 -> 4 | {"(1,4)"} | f + 4 | 5 | arc 4 -> 5 | {"(1,4)","(4,5)"} | f + 5 | 1 | arc 5 -> 1 | {"(1,4)","(4,5)","(5,1)"} | f + 1 | 2 | arc 1 -> 2 | {"(1,4)","(4,5)","(5,1)","(1,2)"} | f + 2 | 3 | arc 2 -> 3 | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} | f + 1 | 3 | arc 1 -> 3 | {"(1,4)","(4,5)","(5,1)","(1,3)"} | f + 1 | 4 | arc 1 -> 4 | {"(1,4)","(4,5)","(5,1)","(1,4)"} | t + 2 | 3 | arc 2 -> 3 | {"(2,3)"} | f + 4 | 5 | arc 4 -> 5 | {"(4,5)"} | f + 5 | 1 | arc 5 -> 1 | {"(4,5)","(5,1)"} | f + 1 | 2 | arc 1 -> 2 | {"(4,5)","(5,1)","(1,2)"} | f + 2 | 3 | arc 2 -> 3 | {"(4,5)","(5,1)","(1,2)","(2,3)"} | f + 1 | 3 | arc 1 -> 3 | {"(4,5)","(5,1)","(1,3)"} | f + 1 | 4 | arc 1 -> 4 | {"(4,5)","(5,1)","(1,4)"} | f + 4 | 5 | arc 4 -> 5 | {"(4,5)","(5,1)","(1,4)","(4,5)"} | t + 5 | 1 | arc 5 -> 1 | {"(5,1)"} | f + 1 | 2 | arc 1 -> 2 | {"(5,1)","(1,2)"} | f + 2 | 3 | arc 2 -> 3 | {"(5,1)","(1,2)","(2,3)"} | f + 1 | 3 | arc 1 -> 3 | {"(5,1)","(1,3)"} | f + 1 | 4 | arc 1 -> 4 | {"(5,1)","(1,4)"} | f + 4 | 5 | arc 4 -> 5 | {"(5,1)","(1,4)","(4,5)"} | f + 5 | 1 | arc 5 -> 1 | {"(5,1)","(1,4)","(4,5)","(5,1)"} | t +(25 rows) + +-- +-- test multiple WITH queries +-- +WITH RECURSIVE + y (id) AS (VALUES (1)), + x (id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5) +SELECT * FROM x; + id +---- + 1 + 2 + 3 + 4 + 5 +(5 rows) + +-- forward reference OK +WITH RECURSIVE + x(id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5), + y(id) AS (values (1)) + SELECT * FROM x; + id +---- + 1 + 2 + 3 + 4 + 5 +(5 rows) + +WITH RECURSIVE + x(id) AS + (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5), + y(id) AS + (VALUES (1) UNION ALL SELECT id+1 FROM y WHERE id < 10) + SELECT y.*, x.* FROM y LEFT JOIN x USING (id); + id | id +----+---- + 1 | 1 + 2 | 2 + 3 | 3 + 4 | 4 + 5 | 5 + 6 | + 7 | + 8 | + 9 | + 10 | +(10 rows) + +WITH RECURSIVE + x(id) AS + (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5), + y(id) AS + (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 10) + SELECT y.*, x.* FROM y LEFT JOIN x USING (id); + id | id +----+---- + 1 | 1 + 2 | 2 + 3 | 3 + 4 | 4 + 5 | 5 + 6 | +(6 rows) + +WITH RECURSIVE + x(id) AS + (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ), + y(id) AS + (SELECT * FROM x UNION ALL SELECT * FROM x), + z(id) AS + (SELECT * FROM x UNION ALL SELECT id+1 FROM z WHERE id < 10) + SELECT * FROM z; + id +---- + 1 + 2 + 3 + 2 + 3 + 4 + 3 + 4 + 5 + 4 + 5 + 6 + 5 + 6 + 7 + 6 + 7 + 8 + 7 + 8 + 9 + 8 + 9 + 10 + 9 + 10 + 10 +(27 rows) + +WITH RECURSIVE + x(id) AS + (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ), + y(id) AS + (SELECT * FROM x UNION ALL SELECT * FROM x), + z(id) AS + (SELECT * FROM y UNION ALL SELECT id+1 FROM z WHERE id < 10) + SELECT * FROM z; + id +---- + 1 + 2 + 3 + 1 + 2 + 3 + 2 + 3 + 4 + 2 + 3 + 4 + 3 + 4 + 5 + 3 + 4 + 5 + 4 + 5 + 6 + 4 + 5 + 6 + 5 + 6 + 7 + 5 + 6 + 7 + 6 + 7 + 8 + 6 + 7 + 8 + 7 + 8 + 9 + 7 + 8 + 9 + 8 + 9 + 10 + 8 + 9 + 10 + 9 + 10 + 9 + 10 + 10 + 10 +(54 rows) + +-- +-- Test WITH attached to a data-modifying statement +-- +CREATE TEMPORARY TABLE y (a INTEGER); +INSERT INTO y SELECT generate_series(1, 10); +WITH t AS ( + SELECT a FROM y +) +INSERT INTO y +SELECT a+20 FROM t RETURNING *; + a +---- + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 +(10 rows) + +SELECT * FROM y; + a +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 +(20 rows) + +WITH t AS ( + SELECT a FROM y +) +UPDATE y SET a = y.a-10 FROM t WHERE y.a > 20 AND t.a = y.a RETURNING y.a; + a +---- + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 +(10 rows) + +SELECT * FROM y; + a +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 +(20 rows) + +WITH RECURSIVE t(a) AS ( + SELECT 11 + UNION ALL + SELECT a+1 FROM t WHERE a < 50 +) +DELETE FROM y USING t WHERE t.a = y.a RETURNING y.a; + a +---- + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 +(10 rows) + +SELECT * FROM y; + a +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 +(10 rows) + +DROP TABLE y; +-- +-- error cases +-- +-- INTERSECT +WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT SELECT n+1 FROM x) + SELECT * FROM x; +ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term +LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT SELECT n+1 FROM x... + ^ +WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT ALL SELECT n+1 FROM x) + SELECT * FROM x; +ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term +LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT ALL SELECT n+1 FR... + ^ +-- EXCEPT +WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT SELECT n+1 FROM x) + SELECT * FROM x; +ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term +LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT SELECT n+1 FROM x) + ^ +WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT ALL SELECT n+1 FROM x) + SELECT * FROM x; +ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term +LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT ALL SELECT n+1 FROM ... + ^ +-- no non-recursive term +WITH RECURSIVE x(n) AS (SELECT n FROM x) + SELECT * FROM x; +ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term +LINE 1: WITH RECURSIVE x(n) AS (SELECT n FROM x) + ^ +-- recursive term in the left hand side (strictly speaking, should allow this) +WITH RECURSIVE x(n) AS (SELECT n FROM x UNION ALL SELECT 1) + SELECT * FROM x; +ERROR: recursive reference to query "x" must not appear within its non-recursive term +LINE 1: WITH RECURSIVE x(n) AS (SELECT n FROM x UNION ALL SELECT 1) + ^ +CREATE TEMPORARY TABLE y (a INTEGER); +INSERT INTO y SELECT generate_series(1, 10); +-- LEFT JOIN +WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 + UNION ALL + SELECT x.n+1 FROM y LEFT JOIN x ON x.n = y.a WHERE n < 10) +SELECT * FROM x; +ERROR: recursive reference to query "x" must not appear within an outer join +LINE 3: SELECT x.n+1 FROM y LEFT JOIN x ON x.n = y.a WHERE n < 10) + ^ +-- RIGHT JOIN +WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 + UNION ALL + SELECT x.n+1 FROM x RIGHT JOIN y ON x.n = y.a WHERE n < 10) +SELECT * FROM x; +ERROR: recursive reference to query "x" must not appear within an outer join +LINE 3: SELECT x.n+1 FROM x RIGHT JOIN y ON x.n = y.a WHERE n < 10) + ^ +-- FULL JOIN +WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 + UNION ALL + SELECT x.n+1 FROM x FULL JOIN y ON x.n = y.a WHERE n < 10) +SELECT * FROM x; +ERROR: recursive reference to query "x" must not appear within an outer join +LINE 3: SELECT x.n+1 FROM x FULL JOIN y ON x.n = y.a WHERE n < 10) + ^ +-- subquery +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x + WHERE n IN (SELECT * FROM x)) + SELECT * FROM x; +ERROR: recursive reference to query "x" must not appear within a subquery +LINE 2: WHERE n IN (SELECT * FROM x)) + ^ +-- aggregate functions +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT count(*) FROM x) + SELECT * FROM x; +ERROR: aggregate functions are not allowed in a recursive query's recursive term +LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT count(*) F... + ^ +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT sum(n) FROM x) + SELECT * FROM x; +ERROR: aggregate functions are not allowed in a recursive query's recursive term +LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT sum(n) FRO... + ^ +-- ORDER BY +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x ORDER BY 1) + SELECT * FROM x; +ERROR: ORDER BY in a recursive query is not implemented +LINE 1: ...VE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x ORDER BY 1) + ^ +-- LIMIT/OFFSET +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x LIMIT 10 OFFSET 1) + SELECT * FROM x; +ERROR: OFFSET in a recursive query is not implemented +LINE 1: ... AS (SELECT 1 UNION ALL SELECT n+1 FROM x LIMIT 10 OFFSET 1) + ^ +-- FOR UPDATE +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x FOR UPDATE) + SELECT * FROM x; +ERROR: FOR UPDATE/SHARE in a recursive query is not implemented +-- target list has a recursive query name +WITH RECURSIVE x(id) AS (values (1) + UNION ALL + SELECT (SELECT * FROM x) FROM x WHERE id < 5 +) SELECT * FROM x; +ERROR: recursive reference to query "x" must not appear within a subquery +LINE 3: SELECT (SELECT * FROM x) FROM x WHERE id < 5 + ^ +-- mutual recursive query (not implemented) +WITH RECURSIVE + x (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM y WHERE id < 5), + y (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 5) +SELECT * FROM x; +ERROR: mutual recursion between WITH items is not implemented +LINE 2: x (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM y WHERE id ... + ^ +-- non-linear recursion is not allowed +WITH RECURSIVE foo(i) AS + (values (1) + UNION ALL + (SELECT i+1 FROM foo WHERE i < 10 + UNION ALL + SELECT i+1 FROM foo WHERE i < 5) +) SELECT * FROM foo; +ERROR: recursive reference to query "foo" must not appear more than once +LINE 6: SELECT i+1 FROM foo WHERE i < 5) + ^ +WITH RECURSIVE foo(i) AS + (values (1) + UNION ALL + SELECT * FROM + (SELECT i+1 FROM foo WHERE i < 10 + UNION ALL + SELECT i+1 FROM foo WHERE i < 5) AS t +) SELECT * FROM foo; +ERROR: recursive reference to query "foo" must not appear more than once +LINE 7: SELECT i+1 FROM foo WHERE i < 5) AS t + ^ +WITH RECURSIVE foo(i) AS + (values (1) + UNION ALL + (SELECT i+1 FROM foo WHERE i < 10 + EXCEPT + SELECT i+1 FROM foo WHERE i < 5) +) SELECT * FROM foo; +ERROR: recursive reference to query "foo" must not appear within EXCEPT +LINE 6: SELECT i+1 FROM foo WHERE i < 5) + ^ +WITH RECURSIVE foo(i) AS + (values (1) + UNION ALL + (SELECT i+1 FROM foo WHERE i < 10 + INTERSECT + SELECT i+1 FROM foo WHERE i < 5) +) SELECT * FROM foo; +ERROR: recursive reference to query "foo" must not appear more than once +LINE 6: SELECT i+1 FROM foo WHERE i < 5) + ^ +-- Wrong type induced from non-recursive term +WITH RECURSIVE foo(i) AS + (SELECT i FROM (VALUES(1),(2)) t(i) + UNION ALL + SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10) +SELECT * FROM foo; +ERROR: recursive query "foo" column 1 has type integer in non-recursive term but type numeric overall +LINE 2: (SELECT i FROM (VALUES(1),(2)) t(i) + ^ +HINT: Cast the output of the non-recursive term to the correct type. +-- rejects different typmod, too (should we allow this?) +WITH RECURSIVE foo(i) AS + (SELECT i::numeric(3,0) FROM (VALUES(1),(2)) t(i) + UNION ALL + SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10) +SELECT * FROM foo; +ERROR: recursive query "foo" column 1 has type numeric(3,0) in non-recursive term but type numeric overall +LINE 2: (SELECT i::numeric(3,0) FROM (VALUES(1),(2)) t(i) + ^ +HINT: Cast the output of the non-recursive term to the correct type. +-- disallow OLD/NEW reference in CTE +CREATE TEMPORARY TABLE x (n integer); +CREATE RULE r2 AS ON UPDATE TO x DO INSTEAD + WITH t AS (SELECT OLD.*) UPDATE y SET a = t.n FROM t; +ERROR: cannot refer to OLD within WITH query +-- +-- test for bug #4902 +-- +with cte(foo) as ( values(42) ) values((select foo from cte)); + column1 +--------- + 42 +(1 row) + +with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q; + foo +----- + 42 +(1 row) + +-- test CTE referencing an outer-level variable (to see that changed-parameter +-- signaling still works properly after fixing this bug) +select ( with cte(foo) as ( values(f1) ) + select (select foo from cte) ) +from int4_tbl; + foo +------------- + 0 + 123456 + -123456 + 2147483647 + -2147483647 +(5 rows) + +select ( with cte(foo) as ( values(f1) ) + values((select foo from cte)) ) +from int4_tbl; + column1 +------------- + 0 + 123456 + -123456 + 2147483647 + -2147483647 +(5 rows) + +-- +-- test for nested-recursive-WITH bug +-- +WITH RECURSIVE t(j) AS ( + WITH RECURSIVE s(i) AS ( + VALUES (1) + UNION ALL + SELECT i+1 FROM s WHERE i < 10 + ) + SELECT i FROM s + UNION ALL + SELECT j+1 FROM t WHERE j < 10 +) +SELECT * FROM t; + j +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 5 + 6 + 7 + 8 + 9 + 10 + 6 + 7 + 8 + 9 + 10 + 7 + 8 + 9 + 10 + 8 + 9 + 10 + 9 + 10 + 10 +(55 rows) + +-- +-- test WITH attached to intermediate-level set operation +-- +WITH outermost(x) AS ( + SELECT 1 + UNION (WITH innermost as (SELECT 2) + SELECT * FROM innermost + UNION SELECT 3) +) +SELECT * FROM outermost ORDER BY 1; + x +--- + 1 + 2 + 3 +(3 rows) + +WITH outermost(x) AS ( + SELECT 1 + UNION (WITH innermost as (SELECT 2) + SELECT * FROM outermost -- fail + UNION SELECT * FROM innermost) +) +SELECT * FROM outermost ORDER BY 1; +ERROR: relation "outermost" does not exist +LINE 4: SELECT * FROM outermost + ^ +DETAIL: There is a WITH item named "outermost", but it cannot be referenced from this part of the query. +HINT: Use WITH RECURSIVE, or re-order the WITH items to remove forward references. +WITH RECURSIVE outermost(x) AS ( + SELECT 1 + UNION (WITH innermost as (SELECT 2) + SELECT * FROM outermost + UNION SELECT * FROM innermost) +) +SELECT * FROM outermost ORDER BY 1; + x +--- + 1 + 2 +(2 rows) + +WITH RECURSIVE outermost(x) AS ( + WITH innermost as (SELECT 2 FROM outermost) -- fail + SELECT * FROM innermost + UNION SELECT * from outermost +) +SELECT * FROM outermost ORDER BY 1; +ERROR: recursive reference to query "outermost" must not appear within a subquery +LINE 2: WITH innermost as (SELECT 2 FROM outermost) + ^ +-- +-- This test will fail with the old implementation of PARAM_EXEC parameter +-- assignment, because the "q1" Var passed down to A's targetlist subselect +-- looks exactly like the "A.id" Var passed down to C's subselect, causing +-- the old code to give them the same runtime PARAM_EXEC slot. But the +-- lifespans of the two parameters overlap, thanks to B also reading A. +-- +with +A as ( select q2 as id, (select q1) as x from int8_tbl ), +B as ( select id, row_number() over (partition by id) as r from A ), +C as ( select A.id, array(select B.id from B where B.id = A.id) from A ) +select * from C; + id | array +-------------------+------------------------------------- + 456 | {456} + 4567890123456789 | {4567890123456789,4567890123456789} + 123 | {123} + 4567890123456789 | {4567890123456789,4567890123456789} + -4567890123456789 | {-4567890123456789} +(5 rows) + +-- +-- Test CTEs read in non-initialization orders +-- +WITH RECURSIVE + tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)), + iter (id_key, row_type, link) AS ( + SELECT 0, 'base', 17 + UNION ALL ( + WITH remaining(id_key, row_type, link, min) AS ( + SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER () + FROM tab INNER JOIN iter USING (link) + WHERE tab.id_key > iter.id_key + ), + first_remaining AS ( + SELECT id_key, row_type, link + FROM remaining + WHERE id_key=min + ), + effect AS ( + SELECT tab.id_key, 'new'::text, tab.link + FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key + WHERE e.row_type = 'false' + ) + SELECT * FROM first_remaining + UNION ALL SELECT * FROM effect + ) + ) +SELECT * FROM iter; + id_key | row_type | link +--------+----------+------ + 0 | base | 17 + 1 | true | 17 + 2 | true | 17 + 3 | true | 17 + 4 | true | 17 + 5 | true | 17 + 6 | true | 17 +(7 rows) + +WITH RECURSIVE + tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)), + iter (id_key, row_type, link) AS ( + SELECT 0, 'base', 17 + UNION ( + WITH remaining(id_key, row_type, link, min) AS ( + SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER () + FROM tab INNER JOIN iter USING (link) + WHERE tab.id_key > iter.id_key + ), + first_remaining AS ( + SELECT id_key, row_type, link + FROM remaining + WHERE id_key=min + ), + effect AS ( + SELECT tab.id_key, 'new'::text, tab.link + FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key + WHERE e.row_type = 'false' + ) + SELECT * FROM first_remaining + UNION ALL SELECT * FROM effect + ) + ) +SELECT * FROM iter; + id_key | row_type | link +--------+----------+------ + 0 | base | 17 + 1 | true | 17 + 2 | true | 17 + 3 | true | 17 + 4 | true | 17 + 5 | true | 17 + 6 | true | 17 +(7 rows) + +-- +-- Data-modifying statements in WITH +-- +-- INSERT ... RETURNING +WITH t AS ( + INSERT INTO y + VALUES + (11), + (12), + (13), + (14), + (15), + (16), + (17), + (18), + (19), + (20) + RETURNING * +) +SELECT * FROM t; + a +---- + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 +(10 rows) + +SELECT * FROM y; + a +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 +(20 rows) + +-- UPDATE ... RETURNING +WITH t AS ( + UPDATE y + SET a=a+1 + RETURNING * +) +SELECT * FROM t; + a +---- + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 +(20 rows) + +SELECT * FROM y; + a +---- + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 +(20 rows) + +-- DELETE ... RETURNING +WITH t AS ( + DELETE FROM y + WHERE a <= 10 + RETURNING * +) +SELECT * FROM t; + a +---- + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 +(9 rows) + +SELECT * FROM y; + a +---- + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 +(11 rows) + +-- forward reference +WITH RECURSIVE t AS ( + INSERT INTO y + SELECT a+5 FROM t2 WHERE a > 5 + RETURNING * +), t2 AS ( + UPDATE y SET a=a-11 RETURNING * +) +SELECT * FROM t +UNION ALL +SELECT * FROM t2; + a +---- + 11 + 12 + 13 + 14 + 15 + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 +(16 rows) + +SELECT * FROM y; + a +---- + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 11 + 7 + 12 + 8 + 13 + 9 + 14 + 10 + 15 +(16 rows) + +-- unconditional DO INSTEAD rule +CREATE RULE y_rule AS ON DELETE TO y DO INSTEAD + INSERT INTO y VALUES(42) RETURNING *; +WITH t AS ( + DELETE FROM y RETURNING * +) +SELECT * FROM t; + a +---- + 42 +(1 row) + +SELECT * FROM y; + a +---- + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 11 + 7 + 12 + 8 + 13 + 9 + 14 + 10 + 15 + 42 +(17 rows) + +DROP RULE y_rule ON y; +-- check merging of outer CTE with CTE in a rule action +CREATE TEMP TABLE bug6051 AS + select i from generate_series(1,3) as t(i); +SELECT * FROM bug6051; + i +--- + 1 + 2 + 3 +(3 rows) + +WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) +INSERT INTO bug6051 SELECT * FROM t1; +SELECT * FROM bug6051; + i +--- + 1 + 2 + 3 +(3 rows) + +CREATE TEMP TABLE bug6051_2 (i int); +CREATE RULE bug6051_ins AS ON INSERT TO bug6051 DO INSTEAD + INSERT INTO bug6051_2 + SELECT NEW.i; +WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) +INSERT INTO bug6051 SELECT * FROM t1; +SELECT * FROM bug6051; + i +--- +(0 rows) + +SELECT * FROM bug6051_2; + i +--- + 1 + 2 + 3 +(3 rows) + +-- a truly recursive CTE in the same list +WITH RECURSIVE t(a) AS ( + SELECT 0 + UNION ALL + SELECT a+1 FROM t WHERE a+1 < 5 +), t2 as ( + INSERT INTO y + SELECT * FROM t RETURNING * +) +SELECT * FROM t2 JOIN y USING (a) ORDER BY a; + a +--- + 0 + 1 + 2 + 3 + 4 +(5 rows) + +SELECT * FROM y; + a +---- + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 11 + 7 + 12 + 8 + 13 + 9 + 14 + 10 + 15 + 42 + 0 + 1 + 2 + 3 + 4 +(22 rows) + +-- data-modifying WITH in a modifying statement +WITH t AS ( + DELETE FROM y + WHERE a <= 10 + RETURNING * +) +INSERT INTO y SELECT -a FROM t RETURNING *; + a +----- + 0 + -1 + -2 + -3 + -4 + -5 + -6 + -7 + -8 + -9 + -10 + 0 + -1 + -2 + -3 + -4 +(16 rows) + +SELECT * FROM y; + a +----- + 11 + 12 + 13 + 14 + 15 + 42 + 0 + -1 + -2 + -3 + -4 + -5 + -6 + -7 + -8 + -9 + -10 + 0 + -1 + -2 + -3 + -4 +(22 rows) + +-- check that WITH query is run to completion even if outer query isn't +WITH t AS ( + UPDATE y SET a = a * 100 RETURNING * +) +SELECT * FROM t LIMIT 10; + a +------ + 1100 + 1200 + 1300 + 1400 + 1500 + 4200 + 0 + -100 + -200 + -300 +(10 rows) + +SELECT * FROM y; + a +------- + 1100 + 1200 + 1300 + 1400 + 1500 + 4200 + 0 + -100 + -200 + -300 + -400 + -500 + -600 + -700 + -800 + -900 + -1000 + 0 + -100 + -200 + -300 + -400 +(22 rows) + +-- data-modifying WITH containing INSERT...ON CONFLICT DO UPDATE +CREATE TABLE withz AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i; +ALTER TABLE withz ADD UNIQUE (k); +WITH t AS ( + INSERT INTO withz SELECT i, 'insert' + FROM generate_series(0, 16) i + ON CONFLICT (k) DO UPDATE SET v = withz.v || ', now update' + RETURNING * +) +SELECT * FROM t JOIN y ON t.k = y.a ORDER BY a, k; + k | v | a +---+--------+--- + 0 | insert | 0 + 0 | insert | 0 +(2 rows) + +-- Test EXCLUDED.* reference within CTE +WITH aa AS ( + INSERT INTO withz VALUES(1, 5) ON CONFLICT (k) DO UPDATE SET v = EXCLUDED.v + WHERE withz.k != EXCLUDED.k + RETURNING * +) +SELECT * FROM aa; + k | v +---+--- +(0 rows) + +-- New query/snapshot demonstrates side-effects of previous query. +SELECT * FROM withz ORDER BY k; + k | v +----+------------------ + 0 | insert + 1 | 1 v, now update + 2 | insert + 3 | insert + 4 | 4 v, now update + 5 | insert + 6 | insert + 7 | 7 v, now update + 8 | insert + 9 | insert + 10 | 10 v, now update + 11 | insert + 12 | insert + 13 | 13 v, now update + 14 | insert + 15 | insert + 16 | 16 v, now update +(17 rows) + +-- +-- Ensure subqueries within the update clause work, even if they +-- reference outside values +-- +WITH aa AS (SELECT 1 a, 2 b) +INSERT INTO withz VALUES(1, 'insert') +ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); +WITH aa AS (SELECT 1 a, 2 b) +INSERT INTO withz VALUES(1, 'insert') +ON CONFLICT (k) DO UPDATE SET v = ' update' WHERE withz.k = (SELECT a FROM aa); +WITH aa AS (SELECT 1 a, 2 b) +INSERT INTO withz VALUES(1, 'insert') +ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); +WITH aa AS (SELECT 'a' a, 'b' b UNION ALL SELECT 'a' a, 'b' b) +INSERT INTO withz VALUES(1, 'insert') +ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 'a' LIMIT 1); +WITH aa AS (SELECT 1 a, 2 b) +INSERT INTO withz VALUES(1, (SELECT b || ' insert' FROM aa WHERE a = 1 )) +ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); +-- Update a row more than once, in different parts of a wCTE. That is +-- an allowed, presumably very rare, edge case, but since it was +-- broken in the past, having a test seems worthwhile. +WITH simpletup AS ( + SELECT 2 k, 'Green' v), +upsert_cte AS ( + INSERT INTO withz VALUES(2, 'Blue') ON CONFLICT (k) DO + UPDATE SET (k, v) = (SELECT k, v FROM simpletup WHERE simpletup.k = withz.k) + RETURNING k, v) +INSERT INTO withz VALUES(2, 'Red') ON CONFLICT (k) DO +UPDATE SET (k, v) = (SELECT k, v FROM upsert_cte WHERE upsert_cte.k = withz.k) +RETURNING k, v; + k | v +---+--- +(0 rows) + +DROP TABLE withz; +-- check that run to completion happens in proper ordering +TRUNCATE TABLE y; +INSERT INTO y SELECT generate_series(1, 3); +CREATE TEMPORARY TABLE yy (a INTEGER); +WITH RECURSIVE t1 AS ( + INSERT INTO y SELECT * FROM y RETURNING * +), t2 AS ( + INSERT INTO yy SELECT * FROM t1 RETURNING * +) +SELECT 1; + ?column? +---------- + 1 +(1 row) + +SELECT * FROM y; + a +--- + 1 + 2 + 3 + 1 + 2 + 3 +(6 rows) + +SELECT * FROM yy; + a +--- + 1 + 2 + 3 +(3 rows) + +WITH RECURSIVE t1 AS ( + INSERT INTO yy SELECT * FROM t2 RETURNING * +), t2 AS ( + INSERT INTO y SELECT * FROM y RETURNING * +) +SELECT 1; + ?column? +---------- + 1 +(1 row) + +SELECT * FROM y; + a +--- + 1 + 2 + 3 + 1 + 2 + 3 + 1 + 2 + 3 + 1 + 2 + 3 +(12 rows) + +SELECT * FROM yy; + a +--- + 1 + 2 + 3 + 1 + 2 + 3 + 1 + 2 + 3 +(9 rows) + +-- triggers +TRUNCATE TABLE y; +INSERT INTO y SELECT generate_series(1, 10); +CREATE FUNCTION y_trigger() RETURNS trigger AS $$ +begin + raise notice 'y_trigger: a = %', new.a; + return new; +end; +$$ LANGUAGE plpgsql; +CREATE TRIGGER y_trig BEFORE INSERT ON y FOR EACH ROW + EXECUTE PROCEDURE y_trigger(); +WITH t AS ( + INSERT INTO y + VALUES + (21), + (22), + (23) + RETURNING * +) +SELECT * FROM t; +NOTICE: y_trigger: a = 21 +NOTICE: y_trigger: a = 22 +NOTICE: y_trigger: a = 23 + a +---- + 21 + 22 + 23 +(3 rows) + +SELECT * FROM y; + a +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 21 + 22 + 23 +(13 rows) + +DROP TRIGGER y_trig ON y; +CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH ROW + EXECUTE PROCEDURE y_trigger(); +WITH t AS ( + INSERT INTO y + VALUES + (31), + (32), + (33) + RETURNING * +) +SELECT * FROM t LIMIT 1; +NOTICE: y_trigger: a = 31 +NOTICE: y_trigger: a = 32 +NOTICE: y_trigger: a = 33 + a +---- + 31 +(1 row) + +SELECT * FROM y; + a +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 21 + 22 + 23 + 31 + 32 + 33 +(16 rows) + +DROP TRIGGER y_trig ON y; +CREATE OR REPLACE FUNCTION y_trigger() RETURNS trigger AS $$ +begin + raise notice 'y_trigger'; + return null; +end; +$$ LANGUAGE plpgsql; +CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH STATEMENT + EXECUTE PROCEDURE y_trigger(); +WITH t AS ( + INSERT INTO y + VALUES + (41), + (42), + (43) + RETURNING * +) +SELECT * FROM t; +NOTICE: y_trigger + a +---- + 41 + 42 + 43 +(3 rows) + +SELECT * FROM y; + a +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 21 + 22 + 23 + 31 + 32 + 33 + 41 + 42 + 43 +(19 rows) + +DROP TRIGGER y_trig ON y; +DROP FUNCTION y_trigger(); +-- WITH attached to inherited UPDATE or DELETE +CREATE TEMP TABLE parent ( id int, val text ); +CREATE TEMP TABLE child1 ( ) INHERITS ( parent ); +CREATE TEMP TABLE child2 ( ) INHERITS ( parent ); +INSERT INTO parent VALUES ( 1, 'p1' ); +INSERT INTO child1 VALUES ( 11, 'c11' ),( 12, 'c12' ); +INSERT INTO child2 VALUES ( 23, 'c21' ),( 24, 'c22' ); +WITH rcte AS ( SELECT sum(id) AS totalid FROM parent ) +UPDATE parent SET id = id + totalid FROM rcte; +SELECT * FROM parent; + id | val +----+----- + 72 | p1 + 82 | c11 + 83 | c12 + 94 | c21 + 95 | c22 +(5 rows) + +WITH wcte AS ( INSERT INTO child1 VALUES ( 42, 'new' ) RETURNING id AS newid ) +UPDATE parent SET id = id + newid FROM wcte; +SELECT * FROM parent; + id | val +-----+----- + 114 | p1 + 42 | new + 124 | c11 + 125 | c12 + 136 | c21 + 137 | c22 +(6 rows) + +WITH rcte AS ( SELECT max(id) AS maxid FROM parent ) +DELETE FROM parent USING rcte WHERE id = maxid; +SELECT * FROM parent; + id | val +-----+----- + 114 | p1 + 42 | new + 124 | c11 + 125 | c12 + 136 | c21 +(5 rows) + +WITH wcte AS ( INSERT INTO child2 VALUES ( 42, 'new2' ) RETURNING id AS newid ) +DELETE FROM parent USING wcte WHERE id = newid; +SELECT * FROM parent; + id | val +-----+------ + 114 | p1 + 124 | c11 + 125 | c12 + 136 | c21 + 42 | new2 +(5 rows) + +-- check EXPLAIN VERBOSE for a wCTE with RETURNING +EXPLAIN (VERBOSE, COSTS OFF) +WITH wcte AS ( INSERT INTO int8_tbl VALUES ( 42, 47 ) RETURNING q2 ) +DELETE FROM a USING wcte WHERE aa = q2; + QUERY PLAN +---------------------------------------------------- + Delete on public.a + Delete on public.a + Delete on public.b + Delete on public.c + Delete on public.d + CTE wcte + -> Insert on public.int8_tbl + Output: int8_tbl.q2 + -> Result + Output: '42'::bigint, '47'::bigint + -> Nested Loop + Output: a.ctid, wcte.* + Join Filter: (a.aa = wcte.q2) + -> Seq Scan on public.a + Output: a.ctid, a.aa + -> CTE Scan on wcte + Output: wcte.*, wcte.q2 + -> Nested Loop + Output: b.ctid, wcte.* + Join Filter: (b.aa = wcte.q2) + -> Seq Scan on public.b + Output: b.ctid, b.aa + -> CTE Scan on wcte + Output: wcte.*, wcte.q2 + -> Nested Loop + Output: c.ctid, wcte.* + Join Filter: (c.aa = wcte.q2) + -> Seq Scan on public.c + Output: c.ctid, c.aa + -> CTE Scan on wcte + Output: wcte.*, wcte.q2 + -> Nested Loop + Output: d.ctid, wcte.* + Join Filter: (d.aa = wcte.q2) + -> Seq Scan on public.d + Output: d.ctid, d.aa + -> CTE Scan on wcte + Output: wcte.*, wcte.q2 +(38 rows) + +-- error cases +-- data-modifying WITH tries to use its own output +WITH RECURSIVE t AS ( + INSERT INTO y + SELECT * FROM t +) +VALUES(FALSE); +ERROR: recursive query "t" must not contain data-modifying statements +LINE 1: WITH RECURSIVE t AS ( + ^ +-- no RETURNING in a referenced data-modifying WITH +WITH t AS ( + INSERT INTO y VALUES(0) +) +SELECT * FROM t; +ERROR: WITH query "t" does not have a RETURNING clause +LINE 4: SELECT * FROM t; + ^ +-- data-modifying WITH allowed only at the top level +SELECT * FROM ( + WITH t AS (UPDATE y SET a=a+1 RETURNING *) + SELECT * FROM t +) ss; +ERROR: WITH clause containing a data-modifying statement must be at the top level +LINE 2: WITH t AS (UPDATE y SET a=a+1 RETURNING *) + ^ +-- most variants of rules aren't allowed +CREATE RULE y_rule AS ON INSERT TO y WHERE a=0 DO INSTEAD DELETE FROM y; +WITH t AS ( + INSERT INTO y VALUES(0) +) +VALUES(FALSE); +ERROR: conditional DO INSTEAD rules are not supported for data-modifying statements in WITH +DROP RULE y_rule ON y; +-- check that parser lookahead for WITH doesn't cause any odd behavior +create table foo (with baz); -- fail, WITH is a reserved word +ERROR: syntax error at or near "with" +LINE 1: create table foo (with baz); + ^ +create table foo (with ordinality); -- fail, WITH is a reserved word +ERROR: syntax error at or near "with" +LINE 1: create table foo (with ordinality); + ^ +with ordinality as (select 1 as x) select * from ordinality; + x +--- + 1 +(1 row) + +-- check sane response to attempt to modify CTE relation +WITH test AS (SELECT 42) INSERT INTO test VALUES (1); +ERROR: relation "test" does not exist +LINE 1: WITH test AS (SELECT 42) INSERT INTO test VALUES (1); + ^ +-- check response to attempt to modify table with same name as a CTE (perhaps +-- surprisingly it works, because CTEs don't hide tables from data-modifying +-- statements) +create temp table test (i int); +with test as (select 42) insert into test select * from test; +select * from test; + i +---- + 42 +(1 row) + +drop table test; From e2c2fa0dedf1078744e0073a3b2d3ff79cda4c5a Mon Sep 17 00:00:00 2001 From: Peter Toth Date: Wed, 12 Jun 2019 20:54:17 +0200 Subject: [PATCH 2/8] analysis and regenerate output --- .../resources/sql-tests/inputs/pgSQL/with.sql | 1666 +++++----- .../sql-tests/results/pgSQL/with.sql.out | 2743 +++-------------- 2 files changed, 1378 insertions(+), 3031 deletions(-) diff --git a/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql b/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql index ed2d6cacf0dc..452d7f4658ec 100644 --- a/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql +++ b/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql @@ -10,89 +10,105 @@ -- -- Basic WITH +-- [ORIGINAL SQL] +--WITH q1(x,y) AS (SELECT 1,2) +--SELECT * FROM q1, q1 AS q2; WITH q1(x,y) AS (SELECT 1,2) -SELECT * FROM q1, q1 AS q2; +SELECT * FROM q1 CROSS JOIN q1 AS q2; -- Multiple uses are evaluated only once -SELECT count(*) FROM ( - WITH q1(x) AS (SELECT random() FROM generate_series(1, 5)) - SELECT * FROM q1 - UNION - SELECT * FROM q1 -) ss; +-- [SPARK-19799] Support recursive SQL query +--SELECT count(*) FROM ( +-- WITH q1(x) AS (SELECT random() FROM generate_series(1, 5)) +-- SELECT * FROM q1 +-- UNION +-- SELECT * FROM q1 +--) ss; -- WITH RECURSIVE -- sum of 1..100 -WITH RECURSIVE t(n) AS ( - VALUES (1) -UNION ALL - SELECT n+1 FROM t WHERE n < 100 -) -SELECT sum(n) FROM t; - -WITH RECURSIVE t(n) AS ( - SELECT (VALUES(1)) -UNION ALL - SELECT n+1 FROM t WHERE n < 5 -) -SELECT * FROM t; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE t(n) AS ( +-- VALUES (1) +--UNION ALL +-- SELECT n+1 FROM t WHERE n < 100 +--) +--SELECT sum(n) FROM t; + +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE t(n) AS ( +-- SELECT (VALUES(1)) +--UNION ALL +-- SELECT n+1 FROM t WHERE n < 5 +--) +--SELECT * FROM t; -- recursive view -CREATE RECURSIVE VIEW nums (n) AS - VALUES (1) -UNION ALL - SELECT n+1 FROM nums WHERE n < 5; - -SELECT * FROM nums; - -CREATE OR REPLACE RECURSIVE VIEW nums (n) AS - VALUES (1) -UNION ALL - SELECT n+1 FROM nums WHERE n < 6; +-- [SPARK-24497] Support recursive SQL query +--CREATE RECURSIVE VIEW nums (n) AS +-- VALUES (1) +--UNION ALL +-- SELECT n+1 FROM nums WHERE n < 5; +-- +--SELECT * FROM nums; -SELECT * FROM nums; +-- [SPARK-24497] Support recursive SQL query +--CREATE OR REPLACE RECURSIVE VIEW nums (n) AS +-- VALUES (1) +--UNION ALL +-- SELECT n+1 FROM nums WHERE n < 6; +-- +--SELECT * FROM nums; -- This is an infinite loop with UNION ALL, but not with UNION -WITH RECURSIVE t(n) AS ( - SELECT 1 -UNION - SELECT 10-n FROM t) -SELECT * FROM t; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE t(n) AS ( +-- SELECT 1 +--UNION +-- SELECT 10-n FROM t) +--SELECT * FROM t; -- This'd be an infinite loop, but outside query reads only as much as needed -WITH RECURSIVE t(n) AS ( - VALUES (1) -UNION ALL - SELECT n+1 FROM t) -SELECT * FROM t LIMIT 10; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE t(n) AS ( +-- VALUES (1) +--UNION ALL +-- SELECT n+1 FROM t) +--SELECT * FROM t LIMIT 10; -- UNION case should have same property -WITH RECURSIVE t(n) AS ( - SELECT 1 -UNION - SELECT n+1 FROM t) -SELECT * FROM t LIMIT 10; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE t(n) AS ( +-- SELECT 1 +--UNION +-- SELECT n+1 FROM t) +--SELECT * FROM t LIMIT 10; -- Test behavior with an unknown-type literal in the WITH -WITH q AS (SELECT 'foo' AS x) -SELECT x, x IS OF (text) AS is_text FROM q; - -WITH RECURSIVE t(n) AS ( - SELECT 'foo' -UNION ALL - SELECT n || ' bar' FROM t WHERE length(n) < 20 -) -SELECT n, n IS OF (text) AS is_text FROM t; +-- [SPARK-28146] Support IS OF type predicate +--WITH q AS (SELECT 'foo' AS x) +--SELECT x, x IS OF (text) AS is_text FROM q; + +-- [SPARK-24497] Support recursive SQL query +-- [SPARK-28146] Support IS OF type predicate +--WITH RECURSIVE t(n) AS ( +-- SELECT 'foo' +--UNION ALL +-- SELECT n || ' bar' FROM t WHERE length(n) < 20 +--) +--SELECT n, n IS OF (text) AS is_text FROM t; -- In a perfect world, this would work and resolve the literal as int ... -- but for now, we have to be content with resolving to text too soon. -WITH RECURSIVE t(n) AS ( - SELECT '7' -UNION ALL - SELECT n+1 FROM t WHERE n < 10 -) -SELECT n, n IS OF (int) AS is_int FROM t; +-- [SPARK-24497] Support recursive SQL query +-- [SPARK-28146] Support IS OF type predicate +--WITH RECURSIVE t(n) AS ( +-- SELECT '7' +--UNION ALL +-- SELECT n+1 FROM t WHERE n < 10 +--) +--SELECT n, n IS OF (int) AS is_int FROM t; -- -- Some examples with a tree @@ -104,11 +120,18 @@ SELECT n, n IS OF (int) AS is_int FROM t; -- | +->D-+->F -- +->E-+->G -CREATE TEMP TABLE department ( - id INTEGER PRIMARY KEY, -- department ID - parent_department INTEGER REFERENCES department, -- upper department ID - name TEXT -- department name -); + +-- [ORIGINAL SQL] +--CREATE TEMP TABLE department ( +-- id INTEGER PRIMARY KEY, -- department ID +-- parent_department INTEGER REFERENCES department, -- upper department ID +-- name string -- department name +--); +CREATE TABLE department ( + id INTEGER, -- department ID + parent_department INTEGER, -- upper department ID + name string -- department name +) USING parquet; INSERT INTO department VALUES (0, NULL, 'ROOT'); INSERT INTO department VALUES (1, 0, 'A'); @@ -121,143 +144,162 @@ INSERT INTO department VALUES (7, 5, 'G'); -- extract all departments under 'A'. Result should be A, B, C, D and F -WITH RECURSIVE subdepartment AS -( - -- non recursive term - SELECT name as root_name, * FROM department WHERE name = 'A' - - UNION ALL - - -- recursive term - SELECT sd.root_name, d.* FROM department AS d, subdepartment AS sd - WHERE d.parent_department = sd.id -) -SELECT * FROM subdepartment ORDER BY name; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE subdepartment AS +--( +-- -- non recursive term +-- SELECT name as root_name, * FROM department WHERE name = 'A' +-- +-- UNION ALL +-- +-- -- recursive term +-- SELECT sd.root_name, d.* FROM department AS d, subdepartment AS sd +-- WHERE d.parent_department = sd.id +--) +--SELECT * FROM subdepartment ORDER BY name; -- extract all departments under 'A' with "level" number -WITH RECURSIVE subdepartment(level, id, parent_department, name) AS -( - -- non recursive term - SELECT 1, * FROM department WHERE name = 'A' - - UNION ALL - - -- recursive term - SELECT sd.level + 1, d.* FROM department AS d, subdepartment AS sd - WHERE d.parent_department = sd.id -) -SELECT * FROM subdepartment ORDER BY name; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE subdepartment(level, id, parent_department, name) AS +--( +-- -- non recursive term +-- SELECT 1, * FROM department WHERE name = 'A' +-- +-- UNION ALL +-- +-- -- recursive term +-- SELECT sd.level + 1, d.* FROM department AS d, subdepartment AS sd +-- WHERE d.parent_department = sd.id +--) +--SELECT * FROM subdepartment ORDER BY name; -- extract all departments under 'A' with "level" number. -- Only shows level 2 or more -WITH RECURSIVE subdepartment(level, id, parent_department, name) AS -( - -- non recursive term - SELECT 1, * FROM department WHERE name = 'A' - - UNION ALL - - -- recursive term - SELECT sd.level + 1, d.* FROM department AS d, subdepartment AS sd - WHERE d.parent_department = sd.id -) -SELECT * FROM subdepartment WHERE level >= 2 ORDER BY name; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE subdepartment(level, id, parent_department, name) AS +--( +-- -- non recursive term +-- SELECT 1, * FROM department WHERE name = 'A' +-- +-- UNION ALL +-- +-- -- recursive term +-- SELECT sd.level + 1, d.* FROM department AS d, subdepartment AS sd +-- WHERE d.parent_department = sd.id +--) +--SELECT * FROM subdepartment WHERE level >= 2 ORDER BY name; -- "RECURSIVE" is ignored if the query has no self-reference -WITH RECURSIVE subdepartment AS -( - -- note lack of recursive UNION structure - SELECT * FROM department WHERE name = 'A' -) -SELECT * FROM subdepartment ORDER BY name; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE subdepartment AS +--( +-- -- note lack of recursive UNION structure +-- SELECT * FROM department WHERE name = 'A' +--) +--SELECT * FROM subdepartment ORDER BY name; -- inside subqueries -SELECT count(*) FROM ( - WITH RECURSIVE t(n) AS ( - SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 500 - ) - SELECT * FROM t) AS t WHERE n < ( - SELECT count(*) FROM ( - WITH RECURSIVE t(n) AS ( - SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 100 - ) - SELECT * FROM t WHERE n < 50000 - ) AS t WHERE n < 100); +-- [SPARK-19799] Support WITH clause in subqueries +-- [SPARK-24497] Support recursive SQL query +--SELECT count(*) FROM ( +-- WITH RECURSIVE t(n) AS ( +-- SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 500 +-- ) +-- SELECT * FROM t) AS t WHERE n < ( +-- SELECT count(*) FROM ( +-- WITH RECURSIVE t(n) AS ( +-- SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 100 +-- ) +-- SELECT * FROM t WHERE n < 50000 +-- ) AS t WHERE n < 100); -- use same CTE twice at different subquery levels -WITH q1(x,y) AS ( - SELECT hundred, sum(ten) FROM tenk1 GROUP BY hundred - ) -SELECT count(*) FROM q1 WHERE y > (SELECT sum(y)/100 FROM q1 qsub); +-- [SPARK-24497] Support recursive SQL query +--WITH q1(x,y) AS ( +-- SELECT hundred, sum(ten) FROM tenk1 GROUP BY hundred +-- ) +--SELECT count(*) FROM q1 WHERE y > (SELECT sum(y)/100 FROM q1 qsub); -- via a VIEW -CREATE TEMPORARY VIEW vsubdepartment AS - WITH RECURSIVE subdepartment AS - ( - -- non recursive term - SELECT * FROM department WHERE name = 'A' - UNION ALL - -- recursive term - SELECT d.* FROM department AS d, subdepartment AS sd - WHERE d.parent_department = sd.id - ) - SELECT * FROM subdepartment; - -SELECT * FROM vsubdepartment ORDER BY name; +-- [SPARK-24497] Support recursive SQL query +--CREATE TEMPORARY VIEW vsubdepartment AS +-- WITH RECURSIVE subdepartment AS +-- ( +-- -- non recursive term +-- SELECT * FROM department WHERE name = 'A' +-- UNION ALL +-- -- recursive term +-- SELECT d.* FROM department AS d, subdepartment AS sd +-- WHERE d.parent_department = sd.id +-- ) +-- SELECT * FROM subdepartment; +-- +--SELECT * FROM vsubdepartment ORDER BY name; -- Check reverse listing -SELECT pg_get_viewdef('vsubdepartment'::regclass); -SELECT pg_get_viewdef('vsubdepartment'::regclass, true); +-- [TODO] is there a way to show DDL of a view? +--SELECT pg_get_viewdef('vsubdepartment'::regclass); +--SELECT pg_get_viewdef('vsubdepartment'::regclass, true); -- Another reverse-listing example -CREATE VIEW sums_1_100 AS -WITH RECURSIVE t(n) AS ( - VALUES (1) -UNION ALL - SELECT n+1 FROM t WHERE n < 100 -) -SELECT sum(n) FROM t; - -\d+ sums_1_100 +-- [SPARK-24497] Support recursive SQL query +--CREATE VIEW sums_1_100 AS +--WITH RECURSIVE t(n) AS ( +-- VALUES (1) +--UNION ALL +-- SELECT n+1 FROM t WHERE n < 100 +--) +--SELECT sum(n) FROM t; + +-- [TODO] is there a way to show DDL of a view? +--\d+ sums_1_100 -- corner case in which sub-WITH gets initialized first -with recursive q as ( - select * from department - union all - (with x as (select * from q) - select * from x) - ) -select * from q limit 24; - -with recursive q as ( - select * from department - union all - (with recursive x as ( - select * from department - union all - (select * from q union all select * from x) - ) - select * from x) - ) -select * from q limit 32; +-- [SPARK-24497] Support recursive SQL query +--with recursive q as ( +-- select * from department +-- union all +-- (with x as (select * from q) +-- select * from x) +-- ) +--select * from q limit 24; + +-- [SPARK-24497] Support recursive SQL query +--with recursive q as ( +-- select * from department +-- union all +-- (with recursive x as ( +-- select * from department +-- union all +-- (select * from q union all select * from x) +-- ) +-- select * from x) +-- ) +--select * from q limit 32; -- recursive term has sub-UNION -WITH RECURSIVE t(i,j) AS ( - VALUES (1,2) - UNION ALL - SELECT t2.i, t.j+1 FROM - (SELECT 2 AS i UNION ALL SELECT 3 AS i) AS t2 - JOIN t ON (t2.i = t.i+1)) - - SELECT * FROM t; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE t(i,j) AS ( +-- VALUES (1,2) +-- UNION ALL +-- SELECT t2.i, t.j+1 FROM +-- (SELECT 2 AS i UNION ALL SELECT 3 AS i) AS t2 +-- JOIN t ON (t2.i = t.i+1)) +-- +-- SELECT * FROM t; -- -- different tree example -- -CREATE TEMPORARY TABLE tree( - id INTEGER PRIMARY KEY, - parent_id INTEGER REFERENCES tree(id) -); +-- [ORIGINAL SQL] +--CREATE TEMPORARY TABLE tree( +-- id INTEGER PRIMARY KEY, +-- parent_id INTEGER REFERENCES tree(id) +--); +CREATE TABLE tree( + id INTEGER, + parent_id INTEGER +) USING parquet; INSERT INTO tree VALUES (1, NULL), (2, 1), (3,1), (4,2), (5,2), (6,2), (7,3), (8,3), @@ -266,46 +308,51 @@ VALUES (1, NULL), (2, 1), (3,1), (4,2), (5,2), (6,2), (7,3), (8,3), -- -- get all paths from "second level" nodes to leaf nodes -- -WITH RECURSIVE t(id, path) AS ( - VALUES(1,ARRAY[]::integer[]) -UNION ALL - SELECT tree.id, t.path || tree.id - FROM tree JOIN t ON (tree.parent_id = t.id) -) -SELECT t1.*, t2.* FROM t AS t1 JOIN t AS t2 ON - (t1.path[1] = t2.path[1] AND - array_upper(t1.path,1) = 1 AND - array_upper(t2.path,1) > 1) - ORDER BY t1.id, t2.id; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE t(id, path) AS ( +-- VALUES(1,ARRAY[]::integer[]) +--UNION ALL +-- SELECT tree.id, t.path || tree.id +-- FROM tree JOIN t ON (tree.parent_id = t.id) +--) +--SELECT t1.*, t2.* FROM t AS t1 JOIN t AS t2 ON +-- (t1.path[1] = t2.path[1] AND +-- array_upper(t1.path,1) = 1 AND +-- array_upper(t2.path,1) > 1) +-- ORDER BY t1.id, t2.id; -- just count 'em -WITH RECURSIVE t(id, path) AS ( - VALUES(1,ARRAY[]::integer[]) -UNION ALL - SELECT tree.id, t.path || tree.id - FROM tree JOIN t ON (tree.parent_id = t.id) -) -SELECT t1.id, count(t2.*) FROM t AS t1 JOIN t AS t2 ON - (t1.path[1] = t2.path[1] AND - array_upper(t1.path,1) = 1 AND - array_upper(t2.path,1) > 1) - GROUP BY t1.id - ORDER BY t1.id; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE t(id, path) AS ( +-- VALUES(1,ARRAY[]::integer[]) +--UNION ALL +-- SELECT tree.id, t.path || tree.id +-- FROM tree JOIN t ON (tree.parent_id = t.id) +--) +--SELECT t1.id, count(t2.*) FROM t AS t1 JOIN t AS t2 ON +-- (t1.path[1] = t2.path[1] AND +-- array_upper(t1.path,1) = 1 AND +-- array_upper(t2.path,1) > 1) +-- GROUP BY t1.id +-- ORDER BY t1.id; -- this variant tickled a whole-row-variable bug in 8.4devel -WITH RECURSIVE t(id, path) AS ( - VALUES(1,ARRAY[]::integer[]) -UNION ALL - SELECT tree.id, t.path || tree.id - FROM tree JOIN t ON (tree.parent_id = t.id) -) -SELECT t1.id, t2.path, t2 FROM t AS t1 JOIN t AS t2 ON -(t1.id=t2.id); +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE t(id, path) AS ( +-- VALUES(1,ARRAY[]::integer[]) +--UNION ALL +-- SELECT tree.id, t.path || tree.id +-- FROM tree JOIN t ON (tree.parent_id = t.id) +--) +--SELECT t1.id, t2.path, t2 FROM t AS t1 JOIN t AS t2 ON +--(t1.id=t2.id); -- -- test cycle detection -- -create temp table graph( f int, t int, label text ); +-- [ORIGINAL SQL] +--create temp table graph( f int, t int, label text ); +create table graph( f int, t int, label string ) USING parquet; insert into graph values (1, 2, 'arc 1 -> 2'), @@ -315,101 +362,116 @@ insert into graph values (4, 5, 'arc 4 -> 5'), (5, 1, 'arc 5 -> 1'); -with recursive search_graph(f, t, label, path, cycle) as ( - select *, array[row(g.f, g.t)], false from graph g - union all - select g.*, path || row(g.f, g.t), row(g.f, g.t) = any(path) - from graph g, search_graph sg - where g.f = sg.t and not cycle -) -select * from search_graph; +-- [SPARK-24497] Support recursive SQL query +--with recursive search_graph(f, t, label, path, cycle) as ( +-- select *, array[row(g.f, g.t)], false from graph g +-- union all +-- select g.*, path || row(g.f, g.t), row(g.f, g.t) = any(path) +-- from graph g, search_graph sg +-- where g.f = sg.t and not cycle +--) +--select * from search_graph; -- ordering by the path column has same effect as SEARCH DEPTH FIRST -with recursive search_graph(f, t, label, path, cycle) as ( - select *, array[row(g.f, g.t)], false from graph g - union all - select g.*, path || row(g.f, g.t), row(g.f, g.t) = any(path) - from graph g, search_graph sg - where g.f = sg.t and not cycle -) -select * from search_graph order by path; +-- [SPARK-24497] Support recursive SQL query +--with recursive search_graph(f, t, label, path, cycle) as ( +-- select *, array[row(g.f, g.t)], false from graph g +-- union all +-- select g.*, path || row(g.f, g.t), row(g.f, g.t) = any(path) +-- from graph g, search_graph sg +-- where g.f = sg.t and not cycle +--) +--select * from search_graph order by path; -- -- test multiple WITH queries -- -WITH RECURSIVE - y (id) AS (VALUES (1)), - x (id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5) -SELECT * FROM x; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE +-- y (id) AS (VALUES (1)), +-- x (id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5) +--SELECT * FROM x; -- forward reference OK -WITH RECURSIVE - x(id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5), - y(id) AS (values (1)) - SELECT * FROM x; - -WITH RECURSIVE - x(id) AS - (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5), - y(id) AS - (VALUES (1) UNION ALL SELECT id+1 FROM y WHERE id < 10) - SELECT y.*, x.* FROM y LEFT JOIN x USING (id); - -WITH RECURSIVE - x(id) AS - (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5), - y(id) AS - (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 10) - SELECT y.*, x.* FROM y LEFT JOIN x USING (id); - -WITH RECURSIVE - x(id) AS - (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ), - y(id) AS - (SELECT * FROM x UNION ALL SELECT * FROM x), - z(id) AS - (SELECT * FROM x UNION ALL SELECT id+1 FROM z WHERE id < 10) - SELECT * FROM z; - -WITH RECURSIVE - x(id) AS - (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ), - y(id) AS - (SELECT * FROM x UNION ALL SELECT * FROM x), - z(id) AS - (SELECT * FROM y UNION ALL SELECT id+1 FROM z WHERE id < 10) - SELECT * FROM z; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE +-- x(id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5), +-- y(id) AS (values (1)) +-- SELECT * FROM x; + +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE +-- x(id) AS +-- (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5), +-- y(id) AS +-- (VALUES (1) UNION ALL SELECT id+1 FROM y WHERE id < 10) +-- SELECT y.*, x.* FROM y LEFT JOIN x USING (id); + +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE +-- x(id) AS +-- (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5), +-- y(id) AS +-- (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 10) +-- SELECT y.*, x.* FROM y LEFT JOIN x USING (id); + +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE +-- x(id) AS +-- (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ), +-- y(id) AS +-- (SELECT * FROM x UNION ALL SELECT * FROM x), +-- z(id) AS +-- (SELECT * FROM x UNION ALL SELECT id+1 FROM z WHERE id < 10) +-- SELECT * FROM z; + +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE +-- x(id) AS +-- (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ), +-- y(id) AS +-- (SELECT * FROM x UNION ALL SELECT * FROM x), +-- z(id) AS +-- (SELECT * FROM y UNION ALL SELECT id+1 FROM z WHERE id < 10) +-- SELECT * FROM z; -- -- Test WITH attached to a data-modifying statement -- -CREATE TEMPORARY TABLE y (a INTEGER); -INSERT INTO y SELECT generate_series(1, 10); - -WITH t AS ( - SELECT a FROM y -) -INSERT INTO y -SELECT a+20 FROM t RETURNING *; - -SELECT * FROM y; - -WITH t AS ( - SELECT a FROM y -) -UPDATE y SET a = y.a-10 FROM t WHERE y.a > 20 AND t.a = y.a RETURNING y.a; - -SELECT * FROM y; - -WITH RECURSIVE t(a) AS ( - SELECT 11 - UNION ALL - SELECT a+1 FROM t WHERE a < 50 -) -DELETE FROM y USING t WHERE t.a = y.a RETURNING y.a; +-- [ORIGINAL SQL] +--CREATE TEMPORARY TABLE y (a INTEGER) USING parquet; +CREATE TABLE y (a INTEGER) USING parquet; +-- [ORIGINAL SQL] +--INSERT INTO y SELECT generate_series(1, 10); +INSERT INTO y SELECT EXPLODE(SEQUENCE(1, 10)); + +-- [SPARK-28147] Support RETURNING clause +--WITH t AS ( +-- SELECT a FROM y +--) +--INSERT INTO y +--SELECT a+20 FROM t RETURNING *; +-- +--SELECT * FROM y; -SELECT * FROM y; +-- [NOTE] Spark SQL doesn't support UPDATE statement +----WITH t AS ( +---- SELECT a FROM y +----) +----UPDATE y SET a = y.a-10 FROM t WHERE y.a > 20 AND t.a = y.a RETURNING y.a; +-- +--SELECT * FROM y; + +-- [NOTE] Spark SQL doesn't support DELETE statement +--WITH RECURSIVE t(a) AS ( +-- SELECT 11 +-- UNION ALL +-- SELECT a+1 FROM t WHERE a < 50 +--) +--DELETE FROM y USING t WHERE t.a = y.a RETURNING y.a; +-- +--SELECT * FROM y; DROP TABLE y; @@ -418,203 +480,239 @@ DROP TABLE y; -- -- INTERSECT -WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT SELECT n+1 FROM x) - SELECT * FROM x; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT SELECT n+1 FROM x) +-- SELECT * FROM x; -WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT ALL SELECT n+1 FROM x) - SELECT * FROM x; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT ALL SELECT n+1 FROM x) +-- SELECT * FROM x; -- EXCEPT -WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT SELECT n+1 FROM x) - SELECT * FROM x; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT SELECT n+1 FROM x) +-- SELECT * FROM x; -WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT ALL SELECT n+1 FROM x) - SELECT * FROM x; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT ALL SELECT n+1 FROM x) +-- SELECT * FROM x; -- no non-recursive term -WITH RECURSIVE x(n) AS (SELECT n FROM x) - SELECT * FROM x; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE x(n) AS (SELECT n FROM x) +-- SELECT * FROM x; -- recursive term in the left hand side (strictly speaking, should allow this) -WITH RECURSIVE x(n) AS (SELECT n FROM x UNION ALL SELECT 1) - SELECT * FROM x; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE x(n) AS (SELECT n FROM x UNION ALL SELECT 1) +-- SELECT * FROM x; -CREATE TEMPORARY TABLE y (a INTEGER); -INSERT INTO y SELECT generate_series(1, 10); +-- [ORIGINAL SQL] +--CREATE TEMPORARY TABLE y (a INTEGER); +CREATE TABLE y (a INTEGER) USING parquet; +-- [ORIGINAL SQL] +--INSERT INTO y SELECT generate_series(1, 10); +INSERT INTO y SELECT EXPLODE(SEQUENCE(1, 10)); -- LEFT JOIN -WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 - UNION ALL - SELECT x.n+1 FROM y LEFT JOIN x ON x.n = y.a WHERE n < 10) -SELECT * FROM x; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 +-- UNION ALL +-- SELECT x.n+1 FROM y LEFT JOIN x ON x.n = y.a WHERE n < 10) +--SELECT * FROM x; -- RIGHT JOIN -WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 - UNION ALL - SELECT x.n+1 FROM x RIGHT JOIN y ON x.n = y.a WHERE n < 10) -SELECT * FROM x; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 +-- UNION ALL +-- SELECT x.n+1 FROM x RIGHT JOIN y ON x.n = y.a WHERE n < 10) +--SELECT * FROM x; -- FULL JOIN -WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 - UNION ALL - SELECT x.n+1 FROM x FULL JOIN y ON x.n = y.a WHERE n < 10) -SELECT * FROM x; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 +-- UNION ALL +-- SELECT x.n+1 FROM x FULL JOIN y ON x.n = y.a WHERE n < 10) +--SELECT * FROM x; -- subquery -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x - WHERE n IN (SELECT * FROM x)) - SELECT * FROM x; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x +-- WHERE n IN (SELECT * FROM x)) +-- SELECT * FROM x; -- aggregate functions -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT count(*) FROM x) - SELECT * FROM x; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT count(*) FROM x) +-- SELECT * FROM x; -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT sum(n) FROM x) - SELECT * FROM x; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT sum(n) FROM x) +-- SELECT * FROM x; -- ORDER BY -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x ORDER BY 1) - SELECT * FROM x; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x ORDER BY 1) +-- SELECT * FROM x; -- LIMIT/OFFSET -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x LIMIT 10 OFFSET 1) - SELECT * FROM x; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x LIMIT 10 OFFSET 1) +-- SELECT * FROM x; -- FOR UPDATE -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x FOR UPDATE) - SELECT * FROM x; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x FOR UPDATE) +-- SELECT * FROM x; -- target list has a recursive query name -WITH RECURSIVE x(id) AS (values (1) - UNION ALL - SELECT (SELECT * FROM x) FROM x WHERE id < 5 -) SELECT * FROM x; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE x(id) AS (values (1) +-- UNION ALL +-- SELECT (SELECT * FROM x) FROM x WHERE id < 5 +--) SELECT * FROM x; -- mutual recursive query (not implemented) -WITH RECURSIVE - x (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM y WHERE id < 5), - y (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 5) -SELECT * FROM x; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE +-- x (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM y WHERE id < 5), +-- y (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 5) +--SELECT * FROM x; -- non-linear recursion is not allowed -WITH RECURSIVE foo(i) AS - (values (1) - UNION ALL - (SELECT i+1 FROM foo WHERE i < 10 - UNION ALL - SELECT i+1 FROM foo WHERE i < 5) -) SELECT * FROM foo; - -WITH RECURSIVE foo(i) AS - (values (1) - UNION ALL - SELECT * FROM - (SELECT i+1 FROM foo WHERE i < 10 - UNION ALL - SELECT i+1 FROM foo WHERE i < 5) AS t -) SELECT * FROM foo; - -WITH RECURSIVE foo(i) AS - (values (1) - UNION ALL - (SELECT i+1 FROM foo WHERE i < 10 - EXCEPT - SELECT i+1 FROM foo WHERE i < 5) -) SELECT * FROM foo; - -WITH RECURSIVE foo(i) AS - (values (1) - UNION ALL - (SELECT i+1 FROM foo WHERE i < 10 - INTERSECT - SELECT i+1 FROM foo WHERE i < 5) -) SELECT * FROM foo; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE foo(i) AS +-- (values (1) +-- UNION ALL +-- (SELECT i+1 FROM foo WHERE i < 10 +-- UNION ALL +-- SELECT i+1 FROM foo WHERE i < 5) +--) SELECT * FROM foo; + +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE foo(i) AS +-- (values (1) +-- UNION ALL +-- SELECT * FROM +-- (SELECT i+1 FROM foo WHERE i < 10 +-- UNION ALL +-- SELECT i+1 FROM foo WHERE i < 5) AS t +--) SELECT * FROM foo; + +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE foo(i) AS +-- (values (1) +-- UNION ALL +-- (SELECT i+1 FROM foo WHERE i < 10 +-- EXCEPT +-- SELECT i+1 FROM foo WHERE i < 5) +--) SELECT * FROM foo; + +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE foo(i) AS +-- (values (1) +-- UNION ALL +-- (SELECT i+1 FROM foo WHERE i < 10 +-- INTERSECT +-- SELECT i+1 FROM foo WHERE i < 5) +--) SELECT * FROM foo; -- Wrong type induced from non-recursive term -WITH RECURSIVE foo(i) AS - (SELECT i FROM (VALUES(1),(2)) t(i) - UNION ALL - SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10) -SELECT * FROM foo; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE foo(i) AS +-- (SELECT i FROM (VALUES(1),(2)) t(i) +-- UNION ALL +-- SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10) +--SELECT * FROM foo; -- rejects different typmod, too (should we allow this?) -WITH RECURSIVE foo(i) AS - (SELECT i::numeric(3,0) FROM (VALUES(1),(2)) t(i) - UNION ALL - SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10) -SELECT * FROM foo; - +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE foo(i) AS +-- (SELECT i::numeric(3,0) FROM (VALUES(1),(2)) t(i) +-- UNION ALL +-- SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10) +--SELECT * FROM foo; + +-- [NOTE] Spark SQL doesn't support RULEs -- disallow OLD/NEW reference in CTE -CREATE TEMPORARY TABLE x (n integer); -CREATE RULE r2 AS ON UPDATE TO x DO INSTEAD - WITH t AS (SELECT OLD.*) UPDATE y SET a = t.n FROM t; +--CREATE TABLE x (n integer) USING parquet; +--CREATE RULE r2 AS ON UPDATE TO x DO INSTEAD +-- WITH t AS (SELECT OLD.*) UPDATE y SET a = t.n FROM t; -- -- test for bug #4902 -- -with cte(foo) as ( values(42) ) values((select foo from cte)); +-- [SPARK-19799] Support recursive SQL query +--with cte(foo) as ( values(42) ) values((select foo from cte)); with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q; -- test CTE referencing an outer-level variable (to see that changed-parameter -- signaling still works properly after fixing this bug) -select ( with cte(foo) as ( values(f1) ) - select (select foo from cte) ) -from int4_tbl; +-- [SPARK-19799] Support WITH clause in subqueries +--select ( with cte(foo) as ( values(f1) ) +-- select (select foo from cte) ) +--from int4_tbl; -select ( with cte(foo) as ( values(f1) ) - values((select foo from cte)) ) -from int4_tbl; +-- [SPARK-19799] Support WITH clause in subqueries +--select ( with cte(foo) as ( values(f1) ) +-- values((select foo from cte)) ) +--from int4_tbl; -- -- test for nested-recursive-WITH bug -- -WITH RECURSIVE t(j) AS ( - WITH RECURSIVE s(i) AS ( - VALUES (1) - UNION ALL - SELECT i+1 FROM s WHERE i < 10 - ) - SELECT i FROM s - UNION ALL - SELECT j+1 FROM t WHERE j < 10 -) -SELECT * FROM t; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE t(j) AS ( +-- WITH RECURSIVE s(i) AS ( +-- VALUES (1) +-- UNION ALL +-- SELECT i+1 FROM s WHERE i < 10 +-- ) +-- SELECT i FROM s +-- UNION ALL +-- SELECT j+1 FROM t WHERE j < 10 +--) +--SELECT * FROM t; -- -- test WITH attached to intermediate-level set operation -- -WITH outermost(x) AS ( - SELECT 1 - UNION (WITH innermost as (SELECT 2) - SELECT * FROM innermost - UNION SELECT 3) -) -SELECT * FROM outermost ORDER BY 1; - -WITH outermost(x) AS ( - SELECT 1 - UNION (WITH innermost as (SELECT 2) - SELECT * FROM outermost -- fail - UNION SELECT * FROM innermost) -) -SELECT * FROM outermost ORDER BY 1; - -WITH RECURSIVE outermost(x) AS ( - SELECT 1 - UNION (WITH innermost as (SELECT 2) - SELECT * FROM outermost - UNION SELECT * FROM innermost) -) -SELECT * FROM outermost ORDER BY 1; - -WITH RECURSIVE outermost(x) AS ( - WITH innermost as (SELECT 2 FROM outermost) -- fail - SELECT * FROM innermost - UNION SELECT * from outermost -) -SELECT * FROM outermost ORDER BY 1; +-- [SPARK-19799] Support recursive SQL query +--WITH outermost(x) AS ( +-- SELECT 1 +-- UNION (WITH innermost as (SELECT 2) +-- SELECT * FROM innermost +-- UNION SELECT 3) +--) +--SELECT * FROM outermost ORDER BY 1; + +-- [SPARK-19799] Support recursive SQL query +--WITH outermost(x) AS ( +-- SELECT 1 +-- UNION (WITH innermost as (SELECT 2) +-- SELECT * FROM outermost -- fail +-- UNION SELECT * FROM innermost) +--) +--SELECT * FROM outermost ORDER BY 1; + +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE outermost(x) AS ( +-- SELECT 1 +-- UNION (WITH innermost as (SELECT 2) +-- SELECT * FROM outermost +-- UNION SELECT * FROM innermost) +--) +--SELECT * FROM outermost ORDER BY 1; + +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE outermost(x) AS ( +-- WITH innermost as (SELECT 2 FROM outermost) -- fail +-- SELECT * FROM innermost +-- UNION SELECT * from outermost +--) +--SELECT * FROM outermost ORDER BY 1; -- -- This test will fail with the old implementation of PARAM_EXEC parameter @@ -624,212 +722,246 @@ SELECT * FROM outermost ORDER BY 1; -- lifespans of the two parameters overlap, thanks to B also reading A. -- -with -A as ( select q2 as id, (select q1) as x from int8_tbl ), -B as ( select id, row_number() over (partition by id) as r from A ), -C as ( select A.id, array(select B.id from B where B.id = A.id) from A ) -select * from C; +-- [NOTE] INT8_TBL is created in int8.sql originally +--CREATE TABLE INT8_TBL(q1 bigint, q2 bigint) USING parquet; +--INSERT INTO INT8_TBL VALUES(trim(' 123 '),trim(' 456')); +--INSERT INTO INT8_TBL VALUES(trim('123 '),'4567890123456789'); +--INSERT INTO INT8_TBL VALUES('4567890123456789','123'); +--INSERT INTO INT8_TBL VALUES(+4567890123456789,'4567890123456789'); +--INSERT INTO INT8_TBL VALUES('+4567890123456789','-4567890123456789'); + +-- [SPARK-27878] Support ARRAY(sub-SELECT) expressions +--with +--A as ( select q2 as id, (select q1) as x from int8_tbl ), +--B as ( select id, row_number() over (partition by id) as r from A ), +--C as ( select A.id, array(select B.id from B where B.id = A.id) from A ) +--select * from C; + +--DROP TABLE INT8_TBL; -- -- Test CTEs read in non-initialization orders -- -WITH RECURSIVE - tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)), - iter (id_key, row_type, link) AS ( - SELECT 0, 'base', 17 - UNION ALL ( - WITH remaining(id_key, row_type, link, min) AS ( - SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER () - FROM tab INNER JOIN iter USING (link) - WHERE tab.id_key > iter.id_key - ), - first_remaining AS ( - SELECT id_key, row_type, link - FROM remaining - WHERE id_key=min - ), - effect AS ( - SELECT tab.id_key, 'new'::text, tab.link - FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key - WHERE e.row_type = 'false' - ) - SELECT * FROM first_remaining - UNION ALL SELECT * FROM effect - ) - ) -SELECT * FROM iter; - -WITH RECURSIVE - tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)), - iter (id_key, row_type, link) AS ( - SELECT 0, 'base', 17 - UNION ( - WITH remaining(id_key, row_type, link, min) AS ( - SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER () - FROM tab INNER JOIN iter USING (link) - WHERE tab.id_key > iter.id_key - ), - first_remaining AS ( - SELECT id_key, row_type, link - FROM remaining - WHERE id_key=min - ), - effect AS ( - SELECT tab.id_key, 'new'::text, tab.link - FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key - WHERE e.row_type = 'false' - ) - SELECT * FROM first_remaining - UNION ALL SELECT * FROM effect - ) - ) -SELECT * FROM iter; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE +-- tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)), +-- iter (id_key, row_type, link) AS ( +-- SELECT 0, 'base', 17 +-- UNION ALL ( +-- WITH remaining(id_key, row_type, link, min) AS ( +-- SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER () +-- FROM tab INNER JOIN iter USING (link) +-- WHERE tab.id_key > iter.id_key +-- ), +-- first_remaining AS ( +-- SELECT id_key, row_type, link +-- FROM remaining +-- WHERE id_key=min +-- ), +-- effect AS ( +-- SELECT tab.id_key, 'new'::text, tab.link +-- FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key +-- WHERE e.row_type = 'false' +-- ) +-- SELECT * FROM first_remaining +-- UNION ALL SELECT * FROM effect +-- ) +-- ) +--SELECT * FROM iter; + +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE +-- tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)), +-- iter (id_key, row_type, link) AS ( +-- SELECT 0, 'base', 17 +-- UNION ( +-- WITH remaining(id_key, row_type, link, min) AS ( +-- SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER () +-- FROM tab INNER JOIN iter USING (link) +-- WHERE tab.id_key > iter.id_key +-- ), +-- first_remaining AS ( +-- SELECT id_key, row_type, link +-- FROM remaining +-- WHERE id_key=min +-- ), +-- effect AS ( +-- SELECT tab.id_key, 'new'::text, tab.link +-- FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key +-- WHERE e.row_type = 'false' +-- ) +-- SELECT * FROM first_remaining +-- UNION ALL SELECT * FROM effect +-- ) +-- ) +--SELECT * FROM iter; -- -- Data-modifying statements in WITH -- -- INSERT ... RETURNING -WITH t AS ( - INSERT INTO y - VALUES - (11), - (12), - (13), - (14), - (15), - (16), - (17), - (18), - (19), - (20) - RETURNING * -) -SELECT * FROM t; - -SELECT * FROM y; +-- [SPARK-28147] Support RETURNING clause +--WITH t AS ( +-- INSERT INTO y +-- VALUES +-- (11), +-- (12), +-- (13), +-- (14), +-- (15), +-- (16), +-- (17), +-- (18), +-- (19), +-- (20) +-- RETURNING * +--) +--SELECT * FROM t; +-- +--SELECT * FROM y; -- UPDATE ... RETURNING -WITH t AS ( - UPDATE y - SET a=a+1 - RETURNING * -) -SELECT * FROM t; - -SELECT * FROM y; +-- [NOTE] Spark SQL doesn't support UPDATE statement +--WITH t AS ( +-- UPDATE y +-- SET a=a+1 +-- RETURNING * +--) +--SELECT * FROM t; +-- +--SELECT * FROM y; -- DELETE ... RETURNING -WITH t AS ( - DELETE FROM y - WHERE a <= 10 - RETURNING * -) -SELECT * FROM t; - -SELECT * FROM y; +-- [NOTE] Spark SQL doesn't support DELETE statement +--WITH t AS ( +-- DELETE FROM y +-- WHERE a <= 10 +-- RETURNING * +--) +--SELECT * FROM t; +-- +--SELECT * FROM y; -- forward reference -WITH RECURSIVE t AS ( - INSERT INTO y - SELECT a+5 FROM t2 WHERE a > 5 - RETURNING * -), t2 AS ( - UPDATE y SET a=a-11 RETURNING * -) -SELECT * FROM t -UNION ALL -SELECT * FROM t2; - -SELECT * FROM y; +-- [NOTE] Spark SQL doesn't support UPDATE statement +--WITH RECURSIVE t AS ( +-- INSERT INTO y +-- SELECT a+5 FROM t2 WHERE a > 5 +-- RETURNING * +--), t2 AS ( +-- UPDATE y SET a=a-11 RETURNING * +--) +--SELECT * FROM t +--UNION ALL +--SELECT * FROM t2; +-- +--SELECT * FROM y; -- unconditional DO INSTEAD rule -CREATE RULE y_rule AS ON DELETE TO y DO INSTEAD - INSERT INTO y VALUES(42) RETURNING *; - -WITH t AS ( - DELETE FROM y RETURNING * -) -SELECT * FROM t; - -SELECT * FROM y; +-- [NOTE] Spark SQL doesn't support RULEs +--CREATE RULE y_rule AS ON DELETE TO y DO INSTEAD +-- INSERT INTO y VALUES(42) RETURNING *; + +-- [NOTE] Spark SQL doesn't support DELETE statement +--WITH t AS ( +-- DELETE FROM y RETURNING * +--) +--SELECT * FROM t; +-- +--SELECT * FROM y; -DROP RULE y_rule ON y; +--DROP RULE y_rule ON y; -- check merging of outer CTE with CTE in a rule action -CREATE TEMP TABLE bug6051 AS - select i from generate_series(1,3) as t(i); - -SELECT * FROM bug6051; - -WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) -INSERT INTO bug6051 SELECT * FROM t1; +-- [ORIGINAL SQL] +--CREATE TEMP TABLE bug6051 AS +-- select i from generate_series(1,3) as t(i); +CREATE TABLE bug6051 USING parquet AS + SELECT EXPLODE(SEQUENCE(1,3)) AS i; SELECT * FROM bug6051; -CREATE TEMP TABLE bug6051_2 (i int); +-- [NOTE] Spark SQL doesn't support DELETE statement +--WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) +--INSERT INTO bug6051 SELECT * FROM t1; +-- +--SELECT * FROM bug6051; -CREATE RULE bug6051_ins AS ON INSERT TO bug6051 DO INSTEAD - INSERT INTO bug6051_2 - SELECT NEW.i; +-- [ORIGINAL SQL] +--CREATE TEMP TABLE bug6051_2 (i int); +CREATE TABLE bug6051_2 (i int) USING parquet; -WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) -INSERT INTO bug6051 SELECT * FROM t1; +--CREATE RULE bug6051_ins AS ON INSERT TO bug6051 DO INSTEAD +-- INSERT INTO bug6051_2 +-- SELECT NEW.i; -SELECT * FROM bug6051; -SELECT * FROM bug6051_2; +-- [NOTE] Spark SQL doesn't support DELETE statement +--WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) +--INSERT INTO bug6051 SELECT * FROM t1; +-- +--SELECT * FROM bug6051; +--SELECT * FROM bug6051_2; -- a truly recursive CTE in the same list -WITH RECURSIVE t(a) AS ( - SELECT 0 - UNION ALL - SELECT a+1 FROM t WHERE a+1 < 5 -), t2 as ( - INSERT INTO y - SELECT * FROM t RETURNING * -) -SELECT * FROM t2 JOIN y USING (a) ORDER BY a; - -SELECT * FROM y; +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE t(a) AS ( +-- SELECT 0 +-- UNION ALL +-- SELECT a+1 FROM t WHERE a+1 < 5 +--), t2 as ( +-- INSERT INTO y +-- SELECT * FROM t RETURNING * +--) +--SELECT * FROM t2 JOIN y USING (a) ORDER BY a; +-- +--SELECT * FROM y; -- data-modifying WITH in a modifying statement -WITH t AS ( - DELETE FROM y - WHERE a <= 10 - RETURNING * -) -INSERT INTO y SELECT -a FROM t RETURNING *; - -SELECT * FROM y; +-- [NOTE] Spark SQL doesn't support DELETE statement +--WITH t AS ( +-- DELETE FROM y +-- WHERE a <= 10 +-- RETURNING * +--) +--INSERT INTO y SELECT -a FROM t RETURNING *; +-- +--SELECT * FROM y; -- check that WITH query is run to completion even if outer query isn't -WITH t AS ( - UPDATE y SET a = a * 100 RETURNING * -) -SELECT * FROM t LIMIT 10; - -SELECT * FROM y; +-- [NOTE] Spark SQL doesn't support UPDATE statement +--WITH t AS ( +-- UPDATE y SET a = a * 100 RETURNING * +--) +--SELECT * FROM t LIMIT 10; +-- +--SELECT * FROM y; -- data-modifying WITH containing INSERT...ON CONFLICT DO UPDATE -CREATE TABLE withz AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i; -ALTER TABLE withz ADD UNIQUE (k); - -WITH t AS ( - INSERT INTO withz SELECT i, 'insert' - FROM generate_series(0, 16) i - ON CONFLICT (k) DO UPDATE SET v = withz.v || ', now update' - RETURNING * -) -SELECT * FROM t JOIN y ON t.k = y.a ORDER BY a, k; +-- [ORIGINAL SQL] +--CREATE TABLE withz AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i; +CREATE TABLE withz USING parquet AS SELECT i AS k, CAST(i || ' v' AS string) v FROM (SELECT EXPLODE(SEQUENCE(1, 16, 3)) i); +-- [NOTE] Spark SQL doesn't support UNIQUE constraints +--ALTER TABLE withz ADD UNIQUE (k); + +-- [NOTE] Spark SQL doesn't support UPDATE statement +--WITH t AS ( +-- INSERT INTO withz SELECT i, 'insert' +-- FROM generate_series(0, 16) i +-- ON CONFLICT (k) DO UPDATE SET v = withz.v || ', now update' +-- RETURNING * +--) +--SELECT * FROM t JOIN y ON t.k = y.a ORDER BY a, k; -- Test EXCLUDED.* reference within CTE -WITH aa AS ( - INSERT INTO withz VALUES(1, 5) ON CONFLICT (k) DO UPDATE SET v = EXCLUDED.v - WHERE withz.k != EXCLUDED.k - RETURNING * -) -SELECT * FROM aa; +-- [NOTE] Spark SQL doesn't support ON CONFLICT clause +--WITH aa AS ( +-- INSERT INTO withz VALUES(1, 5) ON CONFLICT (k) DO UPDATE SET v = EXCLUDED.v +-- WHERE withz.k != EXCLUDED.k +-- RETURNING * +--) +--SELECT * FROM aa; -- New query/snapshot demonstrates side-effects of previous query. SELECT * FROM withz ORDER BY k; @@ -838,198 +970,223 @@ SELECT * FROM withz ORDER BY k; -- Ensure subqueries within the update clause work, even if they -- reference outside values -- -WITH aa AS (SELECT 1 a, 2 b) -INSERT INTO withz VALUES(1, 'insert') -ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); -WITH aa AS (SELECT 1 a, 2 b) -INSERT INTO withz VALUES(1, 'insert') -ON CONFLICT (k) DO UPDATE SET v = ' update' WHERE withz.k = (SELECT a FROM aa); -WITH aa AS (SELECT 1 a, 2 b) -INSERT INTO withz VALUES(1, 'insert') -ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); -WITH aa AS (SELECT 'a' a, 'b' b UNION ALL SELECT 'a' a, 'b' b) -INSERT INTO withz VALUES(1, 'insert') -ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 'a' LIMIT 1); -WITH aa AS (SELECT 1 a, 2 b) -INSERT INTO withz VALUES(1, (SELECT b || ' insert' FROM aa WHERE a = 1 )) -ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); +-- [NOTE] Spark SQL doesn't support ON CONFLICT clause +--WITH aa AS (SELECT 1 a, 2 b) +--INSERT INTO withz VALUES(1, 'insert') +--ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); +--WITH aa AS (SELECT 1 a, 2 b) +--INSERT INTO withz VALUES(1, 'insert') +--ON CONFLICT (k) DO UPDATE SET v = ' update' WHERE withz.k = (SELECT a FROM aa); +--WITH aa AS (SELECT 1 a, 2 b) +--INSERT INTO withz VALUES(1, 'insert') +--ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); +--WITH aa AS (SELECT 'a' a, 'b' b UNION ALL SELECT 'a' a, 'b' b) +--INSERT INTO withz VALUES(1, 'insert') +--ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 'a' LIMIT 1); +--WITH aa AS (SELECT 1 a, 2 b) +--INSERT INTO withz VALUES(1, (SELECT b || ' insert' FROM aa WHERE a = 1 )) +--ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); -- Update a row more than once, in different parts of a wCTE. That is -- an allowed, presumably very rare, edge case, but since it was -- broken in the past, having a test seems worthwhile. -WITH simpletup AS ( - SELECT 2 k, 'Green' v), -upsert_cte AS ( - INSERT INTO withz VALUES(2, 'Blue') ON CONFLICT (k) DO - UPDATE SET (k, v) = (SELECT k, v FROM simpletup WHERE simpletup.k = withz.k) - RETURNING k, v) -INSERT INTO withz VALUES(2, 'Red') ON CONFLICT (k) DO -UPDATE SET (k, v) = (SELECT k, v FROM upsert_cte WHERE upsert_cte.k = withz.k) -RETURNING k, v; +-- [NOTE] Spark SQL doesn't support ON CONFLICT clause +--WITH simpletup AS ( +-- SELECT 2 k, 'Green' v), +--upsert_cte AS ( +-- INSERT INTO withz VALUES(2, 'Blue') ON CONFLICT (k) DO +-- UPDATE SET (k, v) = (SELECT k, v FROM simpletup WHERE simpletup.k = withz.k) +-- RETURNING k, v) +--INSERT INTO withz VALUES(2, 'Red') ON CONFLICT (k) DO +--UPDATE SET (k, v) = (SELECT k, v FROM upsert_cte WHERE upsert_cte.k = withz.k) +--RETURNING k, v; DROP TABLE withz; -- check that run to completion happens in proper ordering TRUNCATE TABLE y; -INSERT INTO y SELECT generate_series(1, 3); -CREATE TEMPORARY TABLE yy (a INTEGER); - -WITH RECURSIVE t1 AS ( - INSERT INTO y SELECT * FROM y RETURNING * -), t2 AS ( - INSERT INTO yy SELECT * FROM t1 RETURNING * -) -SELECT 1; +-- [ORIGINAL SQL] +--INSERT INTO y SELECT generate_series(1, 3); +INSERT INTO y SELECT EXPLODE(SEQUENCE(1, 3)); +-- [ORIGINAL SQL] +--CREATE TEMPORARY TABLE yy (a INTEGER); +CREATE TABLE yy (a INTEGER) USING parquet; + +-- [SPARK-24497] Support recursive SQL query +-- [SPARK-28147] Support RETURNING clause +--WITH RECURSIVE t1 AS ( +-- INSERT INTO y SELECT * FROM y RETURNING * +--), t2 AS ( +-- INSERT INTO yy SELECT * FROM t1 RETURNING * +--) +--SELECT 1; SELECT * FROM y; SELECT * FROM yy; -WITH RECURSIVE t1 AS ( - INSERT INTO yy SELECT * FROM t2 RETURNING * -), t2 AS ( - INSERT INTO y SELECT * FROM y RETURNING * -) -SELECT 1; +-- [SPARK-24497] Support recursive SQL query +-- [SPARK-28147] Support RETURNING clause +--WITH RECURSIVE t1 AS ( +-- INSERT INTO yy SELECT * FROM t2 RETURNING * +--), t2 AS ( +-- INSERT INTO y SELECT * FROM y RETURNING * +--) +--SELECT 1; SELECT * FROM y; SELECT * FROM yy; +-- [NOTE] Spark SQL doesn't support TRIGGERs -- triggers - -TRUNCATE TABLE y; -INSERT INTO y SELECT generate_series(1, 10); - -CREATE FUNCTION y_trigger() RETURNS trigger AS $$ -begin - raise notice 'y_trigger: a = %', new.a; - return new; -end; -$$ LANGUAGE plpgsql; - -CREATE TRIGGER y_trig BEFORE INSERT ON y FOR EACH ROW - EXECUTE PROCEDURE y_trigger(); - -WITH t AS ( - INSERT INTO y - VALUES - (21), - (22), - (23) - RETURNING * -) -SELECT * FROM t; - -SELECT * FROM y; - -DROP TRIGGER y_trig ON y; - -CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH ROW - EXECUTE PROCEDURE y_trigger(); - -WITH t AS ( - INSERT INTO y - VALUES - (31), - (32), - (33) - RETURNING * -) -SELECT * FROM t LIMIT 1; - -SELECT * FROM y; - -DROP TRIGGER y_trig ON y; - -CREATE OR REPLACE FUNCTION y_trigger() RETURNS trigger AS $$ -begin - raise notice 'y_trigger'; - return null; -end; -$$ LANGUAGE plpgsql; - -CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH STATEMENT - EXECUTE PROCEDURE y_trigger(); - -WITH t AS ( - INSERT INTO y - VALUES - (41), - (42), - (43) - RETURNING * -) -SELECT * FROM t; - -SELECT * FROM y; - -DROP TRIGGER y_trig ON y; -DROP FUNCTION y_trigger(); +-- +--TRUNCATE TABLE y; +--INSERT INTO y SELECT generate_series(1, 10); +-- +--CREATE FUNCTION y_trigger() RETURNS trigger AS $$ +--begin +-- raise notice 'y_trigger: a = %', new.a; +-- return new; +--end; +--$$ LANGUAGE plpgsql; +-- +-- +--CREATE TRIGGER y_trig BEFORE INSERT ON y FOR EACH ROW +-- EXECUTE PROCEDURE y_trigger(); +-- +--WITH t AS ( +-- INSERT INTO y +-- VALUES +-- (21), +-- (22), +-- (23) +-- RETURNING * +--) +--SELECT * FROM t; +-- +--SELECT * FROM y; +-- +--DROP TRIGGER y_trig ON y; +-- +--CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH ROW +-- EXECUTE PROCEDURE y_trigger(); +-- +--WITH t AS ( +-- INSERT INTO y +-- VALUES +-- (31), +-- (32), +-- (33) +-- RETURNING * +--) +--SELECT * FROM t LIMIT 1; +-- +--SELECT * FROM y; +-- +--DROP TRIGGER y_trig ON y; +-- +--CREATE OR REPLACE FUNCTION y_trigger() RETURNS trigger AS $$ +--begin +-- raise notice 'y_trigger'; +-- return null; +--end; +--$$ LANGUAGE plpgsql; +-- +--CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH STATEMENT +-- EXECUTE PROCEDURE y_trigger(); +-- +--WITH t AS ( +-- INSERT INTO y +-- VALUES +-- (41), +-- (42), +-- (43) +-- RETURNING * +--) +--SELECT * FROM t; +-- +--SELECT * FROM y; +-- +--DROP TRIGGER y_trig ON y; +--DROP FUNCTION y_trigger(); -- WITH attached to inherited UPDATE or DELETE -CREATE TEMP TABLE parent ( id int, val text ); -CREATE TEMP TABLE child1 ( ) INHERITS ( parent ); -CREATE TEMP TABLE child2 ( ) INHERITS ( parent ); +-- [ORIGINAL SQL] +--CREATE TEMP TABLE parent ( id int, val text ); +CREATE TABLE parent ( id int, val string ) USING parquet; +-- [NOTE] Spark SQL doesn't support INHERITS clause +--CREATE TEMP TABLE child1 ( ) INHERITS ( parent ); +-- [NOTE] Spark SQL doesn't support INHERITS clause +--CREATE TEMP TABLE child2 ( ) INHERITS ( parent ); INSERT INTO parent VALUES ( 1, 'p1' ); -INSERT INTO child1 VALUES ( 11, 'c11' ),( 12, 'c12' ); -INSERT INTO child2 VALUES ( 23, 'c21' ),( 24, 'c22' ); +--INSERT INTO child1 VALUES ( 11, 'c11' ),( 12, 'c12' ); +--INSERT INTO child2 VALUES ( 23, 'c21' ),( 24, 'c22' ); -WITH rcte AS ( SELECT sum(id) AS totalid FROM parent ) -UPDATE parent SET id = id + totalid FROM rcte; +-- [NOTE] Spark SQL doesn't support UPDATE statement +--WITH rcte AS ( SELECT sum(id) AS totalid FROM parent ) +--UPDATE parent SET id = id + totalid FROM rcte; SELECT * FROM parent; -WITH wcte AS ( INSERT INTO child1 VALUES ( 42, 'new' ) RETURNING id AS newid ) -UPDATE parent SET id = id + newid FROM wcte; - -SELECT * FROM parent; +-- [SPARK-28147] Support RETURNING clause +--WITH wcte AS ( INSERT INTO child1 VALUES ( 42, 'new' ) RETURNING id AS newid ) +--UPDATE parent SET id = id + newid FROM wcte; +-- +--SELECT * FROM parent; -WITH rcte AS ( SELECT max(id) AS maxid FROM parent ) -DELETE FROM parent USING rcte WHERE id = maxid; +-- [NOTE] Spark SQL doesn't support DELETE statement +--WITH rcte AS ( SELECT max(id) AS maxid FROM parent ) +--DELETE FROM parent USING rcte WHERE id = maxid; SELECT * FROM parent; -WITH wcte AS ( INSERT INTO child2 VALUES ( 42, 'new2' ) RETURNING id AS newid ) -DELETE FROM parent USING wcte WHERE id = newid; - -SELECT * FROM parent; +-- [NOTE] Spark SQL doesn't support DELETE statement +--WITH wcte AS ( INSERT INTO child2 VALUES ( 42, 'new2' ) RETURNING id AS newid ) +--DELETE FROM parent USING wcte WHERE id = newid; +-- +--SELECT * FROM parent; -- check EXPLAIN VERBOSE for a wCTE with RETURNING -EXPLAIN (VERBOSE, COSTS OFF) -WITH wcte AS ( INSERT INTO int8_tbl VALUES ( 42, 47 ) RETURNING q2 ) -DELETE FROM a USING wcte WHERE aa = q2; +-- [NOTE] Spark SQL doesn't support DELETE statement +--EXPLAIN (VERBOSE, COSTS OFF) +--WITH wcte AS ( INSERT INTO int8_tbl VALUES ( 42, 47 ) RETURNING q2 ) +--DELETE FROM a USING wcte WHERE aa = q2; -- error cases -- data-modifying WITH tries to use its own output -WITH RECURSIVE t AS ( - INSERT INTO y - SELECT * FROM t -) -VALUES(FALSE); +-- [SPARK-24497] Support recursive SQL query +--WITH RECURSIVE t AS ( +-- INSERT INTO y +-- SELECT * FROM t +--) +--VALUES(FALSE); -- no RETURNING in a referenced data-modifying WITH -WITH t AS ( - INSERT INTO y VALUES(0) -) -SELECT * FROM t; +-- [SPARK-24497] Support recursive SQL query +--WITH t AS ( +-- INSERT INTO y VALUES(0) +--) +--SELECT * FROM t; -- data-modifying WITH allowed only at the top level -SELECT * FROM ( - WITH t AS (UPDATE y SET a=a+1 RETURNING *) - SELECT * FROM t -) ss; +-- [SPARK-28147] Support RETURNING clause +--SELECT * FROM ( +-- WITH t AS (UPDATE y SET a=a+1 RETURNING *) +-- SELECT * FROM t +--) ss; -- most variants of rules aren't allowed -CREATE RULE y_rule AS ON INSERT TO y WHERE a=0 DO INSTEAD DELETE FROM y; -WITH t AS ( - INSERT INTO y VALUES(0) -) -VALUES(FALSE); -DROP RULE y_rule ON y; +-- [NOTE] Spark SQL doesn't support RULEs +--CREATE RULE y_rule AS ON INSERT TO y WHERE a=0 DO INSTEAD DELETE FROM y; +--WITH t AS ( +-- INSERT INTO y VALUES(0) +--) +--VALUES(FALSE); +--DROP RULE y_rule ON y; -- check that parser lookahead for WITH doesn't cause any odd behavior create table foo (with baz); -- fail, WITH is a reserved word @@ -1042,7 +1199,18 @@ WITH test AS (SELECT 42) INSERT INTO test VALUES (1); -- check response to attempt to modify table with same name as a CTE (perhaps -- surprisingly it works, because CTEs don't hide tables from data-modifying -- statements) -create temp table test (i int); +-- [ORIGINAL SQL] +--create temp table test (i int); +create table test (i int) USING parquet; with test as (select 42) insert into test select * from test; select * from test; drop table test; + +DROP TABLE department; +DROP TABLE tree; +DROP TABLE graph; +DROP TABLE y; +DROP TABLE bug6051; +DROP TABLE bug6051_2; +DROP TABLE yy; +DROP TABLE parent; diff --git a/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out b/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out index 2a2085556bb2..d9cd652294f6 100644 --- a/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out +++ b/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out @@ -1,2291 +1,470 @@ --- --- Tests for common table expressions (WITH query, ... SELECT ...) --- --- Basic WITH +-- Automatically generated by SQLQueryTestSuite +-- Number of queries: 53 + + +-- !query 0 WITH q1(x,y) AS (SELECT 1,2) -SELECT * FROM q1, q1 AS q2; - x | y | x | y ----+---+---+--- - 1 | 2 | 1 | 2 -(1 row) - --- Multiple uses are evaluated only once -SELECT count(*) FROM ( - WITH q1(x) AS (SELECT random() FROM generate_series(1, 5)) - SELECT * FROM q1 - UNION - SELECT * FROM q1 -) ss; - count -------- - 5 -(1 row) - --- WITH RECURSIVE --- sum of 1..100 -WITH RECURSIVE t(n) AS ( - VALUES (1) -UNION ALL - SELECT n+1 FROM t WHERE n < 100 -) -SELECT sum(n) FROM t; - sum ------- - 5050 -(1 row) - -WITH RECURSIVE t(n) AS ( - SELECT (VALUES(1)) -UNION ALL - SELECT n+1 FROM t WHERE n < 5 -) -SELECT * FROM t; - n ---- - 1 - 2 - 3 - 4 - 5 -(5 rows) - --- recursive view -CREATE RECURSIVE VIEW nums (n) AS - VALUES (1) -UNION ALL - SELECT n+1 FROM nums WHERE n < 5; -SELECT * FROM nums; - n ---- - 1 - 2 - 3 - 4 - 5 -(5 rows) - -CREATE OR REPLACE RECURSIVE VIEW nums (n) AS - VALUES (1) -UNION ALL - SELECT n+1 FROM nums WHERE n < 6; -SELECT * FROM nums; - n ---- - 1 - 2 - 3 - 4 - 5 - 6 -(6 rows) - --- This is an infinite loop with UNION ALL, but not with UNION -WITH RECURSIVE t(n) AS ( - SELECT 1 -UNION - SELECT 10-n FROM t) -SELECT * FROM t; - n ---- - 1 - 9 -(2 rows) - --- This'd be an infinite loop, but outside query reads only as much as needed -WITH RECURSIVE t(n) AS ( - VALUES (1) -UNION ALL - SELECT n+1 FROM t) -SELECT * FROM t LIMIT 10; - n ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 -(10 rows) - --- UNION case should have same property -WITH RECURSIVE t(n) AS ( - SELECT 1 -UNION - SELECT n+1 FROM t) -SELECT * FROM t LIMIT 10; - n ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 -(10 rows) - --- Test behavior with an unknown-type literal in the WITH -WITH q AS (SELECT 'foo' AS x) -SELECT x, x IS OF (text) AS is_text FROM q; - x | is_text ------+--------- - foo | t -(1 row) - -WITH RECURSIVE t(n) AS ( - SELECT 'foo' -UNION ALL - SELECT n || ' bar' FROM t WHERE length(n) < 20 -) -SELECT n, n IS OF (text) AS is_text FROM t; - n | is_text --------------------------+--------- - foo | t - foo bar | t - foo bar bar | t - foo bar bar bar | t - foo bar bar bar bar | t - foo bar bar bar bar bar | t -(6 rows) - --- In a perfect world, this would work and resolve the literal as int ... --- but for now, we have to be content with resolving to text too soon. -WITH RECURSIVE t(n) AS ( - SELECT '7' -UNION ALL - SELECT n+1 FROM t WHERE n < 10 -) -SELECT n, n IS OF (int) AS is_int FROM t; -ERROR: operator does not exist: text + integer -LINE 4: SELECT n+1 FROM t WHERE n < 10 - ^ -HINT: No operator matches the given name and argument types. You might need to add explicit type casts. --- --- Some examples with a tree --- --- department structure represented here is as follows: --- --- ROOT-+->A-+->B-+->C --- | | --- | +->D-+->F --- +->E-+->G -CREATE TEMP TABLE department ( - id INTEGER PRIMARY KEY, -- department ID - parent_department INTEGER REFERENCES department, -- upper department ID - name TEXT -- department name -); -INSERT INTO department VALUES (0, NULL, 'ROOT'); -INSERT INTO department VALUES (1, 0, 'A'); -INSERT INTO department VALUES (2, 1, 'B'); -INSERT INTO department VALUES (3, 2, 'C'); -INSERT INTO department VALUES (4, 2, 'D'); -INSERT INTO department VALUES (5, 0, 'E'); -INSERT INTO department VALUES (6, 4, 'F'); -INSERT INTO department VALUES (7, 5, 'G'); --- extract all departments under 'A'. Result should be A, B, C, D and F -WITH RECURSIVE subdepartment AS -( - -- non recursive term - SELECT name as root_name, * FROM department WHERE name = 'A' - UNION ALL - -- recursive term - SELECT sd.root_name, d.* FROM department AS d, subdepartment AS sd - WHERE d.parent_department = sd.id -) -SELECT * FROM subdepartment ORDER BY name; - root_name | id | parent_department | name ------------+----+-------------------+------ - A | 1 | 0 | A - A | 2 | 1 | B - A | 3 | 2 | C - A | 4 | 2 | D - A | 6 | 4 | F -(5 rows) - --- extract all departments under 'A' with "level" number -WITH RECURSIVE subdepartment(level, id, parent_department, name) AS -( - -- non recursive term - SELECT 1, * FROM department WHERE name = 'A' - UNION ALL - -- recursive term - SELECT sd.level + 1, d.* FROM department AS d, subdepartment AS sd - WHERE d.parent_department = sd.id -) -SELECT * FROM subdepartment ORDER BY name; - level | id | parent_department | name --------+----+-------------------+------ - 1 | 1 | 0 | A - 2 | 2 | 1 | B - 3 | 3 | 2 | C - 3 | 4 | 2 | D - 4 | 6 | 4 | F -(5 rows) - --- extract all departments under 'A' with "level" number. --- Only shows level 2 or more -WITH RECURSIVE subdepartment(level, id, parent_department, name) AS -( - -- non recursive term - SELECT 1, * FROM department WHERE name = 'A' - UNION ALL - -- recursive term - SELECT sd.level + 1, d.* FROM department AS d, subdepartment AS sd - WHERE d.parent_department = sd.id -) -SELECT * FROM subdepartment WHERE level >= 2 ORDER BY name; - level | id | parent_department | name --------+----+-------------------+------ - 2 | 2 | 1 | B - 3 | 3 | 2 | C - 3 | 4 | 2 | D - 4 | 6 | 4 | F -(4 rows) - --- "RECURSIVE" is ignored if the query has no self-reference -WITH RECURSIVE subdepartment AS -( - -- note lack of recursive UNION structure - SELECT * FROM department WHERE name = 'A' -) -SELECT * FROM subdepartment ORDER BY name; - id | parent_department | name -----+-------------------+------ - 1 | 0 | A -(1 row) - --- inside subqueries -SELECT count(*) FROM ( - WITH RECURSIVE t(n) AS ( - SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 500 - ) - SELECT * FROM t) AS t WHERE n < ( - SELECT count(*) FROM ( - WITH RECURSIVE t(n) AS ( - SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 100 - ) - SELECT * FROM t WHERE n < 50000 - ) AS t WHERE n < 100); - count -------- - 98 -(1 row) - --- use same CTE twice at different subquery levels -WITH q1(x,y) AS ( - SELECT hundred, sum(ten) FROM tenk1 GROUP BY hundred - ) -SELECT count(*) FROM q1 WHERE y > (SELECT sum(y)/100 FROM q1 qsub); - count -------- - 50 -(1 row) - --- via a VIEW -CREATE TEMPORARY VIEW vsubdepartment AS - WITH RECURSIVE subdepartment AS - ( - -- non recursive term - SELECT * FROM department WHERE name = 'A' - UNION ALL - -- recursive term - SELECT d.* FROM department AS d, subdepartment AS sd - WHERE d.parent_department = sd.id - ) - SELECT * FROM subdepartment; -SELECT * FROM vsubdepartment ORDER BY name; - id | parent_department | name -----+-------------------+------ - 1 | 0 | A - 2 | 1 | B - 3 | 2 | C - 4 | 2 | D - 6 | 4 | F -(5 rows) - --- Check reverse listing -SELECT pg_get_viewdef('vsubdepartment'::regclass); - pg_get_viewdef ------------------------------------------------ - WITH RECURSIVE subdepartment AS ( + - SELECT department.id, + - department.parent_department, + - department.name + - FROM department + - WHERE (department.name = 'A'::text)+ - UNION ALL + - SELECT d.id, + - d.parent_department, + - d.name + - FROM department d, + - subdepartment sd + - WHERE (d.parent_department = sd.id)+ - ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + - FROM subdepartment; -(1 row) - -SELECT pg_get_viewdef('vsubdepartment'::regclass, true); - pg_get_viewdef ---------------------------------------------- - WITH RECURSIVE subdepartment AS ( + - SELECT department.id, + - department.parent_department, + - department.name + - FROM department + - WHERE department.name = 'A'::text+ - UNION ALL + - SELECT d.id, + - d.parent_department, + - d.name + - FROM department d, + - subdepartment sd + - WHERE d.parent_department = sd.id+ - ) + - SELECT subdepartment.id, + - subdepartment.parent_department, + - subdepartment.name + - FROM subdepartment; -(1 row) - --- Another reverse-listing example -CREATE VIEW sums_1_100 AS -WITH RECURSIVE t(n) AS ( - VALUES (1) -UNION ALL - SELECT n+1 FROM t WHERE n < 100 -) -SELECT sum(n) FROM t; -\d+ sums_1_100 - View "public.sums_1_100" - Column | Type | Collation | Nullable | Default | Storage | Description ---------+--------+-----------+----------+---------+---------+------------- - sum | bigint | | | | plain | -View definition: - WITH RECURSIVE t(n) AS ( - VALUES (1) - UNION ALL - SELECT t_1.n + 1 - FROM t t_1 - WHERE t_1.n < 100 - ) - SELECT sum(t.n) AS sum - FROM t; - --- corner case in which sub-WITH gets initialized first -with recursive q as ( - select * from department - union all - (with x as (select * from q) - select * from x) - ) -select * from q limit 24; - id | parent_department | name -----+-------------------+------ - 0 | | ROOT - 1 | 0 | A - 2 | 1 | B - 3 | 2 | C - 4 | 2 | D - 5 | 0 | E - 6 | 4 | F - 7 | 5 | G - 0 | | ROOT - 1 | 0 | A - 2 | 1 | B - 3 | 2 | C - 4 | 2 | D - 5 | 0 | E - 6 | 4 | F - 7 | 5 | G - 0 | | ROOT - 1 | 0 | A - 2 | 1 | B - 3 | 2 | C - 4 | 2 | D - 5 | 0 | E - 6 | 4 | F - 7 | 5 | G -(24 rows) - -with recursive q as ( - select * from department - union all - (with recursive x as ( - select * from department - union all - (select * from q union all select * from x) - ) - select * from x) - ) -select * from q limit 32; - id | parent_department | name -----+-------------------+------ - 0 | | ROOT - 1 | 0 | A - 2 | 1 | B - 3 | 2 | C - 4 | 2 | D - 5 | 0 | E - 6 | 4 | F - 7 | 5 | G - 0 | | ROOT - 1 | 0 | A - 2 | 1 | B - 3 | 2 | C - 4 | 2 | D - 5 | 0 | E - 6 | 4 | F - 7 | 5 | G - 0 | | ROOT - 1 | 0 | A - 2 | 1 | B - 3 | 2 | C - 4 | 2 | D - 5 | 0 | E - 6 | 4 | F - 7 | 5 | G - 0 | | ROOT - 1 | 0 | A - 2 | 1 | B - 3 | 2 | C - 4 | 2 | D - 5 | 0 | E - 6 | 4 | F - 7 | 5 | G -(32 rows) - --- recursive term has sub-UNION -WITH RECURSIVE t(i,j) AS ( - VALUES (1,2) - UNION ALL - SELECT t2.i, t.j+1 FROM - (SELECT 2 AS i UNION ALL SELECT 3 AS i) AS t2 - JOIN t ON (t2.i = t.i+1)) - SELECT * FROM t; - i | j ----+--- - 1 | 2 - 2 | 3 - 3 | 4 -(3 rows) - --- --- different tree example --- -CREATE TEMPORARY TABLE tree( - id INTEGER PRIMARY KEY, - parent_id INTEGER REFERENCES tree(id) -); +SELECT * FROM q1 CROSS JOIN q1 AS q2 +-- !query 0 schema +struct +-- !query 0 output +1 2 1 2 + + +-- !query 1 +CREATE TABLE department ( + id INTEGER, -- department ID + parent_department INTEGER, -- upper department ID + name string -- department name +) USING parquet +-- !query 1 schema +struct<> +-- !query 1 output + + + +-- !query 2 +INSERT INTO department VALUES (0, NULL, 'ROOT') +-- !query 2 schema +struct<> +-- !query 2 output + + + +-- !query 3 +INSERT INTO department VALUES (1, 0, 'A') +-- !query 3 schema +struct<> +-- !query 3 output + + + +-- !query 4 +INSERT INTO department VALUES (2, 1, 'B') +-- !query 4 schema +struct<> +-- !query 4 output + + + +-- !query 5 +INSERT INTO department VALUES (3, 2, 'C') +-- !query 5 schema +struct<> +-- !query 5 output + + + +-- !query 6 +INSERT INTO department VALUES (4, 2, 'D') +-- !query 6 schema +struct<> +-- !query 6 output + + + +-- !query 7 +INSERT INTO department VALUES (5, 0, 'E') +-- !query 7 schema +struct<> +-- !query 7 output + + + +-- !query 8 +INSERT INTO department VALUES (6, 4, 'F') +-- !query 8 schema +struct<> +-- !query 8 output + + + +-- !query 9 +INSERT INTO department VALUES (7, 5, 'G') +-- !query 9 schema +struct<> +-- !query 9 output + + + +-- !query 10 +CREATE TABLE tree( + id INTEGER, + parent_id INTEGER +) USING parquet +-- !query 10 schema +struct<> +-- !query 10 output + + + +-- !query 11 INSERT INTO tree VALUES (1, NULL), (2, 1), (3,1), (4,2), (5,2), (6,2), (7,3), (8,3), - (9,4), (10,4), (11,7), (12,7), (13,7), (14, 9), (15,11), (16,11); --- --- get all paths from "second level" nodes to leaf nodes --- -WITH RECURSIVE t(id, path) AS ( - VALUES(1,ARRAY[]::integer[]) -UNION ALL - SELECT tree.id, t.path || tree.id - FROM tree JOIN t ON (tree.parent_id = t.id) -) -SELECT t1.*, t2.* FROM t AS t1 JOIN t AS t2 ON - (t1.path[1] = t2.path[1] AND - array_upper(t1.path,1) = 1 AND - array_upper(t2.path,1) > 1) - ORDER BY t1.id, t2.id; - id | path | id | path -----+------+----+------------- - 2 | {2} | 4 | {2,4} - 2 | {2} | 5 | {2,5} - 2 | {2} | 6 | {2,6} - 2 | {2} | 9 | {2,4,9} - 2 | {2} | 10 | {2,4,10} - 2 | {2} | 14 | {2,4,9,14} - 3 | {3} | 7 | {3,7} - 3 | {3} | 8 | {3,8} - 3 | {3} | 11 | {3,7,11} - 3 | {3} | 12 | {3,7,12} - 3 | {3} | 13 | {3,7,13} - 3 | {3} | 15 | {3,7,11,15} - 3 | {3} | 16 | {3,7,11,16} -(13 rows) - --- just count 'em -WITH RECURSIVE t(id, path) AS ( - VALUES(1,ARRAY[]::integer[]) -UNION ALL - SELECT tree.id, t.path || tree.id - FROM tree JOIN t ON (tree.parent_id = t.id) -) -SELECT t1.id, count(t2.*) FROM t AS t1 JOIN t AS t2 ON - (t1.path[1] = t2.path[1] AND - array_upper(t1.path,1) = 1 AND - array_upper(t2.path,1) > 1) - GROUP BY t1.id - ORDER BY t1.id; - id | count -----+------- - 2 | 6 - 3 | 7 -(2 rows) - --- this variant tickled a whole-row-variable bug in 8.4devel -WITH RECURSIVE t(id, path) AS ( - VALUES(1,ARRAY[]::integer[]) -UNION ALL - SELECT tree.id, t.path || tree.id - FROM tree JOIN t ON (tree.parent_id = t.id) -) -SELECT t1.id, t2.path, t2 FROM t AS t1 JOIN t AS t2 ON -(t1.id=t2.id); - id | path | t2 -----+-------------+-------------------- - 1 | {} | (1,{}) - 2 | {2} | (2,{2}) - 3 | {3} | (3,{3}) - 4 | {2,4} | (4,"{2,4}") - 5 | {2,5} | (5,"{2,5}") - 6 | {2,6} | (6,"{2,6}") - 7 | {3,7} | (7,"{3,7}") - 8 | {3,8} | (8,"{3,8}") - 9 | {2,4,9} | (9,"{2,4,9}") - 10 | {2,4,10} | (10,"{2,4,10}") - 11 | {3,7,11} | (11,"{3,7,11}") - 12 | {3,7,12} | (12,"{3,7,12}") - 13 | {3,7,13} | (13,"{3,7,13}") - 14 | {2,4,9,14} | (14,"{2,4,9,14}") - 15 | {3,7,11,15} | (15,"{3,7,11,15}") - 16 | {3,7,11,16} | (16,"{3,7,11,16}") -(16 rows) - --- --- test cycle detection --- -create temp table graph( f int, t int, label text ); + (9,4), (10,4), (11,7), (12,7), (13,7), (14, 9), (15,11), (16,11) +-- !query 11 schema +struct<> +-- !query 11 output + + + +-- !query 12 +create table graph( f int, t int, label string ) USING parquet +-- !query 12 schema +struct<> +-- !query 12 output + + + +-- !query 13 insert into graph values (1, 2, 'arc 1 -> 2'), (1, 3, 'arc 1 -> 3'), (2, 3, 'arc 2 -> 3'), (1, 4, 'arc 1 -> 4'), (4, 5, 'arc 4 -> 5'), - (5, 1, 'arc 5 -> 1'); -with recursive search_graph(f, t, label, path, cycle) as ( - select *, array[row(g.f, g.t)], false from graph g - union all - select g.*, path || row(g.f, g.t), row(g.f, g.t) = any(path) - from graph g, search_graph sg - where g.f = sg.t and not cycle -) -select * from search_graph; - f | t | label | path | cycle ----+---+------------+-------------------------------------------+------- - 1 | 2 | arc 1 -> 2 | {"(1,2)"} | f - 1 | 3 | arc 1 -> 3 | {"(1,3)"} | f - 2 | 3 | arc 2 -> 3 | {"(2,3)"} | f - 1 | 4 | arc 1 -> 4 | {"(1,4)"} | f - 4 | 5 | arc 4 -> 5 | {"(4,5)"} | f - 5 | 1 | arc 5 -> 1 | {"(5,1)"} | f - 1 | 2 | arc 1 -> 2 | {"(5,1)","(1,2)"} | f - 1 | 3 | arc 1 -> 3 | {"(5,1)","(1,3)"} | f - 1 | 4 | arc 1 -> 4 | {"(5,1)","(1,4)"} | f - 2 | 3 | arc 2 -> 3 | {"(1,2)","(2,3)"} | f - 4 | 5 | arc 4 -> 5 | {"(1,4)","(4,5)"} | f - 5 | 1 | arc 5 -> 1 | {"(4,5)","(5,1)"} | f - 1 | 2 | arc 1 -> 2 | {"(4,5)","(5,1)","(1,2)"} | f - 1 | 3 | arc 1 -> 3 | {"(4,5)","(5,1)","(1,3)"} | f - 1 | 4 | arc 1 -> 4 | {"(4,5)","(5,1)","(1,4)"} | f - 2 | 3 | arc 2 -> 3 | {"(5,1)","(1,2)","(2,3)"} | f - 4 | 5 | arc 4 -> 5 | {"(5,1)","(1,4)","(4,5)"} | f - 5 | 1 | arc 5 -> 1 | {"(1,4)","(4,5)","(5,1)"} | f - 1 | 2 | arc 1 -> 2 | {"(1,4)","(4,5)","(5,1)","(1,2)"} | f - 1 | 3 | arc 1 -> 3 | {"(1,4)","(4,5)","(5,1)","(1,3)"} | f - 1 | 4 | arc 1 -> 4 | {"(1,4)","(4,5)","(5,1)","(1,4)"} | t - 2 | 3 | arc 2 -> 3 | {"(4,5)","(5,1)","(1,2)","(2,3)"} | f - 4 | 5 | arc 4 -> 5 | {"(4,5)","(5,1)","(1,4)","(4,5)"} | t - 5 | 1 | arc 5 -> 1 | {"(5,1)","(1,4)","(4,5)","(5,1)"} | t - 2 | 3 | arc 2 -> 3 | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} | f -(25 rows) - --- ordering by the path column has same effect as SEARCH DEPTH FIRST -with recursive search_graph(f, t, label, path, cycle) as ( - select *, array[row(g.f, g.t)], false from graph g - union all - select g.*, path || row(g.f, g.t), row(g.f, g.t) = any(path) - from graph g, search_graph sg - where g.f = sg.t and not cycle -) -select * from search_graph order by path; - f | t | label | path | cycle ----+---+------------+-------------------------------------------+------- - 1 | 2 | arc 1 -> 2 | {"(1,2)"} | f - 2 | 3 | arc 2 -> 3 | {"(1,2)","(2,3)"} | f - 1 | 3 | arc 1 -> 3 | {"(1,3)"} | f - 1 | 4 | arc 1 -> 4 | {"(1,4)"} | f - 4 | 5 | arc 4 -> 5 | {"(1,4)","(4,5)"} | f - 5 | 1 | arc 5 -> 1 | {"(1,4)","(4,5)","(5,1)"} | f - 1 | 2 | arc 1 -> 2 | {"(1,4)","(4,5)","(5,1)","(1,2)"} | f - 2 | 3 | arc 2 -> 3 | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} | f - 1 | 3 | arc 1 -> 3 | {"(1,4)","(4,5)","(5,1)","(1,3)"} | f - 1 | 4 | arc 1 -> 4 | {"(1,4)","(4,5)","(5,1)","(1,4)"} | t - 2 | 3 | arc 2 -> 3 | {"(2,3)"} | f - 4 | 5 | arc 4 -> 5 | {"(4,5)"} | f - 5 | 1 | arc 5 -> 1 | {"(4,5)","(5,1)"} | f - 1 | 2 | arc 1 -> 2 | {"(4,5)","(5,1)","(1,2)"} | f - 2 | 3 | arc 2 -> 3 | {"(4,5)","(5,1)","(1,2)","(2,3)"} | f - 1 | 3 | arc 1 -> 3 | {"(4,5)","(5,1)","(1,3)"} | f - 1 | 4 | arc 1 -> 4 | {"(4,5)","(5,1)","(1,4)"} | f - 4 | 5 | arc 4 -> 5 | {"(4,5)","(5,1)","(1,4)","(4,5)"} | t - 5 | 1 | arc 5 -> 1 | {"(5,1)"} | f - 1 | 2 | arc 1 -> 2 | {"(5,1)","(1,2)"} | f - 2 | 3 | arc 2 -> 3 | {"(5,1)","(1,2)","(2,3)"} | f - 1 | 3 | arc 1 -> 3 | {"(5,1)","(1,3)"} | f - 1 | 4 | arc 1 -> 4 | {"(5,1)","(1,4)"} | f - 4 | 5 | arc 4 -> 5 | {"(5,1)","(1,4)","(4,5)"} | f - 5 | 1 | arc 5 -> 1 | {"(5,1)","(1,4)","(4,5)","(5,1)"} | t -(25 rows) - --- --- test multiple WITH queries --- -WITH RECURSIVE - y (id) AS (VALUES (1)), - x (id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5) -SELECT * FROM x; - id ----- - 1 - 2 - 3 - 4 - 5 -(5 rows) - --- forward reference OK -WITH RECURSIVE - x(id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5), - y(id) AS (values (1)) - SELECT * FROM x; - id ----- - 1 - 2 - 3 - 4 - 5 -(5 rows) - -WITH RECURSIVE - x(id) AS - (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5), - y(id) AS - (VALUES (1) UNION ALL SELECT id+1 FROM y WHERE id < 10) - SELECT y.*, x.* FROM y LEFT JOIN x USING (id); - id | id -----+---- - 1 | 1 - 2 | 2 - 3 | 3 - 4 | 4 - 5 | 5 - 6 | - 7 | - 8 | - 9 | - 10 | -(10 rows) - -WITH RECURSIVE - x(id) AS - (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5), - y(id) AS - (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 10) - SELECT y.*, x.* FROM y LEFT JOIN x USING (id); - id | id -----+---- - 1 | 1 - 2 | 2 - 3 | 3 - 4 | 4 - 5 | 5 - 6 | -(6 rows) - -WITH RECURSIVE - x(id) AS - (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ), - y(id) AS - (SELECT * FROM x UNION ALL SELECT * FROM x), - z(id) AS - (SELECT * FROM x UNION ALL SELECT id+1 FROM z WHERE id < 10) - SELECT * FROM z; - id ----- - 1 - 2 - 3 - 2 - 3 - 4 - 3 - 4 - 5 - 4 - 5 - 6 - 5 - 6 - 7 - 6 - 7 - 8 - 7 - 8 - 9 - 8 - 9 - 10 - 9 - 10 - 10 -(27 rows) - -WITH RECURSIVE - x(id) AS - (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ), - y(id) AS - (SELECT * FROM x UNION ALL SELECT * FROM x), - z(id) AS - (SELECT * FROM y UNION ALL SELECT id+1 FROM z WHERE id < 10) - SELECT * FROM z; - id ----- - 1 - 2 - 3 - 1 - 2 - 3 - 2 - 3 - 4 - 2 - 3 - 4 - 3 - 4 - 5 - 3 - 4 - 5 - 4 - 5 - 6 - 4 - 5 - 6 - 5 - 6 - 7 - 5 - 6 - 7 - 6 - 7 - 8 - 6 - 7 - 8 - 7 - 8 - 9 - 7 - 8 - 9 - 8 - 9 - 10 - 8 - 9 - 10 - 9 - 10 - 9 - 10 - 10 - 10 -(54 rows) - --- --- Test WITH attached to a data-modifying statement --- -CREATE TEMPORARY TABLE y (a INTEGER); -INSERT INTO y SELECT generate_series(1, 10); -WITH t AS ( - SELECT a FROM y -) -INSERT INTO y -SELECT a+20 FROM t RETURNING *; - a ----- - 21 - 22 - 23 - 24 - 25 - 26 - 27 - 28 - 29 - 30 -(10 rows) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 21 - 22 - 23 - 24 - 25 - 26 - 27 - 28 - 29 - 30 -(20 rows) - -WITH t AS ( - SELECT a FROM y -) -UPDATE y SET a = y.a-10 FROM t WHERE y.a > 20 AND t.a = y.a RETURNING y.a; - a ----- - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 -(10 rows) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 -(20 rows) - -WITH RECURSIVE t(a) AS ( - SELECT 11 - UNION ALL - SELECT a+1 FROM t WHERE a < 50 -) -DELETE FROM y USING t WHERE t.a = y.a RETURNING y.a; - a ----- - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 -(10 rows) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 -(10 rows) - -DROP TABLE y; --- --- error cases --- --- INTERSECT -WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT SELECT n+1 FROM x) - SELECT * FROM x; -ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term -LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT SELECT n+1 FROM x... - ^ -WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT ALL SELECT n+1 FROM x) - SELECT * FROM x; -ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term -LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT ALL SELECT n+1 FR... - ^ --- EXCEPT -WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT SELECT n+1 FROM x) - SELECT * FROM x; -ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term -LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT SELECT n+1 FROM x) - ^ -WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT ALL SELECT n+1 FROM x) - SELECT * FROM x; -ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term -LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT ALL SELECT n+1 FROM ... - ^ --- no non-recursive term -WITH RECURSIVE x(n) AS (SELECT n FROM x) - SELECT * FROM x; -ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term -LINE 1: WITH RECURSIVE x(n) AS (SELECT n FROM x) - ^ --- recursive term in the left hand side (strictly speaking, should allow this) -WITH RECURSIVE x(n) AS (SELECT n FROM x UNION ALL SELECT 1) - SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within its non-recursive term -LINE 1: WITH RECURSIVE x(n) AS (SELECT n FROM x UNION ALL SELECT 1) - ^ -CREATE TEMPORARY TABLE y (a INTEGER); -INSERT INTO y SELECT generate_series(1, 10); --- LEFT JOIN -WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 - UNION ALL - SELECT x.n+1 FROM y LEFT JOIN x ON x.n = y.a WHERE n < 10) -SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within an outer join -LINE 3: SELECT x.n+1 FROM y LEFT JOIN x ON x.n = y.a WHERE n < 10) - ^ --- RIGHT JOIN -WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 - UNION ALL - SELECT x.n+1 FROM x RIGHT JOIN y ON x.n = y.a WHERE n < 10) -SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within an outer join -LINE 3: SELECT x.n+1 FROM x RIGHT JOIN y ON x.n = y.a WHERE n < 10) - ^ --- FULL JOIN -WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 - UNION ALL - SELECT x.n+1 FROM x FULL JOIN y ON x.n = y.a WHERE n < 10) -SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within an outer join -LINE 3: SELECT x.n+1 FROM x FULL JOIN y ON x.n = y.a WHERE n < 10) - ^ --- subquery -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x - WHERE n IN (SELECT * FROM x)) - SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within a subquery -LINE 2: WHERE n IN (SELECT * FROM x)) - ^ --- aggregate functions -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT count(*) FROM x) - SELECT * FROM x; -ERROR: aggregate functions are not allowed in a recursive query's recursive term -LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT count(*) F... - ^ -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT sum(n) FROM x) - SELECT * FROM x; -ERROR: aggregate functions are not allowed in a recursive query's recursive term -LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT sum(n) FRO... - ^ --- ORDER BY -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x ORDER BY 1) - SELECT * FROM x; -ERROR: ORDER BY in a recursive query is not implemented -LINE 1: ...VE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x ORDER BY 1) - ^ --- LIMIT/OFFSET -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x LIMIT 10 OFFSET 1) - SELECT * FROM x; -ERROR: OFFSET in a recursive query is not implemented -LINE 1: ... AS (SELECT 1 UNION ALL SELECT n+1 FROM x LIMIT 10 OFFSET 1) - ^ --- FOR UPDATE -WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x FOR UPDATE) - SELECT * FROM x; -ERROR: FOR UPDATE/SHARE in a recursive query is not implemented --- target list has a recursive query name -WITH RECURSIVE x(id) AS (values (1) - UNION ALL - SELECT (SELECT * FROM x) FROM x WHERE id < 5 -) SELECT * FROM x; -ERROR: recursive reference to query "x" must not appear within a subquery -LINE 3: SELECT (SELECT * FROM x) FROM x WHERE id < 5 - ^ --- mutual recursive query (not implemented) -WITH RECURSIVE - x (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM y WHERE id < 5), - y (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 5) -SELECT * FROM x; -ERROR: mutual recursion between WITH items is not implemented -LINE 2: x (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM y WHERE id ... - ^ --- non-linear recursion is not allowed -WITH RECURSIVE foo(i) AS - (values (1) - UNION ALL - (SELECT i+1 FROM foo WHERE i < 10 - UNION ALL - SELECT i+1 FROM foo WHERE i < 5) -) SELECT * FROM foo; -ERROR: recursive reference to query "foo" must not appear more than once -LINE 6: SELECT i+1 FROM foo WHERE i < 5) - ^ -WITH RECURSIVE foo(i) AS - (values (1) - UNION ALL - SELECT * FROM - (SELECT i+1 FROM foo WHERE i < 10 - UNION ALL - SELECT i+1 FROM foo WHERE i < 5) AS t -) SELECT * FROM foo; -ERROR: recursive reference to query "foo" must not appear more than once -LINE 7: SELECT i+1 FROM foo WHERE i < 5) AS t - ^ -WITH RECURSIVE foo(i) AS - (values (1) - UNION ALL - (SELECT i+1 FROM foo WHERE i < 10 - EXCEPT - SELECT i+1 FROM foo WHERE i < 5) -) SELECT * FROM foo; -ERROR: recursive reference to query "foo" must not appear within EXCEPT -LINE 6: SELECT i+1 FROM foo WHERE i < 5) - ^ -WITH RECURSIVE foo(i) AS - (values (1) - UNION ALL - (SELECT i+1 FROM foo WHERE i < 10 - INTERSECT - SELECT i+1 FROM foo WHERE i < 5) -) SELECT * FROM foo; -ERROR: recursive reference to query "foo" must not appear more than once -LINE 6: SELECT i+1 FROM foo WHERE i < 5) - ^ --- Wrong type induced from non-recursive term -WITH RECURSIVE foo(i) AS - (SELECT i FROM (VALUES(1),(2)) t(i) - UNION ALL - SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10) -SELECT * FROM foo; -ERROR: recursive query "foo" column 1 has type integer in non-recursive term but type numeric overall -LINE 2: (SELECT i FROM (VALUES(1),(2)) t(i) - ^ -HINT: Cast the output of the non-recursive term to the correct type. --- rejects different typmod, too (should we allow this?) -WITH RECURSIVE foo(i) AS - (SELECT i::numeric(3,0) FROM (VALUES(1),(2)) t(i) - UNION ALL - SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10) -SELECT * FROM foo; -ERROR: recursive query "foo" column 1 has type numeric(3,0) in non-recursive term but type numeric overall -LINE 2: (SELECT i::numeric(3,0) FROM (VALUES(1),(2)) t(i) - ^ -HINT: Cast the output of the non-recursive term to the correct type. --- disallow OLD/NEW reference in CTE -CREATE TEMPORARY TABLE x (n integer); -CREATE RULE r2 AS ON UPDATE TO x DO INSTEAD - WITH t AS (SELECT OLD.*) UPDATE y SET a = t.n FROM t; -ERROR: cannot refer to OLD within WITH query --- --- test for bug #4902 --- -with cte(foo) as ( values(42) ) values((select foo from cte)); - column1 ---------- - 42 -(1 row) - -with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q; - foo ------ - 42 -(1 row) - --- test CTE referencing an outer-level variable (to see that changed-parameter --- signaling still works properly after fixing this bug) -select ( with cte(foo) as ( values(f1) ) - select (select foo from cte) ) -from int4_tbl; - foo -------------- - 0 - 123456 - -123456 - 2147483647 - -2147483647 -(5 rows) - -select ( with cte(foo) as ( values(f1) ) - values((select foo from cte)) ) -from int4_tbl; - column1 -------------- - 0 - 123456 - -123456 - 2147483647 - -2147483647 -(5 rows) - --- --- test for nested-recursive-WITH bug --- -WITH RECURSIVE t(j) AS ( - WITH RECURSIVE s(i) AS ( - VALUES (1) - UNION ALL - SELECT i+1 FROM s WHERE i < 10 - ) - SELECT i FROM s - UNION ALL - SELECT j+1 FROM t WHERE j < 10 -) -SELECT * FROM t; - j ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 5 - 6 - 7 - 8 - 9 - 10 - 6 - 7 - 8 - 9 - 10 - 7 - 8 - 9 - 10 - 8 - 9 - 10 - 9 - 10 - 10 -(55 rows) - --- --- test WITH attached to intermediate-level set operation --- -WITH outermost(x) AS ( - SELECT 1 - UNION (WITH innermost as (SELECT 2) - SELECT * FROM innermost - UNION SELECT 3) -) -SELECT * FROM outermost ORDER BY 1; - x ---- - 1 - 2 - 3 -(3 rows) - -WITH outermost(x) AS ( - SELECT 1 - UNION (WITH innermost as (SELECT 2) - SELECT * FROM outermost -- fail - UNION SELECT * FROM innermost) -) -SELECT * FROM outermost ORDER BY 1; -ERROR: relation "outermost" does not exist -LINE 4: SELECT * FROM outermost - ^ -DETAIL: There is a WITH item named "outermost", but it cannot be referenced from this part of the query. -HINT: Use WITH RECURSIVE, or re-order the WITH items to remove forward references. -WITH RECURSIVE outermost(x) AS ( - SELECT 1 - UNION (WITH innermost as (SELECT 2) - SELECT * FROM outermost - UNION SELECT * FROM innermost) -) -SELECT * FROM outermost ORDER BY 1; - x ---- - 1 - 2 -(2 rows) - -WITH RECURSIVE outermost(x) AS ( - WITH innermost as (SELECT 2 FROM outermost) -- fail - SELECT * FROM innermost - UNION SELECT * from outermost -) -SELECT * FROM outermost ORDER BY 1; -ERROR: recursive reference to query "outermost" must not appear within a subquery -LINE 2: WITH innermost as (SELECT 2 FROM outermost) - ^ --- --- This test will fail with the old implementation of PARAM_EXEC parameter --- assignment, because the "q1" Var passed down to A's targetlist subselect --- looks exactly like the "A.id" Var passed down to C's subselect, causing --- the old code to give them the same runtime PARAM_EXEC slot. But the --- lifespans of the two parameters overlap, thanks to B also reading A. --- -with -A as ( select q2 as id, (select q1) as x from int8_tbl ), -B as ( select id, row_number() over (partition by id) as r from A ), -C as ( select A.id, array(select B.id from B where B.id = A.id) from A ) -select * from C; - id | array --------------------+------------------------------------- - 456 | {456} - 4567890123456789 | {4567890123456789,4567890123456789} - 123 | {123} - 4567890123456789 | {4567890123456789,4567890123456789} - -4567890123456789 | {-4567890123456789} -(5 rows) - --- --- Test CTEs read in non-initialization orders --- -WITH RECURSIVE - tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)), - iter (id_key, row_type, link) AS ( - SELECT 0, 'base', 17 - UNION ALL ( - WITH remaining(id_key, row_type, link, min) AS ( - SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER () - FROM tab INNER JOIN iter USING (link) - WHERE tab.id_key > iter.id_key - ), - first_remaining AS ( - SELECT id_key, row_type, link - FROM remaining - WHERE id_key=min - ), - effect AS ( - SELECT tab.id_key, 'new'::text, tab.link - FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key - WHERE e.row_type = 'false' - ) - SELECT * FROM first_remaining - UNION ALL SELECT * FROM effect - ) - ) -SELECT * FROM iter; - id_key | row_type | link ---------+----------+------ - 0 | base | 17 - 1 | true | 17 - 2 | true | 17 - 3 | true | 17 - 4 | true | 17 - 5 | true | 17 - 6 | true | 17 -(7 rows) - -WITH RECURSIVE - tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)), - iter (id_key, row_type, link) AS ( - SELECT 0, 'base', 17 - UNION ( - WITH remaining(id_key, row_type, link, min) AS ( - SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER () - FROM tab INNER JOIN iter USING (link) - WHERE tab.id_key > iter.id_key - ), - first_remaining AS ( - SELECT id_key, row_type, link - FROM remaining - WHERE id_key=min - ), - effect AS ( - SELECT tab.id_key, 'new'::text, tab.link - FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key - WHERE e.row_type = 'false' - ) - SELECT * FROM first_remaining - UNION ALL SELECT * FROM effect - ) - ) -SELECT * FROM iter; - id_key | row_type | link ---------+----------+------ - 0 | base | 17 - 1 | true | 17 - 2 | true | 17 - 3 | true | 17 - 4 | true | 17 - 5 | true | 17 - 6 | true | 17 -(7 rows) - --- --- Data-modifying statements in WITH --- --- INSERT ... RETURNING -WITH t AS ( - INSERT INTO y - VALUES - (11), - (12), - (13), - (14), - (15), - (16), - (17), - (18), - (19), - (20) - RETURNING * -) -SELECT * FROM t; - a ----- - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 -(10 rows) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 -(20 rows) - --- UPDATE ... RETURNING -WITH t AS ( - UPDATE y - SET a=a+1 - RETURNING * -) -SELECT * FROM t; - a ----- - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 - 21 -(20 rows) - -SELECT * FROM y; - a ----- - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 - 21 -(20 rows) - --- DELETE ... RETURNING -WITH t AS ( - DELETE FROM y - WHERE a <= 10 - RETURNING * -) -SELECT * FROM t; - a ----- - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 -(9 rows) - -SELECT * FROM y; - a ----- - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - 20 - 21 -(11 rows) - --- forward reference -WITH RECURSIVE t AS ( - INSERT INTO y - SELECT a+5 FROM t2 WHERE a > 5 - RETURNING * -), t2 AS ( - UPDATE y SET a=a-11 RETURNING * -) -SELECT * FROM t -UNION ALL -SELECT * FROM t2; - a ----- - 11 - 12 - 13 - 14 - 15 - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 -(16 rows) - -SELECT * FROM y; - a ----- - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 11 - 7 - 12 - 8 - 13 - 9 - 14 - 10 - 15 -(16 rows) - --- unconditional DO INSTEAD rule -CREATE RULE y_rule AS ON DELETE TO y DO INSTEAD - INSERT INTO y VALUES(42) RETURNING *; -WITH t AS ( - DELETE FROM y RETURNING * -) -SELECT * FROM t; - a ----- - 42 -(1 row) - -SELECT * FROM y; - a ----- - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 11 - 7 - 12 - 8 - 13 - 9 - 14 - 10 - 15 - 42 -(17 rows) - -DROP RULE y_rule ON y; --- check merging of outer CTE with CTE in a rule action -CREATE TEMP TABLE bug6051 AS - select i from generate_series(1,3) as t(i); -SELECT * FROM bug6051; - i ---- - 1 - 2 - 3 -(3 rows) - -WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) -INSERT INTO bug6051 SELECT * FROM t1; -SELECT * FROM bug6051; - i ---- - 1 - 2 - 3 -(3 rows) - -CREATE TEMP TABLE bug6051_2 (i int); -CREATE RULE bug6051_ins AS ON INSERT TO bug6051 DO INSTEAD - INSERT INTO bug6051_2 - SELECT NEW.i; -WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) -INSERT INTO bug6051 SELECT * FROM t1; -SELECT * FROM bug6051; - i ---- -(0 rows) - -SELECT * FROM bug6051_2; - i ---- - 1 - 2 - 3 -(3 rows) - --- a truly recursive CTE in the same list -WITH RECURSIVE t(a) AS ( - SELECT 0 - UNION ALL - SELECT a+1 FROM t WHERE a+1 < 5 -), t2 as ( - INSERT INTO y - SELECT * FROM t RETURNING * -) -SELECT * FROM t2 JOIN y USING (a) ORDER BY a; - a ---- - 0 - 1 - 2 - 3 - 4 -(5 rows) - -SELECT * FROM y; - a ----- - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 11 - 7 - 12 - 8 - 13 - 9 - 14 - 10 - 15 - 42 - 0 - 1 - 2 - 3 - 4 -(22 rows) - --- data-modifying WITH in a modifying statement -WITH t AS ( - DELETE FROM y - WHERE a <= 10 - RETURNING * -) -INSERT INTO y SELECT -a FROM t RETURNING *; - a ------ - 0 - -1 - -2 - -3 - -4 - -5 - -6 - -7 - -8 - -9 - -10 - 0 - -1 - -2 - -3 - -4 -(16 rows) - -SELECT * FROM y; - a ------ - 11 - 12 - 13 - 14 - 15 - 42 - 0 - -1 - -2 - -3 - -4 - -5 - -6 - -7 - -8 - -9 - -10 - 0 - -1 - -2 - -3 - -4 -(22 rows) - --- check that WITH query is run to completion even if outer query isn't -WITH t AS ( - UPDATE y SET a = a * 100 RETURNING * -) -SELECT * FROM t LIMIT 10; - a ------- - 1100 - 1200 - 1300 - 1400 - 1500 - 4200 - 0 - -100 - -200 - -300 -(10 rows) - -SELECT * FROM y; - a -------- - 1100 - 1200 - 1300 - 1400 - 1500 - 4200 - 0 - -100 - -200 - -300 - -400 - -500 - -600 - -700 - -800 - -900 - -1000 - 0 - -100 - -200 - -300 - -400 -(22 rows) - --- data-modifying WITH containing INSERT...ON CONFLICT DO UPDATE -CREATE TABLE withz AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i; -ALTER TABLE withz ADD UNIQUE (k); -WITH t AS ( - INSERT INTO withz SELECT i, 'insert' - FROM generate_series(0, 16) i - ON CONFLICT (k) DO UPDATE SET v = withz.v || ', now update' - RETURNING * -) -SELECT * FROM t JOIN y ON t.k = y.a ORDER BY a, k; - k | v | a ----+--------+--- - 0 | insert | 0 - 0 | insert | 0 -(2 rows) - --- Test EXCLUDED.* reference within CTE -WITH aa AS ( - INSERT INTO withz VALUES(1, 5) ON CONFLICT (k) DO UPDATE SET v = EXCLUDED.v - WHERE withz.k != EXCLUDED.k - RETURNING * -) -SELECT * FROM aa; - k | v ----+--- -(0 rows) - --- New query/snapshot demonstrates side-effects of previous query. -SELECT * FROM withz ORDER BY k; - k | v -----+------------------ - 0 | insert - 1 | 1 v, now update - 2 | insert - 3 | insert - 4 | 4 v, now update - 5 | insert - 6 | insert - 7 | 7 v, now update - 8 | insert - 9 | insert - 10 | 10 v, now update - 11 | insert - 12 | insert - 13 | 13 v, now update - 14 | insert - 15 | insert - 16 | 16 v, now update -(17 rows) - --- --- Ensure subqueries within the update clause work, even if they --- reference outside values --- -WITH aa AS (SELECT 1 a, 2 b) -INSERT INTO withz VALUES(1, 'insert') -ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); -WITH aa AS (SELECT 1 a, 2 b) -INSERT INTO withz VALUES(1, 'insert') -ON CONFLICT (k) DO UPDATE SET v = ' update' WHERE withz.k = (SELECT a FROM aa); -WITH aa AS (SELECT 1 a, 2 b) -INSERT INTO withz VALUES(1, 'insert') -ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); -WITH aa AS (SELECT 'a' a, 'b' b UNION ALL SELECT 'a' a, 'b' b) -INSERT INTO withz VALUES(1, 'insert') -ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 'a' LIMIT 1); -WITH aa AS (SELECT 1 a, 2 b) -INSERT INTO withz VALUES(1, (SELECT b || ' insert' FROM aa WHERE a = 1 )) -ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); --- Update a row more than once, in different parts of a wCTE. That is --- an allowed, presumably very rare, edge case, but since it was --- broken in the past, having a test seems worthwhile. -WITH simpletup AS ( - SELECT 2 k, 'Green' v), -upsert_cte AS ( - INSERT INTO withz VALUES(2, 'Blue') ON CONFLICT (k) DO - UPDATE SET (k, v) = (SELECT k, v FROM simpletup WHERE simpletup.k = withz.k) - RETURNING k, v) -INSERT INTO withz VALUES(2, 'Red') ON CONFLICT (k) DO -UPDATE SET (k, v) = (SELECT k, v FROM upsert_cte WHERE upsert_cte.k = withz.k) -RETURNING k, v; - k | v ----+--- -(0 rows) - -DROP TABLE withz; --- check that run to completion happens in proper ordering -TRUNCATE TABLE y; -INSERT INTO y SELECT generate_series(1, 3); -CREATE TEMPORARY TABLE yy (a INTEGER); -WITH RECURSIVE t1 AS ( - INSERT INTO y SELECT * FROM y RETURNING * -), t2 AS ( - INSERT INTO yy SELECT * FROM t1 RETURNING * -) -SELECT 1; - ?column? ----------- - 1 -(1 row) - -SELECT * FROM y; - a ---- - 1 - 2 - 3 - 1 - 2 - 3 -(6 rows) - -SELECT * FROM yy; - a ---- - 1 - 2 - 3 -(3 rows) - -WITH RECURSIVE t1 AS ( - INSERT INTO yy SELECT * FROM t2 RETURNING * -), t2 AS ( - INSERT INTO y SELECT * FROM y RETURNING * -) -SELECT 1; - ?column? ----------- - 1 -(1 row) - -SELECT * FROM y; - a ---- - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 -(12 rows) - -SELECT * FROM yy; - a ---- - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 -(9 rows) - --- triggers -TRUNCATE TABLE y; -INSERT INTO y SELECT generate_series(1, 10); -CREATE FUNCTION y_trigger() RETURNS trigger AS $$ -begin - raise notice 'y_trigger: a = %', new.a; - return new; -end; -$$ LANGUAGE plpgsql; -CREATE TRIGGER y_trig BEFORE INSERT ON y FOR EACH ROW - EXECUTE PROCEDURE y_trigger(); -WITH t AS ( - INSERT INTO y - VALUES - (21), - (22), - (23) - RETURNING * -) -SELECT * FROM t; -NOTICE: y_trigger: a = 21 -NOTICE: y_trigger: a = 22 -NOTICE: y_trigger: a = 23 - a ----- - 21 - 22 - 23 -(3 rows) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 21 - 22 - 23 -(13 rows) - -DROP TRIGGER y_trig ON y; -CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH ROW - EXECUTE PROCEDURE y_trigger(); -WITH t AS ( - INSERT INTO y - VALUES - (31), - (32), - (33) - RETURNING * -) -SELECT * FROM t LIMIT 1; -NOTICE: y_trigger: a = 31 -NOTICE: y_trigger: a = 32 -NOTICE: y_trigger: a = 33 - a ----- - 31 -(1 row) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 21 - 22 - 23 - 31 - 32 - 33 -(16 rows) - -DROP TRIGGER y_trig ON y; -CREATE OR REPLACE FUNCTION y_trigger() RETURNS trigger AS $$ -begin - raise notice 'y_trigger'; - return null; -end; -$$ LANGUAGE plpgsql; -CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH STATEMENT - EXECUTE PROCEDURE y_trigger(); -WITH t AS ( - INSERT INTO y - VALUES - (41), - (42), - (43) - RETURNING * -) -SELECT * FROM t; -NOTICE: y_trigger - a ----- - 41 - 42 - 43 -(3 rows) - -SELECT * FROM y; - a ----- - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 21 - 22 - 23 - 31 - 32 - 33 - 41 - 42 - 43 -(19 rows) - -DROP TRIGGER y_trig ON y; -DROP FUNCTION y_trigger(); --- WITH attached to inherited UPDATE or DELETE -CREATE TEMP TABLE parent ( id int, val text ); -CREATE TEMP TABLE child1 ( ) INHERITS ( parent ); -CREATE TEMP TABLE child2 ( ) INHERITS ( parent ); -INSERT INTO parent VALUES ( 1, 'p1' ); -INSERT INTO child1 VALUES ( 11, 'c11' ),( 12, 'c12' ); -INSERT INTO child2 VALUES ( 23, 'c21' ),( 24, 'c22' ); -WITH rcte AS ( SELECT sum(id) AS totalid FROM parent ) -UPDATE parent SET id = id + totalid FROM rcte; -SELECT * FROM parent; - id | val -----+----- - 72 | p1 - 82 | c11 - 83 | c12 - 94 | c21 - 95 | c22 -(5 rows) - -WITH wcte AS ( INSERT INTO child1 VALUES ( 42, 'new' ) RETURNING id AS newid ) -UPDATE parent SET id = id + newid FROM wcte; -SELECT * FROM parent; - id | val ------+----- - 114 | p1 - 42 | new - 124 | c11 - 125 | c12 - 136 | c21 - 137 | c22 -(6 rows) - -WITH rcte AS ( SELECT max(id) AS maxid FROM parent ) -DELETE FROM parent USING rcte WHERE id = maxid; -SELECT * FROM parent; - id | val ------+----- - 114 | p1 - 42 | new - 124 | c11 - 125 | c12 - 136 | c21 -(5 rows) - -WITH wcte AS ( INSERT INTO child2 VALUES ( 42, 'new2' ) RETURNING id AS newid ) -DELETE FROM parent USING wcte WHERE id = newid; -SELECT * FROM parent; - id | val ------+------ - 114 | p1 - 124 | c11 - 125 | c12 - 136 | c21 - 42 | new2 -(5 rows) - --- check EXPLAIN VERBOSE for a wCTE with RETURNING -EXPLAIN (VERBOSE, COSTS OFF) -WITH wcte AS ( INSERT INTO int8_tbl VALUES ( 42, 47 ) RETURNING q2 ) -DELETE FROM a USING wcte WHERE aa = q2; - QUERY PLAN ----------------------------------------------------- - Delete on public.a - Delete on public.a - Delete on public.b - Delete on public.c - Delete on public.d - CTE wcte - -> Insert on public.int8_tbl - Output: int8_tbl.q2 - -> Result - Output: '42'::bigint, '47'::bigint - -> Nested Loop - Output: a.ctid, wcte.* - Join Filter: (a.aa = wcte.q2) - -> Seq Scan on public.a - Output: a.ctid, a.aa - -> CTE Scan on wcte - Output: wcte.*, wcte.q2 - -> Nested Loop - Output: b.ctid, wcte.* - Join Filter: (b.aa = wcte.q2) - -> Seq Scan on public.b - Output: b.ctid, b.aa - -> CTE Scan on wcte - Output: wcte.*, wcte.q2 - -> Nested Loop - Output: c.ctid, wcte.* - Join Filter: (c.aa = wcte.q2) - -> Seq Scan on public.c - Output: c.ctid, c.aa - -> CTE Scan on wcte - Output: wcte.*, wcte.q2 - -> Nested Loop - Output: d.ctid, wcte.* - Join Filter: (d.aa = wcte.q2) - -> Seq Scan on public.d - Output: d.ctid, d.aa - -> CTE Scan on wcte - Output: wcte.*, wcte.q2 -(38 rows) - --- error cases --- data-modifying WITH tries to use its own output -WITH RECURSIVE t AS ( - INSERT INTO y - SELECT * FROM t -) -VALUES(FALSE); -ERROR: recursive query "t" must not contain data-modifying statements -LINE 1: WITH RECURSIVE t AS ( - ^ --- no RETURNING in a referenced data-modifying WITH -WITH t AS ( - INSERT INTO y VALUES(0) -) -SELECT * FROM t; -ERROR: WITH query "t" does not have a RETURNING clause -LINE 4: SELECT * FROM t; - ^ --- data-modifying WITH allowed only at the top level -SELECT * FROM ( - WITH t AS (UPDATE y SET a=a+1 RETURNING *) - SELECT * FROM t -) ss; -ERROR: WITH clause containing a data-modifying statement must be at the top level -LINE 2: WITH t AS (UPDATE y SET a=a+1 RETURNING *) - ^ --- most variants of rules aren't allowed -CREATE RULE y_rule AS ON INSERT TO y WHERE a=0 DO INSTEAD DELETE FROM y; -WITH t AS ( - INSERT INTO y VALUES(0) -) -VALUES(FALSE); -ERROR: conditional DO INSTEAD rules are not supported for data-modifying statements in WITH -DROP RULE y_rule ON y; --- check that parser lookahead for WITH doesn't cause any odd behavior -create table foo (with baz); -- fail, WITH is a reserved word -ERROR: syntax error at or near "with" -LINE 1: create table foo (with baz); - ^ -create table foo (with ordinality); -- fail, WITH is a reserved word -ERROR: syntax error at or near "with" -LINE 1: create table foo (with ordinality); - ^ -with ordinality as (select 1 as x) select * from ordinality; - x ---- - 1 -(1 row) - --- check sane response to attempt to modify CTE relation -WITH test AS (SELECT 42) INSERT INTO test VALUES (1); -ERROR: relation "test" does not exist -LINE 1: WITH test AS (SELECT 42) INSERT INTO test VALUES (1); - ^ --- check response to attempt to modify table with same name as a CTE (perhaps --- surprisingly it works, because CTEs don't hide tables from data-modifying --- statements) -create temp table test (i int); -with test as (select 42) insert into test select * from test; -select * from test; - i ----- - 42 -(1 row) - -drop table test; + (5, 1, 'arc 5 -> 1') +-- !query 13 schema +struct<> +-- !query 13 output + + + +-- !query 14 +CREATE TABLE y (a INTEGER) USING parquet +-- !query 14 schema +struct<> +-- !query 14 output + + + +-- !query 15 +INSERT INTO y SELECT EXPLODE(SEQUENCE(1, 10)) +-- !query 15 schema +struct<> +-- !query 15 output + + + +-- !query 16 +DROP TABLE y +-- !query 16 schema +struct<> +-- !query 16 output + + + +-- !query 17 +CREATE TABLE y (a INTEGER) USING parquet +-- !query 17 schema +struct<> +-- !query 17 output + + + +-- !query 18 +INSERT INTO y SELECT EXPLODE(SEQUENCE(1, 10)) +-- !query 18 schema +struct<> +-- !query 18 output + + + +-- !query 19 +with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q +-- !query 19 schema +struct +-- !query 19 output +42 + + +-- !query 20 +CREATE TABLE bug6051 USING parquet AS + SELECT EXPLODE(SEQUENCE(1,3)) AS i +-- !query 20 schema +struct<> +-- !query 20 output + + + +-- !query 21 +SELECT * FROM bug6051 +-- !query 21 schema +struct +-- !query 21 output +1 +2 +3 + + +-- !query 22 +CREATE TABLE bug6051_2 (i int) USING parquet +-- !query 22 schema +struct<> +-- !query 22 output + + + +-- !query 23 +CREATE TABLE withz USING parquet AS SELECT i AS k, CAST(i || ' v' AS string) v FROM (SELECT EXPLODE(SEQUENCE(1, 16, 3)) i) +-- !query 23 schema +struct<> +-- !query 23 output + + + +-- !query 24 +SELECT * FROM withz ORDER BY k +-- !query 24 schema +struct +-- !query 24 output +1 1 v +4 4 v +7 7 v +10 10 v +13 13 v +16 16 v + + +-- !query 25 +DROP TABLE withz +-- !query 25 schema +struct<> +-- !query 25 output + + + +-- !query 26 +TRUNCATE TABLE y +-- !query 26 schema +struct<> +-- !query 26 output + + + +-- !query 27 +INSERT INTO y SELECT EXPLODE(SEQUENCE(1, 3)) +-- !query 27 schema +struct<> +-- !query 27 output + + + +-- !query 28 +CREATE TABLE yy (a INTEGER) USING parquet +-- !query 28 schema +struct<> +-- !query 28 output + + + +-- !query 29 +SELECT * FROM y +-- !query 29 schema +struct +-- !query 29 output +1 +2 +3 + + +-- !query 30 +SELECT * FROM yy +-- !query 30 schema +struct +-- !query 30 output + + + +-- !query 31 +SELECT * FROM y +-- !query 31 schema +struct +-- !query 31 output +1 +2 +3 + + +-- !query 32 +SELECT * FROM yy +-- !query 32 schema +struct +-- !query 32 output + + + +-- !query 33 +CREATE TABLE parent ( id int, val string ) USING parquet +-- !query 33 schema +struct<> +-- !query 33 output + + + +-- !query 34 +INSERT INTO parent VALUES ( 1, 'p1' ) +-- !query 34 schema +struct<> +-- !query 34 output + + + +-- !query 35 +SELECT * FROM parent +-- !query 35 schema +struct +-- !query 35 output +1 p1 + + +-- !query 36 +SELECT * FROM parent +-- !query 36 schema +struct +-- !query 36 output +1 p1 + + +-- !query 37 +create table foo (with baz) +-- !query 37 schema +struct<> +-- !query 37 output +org.apache.spark.sql.catalyst.parser.ParseException + +DataType baz is not supported.(line 1, pos 23) + +== SQL == +create table foo (with baz) +-----------------------^^^ + + +-- !query 38 +-- fail, WITH is a reserved word +create table foo (with ordinality) +-- !query 38 schema +struct<> +-- !query 38 output +org.apache.spark.sql.catalyst.parser.ParseException + +DataType ordinality is not supported.(line 2, pos 23) + +== SQL == +-- fail, WITH is a reserved word +create table foo (with ordinality) +-----------------------^^^ + + +-- !query 39 +-- fail, WITH is a reserved word +with ordinality as (select 1 as x) select * from ordinality +-- !query 39 schema +struct +-- !query 39 output +1 + + +-- !query 40 +WITH test AS (SELECT 42) INSERT INTO test VALUES (1) +-- !query 40 schema +struct<> +-- !query 40 output +org.apache.spark.sql.AnalysisException +unresolved operator 'InsertIntoTable 'UnresolvedRelation [test], false, false; + + +-- !query 41 +create table test (i int) USING parquet +-- !query 41 schema +struct<> +-- !query 41 output + + + +-- !query 42 +with test as (select 42) insert into test select * from test +-- !query 42 schema +struct<> +-- !query 42 output + + + +-- !query 43 +select * from test +-- !query 43 schema +struct +-- !query 43 output +42 + + +-- !query 44 +drop table test +-- !query 44 schema +struct<> +-- !query 44 output + + + +-- !query 45 +DROP TABLE department +-- !query 45 schema +struct<> +-- !query 45 output + + + +-- !query 46 +DROP TABLE tree +-- !query 46 schema +struct<> +-- !query 46 output + + + +-- !query 47 +DROP TABLE graph +-- !query 47 schema +struct<> +-- !query 47 output + + + +-- !query 48 +DROP TABLE y +-- !query 48 schema +struct<> +-- !query 48 output + + + +-- !query 49 +DROP TABLE bug6051 +-- !query 49 schema +struct<> +-- !query 49 output + + + +-- !query 50 +DROP TABLE bug6051_2 +-- !query 50 schema +struct<> +-- !query 50 output + + + +-- !query 51 +DROP TABLE yy +-- !query 51 schema +struct<> +-- !query 51 output + + + +-- !query 52 +DROP TABLE parent +-- !query 52 schema +struct<> +-- !query 52 output + From 8c12fce97577e0e3a914f57f83f37d0aad4ffa31 Mon Sep 17 00:00:00 2001 From: Peter Toth Date: Fri, 28 Jun 2019 15:42:03 +0200 Subject: [PATCH 3/8] fix review findings --- .../resources/sql-tests/inputs/pgSQL/with.sql | 14 +- .../sql-tests/results/pgSQL/with.sql.out | 258 ++++++++++-------- 2 files changed, 147 insertions(+), 125 deletions(-) diff --git a/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql b/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql index 452d7f4658ec..2a91015d971e 100644 --- a/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql +++ b/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql @@ -4,17 +4,18 @@ -- -- WITH -- https://github.com/postgres/postgres/blob/REL_12_BETA1/src/test/regress/sql/with.sql +-- +-- This test suite contains two Cartesian products without using explicit CROSS JOIN syntax. +-- Thus, we set spark.sql.crossJoin.enabled to true. +set spark.sql.crossJoin.enabled=true; -- -- Tests for common table expressions (WITH query, ... SELECT ...) -- -- Basic WITH --- [ORIGINAL SQL] ---WITH q1(x,y) AS (SELECT 1,2) ---SELECT * FROM q1, q1 AS q2; WITH q1(x,y) AS (SELECT 1,2) -SELECT * FROM q1 CROSS JOIN q1 AS q2; +SELECT * FROM q1, q1 AS q2; -- Multiple uses are evaluated only once -- [SPARK-19799] Support recursive SQL query @@ -1206,6 +1207,10 @@ with test as (select 42) insert into test select * from test; select * from test; drop table test; +-- +-- Clean up +-- + DROP TABLE department; DROP TABLE tree; DROP TABLE graph; @@ -1214,3 +1219,4 @@ DROP TABLE bug6051; DROP TABLE bug6051_2; DROP TABLE yy; DROP TABLE parent; +set spark.sql.crossJoin.enabled=false; diff --git a/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out b/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out index d9cd652294f6..9294efebf996 100644 --- a/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out +++ b/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out @@ -1,30 +1,30 @@ -- Automatically generated by SQLQueryTestSuite --- Number of queries: 53 +-- Number of queries: 55 -- !query 0 -WITH q1(x,y) AS (SELECT 1,2) -SELECT * FROM q1 CROSS JOIN q1 AS q2 +set spark.sql.crossJoin.enabled=true -- !query 0 schema -struct +struct -- !query 0 output -1 2 1 2 +spark.sql.crossJoin.enabled true -- !query 1 -CREATE TABLE department ( - id INTEGER, -- department ID - parent_department INTEGER, -- upper department ID - name string -- department name -) USING parquet +WITH q1(x,y) AS (SELECT 1,2) +SELECT * FROM q1, q1 AS q2 -- !query 1 schema -struct<> +struct -- !query 1 output - +1 2 1 2 -- !query 2 -INSERT INTO department VALUES (0, NULL, 'ROOT') +CREATE TABLE department ( + id INTEGER, -- department ID + parent_department INTEGER, -- upper department ID + name string -- department name +) USING parquet -- !query 2 schema struct<> -- !query 2 output @@ -32,7 +32,7 @@ struct<> -- !query 3 -INSERT INTO department VALUES (1, 0, 'A') +INSERT INTO department VALUES (0, NULL, 'ROOT') -- !query 3 schema struct<> -- !query 3 output @@ -40,7 +40,7 @@ struct<> -- !query 4 -INSERT INTO department VALUES (2, 1, 'B') +INSERT INTO department VALUES (1, 0, 'A') -- !query 4 schema struct<> -- !query 4 output @@ -48,7 +48,7 @@ struct<> -- !query 5 -INSERT INTO department VALUES (3, 2, 'C') +INSERT INTO department VALUES (2, 1, 'B') -- !query 5 schema struct<> -- !query 5 output @@ -56,7 +56,7 @@ struct<> -- !query 6 -INSERT INTO department VALUES (4, 2, 'D') +INSERT INTO department VALUES (3, 2, 'C') -- !query 6 schema struct<> -- !query 6 output @@ -64,7 +64,7 @@ struct<> -- !query 7 -INSERT INTO department VALUES (5, 0, 'E') +INSERT INTO department VALUES (4, 2, 'D') -- !query 7 schema struct<> -- !query 7 output @@ -72,7 +72,7 @@ struct<> -- !query 8 -INSERT INTO department VALUES (6, 4, 'F') +INSERT INTO department VALUES (5, 0, 'E') -- !query 8 schema struct<> -- !query 8 output @@ -80,7 +80,7 @@ struct<> -- !query 9 -INSERT INTO department VALUES (7, 5, 'G') +INSERT INTO department VALUES (6, 4, 'F') -- !query 9 schema struct<> -- !query 9 output @@ -88,10 +88,7 @@ struct<> -- !query 10 -CREATE TABLE tree( - id INTEGER, - parent_id INTEGER -) USING parquet +INSERT INTO department VALUES (7, 5, 'G') -- !query 10 schema struct<> -- !query 10 output @@ -99,9 +96,10 @@ struct<> -- !query 11 -INSERT INTO tree -VALUES (1, NULL), (2, 1), (3,1), (4,2), (5,2), (6,2), (7,3), (8,3), - (9,4), (10,4), (11,7), (12,7), (13,7), (14, 9), (15,11), (16,11) +CREATE TABLE tree( + id INTEGER, + parent_id INTEGER +) USING parquet -- !query 11 schema struct<> -- !query 11 output @@ -109,7 +107,9 @@ struct<> -- !query 12 -create table graph( f int, t int, label string ) USING parquet +INSERT INTO tree +VALUES (1, NULL), (2, 1), (3,1), (4,2), (5,2), (6,2), (7,3), (8,3), + (9,4), (10,4), (11,7), (12,7), (13,7), (14, 9), (15,11), (16,11) -- !query 12 schema struct<> -- !query 12 output @@ -117,13 +117,7 @@ struct<> -- !query 13 -insert into graph values - (1, 2, 'arc 1 -> 2'), - (1, 3, 'arc 1 -> 3'), - (2, 3, 'arc 2 -> 3'), - (1, 4, 'arc 1 -> 4'), - (4, 5, 'arc 4 -> 5'), - (5, 1, 'arc 5 -> 1') +create table graph( f int, t int, label string ) USING parquet -- !query 13 schema struct<> -- !query 13 output @@ -131,7 +125,13 @@ struct<> -- !query 14 -CREATE TABLE y (a INTEGER) USING parquet +insert into graph values + (1, 2, 'arc 1 -> 2'), + (1, 3, 'arc 1 -> 3'), + (2, 3, 'arc 2 -> 3'), + (1, 4, 'arc 1 -> 4'), + (4, 5, 'arc 4 -> 5'), + (5, 1, 'arc 5 -> 1') -- !query 14 schema struct<> -- !query 14 output @@ -139,7 +139,7 @@ struct<> -- !query 15 -INSERT INTO y SELECT EXPLODE(SEQUENCE(1, 10)) +CREATE TABLE y (a INTEGER) USING parquet -- !query 15 schema struct<> -- !query 15 output @@ -147,7 +147,7 @@ struct<> -- !query 16 -DROP TABLE y +INSERT INTO y SELECT EXPLODE(SEQUENCE(1, 10)) -- !query 16 schema struct<> -- !query 16 output @@ -155,7 +155,7 @@ struct<> -- !query 17 -CREATE TABLE y (a INTEGER) USING parquet +DROP TABLE y -- !query 17 schema struct<> -- !query 17 output @@ -163,7 +163,7 @@ struct<> -- !query 18 -INSERT INTO y SELECT EXPLODE(SEQUENCE(1, 10)) +CREATE TABLE y (a INTEGER) USING parquet -- !query 18 schema struct<> -- !query 18 output @@ -171,53 +171,61 @@ struct<> -- !query 19 -with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q +INSERT INTO y SELECT EXPLODE(SEQUENCE(1, 10)) -- !query 19 schema -struct +struct<> -- !query 19 output -42 + -- !query 20 +with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q +-- !query 20 schema +struct +-- !query 20 output +42 + + +-- !query 21 CREATE TABLE bug6051 USING parquet AS SELECT EXPLODE(SEQUENCE(1,3)) AS i --- !query 20 schema +-- !query 21 schema struct<> --- !query 20 output +-- !query 21 output --- !query 21 +-- !query 22 SELECT * FROM bug6051 --- !query 21 schema +-- !query 22 schema struct --- !query 21 output +-- !query 22 output 1 2 3 --- !query 22 +-- !query 23 CREATE TABLE bug6051_2 (i int) USING parquet --- !query 22 schema +-- !query 23 schema struct<> --- !query 22 output +-- !query 23 output --- !query 23 +-- !query 24 CREATE TABLE withz USING parquet AS SELECT i AS k, CAST(i || ' v' AS string) v FROM (SELECT EXPLODE(SEQUENCE(1, 16, 3)) i) --- !query 23 schema +-- !query 24 schema struct<> --- !query 23 output +-- !query 24 output --- !query 24 +-- !query 25 SELECT * FROM withz ORDER BY k --- !query 24 schema +-- !query 25 schema struct --- !query 24 output +-- !query 25 output 1 1 v 4 4 v 7 7 v @@ -226,16 +234,8 @@ struct 16 16 v --- !query 25 -DROP TABLE withz --- !query 25 schema -struct<> --- !query 25 output - - - -- !query 26 -TRUNCATE TABLE y +DROP TABLE withz -- !query 26 schema struct<> -- !query 26 output @@ -243,7 +243,7 @@ struct<> -- !query 27 -INSERT INTO y SELECT EXPLODE(SEQUENCE(1, 3)) +TRUNCATE TABLE y -- !query 27 schema struct<> -- !query 27 output @@ -251,7 +251,7 @@ struct<> -- !query 28 -CREATE TABLE yy (a INTEGER) USING parquet +INSERT INTO y SELECT EXPLODE(SEQUENCE(1, 3)) -- !query 28 schema struct<> -- !query 28 output @@ -259,51 +259,51 @@ struct<> -- !query 29 -SELECT * FROM y +CREATE TABLE yy (a INTEGER) USING parquet -- !query 29 schema -struct +struct<> -- !query 29 output -1 -2 -3 + -- !query 30 -SELECT * FROM yy +SELECT * FROM y -- !query 30 schema struct -- !query 30 output - +1 +2 +3 -- !query 31 -SELECT * FROM y +SELECT * FROM yy -- !query 31 schema struct -- !query 31 output -1 -2 -3 + -- !query 32 -SELECT * FROM yy +SELECT * FROM y -- !query 32 schema struct -- !query 32 output - +1 +2 +3 -- !query 33 -CREATE TABLE parent ( id int, val string ) USING parquet +SELECT * FROM yy -- !query 33 schema -struct<> +struct -- !query 33 output -- !query 34 -INSERT INTO parent VALUES ( 1, 'p1' ) +CREATE TABLE parent ( id int, val string ) USING parquet -- !query 34 schema struct<> -- !query 34 output @@ -311,11 +311,11 @@ struct<> -- !query 35 -SELECT * FROM parent +INSERT INTO parent VALUES ( 1, 'p1' ) -- !query 35 schema -struct +struct<> -- !query 35 output -1 p1 + -- !query 36 @@ -327,10 +327,18 @@ struct -- !query 37 -create table foo (with baz) +SELECT * FROM parent -- !query 37 schema -struct<> +struct -- !query 37 output +1 p1 + + +-- !query 38 +create table foo (with baz) +-- !query 38 schema +struct<> +-- !query 38 output org.apache.spark.sql.catalyst.parser.ParseException DataType baz is not supported.(line 1, pos 23) @@ -340,12 +348,12 @@ create table foo (with baz) -----------------------^^^ --- !query 38 +-- !query 39 -- fail, WITH is a reserved word create table foo (with ordinality) --- !query 38 schema +-- !query 39 schema struct<> --- !query 38 output +-- !query 39 output org.apache.spark.sql.catalyst.parser.ParseException DataType ordinality is not supported.(line 2, pos 23) @@ -356,34 +364,26 @@ create table foo (with ordinality) -----------------------^^^ --- !query 39 +-- !query 40 -- fail, WITH is a reserved word with ordinality as (select 1 as x) select * from ordinality --- !query 39 schema -struct --- !query 39 output -1 - - --- !query 40 -WITH test AS (SELECT 42) INSERT INTO test VALUES (1) -- !query 40 schema -struct<> +struct -- !query 40 output -org.apache.spark.sql.AnalysisException -unresolved operator 'InsertIntoTable 'UnresolvedRelation [test], false, false; +1 -- !query 41 -create table test (i int) USING parquet +WITH test AS (SELECT 42) INSERT INTO test VALUES (1) -- !query 41 schema struct<> -- !query 41 output - +org.apache.spark.sql.AnalysisException +unresolved operator 'InsertIntoTable 'UnresolvedRelation [test], false, false; -- !query 42 -with test as (select 42) insert into test select * from test +create table test (i int) USING parquet -- !query 42 schema struct<> -- !query 42 output @@ -391,23 +391,23 @@ struct<> -- !query 43 -select * from test +with test as (select 42) insert into test select * from test -- !query 43 schema -struct +struct<> -- !query 43 output -42 + -- !query 44 -drop table test +select * from test -- !query 44 schema -struct<> +struct -- !query 44 output - +42 -- !query 45 -DROP TABLE department +drop table test -- !query 45 schema struct<> -- !query 45 output @@ -415,7 +415,7 @@ struct<> -- !query 46 -DROP TABLE tree +DROP TABLE department -- !query 46 schema struct<> -- !query 46 output @@ -423,7 +423,7 @@ struct<> -- !query 47 -DROP TABLE graph +DROP TABLE tree -- !query 47 schema struct<> -- !query 47 output @@ -431,7 +431,7 @@ struct<> -- !query 48 -DROP TABLE y +DROP TABLE graph -- !query 48 schema struct<> -- !query 48 output @@ -439,7 +439,7 @@ struct<> -- !query 49 -DROP TABLE bug6051 +DROP TABLE y -- !query 49 schema struct<> -- !query 49 output @@ -447,7 +447,7 @@ struct<> -- !query 50 -DROP TABLE bug6051_2 +DROP TABLE bug6051 -- !query 50 schema struct<> -- !query 50 output @@ -455,7 +455,7 @@ struct<> -- !query 51 -DROP TABLE yy +DROP TABLE bug6051_2 -- !query 51 schema struct<> -- !query 51 output @@ -463,8 +463,24 @@ struct<> -- !query 52 -DROP TABLE parent +DROP TABLE yy -- !query 52 schema struct<> -- !query 52 output + + +-- !query 53 +DROP TABLE parent +-- !query 53 schema +struct<> +-- !query 53 output + + + +-- !query 54 +set spark.sql.crossJoin.enabled=false +-- !query 54 schema +struct +-- !query 54 output +spark.sql.crossJoin.enabled false From aa2242c02dcecb89b0814b6e17503c45970023bd Mon Sep 17 00:00:00 2001 From: Peter Toth Date: Thu, 4 Jul 2019 15:18:51 +0200 Subject: [PATCH 4/8] update with.sql source --- sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql b/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql index 2a91015d971e..179b5396c645 100644 --- a/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql +++ b/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql @@ -3,7 +3,7 @@ -- -- -- WITH --- https://github.com/postgres/postgres/blob/REL_12_BETA1/src/test/regress/sql/with.sql +-- https://github.com/postgres/postgres/blob/REL_12_BETA2/src/test/regress/sql/with.sql -- -- This test suite contains two Cartesian products without using explicit CROSS JOIN syntax. -- Thus, we set spark.sql.crossJoin.enabled to true. From ad7a86c5e699ac8133ddc807937b94a07412bb08 Mon Sep 17 00:00:00 2001 From: Peter Toth Date: Fri, 5 Jul 2019 10:28:43 +0200 Subject: [PATCH 5/8] rebase onto master and regenerate expected results --- .../src/test/resources/sql-tests/results/pgSQL/with.sql.out | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out b/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out index 9294efebf996..831218523853 100644 --- a/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out +++ b/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out @@ -379,7 +379,7 @@ WITH test AS (SELECT 42) INSERT INTO test VALUES (1) struct<> -- !query 41 output org.apache.spark.sql.AnalysisException -unresolved operator 'InsertIntoTable 'UnresolvedRelation [test], false, false; +Table not found: test; -- !query 42 From 5b9997ec706bafe2b17e00f2415d1f31f8564dad Mon Sep 17 00:00:00 2001 From: Peter Toth Date: Fri, 5 Jul 2019 10:28:43 +0200 Subject: [PATCH 6/8] recheck and regenerate expected results --- .../resources/sql-tests/inputs/pgSQL/with.sql | 74 +++++----- .../sql-tests/results/pgSQL/with.sql.out | 139 +++++++++--------- 2 files changed, 99 insertions(+), 114 deletions(-) diff --git a/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql b/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql index 179b5396c645..90fb7c12f3ca 100644 --- a/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql +++ b/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql @@ -8,6 +8,8 @@ -- This test suite contains two Cartesian products without using explicit CROSS JOIN syntax. -- Thus, we set spark.sql.crossJoin.enabled to true. set spark.sql.crossJoin.enabled=true; +-- This test uses the generate_series(...) function which is rewritten to EXPLODE(SEQUENCE(...)) as +-- it's feature tracking ticket SPARK-27767 is closed as Won't Do. -- -- Tests for common table expressions (WITH query, ... SELECT ...) @@ -18,13 +20,20 @@ WITH q1(x,y) AS (SELECT 1,2) SELECT * FROM q1, q1 AS q2; -- Multiple uses are evaluated only once --- [SPARK-19799] Support recursive SQL query +-- [SPARK-28299] Evaluation of multiple CTE uses +-- [ORIGINAL SQL] --SELECT count(*) FROM ( -- WITH q1(x) AS (SELECT random() FROM generate_series(1, 5)) -- SELECT * FROM q1 -- UNION -- SELECT * FROM q1 --) ss; +SELECT count(*) FROM ( + WITH q1(x) AS (SELECT rand() FROM (SELECT EXPLODE(SEQUENCE(1, 5)))) + SELECT * FROM q1 + UNION + SELECT * FROM q1 +) ss; -- WITH RECURSIVE @@ -200,7 +209,6 @@ INSERT INTO department VALUES (7, 5, 'G'); --SELECT * FROM subdepartment ORDER BY name; -- inside subqueries --- [SPARK-19799] Support WITH clause in subqueries -- [SPARK-24497] Support recursive SQL query --SELECT count(*) FROM ( -- WITH RECURSIVE t(n) AS ( @@ -441,7 +449,7 @@ insert into graph values -- -- [ORIGINAL SQL] ---CREATE TEMPORARY TABLE y (a INTEGER) USING parquet; +--CREATE TEMPORARY TABLE y (a INTEGER); CREATE TABLE y (a INTEGER) USING parquet; -- [ORIGINAL SQL] --INSERT INTO y SELECT generate_series(1, 10); @@ -644,18 +652,20 @@ INSERT INTO y SELECT EXPLODE(SEQUENCE(1, 10)); -- -- test for bug #4902 -- --- [SPARK-19799] Support recursive SQL query +-- [SPARK-28296] Improved VALUES support --with cte(foo) as ( values(42) ) values((select foo from cte)); with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q; -- test CTE referencing an outer-level variable (to see that changed-parameter -- signaling still works properly after fixing this bug) --- [SPARK-19799] Support WITH clause in subqueries +-- [SPARK-28296] Improved VALUES support +-- [SPARK-28297] Handling outer links in CTE subquery expressions --select ( with cte(foo) as ( values(f1) ) -- select (select foo from cte) ) --from int4_tbl; --- [SPARK-19799] Support WITH clause in subqueries +-- [SPARK-28296] Improved VALUES support +-- [SPARK-28297] Handling outer links in CTE subquery expressions --select ( with cte(foo) as ( values(f1) ) -- values((select foo from cte)) ) --from int4_tbl; @@ -680,23 +690,21 @@ with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q; -- test WITH attached to intermediate-level set operation -- --- [SPARK-19799] Support recursive SQL query ---WITH outermost(x) AS ( --- SELECT 1 --- UNION (WITH innermost as (SELECT 2) --- SELECT * FROM innermost --- UNION SELECT 3) ---) ---SELECT * FROM outermost ORDER BY 1; +WITH outermost(x) AS ( + SELECT 1 + UNION (WITH innermost as (SELECT 2) + SELECT * FROM innermost + UNION SELECT 3) +) +SELECT * FROM outermost ORDER BY 1; --- [SPARK-19799] Support recursive SQL query ---WITH outermost(x) AS ( --- SELECT 1 --- UNION (WITH innermost as (SELECT 2) --- SELECT * FROM outermost -- fail --- UNION SELECT * FROM innermost) ---) ---SELECT * FROM outermost ORDER BY 1; +WITH outermost(x) AS ( + SELECT 1 + UNION (WITH innermost as (SELECT 2) + SELECT * FROM outermost -- fail + UNION SELECT * FROM innermost) +) +SELECT * FROM outermost ORDER BY 1; -- [SPARK-24497] Support recursive SQL query --WITH RECURSIVE outermost(x) AS ( @@ -723,14 +731,6 @@ with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q; -- lifespans of the two parameters overlap, thanks to B also reading A. -- --- [NOTE] INT8_TBL is created in int8.sql originally ---CREATE TABLE INT8_TBL(q1 bigint, q2 bigint) USING parquet; ---INSERT INTO INT8_TBL VALUES(trim(' 123 '),trim(' 456')); ---INSERT INTO INT8_TBL VALUES(trim('123 '),'4567890123456789'); ---INSERT INTO INT8_TBL VALUES('4567890123456789','123'); ---INSERT INTO INT8_TBL VALUES(+4567890123456789,'4567890123456789'); ---INSERT INTO INT8_TBL VALUES('+4567890123456789','-4567890123456789'); - -- [SPARK-27878] Support ARRAY(sub-SELECT) expressions --with --A as ( select q2 as id, (select q1) as x from int8_tbl ), @@ -738,8 +738,6 @@ with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q; --C as ( select A.id, array(select B.id from B where B.id = A.id) from A ) --select * from C; ---DROP TABLE INT8_TBL; - -- -- Test CTEs read in non-initialization orders -- @@ -876,13 +874,10 @@ with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q; --DROP RULE y_rule ON y; -- check merging of outer CTE with CTE in a rule action --- [ORIGINAL SQL] --CREATE TEMP TABLE bug6051 AS -- select i from generate_series(1,3) as t(i); -CREATE TABLE bug6051 USING parquet AS - SELECT EXPLODE(SEQUENCE(1,3)) AS i; -SELECT * FROM bug6051; +--SELECT * FROM bug6051; -- [NOTE] Spark SQL doesn't support DELETE statement --WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) @@ -890,9 +885,7 @@ SELECT * FROM bug6051; -- --SELECT * FROM bug6051; --- [ORIGINAL SQL] --CREATE TEMP TABLE bug6051_2 (i int); -CREATE TABLE bug6051_2 (i int) USING parquet; --CREATE RULE bug6051_ins AS ON INSERT TO bug6051 DO INSTEAD -- INSERT INTO bug6051_2 @@ -1190,7 +1183,9 @@ SELECT * FROM parent; --DROP RULE y_rule ON y; -- check that parser lookahead for WITH doesn't cause any odd behavior +-- [TODO] WITH should be a reserved keyword? create table foo (with baz); -- fail, WITH is a reserved word +-- [TODO] WITH should be a reserved keyword? create table foo (with ordinality); -- fail, WITH is a reserved word with ordinality as (select 1 as x) select * from ordinality; @@ -1215,8 +1210,5 @@ DROP TABLE department; DROP TABLE tree; DROP TABLE graph; DROP TABLE y; -DROP TABLE bug6051; -DROP TABLE bug6051_2; DROP TABLE yy; DROP TABLE parent; -set spark.sql.crossJoin.enabled=false; diff --git a/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out b/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out index 831218523853..c548922572b8 100644 --- a/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out +++ b/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out @@ -1,5 +1,5 @@ -- Automatically generated by SQLQueryTestSuite --- Number of queries: 55 +-- Number of queries: 52 -- !query 0 @@ -20,19 +20,24 @@ struct -- !query 2 -CREATE TABLE department ( - id INTEGER, -- department ID - parent_department INTEGER, -- upper department ID - name string -- department name -) USING parquet +SELECT count(*) FROM ( + WITH q1(x) AS (SELECT rand() FROM (SELECT EXPLODE(SEQUENCE(1, 5)))) + SELECT * FROM q1 + UNION + SELECT * FROM q1 +) ss -- !query 2 schema -struct<> +struct -- !query 2 output - +10 -- !query 3 -INSERT INTO department VALUES (0, NULL, 'ROOT') +CREATE TABLE department ( + id INTEGER, -- department ID + parent_department INTEGER, -- upper department ID + name string -- department name +) USING parquet -- !query 3 schema struct<> -- !query 3 output @@ -40,7 +45,7 @@ struct<> -- !query 4 -INSERT INTO department VALUES (1, 0, 'A') +INSERT INTO department VALUES (0, NULL, 'ROOT') -- !query 4 schema struct<> -- !query 4 output @@ -48,7 +53,7 @@ struct<> -- !query 5 -INSERT INTO department VALUES (2, 1, 'B') +INSERT INTO department VALUES (1, 0, 'A') -- !query 5 schema struct<> -- !query 5 output @@ -56,7 +61,7 @@ struct<> -- !query 6 -INSERT INTO department VALUES (3, 2, 'C') +INSERT INTO department VALUES (2, 1, 'B') -- !query 6 schema struct<> -- !query 6 output @@ -64,7 +69,7 @@ struct<> -- !query 7 -INSERT INTO department VALUES (4, 2, 'D') +INSERT INTO department VALUES (3, 2, 'C') -- !query 7 schema struct<> -- !query 7 output @@ -72,7 +77,7 @@ struct<> -- !query 8 -INSERT INTO department VALUES (5, 0, 'E') +INSERT INTO department VALUES (4, 2, 'D') -- !query 8 schema struct<> -- !query 8 output @@ -80,7 +85,7 @@ struct<> -- !query 9 -INSERT INTO department VALUES (6, 4, 'F') +INSERT INTO department VALUES (5, 0, 'E') -- !query 9 schema struct<> -- !query 9 output @@ -88,7 +93,7 @@ struct<> -- !query 10 -INSERT INTO department VALUES (7, 5, 'G') +INSERT INTO department VALUES (6, 4, 'F') -- !query 10 schema struct<> -- !query 10 output @@ -96,10 +101,7 @@ struct<> -- !query 11 -CREATE TABLE tree( - id INTEGER, - parent_id INTEGER -) USING parquet +INSERT INTO department VALUES (7, 5, 'G') -- !query 11 schema struct<> -- !query 11 output @@ -107,9 +109,10 @@ struct<> -- !query 12 -INSERT INTO tree -VALUES (1, NULL), (2, 1), (3,1), (4,2), (5,2), (6,2), (7,3), (8,3), - (9,4), (10,4), (11,7), (12,7), (13,7), (14, 9), (15,11), (16,11) +CREATE TABLE tree( + id INTEGER, + parent_id INTEGER +) USING parquet -- !query 12 schema struct<> -- !query 12 output @@ -117,7 +120,9 @@ struct<> -- !query 13 -create table graph( f int, t int, label string ) USING parquet +INSERT INTO tree +VALUES (1, NULL), (2, 1), (3,1), (4,2), (5,2), (6,2), (7,3), (8,3), + (9,4), (10,4), (11,7), (12,7), (13,7), (14, 9), (15,11), (16,11) -- !query 13 schema struct<> -- !query 13 output @@ -125,13 +130,7 @@ struct<> -- !query 14 -insert into graph values - (1, 2, 'arc 1 -> 2'), - (1, 3, 'arc 1 -> 3'), - (2, 3, 'arc 2 -> 3'), - (1, 4, 'arc 1 -> 4'), - (4, 5, 'arc 4 -> 5'), - (5, 1, 'arc 5 -> 1') +create table graph( f int, t int, label string ) USING parquet -- !query 14 schema struct<> -- !query 14 output @@ -139,7 +138,13 @@ struct<> -- !query 15 -CREATE TABLE y (a INTEGER) USING parquet +insert into graph values + (1, 2, 'arc 1 -> 2'), + (1, 3, 'arc 1 -> 3'), + (2, 3, 'arc 2 -> 3'), + (1, 4, 'arc 1 -> 4'), + (4, 5, 'arc 4 -> 5'), + (5, 1, 'arc 5 -> 1') -- !query 15 schema struct<> -- !query 15 output @@ -147,7 +152,7 @@ struct<> -- !query 16 -INSERT INTO y SELECT EXPLODE(SEQUENCE(1, 10)) +CREATE TABLE y (a INTEGER) USING parquet -- !query 16 schema struct<> -- !query 16 output @@ -155,7 +160,7 @@ struct<> -- !query 17 -DROP TABLE y +INSERT INTO y SELECT EXPLODE(SEQUENCE(1, 10)) -- !query 17 schema struct<> -- !query 17 output @@ -163,7 +168,7 @@ struct<> -- !query 18 -CREATE TABLE y (a INTEGER) USING parquet +DROP TABLE y -- !query 18 schema struct<> -- !query 18 output @@ -171,7 +176,7 @@ struct<> -- !query 19 -INSERT INTO y SELECT EXPLODE(SEQUENCE(1, 10)) +CREATE TABLE y (a INTEGER) USING parquet -- !query 19 schema struct<> -- !query 19 output @@ -179,26 +184,31 @@ struct<> -- !query 20 -with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q +INSERT INTO y SELECT EXPLODE(SEQUENCE(1, 10)) -- !query 20 schema -struct +struct<> -- !query 20 output -42 + -- !query 21 -CREATE TABLE bug6051 USING parquet AS - SELECT EXPLODE(SEQUENCE(1,3)) AS i +with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q -- !query 21 schema -struct<> +struct -- !query 21 output - +42 -- !query 22 -SELECT * FROM bug6051 +WITH outermost(x) AS ( + SELECT 1 + UNION (WITH innermost as (SELECT 2) + SELECT * FROM innermost + UNION SELECT 3) +) +SELECT * FROM outermost ORDER BY 1 -- !query 22 schema -struct +struct -- !query 22 output 1 2 @@ -206,11 +216,18 @@ struct -- !query 23 -CREATE TABLE bug6051_2 (i int) USING parquet +WITH outermost(x) AS ( + SELECT 1 + UNION (WITH innermost as (SELECT 2) + SELECT * FROM outermost -- fail + UNION SELECT * FROM innermost) +) +SELECT * FROM outermost ORDER BY 1 -- !query 23 schema struct<> -- !query 23 output - +org.apache.spark.sql.AnalysisException +Table or view not found: outermost; line 4 pos 23 -- !query 24 @@ -447,7 +464,7 @@ struct<> -- !query 50 -DROP TABLE bug6051 +DROP TABLE yy -- !query 50 schema struct<> -- !query 50 output @@ -455,32 +472,8 @@ struct<> -- !query 51 -DROP TABLE bug6051_2 +DROP TABLE parent -- !query 51 schema struct<> -- !query 51 output - - --- !query 52 -DROP TABLE yy --- !query 52 schema -struct<> --- !query 52 output - - - --- !query 53 -DROP TABLE parent --- !query 53 schema -struct<> --- !query 53 output - - - --- !query 54 -set spark.sql.crossJoin.enabled=false --- !query 54 schema -struct --- !query 54 output -spark.sql.crossJoin.enabled false From 4a356e55bff2c6919d45c4217f12a88bd28c16a5 Mon Sep 17 00:00:00 2001 From: Peter Toth Date: Mon, 8 Jul 2019 14:40:19 +0200 Subject: [PATCH 7/8] minor fix --- sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql b/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql index 90fb7c12f3ca..692a8cf4e479 100644 --- a/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql +++ b/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql @@ -885,8 +885,9 @@ SELECT * FROM outermost ORDER BY 1; -- --SELECT * FROM bug6051; +-- [NOTE] Spark SQL doesn't support RULEs --CREATE TEMP TABLE bug6051_2 (i int); - +-- --CREATE RULE bug6051_ins AS ON INSERT TO bug6051 DO INSTEAD -- INSERT INTO bug6051_2 -- SELECT NEW.i; From 615f59273f2ddd1d167627f3e6c62249adca684d Mon Sep 17 00:00:00 2001 From: Peter Toth Date: Thu, 11 Jul 2019 08:52:21 +0200 Subject: [PATCH 8/8] remove todos, set ansi enabled --- .../resources/sql-tests/inputs/pgSQL/with.sql | 20 ++-- .../sql-tests/results/pgSQL/with.sql.out | 96 +++++++++++-------- 2 files changed, 65 insertions(+), 51 deletions(-) diff --git a/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql b/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql index 692a8cf4e479..ff6055ff59bf 100644 --- a/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql +++ b/sql/core/src/test/resources/sql-tests/inputs/pgSQL/with.sql @@ -244,9 +244,8 @@ INSERT INTO department VALUES (7, 5, 'G'); -- SELECT * FROM subdepartment; -- --SELECT * FROM vsubdepartment ORDER BY name; - --- Check reverse listing --- [TODO] is there a way to show DDL of a view? +-- +---- Check reverse listing --SELECT pg_get_viewdef('vsubdepartment'::regclass); --SELECT pg_get_viewdef('vsubdepartment'::regclass, true); @@ -259,8 +258,7 @@ INSERT INTO department VALUES (7, 5, 'G'); -- SELECT n+1 FROM t WHERE n < 100 --) --SELECT sum(n) FROM t; - --- [TODO] is there a way to show DDL of a view? +-- --\d+ sums_1_100 -- corner case in which sub-WITH gets initialized first @@ -465,10 +463,10 @@ INSERT INTO y SELECT EXPLODE(SEQUENCE(1, 10)); --SELECT * FROM y; -- [NOTE] Spark SQL doesn't support UPDATE statement -----WITH t AS ( ----- SELECT a FROM y -----) -----UPDATE y SET a = y.a-10 FROM t WHERE y.a > 20 AND t.a = y.a RETURNING y.a; +--WITH t AS ( +-- SELECT a FROM y +--) +--UPDATE y SET a = y.a-10 FROM t WHERE y.a > 20 AND t.a = y.a RETURNING y.a; -- --SELECT * FROM y; @@ -1184,10 +1182,10 @@ SELECT * FROM parent; --DROP RULE y_rule ON y; -- check that parser lookahead for WITH doesn't cause any odd behavior --- [TODO] WITH should be a reserved keyword? +set spark.sql.parser.ansi.enabled=true; create table foo (with baz); -- fail, WITH is a reserved word --- [TODO] WITH should be a reserved keyword? create table foo (with ordinality); -- fail, WITH is a reserved word +set spark.sql.parser.ansi.enabled=false; with ordinality as (select 1 as x) select * from ordinality; -- check sane response to attempt to modify CTE relation diff --git a/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out b/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out index c548922572b8..c68ee0e4753e 100644 --- a/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out +++ b/sql/core/src/test/resources/sql-tests/results/pgSQL/with.sql.out @@ -1,5 +1,5 @@ -- Automatically generated by SQLQueryTestSuite --- Number of queries: 52 +-- Number of queries: 54 -- !query 0 @@ -352,79 +352,79 @@ struct -- !query 38 -create table foo (with baz) +set spark.sql.parser.ansi.enabled=true -- !query 38 schema -struct<> +struct -- !query 38 output -org.apache.spark.sql.catalyst.parser.ParseException - -DataType baz is not supported.(line 1, pos 23) - -== SQL == -create table foo (with baz) ------------------------^^^ +spark.sql.parser.ansi.enabled true -- !query 39 --- fail, WITH is a reserved word -create table foo (with ordinality) +create table foo (with baz) -- !query 39 schema struct<> -- !query 39 output org.apache.spark.sql.catalyst.parser.ParseException -DataType ordinality is not supported.(line 2, pos 23) +no viable alternative at input 'with'(line 1, pos 18) == SQL == --- fail, WITH is a reserved word -create table foo (with ordinality) ------------------------^^^ +create table foo (with baz) +------------------^^^ -- !query 40 -- fail, WITH is a reserved word -with ordinality as (select 1 as x) select * from ordinality +create table foo (with ordinality) -- !query 40 schema -struct +struct<> -- !query 40 output -1 +org.apache.spark.sql.catalyst.parser.ParseException + +no viable alternative at input 'with'(line 2, pos 18) + +== SQL == +-- fail, WITH is a reserved word +create table foo (with ordinality) +------------------^^^ -- !query 41 -WITH test AS (SELECT 42) INSERT INTO test VALUES (1) +-- fail, WITH is a reserved word +set spark.sql.parser.ansi.enabled=false -- !query 41 schema -struct<> +struct -- !query 41 output -org.apache.spark.sql.AnalysisException -Table not found: test; +spark.sql.parser.ansi.enabled false -- !query 42 -create table test (i int) USING parquet +with ordinality as (select 1 as x) select * from ordinality -- !query 42 schema -struct<> +struct -- !query 42 output - +1 -- !query 43 -with test as (select 42) insert into test select * from test +WITH test AS (SELECT 42) INSERT INTO test VALUES (1) -- !query 43 schema struct<> -- !query 43 output - +org.apache.spark.sql.AnalysisException +Table not found: test; -- !query 44 -select * from test +create table test (i int) USING parquet -- !query 44 schema -struct +struct<> -- !query 44 output -42 + -- !query 45 -drop table test +with test as (select 42) insert into test select * from test -- !query 45 schema struct<> -- !query 45 output @@ -432,15 +432,15 @@ struct<> -- !query 46 -DROP TABLE department +select * from test -- !query 46 schema -struct<> +struct -- !query 46 output - +42 -- !query 47 -DROP TABLE tree +drop table test -- !query 47 schema struct<> -- !query 47 output @@ -448,7 +448,7 @@ struct<> -- !query 48 -DROP TABLE graph +DROP TABLE department -- !query 48 schema struct<> -- !query 48 output @@ -456,7 +456,7 @@ struct<> -- !query 49 -DROP TABLE y +DROP TABLE tree -- !query 49 schema struct<> -- !query 49 output @@ -464,7 +464,7 @@ struct<> -- !query 50 -DROP TABLE yy +DROP TABLE graph -- !query 50 schema struct<> -- !query 50 output @@ -472,8 +472,24 @@ struct<> -- !query 51 -DROP TABLE parent +DROP TABLE y -- !query 51 schema struct<> -- !query 51 output + + +-- !query 52 +DROP TABLE yy +-- !query 52 schema +struct<> +-- !query 52 output + + + +-- !query 53 +DROP TABLE parent +-- !query 53 schema +struct<> +-- !query 53 output +