diff options
Diffstat (limited to 'src/backend/parser/parse_relation.c')
-rw-r--r-- | src/backend/parser/parse_relation.c | 836 |
1 files changed, 474 insertions, 362 deletions
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 0052d21ad62..cd8d75e23d9 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -44,6 +44,7 @@ static void expandRelation(Oid relid, Alias *eref, int location, bool include_dropped, List **colnames, List **colvars); static void expandTupleDesc(TupleDesc tupdesc, Alias *eref, + int count, int offset, int rtindex, int sublevels_up, int location, bool include_dropped, List **colnames, List **colvars); @@ -807,25 +808,20 @@ markVarForSelectPriv(ParseState *pstate, Var *var, RangeTblEntry *rte) /* * buildRelationAliases * Construct the eref column name list for a relation RTE. - * This code is also used for the case of a function RTE returning - * a named composite type or a registered RECORD type. + * This code is also used for function RTEs. * * tupdesc: the physical column information * alias: the user-supplied alias, or NULL if none * eref: the eref Alias to store column names in - * ordinality: true if an ordinality column is to be added * * eref->colnames is filled in. Also, alias->colnames is rebuilt to insert * empty strings for any dropped columns, so that it will be one-to-one with * physical column numbers. * - * If we add an ordinality column, its colname comes from the alias if there - * is one, otherwise we default it. (We don't add it to alias->colnames.) - * * It is an error for there to be more aliases present than required. */ static void -buildRelationAliases(TupleDesc tupdesc, Alias *alias, Alias *eref, bool ordinality) +buildRelationAliases(TupleDesc tupdesc, Alias *alias, Alias *eref) { int maxattrs = tupdesc->natts; ListCell *aliaslc; @@ -877,98 +873,56 @@ buildRelationAliases(TupleDesc tupdesc, Alias *alias, Alias *eref, bool ordinali eref->colnames = lappend(eref->colnames, attrname); } - /* tack on the ordinality column at the end */ - if (ordinality) - { - Value *attrname; - - if (aliaslc) - { - attrname = (Value *) lfirst(aliaslc); - aliaslc = lnext(aliaslc); - alias->colnames = lappend(alias->colnames, attrname); - } - else - { - attrname = makeString(pstrdup("ordinality")); - } - - eref->colnames = lappend(eref->colnames, attrname); - } - /* Too many user-supplied aliases? */ if (aliaslc) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("table \"%s\" has %d columns available but %d columns specified", - eref->aliasname, - maxattrs - numdropped + (ordinality ? 1 : 0), - numaliases))); + eref->aliasname, maxattrs - numdropped, numaliases))); } /* - * buildScalarFunctionAlias - * Construct the eref column name list for a function RTE, + * chooseScalarFunctionAlias + * Select the column alias for a function in a function RTE, * when the function returns a scalar type (not composite or RECORD). * * funcexpr: transformed expression tree for the function call - * funcname: function name (used only for error message) - * alias: the user-supplied alias, or NULL if none - * eref: the eref Alias to store column names in - * ordinality: whether to add an ordinality column - * - * eref->colnames is filled in. + * funcname: function name (as determined by FigureColname) + * alias: the user-supplied alias for the RTE, or NULL if none + * nfuncs: the number of functions appearing in the function RTE * - * The caller must have previously filled in eref->aliasname, which will - * be used as the result column name if no alias is given. - * - * A user-supplied Alias can contain up to two column alias names; one for - * the function result, and one for the ordinality column; it is an error - * to specify more aliases than required. + * Note that the name we choose might be overridden later, if the user-given + * alias includes column alias names. That's of no concern here. */ -static void -buildScalarFunctionAlias(Node *funcexpr, char *funcname, - Alias *alias, Alias *eref, bool ordinality) +static char * +chooseScalarFunctionAlias(Node *funcexpr, char *funcname, + Alias *alias, int nfuncs) { - Assert(eref->colnames == NIL); + char *pname; - /* Use user-specified column alias if there is one. */ - if (alias && alias->colnames != NIL) - { - if (list_length(alias->colnames) > (ordinality ? 2 : 1)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("too many column aliases specified for function %s", - funcname))); - - eref->colnames = copyObject(alias->colnames); - } - else + /* + * If the expression is a simple function call, and the function has a + * single OUT parameter that is named, use the parameter's name. + */ + if (funcexpr && IsA(funcexpr, FuncExpr)) { - char *pname = NULL; - - /* - * If the expression is a simple function call, and the function has a - * single OUT parameter that is named, use the parameter's name. - */ - if (funcexpr && IsA(funcexpr, FuncExpr)) - pname = get_func_result_name(((FuncExpr *) funcexpr)->funcid); - - /* - * Otherwise, use the previously-determined alias name provided by the - * caller (which is not necessarily the function name!) - */ - if (!pname) - pname = eref->aliasname; - - eref->colnames = list_make1(makeString(pname)); + pname = get_func_result_name(((FuncExpr *) funcexpr)->funcid); + if (pname) + return pname; } - /* If we don't have a name for the ordinality column yet, supply a default. */ - if (ordinality && list_length(eref->colnames) < 2) - eref->colnames = lappend(eref->colnames, makeString(pstrdup("ordinality"))); + /* + * If there's just one function in the RTE, and the user gave an RTE alias + * name, use that name. (This makes FROM func() AS foo use "foo" as the + * column name as well as the table alias.) + */ + if (nfuncs == 1 && alias) + return alias->aliasname; - return; + /* + * Otherwise use the function name. + */ + return funcname; } /* @@ -1064,7 +1018,7 @@ addRangeTableEntry(ParseState *pstate, * and/or actual column names. */ rte->eref = makeAlias(refname, NIL); - buildRelationAliases(rel->rd_att, alias, rte->eref, false); + buildRelationAliases(rel->rd_att, alias, rte->eref); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1124,7 +1078,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * and/or actual column names. */ rte->eref = makeAlias(refname, NIL); - buildRelationAliases(rel->rd_att, alias, rte->eref, false); + buildRelationAliases(rel->rd_att, alias, rte->eref); /* * Set flags and access permissions. @@ -1230,122 +1184,233 @@ addRangeTableEntryForSubquery(ParseState *pstate, } /* - * Add an entry for a function to the pstate's range table (p_rtable). + * Add an entry for a function (or functions) to the pstate's range table + * (p_rtable). * * This is just like addRangeTableEntry() except that it makes a function RTE. */ RangeTblEntry * addRangeTableEntryForFunction(ParseState *pstate, - char *funcname, - Node *funcexpr, + List *funcnames, + List *funcexprs, + List *coldeflists, RangeFunction *rangefunc, bool lateral, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); - TypeFuncClass functypclass; - Oid funcrettype; - TupleDesc tupdesc; Alias *alias = rangefunc->alias; - List *coldeflist = rangefunc->coldeflist; Alias *eref; + char *aliasname; + int nfuncs = list_length(funcexprs); + TupleDesc *functupdescs; + TupleDesc tupdesc; + ListCell *lc1, + *lc2, + *lc3; + int i; + int j; + int funcno; + int natts, + totalatts; rte->rtekind = RTE_FUNCTION; rte->relid = InvalidOid; rte->subquery = NULL; - rte->funcexpr = funcexpr; - rte->funccoltypes = NIL; - rte->funccoltypmods = NIL; - rte->funccolcollations = NIL; + rte->functions = NIL; /* we'll fill this list below */ + rte->funcordinality = rangefunc->ordinality; rte->alias = alias; - eref = makeAlias(alias ? alias->aliasname : funcname, NIL); - rte->eref = eref; - - /* - * Now determine if the function returns a simple or composite type. - */ - functypclass = get_expr_result_type(funcexpr, - &funcrettype, - &tupdesc); - /* - * A coldeflist is required if the function returns RECORD and hasn't got - * a predetermined record type, and is prohibited otherwise. + * Choose the RTE alias name. We default to using the first function's + * name even when there's more than one; which is maybe arguable but beats + * using something constant like "table". */ - if (coldeflist != NIL) - { - if (functypclass != TYPEFUNC_RECORD) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("a column definition list is only allowed for functions returning \"record\""), - parser_errposition(pstate, exprLocation(funcexpr)))); - } + if (alias) + aliasname = alias->aliasname; else + aliasname = linitial(funcnames); + + eref = makeAlias(aliasname, NIL); + rte->eref = eref; + + /* Process each function ... */ + functupdescs = (TupleDesc *) palloc(nfuncs * sizeof(TupleDesc)); + + totalatts = 0; + funcno = 0; + forthree(lc1, funcexprs, lc2, funcnames, lc3, coldeflists) { - if (functypclass == TYPEFUNC_RECORD) + Node *funcexpr = (Node *) lfirst(lc1); + char *funcname = (char *) lfirst(lc2); + List *coldeflist = (List *) lfirst(lc3); + RangeTblFunction *rtfunc = makeNode(RangeTblFunction); + TypeFuncClass functypclass; + Oid funcrettype; + + /* Initialize RangeTblFunction node */ + rtfunc->funcexpr = funcexpr; + rtfunc->funccolnames = NIL; + rtfunc->funccoltypes = NIL; + rtfunc->funccoltypmods = NIL; + rtfunc->funccolcollations = NIL; + rtfunc->funcparams = NULL; /* not set until planning */ + + /* + * Now determine if the function returns a simple or composite type. + */ + functypclass = get_expr_result_type(funcexpr, + &funcrettype, + &tupdesc); + + /* + * A coldeflist is required if the function returns RECORD and hasn't + * got a predetermined record type, and is prohibited otherwise. + */ + if (coldeflist != NIL) + { + if (functypclass != TYPEFUNC_RECORD) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("a column definition list is only allowed for functions returning \"record\""), + parser_errposition(pstate, + exprLocation((Node *) coldeflist)))); + } + else + { + if (functypclass == TYPEFUNC_RECORD) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("a column definition list is required for functions returning \"record\""), + parser_errposition(pstate, exprLocation(funcexpr)))); + } + + if (functypclass == TYPEFUNC_COMPOSITE) + { + /* Composite data type, e.g. a table's row type */ + Assert(tupdesc); + } + else if (functypclass == TYPEFUNC_SCALAR) + { + /* Base data type, i.e. scalar */ + tupdesc = CreateTemplateTupleDesc(1, false); + TupleDescInitEntry(tupdesc, + (AttrNumber) 1, + chooseScalarFunctionAlias(funcexpr, funcname, + alias, nfuncs), + funcrettype, + -1, + 0); + } + else if (functypclass == TYPEFUNC_RECORD) + { + ListCell *col; + + /* + * Use the column definition list to construct a tupdesc and fill + * in the RangeTblFunction's lists. + */ + tupdesc = CreateTemplateTupleDesc(list_length(coldeflist), false); + i = 1; + foreach(col, coldeflist) + { + ColumnDef *n = (ColumnDef *) lfirst(col); + char *attrname; + Oid attrtype; + int32 attrtypmod; + Oid attrcollation; + + attrname = n->colname; + if (n->typeName->setof) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("column \"%s\" cannot be declared SETOF", + attrname), + parser_errposition(pstate, n->location))); + typenameTypeIdAndMod(pstate, n->typeName, + &attrtype, &attrtypmod); + attrcollation = GetColumnDefCollation(pstate, n, attrtype); + TupleDescInitEntry(tupdesc, + (AttrNumber) i, + attrname, + attrtype, + attrtypmod, + 0); + TupleDescInitEntryCollation(tupdesc, + (AttrNumber) i, + attrcollation); + rtfunc->funccolnames = lappend(rtfunc->funccolnames, + makeString(pstrdup(attrname))); + rtfunc->funccoltypes = lappend_oid(rtfunc->funccoltypes, + attrtype); + rtfunc->funccoltypmods = lappend_int(rtfunc->funccoltypmods, + attrtypmod); + rtfunc->funccolcollations = lappend_oid(rtfunc->funccolcollations, + attrcollation); + + i++; + } + + /* + * Ensure that the coldeflist defines a legal set of names (no + * duplicates) and datatypes (no pseudo-types, for instance). + */ + CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE, false); + } + else ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("a column definition list is required for functions returning \"record\""), + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("function \"%s\" in FROM has unsupported return type %s", + funcname, format_type_be(funcrettype)), parser_errposition(pstate, exprLocation(funcexpr)))); - } - if (functypclass == TYPEFUNC_COMPOSITE) - { - /* Composite data type, e.g. a table's row type */ - Assert(tupdesc); - /* Build the column alias list */ - buildRelationAliases(tupdesc, alias, eref, rangefunc->ordinality); - } - else if (functypclass == TYPEFUNC_SCALAR) - { - /* Base data type, i.e. scalar */ - buildScalarFunctionAlias(funcexpr, funcname, alias, eref, rangefunc->ordinality); + /* Finish off the RangeTblFunction and add it to the RTE's list */ + rtfunc->funccolcount = tupdesc->natts; + rte->functions = lappend(rte->functions, rtfunc); + + /* Save the tupdesc for use below */ + functupdescs[funcno] = tupdesc; + totalatts += tupdesc->natts; + funcno++; } - else if (functypclass == TYPEFUNC_RECORD) - { - ListCell *col; + /* + * If there's more than one function, or we want an ordinality column, we + * have to produce a merged tupdesc. + */ + if (nfuncs > 1 || rangefunc->ordinality) + { if (rangefunc->ordinality) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("WITH ORDINALITY is not supported for functions returning \"record\""), - parser_errposition(pstate, exprLocation(funcexpr)))); + totalatts++; - /* - * Use the column definition list to form the alias list and - * funccoltypes/funccoltypmods/funccolcollations lists. - */ - foreach(col, coldeflist) + /* Merge the tuple descs of each function into a composite one */ + tupdesc = CreateTemplateTupleDesc(totalatts, false); + natts = 0; + for (i = 0; i < nfuncs; i++) { - ColumnDef *n = (ColumnDef *) lfirst(col); - char *attrname; - Oid attrtype; - int32 attrtypmod; - Oid attrcollation; - - attrname = pstrdup(n->colname); - if (n->typeName->setof) - ereport(ERROR, - (errcode(ERRCODE_INVALID_TABLE_DEFINITION), - errmsg("column \"%s\" cannot be declared SETOF", - attrname), - parser_errposition(pstate, n->typeName->location))); - typenameTypeIdAndMod(pstate, n->typeName, &attrtype, &attrtypmod); - attrcollation = GetColumnDefCollation(pstate, n, attrtype); - eref->colnames = lappend(eref->colnames, makeString(attrname)); - rte->funccoltypes = lappend_oid(rte->funccoltypes, attrtype); - rte->funccoltypmods = lappend_int(rte->funccoltypmods, attrtypmod); - rte->funccolcollations = lappend_oid(rte->funccolcollations, - attrcollation); + for (j = 1; j <= functupdescs[i]->natts; j++) + TupleDescCopyEntry(tupdesc, ++natts, functupdescs[i], j); } + + /* Add the ordinality column if needed */ + if (rangefunc->ordinality) + TupleDescInitEntry(tupdesc, + (AttrNumber) ++natts, + "ordinality", + INT8OID, + -1, + 0); + + Assert(natts == totalatts); } else - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("function \"%s\" in FROM has unsupported return type %s", - funcname, format_type_be(funcrettype)), - parser_errposition(pstate, exprLocation(funcexpr)))); + { + /* We can just use the single function's tupdesc as-is */ + tupdesc = functupdescs[0]; + } + + /* Use the tupdesc while assigning column aliases for the RTE */ + buildRelationAliases(tupdesc, alias, eref); /* * Set flags and access permissions. @@ -1354,7 +1419,6 @@ addRangeTableEntryForFunction(ParseState *pstate, * permissions mechanism). */ rte->lateral = lateral; - rte->funcordinality = rangefunc->ordinality; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; @@ -1710,11 +1774,6 @@ addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, * The output lists go into *colnames and *colvars. * If only one of the two kinds of output list is needed, pass NULL for the * output pointer for the unwanted one. - * - * For function RTEs with ORDINALITY, this expansion includes the - * ordinal column, whose type (bigint) had better match the type assumed in the - * executor. The colname for the ordinality column must have been set up already - * in the RTE; it is always last. */ void expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, @@ -1780,107 +1839,115 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, case RTE_FUNCTION: { /* Function RTE */ - TypeFuncClass functypclass; - Oid funcrettype; - TupleDesc tupdesc; - int ordinality_attno = 0; - - functypclass = get_expr_result_type(rte->funcexpr, - &funcrettype, - &tupdesc); - if (functypclass == TYPEFUNC_COMPOSITE) - { - /* Composite data type, e.g. a table's row type */ - Assert(tupdesc); + int atts_done = 0; + ListCell *lc; - /* - * we rely here on the fact that expandTupleDesc doesn't - * care about being passed more aliases than it needs. - */ - expandTupleDesc(tupdesc, rte->eref, - rtindex, sublevels_up, location, - include_dropped, colnames, colvars); - - ordinality_attno = tupdesc->natts + 1; - } - else if (functypclass == TYPEFUNC_SCALAR) + foreach(lc, rte->functions) { - /* Base data type, i.e. scalar */ - if (colnames) - *colnames = lappend(*colnames, - linitial(rte->eref->colnames)); - - if (colvars) + RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc); + TypeFuncClass functypclass; + Oid funcrettype; + TupleDesc tupdesc; + + functypclass = get_expr_result_type(rtfunc->funcexpr, + &funcrettype, + &tupdesc); + if (functypclass == TYPEFUNC_COMPOSITE) { - Var *varnode; - - varnode = makeVar(rtindex, 1, - funcrettype, -1, - exprCollation(rte->funcexpr), - sublevels_up); - varnode->location = location; - - *colvars = lappend(*colvars, varnode); + /* Composite data type, e.g. a table's row type */ + Assert(tupdesc); + expandTupleDesc(tupdesc, rte->eref, + rtfunc->funccolcount, atts_done, + rtindex, sublevels_up, location, + include_dropped, colnames, colvars); } - - ordinality_attno = 2; - } - else if (functypclass == TYPEFUNC_RECORD) - { - if (colnames) - *colnames = copyObject(rte->eref->colnames); - if (colvars) + else if (functypclass == TYPEFUNC_SCALAR) { - ListCell *l1; - ListCell *l2; - ListCell *l3; - int attnum = 0; - - forthree(l1, rte->funccoltypes, - l2, rte->funccoltypmods, - l3, rte->funccolcollations) + /* Base data type, i.e. scalar */ + if (colnames) + *colnames = lappend(*colnames, + list_nth(rte->eref->colnames, + atts_done)); + + if (colvars) { - Oid attrtype = lfirst_oid(l1); - int32 attrtypmod = lfirst_int(l2); - Oid attrcollation = lfirst_oid(l3); Var *varnode; - attnum++; - varnode = makeVar(rtindex, - attnum, - attrtype, - attrtypmod, - attrcollation, + varnode = makeVar(rtindex, atts_done + 1, + funcrettype, -1, + exprCollation(rtfunc->funcexpr), sublevels_up); varnode->location = location; + *colvars = lappend(*colvars, varnode); } } + else if (functypclass == TYPEFUNC_RECORD) + { + if (colnames) + { + List *namelist; + + /* extract appropriate subset of column list */ + namelist = list_copy_tail(rte->eref->colnames, + atts_done); + namelist = list_truncate(namelist, + rtfunc->funccolcount); + *colnames = list_concat(*colnames, namelist); + } - /* note, ordinality is not allowed in this case */ - } - else - { - /* addRangeTableEntryForFunction should've caught this */ - elog(ERROR, "function in FROM has unsupported return type"); + if (colvars) + { + ListCell *l1; + ListCell *l2; + ListCell *l3; + int attnum = atts_done; + + forthree(l1, rtfunc->funccoltypes, + l2, rtfunc->funccoltypmods, + l3, rtfunc->funccolcollations) + { + Oid attrtype = lfirst_oid(l1); + int32 attrtypmod = lfirst_int(l2); + Oid attrcollation = lfirst_oid(l3); + Var *varnode; + + attnum++; + varnode = makeVar(rtindex, + attnum, + attrtype, + attrtypmod, + attrcollation, + sublevels_up); + varnode->location = location; + *colvars = lappend(*colvars, varnode); + } + } + } + else + { + /* addRangeTableEntryForFunction should've caught this */ + elog(ERROR, "function in FROM has unsupported return type"); + } + atts_done += rtfunc->funccolcount; } - /* tack on the extra ordinality column if present */ + /* Append the ordinality column if any */ if (rte->funcordinality) { - Assert(ordinality_attno > 0); - if (colnames) - *colnames = lappend(*colnames, llast(rte->eref->colnames)); + *colnames = lappend(*colnames, + llast(rte->eref->colnames)); if (colvars) { - Var *varnode = makeVar(rtindex, - ordinality_attno, - INT8OID, - -1, - InvalidOid, - sublevels_up); + Var *varnode = makeVar(rtindex, + atts_done + 1, + INT8OID, + -1, + InvalidOid, + sublevels_up); + *colvars = lappend(*colvars, varnode); } } @@ -2051,7 +2118,8 @@ expandRelation(Oid relid, Alias *eref, int rtindex, int sublevels_up, /* Get the tupledesc and turn it over to expandTupleDesc */ rel = relation_open(relid, AccessShareLock); - expandTupleDesc(rel->rd_att, eref, rtindex, sublevels_up, + expandTupleDesc(rel->rd_att, eref, rel->rd_att->natts, 0, + rtindex, sublevels_up, location, include_dropped, colnames, colvars); relation_close(rel, AccessShareLock); @@ -2060,20 +2128,34 @@ expandRelation(Oid relid, Alias *eref, int rtindex, int sublevels_up, /* * expandTupleDesc -- expandRTE subroutine * - * Only the required number of column names are used from the Alias; - * it is not an error to supply too many. (ordinality depends on this) + * Generate names and/or Vars for the first "count" attributes of the tupdesc, + * and append them to colnames/colvars. "offset" is added to the varattno + * that each Var would otherwise have, and we also skip the first "offset" + * entries in eref->colnames. (These provisions allow use of this code for + * an individual composite-returning function in an RTE_FUNCTION RTE.) */ static void -expandTupleDesc(TupleDesc tupdesc, Alias *eref, +expandTupleDesc(TupleDesc tupdesc, Alias *eref, int count, int offset, int rtindex, int sublevels_up, int location, bool include_dropped, List **colnames, List **colvars) { - int maxattrs = tupdesc->natts; - int numaliases = list_length(eref->colnames); + ListCell *aliascell = list_head(eref->colnames); int varattno; - for (varattno = 0; varattno < maxattrs; varattno++) + if (colnames) + { + int i; + + for (i = 0; i < offset; i++) + { + if (aliascell) + aliascell = lnext(aliascell); + } + } + + Assert(count <= tupdesc->natts); + for (varattno = 0; varattno < count; varattno++) { Form_pg_attribute attr = tupdesc->attrs[varattno]; @@ -2093,6 +2175,8 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, makeNullConst(INT4OID, -1, InvalidOid)); } } + if (aliascell) + aliascell = lnext(aliascell); continue; } @@ -2100,10 +2184,16 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, { char *label; - if (varattno < numaliases) - label = strVal(list_nth(eref->colnames, varattno)); + if (aliascell) + { + label = strVal(lfirst(aliascell)); + aliascell = lnext(aliascell); + } else + { + /* If we run out of aliases, use the underlying name */ label = NameStr(attr->attname); + } *colnames = lappend(*colnames, makeString(pstrdup(label))); } @@ -2111,7 +2201,7 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, { Var *varnode; - varnode = makeVar(rtindex, attr->attnum, + varnode = makeVar(rtindex, varattno + offset + 1, attr->atttypid, attr->atttypmod, attr->attcollation, sublevels_up); @@ -2221,9 +2311,6 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum) /* * get_rte_attribute_type * Get attribute type/typmod/collation information from a RangeTblEntry - * - * Once again, for function RTEs we may have to synthesize the - * ordinality column with the correct type. */ void get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, @@ -2278,79 +2365,93 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, case RTE_FUNCTION: { /* Function RTE */ - TypeFuncClass functypclass; - Oid funcrettype; - TupleDesc tupdesc; - - /* - * if ordinality, then a reference to the last column - * in the name list must be referring to the - * ordinality column - */ - if (rte->funcordinality - && attnum == list_length(rte->eref->colnames)) - { - *vartype = INT8OID; - *vartypmod = -1; - *varcollid = InvalidOid; - break; - } - - functypclass = get_expr_result_type(rte->funcexpr, - &funcrettype, - &tupdesc); + ListCell *lc; + int atts_done = 0; - if (functypclass == TYPEFUNC_COMPOSITE) + /* Identify which function covers the requested column */ + foreach(lc, rte->functions) { - /* Composite data type, e.g. a table's row type */ - Form_pg_attribute att_tup; + RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc); - Assert(tupdesc); - - /* this is probably a can't-happen case */ - if (attnum < 1 || attnum > tupdesc->natts) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column %d of relation \"%s\" does not exist", - attnum, - rte->eref->aliasname))); + if (attnum > atts_done && + attnum <= atts_done + rtfunc->funccolcount) + { + TypeFuncClass functypclass; + Oid funcrettype; + TupleDesc tupdesc; - att_tup = tupdesc->attrs[attnum - 1]; + attnum -= atts_done; /* now relative to this func */ + functypclass = get_expr_result_type(rtfunc->funcexpr, + &funcrettype, + &tupdesc); - /* - * If dropped column, pretend it ain't there. See notes - * in scanRTEForColumn. - */ - if (att_tup->attisdropped) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column \"%s\" of relation \"%s\" does not exist", - NameStr(att_tup->attname), - rte->eref->aliasname))); - *vartype = att_tup->atttypid; - *vartypmod = att_tup->atttypmod; - *varcollid = att_tup->attcollation; + if (functypclass == TYPEFUNC_COMPOSITE) + { + /* Composite data type, e.g. a table's row type */ + Form_pg_attribute att_tup; + + Assert(tupdesc); + Assert(attnum <= tupdesc->natts); + att_tup = tupdesc->attrs[attnum - 1]; + + /* + * If dropped column, pretend it ain't there. See + * notes in scanRTEForColumn. + */ + if (att_tup->attisdropped) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + NameStr(att_tup->attname), + rte->eref->aliasname))); + *vartype = att_tup->atttypid; + *vartypmod = att_tup->atttypmod; + *varcollid = att_tup->attcollation; + } + else if (functypclass == TYPEFUNC_SCALAR) + { + /* Base data type, i.e. scalar */ + *vartype = funcrettype; + *vartypmod = -1; + *varcollid = exprCollation(rtfunc->funcexpr); + } + else if (functypclass == TYPEFUNC_RECORD) + { + *vartype = list_nth_oid(rtfunc->funccoltypes, + attnum - 1); + *vartypmod = list_nth_int(rtfunc->funccoltypmods, + attnum - 1); + *varcollid = list_nth_oid(rtfunc->funccolcollations, + attnum - 1); + } + else + { + /* + * addRangeTableEntryForFunction should've caught + * this + */ + elog(ERROR, "function in FROM has unsupported return type"); + } + return; + } + atts_done += rtfunc->funccolcount; } - else if (functypclass == TYPEFUNC_SCALAR) - { - Assert(attnum == 1); - /* Base data type, i.e. scalar */ - *vartype = funcrettype; - *vartypmod = -1; - *varcollid = exprCollation(rte->funcexpr); - } - else if (functypclass == TYPEFUNC_RECORD) + /* If we get here, must be looking for the ordinality column */ + if (rte->funcordinality && attnum == atts_done + 1) { - *vartype = list_nth_oid(rte->funccoltypes, attnum - 1); - *vartypmod = list_nth_int(rte->funccoltypmods, attnum - 1); - *varcollid = list_nth_oid(rte->funccolcollations, attnum - 1); - } - else - { - /* addRangeTableEntryForFunction should've caught this */ - elog(ERROR, "function in FROM has unsupported return type"); + *vartype = INT8OID; + *vartypmod = -1; + *varcollid = InvalidOid; + return; } + + /* this probably can't happen ... */ + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column %d of relation \"%s\" does not exist", + attnum, + rte->eref->aliasname))); } break; case RTE_VALUES: @@ -2456,46 +2557,57 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum) case RTE_FUNCTION: { /* Function RTE */ - Oid funcrettype = exprType(rte->funcexpr); - Oid funcrelid = typeidTypeRelid(funcrettype); + ListCell *lc; + int atts_done = 0; /* - * if ordinality, then a reference to the last column - * in the name list must be referring to the - * ordinality column, which is not dropped + * Dropped attributes are only possible with functions that + * return named composite types. In such a case we have to + * look up the result type to see if it currently has this + * column dropped. So first, loop over the funcs until we + * find the one that covers the requested column. */ - if (rte->funcordinality - && attnum == list_length(rte->eref->colnames)) + foreach(lc, rte->functions) { - result = false; - } - else if (OidIsValid(funcrelid)) - { - /* - * Composite data type, i.e. a table's row type - * - * Same as ordinary relation RTE - */ - HeapTuple tp; - Form_pg_attribute att_tup; - - tp = SearchSysCache2(ATTNUM, - ObjectIdGetDatum(funcrelid), - Int16GetDatum(attnum)); - if (!HeapTupleIsValid(tp)) /* shouldn't happen */ - elog(ERROR, "cache lookup failed for attribute %d of relation %u", - attnum, funcrelid); - att_tup = (Form_pg_attribute) GETSTRUCT(tp); - result = att_tup->attisdropped; - ReleaseSysCache(tp); - } - else - { - /* - * Must be a base data type, i.e. scalar - */ - result = false; + RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc); + + if (attnum > atts_done && + attnum <= atts_done + rtfunc->funccolcount) + { + TypeFuncClass functypclass; + Oid funcrettype; + TupleDesc tupdesc; + + functypclass = get_expr_result_type(rtfunc->funcexpr, + &funcrettype, + &tupdesc); + if (functypclass == TYPEFUNC_COMPOSITE) + { + /* Composite data type, e.g. a table's row type */ + Form_pg_attribute att_tup; + + Assert(tupdesc); + Assert(attnum - atts_done <= tupdesc->natts); + att_tup = tupdesc->attrs[attnum - atts_done - 1]; + return att_tup->attisdropped; + } + /* Otherwise, it can't have any dropped columns */ + return false; + } + atts_done += rtfunc->funccolcount; } + + /* If we get here, must be looking for the ordinality column */ + if (rte->funcordinality && attnum == atts_done + 1) + return false; + + /* this probably can't happen ... */ + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column %d of relation \"%s\" does not exist", + attnum, + rte->eref->aliasname))); + result = false; /* keep compiler quiet */ } break; default: |