1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-17 17:02:08 +03:00

Add SQL Standard WITH ORDINALITY support for UNNEST (and any other SRF)

Author: Andrew Gierth, David Fetter
Reviewers: Dean Rasheed, Jeevan Chalke, Stephen Frost
This commit is contained in:
Greg Stark
2013-07-29 16:38:01 +01:00
parent 55cbfa5366
commit c62736cc37
22 changed files with 1486 additions and 323 deletions

View File

@ -787,18 +787,24 @@ 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.
* a named composite type or a registered RECORD type.
*
* 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)
buildRelationAliases(TupleDesc tupdesc, Alias *alias, Alias *eref, bool ordinality)
{
int maxattrs = tupdesc->natts;
ListCell *aliaslc;
@ -850,12 +856,33 @@ buildRelationAliases(TupleDesc tupdesc, Alias *alias, Alias *eref)
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, numaliases)));
eref->aliasname,
maxattrs - numdropped + (ordinality ? 1 : 0),
numaliases)));
}
/*
@ -867,48 +894,60 @@ buildRelationAliases(TupleDesc tupdesc, Alias *alias, Alias *eref)
* 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.
*
* 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.
*/
static void
buildScalarFunctionAlias(Node *funcexpr, char *funcname,
Alias *alias, Alias *eref)
Alias *alias, Alias *eref, bool ordinality)
{
char *pname;
Assert(eref->colnames == NIL);
/* Use user-specified column alias if there is one. */
if (alias && alias->colnames != NIL)
{
if (list_length(alias->colnames) != 1)
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);
return;
}
/*
* 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))
else
{
pname = get_func_result_name(((FuncExpr *) funcexpr)->funcid);
if (pname)
{
eref->colnames = list_make1(makeString(pname));
return;
}
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));
}
/*
* Otherwise use the previously-determined alias (not necessarily the
* function name!)
*/
eref->colnames = list_make1(makeString(eref->aliasname));
/* 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")));
return;
}
/*
@ -1004,7 +1043,7 @@ addRangeTableEntry(ParseState *pstate,
* and/or actual column names.
*/
rte->eref = makeAlias(refname, NIL);
buildRelationAliases(rel->rd_att, alias, rte->eref);
buildRelationAliases(rel->rd_att, alias, rte->eref, false);
/*
* Drop the rel refcount, but keep the access lock till end of transaction
@ -1064,7 +1103,7 @@ addRangeTableEntryForRelation(ParseState *pstate,
* and/or actual column names.
*/
rte->eref = makeAlias(refname, NIL);
buildRelationAliases(rel->rd_att, alias, rte->eref);
buildRelationAliases(rel->rd_att, alias, rte->eref, false);
/*
* Set flags and access permissions.
@ -1235,17 +1274,23 @@ addRangeTableEntryForFunction(ParseState *pstate,
/* Composite data type, e.g. a table's row type */
Assert(tupdesc);
/* Build the column alias list */
buildRelationAliases(tupdesc, alias, eref);
buildRelationAliases(tupdesc, alias, eref, rangefunc->ordinality);
}
else if (functypclass == TYPEFUNC_SCALAR)
{
/* Base data type, i.e. scalar */
buildScalarFunctionAlias(funcexpr, funcname, alias, eref);
buildScalarFunctionAlias(funcexpr, funcname, alias, eref, rangefunc->ordinality);
}
else if (functypclass == TYPEFUNC_RECORD)
{
ListCell *col;
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))));
/*
* Use the column definition list to form the alias list and
* funccoltypes/funccoltypmods/funccolcollations lists.
@ -1288,6 +1333,7 @@ addRangeTableEntryForFunction(ParseState *pstate,
* permissions mechanism).
*/
rte->lateral = lateral;
rte->funcordinality = rangefunc->ordinality;
rte->inh = false; /* never true for functions */
rte->inFromCl = inFromCl;
@ -1643,6 +1689,11 @@ 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,
@ -1711,6 +1762,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
TypeFuncClass functypclass;
Oid funcrettype;
TupleDesc tupdesc;
int ordinality_attno = 0;
functypclass = get_expr_result_type(rte->funcexpr,
&funcrettype,
@ -1719,9 +1771,16 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
{
/* Composite data type, e.g. a table's row type */
Assert(tupdesc);
/*
* 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)
{
@ -1742,6 +1801,8 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
*colvars = lappend(*colvars, varnode);
}
ordinality_attno = 2;
}
else if (functypclass == TYPEFUNC_RECORD)
{
@ -1774,12 +1835,34 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
*colvars = lappend(*colvars, varnode);
}
}
/* note, ordinality is not allowed in this case */
}
else
{
/* addRangeTableEntryForFunction should've caught this */
elog(ERROR, "function in FROM has unsupported return type");
}
/* tack on the extra ordinality column if present */
if (rte->funcordinality)
{
Assert(ordinality_attno > 0);
if (colnames)
*colnames = lappend(*colnames, llast(rte->eref->colnames));
if (colvars)
{
Var *varnode = makeVar(rtindex,
ordinality_attno,
INT8OID,
-1,
InvalidOid,
sublevels_up);
*colvars = lappend(*colvars, varnode);
}
}
}
break;
case RTE_VALUES:
@ -1955,6 +2038,9 @@ 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)
*/
static void
expandTupleDesc(TupleDesc tupdesc, Alias *eref,
@ -2114,6 +2200,9 @@ 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,
@ -2172,6 +2261,20 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
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);
@ -2182,6 +2285,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
Form_pg_attribute att_tup;
Assert(tupdesc);
/* this is probably a can't-happen case */
if (attnum < 1 || attnum > tupdesc->natts)
ereport(ERROR,
@ -2208,6 +2312,8 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
}
else if (functypclass == TYPEFUNC_SCALAR)
{
Assert(attnum == 1);
/* Base data type, i.e. scalar */
*vartype = funcrettype;
*vartypmod = -1;
@ -2332,7 +2438,17 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
Oid funcrettype = exprType(rte->funcexpr);
Oid funcrelid = typeidTypeRelid(funcrettype);
if (OidIsValid(funcrelid))
/*
* if ordinality, then a reference to the last column
* in the name list must be referring to the
* ordinality column, which is not dropped
*/
if (rte->funcordinality
&& attnum == list_length(rte->eref->colnames))
{
result = false;
}
else if (OidIsValid(funcrelid))
{
/*
* Composite data type, i.e. a table's row type