LCOV - code coverage report
Current view: top level - src/backend/parser - parse_cte.c (source / functions) Hit Total Coverage
Test: PostgreSQL 15devel Lines: 350 387 90.4 %
Date: 2021-12-04 22:09:09 Functions: 9 9 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14