diff options
| author | Tom Lane <tgl@sss.pgh.pa.us> | 2012-07-31 17:56:42 -0400 |
|---|---|---|
| committer | Tom Lane <tgl@sss.pgh.pa.us> | 2012-07-31 17:56:42 -0400 |
| commit | 1e14aa6e30b3bee7ae71ebb15452334de9954a22 (patch) | |
| tree | bb830dbbf25c240878aaaccb40051d649c79742a /src/backend/parser | |
| parent | 75ef476cf351b876e25f2b611fda1315c4e00ec4 (diff) | |
Fix WITH attached to a nested set operation (UNION/INTERSECT/EXCEPT).
Parse analysis neglected to cover the case of a WITH clause attached to an
intermediate-level set operation; it only handled WITH at the top level
or WITH attached to a leaf-level SELECT. Per report from Adam Mackler.
In HEAD, I rearranged the order of SelectStmt's fields to put withClause
with the other fields that can appear on non-leaf SelectStmts. In back
branches, leave it alone to avoid a possible ABI break for third-party
code.
Back-patch to 8.4 where WITH support was added.
Diffstat (limited to 'src/backend/parser')
| -rw-r--r-- | src/backend/parser/analyze.c | 21 | ||||
| -rw-r--r-- | src/backend/parser/parse_cte.c | 15 |
2 files changed, 26 insertions, 10 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 1fb115578bc..66ac4a36288 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -1151,6 +1151,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) Node *limitOffset; Node *limitCount; List *lockingClause; + WithClause *withClause; Node *node; ListCell *left_tlist, *lct, @@ -1193,11 +1194,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) @@ -1205,11 +1208,11 @@ 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 */ - if (stmt->withClause) + /* Process the WITH clause independently of all else */ + if (withClause) { - qry->hasRecursive = stmt->withClause->recursive; - qry->cteList = transformWithClause(pstate, stmt->withClause); + qry->hasRecursive = withClause->recursive; + qry->cteList = transformWithClause(pstate, withClause); } /* @@ -1400,10 +1403,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, UPDATE, or LIMIT - * 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) { @@ -1414,7 +1417,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 41c913c869a..10ec1fe18b1 100644 --- a/src/backend/parser/parse_cte.c +++ b/src/backend/parser/parse_cte.c @@ -627,6 +627,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 @@ -882,7 +894,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) @@ -901,6 +913,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", |
