/src/postgres/src/backend/parser/parse_cte.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * parse_cte.c |
4 | | * handle CTEs (common table expressions) in parser |
5 | | * |
6 | | * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group |
7 | | * Portions Copyright (c) 1994, Regents of the University of California |
8 | | * |
9 | | * |
10 | | * IDENTIFICATION |
11 | | * src/backend/parser/parse_cte.c |
12 | | * |
13 | | *------------------------------------------------------------------------- |
14 | | */ |
15 | | #include "postgres.h" |
16 | | |
17 | | #include "catalog/pg_collation.h" |
18 | | #include "catalog/pg_type.h" |
19 | | #include "nodes/nodeFuncs.h" |
20 | | #include "parser/analyze.h" |
21 | | #include "parser/parse_coerce.h" |
22 | | #include "parser/parse_collate.h" |
23 | | #include "parser/parse_cte.h" |
24 | | #include "parser/parse_expr.h" |
25 | | #include "utils/builtins.h" |
26 | | #include "utils/lsyscache.h" |
27 | | #include "utils/typcache.h" |
28 | | |
29 | | |
30 | | /* Enumeration of contexts in which a self-reference is disallowed */ |
31 | | typedef enum |
32 | | { |
33 | | RECURSION_OK, |
34 | | RECURSION_NONRECURSIVETERM, /* inside the left-hand term */ |
35 | | RECURSION_SUBLINK, /* inside a sublink */ |
36 | | RECURSION_OUTERJOIN, /* inside nullable side of an outer join */ |
37 | | RECURSION_INTERSECT, /* underneath INTERSECT (ALL) */ |
38 | | RECURSION_EXCEPT, /* underneath EXCEPT (ALL) */ |
39 | | } RecursionContext; |
40 | | |
41 | | /* Associated error messages --- each must have one %s for CTE name */ |
42 | | static const char *const recursion_errormsgs[] = { |
43 | | /* RECURSION_OK */ |
44 | | NULL, |
45 | | /* RECURSION_NONRECURSIVETERM */ |
46 | | gettext_noop("recursive reference to query \"%s\" must not appear within its non-recursive term"), |
47 | | /* RECURSION_SUBLINK */ |
48 | | gettext_noop("recursive reference to query \"%s\" must not appear within a subquery"), |
49 | | /* RECURSION_OUTERJOIN */ |
50 | | gettext_noop("recursive reference to query \"%s\" must not appear within an outer join"), |
51 | | /* RECURSION_INTERSECT */ |
52 | | gettext_noop("recursive reference to query \"%s\" must not appear within INTERSECT"), |
53 | | /* RECURSION_EXCEPT */ |
54 | | gettext_noop("recursive reference to query \"%s\" must not appear within EXCEPT") |
55 | | }; |
56 | | |
57 | | /* |
58 | | * For WITH RECURSIVE, we have to find an ordering of the clause members |
59 | | * with no forward references, and determine which members are recursive |
60 | | * (i.e., self-referential). It is convenient to do this with an array |
61 | | * of CteItems instead of a list of CommonTableExprs. |
62 | | */ |
63 | | typedef struct CteItem |
64 | | { |
65 | | CommonTableExpr *cte; /* One CTE to examine */ |
66 | | int id; /* Its ID number for dependencies */ |
67 | | Bitmapset *depends_on; /* CTEs depended on (not including self) */ |
68 | | } CteItem; |
69 | | |
70 | | /* CteState is what we need to pass around in the tree walkers */ |
71 | | typedef struct CteState |
72 | | { |
73 | | /* global state: */ |
74 | | ParseState *pstate; /* global parse state */ |
75 | | CteItem *items; /* array of CTEs and extra data */ |
76 | | int numitems; /* number of CTEs */ |
77 | | /* working state during a tree walk: */ |
78 | | int curitem; /* index of item currently being examined */ |
79 | | List *innerwiths; /* list of lists of CommonTableExpr */ |
80 | | /* working state for checkWellFormedRecursion walk only: */ |
81 | | int selfrefcount; /* number of self-references detected */ |
82 | | RecursionContext context; /* context to allow or disallow self-ref */ |
83 | | } CteState; |
84 | | |
85 | | |
86 | | static void analyzeCTE(ParseState *pstate, CommonTableExpr *cte); |
87 | | |
88 | | /* Dependency processing functions */ |
89 | | static void makeDependencyGraph(CteState *cstate); |
90 | | static bool makeDependencyGraphWalker(Node *node, CteState *cstate); |
91 | | static void WalkInnerWith(Node *stmt, WithClause *withClause, CteState *cstate); |
92 | | static void TopologicalSort(ParseState *pstate, CteItem *items, int numitems); |
93 | | |
94 | | /* Recursion validity checker functions */ |
95 | | static void checkWellFormedRecursion(CteState *cstate); |
96 | | static bool checkWellFormedRecursionWalker(Node *node, CteState *cstate); |
97 | | static void checkWellFormedSelectStmt(SelectStmt *stmt, CteState *cstate); |
98 | | |
99 | | |
100 | | /* |
101 | | * transformWithClause - |
102 | | * Transform the list of WITH clause "common table expressions" into |
103 | | * Query nodes. |
104 | | * |
105 | | * The result is the list of transformed CTEs to be put into the output |
106 | | * Query. (This is in fact the same as the ending value of p_ctenamespace, |
107 | | * but it seems cleaner to not expose that in the function's API.) |
108 | | */ |
109 | | List * |
110 | | transformWithClause(ParseState *pstate, WithClause *withClause) |
111 | 0 | { |
112 | 0 | ListCell *lc; |
113 | | |
114 | | /* Only one WITH clause per query level */ |
115 | 0 | Assert(pstate->p_ctenamespace == NIL); |
116 | 0 | Assert(pstate->p_future_ctes == NIL); |
117 | | |
118 | | /* |
119 | | * For either type of WITH, there must not be duplicate CTE names in the |
120 | | * list. Check this right away so we needn't worry later. |
121 | | * |
122 | | * Also, tentatively mark each CTE as non-recursive, and initialize its |
123 | | * reference count to zero, and set pstate->p_hasModifyingCTE if needed. |
124 | | */ |
125 | 0 | foreach(lc, withClause->ctes) |
126 | 0 | { |
127 | 0 | CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc); |
128 | 0 | ListCell *rest; |
129 | |
|
130 | 0 | for_each_cell(rest, withClause->ctes, lnext(withClause->ctes, lc)) |
131 | 0 | { |
132 | 0 | CommonTableExpr *cte2 = (CommonTableExpr *) lfirst(rest); |
133 | |
|
134 | 0 | if (strcmp(cte->ctename, cte2->ctename) == 0) |
135 | 0 | ereport(ERROR, |
136 | 0 | (errcode(ERRCODE_DUPLICATE_ALIAS), |
137 | 0 | errmsg("WITH query name \"%s\" specified more than once", |
138 | 0 | cte2->ctename), |
139 | 0 | parser_errposition(pstate, cte2->location))); |
140 | 0 | } |
141 | | |
142 | 0 | cte->cterecursive = false; |
143 | 0 | cte->cterefcount = 0; |
144 | |
|
145 | 0 | if (!IsA(cte->ctequery, SelectStmt)) |
146 | 0 | { |
147 | | /* must be a data-modifying statement */ |
148 | 0 | Assert(IsA(cte->ctequery, InsertStmt) || |
149 | 0 | IsA(cte->ctequery, UpdateStmt) || |
150 | 0 | IsA(cte->ctequery, DeleteStmt) || |
151 | 0 | IsA(cte->ctequery, MergeStmt)); |
152 | |
|
153 | 0 | pstate->p_hasModifyingCTE = true; |
154 | 0 | } |
155 | 0 | } |
156 | | |
157 | 0 | if (withClause->recursive) |
158 | 0 | { |
159 | | /* |
160 | | * For WITH RECURSIVE, we rearrange the list elements if needed to |
161 | | * eliminate forward references. First, build a work array and set up |
162 | | * the data structure needed by the tree walkers. |
163 | | */ |
164 | 0 | CteState cstate; |
165 | 0 | int i; |
166 | |
|
167 | 0 | cstate.pstate = pstate; |
168 | 0 | cstate.numitems = list_length(withClause->ctes); |
169 | 0 | cstate.items = (CteItem *) palloc0(cstate.numitems * sizeof(CteItem)); |
170 | 0 | i = 0; |
171 | 0 | foreach(lc, withClause->ctes) |
172 | 0 | { |
173 | 0 | cstate.items[i].cte = (CommonTableExpr *) lfirst(lc); |
174 | 0 | cstate.items[i].id = i; |
175 | 0 | i++; |
176 | 0 | } |
177 | | |
178 | | /* |
179 | | * Find all the dependencies and sort the CteItems into a safe |
180 | | * processing order. Also, mark CTEs that contain self-references. |
181 | | */ |
182 | 0 | makeDependencyGraph(&cstate); |
183 | | |
184 | | /* |
185 | | * Check that recursive queries are well-formed. |
186 | | */ |
187 | 0 | checkWellFormedRecursion(&cstate); |
188 | | |
189 | | /* |
190 | | * Set up the ctenamespace for parse analysis. Per spec, all the WITH |
191 | | * items are visible to all others, so stuff them all in before parse |
192 | | * analysis. We build the list in safe processing order so that the |
193 | | * planner can process the queries in sequence. |
194 | | */ |
195 | 0 | for (i = 0; i < cstate.numitems; i++) |
196 | 0 | { |
197 | 0 | CommonTableExpr *cte = cstate.items[i].cte; |
198 | |
|
199 | 0 | pstate->p_ctenamespace = lappend(pstate->p_ctenamespace, cte); |
200 | 0 | } |
201 | | |
202 | | /* |
203 | | * Do parse analysis in the order determined by the topological sort. |
204 | | */ |
205 | 0 | for (i = 0; i < cstate.numitems; i++) |
206 | 0 | { |
207 | 0 | CommonTableExpr *cte = cstate.items[i].cte; |
208 | |
|
209 | 0 | analyzeCTE(pstate, cte); |
210 | 0 | } |
211 | 0 | } |
212 | 0 | else |
213 | 0 | { |
214 | | /* |
215 | | * For non-recursive WITH, just analyze each CTE in sequence and then |
216 | | * add it to the ctenamespace. This corresponds to the spec's |
217 | | * definition of the scope of each WITH name. However, to allow error |
218 | | * reports to be aware of the possibility of an erroneous reference, |
219 | | * we maintain a list in p_future_ctes of the not-yet-visible CTEs. |
220 | | */ |
221 | 0 | pstate->p_future_ctes = list_copy(withClause->ctes); |
222 | |
|
223 | 0 | foreach(lc, withClause->ctes) |
224 | 0 | { |
225 | 0 | CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc); |
226 | |
|
227 | 0 | analyzeCTE(pstate, cte); |
228 | 0 | pstate->p_ctenamespace = lappend(pstate->p_ctenamespace, cte); |
229 | 0 | pstate->p_future_ctes = list_delete_first(pstate->p_future_ctes); |
230 | 0 | } |
231 | 0 | } |
232 | |
|
233 | 0 | return pstate->p_ctenamespace; |
234 | 0 | } |
235 | | |
236 | | |
237 | | /* |
238 | | * Perform the actual parse analysis transformation of one CTE. All |
239 | | * CTEs it depends on have already been loaded into pstate->p_ctenamespace, |
240 | | * and have been marked with the correct output column names/types. |
241 | | */ |
242 | | static void |
243 | | analyzeCTE(ParseState *pstate, CommonTableExpr *cte) |
244 | 0 | { |
245 | 0 | Query *query; |
246 | 0 | CTESearchClause *search_clause = cte->search_clause; |
247 | 0 | CTECycleClause *cycle_clause = cte->cycle_clause; |
248 | | |
249 | | /* Analysis not done already */ |
250 | 0 | Assert(!IsA(cte->ctequery, Query)); |
251 | | |
252 | | /* |
253 | | * Before analyzing the CTE's query, we'd better identify the data type of |
254 | | * the cycle mark column if any, since the query could refer to that. |
255 | | * Other validity checks on the cycle clause will be done afterwards. |
256 | | */ |
257 | 0 | if (cycle_clause) |
258 | 0 | { |
259 | 0 | TypeCacheEntry *typentry; |
260 | 0 | Oid op; |
261 | |
|
262 | 0 | cycle_clause->cycle_mark_value = |
263 | 0 | transformExpr(pstate, cycle_clause->cycle_mark_value, |
264 | 0 | EXPR_KIND_CYCLE_MARK); |
265 | 0 | cycle_clause->cycle_mark_default = |
266 | 0 | transformExpr(pstate, cycle_clause->cycle_mark_default, |
267 | 0 | EXPR_KIND_CYCLE_MARK); |
268 | |
|
269 | 0 | cycle_clause->cycle_mark_type = |
270 | 0 | select_common_type(pstate, |
271 | 0 | list_make2(cycle_clause->cycle_mark_value, |
272 | 0 | cycle_clause->cycle_mark_default), |
273 | 0 | "CYCLE", NULL); |
274 | 0 | cycle_clause->cycle_mark_value = |
275 | 0 | coerce_to_common_type(pstate, |
276 | 0 | cycle_clause->cycle_mark_value, |
277 | 0 | cycle_clause->cycle_mark_type, |
278 | 0 | "CYCLE/SET/TO"); |
279 | 0 | cycle_clause->cycle_mark_default = |
280 | 0 | coerce_to_common_type(pstate, |
281 | 0 | cycle_clause->cycle_mark_default, |
282 | 0 | cycle_clause->cycle_mark_type, |
283 | 0 | "CYCLE/SET/DEFAULT"); |
284 | |
|
285 | 0 | cycle_clause->cycle_mark_typmod = |
286 | 0 | select_common_typmod(pstate, |
287 | 0 | list_make2(cycle_clause->cycle_mark_value, |
288 | 0 | cycle_clause->cycle_mark_default), |
289 | 0 | cycle_clause->cycle_mark_type); |
290 | |
|
291 | 0 | cycle_clause->cycle_mark_collation = |
292 | 0 | select_common_collation(pstate, |
293 | 0 | list_make2(cycle_clause->cycle_mark_value, |
294 | 0 | cycle_clause->cycle_mark_default), |
295 | 0 | true); |
296 | | |
297 | | /* Might as well look up the relevant <> operator while we are at it */ |
298 | 0 | typentry = lookup_type_cache(cycle_clause->cycle_mark_type, |
299 | 0 | TYPECACHE_EQ_OPR); |
300 | 0 | if (!OidIsValid(typentry->eq_opr)) |
301 | 0 | ereport(ERROR, |
302 | 0 | errcode(ERRCODE_UNDEFINED_FUNCTION), |
303 | 0 | errmsg("could not identify an equality operator for type %s", |
304 | 0 | format_type_be(cycle_clause->cycle_mark_type))); |
305 | 0 | op = get_negator(typentry->eq_opr); |
306 | 0 | if (!OidIsValid(op)) |
307 | 0 | ereport(ERROR, |
308 | 0 | errcode(ERRCODE_UNDEFINED_FUNCTION), |
309 | 0 | errmsg("could not identify an inequality operator for type %s", |
310 | 0 | format_type_be(cycle_clause->cycle_mark_type))); |
311 | | |
312 | 0 | cycle_clause->cycle_mark_neop = op; |
313 | 0 | } |
314 | | |
315 | | /* Now we can get on with analyzing the CTE's query */ |
316 | 0 | query = parse_sub_analyze(cte->ctequery, pstate, cte, false, true); |
317 | 0 | cte->ctequery = (Node *) query; |
318 | | |
319 | | /* |
320 | | * Check that we got something reasonable. These first two cases should |
321 | | * be prevented by the grammar. |
322 | | */ |
323 | 0 | if (!IsA(query, Query)) |
324 | 0 | elog(ERROR, "unexpected non-Query statement in WITH"); |
325 | 0 | if (query->utilityStmt != NULL) |
326 | 0 | elog(ERROR, "unexpected utility statement in WITH"); |
327 | | |
328 | | /* |
329 | | * We disallow data-modifying WITH except at the top level of a query, |
330 | | * because it's not clear when such a modification should be executed. |
331 | | */ |
332 | 0 | if (query->commandType != CMD_SELECT && |
333 | 0 | pstate->parentParseState != NULL) |
334 | 0 | ereport(ERROR, |
335 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
336 | 0 | errmsg("WITH clause containing a data-modifying statement must be at the top level"), |
337 | 0 | parser_errposition(pstate, cte->location))); |
338 | | |
339 | | /* |
340 | | * CTE queries are always marked not canSetTag. (Currently this only |
341 | | * matters for data-modifying statements, for which the flag will be |
342 | | * propagated to the ModifyTable plan node.) |
343 | | */ |
344 | 0 | query->canSetTag = false; |
345 | |
|
346 | 0 | if (!cte->cterecursive) |
347 | 0 | { |
348 | | /* Compute the output column names/types if not done yet */ |
349 | 0 | analyzeCTETargetList(pstate, cte, GetCTETargetList(cte)); |
350 | 0 | } |
351 | 0 | else |
352 | 0 | { |
353 | | /* |
354 | | * Verify that the previously determined output column types and |
355 | | * collations match what the query really produced. We have to check |
356 | | * this because the recursive term could have overridden the |
357 | | * non-recursive term, and we don't have any easy way to fix that. |
358 | | */ |
359 | 0 | ListCell *lctlist, |
360 | 0 | *lctyp, |
361 | 0 | *lctypmod, |
362 | 0 | *lccoll; |
363 | 0 | int varattno; |
364 | |
|
365 | 0 | lctyp = list_head(cte->ctecoltypes); |
366 | 0 | lctypmod = list_head(cte->ctecoltypmods); |
367 | 0 | lccoll = list_head(cte->ctecolcollations); |
368 | 0 | varattno = 0; |
369 | 0 | foreach(lctlist, GetCTETargetList(cte)) |
370 | 0 | { |
371 | 0 | TargetEntry *te = (TargetEntry *) lfirst(lctlist); |
372 | 0 | Node *texpr; |
373 | |
|
374 | 0 | if (te->resjunk) |
375 | 0 | continue; |
376 | 0 | varattno++; |
377 | 0 | Assert(varattno == te->resno); |
378 | 0 | if (lctyp == NULL || lctypmod == NULL || lccoll == NULL) /* shouldn't happen */ |
379 | 0 | elog(ERROR, "wrong number of output columns in WITH"); |
380 | 0 | texpr = (Node *) te->expr; |
381 | 0 | if (exprType(texpr) != lfirst_oid(lctyp) || |
382 | 0 | exprTypmod(texpr) != lfirst_int(lctypmod)) |
383 | 0 | ereport(ERROR, |
384 | 0 | (errcode(ERRCODE_DATATYPE_MISMATCH), |
385 | 0 | errmsg("recursive query \"%s\" column %d has type %s in non-recursive term but type %s overall", |
386 | 0 | cte->ctename, varattno, |
387 | 0 | format_type_with_typemod(lfirst_oid(lctyp), |
388 | 0 | lfirst_int(lctypmod)), |
389 | 0 | format_type_with_typemod(exprType(texpr), |
390 | 0 | exprTypmod(texpr))), |
391 | 0 | errhint("Cast the output of the non-recursive term to the correct type."), |
392 | 0 | parser_errposition(pstate, exprLocation(texpr)))); |
393 | 0 | if (exprCollation(texpr) != lfirst_oid(lccoll)) |
394 | 0 | ereport(ERROR, |
395 | 0 | (errcode(ERRCODE_COLLATION_MISMATCH), |
396 | 0 | errmsg("recursive query \"%s\" column %d has collation \"%s\" in non-recursive term but collation \"%s\" overall", |
397 | 0 | cte->ctename, varattno, |
398 | 0 | get_collation_name(lfirst_oid(lccoll)), |
399 | 0 | get_collation_name(exprCollation(texpr))), |
400 | 0 | errhint("Use the COLLATE clause to set the collation of the non-recursive term."), |
401 | 0 | parser_errposition(pstate, exprLocation(texpr)))); |
402 | 0 | lctyp = lnext(cte->ctecoltypes, lctyp); |
403 | 0 | lctypmod = lnext(cte->ctecoltypmods, lctypmod); |
404 | 0 | lccoll = lnext(cte->ctecolcollations, lccoll); |
405 | 0 | } |
406 | 0 | if (lctyp != NULL || lctypmod != NULL || lccoll != NULL) /* shouldn't happen */ |
407 | 0 | elog(ERROR, "wrong number of output columns in WITH"); |
408 | 0 | } |
409 | | |
410 | | /* |
411 | | * Now make validity checks on the SEARCH and CYCLE clauses, if present. |
412 | | */ |
413 | 0 | if (search_clause || cycle_clause) |
414 | 0 | { |
415 | 0 | Query *ctequery; |
416 | 0 | SetOperationStmt *sos; |
417 | |
|
418 | 0 | if (!cte->cterecursive) |
419 | 0 | ereport(ERROR, |
420 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
421 | 0 | errmsg("WITH query is not recursive"), |
422 | 0 | parser_errposition(pstate, cte->location))); |
423 | | |
424 | | /* |
425 | | * SQL requires a WITH list element (CTE) to be "expandable" in order |
426 | | * to allow a search or cycle clause. That is a stronger requirement |
427 | | * than just being recursive. It basically means the query expression |
428 | | * looks like |
429 | | * |
430 | | * non-recursive query UNION [ALL] recursive query |
431 | | * |
432 | | * and that the recursive query is not itself a set operation. |
433 | | * |
434 | | * As of this writing, most of these criteria are already satisfied by |
435 | | * all recursive CTEs allowed by PostgreSQL. In the future, if |
436 | | * further variants recursive CTEs are accepted, there might be |
437 | | * further checks required here to determine what is "expandable". |
438 | | */ |
439 | | |
440 | 0 | ctequery = castNode(Query, cte->ctequery); |
441 | 0 | Assert(ctequery->setOperations); |
442 | 0 | sos = castNode(SetOperationStmt, ctequery->setOperations); |
443 | | |
444 | | /* |
445 | | * This left side check is not required for expandability, but |
446 | | * rewriteSearchAndCycle() doesn't currently have support for it, so |
447 | | * we catch it here. |
448 | | */ |
449 | 0 | if (!IsA(sos->larg, RangeTblRef)) |
450 | 0 | ereport(ERROR, |
451 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
452 | 0 | errmsg("with a SEARCH or CYCLE clause, the left side of the UNION must be a SELECT"))); |
453 | | |
454 | 0 | if (!IsA(sos->rarg, RangeTblRef)) |
455 | 0 | ereport(ERROR, |
456 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
457 | 0 | errmsg("with a SEARCH or CYCLE clause, the right side of the UNION must be a SELECT"))); |
458 | 0 | } |
459 | | |
460 | 0 | if (search_clause) |
461 | 0 | { |
462 | 0 | ListCell *lc; |
463 | 0 | List *seen = NIL; |
464 | |
|
465 | 0 | foreach(lc, search_clause->search_col_list) |
466 | 0 | { |
467 | 0 | String *colname = lfirst_node(String, lc); |
468 | |
|
469 | 0 | if (!list_member(cte->ctecolnames, colname)) |
470 | 0 | ereport(ERROR, |
471 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
472 | 0 | errmsg("search column \"%s\" not in WITH query column list", |
473 | 0 | strVal(colname)), |
474 | 0 | parser_errposition(pstate, search_clause->location))); |
475 | | |
476 | 0 | if (list_member(seen, colname)) |
477 | 0 | ereport(ERROR, |
478 | 0 | (errcode(ERRCODE_DUPLICATE_COLUMN), |
479 | 0 | errmsg("search column \"%s\" specified more than once", |
480 | 0 | strVal(colname)), |
481 | 0 | parser_errposition(pstate, search_clause->location))); |
482 | 0 | seen = lappend(seen, colname); |
483 | 0 | } |
484 | | |
485 | 0 | if (list_member(cte->ctecolnames, makeString(search_clause->search_seq_column))) |
486 | 0 | ereport(ERROR, |
487 | 0 | errcode(ERRCODE_SYNTAX_ERROR), |
488 | 0 | errmsg("search sequence column name \"%s\" already used in WITH query column list", |
489 | 0 | search_clause->search_seq_column), |
490 | 0 | parser_errposition(pstate, search_clause->location)); |
491 | 0 | } |
492 | | |
493 | 0 | if (cycle_clause) |
494 | 0 | { |
495 | 0 | ListCell *lc; |
496 | 0 | List *seen = NIL; |
497 | |
|
498 | 0 | foreach(lc, cycle_clause->cycle_col_list) |
499 | 0 | { |
500 | 0 | String *colname = lfirst_node(String, lc); |
501 | |
|
502 | 0 | if (!list_member(cte->ctecolnames, colname)) |
503 | 0 | ereport(ERROR, |
504 | 0 | (errcode(ERRCODE_SYNTAX_ERROR), |
505 | 0 | errmsg("cycle column \"%s\" not in WITH query column list", |
506 | 0 | strVal(colname)), |
507 | 0 | parser_errposition(pstate, cycle_clause->location))); |
508 | | |
509 | 0 | if (list_member(seen, colname)) |
510 | 0 | ereport(ERROR, |
511 | 0 | (errcode(ERRCODE_DUPLICATE_COLUMN), |
512 | 0 | errmsg("cycle column \"%s\" specified more than once", |
513 | 0 | strVal(colname)), |
514 | 0 | parser_errposition(pstate, cycle_clause->location))); |
515 | 0 | seen = lappend(seen, colname); |
516 | 0 | } |
517 | | |
518 | 0 | if (list_member(cte->ctecolnames, makeString(cycle_clause->cycle_mark_column))) |
519 | 0 | ereport(ERROR, |
520 | 0 | errcode(ERRCODE_SYNTAX_ERROR), |
521 | 0 | errmsg("cycle mark column name \"%s\" already used in WITH query column list", |
522 | 0 | cycle_clause->cycle_mark_column), |
523 | 0 | parser_errposition(pstate, cycle_clause->location)); |
524 | | |
525 | 0 | if (list_member(cte->ctecolnames, makeString(cycle_clause->cycle_path_column))) |
526 | 0 | ereport(ERROR, |
527 | 0 | errcode(ERRCODE_SYNTAX_ERROR), |
528 | 0 | errmsg("cycle path column name \"%s\" already used in WITH query column list", |
529 | 0 | cycle_clause->cycle_path_column), |
530 | 0 | parser_errposition(pstate, cycle_clause->location)); |
531 | | |
532 | 0 | if (strcmp(cycle_clause->cycle_mark_column, |
533 | 0 | cycle_clause->cycle_path_column) == 0) |
534 | 0 | ereport(ERROR, |
535 | 0 | errcode(ERRCODE_SYNTAX_ERROR), |
536 | 0 | errmsg("cycle mark column name and cycle path column name are the same"), |
537 | 0 | parser_errposition(pstate, cycle_clause->location)); |
538 | 0 | } |
539 | | |
540 | 0 | if (search_clause && cycle_clause) |
541 | 0 | { |
542 | 0 | if (strcmp(search_clause->search_seq_column, |
543 | 0 | cycle_clause->cycle_mark_column) == 0) |
544 | 0 | ereport(ERROR, |
545 | 0 | errcode(ERRCODE_SYNTAX_ERROR), |
546 | 0 | errmsg("search sequence column name and cycle mark column name are the same"), |
547 | 0 | parser_errposition(pstate, search_clause->location)); |
548 | | |
549 | 0 | if (strcmp(search_clause->search_seq_column, |
550 | 0 | cycle_clause->cycle_path_column) == 0) |
551 | 0 | ereport(ERROR, |
552 | 0 | errcode(ERRCODE_SYNTAX_ERROR), |
553 | 0 | errmsg("search sequence column name and cycle path column name are the same"), |
554 | 0 | parser_errposition(pstate, search_clause->location)); |
555 | 0 | } |
556 | 0 | } |
557 | | |
558 | | /* |
559 | | * Compute derived fields of a CTE, given the transformed output targetlist |
560 | | * |
561 | | * For a nonrecursive CTE, this is called after transforming the CTE's query. |
562 | | * For a recursive CTE, we call it after transforming the non-recursive term, |
563 | | * and pass the targetlist emitted by the non-recursive term only. |
564 | | * |
565 | | * Note: in the recursive case, the passed pstate is actually the one being |
566 | | * used to analyze the CTE's query, so it is one level lower down than in |
567 | | * the nonrecursive case. This doesn't matter since we only use it for |
568 | | * error message context anyway. |
569 | | */ |
570 | | void |
571 | | analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist) |
572 | 0 | { |
573 | 0 | int numaliases; |
574 | 0 | int varattno; |
575 | 0 | ListCell *tlistitem; |
576 | | |
577 | | /* Not done already ... */ |
578 | 0 | Assert(cte->ctecolnames == NIL); |
579 | | |
580 | | /* |
581 | | * We need to determine column names, types, and collations. The alias |
582 | | * column names override anything coming from the query itself. (Note: |
583 | | * the SQL spec says that the alias list must be empty or exactly as long |
584 | | * as the output column set; but we allow it to be shorter for consistency |
585 | | * with Alias handling.) |
586 | | */ |
587 | 0 | cte->ctecolnames = copyObject(cte->aliascolnames); |
588 | 0 | cte->ctecoltypes = cte->ctecoltypmods = cte->ctecolcollations = NIL; |
589 | 0 | numaliases = list_length(cte->aliascolnames); |
590 | 0 | varattno = 0; |
591 | 0 | foreach(tlistitem, tlist) |
592 | 0 | { |
593 | 0 | TargetEntry *te = (TargetEntry *) lfirst(tlistitem); |
594 | 0 | Oid coltype; |
595 | 0 | int32 coltypmod; |
596 | 0 | Oid colcoll; |
597 | |
|
598 | 0 | if (te->resjunk) |
599 | 0 | continue; |
600 | 0 | varattno++; |
601 | 0 | Assert(varattno == te->resno); |
602 | 0 | if (varattno > numaliases) |
603 | 0 | { |
604 | 0 | char *attrname; |
605 | |
|
606 | 0 | attrname = pstrdup(te->resname); |
607 | 0 | cte->ctecolnames = lappend(cte->ctecolnames, makeString(attrname)); |
608 | 0 | } |
609 | 0 | coltype = exprType((Node *) te->expr); |
610 | 0 | coltypmod = exprTypmod((Node *) te->expr); |
611 | 0 | colcoll = exprCollation((Node *) te->expr); |
612 | | |
613 | | /* |
614 | | * If the CTE is recursive, force the exposed column type of any |
615 | | * "unknown" column to "text". We must deal with this here because |
616 | | * we're called on the non-recursive term before there's been any |
617 | | * attempt to force unknown output columns to some other type. We |
618 | | * have to resolve unknowns before looking at the recursive term. |
619 | | * |
620 | | * The column might contain 'foo' COLLATE "bar", so don't override |
621 | | * collation if it's already set. |
622 | | */ |
623 | 0 | if (cte->cterecursive && coltype == UNKNOWNOID) |
624 | 0 | { |
625 | 0 | coltype = TEXTOID; |
626 | 0 | coltypmod = -1; /* should be -1 already, but be sure */ |
627 | 0 | if (!OidIsValid(colcoll)) |
628 | 0 | colcoll = DEFAULT_COLLATION_OID; |
629 | 0 | } |
630 | 0 | cte->ctecoltypes = lappend_oid(cte->ctecoltypes, coltype); |
631 | 0 | cte->ctecoltypmods = lappend_int(cte->ctecoltypmods, coltypmod); |
632 | 0 | cte->ctecolcollations = lappend_oid(cte->ctecolcollations, colcoll); |
633 | 0 | } |
634 | 0 | if (varattno < numaliases) |
635 | 0 | ereport(ERROR, |
636 | 0 | (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), |
637 | 0 | errmsg("WITH query \"%s\" has %d columns available but %d columns specified", |
638 | 0 | cte->ctename, varattno, numaliases), |
639 | 0 | parser_errposition(pstate, cte->location))); |
640 | 0 | } |
641 | | |
642 | | |
643 | | /* |
644 | | * Identify the cross-references of a list of WITH RECURSIVE items, |
645 | | * and sort into an order that has no forward references. |
646 | | */ |
647 | | static void |
648 | | makeDependencyGraph(CteState *cstate) |
649 | 0 | { |
650 | 0 | int i; |
651 | |
|
652 | 0 | for (i = 0; i < cstate->numitems; i++) |
653 | 0 | { |
654 | 0 | CommonTableExpr *cte = cstate->items[i].cte; |
655 | |
|
656 | 0 | cstate->curitem = i; |
657 | 0 | cstate->innerwiths = NIL; |
658 | 0 | makeDependencyGraphWalker((Node *) cte->ctequery, cstate); |
659 | 0 | Assert(cstate->innerwiths == NIL); |
660 | 0 | } |
661 | |
|
662 | 0 | TopologicalSort(cstate->pstate, cstate->items, cstate->numitems); |
663 | 0 | } |
664 | | |
665 | | /* |
666 | | * Tree walker function to detect cross-references and self-references of the |
667 | | * CTEs in a WITH RECURSIVE list. |
668 | | */ |
669 | | static bool |
670 | | makeDependencyGraphWalker(Node *node, CteState *cstate) |
671 | 0 | { |
672 | 0 | if (node == NULL) |
673 | 0 | return false; |
674 | 0 | if (IsA(node, RangeVar)) |
675 | 0 | { |
676 | 0 | RangeVar *rv = (RangeVar *) node; |
677 | | |
678 | | /* If unqualified name, might be a CTE reference */ |
679 | 0 | if (!rv->schemaname) |
680 | 0 | { |
681 | 0 | ListCell *lc; |
682 | 0 | int i; |
683 | | |
684 | | /* ... but first see if it's captured by an inner WITH */ |
685 | 0 | foreach(lc, cstate->innerwiths) |
686 | 0 | { |
687 | 0 | List *withlist = (List *) lfirst(lc); |
688 | 0 | ListCell *lc2; |
689 | |
|
690 | 0 | foreach(lc2, withlist) |
691 | 0 | { |
692 | 0 | CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc2); |
693 | |
|
694 | 0 | if (strcmp(rv->relname, cte->ctename) == 0) |
695 | 0 | return false; /* yes, so bail out */ |
696 | 0 | } |
697 | 0 | } |
698 | | |
699 | | /* No, could be a reference to the query level we are working on */ |
700 | 0 | for (i = 0; i < cstate->numitems; i++) |
701 | 0 | { |
702 | 0 | CommonTableExpr *cte = cstate->items[i].cte; |
703 | |
|
704 | 0 | if (strcmp(rv->relname, cte->ctename) == 0) |
705 | 0 | { |
706 | 0 | int myindex = cstate->curitem; |
707 | |
|
708 | 0 | if (i != myindex) |
709 | 0 | { |
710 | | /* Add cross-item dependency */ |
711 | 0 | cstate->items[myindex].depends_on = |
712 | 0 | bms_add_member(cstate->items[myindex].depends_on, |
713 | 0 | cstate->items[i].id); |
714 | 0 | } |
715 | 0 | else |
716 | 0 | { |
717 | | /* Found out this one is self-referential */ |
718 | 0 | cte->cterecursive = true; |
719 | 0 | } |
720 | 0 | break; |
721 | 0 | } |
722 | 0 | } |
723 | 0 | } |
724 | 0 | return false; |
725 | 0 | } |
726 | 0 | if (IsA(node, SelectStmt)) |
727 | 0 | { |
728 | 0 | SelectStmt *stmt = (SelectStmt *) node; |
729 | |
|
730 | 0 | if (stmt->withClause) |
731 | 0 | { |
732 | | /* Examine the WITH clause and the SelectStmt */ |
733 | 0 | WalkInnerWith(node, stmt->withClause, cstate); |
734 | | /* We're done examining the SelectStmt */ |
735 | 0 | return false; |
736 | 0 | } |
737 | | /* if no WITH clause, just fall through for normal processing */ |
738 | 0 | } |
739 | 0 | else if (IsA(node, InsertStmt)) |
740 | 0 | { |
741 | 0 | InsertStmt *stmt = (InsertStmt *) node; |
742 | |
|
743 | 0 | if (stmt->withClause) |
744 | 0 | { |
745 | | /* Examine the WITH clause and the InsertStmt */ |
746 | 0 | WalkInnerWith(node, stmt->withClause, cstate); |
747 | | /* We're done examining the InsertStmt */ |
748 | 0 | return false; |
749 | 0 | } |
750 | | /* if no WITH clause, just fall through for normal processing */ |
751 | 0 | } |
752 | 0 | else if (IsA(node, DeleteStmt)) |
753 | 0 | { |
754 | 0 | DeleteStmt *stmt = (DeleteStmt *) node; |
755 | |
|
756 | 0 | if (stmt->withClause) |
757 | 0 | { |
758 | | /* Examine the WITH clause and the DeleteStmt */ |
759 | 0 | WalkInnerWith(node, stmt->withClause, cstate); |
760 | | /* We're done examining the DeleteStmt */ |
761 | 0 | return false; |
762 | 0 | } |
763 | | /* if no WITH clause, just fall through for normal processing */ |
764 | 0 | } |
765 | 0 | else if (IsA(node, UpdateStmt)) |
766 | 0 | { |
767 | 0 | UpdateStmt *stmt = (UpdateStmt *) node; |
768 | |
|
769 | 0 | if (stmt->withClause) |
770 | 0 | { |
771 | | /* Examine the WITH clause and the UpdateStmt */ |
772 | 0 | WalkInnerWith(node, stmt->withClause, cstate); |
773 | | /* We're done examining the UpdateStmt */ |
774 | 0 | return false; |
775 | 0 | } |
776 | | /* if no WITH clause, just fall through for normal processing */ |
777 | 0 | } |
778 | 0 | else if (IsA(node, MergeStmt)) |
779 | 0 | { |
780 | 0 | MergeStmt *stmt = (MergeStmt *) node; |
781 | |
|
782 | 0 | if (stmt->withClause) |
783 | 0 | { |
784 | | /* Examine the WITH clause and the MergeStmt */ |
785 | 0 | WalkInnerWith(node, stmt->withClause, cstate); |
786 | | /* We're done examining the MergeStmt */ |
787 | 0 | return false; |
788 | 0 | } |
789 | | /* if no WITH clause, just fall through for normal processing */ |
790 | 0 | } |
791 | 0 | else if (IsA(node, WithClause)) |
792 | 0 | { |
793 | | /* |
794 | | * Prevent raw_expression_tree_walker from recursing directly into a |
795 | | * WITH clause. We need that to happen only under the control of the |
796 | | * code above. |
797 | | */ |
798 | 0 | return false; |
799 | 0 | } |
800 | 0 | return raw_expression_tree_walker(node, |
801 | 0 | makeDependencyGraphWalker, |
802 | 0 | cstate); |
803 | 0 | } |
804 | | |
805 | | /* |
806 | | * makeDependencyGraphWalker's recursion into a statement having a WITH clause. |
807 | | * |
808 | | * This subroutine is concerned with updating the innerwiths list correctly |
809 | | * based on the visibility rules for CTE names. |
810 | | */ |
811 | | static void |
812 | | WalkInnerWith(Node *stmt, WithClause *withClause, CteState *cstate) |
813 | 0 | { |
814 | 0 | ListCell *lc; |
815 | |
|
816 | 0 | if (withClause->recursive) |
817 | 0 | { |
818 | | /* |
819 | | * In the RECURSIVE case, all query names of the WITH are visible to |
820 | | * all WITH items as well as the main query. So push them all on, |
821 | | * process, pop them all off. |
822 | | */ |
823 | 0 | cstate->innerwiths = lcons(withClause->ctes, cstate->innerwiths); |
824 | 0 | foreach(lc, withClause->ctes) |
825 | 0 | { |
826 | 0 | CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc); |
827 | |
|
828 | 0 | (void) makeDependencyGraphWalker(cte->ctequery, cstate); |
829 | 0 | } |
830 | 0 | (void) raw_expression_tree_walker(stmt, |
831 | 0 | makeDependencyGraphWalker, |
832 | 0 | cstate); |
833 | 0 | cstate->innerwiths = list_delete_first(cstate->innerwiths); |
834 | 0 | } |
835 | 0 | else |
836 | 0 | { |
837 | | /* |
838 | | * In the non-RECURSIVE case, query names are visible to the WITH |
839 | | * items after them and to the main query. |
840 | | */ |
841 | 0 | cstate->innerwiths = lcons(NIL, cstate->innerwiths); |
842 | 0 | foreach(lc, withClause->ctes) |
843 | 0 | { |
844 | 0 | CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc); |
845 | 0 | ListCell *cell1; |
846 | |
|
847 | 0 | (void) makeDependencyGraphWalker(cte->ctequery, cstate); |
848 | | /* note that recursion could mutate innerwiths list */ |
849 | 0 | cell1 = list_head(cstate->innerwiths); |
850 | 0 | lfirst(cell1) = lappend((List *) lfirst(cell1), cte); |
851 | 0 | } |
852 | 0 | (void) raw_expression_tree_walker(stmt, |
853 | 0 | makeDependencyGraphWalker, |
854 | 0 | cstate); |
855 | 0 | cstate->innerwiths = list_delete_first(cstate->innerwiths); |
856 | 0 | } |
857 | 0 | } |
858 | | |
859 | | /* |
860 | | * Sort by dependencies, using a standard topological sort operation |
861 | | */ |
862 | | static void |
863 | | TopologicalSort(ParseState *pstate, CteItem *items, int numitems) |
864 | 0 | { |
865 | 0 | int i, |
866 | 0 | j; |
867 | | |
868 | | /* for each position in sequence ... */ |
869 | 0 | for (i = 0; i < numitems; i++) |
870 | 0 | { |
871 | | /* ... scan the remaining items to find one that has no dependencies */ |
872 | 0 | for (j = i; j < numitems; j++) |
873 | 0 | { |
874 | 0 | if (bms_is_empty(items[j].depends_on)) |
875 | 0 | break; |
876 | 0 | } |
877 | | |
878 | | /* if we didn't find one, the dependency graph has a cycle */ |
879 | 0 | if (j >= numitems) |
880 | 0 | ereport(ERROR, |
881 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
882 | 0 | errmsg("mutual recursion between WITH items is not implemented"), |
883 | 0 | parser_errposition(pstate, items[i].cte->location))); |
884 | | |
885 | | /* |
886 | | * Found one. Move it to front and remove it from every other item's |
887 | | * dependencies. |
888 | | */ |
889 | 0 | if (i != j) |
890 | 0 | { |
891 | 0 | CteItem tmp; |
892 | |
|
893 | 0 | tmp = items[i]; |
894 | 0 | items[i] = items[j]; |
895 | 0 | items[j] = tmp; |
896 | 0 | } |
897 | | |
898 | | /* |
899 | | * Items up through i are known to have no dependencies left, so we |
900 | | * can skip them in this loop. |
901 | | */ |
902 | 0 | for (j = i + 1; j < numitems; j++) |
903 | 0 | { |
904 | 0 | items[j].depends_on = bms_del_member(items[j].depends_on, |
905 | 0 | items[i].id); |
906 | 0 | } |
907 | 0 | } |
908 | 0 | } |
909 | | |
910 | | |
911 | | /* |
912 | | * Check that recursive queries are well-formed. |
913 | | */ |
914 | | static void |
915 | | checkWellFormedRecursion(CteState *cstate) |
916 | 0 | { |
917 | 0 | int i; |
918 | |
|
919 | 0 | for (i = 0; i < cstate->numitems; i++) |
920 | 0 | { |
921 | 0 | CommonTableExpr *cte = cstate->items[i].cte; |
922 | 0 | SelectStmt *stmt = (SelectStmt *) cte->ctequery; |
923 | |
|
924 | 0 | Assert(!IsA(stmt, Query)); /* not analyzed yet */ |
925 | | |
926 | | /* Ignore items that weren't found to be recursive */ |
927 | 0 | if (!cte->cterecursive) |
928 | 0 | continue; |
929 | | |
930 | | /* Must be a SELECT statement */ |
931 | 0 | if (!IsA(stmt, SelectStmt)) |
932 | 0 | ereport(ERROR, |
933 | 0 | (errcode(ERRCODE_INVALID_RECURSION), |
934 | 0 | errmsg("recursive query \"%s\" must not contain data-modifying statements", |
935 | 0 | cte->ctename), |
936 | 0 | parser_errposition(cstate->pstate, cte->location))); |
937 | | |
938 | | /* Must have top-level UNION */ |
939 | 0 | if (stmt->op != SETOP_UNION) |
940 | 0 | ereport(ERROR, |
941 | 0 | (errcode(ERRCODE_INVALID_RECURSION), |
942 | 0 | errmsg("recursive query \"%s\" does not have the form non-recursive-term UNION [ALL] recursive-term", |
943 | 0 | cte->ctename), |
944 | 0 | parser_errposition(cstate->pstate, cte->location))); |
945 | | |
946 | | /* |
947 | | * Really, we should insist that there not be a top-level WITH, since |
948 | | * syntactically that would enclose the UNION. However, we've not |
949 | | * done so in the past and it's probably too late to change. Settle |
950 | | * for insisting that WITH not contain a self-reference. Test this |
951 | | * before examining the UNION arms, to avoid issuing confusing errors |
952 | | * in such cases. |
953 | | */ |
954 | 0 | if (stmt->withClause) |
955 | 0 | { |
956 | 0 | cstate->curitem = i; |
957 | 0 | cstate->innerwiths = NIL; |
958 | 0 | cstate->selfrefcount = 0; |
959 | 0 | cstate->context = RECURSION_SUBLINK; |
960 | 0 | checkWellFormedRecursionWalker((Node *) stmt->withClause->ctes, |
961 | 0 | cstate); |
962 | 0 | Assert(cstate->innerwiths == NIL); |
963 | 0 | } |
964 | | |
965 | | /* |
966 | | * Disallow ORDER BY and similar decoration atop the UNION. These |
967 | | * don't make sense because it's impossible to figure out what they |
968 | | * mean when we have only part of the recursive query's results. (If |
969 | | * we did allow them, we'd have to check for recursive references |
970 | | * inside these subtrees. As for WITH, we have to do this before |
971 | | * examining the UNION arms, to avoid issuing confusing errors if |
972 | | * there is a recursive reference here.) |
973 | | */ |
974 | 0 | if (stmt->sortClause) |
975 | 0 | ereport(ERROR, |
976 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
977 | 0 | errmsg("ORDER BY in a recursive query is not implemented"), |
978 | 0 | parser_errposition(cstate->pstate, |
979 | 0 | exprLocation((Node *) stmt->sortClause)))); |
980 | 0 | if (stmt->limitOffset) |
981 | 0 | ereport(ERROR, |
982 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
983 | 0 | errmsg("OFFSET in a recursive query is not implemented"), |
984 | 0 | parser_errposition(cstate->pstate, |
985 | 0 | exprLocation(stmt->limitOffset)))); |
986 | 0 | if (stmt->limitCount) |
987 | 0 | ereport(ERROR, |
988 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
989 | 0 | errmsg("LIMIT in a recursive query is not implemented"), |
990 | 0 | parser_errposition(cstate->pstate, |
991 | 0 | exprLocation(stmt->limitCount)))); |
992 | 0 | if (stmt->lockingClause) |
993 | 0 | ereport(ERROR, |
994 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
995 | 0 | errmsg("FOR UPDATE/SHARE in a recursive query is not implemented"), |
996 | 0 | parser_errposition(cstate->pstate, |
997 | 0 | exprLocation((Node *) stmt->lockingClause)))); |
998 | | |
999 | | /* |
1000 | | * Now we can get on with checking the UNION operands themselves. |
1001 | | * |
1002 | | * The left-hand operand mustn't contain a self-reference at all. |
1003 | | */ |
1004 | 0 | cstate->curitem = i; |
1005 | 0 | cstate->innerwiths = NIL; |
1006 | 0 | cstate->selfrefcount = 0; |
1007 | 0 | cstate->context = RECURSION_NONRECURSIVETERM; |
1008 | 0 | checkWellFormedRecursionWalker((Node *) stmt->larg, cstate); |
1009 | 0 | Assert(cstate->innerwiths == NIL); |
1010 | | |
1011 | | /* Right-hand operand should contain one reference in a valid place */ |
1012 | 0 | cstate->curitem = i; |
1013 | 0 | cstate->innerwiths = NIL; |
1014 | 0 | cstate->selfrefcount = 0; |
1015 | 0 | cstate->context = RECURSION_OK; |
1016 | 0 | checkWellFormedRecursionWalker((Node *) stmt->rarg, cstate); |
1017 | 0 | Assert(cstate->innerwiths == NIL); |
1018 | 0 | if (cstate->selfrefcount != 1) /* shouldn't happen */ |
1019 | 0 | elog(ERROR, "missing recursive reference"); |
1020 | 0 | } |
1021 | 0 | } |
1022 | | |
1023 | | /* |
1024 | | * Tree walker function to detect invalid self-references in a recursive query. |
1025 | | */ |
1026 | | static bool |
1027 | | checkWellFormedRecursionWalker(Node *node, CteState *cstate) |
1028 | 0 | { |
1029 | 0 | RecursionContext save_context = cstate->context; |
1030 | |
|
1031 | 0 | if (node == NULL) |
1032 | 0 | return false; |
1033 | 0 | if (IsA(node, RangeVar)) |
1034 | 0 | { |
1035 | 0 | RangeVar *rv = (RangeVar *) node; |
1036 | | |
1037 | | /* If unqualified name, might be a CTE reference */ |
1038 | 0 | if (!rv->schemaname) |
1039 | 0 | { |
1040 | 0 | ListCell *lc; |
1041 | 0 | CommonTableExpr *mycte; |
1042 | | |
1043 | | /* ... but first see if it's captured by an inner WITH */ |
1044 | 0 | foreach(lc, cstate->innerwiths) |
1045 | 0 | { |
1046 | 0 | List *withlist = (List *) lfirst(lc); |
1047 | 0 | ListCell *lc2; |
1048 | |
|
1049 | 0 | foreach(lc2, withlist) |
1050 | 0 | { |
1051 | 0 | CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc2); |
1052 | |
|
1053 | 0 | if (strcmp(rv->relname, cte->ctename) == 0) |
1054 | 0 | return false; /* yes, so bail out */ |
1055 | 0 | } |
1056 | 0 | } |
1057 | | |
1058 | | /* No, could be a reference to the query level we are working on */ |
1059 | 0 | mycte = cstate->items[cstate->curitem].cte; |
1060 | 0 | if (strcmp(rv->relname, mycte->ctename) == 0) |
1061 | 0 | { |
1062 | | /* Found a recursive reference to the active query */ |
1063 | 0 | if (cstate->context != RECURSION_OK) |
1064 | 0 | ereport(ERROR, |
1065 | 0 | (errcode(ERRCODE_INVALID_RECURSION), |
1066 | 0 | errmsg(recursion_errormsgs[cstate->context], |
1067 | 0 | mycte->ctename), |
1068 | 0 | parser_errposition(cstate->pstate, |
1069 | 0 | rv->location))); |
1070 | | /* Count references */ |
1071 | 0 | if (++(cstate->selfrefcount) > 1) |
1072 | 0 | ereport(ERROR, |
1073 | 0 | (errcode(ERRCODE_INVALID_RECURSION), |
1074 | 0 | errmsg("recursive reference to query \"%s\" must not appear more than once", |
1075 | 0 | mycte->ctename), |
1076 | 0 | parser_errposition(cstate->pstate, |
1077 | 0 | rv->location))); |
1078 | 0 | } |
1079 | 0 | } |
1080 | 0 | return false; |
1081 | 0 | } |
1082 | 0 | if (IsA(node, SelectStmt)) |
1083 | 0 | { |
1084 | 0 | SelectStmt *stmt = (SelectStmt *) node; |
1085 | 0 | ListCell *lc; |
1086 | |
|
1087 | 0 | if (stmt->withClause) |
1088 | 0 | { |
1089 | 0 | if (stmt->withClause->recursive) |
1090 | 0 | { |
1091 | | /* |
1092 | | * In the RECURSIVE case, all query names of the WITH are |
1093 | | * visible to all WITH items as well as the main query. So |
1094 | | * push them all on, process, pop them all off. |
1095 | | */ |
1096 | 0 | cstate->innerwiths = lcons(stmt->withClause->ctes, |
1097 | 0 | cstate->innerwiths); |
1098 | 0 | foreach(lc, stmt->withClause->ctes) |
1099 | 0 | { |
1100 | 0 | CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc); |
1101 | |
|
1102 | 0 | (void) checkWellFormedRecursionWalker(cte->ctequery, cstate); |
1103 | 0 | } |
1104 | 0 | checkWellFormedSelectStmt(stmt, cstate); |
1105 | 0 | cstate->innerwiths = list_delete_first(cstate->innerwiths); |
1106 | 0 | } |
1107 | 0 | else |
1108 | 0 | { |
1109 | | /* |
1110 | | * In the non-RECURSIVE case, query names are visible to the |
1111 | | * WITH items after them and to the main query. |
1112 | | */ |
1113 | 0 | cstate->innerwiths = lcons(NIL, cstate->innerwiths); |
1114 | 0 | foreach(lc, stmt->withClause->ctes) |
1115 | 0 | { |
1116 | 0 | CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc); |
1117 | 0 | ListCell *cell1; |
1118 | |
|
1119 | 0 | (void) checkWellFormedRecursionWalker(cte->ctequery, cstate); |
1120 | | /* note that recursion could mutate innerwiths list */ |
1121 | 0 | cell1 = list_head(cstate->innerwiths); |
1122 | 0 | lfirst(cell1) = lappend((List *) lfirst(cell1), cte); |
1123 | 0 | } |
1124 | 0 | checkWellFormedSelectStmt(stmt, cstate); |
1125 | 0 | cstate->innerwiths = list_delete_first(cstate->innerwiths); |
1126 | 0 | } |
1127 | 0 | } |
1128 | 0 | else |
1129 | 0 | checkWellFormedSelectStmt(stmt, cstate); |
1130 | | /* We're done examining the SelectStmt */ |
1131 | 0 | return false; |
1132 | 0 | } |
1133 | 0 | if (IsA(node, WithClause)) |
1134 | 0 | { |
1135 | | /* |
1136 | | * Prevent raw_expression_tree_walker from recursing directly into a |
1137 | | * WITH clause. We need that to happen only under the control of the |
1138 | | * code above. |
1139 | | */ |
1140 | 0 | return false; |
1141 | 0 | } |
1142 | 0 | if (IsA(node, JoinExpr)) |
1143 | 0 | { |
1144 | 0 | JoinExpr *j = (JoinExpr *) node; |
1145 | |
|
1146 | 0 | switch (j->jointype) |
1147 | 0 | { |
1148 | 0 | case JOIN_INNER: |
1149 | 0 | checkWellFormedRecursionWalker(j->larg, cstate); |
1150 | 0 | checkWellFormedRecursionWalker(j->rarg, cstate); |
1151 | 0 | checkWellFormedRecursionWalker(j->quals, cstate); |
1152 | 0 | break; |
1153 | 0 | case JOIN_LEFT: |
1154 | 0 | checkWellFormedRecursionWalker(j->larg, cstate); |
1155 | 0 | if (save_context == RECURSION_OK) |
1156 | 0 | cstate->context = RECURSION_OUTERJOIN; |
1157 | 0 | checkWellFormedRecursionWalker(j->rarg, cstate); |
1158 | 0 | cstate->context = save_context; |
1159 | 0 | checkWellFormedRecursionWalker(j->quals, cstate); |
1160 | 0 | break; |
1161 | 0 | case JOIN_FULL: |
1162 | 0 | if (save_context == RECURSION_OK) |
1163 | 0 | cstate->context = RECURSION_OUTERJOIN; |
1164 | 0 | checkWellFormedRecursionWalker(j->larg, cstate); |
1165 | 0 | checkWellFormedRecursionWalker(j->rarg, cstate); |
1166 | 0 | cstate->context = save_context; |
1167 | 0 | checkWellFormedRecursionWalker(j->quals, cstate); |
1168 | 0 | break; |
1169 | 0 | case JOIN_RIGHT: |
1170 | 0 | if (save_context == RECURSION_OK) |
1171 | 0 | cstate->context = RECURSION_OUTERJOIN; |
1172 | 0 | checkWellFormedRecursionWalker(j->larg, cstate); |
1173 | 0 | cstate->context = save_context; |
1174 | 0 | checkWellFormedRecursionWalker(j->rarg, cstate); |
1175 | 0 | checkWellFormedRecursionWalker(j->quals, cstate); |
1176 | 0 | break; |
1177 | 0 | default: |
1178 | 0 | elog(ERROR, "unrecognized join type: %d", |
1179 | 0 | (int) j->jointype); |
1180 | 0 | } |
1181 | 0 | return false; |
1182 | 0 | } |
1183 | 0 | if (IsA(node, SubLink)) |
1184 | 0 | { |
1185 | 0 | SubLink *sl = (SubLink *) node; |
1186 | | |
1187 | | /* |
1188 | | * we intentionally override outer context, since subquery is |
1189 | | * independent |
1190 | | */ |
1191 | 0 | cstate->context = RECURSION_SUBLINK; |
1192 | 0 | checkWellFormedRecursionWalker(sl->subselect, cstate); |
1193 | 0 | cstate->context = save_context; |
1194 | 0 | checkWellFormedRecursionWalker(sl->testexpr, cstate); |
1195 | 0 | return false; |
1196 | 0 | } |
1197 | 0 | return raw_expression_tree_walker(node, |
1198 | 0 | checkWellFormedRecursionWalker, |
1199 | 0 | cstate); |
1200 | 0 | } |
1201 | | |
1202 | | /* |
1203 | | * subroutine for checkWellFormedRecursionWalker: process a SelectStmt |
1204 | | * without worrying about its WITH clause |
1205 | | */ |
1206 | | static void |
1207 | | checkWellFormedSelectStmt(SelectStmt *stmt, CteState *cstate) |
1208 | 0 | { |
1209 | 0 | RecursionContext save_context = cstate->context; |
1210 | |
|
1211 | 0 | if (save_context != RECURSION_OK) |
1212 | 0 | { |
1213 | | /* just recurse without changing state */ |
1214 | 0 | raw_expression_tree_walker((Node *) stmt, |
1215 | 0 | checkWellFormedRecursionWalker, |
1216 | 0 | cstate); |
1217 | 0 | } |
1218 | 0 | else |
1219 | 0 | { |
1220 | 0 | switch (stmt->op) |
1221 | 0 | { |
1222 | 0 | case SETOP_NONE: |
1223 | 0 | case SETOP_UNION: |
1224 | 0 | raw_expression_tree_walker((Node *) stmt, |
1225 | 0 | checkWellFormedRecursionWalker, |
1226 | 0 | cstate); |
1227 | 0 | break; |
1228 | 0 | case SETOP_INTERSECT: |
1229 | 0 | if (stmt->all) |
1230 | 0 | cstate->context = RECURSION_INTERSECT; |
1231 | 0 | checkWellFormedRecursionWalker((Node *) stmt->larg, |
1232 | 0 | cstate); |
1233 | 0 | checkWellFormedRecursionWalker((Node *) stmt->rarg, |
1234 | 0 | cstate); |
1235 | 0 | cstate->context = save_context; |
1236 | 0 | checkWellFormedRecursionWalker((Node *) stmt->sortClause, |
1237 | 0 | cstate); |
1238 | 0 | checkWellFormedRecursionWalker((Node *) stmt->limitOffset, |
1239 | 0 | cstate); |
1240 | 0 | checkWellFormedRecursionWalker((Node *) stmt->limitCount, |
1241 | 0 | cstate); |
1242 | 0 | checkWellFormedRecursionWalker((Node *) stmt->lockingClause, |
1243 | 0 | cstate); |
1244 | | /* stmt->withClause is intentionally ignored here */ |
1245 | 0 | break; |
1246 | 0 | case SETOP_EXCEPT: |
1247 | 0 | if (stmt->all) |
1248 | 0 | cstate->context = RECURSION_EXCEPT; |
1249 | 0 | checkWellFormedRecursionWalker((Node *) stmt->larg, |
1250 | 0 | cstate); |
1251 | 0 | cstate->context = RECURSION_EXCEPT; |
1252 | 0 | checkWellFormedRecursionWalker((Node *) stmt->rarg, |
1253 | 0 | cstate); |
1254 | 0 | cstate->context = save_context; |
1255 | 0 | checkWellFormedRecursionWalker((Node *) stmt->sortClause, |
1256 | 0 | cstate); |
1257 | 0 | checkWellFormedRecursionWalker((Node *) stmt->limitOffset, |
1258 | 0 | cstate); |
1259 | 0 | checkWellFormedRecursionWalker((Node *) stmt->limitCount, |
1260 | 0 | cstate); |
1261 | 0 | checkWellFormedRecursionWalker((Node *) stmt->lockingClause, |
1262 | 0 | cstate); |
1263 | | /* stmt->withClause is intentionally ignored here */ |
1264 | 0 | break; |
1265 | 0 | default: |
1266 | 0 | elog(ERROR, "unrecognized set op: %d", |
1267 | 0 | (int) stmt->op); |
1268 | 0 | } |
1269 | 0 | } |
1270 | 0 | } |