*resultTupleDesc = BlessTupleDesc(tupdesc);
return TYPEFUNC_COMPOSITE;
}
+ else if (expr && IsA(expr, Const) &&
+ ((Const *) expr)->consttype == RECORDOID &&
+ !((Const *) expr)->constisnull)
+ {
+ /*
+ * When EXPLAIN'ing some queries with SEARCH/CYCLE clauses, we may
+ * need to resolve field names of a RECORD-type Const. The datum
+ * should contain a typmod that will tell us that.
+ */
+ HeapTupleHeader rec;
+ Oid tupType;
+ int32 tupTypmod;
+
+ rec = DatumGetHeapTupleHeader(((Const *) expr)->constvalue);
+ tupType = HeapTupleHeaderGetTypeId(rec);
+ tupTypmod = HeapTupleHeaderGetTypMod(rec);
+ if (resultTypeId)
+ *resultTypeId = tupType;
+ if (tupType != RECORDOID || tupTypmod >= 0)
+ {
+ /* Should be able to look it up */
+ if (resultTupleDesc)
+ *resultTupleDesc = lookup_rowtype_tupdesc_copy(tupType,
+ tupTypmod);
+ return TYPEFUNC_COMPOSITE;
+ }
+ else
+ {
+ /* This shouldn't really happen ... */
+ if (resultTupleDesc)
+ *resultTupleDesc = NULL;
+ return TYPEFUNC_RECORD;
+ }
+ }
else
{
/* handle as a generic expression; no chance to resolve RECORD */
4 | 5 | arc 4 -> 5 | (1,4,5)
(7 rows)
+-- a constant initial value causes issues for EXPLAIN
+explain (verbose, costs off)
+with recursive test as (
+ select 1 as x
+ union all
+ select x + 1
+ from test
+) search depth first by x set y
+select * from test limit 5;
+ QUERY PLAN
+-----------------------------------------------------------------------------------------
+ Limit
+ Output: test.x, test.y
+ CTE test
+ -> Recursive Union
+ -> Result
+ Output: 1, '{(1)}'::record[]
+ -> WorkTable Scan on test test_1
+ Output: (test_1.x + 1), array_cat(test_1.y, ARRAY[ROW((test_1.x + 1))])
+ -> CTE Scan on test
+ Output: test.x, test.y
+(10 rows)
+
+with recursive test as (
+ select 1 as x
+ union all
+ select x + 1
+ from test
+) search depth first by x set y
+select * from test limit 5;
+ x | y
+---+-----------------------
+ 1 | {(1)}
+ 2 | {(1),(2)}
+ 3 | {(1),(2),(3)}
+ 4 | {(1),(2),(3),(4)}
+ 5 | {(1),(2),(3),(4),(5)}
+(5 rows)
+
+explain (verbose, costs off)
+with recursive test as (
+ select 1 as x
+ union all
+ select x + 1
+ from test
+) search breadth first by x set y
+select * from test limit 5;
+ QUERY PLAN
+--------------------------------------------------------------------------------------------
+ Limit
+ Output: test.x, test.y
+ CTE test
+ -> Recursive Union
+ -> Result
+ Output: 1, '(0,1)'::record
+ -> WorkTable Scan on test test_1
+ Output: (test_1.x + 1), ROW(int8inc((test_1.y)."*DEPTH*"), (test_1.x + 1))
+ -> CTE Scan on test
+ Output: test.x, test.y
+(10 rows)
+
+with recursive test as (
+ select 1 as x
+ union all
+ select x + 1
+ from test
+) search breadth first by x set y
+select * from test limit 5;
+ x | y
+---+-------
+ 1 | (0,1)
+ 2 | (1,2)
+ 3 | (2,3)
+ 4 | (3,4)
+ 5 | (4,5)
+(5 rows)
+
-- various syntax errors
with recursive search_graph(f, t, label) as (
select * from graph0 g
2 | 3 | arc 2 -> 3 | N | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"}
(25 rows)
+explain (verbose, costs off)
+with recursive test as (
+ select 0 as x
+ union all
+ select (x + 1) % 10
+ from test
+) cycle x set is_cycle using path
+select * from test;
+ QUERY PLAN
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ CTE Scan on test
+ Output: test.x, test.is_cycle, test.path
+ CTE test
+ -> Recursive Union
+ -> Result
+ Output: 0, false, '{(0)}'::record[]
+ -> WorkTable Scan on test test_1
+ 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))])
+ Filter: (NOT test_1.is_cycle)
+(9 rows)
+
+with recursive test as (
+ select 0 as x
+ union all
+ select (x + 1) % 10
+ from test
+) cycle x set is_cycle using path
+select * from test;
+ x | is_cycle | path
+---+----------+-----------------------------------------------
+ 0 | f | {(0)}
+ 1 | f | {(0),(1)}
+ 2 | f | {(0),(1),(2)}
+ 3 | f | {(0),(1),(2),(3)}
+ 4 | f | {(0),(1),(2),(3),(4)}
+ 5 | f | {(0),(1),(2),(3),(4),(5)}
+ 6 | f | {(0),(1),(2),(3),(4),(5),(6)}
+ 7 | f | {(0),(1),(2),(3),(4),(5),(6),(7)}
+ 8 | f | {(0),(1),(2),(3),(4),(5),(6),(7),(8)}
+ 9 | f | {(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)}
+ 0 | t | {(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(0)}
+(11 rows)
+
-- multiple CTEs
with recursive
graph(f, t, label) as (
) search breadth first by f, t set seq
select * from search_graph order by seq;
+-- a constant initial value causes issues for EXPLAIN
+explain (verbose, costs off)
+with recursive test as (
+ select 1 as x
+ union all
+ select x + 1
+ from test
+) search depth first by x set y
+select * from test limit 5;
+
+with recursive test as (
+ select 1 as x
+ union all
+ select x + 1
+ from test
+) search depth first by x set y
+select * from test limit 5;
+
+explain (verbose, costs off)
+with recursive test as (
+ select 1 as x
+ union all
+ select x + 1
+ from test
+) search breadth first by x set y
+select * from test limit 5;
+
+with recursive test as (
+ select 1 as x
+ union all
+ select x + 1
+ from test
+) search breadth first by x set y
+select * from test limit 5;
+
-- various syntax errors
with recursive search_graph(f, t, label) as (
select * from graph0 g
) cycle f, t set is_cycle to 'Y' default 'N' using path
select * from search_graph;
+explain (verbose, costs off)
+with recursive test as (
+ select 0 as x
+ union all
+ select (x + 1) % 10
+ from test
+) cycle x set is_cycle using path
+select * from test;
+
+with recursive test as (
+ select 0 as x
+ union all
+ select (x + 1) % 10
+ from test
+) cycle x set is_cycle using path
+select * from test;
+
-- multiple CTEs
with recursive
graph(f, t, label) as (