-- -- Tests for common table expressions (WITH query, ... SELECT ...) --
-- Basic WITH WITH q1(x,y) AS (SELECT1,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) UNIONALL SELECT n+1FROM t WHERE n < 100
) SELECT sum(n) FROM t;
WITH RECURSIVE t(n) AS ( SELECT (VALUES(1)) UNIONALL SELECT n+1FROM t WHERE n < 5
) SELECT * FROM t;
-- UNION DISTINCT requires hashable type WITH RECURSIVE t(n) AS ( VALUES ('01'::varbit) UNION SELECT n || '10'::varbit FROM t WHERE n < '100'::varbit
) SELECT n FROM t;
-- recursive view CREATE RECURSIVE VIEW nums (n) AS VALUES (1) UNIONALL SELECT n+1FROM nums WHERE n < 5;
SELECT * FROM nums;
CREATEORREPLACE RECURSIVE VIEW nums (n) AS VALUES (1) UNIONALL SELECT n+1FROM nums WHERE n < 6;
SELECT * FROM nums;
-- This is an infinite loop with UNION ALL, but not with UNION WITH RECURSIVE t(n) AS ( SELECT1 UNION SELECT10-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) UNIONALL SELECT n+1FROM t) SELECT * FROM t LIMIT10;
-- UNION case should have same property WITH RECURSIVE t(n) AS ( SELECT1 UNION SELECT n+1FROM t) SELECT * FROM t LIMIT10;
-- Test behavior with an unknown-type literal in the WITH WITH q AS (SELECT'foo'AS x) SELECT x, pg_typeof(x) FROM q;
WITH RECURSIVE t(n) AS ( SELECT'foo' UNIONALL SELECT n || ' bar'FROM t WHERE length(n) < 20
) SELECT n, pg_typeof(n) 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' UNIONALL SELECT n+1FROM t WHERE n < 10
) SELECT n, pg_typeof(n) FROM t;
-- Deeply nested WITH caused a list-munging problem in v13 -- Detection of cross-references and self-references WITH RECURSIVE w1(c1) AS
(WITH w2(c2) AS
(WITH w3(c3) AS
(WITH w4(c4) AS
(WITH w5(c5) AS
(WITH RECURSIVE w6(c6) AS
(WITH w6(c6) AS
(WITH w8(c8) AS
(SELECT1) SELECT * FROM w8) SELECT * FROM w6) SELECT * FROM w6) SELECT * FROM w5) SELECT * FROM w4) SELECT * FROM w3) SELECT * FROM w2) SELECT * FROM w1; -- Detection of invalid self-references WITH RECURSIVE outermost(x) AS ( SELECT1 UNION (WITH innermost1 AS ( SELECT2 UNION (WITH innermost2 AS ( SELECT3 UNION (WITH innermost3 AS ( SELECT4 UNION (WITH innermost4 AS ( SELECT5 UNION (WITH innermost5 AS ( SELECT6 UNION (WITH innermost6 AS
(SELECT7) SELECT * FROM innermost6)) SELECT * FROM innermost5)) SELECT * FROM innermost4)) SELECT * FROM innermost3)) SELECT * FROM innermost2)) SELECT * FROM outermost UNIONSELECT * FROM innermost1)
) SELECT * FROM outermost ORDERBY1;
-- -- 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 INTEGERPRIMARYKEY, -- department ID
parent_department INTEGERREFERENCES department, -- upper department ID
name TEXT -- department name
);
INSERTINTO department VALUES (0, NULL, 'ROOT'); INSERTINTO department VALUES (1, 0, 'A'); INSERTINTO department VALUES (2, 1, 'B'); INSERTINTO department VALUES (3, 2, 'C'); INSERTINTO department VALUES (4, 2, 'D'); INSERTINTO department VALUES (5, 0, 'E'); INSERTINTO department VALUES (6, 4, 'F'); INSERTINTO 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'
UNIONALL
-- recursive term SELECT sd.root_name, d.* FROM department AS d, subdepartment AS sd WHERE d.parent_department = sd.id
) SELECT * FROM subdepartment ORDERBY name;
-- extract all departments under 'A' with "level" number WITH RECURSIVE subdepartment(level, id, parent_department, name) AS
( -- non recursive term SELECT1, * FROM department WHERE name = 'A'
UNIONALL
-- recursive term SELECT sd.level + 1, d.* FROM department AS d, subdepartment AS sd WHERE d.parent_department = sd.id
) SELECT * FROM subdepartment ORDERBY 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 SELECT1, * FROM department WHERE name = 'A'
UNIONALL
-- 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 >= 2ORDERBY 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 ORDERBY name;
-- exercise the deduplication code of a UNION with mixed input slot types WITH RECURSIVE subdepartment AS
( -- select all columns to prevent projection SELECT id, parent_department, name FROM department WHERE name = 'A'
UNION
-- joins do projection SELECT d.id, d.parent_department, d.name FROM department AS d INNERJOIN subdepartment AS sd ON d.parent_department = sd.id
) SELECT * FROM subdepartment ORDERBY name;
-- inside subqueries SELECT count(*) FROM ( WITH RECURSIVE t(n) AS ( SELECT1UNIONALLSELECT n + 1FROM t WHERE n < 500
) SELECT * FROM t) AS t WHERE n < ( SELECT count(*) FROM ( WITH RECURSIVE t(n) AS ( SELECT1UNIONALLSELECT n + 1FROM 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 GROUPBY hundred
) SELECT count(*) FROM q1 WHERE y > (SELECT sum(y)/100FROM q1 qsub);
-- via a VIEW CREATE TEMPORARY VIEW vsubdepartment AS WITH RECURSIVE subdepartment AS
( -- non recursive term SELECT * FROM department WHERE name = 'A' UNIONALL -- recursive term SELECT d.* FROM department AS d, subdepartment AS sd WHERE d.parent_department = sd.id
) SELECT * FROM subdepartment;
-- Another reverse-listing example CREATE VIEW sums_1_100 AS WITH RECURSIVE t(n) AS ( VALUES (1) UNIONALL SELECT n+1FROM 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 unionall
(with x as (select * from q) select * from x)
) select * from q limit24;
with recursive q as ( select * from department unionall
(with recursive x as ( select * from department unionall
(select * from q unionallselect * from x)
) select * from x)
) select * from q limit32;
-- recursive term has sub-UNION WITH RECURSIVE t(i,j) AS ( VALUES (1,2) UNIONALL SELECT t2.i, t.j+1FROM
(SELECT2AS i UNIONALLSELECT3AS i) AS t2 JOIN t ON (t2.i = t.i+1))
SELECT * FROM t;
-- -- different tree example -- CREATE TEMPORARY TABLE tree(
id INTEGERPRIMARYKEY,
parent_id INTEGERREFERENCES tree(id)
);
-- -- get all paths from "second level" nodes to leaf nodes -- WITH RECURSIVE t(id, path) AS ( VALUES(1,ARRAY[]::integer[]) UNIONALL 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) = 1AND
array_upper(t2.path,1) > 1) ORDERBY t1.id, t2.id;
-- just count 'em WITH RECURSIVE t(id, path) AS ( VALUES(1,ARRAY[]::integer[]) UNIONALL 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) = 1AND
array_upper(t2.path,1) > 1) GROUPBY t1.id ORDERBY t1.id;
-- this variant tickled a whole-row-variable bug in 8.4devel WITH RECURSIVE t(id, path) AS ( VALUES(1,ARRAY[]::integer[]) UNIONALL 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);
CREATE TEMP TABLE duplicates (a INTNOTNULL); INSERTINTO duplicates VALUES(1), (1);
-- Try out a recursive UNION case where the non-recursive part's table slot -- uses TTSOpsBufferHeapTuple and contains duplicate rows. WITH RECURSIVE cte (a) as ( SELECT a FROM duplicates UNION SELECT a FROM cte
) SELECT a FROM cte;
-- test that column statistics from a materialized CTE are available -- to upper planner (otherwise, we'd get a stupider plan) explain (costs off) with x as materialized (select unique1 from tenk1 b) select count(*) from tenk1 a where unique1 in (select * from x);
explain (costs off) with x as materialized (insertinto tenk1 defaultvalues returning unique1) select count(*) from tenk1 a where unique1 in (select * from x);
-- test that pathkeys from a materialized CTE are propagated up to the -- outer query explain (costs off) with x as materialized (select unique1 from tenk1 b orderby unique1) select count(*) from tenk1 a where unique1 in (select * from x);
-- SEARCH clause
create temp table graph0( f int, t int, label text );
explain (verbose, costs off) with recursive search_graph(f, t, label) as ( select * from graph0 g unionall select g.* from graph0 g, search_graph sg where g.f = sg.t
) search depth first by f, t set seq select * from search_graph orderby seq;
with recursive search_graph(f, t, label) as ( select * from graph0 g unionall select g.* from graph0 g, search_graph sg where g.f = sg.t
) search depth first by f, t set seq select * from search_graph orderby seq;
with recursive search_graph(f, t, label) as ( select * from graph0 g uniondistinct select g.* from graph0 g, search_graph sg where g.f = sg.t
) search depth first by f, t set seq select * from search_graph orderby seq;
explain (verbose, costs off) with recursive search_graph(f, t, label) as ( select * from graph0 g unionall select g.* from graph0 g, search_graph sg where g.f = sg.t
) search breadth first by f, t set seq select * from search_graph orderby seq;
with recursive search_graph(f, t, label) as ( select * from graph0 g unionall select g.* from graph0 g, search_graph sg where g.f = sg.t
) search breadth first by f, t set seq select * from search_graph orderby seq;
with recursive search_graph(f, t, label) as ( select * from graph0 g uniondistinct select g.* from graph0 g, search_graph sg where g.f = sg.t
) search breadth first by f, t set seq select * from search_graph orderby seq;
-- a constant initial value causes issues for EXPLAIN explain (verbose, costs off) with recursive test as ( select1as x unionall select x + 1 from test
) search depth first by x set y select * from test limit5;
with recursive test as ( select1as x unionall select x + 1 from test
) search depth first by x set y select * from test limit5;
explain (verbose, costs off) with recursive test as ( select1as x unionall select x + 1 from test
) search breadth first by x set y select * from test limit5;
with recursive test as ( select1as x unionall select x + 1 from test
) search breadth first by x set y select * from test limit5;
-- various syntax errors with recursive search_graph(f, t, label) as ( select * from graph0 g unionall select g.* from graph0 g, search_graph sg where g.f = sg.t
) search depth first by foo, tar set seq select * from search_graph;
with recursive search_graph(f, t, label) as ( select * from graph0 g unionall select g.* from graph0 g, search_graph sg where g.f = sg.t
) search depth first by f, t setlabel select * from search_graph;
with recursive search_graph(f, t, label) as ( select * from graph0 g unionall select g.* from graph0 g, search_graph sg where g.f = sg.t
) search depth first by f, t, f set seq select * from search_graph;
with recursive search_graph(f, t, label) as ( select * from graph0 g unionall select * from graph0 g unionall select g.* from graph0 g, search_graph sg where g.f = sg.t
) search depth first by f, t set seq select * from search_graph orderby seq;
with recursive search_graph(f, t, label) as ( select * from graph0 g unionall
(select * from graph0 g unionall select g.* from graph0 g, search_graph sg where g.f = sg.t)
) search depth first by f, t set seq select * from search_graph orderby seq;
-- check that we distinguish same CTE name used at different levels -- (this case could be supported, perhaps, but it isn't today) with recursive x(col) as ( select1 union
(with x as (select * from x) select * from x)
) search depth first by col set seq select * from x;
-- test ruleutils and view expansion create temp view v_search as with recursive search_graph(f, t, label) as ( select * from graph0 g unionall select g.* from graph0 g, search_graph sg where g.f = sg.t
) search depth first by f, t set seq select f, t, labelfrom search_graph;
select pg_get_viewdef('v_search');
select * from v_search;
-- -- test cycle detection -- create temp table graph( f int, t int, label text );
with recursive search_graph(f, t, label, is_cycle, path) as ( select *, false, array[row(g.f, g.t)] from graph g unionall select g.*, row(g.f, g.t) = any(path), path || row(g.f, g.t) from graph g, search_graph sg where g.f = sg.t andnot is_cycle
) select * from search_graph;
-- UNION DISTINCT exercises row type hashing support with recursive search_graph(f, t, label, is_cycle, path) as ( select *, false, array[row(g.f, g.t)] from graph g uniondistinct select g.*, row(g.f, g.t) = any(path), path || row(g.f, g.t) from graph g, search_graph sg where g.f = sg.t andnot is_cycle
) select * from search_graph;
-- ordering by the path column has same effect as SEARCH DEPTH FIRST with recursive search_graph(f, t, label, is_cycle, path) as ( select *, false, array[row(g.f, g.t)] from graph g unionall select g.*, row(g.f, g.t) = any(path), path || row(g.f, g.t) from graph g, search_graph sg where g.f = sg.t andnot is_cycle
) select * from search_graph orderby path;
-- CYCLE clause
explain (verbose, costs off) with recursive search_graph(f, t, label) as ( select * from graph g unionall select g.* from graph g, search_graph sg where g.f = sg.t
) cycle f, t set is_cycle using path select * from search_graph;
with recursive search_graph(f, t, label) as ( select * from graph g unionall select g.* from graph g, search_graph sg where g.f = sg.t
) cycle f, t set is_cycle using path select * from search_graph;
with recursive search_graph(f, t, label) as ( select * from graph g uniondistinct select g.* from graph g, search_graph sg where g.f = sg.t
) cycle f, t set is_cycle to'Y'default'N'using path select * from search_graph;
explain (verbose, costs off) with recursive test as ( select0as x unionall select (x + 1) % 10 from test
) cycle x set is_cycle using path select * from test;
with recursive test as ( select0as x unionall select (x + 1) % 10 from test
) cycle x set is_cycle using path select * from test;
with recursive test as ( select0as x unionall select (x + 1) % 10 from test wherenot is_cycle -- redundant, but legal
) cycle x set is_cycle using path select * from test;
-- multiple CTEs with recursive
graph(f, t, label) as ( 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')
),
search_graph(f, t, label) as ( select * from graph g unionall select g.* from graph g, search_graph sg where g.f = sg.t
) cycle f, t set is_cycle totruedefaultfalseusing path select f, t, labelfrom search_graph;
-- star expansion with recursive a as ( select1as b unionall select * from a
) cycle b set c using p select * from a;
-- search+cycle with recursive search_graph(f, t, label) as ( select * from graph g unionall select g.* from graph g, search_graph sg where g.f = sg.t
) search depth first by f, t set seq
cycle f, t set is_cycle using path select * from search_graph;
with recursive search_graph(f, t, label) as ( select * from graph g unionall select g.* from graph g, search_graph sg where g.f = sg.t
) search breadth first by f, t set seq
cycle f, t set is_cycle using path select * from search_graph;
-- various syntax errors with recursive search_graph(f, t, label) as ( select * from graph g unionall select g.* from graph g, search_graph sg where g.f = sg.t
) cycle foo, tar set is_cycle using path select * from search_graph;
with recursive search_graph(f, t, label) as ( select * from graph g unionall select g.* from graph g, search_graph sg where g.f = sg.t
) cycle f, t set is_cycle totruedefault55using path select * from search_graph;
with recursive search_graph(f, t, label) as ( select * from graph g unionall select g.* from graph g, search_graph sg where g.f = sg.t
) cycle f, t set is_cycle to point '(1,1)'default point '(0,0)'using path select * from search_graph;
with recursive search_graph(f, t, label) as ( select * from graph g unionall select g.* from graph g, search_graph sg where g.f = sg.t
) cycle f, t setlabeltotruedefaultfalseusing path select * from search_graph;
with recursive search_graph(f, t, label) as ( select * from graph g unionall select g.* from graph g, search_graph sg where g.f = sg.t
) cycle f, t set is_cycle totruedefaultfalseusinglabel select * from search_graph;
with recursive search_graph(f, t, label) as ( select * from graph g unionall select g.* from graph g, search_graph sg where g.f = sg.t
) cycle f, t set foo totruedefaultfalseusing foo select * from search_graph;
with recursive search_graph(f, t, label) as ( select * from graph g unionall select g.* from graph g, search_graph sg where g.f = sg.t
) cycle f, t, f set is_cycle totruedefaultfalseusing path select * from search_graph;
with recursive search_graph(f, t, label) as ( select * from graph g unionall select g.* from graph g, search_graph sg where g.f = sg.t
) search depth first by f, t set foo
cycle f, t set foo totruedefaultfalseusing path select * from search_graph;
with recursive search_graph(f, t, label) as ( select * from graph g unionall select g.* from graph g, search_graph sg where g.f = sg.t
) search depth first by f, t set foo
cycle f, t set is_cycle totruedefaultfalseusing foo select * from search_graph;
-- test ruleutils and view expansion create temp view v_cycle1 as with recursive search_graph(f, t, label) as ( select * from graph g unionall select g.* from graph g, search_graph sg where g.f = sg.t
) cycle f, t set is_cycle using path select f, t, labelfrom search_graph;
create temp view v_cycle2 as with recursive search_graph(f, t, label) as ( select * from graph g unionall select g.* from graph g, search_graph sg where g.f = sg.t
) cycle f, t set is_cycle to'Y'default'N'using path select f, t, labelfrom search_graph;
-- -- test multiple WITH queries -- WITH RECURSIVE
y (id) AS (VALUES (1)),
x (id) AS (SELECT * FROM y UNIONALLSELECT id+1FROM x WHERE id < 5) SELECT * FROM x;
-- forward reference OK WITH RECURSIVE
x(id) AS (SELECT * FROM y UNIONALLSELECT id+1FROM x WHERE id < 5),
y(id) AS (values (1)) SELECT * FROM x;
WITH RECURSIVE
x(id) AS
(VALUES (1) UNIONALLSELECT id+1FROM x WHERE id < 5),
y(id) AS
(VALUES (1) UNIONALLSELECT id+1FROM y WHERE id < 10) SELECT y.*, x.* FROM y LEFTJOIN x USING (id);
WITH RECURSIVE
x(id) AS
(VALUES (1) UNIONALLSELECT id+1FROM x WHERE id < 5),
y(id) AS
(VALUES (1) UNIONALLSELECT id+1FROM x WHERE id < 10) SELECT y.*, x.* FROM y LEFTJOIN x USING (id);
WITH RECURSIVE
x(id) AS
(SELECT1UNIONALLSELECT id+1FROM x WHERE id < 3 ),
y(id) AS
(SELECT * FROM x UNIONALLSELECT * FROM x),
z(id) AS
(SELECT * FROM x UNIONALLSELECT id+1FROM z WHERE id < 10) SELECT * FROM z;
WITH RECURSIVE
x(id) AS
(SELECT1UNIONALLSELECT id+1FROM x WHERE id < 3 ),
y(id) AS
(SELECT * FROM x UNIONALLSELECT * FROM x),
z(id) AS
(SELECT * FROM y UNIONALLSELECT id+1FROM z WHERE id < 10) SELECT * FROM z;
-- -- Test WITH attached to a data-modifying statement --
CREATE TEMPORARY TABLE y (a INTEGER); INSERTINTO y SELECT generate_series(1, 10);
WITH t AS ( SELECT a FROM y
) INSERTINTO y SELECT a+20FROM t RETURNING *;
SELECT * FROM y;
WITH t AS ( SELECT a FROM y
) UPDATE y SET a = y.a-10FROM t WHERE y.a > 20AND t.a = y.a RETURNING y.a;
SELECT * FROM y;
WITH RECURSIVE t(a) AS ( SELECT11 UNIONALL SELECT a+1FROM t WHERE a < 50
) DELETEFROM y USING t WHERE t.a = y.a RETURNING y.a;
SELECT * FROM y;
DROPTABLE y;
-- -- error cases --
WITH x(n, b) AS (SELECT1) SELECT * FROM x;
-- INTERSECT WITH RECURSIVE x(n) AS (SELECT1 INTERSECT SELECT n+1FROM x) SELECT * FROM x;
WITH RECURSIVE x(n) AS (SELECT1 INTERSECT ALLSELECT n+1FROM x) SELECT * FROM x;
-- EXCEPT WITH RECURSIVE x(n) AS (SELECT1 EXCEPT SELECT n+1FROM x) SELECT * FROM x;
WITH RECURSIVE x(n) AS (SELECT1 EXCEPT ALLSELECT n+1FROM 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 UNIONALLSELECT1) SELECT * FROM x;
-- allow this, because we historically have WITH RECURSIVE x(n) AS ( WITH x1 AS (SELECT1AS n) SELECT0 UNION SELECT * FROM x1) SELECT * FROM x;
-- but this should be rejected WITH RECURSIVE x(n) AS ( WITH x1 AS (SELECT1FROM x) SELECT0 UNION SELECT * FROM x1) SELECT * FROM x;
-- and this too WITH RECURSIVE x(n) AS (
(WITH x1 AS (SELECT1FROM x) SELECT * FROM x1) UNION SELECT0) SELECT * FROM x;
-- and this WITH RECURSIVE x(n) AS ( SELECT0UNIONSELECT1 ORDERBY (SELECT n FROM x)) SELECT * FROM x;
-- and this WITH RECURSIVE x(n) AS ( WITH sub_cte AS (SELECT * FROM x) DELETEFROM graph RETURNING f) SELECT * FROM x;
CREATE TEMPORARY TABLE y (a INTEGER); INSERTINTO y SELECT generate_series(1, 10);
-- LEFT JOIN
WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 UNIONALL SELECT x.n+1FROM y LEFTJOIN 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 UNIONALL SELECT x.n+1FROM x RIGHTJOIN 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 UNIONALL SELECT x.n+1FROM x FULL JOIN y ON x.n = y.a WHERE n < 10) SELECT * FROM x;
-- subquery WITH RECURSIVE x(n) AS (SELECT1UNIONALLSELECT n+1FROM x WHERE n IN (SELECT * FROM x)) SELECT * FROM x;
-- aggregate functions WITH RECURSIVE x(n) AS (SELECT1UNIONALLSELECT count(*) FROM x) SELECT * FROM x;
WITH RECURSIVE x(n) AS (SELECT1UNIONALLSELECT sum(n) FROM x) SELECT * FROM x;
-- ORDER BY WITH RECURSIVE x(n) AS (SELECT1UNIONALLSELECT n+1FROM x ORDERBY1) SELECT * FROM x;
-- LIMIT/OFFSET WITH RECURSIVE x(n) AS (SELECT1UNIONALLSELECT n+1FROM x LIMIT10 OFFSET 1) SELECT * FROM x;
-- FOR UPDATE WITH RECURSIVE x(n) AS (SELECT1UNIONALLSELECT n+1FROM x FORUPDATE) SELECT * FROM x;
-- target list has a recursive query name WITH RECURSIVE x(id) AS (values (1) UNIONALL SELECT (SELECT * FROM x) FROM x WHERE id < 5
) SELECT * FROM x;
-- mutual recursive query (not implemented) WITH RECURSIVE
x (id) AS (SELECT1UNIONALLSELECT id+1FROM y WHERE id < 5),
y (id) AS (SELECT1UNIONALLSELECT id+1FROM x WHERE id < 5) SELECT * FROM x;
-- non-linear recursion is not allowed WITH RECURSIVE foo(i) AS
(values (1) UNIONALL
(SELECT i+1FROM foo WHERE i < 10 UNIONALL SELECT i+1FROM foo WHERE i < 5)
) SELECT * FROM foo;
WITH RECURSIVE foo(i) AS
(values (1) UNIONALL SELECT * FROM
(SELECT i+1FROM foo WHERE i < 10 UNIONALL SELECT i+1FROM foo WHERE i < 5) AS t
) SELECT * FROM foo;
WITH RECURSIVE foo(i) AS
(values (1) UNIONALL
(SELECT i+1FROM foo WHERE i < 10
EXCEPT SELECT i+1FROM foo WHERE i < 5)
) SELECT * FROM foo;
WITH RECURSIVE foo(i) AS
(values (1) UNIONALL
(SELECT i+1FROM foo WHERE i < 10
INTERSECT SELECT i+1FROM 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) UNIONALL 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) UNIONALL 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 ASONUPDATETO 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 ( select42 ) 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 bug #19055: interaction of WITH with aggregates -- -- For now, we just throw an error if there's a use of a CTE below the -- semantic level that the SQL standard assigns to the aggregate. -- It's not entirely clear what we could do instead that doesn't risk -- breaking more things than it fixes. select f1, (with cte1(x,y) as (select1,2) select count((select i4.f1 from cte1))) as ss from int4_tbl i4;
-- -- test for bug #19106: interaction of WITH with aggregates -- -- the initial fix for #19055 was too aggressive and broke this case explain (verbose, costs off) with a as ( select id from (values (1), (2)) as v(id) ),
b as ( select max((select sum(id) from a)) as agg ) select agg from b;
with a as ( select id from (values (1), (2)) as v(id) ),
b as ( select max((select sum(id) from a)) as agg ) select agg from b;
-- -- test for nested-recursive-WITH bug -- WITH RECURSIVE t(j) AS ( WITH RECURSIVE s(i) AS ( VALUES (1) UNIONALL SELECT i+1FROM s WHERE i < 10
) SELECT i FROM s UNIONALL SELECT j+1FROM t WHERE j < 10
) SELECT * FROM t;
-- -- test WITH attached to intermediate-level set operation --
WITH outermost(x) AS ( SELECT1 UNION (WITH innermost as (SELECT2) SELECT * FROM innermost UNIONSELECT3)
) SELECT * FROM outermost ORDERBY1;
WITH outermost(x) AS ( SELECT1 UNION (WITH innermost as (SELECT2) SELECT * FROM outermost -- fail UNIONSELECT * FROM innermost)
) SELECT * FROM outermost ORDERBY1;
WITH RECURSIVE outermost(x) AS ( SELECT1 UNION (WITH innermost as (SELECT2) SELECT * FROM outermost UNIONSELECT * FROM innermost)
) SELECT * FROM outermost ORDERBY1;
WITH RECURSIVE outermost(x) AS ( WITH innermost as (SELECT2FROM outermost) -- fail SELECT * FROM innermost UNIONSELECT * from outermost
) SELECT * FROM outermost ORDERBY1;
-- -- 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 ( SELECT0, 'base', 17 UNIONALL ( WITH remaining(id_key, row_type, link, min) AS ( SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER () FROM tab INNERJOIN 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 INNERJOIN tab ON e.id_key=tab.id_key WHERE e.row_type = 'false'
) SELECT * FROM first_remaining UNIONALLSELECT * 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 ( SELECT0, '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 INNERJOIN 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 INNERJOIN tab ON e.id_key=tab.id_key WHERE e.row_type = 'false'
) SELECT * FROM first_remaining UNIONALLSELECT * FROM effect
)
) SELECT * FROM iter;
-- -- Data-modifying statements in WITH --
-- INSERT ... RETURNING WITH t AS ( INSERTINTO 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 ( DELETEFROM y WHERE a <= 10
RETURNING *
) SELECT * FROM t;
SELECT * FROM y;
-- forward reference WITH RECURSIVE t AS ( INSERTINTO y SELECT a+5FROM t2 WHERE a > 5
RETURNING *
), t2 AS ( UPDATE y SET a=a-11 RETURNING *
) SELECT * FROM t UNIONALL SELECT * FROM t2;
SELECT * FROM y;
-- unconditional DO INSTEAD rule CREATE RULE y_rule ASONDELETETO y DO INSTEAD INSERTINTO y VALUES(42) RETURNING *;
WITH t AS ( DELETEFROM 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 ( DELETEFROM bug6051 RETURNING * ) INSERTINTO bug6051 SELECT * FROM t1;
SELECT * FROM bug6051;
CREATE TEMP TABLE bug6051_2 (i int);
CREATE RULE bug6051_ins ASONINSERTTO bug6051 DO INSTEAD INSERTINTO bug6051_2 VALUES(NEW.i);
WITH t1 AS ( DELETEFROM bug6051 RETURNING * ) INSERTINTO bug6051 SELECT * FROM t1;
SELECT * FROM bug6051; SELECT * FROM bug6051_2;
-- check INSERT ... SELECT rule actions are disallowed on commands -- that have modifyingCTEs CREATEORREPLACE RULE bug6051_ins ASONINSERTTO bug6051 DO INSTEAD INSERTINTO bug6051_2 SELECT NEW.i;
WITH t1 AS ( DELETEFROM bug6051 RETURNING * ) INSERTINTO bug6051 SELECT * FROM t1;
-- silly example to verify that hasModifyingCTE flag is propagated CREATE TEMP TABLE bug6051_3 AS SELECT a FROM generate_series(11,13) AS a;
CREATE RULE bug6051_3_ins ASONINSERTTO bug6051_3 DO INSTEAD SELECT i FROM bug6051_2;
BEGIN; SET LOCAL debug_parallel_query = on;
WITH t1 AS ( DELETEFROM bug6051_3 RETURNING * ) INSERTINTO bug6051_3 SELECT * FROM t1;
COMMIT;
SELECT * FROM bug6051_3;
-- check that recursive CTE processing doesn't rewrite a CTE more than once -- (must not try to expand GENERATED ALWAYS IDENTITY columns more than once) CREATE TEMP TABLE id_alw1 (i int GENERATED ALWAYS AS IDENTITY);
CREATE TEMP TABLE id_alw2 (i int GENERATED ALWAYS AS IDENTITY); CREATE TEMP VIEW id_alw2_view ASSELECT * FROM id_alw2;
CREATE TEMP TABLE id_alw3 (i int GENERATED ALWAYS AS IDENTITY); CREATE RULE id_alw3_ins ASONINSERTTO id_alw3 DO INSTEAD WITH t1 AS (INSERTINTO id_alw1 DEFAULTVALUES RETURNING i) INSERTINTO id_alw2_view DEFAULTVALUES RETURNING i; CREATE TEMP VIEW id_alw3_view ASSELECT * FROM id_alw3;
CREATE TEMP TABLE id_alw4 (i int GENERATED ALWAYS AS IDENTITY);
WITH t4 AS (INSERTINTO id_alw4 DEFAULTVALUES RETURNING i) INSERTINTO id_alw3_view DEFAULTVALUES RETURNING i;
SELECT * from id_alw1; SELECT * from id_alw2; SELECT * from id_alw3; SELECT * from id_alw4;
-- check case where CTE reference is removed due to optimization EXPLAIN (VERBOSE, COSTS OFF) SELECT q1 FROM
( WITH t_cte AS (SELECT * FROM int8_tbl t) SELECT q1, (SELECT q2 FROM t_cte WHERE t_cte.q1 = i8.q1) AS t_sub FROM int8_tbl i8
) ss;
SELECT q1 FROM
( WITH t_cte AS (SELECT * FROM int8_tbl t) SELECT q1, (SELECT q2 FROM t_cte WHERE t_cte.q1 = i8.q1) AS t_sub FROM int8_tbl i8
) ss;
EXPLAIN (VERBOSE, COSTS OFF) SELECT q1 FROM
( WITH t_cte AS MATERIALIZED (SELECT * FROM int8_tbl t) SELECT q1, (SELECT q2 FROM t_cte WHERE t_cte.q1 = i8.q1) AS t_sub FROM int8_tbl i8
) ss;
SELECT q1 FROM
( WITH t_cte AS MATERIALIZED (SELECT * FROM int8_tbl t) SELECT q1, (SELECT q2 FROM t_cte WHERE t_cte.q1 = i8.q1) AS t_sub FROM int8_tbl i8
) ss;
-- a truly recursive CTE in the same list WITH RECURSIVE t(a) AS ( SELECT0 UNIONALL SELECT a+1FROM t WHERE a+1 < 5
), t2 as ( INSERTINTO y SELECT * FROM t RETURNING *
) SELECT * FROM t2 JOIN y USING (a) ORDERBY a;
SELECT * FROM y;
-- data-modifying WITH in a modifying statement WITH t AS ( DELETEFROM y WHERE a <= 10
RETURNING *
) INSERTINTO 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 LIMIT10;
SELECT * FROM y;
-- data-modifying WITH containing INSERT...ON CONFLICT DO UPDATE CREATETABLE withz ASSELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i; ALTERTABLE withz ADDUNIQUE (k);
WITH t AS ( INSERTINTO withz SELECT i, 'insert' FROM generate_series(0, 16) i ON CONFLICT (k) DO UPDATESET v = withz.v || ', now update'
RETURNING *
) SELECT * FROM t JOIN y ON t.k = y.a ORDERBY a, k;
-- Test EXCLUDED.* reference within CTE WITH aa AS ( INSERTINTO withz VALUES(1, 5) ON CONFLICT (k) DO UPDATESET v = EXCLUDED.v WHERE withz.k != EXCLUDED.k
RETURNING *
) SELECT * FROM aa;
-- New query/snapshot demonstrates side-effects of previous query. SELECT * FROM withz ORDERBY k;
-- -- Ensure subqueries within the update clause work, even if they -- reference outside values -- WITH aa AS (SELECT1 a, 2 b) INSERTINTO withz VALUES(1, 'insert') ON CONFLICT (k) DO UPDATESET v = (SELECT b || ' update'FROM aa WHERE a = 1LIMIT1); WITH aa AS (SELECT1 a, 2 b) INSERTINTO withz VALUES(1, 'insert') ON CONFLICT (k) DO UPDATESET v = ' update'WHERE withz.k = (SELECT a FROM aa); WITH aa AS (SELECT1 a, 2 b) INSERTINTO withz VALUES(1, 'insert') ON CONFLICT (k) DO UPDATESET v = (SELECT b || ' update'FROM aa WHERE a = 1LIMIT1); WITH aa AS (SELECT'a' a, 'b' b UNIONALLSELECT'a' a, 'b' b) INSERTINTO withz VALUES(1, 'insert') ON CONFLICT (k) DO UPDATESET v = (SELECT b || ' update'FROM aa WHERE a = 'a'LIMIT1); WITH aa AS (SELECT1 a, 2 b) INSERTINTO withz VALUES(1, (SELECT b || ' insert'FROM aa WHERE a = 1 )) ON CONFLICT (k) DO UPDATESET v = (SELECT b || ' update'FROM aa WHERE a = 1LIMIT1);
-- 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 ( SELECT2 k, 'Green' v),
upsert_cte AS ( INSERTINTO withz VALUES(2, 'Blue') ON CONFLICT (k) DO UPDATESET (k, v) = (SELECT k, v FROM simpletup WHERE simpletup.k = withz.k)
RETURNING k, v) INSERTINTO withz VALUES(2, 'Red') ON CONFLICT (k) DO UPDATESET (k, v) = (SELECT k, v FROM upsert_cte WHERE upsert_cte.k = withz.k)
RETURNING k, v;
DROPTABLE withz;
-- WITH referenced by MERGE statement CREATETABLE m ASSELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i; ALTERTABLE m ADDUNIQUE (k);
WITH RECURSIVE cte_basic AS (SELECT1 a, 'cte_basic val' b)
MERGE INTO m USING (select0 k, 'merge source SubPlan' v) o ON m.k=o.k WHEN MATCHED THENUPDATESET v = (SELECT b || ' merge update'FROM cte_basic WHERE cte_basic.a = m.k LIMIT1) WHENNOT MATCHED THENINSERTVALUES(o.k, o.v);
-- Basic: WITH cte_basic AS MATERIALIZED (SELECT1 a, 'cte_basic val' b)
MERGE INTO m USING (select0 k, 'merge source SubPlan' v offset 0) o ON m.k=o.k WHEN MATCHED THENUPDATESET v = (SELECT b || ' merge update'FROM cte_basic WHERE cte_basic.a = m.k LIMIT1) WHENNOT MATCHED THENINSERTVALUES(o.k, o.v); -- Examine SELECT * FROM m where k = 0;
-- See EXPLAIN output for same query: EXPLAIN (VERBOSE, COSTS OFF) WITH cte_basic AS MATERIALIZED (SELECT1 a, 'cte_basic val' b)
MERGE INTO m USING (select0 k, 'merge source SubPlan' v offset 0) o ON m.k=o.k WHEN MATCHED THENUPDATESET v = (SELECT b || ' merge update'FROM cte_basic WHERE cte_basic.a = m.k LIMIT1) WHENNOT MATCHED THENINSERTVALUES(o.k, o.v);
-- InitPlan WITH cte_init AS MATERIALIZED (SELECT1 a, 'cte_init val' b)
MERGE INTO m USING (select1 k, 'merge source InitPlan' v offset 0) o ON m.k=o.k WHEN MATCHED THENUPDATESET v = (SELECT b || ' merge update'FROM cte_init WHERE a = 1LIMIT1) WHENNOT MATCHED THENINSERTVALUES(o.k, o.v); -- Examine SELECT * FROM m where k = 1;
-- See EXPLAIN output for same query: EXPLAIN (VERBOSE, COSTS OFF) WITH cte_init AS MATERIALIZED (SELECT1 a, 'cte_init val' b)
MERGE INTO m USING (select1 k, 'merge source InitPlan' v offset 0) o ON m.k=o.k WHEN MATCHED THENUPDATESET v = (SELECT b || ' merge update'FROM cte_init WHERE a = 1LIMIT1) WHENNOT MATCHED THENINSERTVALUES(o.k, o.v);
-- MERGE source comes from CTE: WITH merge_source_cte AS MATERIALIZED (SELECT15 a, 'merge_source_cte val' b)
MERGE INTO m USING (select * from merge_source_cte) o ON m.k=o.a WHEN MATCHED THENUPDATESET v = (SELECT b || merge_source_cte.*::text || ' merge update'FROM merge_source_cte WHERE a = 15) WHENNOT MATCHED THENINSERTVALUES(o.a, o.b || (SELECT merge_source_cte.*::text || ' merge insert'FROM merge_source_cte)); -- Examine SELECT * FROM m where k = 15;
-- See EXPLAIN output for same query: EXPLAIN (VERBOSE, COSTS OFF) WITH merge_source_cte AS MATERIALIZED (SELECT15 a, 'merge_source_cte val' b)
MERGE INTO m USING (select * from merge_source_cte) o ON m.k=o.a WHEN MATCHED THENUPDATESET v = (SELECT b || merge_source_cte.*::text || ' merge update'FROM merge_source_cte WHERE a = 15) WHENNOT MATCHED THENINSERTVALUES(o.a, o.b || (SELECT merge_source_cte.*::text || ' merge insert'FROM merge_source_cte));
DROPTABLE m;
-- check that run to completion happens in proper ordering
TRUNCATE TABLE y; INSERTINTO y SELECT generate_series(1, 3); CREATE TEMPORARY TABLE yy (a INTEGER);
WITH RECURSIVE t1 AS ( INSERTINTO y SELECT * FROM y RETURNING *
), t2 AS ( INSERTINTO yy SELECT * FROM t1 RETURNING *
) SELECT1;
SELECT * FROM y; SELECT * FROM yy;
WITH RECURSIVE t1 AS ( INSERTINTO yy SELECT * FROM t2 RETURNING *
), t2 AS ( INSERTINTO y SELECT * FROM y RETURNING *
) SELECT1;
SELECT * FROM y; SELECT * FROM yy;
-- triggers
TRUNCATE TABLE y; INSERTINTO y SELECT generate_series(1, 10);
CREATE FUNCTION y_trigger() RETURNS triggerAS $$
begin
raise notice 'y_trigger: a = %', new.a; return new;
end;
$$ LANGUAGE plpgsql;
CREATETRIGGER y_trig BEFOREINSERTON y FOREACH ROW
EXECUTE PROCEDURE y_trigger();
WITH t AS ( INSERTINTO y VALUES
(21),
(22),
(23)
RETURNING *
) SELECT * FROM t;
SELECT * FROM y;
DROPTRIGGER y_trig ON y;
CREATETRIGGER y_trig AFTER INSERTON y FOREACH ROW
EXECUTE PROCEDURE y_trigger();
WITH t AS ( INSERTINTO y VALUES
(31),
(32),
(33)
RETURNING *
) SELECT * FROM t LIMIT1;
SELECT * FROM y;
DROPTRIGGER y_trig ON y;
CREATEORREPLACE FUNCTION y_trigger() RETURNS triggerAS $$
begin
raise notice 'y_trigger'; returnnull;
end;
$$ LANGUAGE plpgsql;
CREATETRIGGER y_trig AFTER INSERTON y FOREACH STATEMENT
EXECUTE PROCEDURE y_trigger();
WITH t AS ( INSERTINTO y VALUES
(41),
(42),
(43)
RETURNING *
) SELECT * FROM t;
SELECT * FROM y;
DROPTRIGGER 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 );
WITH rcte AS ( SELECT sum(id) AS totalid FROM parent ) UPDATE parent SET id = id + totalid FROM rcte;
SELECT * FROM parent;
WITH wcte AS ( INSERTINTO 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 ) DELETEFROM parent USING rcte WHERE id = maxid;
SELECT * FROM parent;
WITH wcte AS ( INSERTINTO child2 VALUES ( 42, 'new2' ) RETURNING id AS newid ) DELETEFROM parent USING wcte WHERE id = newid;
SELECT * FROM parent;
-- check EXPLAIN VERBOSE for a wCTE with RETURNING
EXPLAIN (VERBOSE, COSTS OFF) WITH wcte AS ( INSERTINTO int8_tbl VALUES ( 42, 47 ) RETURNING q2 ) DELETEFROM a_star USING wcte WHERE aa = q2;
-- error cases
-- data-modifying WITH tries to use its own output WITH RECURSIVE t AS ( INSERTINTO y SELECT * FROM t
) VALUES(FALSE);
-- no RETURNING in a referenced data-modifying WITH WITH t AS ( INSERTINTO y VALUES(0)
) SELECT * FROM t;
-- RETURNING tries to return its own output WITH RECURSIVE t(action, a) AS (
MERGE INTO y USING (VALUES (11)) v(a) ON y.a = v.a WHENNOT MATCHED THENINSERTVALUES (v.a)
RETURNING merge_action(), (SELECT a FROM t)
) 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 ASONINSERTTO y WHERE a=0 DO INSTEAD DELETEFROM y; WITH t AS ( INSERTINTO y VALUES(0)
) VALUES(FALSE); CREATEORREPLACE RULE y_rule ASONINSERTTO y DO INSTEAD NOTHING; WITH t AS ( INSERTINTO y VALUES(0)
) VALUES(FALSE); CREATEORREPLACE RULE y_rule ASONINSERTTO y DO INSTEAD NOTIFY foo; WITH t AS ( INSERTINTO y VALUES(0)
) VALUES(FALSE); CREATEORREPLACE RULE y_rule ASONINSERTTO y DO ALSO NOTIFY foo; WITH t AS ( INSERTINTO y VALUES(0)
) VALUES(FALSE); CREATEORREPLACE RULE y_rule ASONINSERTTO y
DO INSTEAD (NOTIFY foo; NOTIFY bar); WITH t AS ( INSERTINTO y VALUES(0)
) VALUES(FALSE); DROP RULE y_rule ON y;
-- check that parser lookahead for WITH doesn't cause any odd behavior createtable foo (with baz); -- fail, WITH is a reserved word createtable foo (with ordinality); -- fail, WITH is a reserved word with ordinality as (select1as x) select * from ordinality;
-- check sane response to attempt to modify CTE relation WITH with_test AS (SELECT42) INSERTINTO with_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 with_test (i int); with with_test as (select42) insertinto with_test select * from with_test; select * from with_test; droptable with_test;
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.29 Sekunden
(vorverarbeitet am 2026-08-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.