diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/parser/parse_cte.c | 152 | ||||
-rw-r--r-- | src/test/regress/expected/with.out | 8 | ||||
-rw-r--r-- | src/test/regress/sql/with.sql | 7 |
3 files changed, 124 insertions, 43 deletions
diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c index 79673621c9b..366fd901d9d 100644 --- a/src/backend/parser/parse_cte.c +++ b/src/backend/parser/parse_cte.c @@ -88,6 +88,7 @@ static void analyzeCTE(ParseState *pstate, CommonTableExpr *cte); /* Dependency processing functions */ static void makeDependencyGraph(CteState *cstate); static bool makeDependencyGraphWalker(Node *node, CteState *cstate); +static void WalkInnerWith(Node *stmt, WithClause *withClause, CteState *cstate); static void TopologicalSort(ParseState *pstate, CteItem *items, int numitems); /* Recursion validity checker functions */ @@ -725,58 +726,69 @@ makeDependencyGraphWalker(Node *node, CteState *cstate) if (IsA(node, SelectStmt)) { SelectStmt *stmt = (SelectStmt *) node; - ListCell *lc; if (stmt->withClause) { - if (stmt->withClause->recursive) - { - /* - * In the RECURSIVE case, all query names of the WITH are - * visible to all WITH items as well as the main query. So - * push them all on, process, pop them all off. - */ - cstate->innerwiths = lcons(stmt->withClause->ctes, - cstate->innerwiths); - foreach(lc, stmt->withClause->ctes) - { - CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc); + /* Examine the WITH clause and the SelectStmt */ + WalkInnerWith(node, stmt->withClause, cstate); + /* We're done examining the SelectStmt */ + return false; + } + /* if no WITH clause, just fall through for normal processing */ + } + else if (IsA(node, InsertStmt)) + { + InsertStmt *stmt = (InsertStmt *) node; - (void) makeDependencyGraphWalker(cte->ctequery, cstate); - } - (void) raw_expression_tree_walker(node, - makeDependencyGraphWalker, - cstate); - cstate->innerwiths = list_delete_first(cstate->innerwiths); - } - else - { - /* - * In the non-RECURSIVE case, query names are visible to the - * WITH items after them and to the main query. - */ - cstate->innerwiths = lcons(NIL, cstate->innerwiths); - foreach(lc, stmt->withClause->ctes) - { - CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc); - ListCell *cell1; + if (stmt->withClause) + { + /* Examine the WITH clause and the InsertStmt */ + WalkInnerWith(node, stmt->withClause, cstate); + /* We're done examining the InsertStmt */ + return false; + } + /* if no WITH clause, just fall through for normal processing */ + } + else if (IsA(node, DeleteStmt)) + { + DeleteStmt *stmt = (DeleteStmt *) node; - (void) makeDependencyGraphWalker(cte->ctequery, cstate); - /* note that recursion could mutate innerwiths list */ - cell1 = list_head(cstate->innerwiths); - lfirst(cell1) = lappend((List *) lfirst(cell1), cte); - } - (void) raw_expression_tree_walker(node, - makeDependencyGraphWalker, - cstate); - cstate->innerwiths = list_delete_first(cstate->innerwiths); - } - /* We're done examining the SelectStmt */ + if (stmt->withClause) + { + /* Examine the WITH clause and the DeleteStmt */ + WalkInnerWith(node, stmt->withClause, cstate); + /* We're done examining the DeleteStmt */ return false; } /* if no WITH clause, just fall through for normal processing */ } - if (IsA(node, WithClause)) + else if (IsA(node, UpdateStmt)) + { + UpdateStmt *stmt = (UpdateStmt *) node; + + if (stmt->withClause) + { + /* Examine the WITH clause and the UpdateStmt */ + WalkInnerWith(node, stmt->withClause, cstate); + /* We're done examining the UpdateStmt */ + return false; + } + /* if no WITH clause, just fall through for normal processing */ + } + else if (IsA(node, MergeStmt)) + { + MergeStmt *stmt = (MergeStmt *) node; + + if (stmt->withClause) + { + /* Examine the WITH clause and the MergeStmt */ + WalkInnerWith(node, stmt->withClause, cstate); + /* We're done examining the MergeStmt */ + return false; + } + /* if no WITH clause, just fall through for normal processing */ + } + else if (IsA(node, WithClause)) { /* * Prevent raw_expression_tree_walker from recursing directly into a @@ -791,6 +803,60 @@ makeDependencyGraphWalker(Node *node, CteState *cstate) } /* + * makeDependencyGraphWalker's recursion into a statement having a WITH clause. + * + * This subroutine is concerned with updating the innerwiths list correctly + * based on the visibility rules for CTE names. + */ +static void +WalkInnerWith(Node *stmt, WithClause *withClause, CteState *cstate) +{ + ListCell *lc; + + if (withClause->recursive) + { + /* + * In the RECURSIVE case, all query names of the WITH are visible to + * all WITH items as well as the main query. So push them all on, + * process, pop them all off. + */ + cstate->innerwiths = lcons(withClause->ctes, cstate->innerwiths); + foreach(lc, withClause->ctes) + { + CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc); + + (void) makeDependencyGraphWalker(cte->ctequery, cstate); + } + (void) raw_expression_tree_walker(stmt, + makeDependencyGraphWalker, + cstate); + cstate->innerwiths = list_delete_first(cstate->innerwiths); + } + else + { + /* + * In the non-RECURSIVE case, query names are visible to the WITH + * items after them and to the main query. + */ + cstate->innerwiths = lcons(NIL, cstate->innerwiths); + foreach(lc, withClause->ctes) + { + CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc); + ListCell *cell1; + + (void) makeDependencyGraphWalker(cte->ctequery, cstate); + /* note that recursion could mutate innerwiths list */ + cell1 = list_head(cstate->innerwiths); + lfirst(cell1) = lappend((List *) lfirst(cell1), cte); + } + (void) raw_expression_tree_walker(stmt, + makeDependencyGraphWalker, + cstate); + cstate->innerwiths = list_delete_first(cstate->innerwiths); + } +} + +/* * Sort by dependencies, using a standard topological sort operation */ static void diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 7a51e2eb757..26c88505140 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -2104,6 +2104,14 @@ WITH RECURSIVE x(n) AS ( ERROR: ORDER BY in a recursive query is not implemented LINE 3: ORDER BY (SELECT n FROM x)) ^ +-- and this +WITH RECURSIVE x(n) AS ( + WITH sub_cte AS (SELECT * FROM x) + DELETE FROM graph RETURNING f) + SELECT * FROM x; +ERROR: recursive query "x" must not contain data-modifying statements +LINE 1: WITH RECURSIVE x(n) AS ( + ^ CREATE TEMPORARY TABLE y (a INTEGER); INSERT INTO y SELECT generate_series(1, 10); -- LEFT JOIN diff --git a/src/test/regress/sql/with.sql b/src/test/regress/sql/with.sql index dcdaab5eff0..b1cae161290 100644 --- a/src/test/regress/sql/with.sql +++ b/src/test/regress/sql/with.sql @@ -963,6 +963,13 @@ WITH RECURSIVE x(n) AS ( ORDER BY (SELECT n FROM x)) SELECT * FROM x; +-- and this +WITH RECURSIVE x(n) AS ( + WITH sub_cte AS (SELECT * FROM x) + DELETE FROM graph RETURNING f) + SELECT * FROM x; + + CREATE TEMPORARY TABLE y (a INTEGER); INSERT INTO y SELECT generate_series(1, 10); |