aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/nodes/copyfuncs.c2
-rw-r--r--src/backend/nodes/equalfuncs.c2
-rw-r--r--src/backend/nodes/nodeFuncs.c4
-rw-r--r--src/backend/nodes/outfuncs.c2
-rw-r--r--src/backend/parser/analyze.c29
-rw-r--r--src/backend/parser/parse_cte.c15
-rw-r--r--src/backend/parser/parse_type.c2
-rw-r--r--src/include/nodes/parsenodes.h2
-rw-r--r--src/test/regress/expected/with.out51
-rw-r--r--src/test/regress/sql/with.sql35
10 files changed, 123 insertions, 21 deletions
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 9d9de7c4251..799930ad611 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2494,12 +2494,12 @@ _copySelectStmt(const SelectStmt *from)
COPY_NODE_FIELD(groupClause);
COPY_NODE_FIELD(havingClause);
COPY_NODE_FIELD(windowClause);
- COPY_NODE_FIELD(withClause);
COPY_NODE_FIELD(valuesLists);
COPY_NODE_FIELD(sortClause);
COPY_NODE_FIELD(limitOffset);
COPY_NODE_FIELD(limitCount);
COPY_NODE_FIELD(lockingClause);
+ COPY_NODE_FIELD(withClause);
COPY_SCALAR_FIELD(op);
COPY_SCALAR_FIELD(all);
COPY_NODE_FIELD(larg);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 6d4030a3224..802b0636714 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -976,12 +976,12 @@ _equalSelectStmt(const SelectStmt *a, const SelectStmt *b)
COMPARE_NODE_FIELD(groupClause);
COMPARE_NODE_FIELD(havingClause);
COMPARE_NODE_FIELD(windowClause);
- COMPARE_NODE_FIELD(withClause);
COMPARE_NODE_FIELD(valuesLists);
COMPARE_NODE_FIELD(sortClause);
COMPARE_NODE_FIELD(limitOffset);
COMPARE_NODE_FIELD(limitCount);
COMPARE_NODE_FIELD(lockingClause);
+ COMPARE_NODE_FIELD(withClause);
COMPARE_SCALAR_FIELD(op);
COMPARE_SCALAR_FIELD(all);
COMPARE_NODE_FIELD(larg);
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 813d1da1a28..b130902dcf2 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -2909,8 +2909,6 @@ raw_expression_tree_walker(Node *node,
return true;
if (walker(stmt->windowClause, context))
return true;
- if (walker(stmt->withClause, context))
- return true;
if (walker(stmt->valuesLists, context))
return true;
if (walker(stmt->sortClause, context))
@@ -2921,6 +2919,8 @@ raw_expression_tree_walker(Node *node,
return true;
if (walker(stmt->lockingClause, context))
return true;
+ if (walker(stmt->withClause, context))
+ return true;
if (walker(stmt->larg, context))
return true;
if (walker(stmt->rarg, context))
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 91b54265afe..b83bd1c9fdb 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2037,12 +2037,12 @@ _outSelectStmt(StringInfo str, const SelectStmt *node)
WRITE_NODE_FIELD(groupClause);
WRITE_NODE_FIELD(havingClause);
WRITE_NODE_FIELD(windowClause);
- WRITE_NODE_FIELD(withClause);
WRITE_NODE_FIELD(valuesLists);
WRITE_NODE_FIELD(sortClause);
WRITE_NODE_FIELD(limitOffset);
WRITE_NODE_FIELD(limitCount);
WRITE_NODE_FIELD(lockingClause);
+ WRITE_NODE_FIELD(withClause);
WRITE_ENUM_FIELD(op, SetOperation);
WRITE_BOOL_FIELD(all);
WRITE_NODE_FIELD(larg);
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index bfd3ab941a7..263edb5a7a6 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -1322,6 +1322,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
Node *limitOffset;
Node *limitCount;
List *lockingClause;
+ WithClause *withClause;
Node *node;
ListCell *left_tlist,
*lct,
@@ -1338,14 +1339,6 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
qry->commandType = CMD_SELECT;
- /* process the WITH clause independently of all else */
- if (stmt->withClause)
- {
- qry->hasRecursive = stmt->withClause->recursive;
- qry->cteList = transformWithClause(pstate, stmt->withClause);
- qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
- }
-
/*
* Find leftmost leaf SelectStmt. We currently only need to do this in
* order to deliver a suitable error message if there's an INTO clause
@@ -1375,11 +1368,13 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
limitOffset = stmt->limitOffset;
limitCount = stmt->limitCount;
lockingClause = stmt->lockingClause;
+ withClause = stmt->withClause;
stmt->sortClause = NIL;
stmt->limitOffset = NULL;
stmt->limitCount = NULL;
stmt->lockingClause = NIL;
+ stmt->withClause = NULL;
/* We don't support FOR UPDATE/SHARE with set ops at the moment. */
if (lockingClause)
@@ -1387,6 +1382,14 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT")));
+ /* Process the WITH clause independently of all else */
+ if (withClause)
+ {
+ qry->hasRecursive = withClause->recursive;
+ qry->cteList = transformWithClause(pstate, withClause);
+ qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
+ }
+
/*
* Recursively transform the components of the tree.
*/
@@ -1572,10 +1575,10 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT")));
/*
- * If an internal node of a set-op tree has ORDER BY, LIMIT, or FOR UPDATE
- * clauses attached, we need to treat it like a leaf node to generate an
- * independent sub-Query tree. Otherwise, it can be represented by a
- * SetOperationStmt node underneath the parent Query.
+ * If an internal node of a set-op tree has ORDER BY, LIMIT, FOR UPDATE,
+ * or WITH clauses attached, we need to treat it like a leaf node to
+ * generate an independent sub-Query tree. Otherwise, it can be
+ * represented by a SetOperationStmt node underneath the parent Query.
*/
if (stmt->op == SETOP_NONE)
{
@@ -1586,7 +1589,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
{
Assert(stmt->larg != NULL && stmt->rarg != NULL);
if (stmt->sortClause || stmt->limitOffset || stmt->limitCount ||
- stmt->lockingClause)
+ stmt->lockingClause || stmt->withClause)
isLeaf = true;
else
isLeaf = false;
diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c
index 2a7d4cd0724..d04fb6dec14 100644
--- a/src/backend/parser/parse_cte.c
+++ b/src/backend/parser/parse_cte.c
@@ -678,6 +678,18 @@ checkWellFormedRecursion(CteState *cstate)
if (cstate->selfrefcount != 1) /* shouldn't happen */
elog(ERROR, "missing recursive reference");
+ /* WITH mustn't contain self-reference, either */
+ if (stmt->withClause)
+ {
+ cstate->curitem = i;
+ cstate->innerwiths = NIL;
+ cstate->selfrefcount = 0;
+ cstate->context = RECURSION_SUBLINK;
+ checkWellFormedRecursionWalker((Node *) stmt->withClause->ctes,
+ cstate);
+ Assert(cstate->innerwiths == NIL);
+ }
+
/*
* Disallow ORDER BY and similar decoration atop the UNION. These
* don't make sense because it's impossible to figure out what they
@@ -933,7 +945,7 @@ checkWellFormedSelectStmt(SelectStmt *stmt, CteState *cstate)
cstate);
checkWellFormedRecursionWalker((Node *) stmt->lockingClause,
cstate);
- break;
+ /* stmt->withClause is intentionally ignored here */
break;
case SETOP_EXCEPT:
if (stmt->all)
@@ -952,6 +964,7 @@ checkWellFormedSelectStmt(SelectStmt *stmt, CteState *cstate)
cstate);
checkWellFormedRecursionWalker((Node *) stmt->lockingClause,
cstate);
+ /* stmt->withClause is intentionally ignored here */
break;
default:
elog(ERROR, "unrecognized set op: %d",
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 871a7d1ce30..cf2ff0cf70a 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -705,12 +705,12 @@ parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p)
stmt->groupClause != NIL ||
stmt->havingClause != NULL ||
stmt->windowClause != NIL ||
- stmt->withClause != NULL ||
stmt->valuesLists != NIL ||
stmt->sortClause != NIL ||
stmt->limitOffset != NULL ||
stmt->limitCount != NULL ||
stmt->lockingClause != NIL ||
+ stmt->withClause != NULL ||
stmt->op != SETOP_NONE)
goto fail;
if (list_length(stmt->targetList) != 1)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 1f89cd51595..119e1ed2f6e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1016,7 +1016,6 @@ typedef struct SelectStmt
List *groupClause; /* GROUP BY clauses */
Node *havingClause; /* HAVING conditional-expression */
List *windowClause; /* WINDOW window_name AS (...), ... */
- WithClause *withClause; /* WITH clause */
/*
* In a "leaf" node representing a VALUES list, the above fields are all
@@ -1036,6 +1035,7 @@ typedef struct SelectStmt
Node *limitOffset; /* # of result tuples to skip */
Node *limitCount; /* # of result tuples to return */
List *lockingClause; /* FOR UPDATE (list of LockingClause's) */
+ WithClause *withClause; /* WITH clause */
/*
* These fields are used only in upper-level SelectStmts.
diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out
index 2a41736cc8a..0d59ea3fdf9 100644
--- a/src/test/regress/expected/with.out
+++ b/src/test/regress/expected/with.out
@@ -1159,6 +1159,57 @@ SELECT * FROM t;
(55 rows)
--
+-- test WITH attached to intermediate-level set operation
+--
+WITH outermost(x) AS (
+ SELECT 1
+ UNION (WITH innermost as (SELECT 2)
+ SELECT * FROM innermost
+ UNION SELECT 3)
+)
+SELECT * FROM outermost;
+ x
+---
+ 1
+ 2
+ 3
+(3 rows)
+
+WITH outermost(x) AS (
+ SELECT 1
+ UNION (WITH innermost as (SELECT 2)
+ SELECT * FROM outermost -- fail
+ UNION SELECT * FROM innermost)
+)
+SELECT * FROM outermost;
+ERROR: relation "outermost" does not exist
+LINE 4: SELECT * FROM outermost
+ ^
+DETAIL: There is a WITH item named "outermost", but it cannot be referenced from this part of the query.
+HINT: Use WITH RECURSIVE, or re-order the WITH items to remove forward references.
+WITH RECURSIVE outermost(x) AS (
+ SELECT 1
+ UNION (WITH innermost as (SELECT 2)
+ SELECT * FROM outermost
+ UNION SELECT * FROM innermost)
+)
+SELECT * FROM outermost;
+ x
+---
+ 1
+ 2
+(2 rows)
+
+WITH RECURSIVE outermost(x) AS (
+ WITH innermost as (SELECT 2 FROM outermost) -- fail
+ SELECT * FROM innermost
+ UNION SELECT * from outermost
+)
+SELECT * FROM outermost;
+ERROR: recursive reference to query "outermost" must not appear within a subquery
+LINE 2: WITH innermost as (SELECT 2 FROM outermost)
+ ^
+--
-- Data-modifying statements in WITH
--
-- INSERT ... RETURNING
diff --git a/src/test/regress/sql/with.sql b/src/test/regress/sql/with.sql
index 9c6732b9611..22fdddc4eea 100644
--- a/src/test/regress/sql/with.sql
+++ b/src/test/regress/sql/with.sql
@@ -540,6 +540,41 @@ WITH RECURSIVE t(j) AS (
SELECT * FROM t;
--
+-- test WITH attached to intermediate-level set operation
+--
+
+WITH outermost(x) AS (
+ SELECT 1
+ UNION (WITH innermost as (SELECT 2)
+ SELECT * FROM innermost
+ UNION SELECT 3)
+)
+SELECT * FROM outermost;
+
+WITH outermost(x) AS (
+ SELECT 1
+ UNION (WITH innermost as (SELECT 2)
+ SELECT * FROM outermost -- fail
+ UNION SELECT * FROM innermost)
+)
+SELECT * FROM outermost;
+
+WITH RECURSIVE outermost(x) AS (
+ SELECT 1
+ UNION (WITH innermost as (SELECT 2)
+ SELECT * FROM outermost
+ UNION SELECT * FROM innermost)
+)
+SELECT * FROM outermost;
+
+WITH RECURSIVE outermost(x) AS (
+ WITH innermost as (SELECT 2 FROM outermost) -- fail
+ SELECT * FROM innermost
+ UNION SELECT * from outermost
+)
+SELECT * FROM outermost;
+
+--
-- Data-modifying statements in WITH
--