static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt);
static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
- List **colInfo);
+ bool isTopLevel, List **colInfo);
+static void determineRecursiveColTypes(ParseState *pstate,
+ Node *larg, List *lcolinfo);
static void applyColumnNames(List *dst, List *src);
static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
static List *transformReturningList(ParseState *pstate, List *returningList);
* Entry point for recursively analyzing a sub-statement.
*/
Query *
-parse_sub_analyze(Node *parseTree, ParseState *parentParseState)
+parse_sub_analyze(Node *parseTree, ParseState *parentParseState,
+ CommonTableExpr *parentCTE)
{
ParseState *pstate = make_parsestate(parentParseState);
Query *query;
+ pstate->p_parent_cte = parentCTE;
+
query = transformStmt(pstate, parseTree);
free_parsestate(pstate);
* Recursively transform the components of the tree.
*/
sostmt = (SetOperationStmt *) transformSetOperationTree(pstate, stmt,
+ true,
&socolinfo);
Assert(sostmt && IsA(sostmt, SetOperationStmt));
qry->setOperations = (Node *) sostmt;
*/
static Node *
transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
- List **colInfo)
+ bool isTopLevel, List **colInfo)
{
bool isLeaf;
* of this sub-query, because they are not in the toplevel pstate's
* namespace list.
*/
- selectQuery = parse_sub_analyze((Node *) stmt, pstate);
+ selectQuery = parse_sub_analyze((Node *) stmt, pstate, NULL);
/*
* Check for bogus references to Vars on the current query level (but
op->all = stmt->all;
/*
- * Recursively transform the child nodes.
+ * Recursively transform the left child node.
*/
op->larg = transformSetOperationTree(pstate, stmt->larg,
+ false,
&lcolinfo);
+
+ /*
+ * If we are processing a recursive union query, now is the time
+ * to examine the non-recursive term's output columns and mark the
+ * containing CTE as having those result columns. We should do this
+ * only at the topmost setop of the CTE, of course.
+ */
+ if (isTopLevel &&
+ pstate->p_parent_cte &&
+ pstate->p_parent_cte->cterecursive)
+ determineRecursiveColTypes(pstate, op->larg, lcolinfo);
+
+ /*
+ * Recursively transform the right child node.
+ */
op->rarg = transformSetOperationTree(pstate, stmt->rarg,
+ false,
&rcolinfo);
/*
}
}
+/*
+ * Process the outputs of the non-recursive term of a recursive union
+ * to set up the parent CTE's columns
+ */
+static void
+determineRecursiveColTypes(ParseState *pstate, Node *larg, List *lcolinfo)
+{
+ Node *node;
+ int leftmostRTI;
+ Query *leftmostQuery;
+ List *targetList;
+ ListCell *left_tlist;
+ ListCell *lci;
+ int next_resno;
+
+ /*
+ * Find leftmost leaf SELECT
+ */
+ node = larg;
+ while (node && IsA(node, SetOperationStmt))
+ node = ((SetOperationStmt *) node)->larg;
+ Assert(node && IsA(node, RangeTblRef));
+ leftmostRTI = ((RangeTblRef *) node)->rtindex;
+ leftmostQuery = rt_fetch(leftmostRTI, pstate->p_rtable)->subquery;
+ Assert(leftmostQuery != NULL);
+
+ /*
+ * Generate dummy targetlist using column names of leftmost select
+ * and dummy result expressions of the non-recursive term.
+ */
+ targetList = NIL;
+ left_tlist = list_head(leftmostQuery->targetList);
+ next_resno = 1;
+
+ foreach(lci, lcolinfo)
+ {
+ Expr *lcolexpr = (Expr *) lfirst(lci);
+ TargetEntry *lefttle = (TargetEntry *) lfirst(left_tlist);
+ char *colName;
+ TargetEntry *tle;
+
+ Assert(!lefttle->resjunk);
+ colName = pstrdup(lefttle->resname);
+ tle = makeTargetEntry(lcolexpr,
+ next_resno++,
+ colName,
+ false);
+ targetList = lappend(targetList, tle);
+ left_tlist = lnext(left_tlist);
+ }
+
+ /* Now build CTE's output column info using dummy targetlist */
+ analyzeCTETargetList(pstate, pstate->p_parent_cte, targetList);
+}
+
/*
* Attach column names from a ColumnDef list to a TargetEntry list
* (for CREATE TABLE AS)
/*
* Analyze and transform the subquery.
*/
- query = parse_sub_analyze(r->subquery, pstate);
+ query = parse_sub_analyze(r->subquery, pstate, NULL);
/*
* Check that we got something reasonable. Many of these conditions are
{
CommonTableExpr *cte; /* One CTE to examine */
int id; /* Its ID number for dependencies */
- Node *non_recursive_term; /* Its nonrecursive part, if
- * identified */
Bitmapset *depends_on; /* CTEs depended on (not including self) */
} CteItem;
static void analyzeCTE(ParseState *pstate, CommonTableExpr *cte);
-static void analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist);
/* Dependency processing functions */
static void makeDependencyGraph(CteState *cstate);
{
CommonTableExpr *cte = cstate.items[i].cte;
- /*
- * If it's recursive, we have to do a throwaway parse analysis of
- * the non-recursive term in order to determine the set of output
- * columns for the recursive CTE.
- */
- if (cte->cterecursive)
- {
- Node *nrt;
- Query *nrq;
-
- if (!cstate.items[i].non_recursive_term)
- elog(ERROR, "could not find non-recursive term for %s",
- cte->ctename);
- /* copy the term to be sure we don't modify original query */
- nrt = copyObject(cstate.items[i].non_recursive_term);
- nrq = parse_sub_analyze(nrt, pstate);
- analyzeCTETargetList(pstate, cte, nrq->targetList);
- }
-
analyzeCTE(pstate, cte);
}
}
/* Analysis not done already */
Assert(IsA(cte->ctequery, SelectStmt));
- query = parse_sub_analyze(cte->ctequery, pstate);
+ query = parse_sub_analyze(cte->ctequery, pstate, cte);
cte->ctequery = (Node *) query;
/*
/*
* Compute derived fields of a CTE, given the transformed output targetlist
+ *
+ * For a nonrecursive CTE, this is called after transforming the CTE's query.
+ * For a recursive CTE, we call it after transforming the non-recursive term,
+ * and pass the targetlist emitted by the non-recursive term only.
+ *
+ * Note: in the recursive case, the passed pstate is actually the one being
+ * used to analyze the CTE's query, so it is one level lower down than in
+ * the nonrecursive case. This doesn't matter since we only use it for
+ * error message context anyway.
*/
-static void
+void
analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
{
int numaliases;
int varattno;
ListCell *tlistitem;
+ /* Not done already ... */
+ Assert(cte->ctecolnames == NIL);
+
/*
* We need to determine column names and types. The alias column names
* override anything coming from the query itself. (Note: the SQL spec
errmsg("FOR UPDATE/SHARE in a recursive query is not implemented"),
parser_errposition(cstate->pstate,
exprLocation((Node *) stmt->lockingClause))));
-
- /*
- * Save non_recursive_term.
- */
- cstate->items[i].non_recursive_term = (Node *) stmt->larg;
}
}
return result;
pstate->p_hasSubLinks = true;
- qtree = parse_sub_analyze(sublink->subselect, pstate);
+ qtree = parse_sub_analyze(sublink->subselect, pstate, NULL);
/*
* Check that we got something reasonable. Many of these conditions are
extern Query *parse_analyze_varparams(Node *parseTree, const char *sourceText,
Oid **paramTypes, int *numParams);
-extern Query *parse_sub_analyze(Node *parseTree, ParseState *parentParseState);
+extern Query *parse_sub_analyze(Node *parseTree, ParseState *parentParseState,
+ CommonTableExpr *parentCTE);
extern Query *transformStmt(ParseState *pstate, Node *parseTree);
extern bool analyze_requires_snapshot(Node *parseTree);
extern List *transformWithClause(ParseState *pstate, WithClause *withClause);
+extern void analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte,
+ List *tlist);
+
#endif /* PARSE_CTE_H */
* p_future_ctes: list of CommonTableExprs (WITH items) that are not yet
* visible due to scope rules. This is used to help improve error messages.
*
+ * p_parent_cte: CommonTableExpr that immediately contains the current query,
+ * if any.
+ *
* p_windowdefs: list of WindowDefs representing WINDOW and OVER clauses.
* We collect these while transforming expressions and then transform them
* afterwards (so that any resjunk tlist items needed for the sort/group
List *p_varnamespace; /* current namespace for columns */
List *p_ctenamespace; /* current namespace for common table exprs */
List *p_future_ctes; /* common table exprs not yet in namespace */
+ CommonTableExpr *p_parent_cte; /* this query's containing CTE */
List *p_windowdefs; /* raw representations of window clauses */
Oid *p_paramtypes; /* OIDs of types for $n parameter symbols */
int p_numparams; /* allocated size of p_paramtypes[] */
-2147483647
(5 rows)
+--
+-- test for nested-recursive-WITH bug
+--
+WITH RECURSIVE t(j) AS (
+ WITH RECURSIVE s(i) AS (
+ VALUES (1)
+ UNION ALL
+ SELECT i+1 FROM s WHERE i < 10
+ )
+ SELECT i FROM s
+ UNION ALL
+ SELECT j+1 FROM t WHERE j < 10
+)
+SELECT * FROM t;
+ j
+----
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 6
+ 7
+ 8
+ 9
+ 10
+ 7
+ 8
+ 9
+ 10
+ 8
+ 9
+ 10
+ 9
+ 10
+ 10
+(55 rows)
+
select ( with cte(foo) as ( values(f1) )
values((select foo from cte)) )
from int4_tbl;
+
+--
+-- test for nested-recursive-WITH bug
+--
+WITH RECURSIVE t(j) AS (
+ WITH RECURSIVE s(i) AS (
+ VALUES (1)
+ UNION ALL
+ SELECT i+1 FROM s WHERE i < 10
+ )
+ SELECT i FROM s
+ UNION ALL
+ SELECT j+1 FROM t WHERE j < 10
+)
+SELECT * FROM t;