* The Param slot associated with the CTE query is used to hold a pointer
* to the CteState of the first CteScan node that initializes for this
* CTE. This node will be the one that holds the shared state for all the
- * CTEs.
+ * CTEs, particularly the shared tuplestore.
*/
prmdata = &(estate->es_param_exec_vals[node->cteParam]);
Assert(prmdata->execPlan == NULL);
* If I am the leader, free the tuplestore.
*/
if (node->leader == node)
+ {
tuplestore_end(node->cte_table);
+ node->cte_table = NULL;
+ }
}
/* ----------------------------------------------------------------
ExecScanReScan(&node->ss);
- if (node->leader == node)
+ /*
+ * Clear the tuplestore if a new scan of the underlying CTE is required.
+ * This implicitly resets all the tuplestore's read pointers. Note that
+ * multiple CTE nodes might redundantly clear the tuplestore; that's OK,
+ * and not unduly expensive. We'll stop taking this path as soon as
+ * somebody has attempted to read something from the underlying CTE
+ * (thereby causing its chgParam to be cleared).
+ */
+ if (node->leader->cteplanstate->chgParam != NULL)
{
- /*
- * The leader is responsible for clearing the tuplestore if a new scan
- * of the underlying CTE is required.
- */
- if (node->cteplanstate->chgParam != NULL)
- {
- tuplestore_clear(tuplestorestate);
- node->eof_cte = false;
- }
- else
- {
- tuplestore_select_read_pointer(tuplestorestate, node->readptr);
- tuplestore_rescan(tuplestorestate);
- }
+ tuplestore_clear(tuplestorestate);
+ node->leader->eof_cte = false;
}
else
{
- /* Not leader, so just rewind my own pointer */
+ /*
+ * Else, just rewind my own pointer. Either the underlying CTE
+ * doesn't need a rescan (and we can re-read what's in the tuplestore
+ * now), or somebody else already took care of it.
+ */
tuplestore_select_read_pointer(tuplestorestate, node->readptr);
tuplestore_rescan(tuplestorestate);
}
ERROR: recursive reference to query "outermost" must not appear within a subquery
LINE 2: WITH innermost as (SELECT 2 FROM outermost)
^
+--
+-- Test CTEs read in non-initialization orders
+--
+WITH RECURSIVE
+ tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)),
+ iter (id_key, row_type, link) AS (
+ SELECT 0, 'base', 17
+ UNION ALL (
+ WITH remaining(id_key, row_type, link, min) AS (
+ SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER ()
+ FROM tab INNER JOIN iter USING (link)
+ WHERE tab.id_key > iter.id_key
+ ),
+ first_remaining AS (
+ SELECT id_key, row_type, link
+ FROM remaining
+ WHERE id_key=min
+ ),
+ effect AS (
+ SELECT tab.id_key, 'new'::text, tab.link
+ FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key
+ WHERE e.row_type = 'false'
+ )
+ SELECT * FROM first_remaining
+ UNION ALL SELECT * FROM effect
+ )
+ )
+SELECT * FROM iter;
+ id_key | row_type | link
+--------+----------+------
+ 0 | base | 17
+ 1 | true | 17
+ 2 | true | 17
+ 3 | true | 17
+ 4 | true | 17
+ 5 | true | 17
+ 6 | true | 17
+(7 rows)
+
+WITH RECURSIVE
+ tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)),
+ iter (id_key, row_type, link) AS (
+ SELECT 0, 'base', 17
+ UNION (
+ WITH remaining(id_key, row_type, link, min) AS (
+ SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER ()
+ FROM tab INNER JOIN iter USING (link)
+ WHERE tab.id_key > iter.id_key
+ ),
+ first_remaining AS (
+ SELECT id_key, row_type, link
+ FROM remaining
+ WHERE id_key=min
+ ),
+ effect AS (
+ SELECT tab.id_key, 'new'::text, tab.link
+ FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key
+ WHERE e.row_type = 'false'
+ )
+ SELECT * FROM first_remaining
+ UNION ALL SELECT * FROM effect
+ )
+ )
+SELECT * FROM iter;
+ id_key | row_type | link
+--------+----------+------
+ 0 | base | 17
+ 1 | true | 17
+ 2 | true | 17
+ 3 | true | 17
+ 4 | true | 17
+ 5 | true | 17
+ 6 | true | 17
+(7 rows)
+
--
-- Data-modifying statements in WITH
--
)
SELECT * FROM outermost;
+--
+-- Test CTEs read in non-initialization orders
+--
+
+WITH RECURSIVE
+ tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)),
+ iter (id_key, row_type, link) AS (
+ SELECT 0, 'base', 17
+ UNION ALL (
+ WITH remaining(id_key, row_type, link, min) AS (
+ SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER ()
+ FROM tab INNER JOIN iter USING (link)
+ WHERE tab.id_key > iter.id_key
+ ),
+ first_remaining AS (
+ SELECT id_key, row_type, link
+ FROM remaining
+ WHERE id_key=min
+ ),
+ effect AS (
+ SELECT tab.id_key, 'new'::text, tab.link
+ FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key
+ WHERE e.row_type = 'false'
+ )
+ SELECT * FROM first_remaining
+ UNION ALL SELECT * FROM effect
+ )
+ )
+SELECT * FROM iter;
+
+WITH RECURSIVE
+ tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)),
+ iter (id_key, row_type, link) AS (
+ SELECT 0, 'base', 17
+ UNION (
+ WITH remaining(id_key, row_type, link, min) AS (
+ SELECT tab.id_key, 'true'::text, iter.link, MIN(tab.id_key) OVER ()
+ FROM tab INNER JOIN iter USING (link)
+ WHERE tab.id_key > iter.id_key
+ ),
+ first_remaining AS (
+ SELECT id_key, row_type, link
+ FROM remaining
+ WHERE id_key=min
+ ),
+ effect AS (
+ SELECT tab.id_key, 'new'::text, tab.link
+ FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key
+ WHERE e.row_type = 'false'
+ )
+ SELECT * FROM first_remaining
+ UNION ALL SELECT * FROM effect
+ )
+ )
+SELECT * FROM iter;
+
--
-- Data-modifying statements in WITH
--