1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-18 17:42:25 +03:00

Fix bug with WITH RECURSIVE immediately inside WITH RECURSIVE. 99% of the

code was already okay with this, but the hack that obtained the output
column types of a recursive union in advance of doing real parse analysis
of the recursive union forgot to handle the case where there was an inner
WITH clause available to the non-recursive term.  Best fix seems to be to
refactor so that we don't need the "throwaway" parse analysis step at all.
Instead, teach the transformSetOperationStmt code to set up the CTE's output
column information after it's processed the non-recursive term normally.
Per report from David Fetter.
This commit is contained in:
Tom Lane
2009-09-09 03:32:52 +00:00
parent d69a419e68
commit 255f66efa9
9 changed files with 203 additions and 44 deletions

View File

@ -17,7 +17,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.390 2009/08/27 20:08:02 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.391 2009/09/09 03:32:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -50,7 +50,9 @@ static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
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);
@ -135,11 +137,14 @@ parse_analyze_varparams(Node *parseTree, const char *sourceText,
* 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);
@ -1199,6 +1204,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
* Recursively transform the components of the tree.
*/
sostmt = (SetOperationStmt *) transformSetOperationTree(pstate, stmt,
true,
&socolinfo);
Assert(sostmt && IsA(sostmt, SetOperationStmt));
qry->setOperations = (Node *) sostmt;
@ -1359,7 +1365,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
*/
static Node *
transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
List **colInfo)
bool isTopLevel, List **colInfo)
{
bool isLeaf;
@ -1418,7 +1424,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
* 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
@ -1485,11 +1491,28 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
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);
/*
@ -1584,6 +1607,61 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
}
}
/*
* 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)