aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/parser/parse_cte.c152
-rw-r--r--src/test/regress/expected/with.out8
-rw-r--r--src/test/regress/sql/with.sql7
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);