diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2013-11-21 19:37:02 -0500 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2013-11-21 19:37:20 -0500 |
commit | 784e762e886e6f72f548da86a27cd2ead87dbd1c (patch) | |
tree | 9c21fc1545c96a655ec4591e1ba3c8d99cdfccf8 /src/backend/parser/parse_relation.c | |
parent | 38f432898131270e5b64245786cb67f322538bae (diff) | |
download | postgresql-784e762e886e6f72f548da86a27cd2ead87dbd1c.tar.gz postgresql-784e762e886e6f72f548da86a27cd2ead87dbd1c.zip |
Support multi-argument UNNEST(), and TABLE() syntax for multiple functions.
This patch adds the ability to write TABLE( function1(), function2(), ...)
as a single FROM-clause entry. The result is the concatenation of the
first row from each function, followed by the second row from each
function, etc; with NULLs inserted if any function produces fewer rows than
others. This is believed to be a much more useful behavior than what
Postgres currently does with multiple SRFs in a SELECT list.
This syntax also provides a reasonable way to combine use of column
definition lists with WITH ORDINALITY: put the column definition list
inside TABLE(), where it's clear that it doesn't control the ordinality
column as well.
Also implement SQL-compliant multiple-argument UNNEST(), by turning
UNNEST(a,b,c) into TABLE(unnest(a), unnest(b), unnest(c)).
The SQL standard specifies TABLE() with only a single function, not
multiple functions, and it seems to require an implicit UNNEST() which is
not what this patch does. There may be something wrong with that reading
of the spec, though, because if it's right then the spec's TABLE() is just
a pointless alternative spelling of UNNEST(). After further review of
that, we might choose to adopt a different syntax for what this patch does,
but in any case this functionality seems clearly worthwhile.
Andrew Gierth, reviewed by Zoltán Böszörményi and Heikki Linnakangas, and
significantly revised by me
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: |