2 -- Tests for common table expressions (WITH query, ... SELECT ...)
5 WITH q1(x,y) AS (SELECT 1,2)
6 SELECT * FROM q1, q1 AS q2;
12 -- Multiple uses are evaluated only once
13 SELECT count(*) FROM (
14 WITH q1(x) AS (SELECT random() FROM generate_series(1, 5))
26 WITH RECURSIVE t(n) AS (
29 SELECT n+1 FROM t WHERE n < 100
37 WITH RECURSIVE t(n) AS (
40 SELECT n+1 FROM t WHERE n < 5
52 -- UNION DISTINCT requires hashable type
53 WITH RECURSIVE t(n) AS (
56 SELECT n || '10'::varbit FROM t WHERE n < '100'::varbit
59 ERROR: could not implement recursive UNION
60 DETAIL: All column datatypes must be hashable.
62 CREATE RECURSIVE VIEW nums (n) AS
65 SELECT n+1 FROM nums WHERE n < 5;
76 CREATE OR REPLACE RECURSIVE VIEW nums (n) AS
79 SELECT n+1 FROM nums WHERE n < 6;
91 -- This is an infinite loop with UNION ALL, but not with UNION
92 WITH RECURSIVE t(n) AS (
103 -- This'd be an infinite loop, but outside query reads only as much as needed
104 WITH RECURSIVE t(n) AS (
108 SELECT * FROM t LIMIT 10;
123 -- UNION case should have same property
124 WITH RECURSIVE t(n) AS (
128 SELECT * FROM t LIMIT 10;
143 -- Test behavior with an unknown-type literal in the WITH
144 WITH q AS (SELECT 'foo' AS x)
145 SELECT x, pg_typeof(x) FROM q;
151 WITH RECURSIVE t(n) AS (
154 SELECT n || ' bar' FROM t WHERE length(n) < 20
156 SELECT n, pg_typeof(n) FROM t;
158 -------------------------+-----------
162 foo bar bar bar | text
163 foo bar bar bar bar | text
164 foo bar bar bar bar bar | text
167 -- In a perfect world, this would work and resolve the literal as int ...
168 -- but for now, we have to be content with resolving to text too soon.
169 WITH RECURSIVE t(n) AS (
172 SELECT n+1 FROM t WHERE n < 10
174 SELECT n, pg_typeof(n) FROM t;
175 ERROR: operator does not exist: text + integer
176 LINE 4: SELECT n+1 FROM t WHERE n < 10
178 HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
179 -- Deeply nested WITH caused a list-munging problem in v13
180 -- Detection of cross-references and self-references
181 WITH RECURSIVE w1(c1) AS
186 (WITH RECURSIVE w6(c6) AS
203 -- Detection of invalid self-references
204 WITH RECURSIVE outermost(x) AS (
206 UNION (WITH innermost1 AS (
208 UNION (WITH innermost2 AS (
210 UNION (WITH innermost3 AS (
212 UNION (WITH innermost4 AS (
214 UNION (WITH innermost5 AS (
216 UNION (WITH innermost6 AS
218 SELECT * FROM innermost6))
219 SELECT * FROM innermost5))
220 SELECT * FROM innermost4))
221 SELECT * FROM innermost3))
222 SELECT * FROM innermost2))
223 SELECT * FROM outermost
224 UNION SELECT * FROM innermost1)
226 SELECT * FROM outermost ORDER BY 1;
239 -- Some examples with a tree
241 -- department structure represented here is as follows:
243 -- ROOT-+->A-+->B-+->C
247 CREATE TEMP TABLE department (
248 id INTEGER PRIMARY KEY, -- department ID
249 parent_department INTEGER REFERENCES department, -- upper department ID
250 name TEXT -- department name
252 INSERT INTO department VALUES (0, NULL, 'ROOT');
253 INSERT INTO department VALUES (1, 0, 'A');
254 INSERT INTO department VALUES (2, 1, 'B');
255 INSERT INTO department VALUES (3, 2, 'C');
256 INSERT INTO department VALUES (4, 2, 'D');
257 INSERT INTO department VALUES (5, 0, 'E');
258 INSERT INTO department VALUES (6, 4, 'F');
259 INSERT INTO department VALUES (7, 5, 'G');
260 -- extract all departments under 'A'. Result should be A, B, C, D and F
261 WITH RECURSIVE subdepartment AS
263 -- non recursive term
264 SELECT name as root_name, * FROM department WHERE name = 'A'
267 SELECT sd.root_name, d.* FROM department AS d, subdepartment AS sd
268 WHERE d.parent_department = sd.id
270 SELECT * FROM subdepartment ORDER BY name;
271 root_name | id | parent_department | name
272 -----------+----+-------------------+------
280 -- extract all departments under 'A' with "level" number
281 WITH RECURSIVE subdepartment(level, id, parent_department, name) AS
283 -- non recursive term
284 SELECT 1, * FROM department WHERE name = 'A'
287 SELECT sd.level + 1, d.* FROM department AS d, subdepartment AS sd
288 WHERE d.parent_department = sd.id
290 SELECT * FROM subdepartment ORDER BY name;
291 level | id | parent_department | name
292 -------+----+-------------------+------
300 -- extract all departments under 'A' with "level" number.
301 -- Only shows level 2 or more
302 WITH RECURSIVE subdepartment(level, id, parent_department, name) AS
304 -- non recursive term
305 SELECT 1, * FROM department WHERE name = 'A'
308 SELECT sd.level + 1, d.* FROM department AS d, subdepartment AS sd
309 WHERE d.parent_department = sd.id
311 SELECT * FROM subdepartment WHERE level >= 2 ORDER BY name;
312 level | id | parent_department | name
313 -------+----+-------------------+------
320 -- "RECURSIVE" is ignored if the query has no self-reference
321 WITH RECURSIVE subdepartment AS
323 -- note lack of recursive UNION structure
324 SELECT * FROM department WHERE name = 'A'
326 SELECT * FROM subdepartment ORDER BY name;
327 id | parent_department | name
328 ----+-------------------+------
333 SELECT count(*) FROM (
334 WITH RECURSIVE t(n) AS (
335 SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 500
337 SELECT * FROM t) AS t WHERE n < (
338 SELECT count(*) FROM (
339 WITH RECURSIVE t(n) AS (
340 SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 100
342 SELECT * FROM t WHERE n < 50000
343 ) AS t WHERE n < 100);
349 -- use same CTE twice at different subquery levels
351 SELECT hundred, sum(ten) FROM tenk1 GROUP BY hundred
353 SELECT count(*) FROM q1 WHERE y > (SELECT sum(y)/100 FROM q1 qsub);
360 CREATE TEMPORARY VIEW vsubdepartment AS
361 WITH RECURSIVE subdepartment AS
363 -- non recursive term
364 SELECT * FROM department WHERE name = 'A'
367 SELECT d.* FROM department AS d, subdepartment AS sd
368 WHERE d.parent_department = sd.id
370 SELECT * FROM subdepartment;
371 SELECT * FROM vsubdepartment ORDER BY name;
372 id | parent_department | name
373 ----+-------------------+------
381 -- Check reverse listing
382 SELECT pg_get_viewdef('vsubdepartment'::regclass);
384 -----------------------------------------------
385 WITH RECURSIVE subdepartment AS ( +
386 SELECT department.id, +
387 department.parent_department, +
390 WHERE (department.name = 'A'::text)+
393 d.parent_department, +
397 WHERE (d.parent_department = sd.id)+
405 SELECT pg_get_viewdef('vsubdepartment'::regclass, true);
407 ---------------------------------------------
408 WITH RECURSIVE subdepartment AS ( +
409 SELECT department.id, +
410 department.parent_department, +
413 WHERE department.name = 'A'::text+
416 d.parent_department, +
420 WHERE d.parent_department = sd.id+
428 -- Another reverse-listing example
429 CREATE VIEW sums_1_100 AS
430 WITH RECURSIVE t(n) AS (
433 SELECT n+1 FROM t WHERE n < 100
435 SELECT sum(n) FROM t;
437 View "public.sums_1_100"
438 Column | Type | Collation | Nullable | Default | Storage | Description
439 --------+--------+-----------+----------+---------+---------+-------------
440 sum | bigint | | | | plain |
442 WITH RECURSIVE t(n) AS (
452 -- corner case in which sub-WITH gets initialized first
453 with recursive q as (
454 select * from department
456 (with x as (select * from q)
459 select * from q limit 24;
460 id | parent_department | name
461 ----+-------------------+------
488 with recursive q as (
489 select * from department
491 (with recursive x as (
492 select * from department
494 (select * from q union all select * from x)
498 select * from q limit 32;
499 id | parent_department | name
500 ----+-------------------+------
535 -- recursive term has sub-UNION
536 WITH RECURSIVE t(i,j) AS (
539 SELECT t2.i, t.j+1 FROM
540 (SELECT 2 AS i UNION ALL SELECT 3 AS i) AS t2
541 JOIN t ON (t2.i = t.i+1))
551 -- different tree example
553 CREATE TEMPORARY TABLE tree(
554 id INTEGER PRIMARY KEY,
555 parent_id INTEGER REFERENCES tree(id)
558 VALUES (1, NULL), (2, 1), (3,1), (4,2), (5,2), (6,2), (7,3), (8,3),
559 (9,4), (10,4), (11,7), (12,7), (13,7), (14, 9), (15,11), (16,11);
561 -- get all paths from "second level" nodes to leaf nodes
563 WITH RECURSIVE t(id, path) AS (
564 VALUES(1,ARRAY[]::integer[])
566 SELECT tree.id, t.path || tree.id
567 FROM tree JOIN t ON (tree.parent_id = t.id)
569 SELECT t1.*, t2.* FROM t AS t1 JOIN t AS t2 ON
570 (t1.path[1] = t2.path[1] AND
571 array_upper(t1.path,1) = 1 AND
572 array_upper(t2.path,1) > 1)
573 ORDER BY t1.id, t2.id;
574 id | path | id | path
575 ----+------+----+-------------
579 2 | {2} | 9 | {2,4,9}
580 2 | {2} | 10 | {2,4,10}
581 2 | {2} | 14 | {2,4,9,14}
584 3 | {3} | 11 | {3,7,11}
585 3 | {3} | 12 | {3,7,12}
586 3 | {3} | 13 | {3,7,13}
587 3 | {3} | 15 | {3,7,11,15}
588 3 | {3} | 16 | {3,7,11,16}
592 WITH RECURSIVE t(id, path) AS (
593 VALUES(1,ARRAY[]::integer[])
595 SELECT tree.id, t.path || tree.id
596 FROM tree JOIN t ON (tree.parent_id = t.id)
598 SELECT t1.id, count(t2.*) FROM t AS t1 JOIN t AS t2 ON
599 (t1.path[1] = t2.path[1] AND
600 array_upper(t1.path,1) = 1 AND
601 array_upper(t2.path,1) > 1)
610 -- this variant tickled a whole-row-variable bug in 8.4devel
611 WITH RECURSIVE t(id, path) AS (
612 VALUES(1,ARRAY[]::integer[])
614 SELECT tree.id, t.path || tree.id
615 FROM tree JOIN t ON (tree.parent_id = t.id)
617 SELECT t1.id, t2.path, t2 FROM t AS t1 JOIN t AS t2 ON
620 ----+-------------+--------------------
624 4 | {2,4} | (4,"{2,4}")
625 5 | {2,5} | (5,"{2,5}")
626 6 | {2,6} | (6,"{2,6}")
627 7 | {3,7} | (7,"{3,7}")
628 8 | {3,8} | (8,"{3,8}")
629 9 | {2,4,9} | (9,"{2,4,9}")
630 10 | {2,4,10} | (10,"{2,4,10}")
631 11 | {3,7,11} | (11,"{3,7,11}")
632 12 | {3,7,12} | (12,"{3,7,12}")
633 13 | {3,7,13} | (13,"{3,7,13}")
634 14 | {2,4,9,14} | (14,"{2,4,9,14}")
635 15 | {3,7,11,15} | (15,"{3,7,11,15}")
636 16 | {3,7,11,16} | (16,"{3,7,11,16}")
639 -- test that column statistics from a materialized CTE are available
640 -- to upper planner (otherwise, we'd get a stupider plan)
642 with x as materialized (select unique1 from tenk1 b)
643 select count(*) from tenk1 a
644 where unique1 in (select * from x);
646 ------------------------------------------------------------
649 -> Index Only Scan using tenk1_unique1 on tenk1 b
651 Hash Cond: (a.unique1 = x.unique1)
652 -> Index Only Scan using tenk1_unique1 on tenk1 a
658 with x as materialized (insert into tenk1 default values returning unique1)
659 select count(*) from tenk1 a
660 where unique1 in (select * from x);
662 ------------------------------------------------------------
671 -> Index Only Scan using tenk1_unique1 on tenk1 a
672 Index Cond: (unique1 = x.unique1)
675 -- test that pathkeys from a materialized CTE are propagated up to the
678 with x as materialized (select unique1 from tenk1 b order by unique1)
679 select count(*) from tenk1 a
680 where unique1 in (select * from x);
682 ------------------------------------------------------------
685 -> Index Only Scan using tenk1_unique1 on tenk1 b
687 Merge Cond: (a.unique1 = x.unique1)
688 -> Index Only Scan using tenk1_unique1 on tenk1 a
693 create temp table graph0( f int, t int, label text );
694 insert into graph0 values
695 (1, 2, 'arc 1 -> 2'),
696 (1, 3, 'arc 1 -> 3'),
697 (2, 3, 'arc 2 -> 3'),
698 (1, 4, 'arc 1 -> 4'),
699 (4, 5, 'arc 4 -> 5');
700 explain (verbose, costs off)
701 with recursive search_graph(f, t, label) as (
702 select * from graph0 g
705 from graph0 g, search_graph sg
707 ) search depth first by f, t set seq
708 select * from search_graph order by seq;
710 ----------------------------------------------------------------------------------------------
712 Output: search_graph.f, search_graph.t, search_graph.label, search_graph.seq
713 Sort Key: search_graph.seq
716 -> Seq Scan on pg_temp.graph0 g
717 Output: g.f, g.t, g.label, ARRAY[ROW(g.f, g.t)]
719 Output: g_1.f, g_1.t, g_1.label, array_cat(sg.seq, ARRAY[ROW(g_1.f, g_1.t)])
720 Merge Cond: (g_1.f = sg.t)
722 Output: g_1.f, g_1.t, g_1.label
724 -> Seq Scan on pg_temp.graph0 g_1
725 Output: g_1.f, g_1.t, g_1.label
729 -> WorkTable Scan on search_graph sg
731 -> CTE Scan on search_graph
732 Output: search_graph.f, search_graph.t, search_graph.label, search_graph.seq
735 with recursive search_graph(f, t, label) as (
736 select * from graph0 g
739 from graph0 g, search_graph sg
741 ) search depth first by f, t set seq
742 select * from search_graph order by seq;
744 ---+---+------------+-------------------
745 1 | 2 | arc 1 -> 2 | {"(1,2)"}
746 2 | 3 | arc 2 -> 3 | {"(1,2)","(2,3)"}
747 1 | 3 | arc 1 -> 3 | {"(1,3)"}
748 1 | 4 | arc 1 -> 4 | {"(1,4)"}
749 4 | 5 | arc 4 -> 5 | {"(1,4)","(4,5)"}
750 2 | 3 | arc 2 -> 3 | {"(2,3)"}
751 4 | 5 | arc 4 -> 5 | {"(4,5)"}
754 with recursive search_graph(f, t, label) as (
755 select * from graph0 g
758 from graph0 g, search_graph sg
760 ) search depth first by f, t set seq
761 select * from search_graph order by seq;
763 ---+---+------------+-------------------
764 1 | 2 | arc 1 -> 2 | {"(1,2)"}
765 2 | 3 | arc 2 -> 3 | {"(1,2)","(2,3)"}
766 1 | 3 | arc 1 -> 3 | {"(1,3)"}
767 1 | 4 | arc 1 -> 4 | {"(1,4)"}
768 4 | 5 | arc 4 -> 5 | {"(1,4)","(4,5)"}
769 2 | 3 | arc 2 -> 3 | {"(2,3)"}
770 4 | 5 | arc 4 -> 5 | {"(4,5)"}
773 explain (verbose, costs off)
774 with recursive search_graph(f, t, label) as (
775 select * from graph0 g
778 from graph0 g, search_graph sg
780 ) search breadth first by f, t set seq
781 select * from search_graph order by seq;
783 -------------------------------------------------------------------------------------------------
785 Output: search_graph.f, search_graph.t, search_graph.label, search_graph.seq
786 Sort Key: search_graph.seq
789 -> Seq Scan on pg_temp.graph0 g
790 Output: g.f, g.t, g.label, ROW('0'::bigint, g.f, g.t)
792 Output: g_1.f, g_1.t, g_1.label, ROW(int8inc((sg.seq)."*DEPTH*"), g_1.f, g_1.t)
793 Merge Cond: (g_1.f = sg.t)
795 Output: g_1.f, g_1.t, g_1.label
797 -> Seq Scan on pg_temp.graph0 g_1
798 Output: g_1.f, g_1.t, g_1.label
802 -> WorkTable Scan on search_graph sg
804 -> CTE Scan on search_graph
805 Output: search_graph.f, search_graph.t, search_graph.label, search_graph.seq
808 with recursive search_graph(f, t, label) as (
809 select * from graph0 g
812 from graph0 g, search_graph sg
814 ) search breadth first by f, t set seq
815 select * from search_graph order by seq;
817 ---+---+------------+---------
818 1 | 2 | arc 1 -> 2 | (0,1,2)
819 1 | 3 | arc 1 -> 3 | (0,1,3)
820 1 | 4 | arc 1 -> 4 | (0,1,4)
821 2 | 3 | arc 2 -> 3 | (0,2,3)
822 4 | 5 | arc 4 -> 5 | (0,4,5)
823 2 | 3 | arc 2 -> 3 | (1,2,3)
824 4 | 5 | arc 4 -> 5 | (1,4,5)
827 with recursive search_graph(f, t, label) as (
828 select * from graph0 g
831 from graph0 g, search_graph sg
833 ) search breadth first by f, t set seq
834 select * from search_graph order by seq;
836 ---+---+------------+---------
837 1 | 2 | arc 1 -> 2 | (0,1,2)
838 1 | 3 | arc 1 -> 3 | (0,1,3)
839 1 | 4 | arc 1 -> 4 | (0,1,4)
840 2 | 3 | arc 2 -> 3 | (0,2,3)
841 4 | 5 | arc 4 -> 5 | (0,4,5)
842 2 | 3 | arc 2 -> 3 | (1,2,3)
843 4 | 5 | arc 4 -> 5 | (1,4,5)
846 -- a constant initial value causes issues for EXPLAIN
847 explain (verbose, costs off)
848 with recursive test as (
853 ) search depth first by x set y
854 select * from test limit 5;
856 -----------------------------------------------------------------------------------------
858 Output: test.x, test.y
862 Output: 1, '{(1)}'::record[]
863 -> WorkTable Scan on test test_1
864 Output: (test_1.x + 1), array_cat(test_1.y, ARRAY[ROW((test_1.x + 1))])
866 Output: test.x, test.y
869 with recursive test as (
874 ) search depth first by x set y
875 select * from test limit 5;
877 ---+-----------------------
881 4 | {(1),(2),(3),(4)}
882 5 | {(1),(2),(3),(4),(5)}
885 explain (verbose, costs off)
886 with recursive test as (
891 ) search breadth first by x set y
892 select * from test limit 5;
894 --------------------------------------------------------------------------------------------
896 Output: test.x, test.y
900 Output: 1, '(0,1)'::record
901 -> WorkTable Scan on test test_1
902 Output: (test_1.x + 1), ROW(int8inc((test_1.y)."*DEPTH*"), (test_1.x + 1))
904 Output: test.x, test.y
907 with recursive test as (
912 ) search breadth first by x set y
913 select * from test limit 5;
923 -- various syntax errors
924 with recursive search_graph(f, t, label) as (
925 select * from graph0 g
928 from graph0 g, search_graph sg
930 ) search depth first by foo, tar set seq
931 select * from search_graph;
932 ERROR: search column "foo" not in WITH query column list
933 LINE 7: ) search depth first by foo, tar set seq
935 with recursive search_graph(f, t, label) as (
936 select * from graph0 g
939 from graph0 g, search_graph sg
941 ) search depth first by f, t set label
942 select * from search_graph;
943 ERROR: search sequence column name "label" already used in WITH query column list
944 LINE 7: ) search depth first by f, t set label
946 with recursive search_graph(f, t, label) as (
947 select * from graph0 g
950 from graph0 g, search_graph sg
952 ) search depth first by f, t, f set seq
953 select * from search_graph;
954 ERROR: search column "f" specified more than once
955 LINE 7: ) search depth first by f, t, f set seq
957 with recursive search_graph(f, t, label) as (
958 select * from graph0 g
960 select * from graph0 g
963 from graph0 g, search_graph sg
965 ) search depth first by f, t set seq
966 select * from search_graph order by seq;
967 ERROR: with a SEARCH or CYCLE clause, the left side of the UNION must be a SELECT
968 with recursive search_graph(f, t, label) as (
969 select * from graph0 g
971 (select * from graph0 g
974 from graph0 g, search_graph sg
976 ) search depth first by f, t set seq
977 select * from search_graph order by seq;
978 ERROR: with a SEARCH or CYCLE clause, the right side of the UNION must be a SELECT
979 -- check that we distinguish same CTE name used at different levels
980 -- (this case could be supported, perhaps, but it isn't today)
981 with recursive x(col) as (
984 (with x as (select * from x)
986 ) search depth first by col set seq
988 ERROR: with a SEARCH or CYCLE clause, the recursive reference to WITH query "x" must be at the top level of its right-hand SELECT
989 -- test ruleutils and view expansion
990 create temp view v_search as
991 with recursive search_graph(f, t, label) as (
992 select * from graph0 g
995 from graph0 g, search_graph sg
997 ) search depth first by f, t set seq
998 select f, t, label from search_graph;
999 select pg_get_viewdef('v_search');
1001 ------------------------------------------------
1002 WITH RECURSIVE search_graph(f, t, label) AS (+
1013 WHERE (g.f = sg.t) +
1014 ) SEARCH DEPTH FIRST BY f, t SET seq +
1021 select * from v_search;
1023 ---+---+------------
1034 -- test cycle detection
1036 create temp table graph( f int, t int, label text );
1037 insert into graph values
1038 (1, 2, 'arc 1 -> 2'),
1039 (1, 3, 'arc 1 -> 3'),
1040 (2, 3, 'arc 2 -> 3'),
1041 (1, 4, 'arc 1 -> 4'),
1042 (4, 5, 'arc 4 -> 5'),
1043 (5, 1, 'arc 5 -> 1');
1044 with recursive search_graph(f, t, label, is_cycle, path) as (
1045 select *, false, array[row(g.f, g.t)] from graph g
1047 select g.*, row(g.f, g.t) = any(path), path || row(g.f, g.t)
1048 from graph g, search_graph sg
1049 where g.f = sg.t and not is_cycle
1051 select * from search_graph;
1052 f | t | label | is_cycle | path
1053 ---+---+------------+----------+-------------------------------------------
1054 1 | 2 | arc 1 -> 2 | f | {"(1,2)"}
1055 1 | 3 | arc 1 -> 3 | f | {"(1,3)"}
1056 2 | 3 | arc 2 -> 3 | f | {"(2,3)"}
1057 1 | 4 | arc 1 -> 4 | f | {"(1,4)"}
1058 4 | 5 | arc 4 -> 5 | f | {"(4,5)"}
1059 5 | 1 | arc 5 -> 1 | f | {"(5,1)"}
1060 1 | 2 | arc 1 -> 2 | f | {"(5,1)","(1,2)"}
1061 1 | 3 | arc 1 -> 3 | f | {"(5,1)","(1,3)"}
1062 1 | 4 | arc 1 -> 4 | f | {"(5,1)","(1,4)"}
1063 2 | 3 | arc 2 -> 3 | f | {"(1,2)","(2,3)"}
1064 4 | 5 | arc 4 -> 5 | f | {"(1,4)","(4,5)"}
1065 5 | 1 | arc 5 -> 1 | f | {"(4,5)","(5,1)"}
1066 1 | 2 | arc 1 -> 2 | f | {"(4,5)","(5,1)","(1,2)"}
1067 1 | 3 | arc 1 -> 3 | f | {"(4,5)","(5,1)","(1,3)"}
1068 1 | 4 | arc 1 -> 4 | f | {"(4,5)","(5,1)","(1,4)"}
1069 2 | 3 | arc 2 -> 3 | f | {"(5,1)","(1,2)","(2,3)"}
1070 4 | 5 | arc 4 -> 5 | f | {"(5,1)","(1,4)","(4,5)"}
1071 5 | 1 | arc 5 -> 1 | f | {"(1,4)","(4,5)","(5,1)"}
1072 1 | 2 | arc 1 -> 2 | f | {"(1,4)","(4,5)","(5,1)","(1,2)"}
1073 1 | 3 | arc 1 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,3)"}
1074 1 | 4 | arc 1 -> 4 | t | {"(1,4)","(4,5)","(5,1)","(1,4)"}
1075 2 | 3 | arc 2 -> 3 | f | {"(4,5)","(5,1)","(1,2)","(2,3)"}
1076 4 | 5 | arc 4 -> 5 | t | {"(4,5)","(5,1)","(1,4)","(4,5)"}
1077 5 | 1 | arc 5 -> 1 | t | {"(5,1)","(1,4)","(4,5)","(5,1)"}
1078 2 | 3 | arc 2 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"}
1081 -- UNION DISTINCT exercises row type hashing support
1082 with recursive search_graph(f, t, label, is_cycle, path) as (
1083 select *, false, array[row(g.f, g.t)] from graph g
1085 select g.*, row(g.f, g.t) = any(path), path || row(g.f, g.t)
1086 from graph g, search_graph sg
1087 where g.f = sg.t and not is_cycle
1089 select * from search_graph;
1090 f | t | label | is_cycle | path
1091 ---+---+------------+----------+-------------------------------------------
1092 1 | 2 | arc 1 -> 2 | f | {"(1,2)"}
1093 1 | 3 | arc 1 -> 3 | f | {"(1,3)"}
1094 2 | 3 | arc 2 -> 3 | f | {"(2,3)"}
1095 1 | 4 | arc 1 -> 4 | f | {"(1,4)"}
1096 4 | 5 | arc 4 -> 5 | f | {"(4,5)"}
1097 5 | 1 | arc 5 -> 1 | f | {"(5,1)"}
1098 1 | 2 | arc 1 -> 2 | f | {"(5,1)","(1,2)"}
1099 1 | 3 | arc 1 -> 3 | f | {"(5,1)","(1,3)"}
1100 1 | 4 | arc 1 -> 4 | f | {"(5,1)","(1,4)"}
1101 2 | 3 | arc 2 -> 3 | f | {"(1,2)","(2,3)"}
1102 4 | 5 | arc 4 -> 5 | f | {"(1,4)","(4,5)"}
1103 5 | 1 | arc 5 -> 1 | f | {"(4,5)","(5,1)"}
1104 1 | 2 | arc 1 -> 2 | f | {"(4,5)","(5,1)","(1,2)"}
1105 1 | 3 | arc 1 -> 3 | f | {"(4,5)","(5,1)","(1,3)"}
1106 1 | 4 | arc 1 -> 4 | f | {"(4,5)","(5,1)","(1,4)"}
1107 2 | 3 | arc 2 -> 3 | f | {"(5,1)","(1,2)","(2,3)"}
1108 4 | 5 | arc 4 -> 5 | f | {"(5,1)","(1,4)","(4,5)"}
1109 5 | 1 | arc 5 -> 1 | f | {"(1,4)","(4,5)","(5,1)"}
1110 1 | 2 | arc 1 -> 2 | f | {"(1,4)","(4,5)","(5,1)","(1,2)"}
1111 1 | 3 | arc 1 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,3)"}
1112 1 | 4 | arc 1 -> 4 | t | {"(1,4)","(4,5)","(5,1)","(1,4)"}
1113 2 | 3 | arc 2 -> 3 | f | {"(4,5)","(5,1)","(1,2)","(2,3)"}
1114 4 | 5 | arc 4 -> 5 | t | {"(4,5)","(5,1)","(1,4)","(4,5)"}
1115 5 | 1 | arc 5 -> 1 | t | {"(5,1)","(1,4)","(4,5)","(5,1)"}
1116 2 | 3 | arc 2 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"}
1119 -- ordering by the path column has same effect as SEARCH DEPTH FIRST
1120 with recursive search_graph(f, t, label, is_cycle, path) as (
1121 select *, false, array[row(g.f, g.t)] from graph g
1123 select g.*, row(g.f, g.t) = any(path), path || row(g.f, g.t)
1124 from graph g, search_graph sg
1125 where g.f = sg.t and not is_cycle
1127 select * from search_graph order by path;
1128 f | t | label | is_cycle | path
1129 ---+---+------------+----------+-------------------------------------------
1130 1 | 2 | arc 1 -> 2 | f | {"(1,2)"}
1131 2 | 3 | arc 2 -> 3 | f | {"(1,2)","(2,3)"}
1132 1 | 3 | arc 1 -> 3 | f | {"(1,3)"}
1133 1 | 4 | arc 1 -> 4 | f | {"(1,4)"}
1134 4 | 5 | arc 4 -> 5 | f | {"(1,4)","(4,5)"}
1135 5 | 1 | arc 5 -> 1 | f | {"(1,4)","(4,5)","(5,1)"}
1136 1 | 2 | arc 1 -> 2 | f | {"(1,4)","(4,5)","(5,1)","(1,2)"}
1137 2 | 3 | arc 2 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"}
1138 1 | 3 | arc 1 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,3)"}
1139 1 | 4 | arc 1 -> 4 | t | {"(1,4)","(4,5)","(5,1)","(1,4)"}
1140 2 | 3 | arc 2 -> 3 | f | {"(2,3)"}
1141 4 | 5 | arc 4 -> 5 | f | {"(4,5)"}
1142 5 | 1 | arc 5 -> 1 | f | {"(4,5)","(5,1)"}
1143 1 | 2 | arc 1 -> 2 | f | {"(4,5)","(5,1)","(1,2)"}
1144 2 | 3 | arc 2 -> 3 | f | {"(4,5)","(5,1)","(1,2)","(2,3)"}
1145 1 | 3 | arc 1 -> 3 | f | {"(4,5)","(5,1)","(1,3)"}
1146 1 | 4 | arc 1 -> 4 | f | {"(4,5)","(5,1)","(1,4)"}
1147 4 | 5 | arc 4 -> 5 | t | {"(4,5)","(5,1)","(1,4)","(4,5)"}
1148 5 | 1 | arc 5 -> 1 | f | {"(5,1)"}
1149 1 | 2 | arc 1 -> 2 | f | {"(5,1)","(1,2)"}
1150 2 | 3 | arc 2 -> 3 | f | {"(5,1)","(1,2)","(2,3)"}
1151 1 | 3 | arc 1 -> 3 | f | {"(5,1)","(1,3)"}
1152 1 | 4 | arc 1 -> 4 | f | {"(5,1)","(1,4)"}
1153 4 | 5 | arc 4 -> 5 | f | {"(5,1)","(1,4)","(4,5)"}
1154 5 | 1 | arc 5 -> 1 | t | {"(5,1)","(1,4)","(4,5)","(5,1)"}
1158 explain (verbose, costs off)
1159 with recursive search_graph(f, t, label) as (
1160 select * from graph g
1163 from graph g, search_graph sg
1165 ) cycle f, t set is_cycle using path
1166 select * from search_graph;
1168 -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
1169 CTE Scan on search_graph
1170 Output: search_graph.f, search_graph.t, search_graph.label, search_graph.is_cycle, search_graph.path
1173 -> Seq Scan on pg_temp.graph g
1174 Output: g.f, g.t, g.label, false, ARRAY[ROW(g.f, g.t)]
1176 Output: g_1.f, g_1.t, g_1.label, CASE WHEN (ROW(g_1.f, g_1.t) = ANY (sg.path)) THEN true ELSE false END, array_cat(sg.path, ARRAY[ROW(g_1.f, g_1.t)])
1177 Merge Cond: (g_1.f = sg.t)
1179 Output: g_1.f, g_1.t, g_1.label
1181 -> Seq Scan on pg_temp.graph g_1
1182 Output: g_1.f, g_1.t, g_1.label
1184 Output: sg.path, sg.t
1186 -> WorkTable Scan on search_graph sg
1187 Output: sg.path, sg.t
1188 Filter: (NOT sg.is_cycle)
1191 with recursive search_graph(f, t, label) as (
1192 select * from graph g
1195 from graph g, search_graph sg
1197 ) cycle f, t set is_cycle using path
1198 select * from search_graph;
1199 f | t | label | is_cycle | path
1200 ---+---+------------+----------+-------------------------------------------
1201 1 | 2 | arc 1 -> 2 | f | {"(1,2)"}
1202 1 | 3 | arc 1 -> 3 | f | {"(1,3)"}
1203 2 | 3 | arc 2 -> 3 | f | {"(2,3)"}
1204 1 | 4 | arc 1 -> 4 | f | {"(1,4)"}
1205 4 | 5 | arc 4 -> 5 | f | {"(4,5)"}
1206 5 | 1 | arc 5 -> 1 | f | {"(5,1)"}
1207 1 | 2 | arc 1 -> 2 | f | {"(5,1)","(1,2)"}
1208 1 | 3 | arc 1 -> 3 | f | {"(5,1)","(1,3)"}
1209 1 | 4 | arc 1 -> 4 | f | {"(5,1)","(1,4)"}
1210 2 | 3 | arc 2 -> 3 | f | {"(1,2)","(2,3)"}
1211 4 | 5 | arc 4 -> 5 | f | {"(1,4)","(4,5)"}
1212 5 | 1 | arc 5 -> 1 | f | {"(4,5)","(5,1)"}
1213 1 | 2 | arc 1 -> 2 | f | {"(4,5)","(5,1)","(1,2)"}
1214 1 | 3 | arc 1 -> 3 | f | {"(4,5)","(5,1)","(1,3)"}
1215 1 | 4 | arc 1 -> 4 | f | {"(4,5)","(5,1)","(1,4)"}
1216 2 | 3 | arc 2 -> 3 | f | {"(5,1)","(1,2)","(2,3)"}
1217 4 | 5 | arc 4 -> 5 | f | {"(5,1)","(1,4)","(4,5)"}
1218 5 | 1 | arc 5 -> 1 | f | {"(1,4)","(4,5)","(5,1)"}
1219 1 | 2 | arc 1 -> 2 | f | {"(1,4)","(4,5)","(5,1)","(1,2)"}
1220 1 | 3 | arc 1 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,3)"}
1221 1 | 4 | arc 1 -> 4 | t | {"(1,4)","(4,5)","(5,1)","(1,4)"}
1222 2 | 3 | arc 2 -> 3 | f | {"(4,5)","(5,1)","(1,2)","(2,3)"}
1223 4 | 5 | arc 4 -> 5 | t | {"(4,5)","(5,1)","(1,4)","(4,5)"}
1224 5 | 1 | arc 5 -> 1 | t | {"(5,1)","(1,4)","(4,5)","(5,1)"}
1225 2 | 3 | arc 2 -> 3 | f | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"}
1228 with recursive search_graph(f, t, label) as (
1229 select * from graph g
1232 from graph g, search_graph sg
1234 ) cycle f, t set is_cycle to 'Y' default 'N' using path
1235 select * from search_graph;
1236 f | t | label | is_cycle | path
1237 ---+---+------------+----------+-------------------------------------------
1238 1 | 2 | arc 1 -> 2 | N | {"(1,2)"}
1239 1 | 3 | arc 1 -> 3 | N | {"(1,3)"}
1240 2 | 3 | arc 2 -> 3 | N | {"(2,3)"}
1241 1 | 4 | arc 1 -> 4 | N | {"(1,4)"}
1242 4 | 5 | arc 4 -> 5 | N | {"(4,5)"}
1243 5 | 1 | arc 5 -> 1 | N | {"(5,1)"}
1244 1 | 2 | arc 1 -> 2 | N | {"(5,1)","(1,2)"}
1245 1 | 3 | arc 1 -> 3 | N | {"(5,1)","(1,3)"}
1246 1 | 4 | arc 1 -> 4 | N | {"(5,1)","(1,4)"}
1247 2 | 3 | arc 2 -> 3 | N | {"(1,2)","(2,3)"}
1248 4 | 5 | arc 4 -> 5 | N | {"(1,4)","(4,5)"}
1249 5 | 1 | arc 5 -> 1 | N | {"(4,5)","(5,1)"}
1250 1 | 2 | arc 1 -> 2 | N | {"(4,5)","(5,1)","(1,2)"}
1251 1 | 3 | arc 1 -> 3 | N | {"(4,5)","(5,1)","(1,3)"}
1252 1 | 4 | arc 1 -> 4 | N | {"(4,5)","(5,1)","(1,4)"}
1253 2 | 3 | arc 2 -> 3 | N | {"(5,1)","(1,2)","(2,3)"}
1254 4 | 5 | arc 4 -> 5 | N | {"(5,1)","(1,4)","(4,5)"}
1255 5 | 1 | arc 5 -> 1 | N | {"(1,4)","(4,5)","(5,1)"}
1256 1 | 2 | arc 1 -> 2 | N | {"(1,4)","(4,5)","(5,1)","(1,2)"}
1257 1 | 3 | arc 1 -> 3 | N | {"(1,4)","(4,5)","(5,1)","(1,3)"}
1258 1 | 4 | arc 1 -> 4 | Y | {"(1,4)","(4,5)","(5,1)","(1,4)"}
1259 2 | 3 | arc 2 -> 3 | N | {"(4,5)","(5,1)","(1,2)","(2,3)"}
1260 4 | 5 | arc 4 -> 5 | Y | {"(4,5)","(5,1)","(1,4)","(4,5)"}
1261 5 | 1 | arc 5 -> 1 | Y | {"(5,1)","(1,4)","(4,5)","(5,1)"}
1262 2 | 3 | arc 2 -> 3 | N | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"}
1265 explain (verbose, costs off)
1266 with recursive test as (
1271 ) cycle x set is_cycle using path
1274 -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1276 Output: test.x, test.is_cycle, test.path
1280 Output: 0, false, '{(0)}'::record[]
1281 -> WorkTable Scan on test test_1
1282 Output: ((test_1.x + 1) % 10), CASE WHEN (ROW(((test_1.x + 1) % 10)) = ANY (test_1.path)) THEN true ELSE false END, array_cat(test_1.path, ARRAY[ROW(((test_1.x + 1) % 10))])
1283 Filter: (NOT test_1.is_cycle)
1286 with recursive test as (
1291 ) cycle x set is_cycle using path
1294 ---+----------+-----------------------------------------------
1297 2 | f | {(0),(1),(2)}
1298 3 | f | {(0),(1),(2),(3)}
1299 4 | f | {(0),(1),(2),(3),(4)}
1300 5 | f | {(0),(1),(2),(3),(4),(5)}
1301 6 | f | {(0),(1),(2),(3),(4),(5),(6)}
1302 7 | f | {(0),(1),(2),(3),(4),(5),(6),(7)}
1303 8 | f | {(0),(1),(2),(3),(4),(5),(6),(7),(8)}
1304 9 | f | {(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)}
1305 0 | t | {(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(0)}
1308 with recursive test as (
1313 where not is_cycle -- redundant, but legal
1314 ) cycle x set is_cycle using path
1317 ---+----------+-----------------------------------------------
1320 2 | f | {(0),(1),(2)}
1321 3 | f | {(0),(1),(2),(3)}
1322 4 | f | {(0),(1),(2),(3),(4)}
1323 5 | f | {(0),(1),(2),(3),(4),(5)}
1324 6 | f | {(0),(1),(2),(3),(4),(5),(6)}
1325 7 | f | {(0),(1),(2),(3),(4),(5),(6),(7)}
1326 8 | f | {(0),(1),(2),(3),(4),(5),(6),(7),(8)}
1327 9 | f | {(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)}
1328 0 | t | {(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(0)}
1333 graph(f, t, label) as (
1334 values (1, 2, 'arc 1 -> 2'),
1335 (1, 3, 'arc 1 -> 3'),
1336 (2, 3, 'arc 2 -> 3'),
1337 (1, 4, 'arc 1 -> 4'),
1338 (4, 5, 'arc 4 -> 5'),
1339 (5, 1, 'arc 5 -> 1')
1341 search_graph(f, t, label) as (
1342 select * from graph g
1345 from graph g, search_graph sg
1347 ) cycle f, t set is_cycle to true default false using path
1348 select f, t, label from search_graph;
1350 ---+---+------------
1379 with recursive a as (
1383 ) cycle b set c using p
1392 with recursive search_graph(f, t, label) as (
1393 select * from graph g
1396 from graph g, search_graph sg
1398 ) search depth first by f, t set seq
1399 cycle f, t set is_cycle using path
1400 select * from search_graph;
1401 f | t | label | seq | is_cycle | path
1402 ---+---+------------+-------------------------------------------+----------+-------------------------------------------
1403 1 | 2 | arc 1 -> 2 | {"(1,2)"} | f | {"(1,2)"}
1404 1 | 3 | arc 1 -> 3 | {"(1,3)"} | f | {"(1,3)"}
1405 2 | 3 | arc 2 -> 3 | {"(2,3)"} | f | {"(2,3)"}
1406 1 | 4 | arc 1 -> 4 | {"(1,4)"} | f | {"(1,4)"}
1407 4 | 5 | arc 4 -> 5 | {"(4,5)"} | f | {"(4,5)"}
1408 5 | 1 | arc 5 -> 1 | {"(5,1)"} | f | {"(5,1)"}
1409 1 | 2 | arc 1 -> 2 | {"(5,1)","(1,2)"} | f | {"(5,1)","(1,2)"}
1410 1 | 3 | arc 1 -> 3 | {"(5,1)","(1,3)"} | f | {"(5,1)","(1,3)"}
1411 1 | 4 | arc 1 -> 4 | {"(5,1)","(1,4)"} | f | {"(5,1)","(1,4)"}
1412 2 | 3 | arc 2 -> 3 | {"(1,2)","(2,3)"} | f | {"(1,2)","(2,3)"}
1413 4 | 5 | arc 4 -> 5 | {"(1,4)","(4,5)"} | f | {"(1,4)","(4,5)"}
1414 5 | 1 | arc 5 -> 1 | {"(4,5)","(5,1)"} | f | {"(4,5)","(5,1)"}
1415 1 | 2 | arc 1 -> 2 | {"(4,5)","(5,1)","(1,2)"} | f | {"(4,5)","(5,1)","(1,2)"}
1416 1 | 3 | arc 1 -> 3 | {"(4,5)","(5,1)","(1,3)"} | f | {"(4,5)","(5,1)","(1,3)"}
1417 1 | 4 | arc 1 -> 4 | {"(4,5)","(5,1)","(1,4)"} | f | {"(4,5)","(5,1)","(1,4)"}
1418 2 | 3 | arc 2 -> 3 | {"(5,1)","(1,2)","(2,3)"} | f | {"(5,1)","(1,2)","(2,3)"}
1419 4 | 5 | arc 4 -> 5 | {"(5,1)","(1,4)","(4,5)"} | f | {"(5,1)","(1,4)","(4,5)"}
1420 5 | 1 | arc 5 -> 1 | {"(1,4)","(4,5)","(5,1)"} | f | {"(1,4)","(4,5)","(5,1)"}
1421 1 | 2 | arc 1 -> 2 | {"(1,4)","(4,5)","(5,1)","(1,2)"} | f | {"(1,4)","(4,5)","(5,1)","(1,2)"}
1422 1 | 3 | arc 1 -> 3 | {"(1,4)","(4,5)","(5,1)","(1,3)"} | f | {"(1,4)","(4,5)","(5,1)","(1,3)"}
1423 1 | 4 | arc 1 -> 4 | {"(1,4)","(4,5)","(5,1)","(1,4)"} | t | {"(1,4)","(4,5)","(5,1)","(1,4)"}
1424 2 | 3 | arc 2 -> 3 | {"(4,5)","(5,1)","(1,2)","(2,3)"} | f | {"(4,5)","(5,1)","(1,2)","(2,3)"}
1425 4 | 5 | arc 4 -> 5 | {"(4,5)","(5,1)","(1,4)","(4,5)"} | t | {"(4,5)","(5,1)","(1,4)","(4,5)"}
1426 5 | 1 | arc 5 -> 1 | {"(5,1)","(1,4)","(4,5)","(5,1)"} | t | {"(5,1)","(1,4)","(4,5)","(5,1)"}
1427 2 | 3 | arc 2 -> 3 | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} | f | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"}
1430 with recursive search_graph(f, t, label) as (
1431 select * from graph g
1434 from graph g, search_graph sg
1436 ) search breadth first by f, t set seq
1437 cycle f, t set is_cycle using path
1438 select * from search_graph;
1439 f | t | label | seq | is_cycle | path
1440 ---+---+------------+---------+----------+-------------------------------------------
1441 1 | 2 | arc 1 -> 2 | (0,1,2) | f | {"(1,2)"}
1442 1 | 3 | arc 1 -> 3 | (0,1,3) | f | {"(1,3)"}
1443 2 | 3 | arc 2 -> 3 | (0,2,3) | f | {"(2,3)"}
1444 1 | 4 | arc 1 -> 4 | (0,1,4) | f | {"(1,4)"}
1445 4 | 5 | arc 4 -> 5 | (0,4,5) | f | {"(4,5)"}
1446 5 | 1 | arc 5 -> 1 | (0,5,1) | f | {"(5,1)"}
1447 1 | 2 | arc 1 -> 2 | (1,1,2) | f | {"(5,1)","(1,2)"}
1448 1 | 3 | arc 1 -> 3 | (1,1,3) | f | {"(5,1)","(1,3)"}
1449 1 | 4 | arc 1 -> 4 | (1,1,4) | f | {"(5,1)","(1,4)"}
1450 2 | 3 | arc 2 -> 3 | (1,2,3) | f | {"(1,2)","(2,3)"}
1451 4 | 5 | arc 4 -> 5 | (1,4,5) | f | {"(1,4)","(4,5)"}
1452 5 | 1 | arc 5 -> 1 | (1,5,1) | f | {"(4,5)","(5,1)"}
1453 1 | 2 | arc 1 -> 2 | (2,1,2) | f | {"(4,5)","(5,1)","(1,2)"}
1454 1 | 3 | arc 1 -> 3 | (2,1,3) | f | {"(4,5)","(5,1)","(1,3)"}
1455 1 | 4 | arc 1 -> 4 | (2,1,4) | f | {"(4,5)","(5,1)","(1,4)"}
1456 2 | 3 | arc 2 -> 3 | (2,2,3) | f | {"(5,1)","(1,2)","(2,3)"}
1457 4 | 5 | arc 4 -> 5 | (2,4,5) | f | {"(5,1)","(1,4)","(4,5)"}
1458 5 | 1 | arc 5 -> 1 | (2,5,1) | f | {"(1,4)","(4,5)","(5,1)"}
1459 1 | 2 | arc 1 -> 2 | (3,1,2) | f | {"(1,4)","(4,5)","(5,1)","(1,2)"}
1460 1 | 3 | arc 1 -> 3 | (3,1,3) | f | {"(1,4)","(4,5)","(5,1)","(1,3)"}
1461 1 | 4 | arc 1 -> 4 | (3,1,4) | t | {"(1,4)","(4,5)","(5,1)","(1,4)"}
1462 2 | 3 | arc 2 -> 3 | (3,2,3) | f | {"(4,5)","(5,1)","(1,2)","(2,3)"}
1463 4 | 5 | arc 4 -> 5 | (3,4,5) | t | {"(4,5)","(5,1)","(1,4)","(4,5)"}
1464 5 | 1 | arc 5 -> 1 | (3,5,1) | t | {"(5,1)","(1,4)","(4,5)","(5,1)"}
1465 2 | 3 | arc 2 -> 3 | (4,2,3) | f | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"}
1468 -- various syntax errors
1469 with recursive search_graph(f, t, label) as (
1470 select * from graph g
1473 from graph g, search_graph sg
1475 ) cycle foo, tar set is_cycle using path
1476 select * from search_graph;
1477 ERROR: cycle column "foo" not in WITH query column list
1478 LINE 7: ) cycle foo, tar set is_cycle using path
1480 with recursive search_graph(f, t, label) as (
1481 select * from graph g
1484 from graph g, search_graph sg
1486 ) cycle f, t set is_cycle to true default 55 using path
1487 select * from search_graph;
1488 ERROR: CYCLE types boolean and integer cannot be matched
1489 LINE 7: ) cycle f, t set is_cycle to true default 55 using path
1491 with recursive search_graph(f, t, label) as (
1492 select * from graph g
1495 from graph g, search_graph sg
1497 ) cycle f, t set is_cycle to point '(1,1)' default point '(0,0)' using path
1498 select * from search_graph;
1499 ERROR: could not identify an equality operator for type point
1500 with recursive search_graph(f, t, label) as (
1501 select * from graph g
1504 from graph g, search_graph sg
1506 ) cycle f, t set label to true default false using path
1507 select * from search_graph;
1508 ERROR: cycle mark column name "label" already used in WITH query column list
1509 LINE 7: ) cycle f, t set label to true default false using path
1511 with recursive search_graph(f, t, label) as (
1512 select * from graph g
1515 from graph g, search_graph sg
1517 ) cycle f, t set is_cycle to true default false using label
1518 select * from search_graph;
1519 ERROR: cycle path column name "label" already used in WITH query column list
1520 LINE 7: ) cycle f, t set is_cycle to true default false using label
1522 with recursive search_graph(f, t, label) as (
1523 select * from graph g
1526 from graph g, search_graph sg
1528 ) cycle f, t set foo to true default false using foo
1529 select * from search_graph;
1530 ERROR: cycle mark column name and cycle path column name are the same
1531 LINE 7: ) cycle f, t set foo to true default false using foo
1533 with recursive search_graph(f, t, label) as (
1534 select * from graph g
1537 from graph g, search_graph sg
1539 ) cycle f, t, f set is_cycle to true default false using path
1540 select * from search_graph;
1541 ERROR: cycle column "f" specified more than once
1542 LINE 7: ) cycle f, t, f set is_cycle to true default false using pat...
1544 with recursive search_graph(f, t, label) as (
1545 select * from graph g
1548 from graph g, search_graph sg
1550 ) search depth first by f, t set foo
1551 cycle f, t set foo to true default false using path
1552 select * from search_graph;
1553 ERROR: search sequence column name and cycle mark column name are the same
1554 LINE 7: ) search depth first by f, t set foo
1556 with recursive search_graph(f, t, label) as (
1557 select * from graph g
1560 from graph g, search_graph sg
1562 ) search depth first by f, t set foo
1563 cycle f, t set is_cycle to true default false using foo
1564 select * from search_graph;
1565 ERROR: search sequence column name and cycle path column name are the same
1566 LINE 7: ) search depth first by f, t set foo
1568 -- test ruleutils and view expansion
1569 create temp view v_cycle1 as
1570 with recursive search_graph(f, t, label) as (
1571 select * from graph g
1574 from graph g, search_graph sg
1576 ) cycle f, t set is_cycle using path
1577 select f, t, label from search_graph;
1578 create temp view v_cycle2 as
1579 with recursive search_graph(f, t, label) as (
1580 select * from graph g
1583 from graph g, search_graph sg
1585 ) cycle f, t set is_cycle to 'Y' default 'N' using path
1586 select f, t, label from search_graph;
1587 select pg_get_viewdef('v_cycle1');
1589 ------------------------------------------------
1590 WITH RECURSIVE search_graph(f, t, label) AS (+
1601 WHERE (g.f = sg.t) +
1602 ) CYCLE f, t SET is_cycle USING path +
1609 select pg_get_viewdef('v_cycle2');
1611 -----------------------------------------------------------------------------
1612 WITH RECURSIVE search_graph(f, t, label) AS ( +
1623 WHERE (g.f = sg.t) +
1624 ) CYCLE f, t SET is_cycle TO 'Y'::text DEFAULT 'N'::text USING path+
1631 select * from v_cycle1;
1633 ---+---+------------
1661 select * from v_cycle2;
1663 ---+---+------------
1692 -- test multiple WITH queries
1695 y (id) AS (VALUES (1)),
1696 x (id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5)
1707 -- forward reference OK
1709 x(id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5),
1710 y(id) AS (values (1))
1723 (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5),
1725 (VALUES (1) UNION ALL SELECT id+1 FROM y WHERE id < 10)
1726 SELECT y.*, x.* FROM y LEFT JOIN x USING (id);
1743 (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5),
1745 (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 10)
1746 SELECT y.*, x.* FROM y LEFT JOIN x USING (id);
1759 (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ),
1761 (SELECT * FROM x UNION ALL SELECT * FROM x),
1763 (SELECT * FROM x UNION ALL SELECT id+1 FROM z WHERE id < 10)
1798 (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ),
1800 (SELECT * FROM x UNION ALL SELECT * FROM x),
1802 (SELECT * FROM y UNION ALL SELECT id+1 FROM z WHERE id < 10)
1863 -- Test WITH attached to a data-modifying statement
1865 CREATE TEMPORARY TABLE y (a INTEGER);
1866 INSERT INTO y SELECT generate_series(1, 10);
1871 SELECT a+20 FROM t RETURNING *;
1914 UPDATE y SET a = y.a-10 FROM t WHERE y.a > 20 AND t.a = y.a RETURNING y.a;
1954 WITH RECURSIVE t(a) AS (
1957 SELECT a+1 FROM t WHERE a < 50
1959 DELETE FROM y USING t WHERE t.a = y.a RETURNING y.a;
1993 WITH x(n, b) AS (SELECT 1)
1995 ERROR: WITH query "x" has 1 columns available but 2 columns specified
1996 LINE 1: WITH x(n, b) AS (SELECT 1)
1999 WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT SELECT n+1 FROM x)
2001 ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term
2002 LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT SELECT n+1 FROM x...
2004 WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT ALL SELECT n+1 FROM x)
2006 ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term
2007 LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT ALL SELECT n+1 FR...
2010 WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT SELECT n+1 FROM x)
2012 ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term
2013 LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT SELECT n+1 FROM x)
2015 WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT ALL SELECT n+1 FROM x)
2017 ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term
2018 LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT ALL SELECT n+1 FROM ...
2020 -- no non-recursive term
2021 WITH RECURSIVE x(n) AS (SELECT n FROM x)
2023 ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term
2024 LINE 1: WITH RECURSIVE x(n) AS (SELECT n FROM x)
2026 -- recursive term in the left hand side (strictly speaking, should allow this)
2027 WITH RECURSIVE x(n) AS (SELECT n FROM x UNION ALL SELECT 1)
2029 ERROR: recursive reference to query "x" must not appear within its non-recursive term
2030 LINE 1: WITH RECURSIVE x(n) AS (SELECT n FROM x UNION ALL SELECT 1)
2032 -- allow this, because we historically have
2033 WITH RECURSIVE x(n) AS (
2034 WITH x1 AS (SELECT 1 AS n)
2045 -- but this should be rejected
2046 WITH RECURSIVE x(n) AS (
2047 WITH x1 AS (SELECT 1 FROM x)
2052 ERROR: recursive reference to query "x" must not appear within a subquery
2053 LINE 2: WITH x1 AS (SELECT 1 FROM x)
2056 WITH RECURSIVE x(n) AS (
2057 (WITH x1 AS (SELECT 1 FROM x) SELECT * FROM x1)
2061 ERROR: recursive reference to query "x" must not appear within its non-recursive term
2062 LINE 2: (WITH x1 AS (SELECT 1 FROM x) SELECT * FROM x1)
2065 WITH RECURSIVE x(n) AS (
2066 SELECT 0 UNION SELECT 1
2067 ORDER BY (SELECT n FROM x))
2069 ERROR: ORDER BY in a recursive query is not implemented
2070 LINE 3: ORDER BY (SELECT n FROM x))
2072 CREATE TEMPORARY TABLE y (a INTEGER);
2073 INSERT INTO y SELECT generate_series(1, 10);
2075 WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1
2077 SELECT x.n+1 FROM y LEFT JOIN x ON x.n = y.a WHERE n < 10)
2079 ERROR: recursive reference to query "x" must not appear within an outer join
2080 LINE 3: SELECT x.n+1 FROM y LEFT JOIN x ON x.n = y.a WHERE n < 10)
2083 WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1
2085 SELECT x.n+1 FROM x RIGHT JOIN y ON x.n = y.a WHERE n < 10)
2087 ERROR: recursive reference to query "x" must not appear within an outer join
2088 LINE 3: SELECT x.n+1 FROM x RIGHT JOIN y ON x.n = y.a WHERE n < 10)
2091 WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1
2093 SELECT x.n+1 FROM x FULL JOIN y ON x.n = y.a WHERE n < 10)
2095 ERROR: recursive reference to query "x" must not appear within an outer join
2096 LINE 3: SELECT x.n+1 FROM x FULL JOIN y ON x.n = y.a WHERE n < 10)
2099 WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x
2100 WHERE n IN (SELECT * FROM x))
2102 ERROR: recursive reference to query "x" must not appear within a subquery
2103 LINE 2: WHERE n IN (SELECT * FROM x))
2105 -- aggregate functions
2106 WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT count(*) FROM x)
2108 ERROR: aggregate functions are not allowed in a recursive query's recursive term
2109 LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT count(*) F...
2111 WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT sum(n) FROM x)
2113 ERROR: aggregate functions are not allowed in a recursive query's recursive term
2114 LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT sum(n) FRO...
2117 WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x ORDER BY 1)
2119 ERROR: ORDER BY in a recursive query is not implemented
2120 LINE 1: ...VE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x ORDER BY 1)
2123 WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x LIMIT 10 OFFSET 1)
2125 ERROR: OFFSET in a recursive query is not implemented
2126 LINE 1: ... AS (SELECT 1 UNION ALL SELECT n+1 FROM x LIMIT 10 OFFSET 1)
2129 WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x FOR UPDATE)
2131 ERROR: FOR UPDATE/SHARE in a recursive query is not implemented
2132 -- target list has a recursive query name
2133 WITH RECURSIVE x(id) AS (values (1)
2135 SELECT (SELECT * FROM x) FROM x WHERE id < 5
2137 ERROR: recursive reference to query "x" must not appear within a subquery
2138 LINE 3: SELECT (SELECT * FROM x) FROM x WHERE id < 5
2140 -- mutual recursive query (not implemented)
2142 x (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM y WHERE id < 5),
2143 y (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 5)
2145 ERROR: mutual recursion between WITH items is not implemented
2146 LINE 2: x (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM y WHERE id ...
2148 -- non-linear recursion is not allowed
2149 WITH RECURSIVE foo(i) AS
2152 (SELECT i+1 FROM foo WHERE i < 10
2154 SELECT i+1 FROM foo WHERE i < 5)
2155 ) SELECT * FROM foo;
2156 ERROR: recursive reference to query "foo" must not appear more than once
2157 LINE 6: SELECT i+1 FROM foo WHERE i < 5)
2159 WITH RECURSIVE foo(i) AS
2163 (SELECT i+1 FROM foo WHERE i < 10
2165 SELECT i+1 FROM foo WHERE i < 5) AS t
2166 ) SELECT * FROM foo;
2167 ERROR: recursive reference to query "foo" must not appear more than once
2168 LINE 7: SELECT i+1 FROM foo WHERE i < 5) AS t
2170 WITH RECURSIVE foo(i) AS
2173 (SELECT i+1 FROM foo WHERE i < 10
2175 SELECT i+1 FROM foo WHERE i < 5)
2176 ) SELECT * FROM foo;
2177 ERROR: recursive reference to query "foo" must not appear within EXCEPT
2178 LINE 6: SELECT i+1 FROM foo WHERE i < 5)
2180 WITH RECURSIVE foo(i) AS
2183 (SELECT i+1 FROM foo WHERE i < 10
2185 SELECT i+1 FROM foo WHERE i < 5)
2186 ) SELECT * FROM foo;
2187 ERROR: recursive reference to query "foo" must not appear more than once
2188 LINE 6: SELECT i+1 FROM foo WHERE i < 5)
2190 -- Wrong type induced from non-recursive term
2191 WITH RECURSIVE foo(i) AS
2192 (SELECT i FROM (VALUES(1),(2)) t(i)
2194 SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10)
2196 ERROR: recursive query "foo" column 1 has type integer in non-recursive term but type numeric overall
2197 LINE 2: (SELECT i FROM (VALUES(1),(2)) t(i)
2199 HINT: Cast the output of the non-recursive term to the correct type.
2200 -- rejects different typmod, too (should we allow this?)
2201 WITH RECURSIVE foo(i) AS
2202 (SELECT i::numeric(3,0) FROM (VALUES(1),(2)) t(i)
2204 SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10)
2206 ERROR: recursive query "foo" column 1 has type numeric(3,0) in non-recursive term but type numeric overall
2207 LINE 2: (SELECT i::numeric(3,0) FROM (VALUES(1),(2)) t(i)
2209 HINT: Cast the output of the non-recursive term to the correct type.
2210 -- disallow OLD/NEW reference in CTE
2211 CREATE TEMPORARY TABLE x (n integer);
2212 CREATE RULE r2 AS ON UPDATE TO x DO INSTEAD
2213 WITH t AS (SELECT OLD.*) UPDATE y SET a = t.n FROM t;
2214 ERROR: cannot refer to OLD within WITH query
2216 -- test for bug #4902
2218 with cte(foo) as ( values(42) ) values((select foo from cte));
2224 with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q;
2230 -- test CTE referencing an outer-level variable (to see that changed-parameter
2231 -- signaling still works properly after fixing this bug)
2232 select ( with cte(foo) as ( values(f1) )
2233 select (select foo from cte) )
2244 select ( with cte(foo) as ( values(f1) )
2245 values((select foo from cte)) )
2257 -- test for nested-recursive-WITH bug
2259 WITH RECURSIVE t(j) AS (
2260 WITH RECURSIVE s(i) AS (
2263 SELECT i+1 FROM s WHERE i < 10
2267 SELECT j+1 FROM t WHERE j < 10
2330 -- test WITH attached to intermediate-level set operation
2332 WITH outermost(x) AS (
2334 UNION (WITH innermost as (SELECT 2)
2335 SELECT * FROM innermost
2338 SELECT * FROM outermost ORDER BY 1;
2346 WITH outermost(x) AS (
2348 UNION (WITH innermost as (SELECT 2)
2349 SELECT * FROM outermost -- fail
2350 UNION SELECT * FROM innermost)
2352 SELECT * FROM outermost ORDER BY 1;
2353 ERROR: relation "outermost" does not exist
2354 LINE 4: SELECT * FROM outermost -- fail
2356 DETAIL: There is a WITH item named "outermost", but it cannot be referenced from this part of the query.
2357 HINT: Use WITH RECURSIVE, or re-order the WITH items to remove forward references.
2358 WITH RECURSIVE outermost(x) AS (
2360 UNION (WITH innermost as (SELECT 2)
2361 SELECT * FROM outermost
2362 UNION SELECT * FROM innermost)
2364 SELECT * FROM outermost ORDER BY 1;
2371 WITH RECURSIVE outermost(x) AS (
2372 WITH innermost as (SELECT 2 FROM outermost) -- fail
2373 SELECT * FROM innermost
2374 UNION SELECT * from outermost
2376 SELECT * FROM outermost ORDER BY 1;
2377 ERROR: recursive reference to query "outermost" must not appear within a subquery
2378 LINE 2: WITH innermost as (SELECT 2 FROM outermost) -- fail
2381 -- This test will fail with the old implementation of PARAM_EXEC parameter
2382 -- assignment, because the "q1" Var passed down to A's targetlist subselect
2383 -- looks exactly like the "A.id" Var passed down to C's subselect, causing
2384 -- the old code to give them the same runtime PARAM_EXEC slot. But the
2385 -- lifespans of the two parameters overlap, thanks to B also reading A.
2388 A as ( select q2 as id, (select q1) as x from int8_tbl ),
2389 B as ( select id, row_number() over (partition by id) as r from A ),
2390 C as ( select A.id, array(select B.id from B where B.id = A.id) from A )
2393 -------------------+-------------------------------------
2395 4567890123456789 | {4567890123456789,4567890123456789}
2397 4567890123456789 | {4567890123456789,4567890123456789}
2398 -4567890123456789 | {-4567890123456789}
2402 -- Test CTEs read in non-initialization orders
2405 tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)),
2406 iter (id_key, row_type, link) AS (
2407 SELECT 0, 'base', 17
2409 WITH remaining(id_key, row_type, link, min) AS (
2410 SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER ()
2411 FROM tab INNER JOIN iter USING (link)
2412 WHERE tab.id_key > iter.id_key
2414 first_remaining AS (
2415 SELECT id_key, row_type, link
2420 SELECT tab.id_key, 'new'::text, tab.link
2421 FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key
2422 WHERE e.row_type = 'false'
2424 SELECT * FROM first_remaining
2425 UNION ALL SELECT * FROM effect
2429 id_key | row_type | link
2430 --------+----------+------
2441 tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)),
2442 iter (id_key, row_type, link) AS (
2443 SELECT 0, 'base', 17
2445 WITH remaining(id_key, row_type, link, min) AS (
2446 SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER ()
2447 FROM tab INNER JOIN iter USING (link)
2448 WHERE tab.id_key > iter.id_key
2450 first_remaining AS (
2451 SELECT id_key, row_type, link
2456 SELECT tab.id_key, 'new'::text, tab.link
2457 FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key
2458 WHERE e.row_type = 'false'
2460 SELECT * FROM first_remaining
2461 UNION ALL SELECT * FROM effect
2465 id_key | row_type | link
2466 --------+----------+------
2477 -- Data-modifying statements in WITH
2479 -- INSERT ... RETURNING
2535 -- UPDATE ... RETURNING
2591 -- DELETE ... RETURNING
2627 -- forward reference
2628 WITH RECURSIVE t AS (
2630 SELECT a+5 FROM t2 WHERE a > 5
2633 UPDATE y SET a=a-11 RETURNING *
2679 -- unconditional DO INSTEAD rule
2680 CREATE RULE y_rule AS ON DELETE TO y DO INSTEAD
2681 INSERT INTO y VALUES(42) RETURNING *;
2683 DELETE FROM y RETURNING *
2713 DROP RULE y_rule ON y;
2714 -- check merging of outer CTE with CTE in a rule action
2715 CREATE TEMP TABLE bug6051 AS
2716 select i from generate_series(1,3) as t(i);
2717 SELECT * FROM bug6051;
2725 WITH t1 AS ( DELETE FROM bug6051 RETURNING * )
2726 INSERT INTO bug6051 SELECT * FROM t1;
2727 SELECT * FROM bug6051;
2735 CREATE TEMP TABLE bug6051_2 (i int);
2736 CREATE RULE bug6051_ins AS ON INSERT TO bug6051 DO INSTEAD
2737 INSERT INTO bug6051_2
2739 WITH t1 AS ( DELETE FROM bug6051 RETURNING * )
2740 INSERT INTO bug6051 SELECT * FROM t1;
2741 SELECT * FROM bug6051;
2746 SELECT * FROM bug6051_2;
2754 -- check INSERT ... SELECT rule actions are disallowed on commands
2755 -- that have modifyingCTEs
2756 CREATE OR REPLACE RULE bug6051_ins AS ON INSERT TO bug6051 DO INSTEAD
2757 INSERT INTO bug6051_2
2759 WITH t1 AS ( DELETE FROM bug6051 RETURNING * )
2760 INSERT INTO bug6051 SELECT * FROM t1;
2761 ERROR: INSERT ... SELECT rule actions are not supported for queries having data-modifying statements in WITH
2762 -- silly example to verify that hasModifyingCTE flag is propagated
2763 CREATE TEMP TABLE bug6051_3 AS
2764 SELECT a FROM generate_series(11,13) AS a;
2765 CREATE RULE bug6051_3_ins AS ON INSERT TO bug6051_3 DO INSTEAD
2766 SELECT i FROM bug6051_2;
2767 BEGIN; SET LOCAL debug_parallel_query = on;
2768 WITH t1 AS ( DELETE FROM bug6051_3 RETURNING * )
2769 INSERT INTO bug6051_3 SELECT * FROM t1;
2784 SELECT * FROM bug6051_3;
2789 -- check case where CTE reference is removed due to optimization
2790 EXPLAIN (VERBOSE, COSTS OFF)
2793 WITH t_cte AS (SELECT * FROM int8_tbl t)
2794 SELECT q1, (SELECT q2 FROM t_cte WHERE t_cte.q1 = i8.q1) AS t_sub
2798 --------------------------------------
2801 -> Seq Scan on public.int8_tbl i8
2802 Output: i8.q1, NULL::bigint
2807 WITH t_cte AS (SELECT * FROM int8_tbl t)
2808 SELECT q1, (SELECT q2 FROM t_cte WHERE t_cte.q1 = i8.q1) AS t_sub
2820 EXPLAIN (VERBOSE, COSTS OFF)
2823 WITH t_cte AS MATERIALIZED (SELECT * FROM int8_tbl t)
2824 SELECT q1, (SELECT q2 FROM t_cte WHERE t_cte.q1 = i8.q1) AS t_sub
2828 ---------------------------------------------
2831 -> Seq Scan on public.int8_tbl i8
2832 Output: i8.q1, NULL::bigint
2834 -> Seq Scan on public.int8_tbl t
2840 WITH t_cte AS MATERIALIZED (SELECT * FROM int8_tbl t)
2841 SELECT q1, (SELECT q2 FROM t_cte WHERE t_cte.q1 = i8.q1) AS t_sub
2853 -- a truly recursive CTE in the same list
2854 WITH RECURSIVE t(a) AS (
2857 SELECT a+1 FROM t WHERE a+1 < 5
2860 SELECT * FROM t RETURNING *
2862 SELECT * FROM t2 JOIN y USING (a) ORDER BY a;
2899 -- data-modifying WITH in a modifying statement
2905 INSERT INTO y SELECT -a FROM t RETURNING *;
2953 -- check that WITH query is run to completion even if outer query isn't
2955 UPDATE y SET a = a * 100 RETURNING *
2957 SELECT * FROM t LIMIT 10;
2999 -- data-modifying WITH containing INSERT...ON CONFLICT DO UPDATE
3000 CREATE TABLE withz AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i;
3001 ALTER TABLE withz ADD UNIQUE (k);
3003 INSERT INTO withz SELECT i, 'insert'
3004 FROM generate_series(0, 16) i
3005 ON CONFLICT (k) DO UPDATE SET v = withz.v || ', now update'
3008 SELECT * FROM t JOIN y ON t.k = y.a ORDER BY a, k;
3015 -- Test EXCLUDED.* reference within CTE
3017 INSERT INTO withz VALUES(1, 5) ON CONFLICT (k) DO UPDATE SET v = EXCLUDED.v
3018 WHERE withz.k != EXCLUDED.k
3026 -- New query/snapshot demonstrates side-effects of previous query.
3027 SELECT * FROM withz ORDER BY k;
3029 ----+------------------
3040 10 | 10 v, now update
3043 13 | 13 v, now update
3046 16 | 16 v, now update
3050 -- Ensure subqueries within the update clause work, even if they
3051 -- reference outside values
3053 WITH aa AS (SELECT 1 a, 2 b)
3054 INSERT INTO withz VALUES(1, 'insert')
3055 ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1);
3056 WITH aa AS (SELECT 1 a, 2 b)
3057 INSERT INTO withz VALUES(1, 'insert')
3058 ON CONFLICT (k) DO UPDATE SET v = ' update' WHERE withz.k = (SELECT a FROM aa);
3059 WITH aa AS (SELECT 1 a, 2 b)
3060 INSERT INTO withz VALUES(1, 'insert')
3061 ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1);
3062 WITH aa AS (SELECT 'a' a, 'b' b UNION ALL SELECT 'a' a, 'b' b)
3063 INSERT INTO withz VALUES(1, 'insert')
3064 ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 'a' LIMIT 1);
3065 WITH aa AS (SELECT 1 a, 2 b)
3066 INSERT INTO withz VALUES(1, (SELECT b || ' insert' FROM aa WHERE a = 1 ))
3067 ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1);
3068 -- Update a row more than once, in different parts of a wCTE. That is
3069 -- an allowed, presumably very rare, edge case, but since it was
3070 -- broken in the past, having a test seems worthwhile.
3072 SELECT 2 k, 'Green' v),
3074 INSERT INTO withz VALUES(2, 'Blue') ON CONFLICT (k) DO
3075 UPDATE SET (k, v) = (SELECT k, v FROM simpletup WHERE simpletup.k = withz.k)
3077 INSERT INTO withz VALUES(2, 'Red') ON CONFLICT (k) DO
3078 UPDATE SET (k, v) = (SELECT k, v FROM upsert_cte WHERE upsert_cte.k = withz.k)
3085 -- WITH referenced by MERGE statement
3086 CREATE TABLE m AS SELECT i AS k, (i || ' v')::text v FROM generate_series(1, 16, 3) i;
3087 ALTER TABLE m ADD UNIQUE (k);
3088 WITH RECURSIVE cte_basic AS (SELECT 1 a, 'cte_basic val' b)
3089 MERGE INTO m USING (select 0 k, 'merge source SubPlan' v) o ON m.k=o.k
3090 WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_basic WHERE cte_basic.a = m.k LIMIT 1)
3091 WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v);
3092 ERROR: WITH RECURSIVE is not supported for MERGE statement
3094 WITH cte_basic AS MATERIALIZED (SELECT 1 a, 'cte_basic val' b)
3095 MERGE INTO m USING (select 0 k, 'merge source SubPlan' v offset 0) o ON m.k=o.k
3096 WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_basic WHERE cte_basic.a = m.k LIMIT 1)
3097 WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v);
3099 SELECT * FROM m where k = 0;
3101 ---+----------------------
3102 0 | merge source SubPlan
3105 -- See EXPLAIN output for same query:
3106 EXPLAIN (VERBOSE, COSTS OFF)
3107 WITH cte_basic AS MATERIALIZED (SELECT 1 a, 'cte_basic val' b)
3108 MERGE INTO m USING (select 0 k, 'merge source SubPlan' v offset 0) o ON m.k=o.k
3109 WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_basic WHERE cte_basic.a = m.k LIMIT 1)
3110 WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v);
3112 -------------------------------------------------------------------
3116 Output: 1, 'cte_basic val'::text
3118 Output: m.ctid, o.k, o.v, o.*
3119 Hash Cond: (m.k = o.k)
3120 -> Seq Scan on public.m
3123 Output: o.k, o.v, o.*
3124 -> Subquery Scan on o
3125 Output: o.k, o.v, o.*
3127 Output: 0, 'merge source SubPlan'::text
3130 Output: ((cte_basic.b || ' merge update'::text))
3131 -> CTE Scan on cte_basic
3132 Output: (cte_basic.b || ' merge update'::text)
3133 Filter: (cte_basic.a = m.k)
3137 WITH cte_init AS MATERIALIZED (SELECT 1 a, 'cte_init val' b)
3138 MERGE INTO m USING (select 1 k, 'merge source InitPlan' v offset 0) o ON m.k=o.k
3139 WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_init WHERE a = 1 LIMIT 1)
3140 WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v);
3142 SELECT * FROM m where k = 1;
3144 ---+---------------------------
3145 1 | cte_init val merge update
3148 -- See EXPLAIN output for same query:
3149 EXPLAIN (VERBOSE, COSTS OFF)
3150 WITH cte_init AS MATERIALIZED (SELECT 1 a, 'cte_init val' b)
3151 MERGE INTO m USING (select 1 k, 'merge source InitPlan' v offset 0) o ON m.k=o.k
3152 WHEN MATCHED THEN UPDATE SET v = (SELECT b || ' merge update' FROM cte_init WHERE a = 1 LIMIT 1)
3153 WHEN NOT MATCHED THEN INSERT VALUES(o.k, o.v);
3155 --------------------------------------------------------------------
3159 Output: 1, 'cte_init val'::text
3162 Output: ((cte_init.b || ' merge update'::text))
3163 -> CTE Scan on cte_init
3164 Output: (cte_init.b || ' merge update'::text)
3165 Filter: (cte_init.a = 1)
3167 Output: m.ctid, o.k, o.v, o.*
3168 Hash Cond: (m.k = o.k)
3169 -> Seq Scan on public.m
3172 Output: o.k, o.v, o.*
3173 -> Subquery Scan on o
3174 Output: o.k, o.v, o.*
3176 Output: 1, 'merge source InitPlan'::text
3179 -- MERGE source comes from CTE:
3180 WITH merge_source_cte AS MATERIALIZED (SELECT 15 a, 'merge_source_cte val' b)
3181 MERGE INTO m USING (select * from merge_source_cte) o ON m.k=o.a
3182 WHEN MATCHED THEN UPDATE SET v = (SELECT b || merge_source_cte.*::text || ' merge update' FROM merge_source_cte WHERE a = 15)
3183 WHEN NOT MATCHED THEN INSERT VALUES(o.a, o.b || (SELECT merge_source_cte.*::text || ' merge insert' FROM merge_source_cte));
3185 SELECT * FROM m where k = 15;
3187 ----+--------------------------------------------------------------
3188 15 | merge_source_cte val(15,"merge_source_cte val") merge insert
3191 -- See EXPLAIN output for same query:
3192 EXPLAIN (VERBOSE, COSTS OFF)
3193 WITH merge_source_cte AS MATERIALIZED (SELECT 15 a, 'merge_source_cte val' b)
3194 MERGE INTO m USING (select * from merge_source_cte) o ON m.k=o.a
3195 WHEN MATCHED THEN UPDATE SET v = (SELECT b || merge_source_cte.*::text || ' merge update' FROM merge_source_cte WHERE a = 15)
3196 WHEN NOT MATCHED THEN INSERT VALUES(o.a, o.b || (SELECT merge_source_cte.*::text || ' merge insert' FROM merge_source_cte));
3198 -----------------------------------------------------------------------------------------------------
3200 CTE merge_source_cte
3202 Output: 15, 'merge_source_cte val'::text
3204 -> CTE Scan on merge_source_cte merge_source_cte_1
3205 Output: ((merge_source_cte_1.b || (merge_source_cte_1.*)::text) || ' merge update'::text)
3206 Filter: (merge_source_cte_1.a = 15)
3208 -> CTE Scan on merge_source_cte merge_source_cte_2
3209 Output: ((merge_source_cte_2.*)::text || ' merge insert'::text)
3211 Output: m.ctid, merge_source_cte.a, merge_source_cte.b, merge_source_cte.*
3212 Hash Cond: (m.k = merge_source_cte.a)
3213 -> Seq Scan on public.m
3216 Output: merge_source_cte.a, merge_source_cte.b, merge_source_cte.*
3217 -> CTE Scan on merge_source_cte
3218 Output: merge_source_cte.a, merge_source_cte.b, merge_source_cte.*
3222 -- check that run to completion happens in proper ordering
3224 INSERT INTO y SELECT generate_series(1, 3);
3225 CREATE TEMPORARY TABLE yy (a INTEGER);
3226 WITH RECURSIVE t1 AS (
3227 INSERT INTO y SELECT * FROM y RETURNING *
3229 INSERT INTO yy SELECT * FROM t1 RETURNING *
3256 WITH RECURSIVE t1 AS (
3257 INSERT INTO yy SELECT * FROM t2 RETURNING *
3259 INSERT INTO y SELECT * FROM y RETURNING *
3300 INSERT INTO y SELECT generate_series(1, 10);
3301 CREATE FUNCTION y_trigger() RETURNS trigger AS $$
3303 raise notice 'y_trigger: a = %', new.a;
3306 $$ LANGUAGE plpgsql;
3307 CREATE TRIGGER y_trig BEFORE INSERT ON y FOR EACH ROW
3308 EXECUTE PROCEDURE y_trigger();
3318 NOTICE: y_trigger: a = 21
3319 NOTICE: y_trigger: a = 22
3320 NOTICE: y_trigger: a = 23
3346 DROP TRIGGER y_trig ON y;
3347 CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH ROW
3348 EXECUTE PROCEDURE y_trigger();
3357 SELECT * FROM t LIMIT 1;
3358 NOTICE: y_trigger: a = 31
3359 NOTICE: y_trigger: a = 32
3360 NOTICE: y_trigger: a = 33
3387 DROP TRIGGER y_trig ON y;
3388 CREATE OR REPLACE FUNCTION y_trigger() RETURNS trigger AS $$
3390 raise notice 'y_trigger';
3393 $$ LANGUAGE plpgsql;
3394 CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH STATEMENT
3395 EXECUTE PROCEDURE y_trigger();
3437 DROP TRIGGER y_trig ON y;
3438 DROP FUNCTION y_trigger();
3439 -- WITH attached to inherited UPDATE or DELETE
3440 CREATE TEMP TABLE parent ( id int, val text );
3441 CREATE TEMP TABLE child1 ( ) INHERITS ( parent );
3442 CREATE TEMP TABLE child2 ( ) INHERITS ( parent );
3443 INSERT INTO parent VALUES ( 1, 'p1' );
3444 INSERT INTO child1 VALUES ( 11, 'c11' ),( 12, 'c12' );
3445 INSERT INTO child2 VALUES ( 23, 'c21' ),( 24, 'c22' );
3446 WITH rcte AS ( SELECT sum(id) AS totalid FROM parent )
3447 UPDATE parent SET id = id + totalid FROM rcte;
3448 SELECT * FROM parent;
3458 WITH wcte AS ( INSERT INTO child1 VALUES ( 42, 'new' ) RETURNING id AS newid )
3459 UPDATE parent SET id = id + newid FROM wcte;
3460 SELECT * FROM parent;
3471 WITH rcte AS ( SELECT max(id) AS maxid FROM parent )
3472 DELETE FROM parent USING rcte WHERE id = maxid;
3473 SELECT * FROM parent;
3483 WITH wcte AS ( INSERT INTO child2 VALUES ( 42, 'new2' ) RETURNING id AS newid )
3484 DELETE FROM parent USING wcte WHERE id = newid;
3485 SELECT * FROM parent;
3495 -- check EXPLAIN VERBOSE for a wCTE with RETURNING
3496 EXPLAIN (VERBOSE, COSTS OFF)
3497 WITH wcte AS ( INSERT INTO int8_tbl VALUES ( 42, 47 ) RETURNING q2 )
3498 DELETE FROM a_star USING wcte WHERE aa = q2;
3500 ---------------------------------------------------------------------------
3501 Delete on public.a_star
3502 Delete on public.a_star a_star_1
3503 Delete on public.b_star a_star_2
3504 Delete on public.c_star a_star_3
3505 Delete on public.d_star a_star_4
3506 Delete on public.e_star a_star_5
3507 Delete on public.f_star a_star_6
3509 -> Insert on public.int8_tbl
3512 Output: '42'::bigint, '47'::bigint
3514 Output: wcte.*, a_star.tableoid, a_star.ctid
3515 Hash Cond: (a_star.aa = wcte.q2)
3517 -> Seq Scan on public.a_star a_star_1
3518 Output: a_star_1.aa, a_star_1.tableoid, a_star_1.ctid
3519 -> Seq Scan on public.b_star a_star_2
3520 Output: a_star_2.aa, a_star_2.tableoid, a_star_2.ctid
3521 -> Seq Scan on public.c_star a_star_3
3522 Output: a_star_3.aa, a_star_3.tableoid, a_star_3.ctid
3523 -> Seq Scan on public.d_star a_star_4
3524 Output: a_star_4.aa, a_star_4.tableoid, a_star_4.ctid
3525 -> Seq Scan on public.e_star a_star_5
3526 Output: a_star_5.aa, a_star_5.tableoid, a_star_5.ctid
3527 -> Seq Scan on public.f_star a_star_6
3528 Output: a_star_6.aa, a_star_6.tableoid, a_star_6.ctid
3530 Output: wcte.*, wcte.q2
3532 Output: wcte.*, wcte.q2
3536 -- data-modifying WITH tries to use its own output
3537 WITH RECURSIVE t AS (
3542 ERROR: recursive query "t" must not contain data-modifying statements
3543 LINE 1: WITH RECURSIVE t AS (
3545 -- no RETURNING in a referenced data-modifying WITH
3547 INSERT INTO y VALUES(0)
3550 ERROR: WITH query "t" does not have a RETURNING clause
3551 LINE 4: SELECT * FROM t;
3553 -- RETURNING tries to return its own output
3554 WITH RECURSIVE t(action, a) AS (
3555 MERGE INTO y USING (VALUES (11)) v(a) ON y.a = v.a
3556 WHEN NOT MATCHED THEN INSERT VALUES (v.a)
3557 RETURNING merge_action(), (SELECT a FROM t)
3560 ERROR: recursive query "t" must not contain data-modifying statements
3561 LINE 1: WITH RECURSIVE t(action, a) AS (
3563 -- data-modifying WITH allowed only at the top level
3565 WITH t AS (UPDATE y SET a=a+1 RETURNING *)
3568 ERROR: WITH clause containing a data-modifying statement must be at the top level
3569 LINE 2: WITH t AS (UPDATE y SET a=a+1 RETURNING *)
3571 -- most variants of rules aren't allowed
3572 CREATE RULE y_rule AS ON INSERT TO y WHERE a=0 DO INSTEAD DELETE FROM y;
3574 INSERT INTO y VALUES(0)
3577 ERROR: conditional DO INSTEAD rules are not supported for data-modifying statements in WITH
3578 CREATE OR REPLACE RULE y_rule AS ON INSERT TO y DO INSTEAD NOTHING;
3580 INSERT INTO y VALUES(0)
3583 ERROR: DO INSTEAD NOTHING rules are not supported for data-modifying statements in WITH
3584 CREATE OR REPLACE RULE y_rule AS ON INSERT TO y DO INSTEAD NOTIFY foo;
3586 INSERT INTO y VALUES(0)
3589 ERROR: DO INSTEAD NOTIFY rules are not supported for data-modifying statements in WITH
3590 CREATE OR REPLACE RULE y_rule AS ON INSERT TO y DO ALSO NOTIFY foo;
3592 INSERT INTO y VALUES(0)
3595 ERROR: DO ALSO rules are not supported for data-modifying statements in WITH
3596 CREATE OR REPLACE RULE y_rule AS ON INSERT TO y
3597 DO INSTEAD (NOTIFY foo; NOTIFY bar);
3599 INSERT INTO y VALUES(0)
3602 ERROR: multi-statement DO INSTEAD rules are not supported for data-modifying statements in WITH
3603 DROP RULE y_rule ON y;
3604 -- check that parser lookahead for WITH doesn't cause any odd behavior
3605 create table foo (with baz); -- fail, WITH is a reserved word
3606 ERROR: syntax error at or near "with"
3607 LINE 1: create table foo (with baz);
3609 create table foo (with ordinality); -- fail, WITH is a reserved word
3610 ERROR: syntax error at or near "with"
3611 LINE 1: create table foo (with ordinality);
3613 with ordinality as (select 1 as x) select * from ordinality;
3619 -- check sane response to attempt to modify CTE relation
3620 WITH with_test AS (SELECT 42) INSERT INTO with_test VALUES (1);
3621 ERROR: relation "with_test" does not exist
3622 LINE 1: WITH with_test AS (SELECT 42) INSERT INTO with_test VALUES (...
3624 -- check response to attempt to modify table with same name as a CTE (perhaps
3625 -- surprisingly it works, because CTEs don't hide tables from data-modifying
3627 create temp table with_test (i int);
3628 with with_test as (select 42) insert into with_test select * from with_test;
3629 select * from with_test;
3635 drop table with_test;