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 | |
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')
-rw-r--r-- | src/backend/parser/gram.y | 98 | ||||
-rw-r--r-- | src/backend/parser/parse_clause.c | 192 | ||||
-rw-r--r-- | src/backend/parser/parse_relation.c | 836 | ||||
-rw-r--r-- | src/backend/parser/parse_type.c | 2 | ||||
-rw-r--r-- | src/backend/parser/parse_utilcmd.c | 2 |
5 files changed, 697 insertions, 433 deletions
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 11f629118b0..19220971da6 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -406,6 +406,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); a_expr b_expr c_expr AexprConst indirection_el columnref in_expr having_clause func_table array_expr ExclusionWhereClause +%type <list> func_table_item func_table_list opt_col_def_list +%type <boolean> opt_ordinality %type <list> ExclusionConstraintList ExclusionConstraintElem %type <list> func_arg_list %type <node> func_arg_expr @@ -613,6 +615,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); */ %token NULLS_FIRST NULLS_LAST WITH_ORDINALITY WITH_TIME + /* Precedence: lowest to highest */ %nonassoc SET /* see relation_expr_opt_alias */ %left UNION EXCEPT @@ -1926,10 +1929,11 @@ alter_table_cmd: n->subtype = AT_AlterColumnType; n->name = $3; n->def = (Node *) def; - /* We only use these three fields of the ColumnDef node */ + /* We only use these fields of the ColumnDef node */ def->typeName = $6; def->collClause = (CollateClause *) $7; def->raw_default = $8; + def->location = @3; $$ = (Node *)n; } /* ALTER FOREIGN TABLE <name> ALTER [COLUMN] <colname> OPTIONS */ @@ -2354,10 +2358,11 @@ alter_type_cmd: n->name = $3; n->def = (Node *) def; n->behavior = $8; - /* We only use these three fields of the ColumnDef node */ + /* We only use these fields of the ColumnDef node */ def->typeName = $6; def->collClause = (CollateClause *) $7; def->raw_default = NULL; + def->location = @3; $$ = (Node *)n; } ; @@ -2782,6 +2787,7 @@ columnDef: ColId Typename create_generic_options ColQualList n->fdwoptions = $3; SplitColQualList($4, &n->constraints, &n->collClause, yyscanner); + n->location = @1; $$ = (Node *)n; } ; @@ -2801,6 +2807,7 @@ columnOptions: ColId WITH OPTIONS ColQualList n->collOid = InvalidOid; SplitColQualList($4, &n->constraints, &n->collClause, yyscanner); + n->location = @1; $$ = (Node *)n; } ; @@ -9648,44 +9655,19 @@ table_ref: relation_expr opt_alias_clause } | func_table func_alias_clause { - RangeFunction *n = makeNode(RangeFunction); - n->lateral = false; - n->ordinality = false; - n->funccallnode = $1; + RangeFunction *n = (RangeFunction *) $1; n->alias = linitial($2); n->coldeflist = lsecond($2); $$ = (Node *) n; } - | func_table WITH_ORDINALITY func_alias_clause - { - RangeFunction *n = makeNode(RangeFunction); - n->lateral = false; - n->ordinality = true; - n->funccallnode = $1; - n->alias = linitial($3); - n->coldeflist = lsecond($3); - $$ = (Node *) n; - } | LATERAL_P func_table func_alias_clause { - RangeFunction *n = makeNode(RangeFunction); + RangeFunction *n = (RangeFunction *) $2; n->lateral = true; - n->ordinality = false; - n->funccallnode = $2; n->alias = linitial($3); n->coldeflist = lsecond($3); $$ = (Node *) n; } - | LATERAL_P func_table WITH_ORDINALITY func_alias_clause - { - RangeFunction *n = makeNode(RangeFunction); - n->lateral = true; - n->ordinality = true; - n->funccallnode = $2; - n->alias = linitial($4); - n->coldeflist = lsecond($4); - $$ = (Node *) n; - } | select_with_parens opt_alias_clause { RangeSubselect *n = makeNode(RangeSubselect); @@ -9996,7 +9978,54 @@ relation_expr_opt_alias: relation_expr %prec UMINUS } ; -func_table: func_expr_windowless { $$ = $1; } +/* + * func_table represents a function invocation in a FROM list. It can be + * a plain function call, like "foo(...)", or a TABLE expression with + * one or more function calls, "TABLE (foo(...), bar(...))", + * optionally with WITH ORDINALITY attached. + * In the TABLE syntax, a column definition list can be given for each + * function, for example: + * TABLE (foo() AS (foo_res_a text, foo_res_b text), + * bar() AS (bar_res_a text, bar_res_b text)) + * It's also possible to attach a column definition list to the RangeFunction + * as a whole, but that's handled by the table_ref production. + */ +func_table: func_expr_windowless opt_ordinality + { + RangeFunction *n = makeNode(RangeFunction); + n->lateral = false; + n->ordinality = $2; + n->is_table = false; + n->functions = list_make1(list_make2($1, NIL)); + /* alias and coldeflist are set by table_ref production */ + $$ = (Node *) n; + } + | TABLE '(' func_table_list ')' opt_ordinality + { + RangeFunction *n = makeNode(RangeFunction); + n->lateral = false; + n->ordinality = $5; + n->is_table = true; + n->functions = $3; + /* alias and coldeflist are set by table_ref production */ + $$ = (Node *) n; + } + ; + +func_table_item: func_expr_windowless opt_col_def_list + { $$ = list_make2($1, $2); } + ; + +func_table_list: func_table_item { $$ = list_make1($1); } + | func_table_list ',' func_table_item { $$ = lappend($1, $3); } + ; + +opt_col_def_list: AS '(' TableFuncElementList ')' { $$ = $3; } + | /*EMPTY*/ { $$ = NIL; } + ; + +opt_ordinality: WITH_ORDINALITY { $$ = true; } + | /*EMPTY*/ { $$ = false; } ; @@ -10051,6 +10080,7 @@ TableFuncElement: ColId Typename opt_collate_clause n->collClause = (CollateClause *) $3; n->collOid = InvalidOid; n->constraints = NIL; + n->location = @1; $$ = (Node *)n; } ; @@ -11172,11 +11202,11 @@ func_application: func_name '(' ')' /* - * func_expr and its cousin func_expr_windowless is split out from c_expr just + * func_expr and its cousin func_expr_windowless are split out from c_expr just * so that we have classifications for "everything that is a function call or - * looks like one". This isn't very important, but it saves us having to document - * which variants are legal in the backwards-compatible functional-index syntax - * for CREATE INDEX. + * looks like one". This isn't very important, but it saves us having to + * document which variants are legal in places like "FROM function()" or the + * backwards-compatible functional-index syntax for CREATE INDEX. * (Note that many of the special SQL functions wouldn't actually make any * sense as functional index entries, but we ignore that consideration here.) */ diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 7a1261d0fd2..8b4c0ae0d3b 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -24,6 +24,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parsetree.h" +#include "parser/parser.h" #include "parser/parse_clause.h" #include "parser/parse_coerce.h" #include "parser/parse_collate.h" @@ -515,24 +516,18 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r) static RangeTblEntry * transformRangeFunction(ParseState *pstate, RangeFunction *r) { - Node *funcexpr; - char *funcname; + List *funcexprs = NIL; + List *funcnames = NIL; + List *coldeflists = NIL; bool is_lateral; RangeTblEntry *rte; - - /* - * Get function name for possible use as alias. We use the same - * transformation rules as for a SELECT output expression. For a FuncCall - * node, the result will be the function name, but it is possible for the - * grammar to hand back other node types. - */ - funcname = FigureColname(r->funccallnode); + ListCell *lc; /* * We make lateral_only names of this level visible, whether or not the - * function is explicitly marked LATERAL. This is needed for SQL spec - * compliance in the case of UNNEST(), and seems useful on convenience - * grounds for all functions in FROM. + * RangeFunction is explicitly marked LATERAL. This is needed for SQL + * spec compliance in the case of UNNEST(), and seems useful on + * convenience grounds for all functions in FROM. * * (LATERAL can't nest within a single pstate level, so we don't need * save/restore logic here.) @@ -541,46 +536,171 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r) pstate->p_lateral_active = true; /* - * Transform the raw expression. + * Transform the raw expressions. + * + * While transforming, also save function names for possible use as alias + * and column names. We use the same transformation rules as for a SELECT + * output expression. For a FuncCall node, the result will be the + * function name, but it is possible for the grammar to hand back other + * node types. + * + * We have to get this info now, because FigureColname only works on raw + * parsetrees. Actually deciding what to do with the names is left up to + * addRangeTableEntryForFunction. + * + * Likewise, collect column definition lists if there were any. But + * complain if we find one here and the RangeFunction has one too. */ - funcexpr = transformExpr(pstate, r->funccallnode, EXPR_KIND_FROM_FUNCTION); + foreach(lc, r->functions) + { + List *pair = (List *) lfirst(lc); + Node *fexpr; + List *coldeflist; + + /* Disassemble the function-call/column-def-list pairs */ + Assert(list_length(pair) == 2); + fexpr = (Node *) linitial(pair); + coldeflist = (List *) lsecond(pair); + + /* + * If we find a function call unnest() with more than one argument and + * no special decoration, transform it into separate unnest() calls on + * each argument. This is a kluge, for sure, but it's less nasty than + * other ways of implementing the SQL-standard UNNEST() syntax. + * + * If there is any decoration (including a coldeflist), we don't + * transform, which probably means a no-such-function error later. We + * could alternatively throw an error right now, but that doesn't seem + * tremendously helpful. If someone is using any such decoration, + * then they're not using the SQL-standard syntax, and they're more + * likely expecting an un-tweaked function call. + * + * Note: the transformation changes a non-schema-qualified unnest() + * function name into schema-qualified pg_catalog.unnest(). This + * choice is also a bit debatable, but it seems reasonable to force + * use of built-in unnest() when we make this transformation. + */ + if (IsA(fexpr, FuncCall)) + { + FuncCall *fc = (FuncCall *) fexpr; + + if (list_length(fc->funcname) == 1 && + strcmp(strVal(linitial(fc->funcname)), "unnest") == 0 && + list_length(fc->args) > 1 && + fc->agg_order == NIL && + fc->agg_filter == NULL && + !fc->agg_star && + !fc->agg_distinct && + !fc->func_variadic && + fc->over == NULL && + coldeflist == NIL) + { + ListCell *lc; + + foreach(lc, fc->args) + { + Node *arg = (Node *) lfirst(lc); + FuncCall *newfc; + + newfc = makeFuncCall(SystemFuncName("unnest"), + list_make1(arg), + fc->location); + + funcexprs = lappend(funcexprs, + transformExpr(pstate, (Node *) newfc, + EXPR_KIND_FROM_FUNCTION)); + + funcnames = lappend(funcnames, + FigureColname((Node *) newfc)); + + /* coldeflist is empty, so no error is possible */ + + coldeflists = lappend(coldeflists, coldeflist); + } + continue; /* done with this function item */ + } + } + + /* normal case ... */ + funcexprs = lappend(funcexprs, + transformExpr(pstate, fexpr, + EXPR_KIND_FROM_FUNCTION)); + + funcnames = lappend(funcnames, + FigureColname(fexpr)); + + if (coldeflist && r->coldeflist) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("multiple column definition lists are not allowed for the same function"), + parser_errposition(pstate, + exprLocation((Node *) r->coldeflist)))); + + coldeflists = lappend(coldeflists, coldeflist); + } pstate->p_lateral_active = false; /* - * We must assign collations now so that we can fill funccolcollations. + * We must assign collations now so that the RTE exposes correct collation + * info for Vars created from it. */ - assign_expr_collations(pstate, funcexpr); + assign_list_collations(pstate, funcexprs); + + /* + * Install the top-level coldeflist if there was one (we already checked + * that there was no conflicting per-function coldeflist). + * + * We only allow this when there's a single function (even after UNNEST + * expansion) and no WITH ORDINALITY. The reason for the latter + * restriction is that it's not real clear whether the ordinality column + * should be in the coldeflist, and users are too likely to make mistakes + * in one direction or the other. Putting the coldeflist inside TABLE() + * is much clearer in this case. + */ + if (r->coldeflist) + { + if (list_length(funcexprs) != 1) + { + if (r->is_table) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("TABLE() with multiple functions cannot have a column definition list"), + errhint("Put a separate column definition list for each function inside TABLE()."), + parser_errposition(pstate, + exprLocation((Node *) r->coldeflist)))); + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("UNNEST() with multiple arguments cannot have a column definition list"), + errhint("Use separate UNNEST() calls inside TABLE(), and attach a column definition list to each one."), + parser_errposition(pstate, + exprLocation((Node *) r->coldeflist)))); + } + if (r->ordinality) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("WITH ORDINALITY cannot be used with a column definition list"), + errhint("Put the column definition list inside TABLE()."), + parser_errposition(pstate, + exprLocation((Node *) r->coldeflist)))); + + coldeflists = list_make1(r->coldeflist); + } /* * Mark the RTE as LATERAL if the user said LATERAL explicitly, or if * there are any lateral cross-references in it. */ - is_lateral = r->lateral || contain_vars_of_level(funcexpr, 0); + is_lateral = r->lateral || contain_vars_of_level((Node *) funcexprs, 0); /* * OK, build an RTE for the function. */ - rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr, + rte = addRangeTableEntryForFunction(pstate, + funcnames, funcexprs, coldeflists, r, is_lateral, true); - /* - * If a coldeflist was supplied, ensure it defines a legal set of names - * (no duplicates) and datatypes (no pseudo-types, for instance). - * addRangeTableEntryForFunction looked up the type names but didn't check - * them further than that. - */ - if (r->coldeflist) - { - TupleDesc tupdesc; - - tupdesc = BuildDescFromLists(rte->eref->colnames, - rte->funccoltypes, - rte->funccoltypmods, - rte->funccolcollations); - CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE, false); - } - return rte; } 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: diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 07fce8a0112..ee6802a6558 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -472,7 +472,7 @@ GetColumnDefCollation(ParseState *pstate, ColumnDef *coldef, Oid typeOid) { Oid result; Oid typcollation = get_typcollation(typeOid); - int location = -1; + int location = coldef->location; if (coldef->collClause) { diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 19d19e5f396..ae2206a1233 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -754,6 +754,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla def->collClause = NULL; def->collOid = attribute->attcollation; def->constraints = NIL; + def->location = -1; /* * Add to column list @@ -969,6 +970,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename) n->collClause = NULL; n->collOid = attr->attcollation; n->constraints = NIL; + n->location = -1; cxt->columns = lappend(cxt->columns, n); } DecrTupleDescRefCount(tupdesc); |