#include "parser/parse_node.h"
Go to the source code of this file.
Functions | |
List * | transformWithClause (ParseState *pstate, WithClause *withClause) |
void | analyzeCTETargetList (ParseState *pstate, CommonTableExpr *cte, List *tlist) |
void analyzeCTETargetList | ( | ParseState * | pstate, | |
CommonTableExpr * | cte, | |||
List * | tlist | |||
) |
Definition at line 352 of file parse_cte.c.
References CommonTableExpr::aliascolnames, Assert, copyObject(), CommonTableExpr::ctecolcollations, CommonTableExpr::ctecolnames, CommonTableExpr::ctecoltypes, CommonTableExpr::ctecoltypmods, CommonTableExpr::ctename, CommonTableExpr::cterecursive, ereport, errcode(), errmsg(), ERROR, TargetEntry::expr, exprCollation(), exprType(), exprTypmod(), lappend(), lappend_int(), lappend_oid(), lfirst, list_length(), CommonTableExpr::location, makeString(), NIL, OidIsValid, parser_errposition(), pstrdup(), TargetEntry::resjunk, TargetEntry::resname, TargetEntry::resno, and UNKNOWNOID.
Referenced by analyzeCTE(), and determineRecursiveColTypes().
{ int numaliases; int varattno; ListCell *tlistitem; /* Not done already ... */ Assert(cte->ctecolnames == NIL); /* * We need to determine column names, types, and collations. The alias * column names override anything coming from the query itself. (Note: * the SQL spec says that the alias list must be empty or exactly as long * as the output column set; but we allow it to be shorter for consistency * with Alias handling.) */ cte->ctecolnames = copyObject(cte->aliascolnames); cte->ctecoltypes = cte->ctecoltypmods = cte->ctecolcollations = NIL; numaliases = list_length(cte->aliascolnames); varattno = 0; foreach(tlistitem, tlist) { TargetEntry *te = (TargetEntry *) lfirst(tlistitem); Oid coltype; int32 coltypmod; Oid colcoll; if (te->resjunk) continue; varattno++; Assert(varattno == te->resno); if (varattno > numaliases) { char *attrname; attrname = pstrdup(te->resname); cte->ctecolnames = lappend(cte->ctecolnames, makeString(attrname)); } coltype = exprType((Node *) te->expr); coltypmod = exprTypmod((Node *) te->expr); colcoll = exprCollation((Node *) te->expr); /* * If the CTE is recursive, force the exposed column type of any * "unknown" column to "text". This corresponds to the fact that * SELECT 'foo' UNION SELECT 'bar' will ultimately produce text. We * might see "unknown" as a result of an untyped literal in the * non-recursive term's select list, and if we don't convert to text * then we'll have a mismatch against the UNION result. * * The column might contain 'foo' COLLATE "bar", so don't override * collation if it's already set. */ if (cte->cterecursive && coltype == UNKNOWNOID) { coltype = TEXTOID; coltypmod = -1; /* should be -1 already, but be sure */ if (!OidIsValid(colcoll)) colcoll = DEFAULT_COLLATION_OID; } cte->ctecoltypes = lappend_oid(cte->ctecoltypes, coltype); cte->ctecoltypmods = lappend_int(cte->ctecoltypmods, coltypmod); cte->ctecolcollations = lappend_oid(cte->ctecolcollations, colcoll); } if (varattno < numaliases) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("WITH query \"%s\" has %d columns available but %d columns specified", cte->ctename, varattno, numaliases), parser_errposition(pstate, cte->location))); }
List* transformWithClause | ( | ParseState * | pstate, | |
WithClause * | withClause | |||
) |
Definition at line 105 of file parse_cte.c.
References analyzeCTE(), Assert, checkWellFormedRecursion(), CteItem::cte, CommonTableExpr::ctename, CommonTableExpr::ctequery, CommonTableExpr::cterecursive, CommonTableExpr::cterefcount, WithClause::ctes, ereport, errcode(), errmsg(), ERROR, for_each_cell, i, CteItem::id, IsA, CteState::items, lappend(), lfirst, list_copy(), list_delete_first(), list_length(), lnext, CommonTableExpr::location, makeDependencyGraph(), NIL, CteState::numitems, ParseState::p_ctenamespace, ParseState::p_future_ctes, ParseState::p_hasModifyingCTE, palloc0(), parser_errposition(), CteState::pstate, and WithClause::recursive.
Referenced by transformDeleteStmt(), transformInsertStmt(), transformSelectStmt(), transformSetOperationStmt(), transformUpdateStmt(), and transformValuesClause().
{ ListCell *lc; /* Only one WITH clause per query level */ Assert(pstate->p_ctenamespace == NIL); Assert(pstate->p_future_ctes == NIL); /* * For either type of WITH, there must not be duplicate CTE names in the * list. Check this right away so we needn't worry later. * * Also, tentatively mark each CTE as non-recursive, and initialize its * reference count to zero, and set pstate->p_hasModifyingCTE if needed. */ foreach(lc, withClause->ctes) { CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc); ListCell *rest; for_each_cell(rest, lnext(lc)) { CommonTableExpr *cte2 = (CommonTableExpr *) lfirst(rest); if (strcmp(cte->ctename, cte2->ctename) == 0) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_ALIAS), errmsg("WITH query name \"%s\" specified more than once", cte2->ctename), parser_errposition(pstate, cte2->location))); } cte->cterecursive = false; cte->cterefcount = 0; if (!IsA(cte->ctequery, SelectStmt)) { /* must be a data-modifying statement */ Assert(IsA(cte->ctequery, InsertStmt) || IsA(cte->ctequery, UpdateStmt) || IsA(cte->ctequery, DeleteStmt)); pstate->p_hasModifyingCTE = true; } } if (withClause->recursive) { /* * For WITH RECURSIVE, we rearrange the list elements if needed to * eliminate forward references. First, build a work array and set up * the data structure needed by the tree walkers. */ CteState cstate; int i; cstate.pstate = pstate; cstate.numitems = list_length(withClause->ctes); cstate.items = (CteItem *) palloc0(cstate.numitems * sizeof(CteItem)); i = 0; foreach(lc, withClause->ctes) { cstate.items[i].cte = (CommonTableExpr *) lfirst(lc); cstate.items[i].id = i; i++; } /* * Find all the dependencies and sort the CteItems into a safe * processing order. Also, mark CTEs that contain self-references. */ makeDependencyGraph(&cstate); /* * Check that recursive queries are well-formed. */ checkWellFormedRecursion(&cstate); /* * Set up the ctenamespace for parse analysis. Per spec, all the WITH * items are visible to all others, so stuff them all in before parse * analysis. We build the list in safe processing order so that the * planner can process the queries in sequence. */ for (i = 0; i < cstate.numitems; i++) { CommonTableExpr *cte = cstate.items[i].cte; pstate->p_ctenamespace = lappend(pstate->p_ctenamespace, cte); } /* * Do parse analysis in the order determined by the topological sort. */ for (i = 0; i < cstate.numitems; i++) { CommonTableExpr *cte = cstate.items[i].cte; analyzeCTE(pstate, cte); } } else { /* * For non-recursive WITH, just analyze each CTE in sequence and then * add it to the ctenamespace. This corresponds to the spec's * definition of the scope of each WITH name. However, to allow error * reports to be aware of the possibility of an erroneous reference, * we maintain a list in p_future_ctes of the not-yet-visible CTEs. */ pstate->p_future_ctes = list_copy(withClause->ctes); foreach(lc, withClause->ctes) { CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc); analyzeCTE(pstate, cte); pstate->p_ctenamespace = lappend(pstate->p_ctenamespace, cte); pstate->p_future_ctes = list_delete_first(pstate->p_future_ctes); } } return pstate->p_ctenamespace; }