aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/gram.y98
-rw-r--r--src/backend/parser/parse_clause.c192
-rw-r--r--src/backend/parser/parse_relation.c836
-rw-r--r--src/backend/parser/parse_type.c2
-rw-r--r--src/backend/parser/parse_utilcmd.c2
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);