Line data Source code
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 3330 : transformWithClause(ParseState *pstate, WithClause *withClause)
111 : {
112 : ListCell *lc;
113 :
114 : /* Only one WITH clause per query level */
115 : Assert(pstate->p_ctenamespace == NIL);
116 : 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 7848 : foreach(lc, withClause->ctes)
126 : {
127 4518 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
128 : ListCell *rest;
129 :
130 6854 : for_each_cell(rest, withClause->ctes, lnext(withClause->ctes, lc))
131 : {
132 2336 : CommonTableExpr *cte2 = (CommonTableExpr *) lfirst(rest);
133 :
134 2336 : if (strcmp(cte->ctename, cte2->ctename) == 0)
135 0 : ereport(ERROR,
136 : (errcode(ERRCODE_DUPLICATE_ALIAS),
137 : errmsg("WITH query name \"%s\" specified more than once",
138 : cte2->ctename),
139 : parser_errposition(pstate, cte2->location)));
140 : }
141 :
142 4518 : cte->cterecursive = false;
143 4518 : cte->cterefcount = 0;
144 :
145 4518 : if (!IsA(cte->ctequery, SelectStmt))
146 : {
147 : /* must be a data-modifying statement */
148 : Assert(IsA(cte->ctequery, InsertStmt) ||
149 : IsA(cte->ctequery, UpdateStmt) ||
150 : IsA(cte->ctequery, DeleteStmt) ||
151 : IsA(cte->ctequery, MergeStmt));
152 :
153 402 : pstate->p_hasModifyingCTE = true;
154 : }
155 : }
156 :
157 3330 : if (withClause->recursive)
158 : {
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 : CteState cstate;
165 : int i;
166 :
167 1338 : cstate.pstate = pstate;
168 1338 : cstate.numitems = list_length(withClause->ctes);
169 1338 : cstate.items = (CteItem *) palloc0(cstate.numitems * sizeof(CteItem));
170 1338 : i = 0;
171 2772 : foreach(lc, withClause->ctes)
172 : {
173 1434 : cstate.items[i].cte = (CommonTableExpr *) lfirst(lc);
174 1434 : cstate.items[i].id = i;
175 1434 : i++;
176 : }
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 1338 : makeDependencyGraph(&cstate);
183 :
184 : /*
185 : * Check that recursive queries are well-formed.
186 : */
187 1332 : 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 2454 : for (i = 0; i < cstate.numitems; i++)
196 : {
197 1272 : CommonTableExpr *cte = cstate.items[i].cte;
198 :
199 1272 : pstate->p_ctenamespace = lappend(pstate->p_ctenamespace, cte);
200 : }
201 :
202 : /*
203 : * Do parse analysis in the order determined by the topological sort.
204 : */
205 2328 : for (i = 0; i < cstate.numitems; i++)
206 : {
207 1272 : CommonTableExpr *cte = cstate.items[i].cte;
208 :
209 1272 : analyzeCTE(pstate, cte);
210 : }
211 : }
212 : else
213 : {
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 1992 : pstate->p_future_ctes = list_copy(withClause->ctes);
222 :
223 5056 : foreach(lc, withClause->ctes)
224 : {
225 3084 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
226 :
227 3084 : analyzeCTE(pstate, cte);
228 3064 : pstate->p_ctenamespace = lappend(pstate->p_ctenamespace, cte);
229 3064 : pstate->p_future_ctes = list_delete_first(pstate->p_future_ctes);
230 : }
231 : }
232 :
233 3028 : return pstate->p_ctenamespace;
234 : }
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 4356 : analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
244 : {
245 : Query *query;
246 4356 : CTESearchClause *search_clause = cte->search_clause;
247 4356 : CTECycleClause *cycle_clause = cte->cycle_clause;
248 :
249 : /* Analysis not done already */
250 : 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 4356 : if (cycle_clause)
258 : {
259 : TypeCacheEntry *typentry;
260 : Oid op;
261 :
262 126 : cycle_clause->cycle_mark_value =
263 126 : transformExpr(pstate, cycle_clause->cycle_mark_value,
264 : EXPR_KIND_CYCLE_MARK);
265 126 : cycle_clause->cycle_mark_default =
266 126 : transformExpr(pstate, cycle_clause->cycle_mark_default,
267 : EXPR_KIND_CYCLE_MARK);
268 :
269 120 : cycle_clause->cycle_mark_type =
270 126 : select_common_type(pstate,
271 126 : list_make2(cycle_clause->cycle_mark_value,
272 : cycle_clause->cycle_mark_default),
273 : "CYCLE", NULL);
274 120 : cycle_clause->cycle_mark_value =
275 120 : coerce_to_common_type(pstate,
276 : cycle_clause->cycle_mark_value,
277 : cycle_clause->cycle_mark_type,
278 : "CYCLE/SET/TO");
279 120 : cycle_clause->cycle_mark_default =
280 120 : coerce_to_common_type(pstate,
281 : cycle_clause->cycle_mark_default,
282 : cycle_clause->cycle_mark_type,
283 : "CYCLE/SET/DEFAULT");
284 :
285 120 : cycle_clause->cycle_mark_typmod =
286 120 : select_common_typmod(pstate,
287 120 : list_make2(cycle_clause->cycle_mark_value,
288 : cycle_clause->cycle_mark_default),
289 : cycle_clause->cycle_mark_type);
290 :
291 120 : cycle_clause->cycle_mark_collation =
292 120 : select_common_collation(pstate,
293 120 : list_make2(cycle_clause->cycle_mark_value,
294 : cycle_clause->cycle_mark_default),
295 : true);
296 :
297 : /* Might as well look up the relevant <> operator while we are at it */
298 120 : typentry = lookup_type_cache(cycle_clause->cycle_mark_type,
299 : TYPECACHE_EQ_OPR);
300 120 : if (!OidIsValid(typentry->eq_opr))
301 6 : ereport(ERROR,
302 : errcode(ERRCODE_UNDEFINED_FUNCTION),
303 : errmsg("could not identify an equality operator for type %s",
304 : format_type_be(cycle_clause->cycle_mark_type)));
305 114 : op = get_negator(typentry->eq_opr);
306 114 : if (!OidIsValid(op))
307 0 : ereport(ERROR,
308 : errcode(ERRCODE_UNDEFINED_FUNCTION),
309 : errmsg("could not identify an inequality operator for type %s",
310 : format_type_be(cycle_clause->cycle_mark_type)));
311 :
312 114 : cycle_clause->cycle_mark_neop = op;
313 : }
314 :
315 : /* Now we can get on with analyzing the CTE's query */
316 4344 : query = parse_sub_analyze(cte->ctequery, pstate, cte, false, true);
317 4318 : 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 4318 : if (!IsA(query, Query))
324 0 : elog(ERROR, "unexpected non-Query statement in WITH");
325 4318 : 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 4318 : if (query->commandType != CMD_SELECT &&
333 384 : pstate->parentParseState != NULL)
334 6 : ereport(ERROR,
335 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
336 : errmsg("WITH clause containing a data-modifying statement must be at the top level"),
337 : 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 4312 : query->canSetTag = false;
345 :
346 4312 : if (!cte->cterecursive)
347 : {
348 : /* Compute the output column names/types if not done yet */
349 3184 : analyzeCTETargetList(pstate, cte, GetCTETargetList(cte));
350 : }
351 : else
352 : {
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 : ListCell *lctlist,
360 : *lctyp,
361 : *lctypmod,
362 : *lccoll;
363 : int varattno;
364 :
365 1128 : lctyp = list_head(cte->ctecoltypes);
366 1128 : lctypmod = list_head(cte->ctecoltypmods);
367 1128 : lccoll = list_head(cte->ctecolcollations);
368 1128 : varattno = 0;
369 3614 : foreach(lctlist, GetCTETargetList(cte))
370 : {
371 2510 : TargetEntry *te = (TargetEntry *) lfirst(lctlist);
372 : Node *texpr;
373 :
374 2510 : if (te->resjunk)
375 0 : continue;
376 2510 : varattno++;
377 : Assert(varattno == te->resno);
378 2510 : if (lctyp == NULL || lctypmod == NULL || lccoll == NULL) /* shouldn't happen */
379 0 : elog(ERROR, "wrong number of output columns in WITH");
380 2510 : texpr = (Node *) te->expr;
381 2510 : if (exprType(texpr) != lfirst_oid(lctyp) ||
382 2504 : exprTypmod(texpr) != lfirst_int(lctypmod))
383 12 : ereport(ERROR,
384 : (errcode(ERRCODE_DATATYPE_MISMATCH),
385 : errmsg("recursive query \"%s\" column %d has type %s in non-recursive term but type %s overall",
386 : cte->ctename, varattno,
387 : format_type_with_typemod(lfirst_oid(lctyp),
388 : lfirst_int(lctypmod)),
389 : format_type_with_typemod(exprType(texpr),
390 : exprTypmod(texpr))),
391 : errhint("Cast the output of the non-recursive term to the correct type."),
392 : parser_errposition(pstate, exprLocation(texpr))));
393 2498 : if (exprCollation(texpr) != lfirst_oid(lccoll))
394 12 : ereport(ERROR,
395 : (errcode(ERRCODE_COLLATION_MISMATCH),
396 : errmsg("recursive query \"%s\" column %d has collation \"%s\" in non-recursive term but collation \"%s\" overall",
397 : cte->ctename, varattno,
398 : get_collation_name(lfirst_oid(lccoll)),
399 : get_collation_name(exprCollation(texpr))),
400 : errhint("Use the COLLATE clause to set the collation of the non-recursive term."),
401 : parser_errposition(pstate, exprLocation(texpr))));
402 2486 : lctyp = lnext(cte->ctecoltypes, lctyp);
403 2486 : lctypmod = lnext(cte->ctecoltypmods, lctypmod);
404 2486 : lccoll = lnext(cte->ctecolcollations, lccoll);
405 : }
406 1104 : if (lctyp != NULL || lctypmod != NULL || lccoll != NULL) /* shouldn't happen */
407 0 : elog(ERROR, "wrong number of output columns in WITH");
408 : }
409 :
410 : /*
411 : * Now make validity checks on the SEARCH and CYCLE clauses, if present.
412 : */
413 4282 : if (search_clause || cycle_clause)
414 : {
415 : Query *ctequery;
416 : SetOperationStmt *sos;
417 :
418 216 : if (!cte->cterecursive)
419 0 : ereport(ERROR,
420 : (errcode(ERRCODE_SYNTAX_ERROR),
421 : errmsg("WITH query is not recursive"),
422 : 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 216 : ctequery = castNode(Query, cte->ctequery);
441 : Assert(ctequery->setOperations);
442 216 : 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 216 : if (!IsA(sos->larg, RangeTblRef))
450 6 : ereport(ERROR,
451 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
452 : errmsg("with a SEARCH or CYCLE clause, the left side of the UNION must be a SELECT")));
453 :
454 210 : if (!IsA(sos->rarg, RangeTblRef))
455 6 : ereport(ERROR,
456 : (errcode(ERRCODE_SYNTAX_ERROR),
457 : errmsg("with a SEARCH or CYCLE clause, the right side of the UNION must be a SELECT")));
458 : }
459 :
460 4270 : if (search_clause)
461 : {
462 : ListCell *lc;
463 114 : List *seen = NIL;
464 :
465 300 : foreach(lc, search_clause->search_col_list)
466 : {
467 198 : String *colname = lfirst_node(String, lc);
468 :
469 198 : if (!list_member(cte->ctecolnames, colname))
470 6 : ereport(ERROR,
471 : (errcode(ERRCODE_SYNTAX_ERROR),
472 : errmsg("search column \"%s\" not in WITH query column list",
473 : strVal(colname)),
474 : parser_errposition(pstate, search_clause->location)));
475 :
476 192 : if (list_member(seen, colname))
477 6 : ereport(ERROR,
478 : (errcode(ERRCODE_DUPLICATE_COLUMN),
479 : errmsg("search column \"%s\" specified more than once",
480 : strVal(colname)),
481 : parser_errposition(pstate, search_clause->location)));
482 186 : seen = lappend(seen, colname);
483 : }
484 :
485 102 : if (list_member(cte->ctecolnames, makeString(search_clause->search_seq_column)))
486 6 : ereport(ERROR,
487 : errcode(ERRCODE_SYNTAX_ERROR),
488 : errmsg("search sequence column name \"%s\" already used in WITH query column list",
489 : search_clause->search_seq_column),
490 : parser_errposition(pstate, search_clause->location));
491 : }
492 :
493 4252 : if (cycle_clause)
494 : {
495 : ListCell *lc;
496 114 : List *seen = NIL;
497 :
498 306 : foreach(lc, cycle_clause->cycle_col_list)
499 : {
500 204 : String *colname = lfirst_node(String, lc);
501 :
502 204 : if (!list_member(cte->ctecolnames, colname))
503 6 : ereport(ERROR,
504 : (errcode(ERRCODE_SYNTAX_ERROR),
505 : errmsg("cycle column \"%s\" not in WITH query column list",
506 : strVal(colname)),
507 : parser_errposition(pstate, cycle_clause->location)));
508 :
509 198 : if (list_member(seen, colname))
510 6 : ereport(ERROR,
511 : (errcode(ERRCODE_DUPLICATE_COLUMN),
512 : errmsg("cycle column \"%s\" specified more than once",
513 : strVal(colname)),
514 : parser_errposition(pstate, cycle_clause->location)));
515 192 : seen = lappend(seen, colname);
516 : }
517 :
518 102 : if (list_member(cte->ctecolnames, makeString(cycle_clause->cycle_mark_column)))
519 6 : ereport(ERROR,
520 : errcode(ERRCODE_SYNTAX_ERROR),
521 : errmsg("cycle mark column name \"%s\" already used in WITH query column list",
522 : cycle_clause->cycle_mark_column),
523 : parser_errposition(pstate, cycle_clause->location));
524 :
525 96 : if (list_member(cte->ctecolnames, makeString(cycle_clause->cycle_path_column)))
526 6 : ereport(ERROR,
527 : errcode(ERRCODE_SYNTAX_ERROR),
528 : errmsg("cycle path column name \"%s\" already used in WITH query column list",
529 : cycle_clause->cycle_path_column),
530 : parser_errposition(pstate, cycle_clause->location));
531 :
532 90 : if (strcmp(cycle_clause->cycle_mark_column,
533 90 : cycle_clause->cycle_path_column) == 0)
534 6 : ereport(ERROR,
535 : errcode(ERRCODE_SYNTAX_ERROR),
536 : errmsg("cycle mark column name and cycle path column name are the same"),
537 : parser_errposition(pstate, cycle_clause->location));
538 : }
539 :
540 4222 : if (search_clause && cycle_clause)
541 : {
542 24 : if (strcmp(search_clause->search_seq_column,
543 24 : cycle_clause->cycle_mark_column) == 0)
544 6 : ereport(ERROR,
545 : errcode(ERRCODE_SYNTAX_ERROR),
546 : errmsg("search sequence column name and cycle mark column name are the same"),
547 : parser_errposition(pstate, search_clause->location));
548 :
549 18 : if (strcmp(search_clause->search_seq_column,
550 18 : cycle_clause->cycle_path_column) == 0)
551 6 : ereport(ERROR,
552 : errcode(ERRCODE_SYNTAX_ERROR),
553 : errmsg("search sequence column name and cycle path column name are the same"),
554 : parser_errposition(pstate, search_clause->location));
555 : }
556 4210 : }
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 4330 : analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
572 : {
573 : int numaliases;
574 : int varattno;
575 : ListCell *tlistitem;
576 :
577 : /* Not done already ... */
578 : 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 4330 : cte->ctecolnames = copyObject(cte->aliascolnames);
588 4330 : cte->ctecoltypes = cte->ctecoltypmods = cte->ctecolcollations = NIL;
589 4330 : numaliases = list_length(cte->aliascolnames);
590 4330 : varattno = 0;
591 14644 : foreach(tlistitem, tlist)
592 : {
593 10314 : TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
594 : Oid coltype;
595 : int32 coltypmod;
596 : Oid colcoll;
597 :
598 10314 : if (te->resjunk)
599 6 : continue;
600 10308 : varattno++;
601 : Assert(varattno == te->resno);
602 10308 : if (varattno > numaliases)
603 : {
604 : char *attrname;
605 :
606 4268 : attrname = pstrdup(te->resname);
607 4268 : cte->ctecolnames = lappend(cte->ctecolnames, makeString(attrname));
608 : }
609 10308 : coltype = exprType((Node *) te->expr);
610 10308 : coltypmod = exprTypmod((Node *) te->expr);
611 10308 : 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 10308 : if (cte->cterecursive && coltype == UNKNOWNOID)
624 : {
625 36 : coltype = TEXTOID;
626 36 : coltypmod = -1; /* should be -1 already, but be sure */
627 36 : if (!OidIsValid(colcoll))
628 36 : colcoll = DEFAULT_COLLATION_OID;
629 : }
630 10308 : cte->ctecoltypes = lappend_oid(cte->ctecoltypes, coltype);
631 10308 : cte->ctecoltypmods = lappend_int(cte->ctecoltypmods, coltypmod);
632 10308 : cte->ctecolcollations = lappend_oid(cte->ctecolcollations, colcoll);
633 : }
634 4330 : if (varattno < numaliases)
635 6 : ereport(ERROR,
636 : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
637 : errmsg("WITH query \"%s\" has %d columns available but %d columns specified",
638 : cte->ctename, varattno, numaliases),
639 : parser_errposition(pstate, cte->location)));
640 4324 : }
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 1338 : makeDependencyGraph(CteState *cstate)
649 : {
650 : int i;
651 :
652 2772 : for (i = 0; i < cstate->numitems; i++)
653 : {
654 1434 : CommonTableExpr *cte = cstate->items[i].cte;
655 :
656 1434 : cstate->curitem = i;
657 1434 : cstate->innerwiths = NIL;
658 1434 : makeDependencyGraphWalker((Node *) cte->ctequery, cstate);
659 : Assert(cstate->innerwiths == NIL);
660 : }
661 :
662 1338 : TopologicalSort(cstate->pstate, cstate->items, cstate->numitems);
663 1332 : }
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 168570 : makeDependencyGraphWalker(Node *node, CteState *cstate)
671 : {
672 168570 : if (node == NULL)
673 92234 : return false;
674 76336 : if (IsA(node, RangeVar))
675 : {
676 7446 : RangeVar *rv = (RangeVar *) node;
677 :
678 : /* If unqualified name, might be a CTE reference */
679 7446 : if (!rv->schemaname)
680 : {
681 : ListCell *lc;
682 : int i;
683 :
684 : /* ... but first see if it's captured by an inner WITH */
685 6364 : foreach(lc, cstate->innerwiths)
686 : {
687 1118 : List *withlist = (List *) lfirst(lc);
688 : ListCell *lc2;
689 :
690 1232 : foreach(lc2, withlist)
691 : {
692 946 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc2);
693 :
694 946 : if (strcmp(rv->relname, cte->ctename) == 0)
695 832 : return false; /* yes, so bail out */
696 : }
697 : }
698 :
699 : /* No, could be a reference to the query level we are working on */
700 9154 : for (i = 0; i < cstate->numitems; i++)
701 : {
702 5384 : CommonTableExpr *cte = cstate->items[i].cte;
703 :
704 5384 : if (strcmp(rv->relname, cte->ctename) == 0)
705 : {
706 1476 : int myindex = cstate->curitem;
707 :
708 1476 : if (i != myindex)
709 : {
710 : /* Add cross-item dependency */
711 126 : cstate->items[myindex].depends_on =
712 126 : bms_add_member(cstate->items[myindex].depends_on,
713 126 : cstate->items[i].id);
714 : }
715 : else
716 : {
717 : /* Found out this one is self-referential */
718 1350 : cte->cterecursive = true;
719 : }
720 1476 : break;
721 : }
722 : }
723 : }
724 6614 : return false;
725 : }
726 68890 : if (IsA(node, SelectStmt))
727 : {
728 5848 : SelectStmt *stmt = (SelectStmt *) node;
729 :
730 5848 : if (stmt->withClause)
731 : {
732 : /* Examine the WITH clause and the SelectStmt */
733 328 : WalkInnerWith(node, stmt->withClause, cstate);
734 : /* We're done examining the SelectStmt */
735 328 : return false;
736 : }
737 : /* if no WITH clause, just fall through for normal processing */
738 : }
739 63042 : else if (IsA(node, InsertStmt))
740 : {
741 42 : InsertStmt *stmt = (InsertStmt *) node;
742 :
743 42 : if (stmt->withClause)
744 : {
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 : }
750 : /* if no WITH clause, just fall through for normal processing */
751 : }
752 63000 : else if (IsA(node, DeleteStmt))
753 : {
754 6 : DeleteStmt *stmt = (DeleteStmt *) node;
755 :
756 6 : if (stmt->withClause)
757 : {
758 : /* Examine the WITH clause and the DeleteStmt */
759 6 : WalkInnerWith(node, stmt->withClause, cstate);
760 : /* We're done examining the DeleteStmt */
761 6 : return false;
762 : }
763 : /* if no WITH clause, just fall through for normal processing */
764 : }
765 62994 : else if (IsA(node, UpdateStmt))
766 : {
767 6 : UpdateStmt *stmt = (UpdateStmt *) node;
768 :
769 6 : if (stmt->withClause)
770 : {
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 : }
776 : /* if no WITH clause, just fall through for normal processing */
777 : }
778 62988 : else if (IsA(node, MergeStmt))
779 : {
780 6 : MergeStmt *stmt = (MergeStmt *) node;
781 :
782 6 : if (stmt->withClause)
783 : {
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 : }
789 : /* if no WITH clause, just fall through for normal processing */
790 : }
791 62982 : else if (IsA(node, WithClause))
792 : {
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 334 : return false;
799 : }
800 68222 : return raw_expression_tree_walker(node,
801 : makeDependencyGraphWalker,
802 : cstate);
803 : }
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 334 : WalkInnerWith(Node *stmt, WithClause *withClause, CteState *cstate)
813 : {
814 : ListCell *lc;
815 :
816 334 : if (withClause->recursive)
817 : {
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 18 : cstate->innerwiths = lcons(withClause->ctes, cstate->innerwiths);
824 36 : foreach(lc, withClause->ctes)
825 : {
826 18 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
827 :
828 18 : (void) makeDependencyGraphWalker(cte->ctequery, cstate);
829 : }
830 18 : (void) raw_expression_tree_walker(stmt,
831 : makeDependencyGraphWalker,
832 : cstate);
833 18 : cstate->innerwiths = list_delete_first(cstate->innerwiths);
834 : }
835 : else
836 : {
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 316 : cstate->innerwiths = lcons(NIL, cstate->innerwiths);
842 656 : foreach(lc, withClause->ctes)
843 : {
844 340 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
845 : ListCell *cell1;
846 :
847 340 : (void) makeDependencyGraphWalker(cte->ctequery, cstate);
848 : /* note that recursion could mutate innerwiths list */
849 340 : cell1 = list_head(cstate->innerwiths);
850 340 : lfirst(cell1) = lappend((List *) lfirst(cell1), cte);
851 : }
852 316 : (void) raw_expression_tree_walker(stmt,
853 : makeDependencyGraphWalker,
854 : cstate);
855 316 : cstate->innerwiths = list_delete_first(cstate->innerwiths);
856 : }
857 334 : }
858 :
859 : /*
860 : * Sort by dependencies, using a standard topological sort operation
861 : */
862 : static void
863 1338 : TopologicalSort(ParseState *pstate, CteItem *items, int numitems)
864 : {
865 : int i,
866 : j;
867 :
868 : /* for each position in sequence ... */
869 2760 : for (i = 0; i < numitems; i++)
870 : {
871 : /* ... scan the remaining items to find one that has no dependencies */
872 1458 : for (j = i; j < numitems; j++)
873 : {
874 1452 : if (bms_is_empty(items[j].depends_on))
875 1422 : break;
876 : }
877 :
878 : /* if we didn't find one, the dependency graph has a cycle */
879 1428 : if (j >= numitems)
880 6 : ereport(ERROR,
881 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
882 : errmsg("mutual recursion between WITH items is not implemented"),
883 : 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 1422 : if (i != j)
890 : {
891 : CteItem tmp;
892 :
893 18 : tmp = items[i];
894 18 : items[i] = items[j];
895 18 : items[j] = tmp;
896 : }
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 1524 : for (j = i + 1; j < numitems; j++)
903 : {
904 102 : items[j].depends_on = bms_del_member(items[j].depends_on,
905 102 : items[i].id);
906 : }
907 : }
908 1332 : }
909 :
910 :
911 : /*
912 : * Check that recursive queries are well-formed.
913 : */
914 : static void
915 1332 : checkWellFormedRecursion(CteState *cstate)
916 : {
917 : int i;
918 :
919 2604 : for (i = 0; i < cstate->numitems; i++)
920 : {
921 1422 : CommonTableExpr *cte = cstate->items[i].cte;
922 1422 : SelectStmt *stmt = (SelectStmt *) cte->ctequery;
923 :
924 : Assert(!IsA(stmt, Query)); /* not analyzed yet */
925 :
926 : /* Ignore items that weren't found to be recursive */
927 1422 : if (!cte->cterecursive)
928 114 : continue;
929 :
930 : /* Must be a SELECT statement */
931 1308 : if (!IsA(stmt, SelectStmt))
932 18 : ereport(ERROR,
933 : (errcode(ERRCODE_INVALID_RECURSION),
934 : errmsg("recursive query \"%s\" must not contain data-modifying statements",
935 : cte->ctename),
936 : parser_errposition(cstate->pstate, cte->location)));
937 :
938 : /* Must have top-level UNION */
939 1290 : if (stmt->op != SETOP_UNION)
940 30 : ereport(ERROR,
941 : (errcode(ERRCODE_INVALID_RECURSION),
942 : errmsg("recursive query \"%s\" does not have the form non-recursive-term UNION [ALL] recursive-term",
943 : cte->ctename),
944 : 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 1260 : if (stmt->withClause)
955 : {
956 18 : cstate->curitem = i;
957 18 : cstate->innerwiths = NIL;
958 18 : cstate->selfrefcount = 0;
959 18 : cstate->context = RECURSION_SUBLINK;
960 18 : checkWellFormedRecursionWalker((Node *) stmt->withClause->ctes,
961 : cstate);
962 : Assert(cstate->innerwiths == NIL);
963 : }
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 1248 : if (stmt->sortClause)
975 12 : ereport(ERROR,
976 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
977 : errmsg("ORDER BY in a recursive query is not implemented"),
978 : parser_errposition(cstate->pstate,
979 : exprLocation((Node *) stmt->sortClause))));
980 1236 : if (stmt->limitOffset)
981 6 : ereport(ERROR,
982 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
983 : errmsg("OFFSET in a recursive query is not implemented"),
984 : parser_errposition(cstate->pstate,
985 : exprLocation(stmt->limitOffset))));
986 1230 : if (stmt->limitCount)
987 0 : ereport(ERROR,
988 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
989 : errmsg("LIMIT in a recursive query is not implemented"),
990 : parser_errposition(cstate->pstate,
991 : exprLocation(stmt->limitCount))));
992 1230 : if (stmt->lockingClause)
993 6 : ereport(ERROR,
994 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
995 : errmsg("FOR UPDATE/SHARE in a recursive query is not implemented"),
996 : parser_errposition(cstate->pstate,
997 : 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 1224 : cstate->curitem = i;
1005 1224 : cstate->innerwiths = NIL;
1006 1224 : cstate->selfrefcount = 0;
1007 1224 : cstate->context = RECURSION_NONRECURSIVETERM;
1008 1224 : checkWellFormedRecursionWalker((Node *) stmt->larg, cstate);
1009 : Assert(cstate->innerwiths == NIL);
1010 :
1011 : /* Right-hand operand should contain one reference in a valid place */
1012 1212 : cstate->curitem = i;
1013 1212 : cstate->innerwiths = NIL;
1014 1212 : cstate->selfrefcount = 0;
1015 1212 : cstate->context = RECURSION_OK;
1016 1212 : checkWellFormedRecursionWalker((Node *) stmt->rarg, cstate);
1017 : Assert(cstate->innerwiths == NIL);
1018 1158 : if (cstate->selfrefcount != 1) /* shouldn't happen */
1019 0 : elog(ERROR, "missing recursive reference");
1020 : }
1021 1182 : }
1022 :
1023 : /*
1024 : * Tree walker function to detect invalid self-references in a recursive query.
1025 : */
1026 : static bool
1027 135066 : checkWellFormedRecursionWalker(Node *node, CteState *cstate)
1028 : {
1029 135066 : RecursionContext save_context = cstate->context;
1030 :
1031 135066 : if (node == NULL)
1032 63410 : return false;
1033 71656 : if (IsA(node, RangeVar))
1034 : {
1035 7128 : RangeVar *rv = (RangeVar *) node;
1036 :
1037 : /* If unqualified name, might be a CTE reference */
1038 7128 : if (!rv->schemaname)
1039 : {
1040 : ListCell *lc;
1041 : CommonTableExpr *mycte;
1042 :
1043 : /* ... but first see if it's captured by an inner WITH */
1044 6010 : foreach(lc, cstate->innerwiths)
1045 : {
1046 992 : List *withlist = (List *) lfirst(lc);
1047 : ListCell *lc2;
1048 :
1049 1088 : foreach(lc2, withlist)
1050 : {
1051 838 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc2);
1052 :
1053 838 : if (strcmp(rv->relname, cte->ctename) == 0)
1054 742 : return false; /* yes, so bail out */
1055 : }
1056 : }
1057 :
1058 : /* No, could be a reference to the query level we are working on */
1059 5018 : mycte = cstate->items[cstate->curitem].cte;
1060 5018 : if (strcmp(rv->relname, mycte->ctename) == 0)
1061 : {
1062 : /* Found a recursive reference to the active query */
1063 1266 : if (cstate->context != RECURSION_OK)
1064 60 : ereport(ERROR,
1065 : (errcode(ERRCODE_INVALID_RECURSION),
1066 : errmsg(recursion_errormsgs[cstate->context],
1067 : mycte->ctename),
1068 : parser_errposition(cstate->pstate,
1069 : rv->location)));
1070 : /* Count references */
1071 1206 : if (++(cstate->selfrefcount) > 1)
1072 18 : ereport(ERROR,
1073 : (errcode(ERRCODE_INVALID_RECURSION),
1074 : errmsg("recursive reference to query \"%s\" must not appear more than once",
1075 : mycte->ctename),
1076 : parser_errposition(cstate->pstate,
1077 : rv->location)));
1078 : }
1079 : }
1080 6308 : return false;
1081 : }
1082 64528 : if (IsA(node, SelectStmt))
1083 : {
1084 4144 : SelectStmt *stmt = (SelectStmt *) node;
1085 : ListCell *lc;
1086 :
1087 4144 : if (stmt->withClause)
1088 : {
1089 250 : if (stmt->withClause->recursive)
1090 : {
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 6 : cstate->innerwiths = lcons(stmt->withClause->ctes,
1097 : cstate->innerwiths);
1098 12 : foreach(lc, stmt->withClause->ctes)
1099 : {
1100 6 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
1101 :
1102 6 : (void) checkWellFormedRecursionWalker(cte->ctequery, cstate);
1103 : }
1104 6 : checkWellFormedSelectStmt(stmt, cstate);
1105 6 : cstate->innerwiths = list_delete_first(cstate->innerwiths);
1106 : }
1107 : else
1108 : {
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 244 : cstate->innerwiths = lcons(NIL, cstate->innerwiths);
1114 506 : foreach(lc, stmt->withClause->ctes)
1115 : {
1116 268 : CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
1117 : ListCell *cell1;
1118 :
1119 268 : (void) checkWellFormedRecursionWalker(cte->ctequery, cstate);
1120 : /* note that recursion could mutate innerwiths list */
1121 262 : cell1 = list_head(cstate->innerwiths);
1122 262 : lfirst(cell1) = lappend((List *) lfirst(cell1), cte);
1123 : }
1124 238 : checkWellFormedSelectStmt(stmt, cstate);
1125 238 : cstate->innerwiths = list_delete_first(cstate->innerwiths);
1126 : }
1127 : }
1128 : else
1129 3894 : checkWellFormedSelectStmt(stmt, cstate);
1130 : /* We're done examining the SelectStmt */
1131 4018 : return false;
1132 : }
1133 60384 : if (IsA(node, WithClause))
1134 : {
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 244 : return false;
1141 : }
1142 60140 : if (IsA(node, JoinExpr))
1143 : {
1144 2940 : JoinExpr *j = (JoinExpr *) node;
1145 :
1146 2940 : switch (j->jointype)
1147 : {
1148 2836 : case JOIN_INNER:
1149 2836 : checkWellFormedRecursionWalker(j->larg, cstate);
1150 2836 : checkWellFormedRecursionWalker(j->rarg, cstate);
1151 2836 : checkWellFormedRecursionWalker(j->quals, cstate);
1152 2836 : break;
1153 92 : case JOIN_LEFT:
1154 92 : checkWellFormedRecursionWalker(j->larg, cstate);
1155 92 : if (save_context == RECURSION_OK)
1156 6 : cstate->context = RECURSION_OUTERJOIN;
1157 92 : checkWellFormedRecursionWalker(j->rarg, cstate);
1158 86 : cstate->context = save_context;
1159 86 : checkWellFormedRecursionWalker(j->quals, cstate);
1160 86 : break;
1161 6 : case JOIN_FULL:
1162 6 : if (save_context == RECURSION_OK)
1163 6 : cstate->context = RECURSION_OUTERJOIN;
1164 6 : 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 6 : case JOIN_RIGHT:
1170 6 : if (save_context == RECURSION_OK)
1171 6 : cstate->context = RECURSION_OUTERJOIN;
1172 6 : 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 : (int) j->jointype);
1180 : }
1181 2922 : return false;
1182 : }
1183 57200 : if (IsA(node, SubLink))
1184 : {
1185 92 : SubLink *sl = (SubLink *) node;
1186 :
1187 : /*
1188 : * we intentionally override outer context, since subquery is
1189 : * independent
1190 : */
1191 92 : cstate->context = RECURSION_SUBLINK;
1192 92 : checkWellFormedRecursionWalker(sl->subselect, cstate);
1193 80 : cstate->context = save_context;
1194 80 : checkWellFormedRecursionWalker(sl->testexpr, cstate);
1195 80 : return false;
1196 : }
1197 57108 : return raw_expression_tree_walker(node,
1198 : checkWellFormedRecursionWalker,
1199 : cstate);
1200 : }
1201 :
1202 : /*
1203 : * subroutine for checkWellFormedRecursionWalker: process a SelectStmt
1204 : * without worrying about its WITH clause
1205 : */
1206 : static void
1207 4138 : checkWellFormedSelectStmt(SelectStmt *stmt, CteState *cstate)
1208 : {
1209 4138 : RecursionContext save_context = cstate->context;
1210 :
1211 4138 : if (save_context != RECURSION_OK)
1212 : {
1213 : /* just recurse without changing state */
1214 1400 : raw_expression_tree_walker((Node *) stmt,
1215 : checkWellFormedRecursionWalker,
1216 : cstate);
1217 : }
1218 : else
1219 : {
1220 2738 : switch (stmt->op)
1221 : {
1222 2726 : case SETOP_NONE:
1223 : case SETOP_UNION:
1224 2726 : raw_expression_tree_walker((Node *) stmt,
1225 : checkWellFormedRecursionWalker,
1226 : cstate);
1227 2660 : break;
1228 6 : case SETOP_INTERSECT:
1229 6 : if (stmt->all)
1230 0 : cstate->context = RECURSION_INTERSECT;
1231 6 : checkWellFormedRecursionWalker((Node *) stmt->larg,
1232 : cstate);
1233 6 : checkWellFormedRecursionWalker((Node *) stmt->rarg,
1234 : cstate);
1235 0 : cstate->context = save_context;
1236 0 : checkWellFormedRecursionWalker((Node *) stmt->sortClause,
1237 : cstate);
1238 0 : checkWellFormedRecursionWalker((Node *) stmt->limitOffset,
1239 : cstate);
1240 0 : checkWellFormedRecursionWalker((Node *) stmt->limitCount,
1241 : cstate);
1242 0 : checkWellFormedRecursionWalker((Node *) stmt->lockingClause,
1243 : cstate);
1244 : /* stmt->withClause is intentionally ignored here */
1245 0 : break;
1246 6 : case SETOP_EXCEPT:
1247 6 : if (stmt->all)
1248 0 : cstate->context = RECURSION_EXCEPT;
1249 6 : checkWellFormedRecursionWalker((Node *) stmt->larg,
1250 : cstate);
1251 6 : cstate->context = RECURSION_EXCEPT;
1252 6 : checkWellFormedRecursionWalker((Node *) stmt->rarg,
1253 : cstate);
1254 0 : cstate->context = save_context;
1255 0 : checkWellFormedRecursionWalker((Node *) stmt->sortClause,
1256 : cstate);
1257 0 : checkWellFormedRecursionWalker((Node *) stmt->limitOffset,
1258 : cstate);
1259 0 : checkWellFormedRecursionWalker((Node *) stmt->limitCount,
1260 : cstate);
1261 0 : checkWellFormedRecursionWalker((Node *) stmt->lockingClause,
1262 : cstate);
1263 : /* stmt->withClause is intentionally ignored here */
1264 0 : break;
1265 0 : default:
1266 0 : elog(ERROR, "unrecognized set op: %d",
1267 : (int) stmt->op);
1268 : }
1269 : }
1270 4018 : }
|