aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_cte.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2024-07-14 13:49:46 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2024-07-14 13:49:46 -0400
commitf96c2c72788cab3005c8bc6b4934b4928b34a529 (patch)
tree6cd60539ca678ab88c840a3a4e796ddf90572b17 /src/backend/parser/parse_cte.c
parentd5e6891502ca9e359aa5f5a381d904fe9d606338 (diff)
downloadpostgresql-f96c2c72788cab3005c8bc6b4934b4928b34a529.tar.gz
postgresql-f96c2c72788cab3005c8bc6b4934b4928b34a529.zip
Avoid unhelpful internal error for incorrect recursive-WITH queries.
checkWellFormedRecursion would issue "missing recursive reference" if a WITH RECURSIVE query contained a single self-reference but that self-reference was inside a top-level WITH, ORDER BY, LIMIT, etc, rather than inside the second arm of the UNION as expected. We already intended to throw more-on-point errors for such cases, but those error checks must be done before examining the UNION arm in order to have the desired results. So this patch need only move some code (and improve the comments). Per bug #18536 from Alexander Lakhin. Back-patch to all supported branches. Discussion: https://postgr.es/m/18536-0a342ec07901203e@postgresql.org
Diffstat (limited to 'src/backend/parser/parse_cte.c')
-rw-r--r--src/backend/parser/parse_cte.c53
1 files changed, 33 insertions, 20 deletions
diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c
index 6826d4f36a1..de9ae9b4834 100644
--- a/src/backend/parser/parse_cte.c
+++ b/src/backend/parser/parse_cte.c
@@ -877,25 +877,14 @@ checkWellFormedRecursion(CteState *cstate)
cte->ctename),
parser_errposition(cstate->pstate, cte->location)));
- /* The left-hand operand mustn't contain self-reference at all */
- cstate->curitem = i;
- cstate->innerwiths = NIL;
- cstate->selfrefcount = 0;
- cstate->context = RECURSION_NONRECURSIVETERM;
- checkWellFormedRecursionWalker((Node *) stmt->larg, cstate);
- Assert(cstate->innerwiths == NIL);
-
- /* Right-hand operand should contain one reference in a valid place */
- cstate->curitem = i;
- cstate->innerwiths = NIL;
- cstate->selfrefcount = 0;
- cstate->context = RECURSION_OK;
- checkWellFormedRecursionWalker((Node *) stmt->rarg, cstate);
- Assert(cstate->innerwiths == NIL);
- if (cstate->selfrefcount != 1) /* shouldn't happen */
- elog(ERROR, "missing recursive reference");
-
- /* WITH mustn't contain self-reference, either */
+ /*
+ * Really, we should insist that there not be a top-level WITH, since
+ * syntactically that would enclose the UNION. However, we've not
+ * done so in the past and it's probably too late to change. Settle
+ * for insisting that WITH not contain a self-reference. Test this
+ * before examining the UNION arms, to avoid issuing confusing errors
+ * in such cases.
+ */
if (stmt->withClause)
{
cstate->curitem = i;
@@ -912,7 +901,9 @@ checkWellFormedRecursion(CteState *cstate)
* don't make sense because it's impossible to figure out what they
* mean when we have only part of the recursive query's results. (If
* we did allow them, we'd have to check for recursive references
- * inside these subtrees.)
+ * inside these subtrees. As for WITH, we have to do this before
+ * examining the UNION arms, to avoid issuing confusing errors if
+ * there is a recursive reference here.)
*/
if (stmt->sortClause)
ereport(ERROR,
@@ -938,6 +929,28 @@ checkWellFormedRecursion(CteState *cstate)
errmsg("FOR UPDATE/SHARE in a recursive query is not implemented"),
parser_errposition(cstate->pstate,
exprLocation((Node *) stmt->lockingClause))));
+
+ /*
+ * Now we can get on with checking the UNION operands themselves.
+ *
+ * The left-hand operand mustn't contain a self-reference at all.
+ */
+ cstate->curitem = i;
+ cstate->innerwiths = NIL;
+ cstate->selfrefcount = 0;
+ cstate->context = RECURSION_NONRECURSIVETERM;
+ checkWellFormedRecursionWalker((Node *) stmt->larg, cstate);
+ Assert(cstate->innerwiths == NIL);
+
+ /* Right-hand operand should contain one reference in a valid place */
+ cstate->curitem = i;
+ cstate->innerwiths = NIL;
+ cstate->selfrefcount = 0;
+ cstate->context = RECURSION_OK;
+ checkWellFormedRecursionWalker((Node *) stmt->rarg, cstate);
+ Assert(cstate->innerwiths == NIL);
+ if (cstate->selfrefcount != 1) /* shouldn't happen */
+ elog(ERROR, "missing recursive reference");
}
}