LCOV - code coverage report
Current view: top level - src/backend/parser - parse_cte.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 95.9 % 533 511
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 10 10
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 71.2 % 532 379

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

Generated by: LCOV version 2.3.2-1