1 /*-------------------------------------------------------------------------
4 * handle CTEs (common table expressions) in parser
6 * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/parser/parse_cte.c
13 *-------------------------------------------------------------------------
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_cte.h"
22 #include "utils/builtins.h"
23 #include "utils/lsyscache.h"
26 /* Enumeration of contexts in which a self-reference is disallowed */
30 RECURSION_NONRECURSIVETERM, /* inside the left-hand term */
31 RECURSION_SUBLINK, /* inside a sublink */
32 RECURSION_OUTERJOIN, /* inside nullable side of an outer join */
33 RECURSION_INTERSECT, /* underneath INTERSECT (ALL) */
34 RECURSION_EXCEPT /* underneath EXCEPT (ALL) */
37 /* Associated error messages --- each must have one %s for CTE name */
38 static const char *const recursion_errormsgs[] = {
41 /* RECURSION_NONRECURSIVETERM */
42 gettext_noop("recursive reference to query \"%s\" must not appear within its non-recursive term"),
43 /* RECURSION_SUBLINK */
44 gettext_noop("recursive reference to query \"%s\" must not appear within a subquery"),
45 /* RECURSION_OUTERJOIN */
46 gettext_noop("recursive reference to query \"%s\" must not appear within an outer join"),
47 /* RECURSION_INTERSECT */
48 gettext_noop("recursive reference to query \"%s\" must not appear within INTERSECT"),
49 /* RECURSION_EXCEPT */
50 gettext_noop("recursive reference to query \"%s\" must not appear within EXCEPT")
54 * For WITH RECURSIVE, we have to find an ordering of the clause members
55 * with no forward references, and determine which members are recursive
56 * (i.e., self-referential). It is convenient to do this with an array
57 * of CteItems instead of a list of CommonTableExprs.
59 typedef struct CteItem
61 CommonTableExpr *cte; /* One CTE to examine */
62 int id; /* Its ID number for dependencies */
63 Bitmapset *depends_on; /* CTEs depended on (not including self) */
66 /* CteState is what we need to pass around in the tree walkers */
67 typedef struct CteState
70 ParseState *pstate; /* global parse state */
71 CteItem *items; /* array of CTEs and extra data */
72 int numitems; /* number of CTEs */
73 /* working state during a tree walk: */
74 int curitem; /* index of item currently being examined */
75 List *innerwiths; /* list of lists of CommonTableExpr */
76 /* working state for checkWellFormedRecursion walk only: */
77 int selfrefcount; /* number of self-references detected */
78 RecursionContext context; /* context to allow or disallow self-ref */
82 static void analyzeCTE(ParseState *pstate, CommonTableExpr *cte);
84 /* Dependency processing functions */
85 static void makeDependencyGraph(CteState *cstate);
86 static bool makeDependencyGraphWalker(Node *node, CteState *cstate);
87 static void TopologicalSort(ParseState *pstate, CteItem *items, int numitems);
89 /* Recursion validity checker functions */
90 static void checkWellFormedRecursion(CteState *cstate);
91 static bool checkWellFormedRecursionWalker(Node *node, CteState *cstate);
92 static void checkWellFormedSelectStmt(SelectStmt *stmt, CteState *cstate);
96 * transformWithClause -
97 * Transform the list of WITH clause "common table expressions" into
100 * The result is the list of transformed CTEs to be put into the output
101 * Query. (This is in fact the same as the ending value of p_ctenamespace,
102 * but it seems cleaner to not expose that in the function's API.)
105 transformWithClause(ParseState *pstate, WithClause *withClause)
109 /* Only one WITH clause per query level */
110 Assert(pstate->p_ctenamespace == NIL);
111 Assert(pstate->p_future_ctes == NIL);
114 * For either type of WITH, there must not be duplicate CTE names in the
115 * list. Check this right away so we needn't worry later.
117 * Also, tentatively mark each CTE as non-recursive, and initialize its
118 * reference count to zero, and set pstate->p_hasModifyingCTE if needed.
120 foreach(lc, withClause->ctes)
122 CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
125 for_each_cell(rest, lnext(lc))
127 CommonTableExpr *cte2 = (CommonTableExpr *) lfirst(rest);
129 if (strcmp(cte->ctename, cte2->ctename) == 0)
131 (errcode(ERRCODE_DUPLICATE_ALIAS),
132 errmsg("WITH query name \"%s\" specified more than once",
134 parser_errposition(pstate, cte2->location)));
137 cte->cterecursive = false;
138 cte->cterefcount = 0;
140 if (!IsA(cte->ctequery, SelectStmt))
142 /* must be a data-modifying statement */
143 Assert(IsA(cte->ctequery, InsertStmt) ||
144 IsA(cte->ctequery, UpdateStmt) ||
145 IsA(cte->ctequery, DeleteStmt));
147 pstate->p_hasModifyingCTE = true;
151 if (withClause->recursive)
154 * For WITH RECURSIVE, we rearrange the list elements if needed to
155 * eliminate forward references. First, build a work array and set up
156 * the data structure needed by the tree walkers.
161 cstate.pstate = pstate;
162 cstate.numitems = list_length(withClause->ctes);
163 cstate.items = (CteItem *) palloc0(cstate.numitems * sizeof(CteItem));
165 foreach(lc, withClause->ctes)
167 cstate.items[i].cte = (CommonTableExpr *) lfirst(lc);
168 cstate.items[i].id = i;
173 * Find all the dependencies and sort the CteItems into a safe
174 * processing order. Also, mark CTEs that contain self-references.
176 makeDependencyGraph(&cstate);
179 * Check that recursive queries are well-formed.
181 checkWellFormedRecursion(&cstate);
184 * Set up the ctenamespace for parse analysis. Per spec, all the WITH
185 * items are visible to all others, so stuff them all in before parse
186 * analysis. We build the list in safe processing order so that the
187 * planner can process the queries in sequence.
189 for (i = 0; i < cstate.numitems; i++)
191 CommonTableExpr *cte = cstate.items[i].cte;
193 pstate->p_ctenamespace = lappend(pstate->p_ctenamespace, cte);
197 * Do parse analysis in the order determined by the topological sort.
199 for (i = 0; i < cstate.numitems; i++)
201 CommonTableExpr *cte = cstate.items[i].cte;
203 analyzeCTE(pstate, cte);
209 * For non-recursive WITH, just analyze each CTE in sequence and then
210 * add it to the ctenamespace. This corresponds to the spec's
211 * definition of the scope of each WITH name. However, to allow error
212 * reports to be aware of the possibility of an erroneous reference,
213 * we maintain a list in p_future_ctes of the not-yet-visible CTEs.
215 pstate->p_future_ctes = list_copy(withClause->ctes);
217 foreach(lc, withClause->ctes)
219 CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
221 analyzeCTE(pstate, cte);
222 pstate->p_ctenamespace = lappend(pstate->p_ctenamespace, cte);
223 pstate->p_future_ctes = list_delete_first(pstate->p_future_ctes);
227 return pstate->p_ctenamespace;
232 * Perform the actual parse analysis transformation of one CTE. All
233 * CTEs it depends on have already been loaded into pstate->p_ctenamespace,
234 * and have been marked with the correct output column names/types.
237 analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
241 /* Analysis not done already */
242 Assert(!IsA(cte->ctequery, Query));
244 query = parse_sub_analyze(cte->ctequery, pstate, cte, false);
245 cte->ctequery = (Node *) query;
248 * Check that we got something reasonable. These first two cases should
249 * be prevented by the grammar.
251 if (!IsA(query, Query))
252 elog(ERROR, "unexpected non-Query statement in WITH");
253 if (query->utilityStmt != NULL)
254 elog(ERROR, "unexpected utility statement in WITH");
256 if (query->intoClause)
258 (errcode(ERRCODE_SYNTAX_ERROR),
259 errmsg("subquery in WITH cannot have SELECT INTO"),
260 parser_errposition(pstate,
261 exprLocation((Node *) query->intoClause))));
264 * We disallow data-modifying WITH except at the top level of a query,
265 * because it's not clear when such a modification should be executed.
267 if (query->commandType != CMD_SELECT &&
268 pstate->parentParseState != NULL)
270 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
271 errmsg("WITH clause containing a data-modifying statement must be at the top level"),
272 parser_errposition(pstate, cte->location)));
275 * CTE queries are always marked not canSetTag. (Currently this only
276 * matters for data-modifying statements, for which the flag will be
277 * propagated to the ModifyTable plan node.)
279 query->canSetTag = false;
281 if (!cte->cterecursive)
283 /* Compute the output column names/types if not done yet */
284 analyzeCTETargetList(pstate, cte, GetCTETargetList(cte));
289 * Verify that the previously determined output column types match
290 * what the query really produced. We have to check this because the
291 * recursive term could have overridden the non-recursive term, and we
292 * don't have any easy way to fix that.
300 lctyp = list_head(cte->ctecoltypes);
301 lctypmod = list_head(cte->ctecoltypmods);
302 lccoll = list_head(cte->ctecolcollations);
304 foreach(lctlist, GetCTETargetList(cte))
306 TargetEntry *te = (TargetEntry *) lfirst(lctlist);
312 Assert(varattno == te->resno);
313 if (lctyp == NULL || lctypmod == NULL || lccoll == NULL) /* shouldn't happen */
314 elog(ERROR, "wrong number of output columns in WITH");
315 texpr = (Node *) te->expr;
316 if (exprType(texpr) != lfirst_oid(lctyp) ||
317 exprTypmod(texpr) != lfirst_int(lctypmod))
319 (errcode(ERRCODE_DATATYPE_MISMATCH),
320 errmsg("recursive query \"%s\" column %d has type %s in non-recursive term but type %s overall",
321 cte->ctename, varattno,
322 format_type_with_typemod(lfirst_oid(lctyp),
323 lfirst_int(lctypmod)),
324 format_type_with_typemod(exprType(texpr),
326 errhint("Cast the output of the non-recursive term to the correct type."),
327 parser_errposition(pstate, exprLocation(texpr))));
328 if (exprCollation(texpr) != lfirst_oid(lccoll))
330 (errcode(ERRCODE_COLLATION_MISMATCH),
331 errmsg("recursive query \"%s\" column %d has collation \"%s\" in non-recursive term but collation \"%s\" overall",
332 cte->ctename, varattno,
333 get_collation_name(lfirst_oid(lccoll)),
334 get_collation_name(exprCollation(texpr))),
335 errhint("Use the COLLATE clause to set the collation of the non-recursive term."),
336 parser_errposition(pstate, exprLocation(texpr))));
337 lctyp = lnext(lctyp);
338 lctypmod = lnext(lctypmod);
339 lccoll = lnext(lccoll);
341 if (lctyp != NULL || lctypmod != NULL || lccoll != NULL) /* shouldn't happen */
342 elog(ERROR, "wrong number of output columns in WITH");
347 * Compute derived fields of a CTE, given the transformed output targetlist
349 * For a nonrecursive CTE, this is called after transforming the CTE's query.
350 * For a recursive CTE, we call it after transforming the non-recursive term,
351 * and pass the targetlist emitted by the non-recursive term only.
353 * Note: in the recursive case, the passed pstate is actually the one being
354 * used to analyze the CTE's query, so it is one level lower down than in
355 * the nonrecursive case. This doesn't matter since we only use it for
356 * error message context anyway.
359 analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
365 /* Not done already ... */
366 Assert(cte->ctecolnames == NIL);
369 * We need to determine column names and types. The alias column names
370 * override anything coming from the query itself. (Note: the SQL spec
371 * says that the alias list must be empty or exactly as long as the output
372 * column set; but we allow it to be shorter for consistency with Alias
375 cte->ctecolnames = copyObject(cte->aliascolnames);
376 cte->ctecoltypes = cte->ctecoltypmods = cte->ctecolcollations = NIL;
377 numaliases = list_length(cte->aliascolnames);
379 foreach(tlistitem, tlist)
381 TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
389 Assert(varattno == te->resno);
390 if (varattno > numaliases)
394 attrname = pstrdup(te->resname);
395 cte->ctecolnames = lappend(cte->ctecolnames, makeString(attrname));
397 coltype = exprType((Node *) te->expr);
398 coltypmod = exprTypmod((Node *) te->expr);
399 colcoll = exprCollation((Node *) te->expr);
402 * If the CTE is recursive, force the exposed column type of any
403 * "unknown" column to "text". This corresponds to the fact that
404 * SELECT 'foo' UNION SELECT 'bar' will ultimately produce text. We
405 * might see "unknown" as a result of an untyped literal in the
406 * non-recursive term's select list, and if we don't convert to text
407 * then we'll have a mismatch against the UNION result.
409 if (cte->cterecursive && coltype == UNKNOWNOID)
412 coltypmod = -1; /* should be -1 already, but be sure */
413 colcoll = DEFAULT_COLLATION_OID;
415 cte->ctecoltypes = lappend_oid(cte->ctecoltypes, coltype);
416 cte->ctecoltypmods = lappend_int(cte->ctecoltypmods, coltypmod);
417 cte->ctecolcollations = lappend_oid(cte->ctecolcollations, colcoll);
419 if (varattno < numaliases)
421 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
422 errmsg("WITH query \"%s\" has %d columns available but %d columns specified",
423 cte->ctename, varattno, numaliases),
424 parser_errposition(pstate, cte->location)));
429 * Identify the cross-references of a list of WITH RECURSIVE items,
430 * and sort into an order that has no forward references.
433 makeDependencyGraph(CteState *cstate)
437 for (i = 0; i < cstate->numitems; i++)
439 CommonTableExpr *cte = cstate->items[i].cte;
442 cstate->innerwiths = NIL;
443 makeDependencyGraphWalker((Node *) cte->ctequery, cstate);
444 Assert(cstate->innerwiths == NIL);
447 TopologicalSort(cstate->pstate, cstate->items, cstate->numitems);
451 * Tree walker function to detect cross-references and self-references of the
452 * CTEs in a WITH RECURSIVE list.
455 makeDependencyGraphWalker(Node *node, CteState *cstate)
459 if (IsA(node, RangeVar))
461 RangeVar *rv = (RangeVar *) node;
463 /* If unqualified name, might be a CTE reference */
469 /* ... but first see if it's captured by an inner WITH */
470 foreach(lc, cstate->innerwiths)
472 List *withlist = (List *) lfirst(lc);
475 foreach(lc2, withlist)
477 CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc2);
479 if (strcmp(rv->relname, cte->ctename) == 0)
480 return false; /* yes, so bail out */
484 /* No, could be a reference to the query level we are working on */
485 for (i = 0; i < cstate->numitems; i++)
487 CommonTableExpr *cte = cstate->items[i].cte;
489 if (strcmp(rv->relname, cte->ctename) == 0)
491 int myindex = cstate->curitem;
495 /* Add cross-item dependency */
496 cstate->items[myindex].depends_on =
497 bms_add_member(cstate->items[myindex].depends_on,
498 cstate->items[i].id);
502 /* Found out this one is self-referential */
503 cte->cterecursive = true;
511 if (IsA(node, SelectStmt))
513 SelectStmt *stmt = (SelectStmt *) node;
516 if (stmt->withClause)
518 if (stmt->withClause->recursive)
521 * In the RECURSIVE case, all query names of the WITH are
522 * visible to all WITH items as well as the main query. So
523 * push them all on, process, pop them all off.
525 cstate->innerwiths = lcons(stmt->withClause->ctes,
527 foreach(lc, stmt->withClause->ctes)
529 CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
531 (void) makeDependencyGraphWalker(cte->ctequery, cstate);
533 (void) raw_expression_tree_walker(node,
534 makeDependencyGraphWalker,
536 cstate->innerwiths = list_delete_first(cstate->innerwiths);
541 * In the non-RECURSIVE case, query names are visible to the
542 * WITH items after them and to the main query.
546 cstate->innerwiths = lcons(NIL, cstate->innerwiths);
547 cell1 = list_head(cstate->innerwiths);
548 foreach(lc, stmt->withClause->ctes)
550 CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
552 (void) makeDependencyGraphWalker(cte->ctequery, cstate);
553 lfirst(cell1) = lappend((List *) lfirst(cell1), cte);
555 (void) raw_expression_tree_walker(node,
556 makeDependencyGraphWalker,
558 cstate->innerwiths = list_delete_first(cstate->innerwiths);
560 /* We're done examining the SelectStmt */
563 /* if no WITH clause, just fall through for normal processing */
565 if (IsA(node, WithClause))
568 * Prevent raw_expression_tree_walker from recursing directly into a
569 * WITH clause. We need that to happen only under the control of the
574 return raw_expression_tree_walker(node,
575 makeDependencyGraphWalker,
580 * Sort by dependencies, using a standard topological sort operation
583 TopologicalSort(ParseState *pstate, CteItem *items, int numitems)
588 /* for each position in sequence ... */
589 for (i = 0; i < numitems; i++)
591 /* ... scan the remaining items to find one that has no dependencies */
592 for (j = i; j < numitems; j++)
594 if (bms_is_empty(items[j].depends_on))
598 /* if we didn't find one, the dependency graph has a cycle */
601 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
602 errmsg("mutual recursion between WITH items is not implemented"),
603 parser_errposition(pstate, items[i].cte->location)));
606 * Found one. Move it to front and remove it from every other item's
619 * Items up through i are known to have no dependencies left, so we
620 * can skip them in this loop.
622 for (j = i + 1; j < numitems; j++)
624 items[j].depends_on = bms_del_member(items[j].depends_on,
632 * Check that recursive queries are well-formed.
635 checkWellFormedRecursion(CteState *cstate)
639 for (i = 0; i < cstate->numitems; i++)
641 CommonTableExpr *cte = cstate->items[i].cte;
642 SelectStmt *stmt = (SelectStmt *) cte->ctequery;
644 Assert(!IsA(stmt, Query)); /* not analyzed yet */
646 /* Ignore items that weren't found to be recursive */
647 if (!cte->cterecursive)
650 /* Must be a SELECT statement */
651 if (!IsA(stmt, SelectStmt))
653 (errcode(ERRCODE_INVALID_RECURSION),
654 errmsg("recursive query \"%s\" must not contain data-modifying statements",
656 parser_errposition(cstate->pstate, cte->location)));
658 /* Must have top-level UNION */
659 if (stmt->op != SETOP_UNION)
661 (errcode(ERRCODE_INVALID_RECURSION),
662 errmsg("recursive query \"%s\" does not have the form non-recursive-term UNION [ALL] recursive-term",
664 parser_errposition(cstate->pstate, cte->location)));
666 /* The left-hand operand mustn't contain self-reference at all */
668 cstate->innerwiths = NIL;
669 cstate->selfrefcount = 0;
670 cstate->context = RECURSION_NONRECURSIVETERM;
671 checkWellFormedRecursionWalker((Node *) stmt->larg, cstate);
672 Assert(cstate->innerwiths == NIL);
674 /* Right-hand operand should contain one reference in a valid place */
676 cstate->innerwiths = NIL;
677 cstate->selfrefcount = 0;
678 cstate->context = RECURSION_OK;
679 checkWellFormedRecursionWalker((Node *) stmt->rarg, cstate);
680 Assert(cstate->innerwiths == NIL);
681 if (cstate->selfrefcount != 1) /* shouldn't happen */
682 elog(ERROR, "missing recursive reference");
685 * Disallow ORDER BY and similar decoration atop the UNION. These
686 * don't make sense because it's impossible to figure out what they
687 * mean when we have only part of the recursive query's results. (If
688 * we did allow them, we'd have to check for recursive references
689 * inside these subtrees.)
691 if (stmt->sortClause)
693 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
694 errmsg("ORDER BY in a recursive query is not implemented"),
695 parser_errposition(cstate->pstate,
696 exprLocation((Node *) stmt->sortClause))));
697 if (stmt->limitOffset)
699 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
700 errmsg("OFFSET in a recursive query is not implemented"),
701 parser_errposition(cstate->pstate,
702 exprLocation(stmt->limitOffset))));
703 if (stmt->limitCount)
705 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
706 errmsg("LIMIT in a recursive query is not implemented"),
707 parser_errposition(cstate->pstate,
708 exprLocation(stmt->limitCount))));
709 if (stmt->lockingClause)
711 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
712 errmsg("FOR UPDATE/SHARE in a recursive query is not implemented"),
713 parser_errposition(cstate->pstate,
714 exprLocation((Node *) stmt->lockingClause))));
719 * Tree walker function to detect invalid self-references in a recursive query.
722 checkWellFormedRecursionWalker(Node *node, CteState *cstate)
724 RecursionContext save_context = cstate->context;
728 if (IsA(node, RangeVar))
730 RangeVar *rv = (RangeVar *) node;
732 /* If unqualified name, might be a CTE reference */
736 CommonTableExpr *mycte;
738 /* ... but first see if it's captured by an inner WITH */
739 foreach(lc, cstate->innerwiths)
741 List *withlist = (List *) lfirst(lc);
744 foreach(lc2, withlist)
746 CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc2);
748 if (strcmp(rv->relname, cte->ctename) == 0)
749 return false; /* yes, so bail out */
753 /* No, could be a reference to the query level we are working on */
754 mycte = cstate->items[cstate->curitem].cte;
755 if (strcmp(rv->relname, mycte->ctename) == 0)
757 /* Found a recursive reference to the active query */
758 if (cstate->context != RECURSION_OK)
760 (errcode(ERRCODE_INVALID_RECURSION),
761 errmsg(recursion_errormsgs[cstate->context],
763 parser_errposition(cstate->pstate,
765 /* Count references */
766 if (++(cstate->selfrefcount) > 1)
768 (errcode(ERRCODE_INVALID_RECURSION),
769 errmsg("recursive reference to query \"%s\" must not appear more than once",
771 parser_errposition(cstate->pstate,
777 if (IsA(node, SelectStmt))
779 SelectStmt *stmt = (SelectStmt *) node;
782 if (stmt->withClause)
784 if (stmt->withClause->recursive)
787 * In the RECURSIVE case, all query names of the WITH are
788 * visible to all WITH items as well as the main query. So
789 * push them all on, process, pop them all off.
791 cstate->innerwiths = lcons(stmt->withClause->ctes,
793 foreach(lc, stmt->withClause->ctes)
795 CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
797 (void) checkWellFormedRecursionWalker(cte->ctequery, cstate);
799 checkWellFormedSelectStmt(stmt, cstate);
800 cstate->innerwiths = list_delete_first(cstate->innerwiths);
805 * In the non-RECURSIVE case, query names are visible to the
806 * WITH items after them and to the main query.
810 cstate->innerwiths = lcons(NIL, cstate->innerwiths);
811 cell1 = list_head(cstate->innerwiths);
812 foreach(lc, stmt->withClause->ctes)
814 CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
816 (void) checkWellFormedRecursionWalker(cte->ctequery, cstate);
817 lfirst(cell1) = lappend((List *) lfirst(cell1), cte);
819 checkWellFormedSelectStmt(stmt, cstate);
820 cstate->innerwiths = list_delete_first(cstate->innerwiths);
824 checkWellFormedSelectStmt(stmt, cstate);
825 /* We're done examining the SelectStmt */
828 if (IsA(node, WithClause))
831 * Prevent raw_expression_tree_walker from recursing directly into a
832 * WITH clause. We need that to happen only under the control of the
837 if (IsA(node, JoinExpr))
839 JoinExpr *j = (JoinExpr *) node;
844 checkWellFormedRecursionWalker(j->larg, cstate);
845 checkWellFormedRecursionWalker(j->rarg, cstate);
846 checkWellFormedRecursionWalker(j->quals, cstate);
849 checkWellFormedRecursionWalker(j->larg, cstate);
850 if (save_context == RECURSION_OK)
851 cstate->context = RECURSION_OUTERJOIN;
852 checkWellFormedRecursionWalker(j->rarg, cstate);
853 cstate->context = save_context;
854 checkWellFormedRecursionWalker(j->quals, cstate);
857 if (save_context == RECURSION_OK)
858 cstate->context = RECURSION_OUTERJOIN;
859 checkWellFormedRecursionWalker(j->larg, cstate);
860 checkWellFormedRecursionWalker(j->rarg, cstate);
861 cstate->context = save_context;
862 checkWellFormedRecursionWalker(j->quals, cstate);
865 if (save_context == RECURSION_OK)
866 cstate->context = RECURSION_OUTERJOIN;
867 checkWellFormedRecursionWalker(j->larg, cstate);
868 cstate->context = save_context;
869 checkWellFormedRecursionWalker(j->rarg, cstate);
870 checkWellFormedRecursionWalker(j->quals, cstate);
873 elog(ERROR, "unrecognized join type: %d",
878 if (IsA(node, SubLink))
880 SubLink *sl = (SubLink *) node;
883 * we intentionally override outer context, since subquery is
886 cstate->context = RECURSION_SUBLINK;
887 checkWellFormedRecursionWalker(sl->subselect, cstate);
888 cstate->context = save_context;
889 checkWellFormedRecursionWalker(sl->testexpr, cstate);
892 return raw_expression_tree_walker(node,
893 checkWellFormedRecursionWalker,
898 * subroutine for checkWellFormedRecursionWalker: process a SelectStmt
899 * without worrying about its WITH clause
902 checkWellFormedSelectStmt(SelectStmt *stmt, CteState *cstate)
904 RecursionContext save_context = cstate->context;
906 if (save_context != RECURSION_OK)
908 /* just recurse without changing state */
909 raw_expression_tree_walker((Node *) stmt,
910 checkWellFormedRecursionWalker,
919 raw_expression_tree_walker((Node *) stmt,
920 checkWellFormedRecursionWalker,
923 case SETOP_INTERSECT:
925 cstate->context = RECURSION_INTERSECT;
926 checkWellFormedRecursionWalker((Node *) stmt->larg,
928 checkWellFormedRecursionWalker((Node *) stmt->rarg,
930 cstate->context = save_context;
931 checkWellFormedRecursionWalker((Node *) stmt->sortClause,
933 checkWellFormedRecursionWalker((Node *) stmt->limitOffset,
935 checkWellFormedRecursionWalker((Node *) stmt->limitCount,
937 checkWellFormedRecursionWalker((Node *) stmt->lockingClause,
943 cstate->context = RECURSION_EXCEPT;
944 checkWellFormedRecursionWalker((Node *) stmt->larg,
946 cstate->context = RECURSION_EXCEPT;
947 checkWellFormedRecursionWalker((Node *) stmt->rarg,
949 cstate->context = save_context;
950 checkWellFormedRecursionWalker((Node *) stmt->sortClause,
952 checkWellFormedRecursionWalker((Node *) stmt->limitOffset,
954 checkWellFormedRecursionWalker((Node *) stmt->limitCount,
956 checkWellFormedRecursionWalker((Node *) stmt->lockingClause,
960 elog(ERROR, "unrecognized set op: %d",