mirror of
https://github.com/postgres/postgres.git
synced 2025-09-03 15:22:11 +03:00
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
This commit is contained in:
@@ -504,11 +504,7 @@ _copyFunctionScan(const FunctionScan *from)
|
||||
/*
|
||||
* copy remainder of node
|
||||
*/
|
||||
COPY_NODE_FIELD(funcexpr);
|
||||
COPY_NODE_FIELD(funccolnames);
|
||||
COPY_NODE_FIELD(funccoltypes);
|
||||
COPY_NODE_FIELD(funccoltypmods);
|
||||
COPY_NODE_FIELD(funccolcollations);
|
||||
COPY_NODE_FIELD(functions);
|
||||
COPY_SCALAR_FIELD(funcordinality);
|
||||
|
||||
return newnode;
|
||||
@@ -1981,10 +1977,7 @@ _copyRangeTblEntry(const RangeTblEntry *from)
|
||||
COPY_SCALAR_FIELD(security_barrier);
|
||||
COPY_SCALAR_FIELD(jointype);
|
||||
COPY_NODE_FIELD(joinaliasvars);
|
||||
COPY_NODE_FIELD(funcexpr);
|
||||
COPY_NODE_FIELD(funccoltypes);
|
||||
COPY_NODE_FIELD(funccoltypmods);
|
||||
COPY_NODE_FIELD(funccolcollations);
|
||||
COPY_NODE_FIELD(functions);
|
||||
COPY_SCALAR_FIELD(funcordinality);
|
||||
COPY_NODE_FIELD(values_lists);
|
||||
COPY_NODE_FIELD(values_collations);
|
||||
@@ -2007,6 +2000,22 @@ _copyRangeTblEntry(const RangeTblEntry *from)
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static RangeTblFunction *
|
||||
_copyRangeTblFunction(const RangeTblFunction *from)
|
||||
{
|
||||
RangeTblFunction *newnode = makeNode(RangeTblFunction);
|
||||
|
||||
COPY_NODE_FIELD(funcexpr);
|
||||
COPY_SCALAR_FIELD(funccolcount);
|
||||
COPY_NODE_FIELD(funccolnames);
|
||||
COPY_NODE_FIELD(funccoltypes);
|
||||
COPY_NODE_FIELD(funccoltypmods);
|
||||
COPY_NODE_FIELD(funccolcollations);
|
||||
COPY_BITMAPSET_FIELD(funcparams);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static WithCheckOption *
|
||||
_copyWithCheckOption(const WithCheckOption *from)
|
||||
{
|
||||
@@ -2299,9 +2308,10 @@ _copyRangeFunction(const RangeFunction *from)
|
||||
{
|
||||
RangeFunction *newnode = makeNode(RangeFunction);
|
||||
|
||||
COPY_SCALAR_FIELD(ordinality);
|
||||
COPY_SCALAR_FIELD(lateral);
|
||||
COPY_NODE_FIELD(funccallnode);
|
||||
COPY_SCALAR_FIELD(ordinality);
|
||||
COPY_SCALAR_FIELD(is_table);
|
||||
COPY_NODE_FIELD(functions);
|
||||
COPY_NODE_FIELD(alias);
|
||||
COPY_NODE_FIELD(coldeflist);
|
||||
|
||||
@@ -2366,6 +2376,7 @@ _copyColumnDef(const ColumnDef *from)
|
||||
COPY_SCALAR_FIELD(collOid);
|
||||
COPY_NODE_FIELD(constraints);
|
||||
COPY_NODE_FIELD(fdwoptions);
|
||||
COPY_LOCATION_FIELD(location);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
@@ -4550,6 +4561,9 @@ copyObject(const void *from)
|
||||
case T_RangeTblEntry:
|
||||
retval = _copyRangeTblEntry(from);
|
||||
break;
|
||||
case T_RangeTblFunction:
|
||||
retval = _copyRangeTblFunction(from);
|
||||
break;
|
||||
case T_WithCheckOption:
|
||||
retval = _copyWithCheckOption(from);
|
||||
break;
|
||||
|
@@ -2140,9 +2140,10 @@ _equalRangeSubselect(const RangeSubselect *a, const RangeSubselect *b)
|
||||
static bool
|
||||
_equalRangeFunction(const RangeFunction *a, const RangeFunction *b)
|
||||
{
|
||||
COMPARE_SCALAR_FIELD(ordinality);
|
||||
COMPARE_SCALAR_FIELD(lateral);
|
||||
COMPARE_NODE_FIELD(funccallnode);
|
||||
COMPARE_SCALAR_FIELD(ordinality);
|
||||
COMPARE_SCALAR_FIELD(is_table);
|
||||
COMPARE_NODE_FIELD(functions);
|
||||
COMPARE_NODE_FIELD(alias);
|
||||
COMPARE_NODE_FIELD(coldeflist);
|
||||
|
||||
@@ -2179,6 +2180,7 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
|
||||
COMPARE_SCALAR_FIELD(collOid);
|
||||
COMPARE_NODE_FIELD(constraints);
|
||||
COMPARE_NODE_FIELD(fdwoptions);
|
||||
COMPARE_LOCATION_FIELD(location);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -2245,10 +2247,7 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b)
|
||||
COMPARE_SCALAR_FIELD(security_barrier);
|
||||
COMPARE_SCALAR_FIELD(jointype);
|
||||
COMPARE_NODE_FIELD(joinaliasvars);
|
||||
COMPARE_NODE_FIELD(funcexpr);
|
||||
COMPARE_NODE_FIELD(funccoltypes);
|
||||
COMPARE_NODE_FIELD(funccoltypmods);
|
||||
COMPARE_NODE_FIELD(funccolcollations);
|
||||
COMPARE_NODE_FIELD(functions);
|
||||
COMPARE_SCALAR_FIELD(funcordinality);
|
||||
COMPARE_NODE_FIELD(values_lists);
|
||||
COMPARE_NODE_FIELD(values_collations);
|
||||
@@ -2271,6 +2270,20 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalRangeTblFunction(const RangeTblFunction *a, const RangeTblFunction *b)
|
||||
{
|
||||
COMPARE_NODE_FIELD(funcexpr);
|
||||
COMPARE_SCALAR_FIELD(funccolcount);
|
||||
COMPARE_NODE_FIELD(funccolnames);
|
||||
COMPARE_NODE_FIELD(funccoltypes);
|
||||
COMPARE_NODE_FIELD(funccoltypmods);
|
||||
COMPARE_NODE_FIELD(funccolcollations);
|
||||
COMPARE_BITMAPSET_FIELD(funcparams);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalWithCheckOption(const WithCheckOption *a, const WithCheckOption *b)
|
||||
{
|
||||
@@ -3018,6 +3031,9 @@ equal(const void *a, const void *b)
|
||||
case T_RangeTblEntry:
|
||||
retval = _equalRangeTblEntry(a, b);
|
||||
break;
|
||||
case T_RangeTblFunction:
|
||||
retval = _equalRangeTblFunction(a, b);
|
||||
break;
|
||||
case T_WithCheckOption:
|
||||
retval = _equalWithCheckOption(a, b);
|
||||
break;
|
||||
|
@@ -122,14 +122,10 @@ makeVarFromTargetEntry(Index varno,
|
||||
* a rowtype; either a named composite type, or RECORD. This function
|
||||
* encapsulates the logic for determining the correct rowtype OID to use.
|
||||
*
|
||||
* If allowScalar is true, then for the case where the RTE is a function
|
||||
* If allowScalar is true, then for the case where the RTE is a single function
|
||||
* returning a non-composite result type, we produce a normal Var referencing
|
||||
* the function's result directly, instead of the single-column composite
|
||||
* value that the whole-row notation might otherwise suggest.
|
||||
*
|
||||
* We also handle the specific case of function RTEs with ordinality,
|
||||
* where the additional column has to be added. This forces the result
|
||||
* to be composite and RECORD type.
|
||||
*/
|
||||
Var *
|
||||
makeWholeRowVar(RangeTblEntry *rte,
|
||||
@@ -139,6 +135,7 @@ makeWholeRowVar(RangeTblEntry *rte,
|
||||
{
|
||||
Var *result;
|
||||
Oid toid;
|
||||
Node *fexpr;
|
||||
|
||||
switch (rte->rtekind)
|
||||
{
|
||||
@@ -157,31 +154,27 @@ makeWholeRowVar(RangeTblEntry *rte,
|
||||
break;
|
||||
|
||||
case RTE_FUNCTION:
|
||||
/*
|
||||
* RTE is a function with or without ordinality. We map the
|
||||
* cases as follows:
|
||||
*
|
||||
* If ordinality is set, we return a composite var even if
|
||||
* the function is a scalar. This var is always of RECORD type.
|
||||
*
|
||||
* If ordinality is not set but the function returns a row,
|
||||
* we keep the function's return type.
|
||||
*
|
||||
* If the function is a scalar, we do what allowScalar requests.
|
||||
*/
|
||||
toid = exprType(rte->funcexpr);
|
||||
|
||||
if (rte->funcordinality)
|
||||
/*
|
||||
* If there's more than one function, or ordinality is requested,
|
||||
* force a RECORD result, since there's certainly more than one
|
||||
* column involved and it can't be a known named type.
|
||||
*/
|
||||
if (rte->funcordinality || list_length(rte->functions) != 1)
|
||||
{
|
||||
/* ORDINALITY always produces an anonymous RECORD result */
|
||||
/* always produces an anonymous RECORD result */
|
||||
result = makeVar(varno,
|
||||
InvalidAttrNumber,
|
||||
RECORDOID,
|
||||
-1,
|
||||
InvalidOid,
|
||||
varlevelsup);
|
||||
break;
|
||||
}
|
||||
else if (type_is_rowtype(toid))
|
||||
|
||||
fexpr = ((RangeTblFunction *) linitial(rte->functions))->funcexpr;
|
||||
toid = exprType(fexpr);
|
||||
if (type_is_rowtype(toid))
|
||||
{
|
||||
/* func returns composite; same as relation case */
|
||||
result = makeVar(varno,
|
||||
@@ -198,7 +191,7 @@ makeWholeRowVar(RangeTblEntry *rte,
|
||||
1,
|
||||
toid,
|
||||
-1,
|
||||
exprCollation(rte->funcexpr),
|
||||
exprCollation(fexpr),
|
||||
varlevelsup);
|
||||
}
|
||||
else
|
||||
@@ -214,6 +207,7 @@ makeWholeRowVar(RangeTblEntry *rte,
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
/*
|
||||
* RTE is a join, subselect, or VALUES. We represent this as a
|
||||
* whole-row Var of RECORD type. (Note that in most cases the Var
|
||||
@@ -541,23 +535,21 @@ makeDefElemExtended(char *nameSpace, char *name, Node *arg,
|
||||
* makeFuncCall -
|
||||
*
|
||||
* Initialize a FuncCall struct with the information every caller must
|
||||
* supply. Any non-default parameters have to be handled by the
|
||||
* caller.
|
||||
*
|
||||
* supply. Any non-default parameters have to be inserted by the caller.
|
||||
*/
|
||||
|
||||
FuncCall *
|
||||
makeFuncCall(List *name, List *args, int location)
|
||||
{
|
||||
FuncCall *n = makeNode(FuncCall);
|
||||
FuncCall *n = makeNode(FuncCall);
|
||||
|
||||
n->funcname = name;
|
||||
n->args = args;
|
||||
n->location = location;
|
||||
n->agg_order = NIL;
|
||||
n->agg_filter = NULL;
|
||||
n->agg_star = FALSE;
|
||||
n->agg_distinct = FALSE;
|
||||
n->func_variadic = FALSE;
|
||||
n->over = NULL;
|
||||
n->location = location;
|
||||
return n;
|
||||
}
|
||||
|
@@ -1447,6 +1447,9 @@ exprLocation(const Node *expr)
|
||||
case T_TypeName:
|
||||
loc = ((const TypeName *) expr)->location;
|
||||
break;
|
||||
case T_ColumnDef:
|
||||
loc = ((const ColumnDef *) expr)->location;
|
||||
break;
|
||||
case T_Constraint:
|
||||
loc = ((const Constraint *) expr)->location;
|
||||
break;
|
||||
@@ -1901,6 +1904,8 @@ expression_tree_walker(Node *node,
|
||||
break;
|
||||
case T_PlaceHolderInfo:
|
||||
return walker(((PlaceHolderInfo *) node)->ph_var, context);
|
||||
case T_RangeTblFunction:
|
||||
return walker(((RangeTblFunction *) node)->funcexpr, context);
|
||||
default:
|
||||
elog(ERROR, "unrecognized node type: %d",
|
||||
(int) nodeTag(node));
|
||||
@@ -2000,7 +2005,7 @@ range_table_walker(List *rtable,
|
||||
return true;
|
||||
break;
|
||||
case RTE_FUNCTION:
|
||||
if (walker(rte->funcexpr, context))
|
||||
if (walker(rte->functions, context))
|
||||
return true;
|
||||
break;
|
||||
case RTE_VALUES:
|
||||
@@ -2615,6 +2620,17 @@ expression_tree_mutator(Node *node,
|
||||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
case T_RangeTblFunction:
|
||||
{
|
||||
RangeTblFunction *rtfunc = (RangeTblFunction *) node;
|
||||
RangeTblFunction *newnode;
|
||||
|
||||
FLATCOPY(newnode, rtfunc, RangeTblFunction);
|
||||
MUTATE(newnode->funcexpr, rtfunc->funcexpr, Node *);
|
||||
/* Assume we need not copy the coldef info lists */
|
||||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized node type: %d",
|
||||
(int) nodeTag(node));
|
||||
@@ -2725,7 +2741,7 @@ range_table_mutator(List *rtable,
|
||||
}
|
||||
break;
|
||||
case RTE_FUNCTION:
|
||||
MUTATE(newrte->funcexpr, rte->funcexpr, Node *);
|
||||
MUTATE(newrte->functions, rte->functions, List *);
|
||||
break;
|
||||
case RTE_VALUES:
|
||||
MUTATE(newrte->values_lists, rte->values_lists, List *);
|
||||
@@ -3113,10 +3129,12 @@ raw_expression_tree_walker(Node *node,
|
||||
{
|
||||
RangeFunction *rf = (RangeFunction *) node;
|
||||
|
||||
if (walker(rf->funccallnode, context))
|
||||
if (walker(rf->functions, context))
|
||||
return true;
|
||||
if (walker(rf->alias, context))
|
||||
return true;
|
||||
if (walker(rf->coldeflist, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_TypeName:
|
||||
|
@@ -516,11 +516,7 @@ _outFunctionScan(StringInfo str, const FunctionScan *node)
|
||||
|
||||
_outScanInfo(str, (const Scan *) node);
|
||||
|
||||
WRITE_NODE_FIELD(funcexpr);
|
||||
WRITE_NODE_FIELD(funccolnames);
|
||||
WRITE_NODE_FIELD(funccoltypes);
|
||||
WRITE_NODE_FIELD(funccoltypmods);
|
||||
WRITE_NODE_FIELD(funccolcollations);
|
||||
WRITE_NODE_FIELD(functions);
|
||||
WRITE_BOOL_FIELD(funcordinality);
|
||||
}
|
||||
|
||||
@@ -2154,6 +2150,7 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
|
||||
WRITE_OID_FIELD(collOid);
|
||||
WRITE_NODE_FIELD(constraints);
|
||||
WRITE_NODE_FIELD(fdwoptions);
|
||||
WRITE_LOCATION_FIELD(location);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -2382,10 +2379,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
|
||||
WRITE_NODE_FIELD(joinaliasvars);
|
||||
break;
|
||||
case RTE_FUNCTION:
|
||||
WRITE_NODE_FIELD(funcexpr);
|
||||
WRITE_NODE_FIELD(funccoltypes);
|
||||
WRITE_NODE_FIELD(funccoltypmods);
|
||||
WRITE_NODE_FIELD(funccolcollations);
|
||||
WRITE_NODE_FIELD(functions);
|
||||
WRITE_BOOL_FIELD(funcordinality);
|
||||
break;
|
||||
case RTE_VALUES:
|
||||
@@ -2414,6 +2408,20 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
|
||||
WRITE_BITMAPSET_FIELD(modifiedCols);
|
||||
}
|
||||
|
||||
static void
|
||||
_outRangeTblFunction(StringInfo str, const RangeTblFunction *node)
|
||||
{
|
||||
WRITE_NODE_TYPE("RANGETBLFUNCTION");
|
||||
|
||||
WRITE_NODE_FIELD(funcexpr);
|
||||
WRITE_INT_FIELD(funccolcount);
|
||||
WRITE_NODE_FIELD(funccolnames);
|
||||
WRITE_NODE_FIELD(funccoltypes);
|
||||
WRITE_NODE_FIELD(funccoltypmods);
|
||||
WRITE_NODE_FIELD(funccolcollations);
|
||||
WRITE_BITMAPSET_FIELD(funcparams);
|
||||
}
|
||||
|
||||
static void
|
||||
_outAExpr(StringInfo str, const A_Expr *node)
|
||||
{
|
||||
@@ -2619,9 +2627,10 @@ _outRangeFunction(StringInfo str, const RangeFunction *node)
|
||||
{
|
||||
WRITE_NODE_TYPE("RANGEFUNCTION");
|
||||
|
||||
WRITE_BOOL_FIELD(ordinality);
|
||||
WRITE_BOOL_FIELD(lateral);
|
||||
WRITE_NODE_FIELD(funccallnode);
|
||||
WRITE_BOOL_FIELD(ordinality);
|
||||
WRITE_BOOL_FIELD(is_table);
|
||||
WRITE_NODE_FIELD(functions);
|
||||
WRITE_NODE_FIELD(alias);
|
||||
WRITE_NODE_FIELD(coldeflist);
|
||||
}
|
||||
@@ -3156,6 +3165,9 @@ _outNode(StringInfo str, const void *obj)
|
||||
case T_RangeTblEntry:
|
||||
_outRangeTblEntry(str, obj);
|
||||
break;
|
||||
case T_RangeTblFunction:
|
||||
_outRangeTblFunction(str, obj);
|
||||
break;
|
||||
case T_A_Expr:
|
||||
_outAExpr(str, obj);
|
||||
break;
|
||||
|
@@ -1220,10 +1220,7 @@ _readRangeTblEntry(void)
|
||||
READ_NODE_FIELD(joinaliasvars);
|
||||
break;
|
||||
case RTE_FUNCTION:
|
||||
READ_NODE_FIELD(funcexpr);
|
||||
READ_NODE_FIELD(funccoltypes);
|
||||
READ_NODE_FIELD(funccoltypmods);
|
||||
READ_NODE_FIELD(funccolcollations);
|
||||
READ_NODE_FIELD(functions);
|
||||
READ_BOOL_FIELD(funcordinality);
|
||||
break;
|
||||
case RTE_VALUES:
|
||||
@@ -1255,6 +1252,25 @@ _readRangeTblEntry(void)
|
||||
READ_DONE();
|
||||
}
|
||||
|
||||
/*
|
||||
* _readRangeTblFunction
|
||||
*/
|
||||
static RangeTblFunction *
|
||||
_readRangeTblFunction(void)
|
||||
{
|
||||
READ_LOCALS(RangeTblFunction);
|
||||
|
||||
READ_NODE_FIELD(funcexpr);
|
||||
READ_INT_FIELD(funccolcount);
|
||||
READ_NODE_FIELD(funccolnames);
|
||||
READ_NODE_FIELD(funccoltypes);
|
||||
READ_NODE_FIELD(funccoltypmods);
|
||||
READ_NODE_FIELD(funccolcollations);
|
||||
READ_BITMAPSET_FIELD(funcparams);
|
||||
|
||||
READ_DONE();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* parseNodeString
|
||||
@@ -1378,6 +1394,8 @@ parseNodeString(void)
|
||||
return_value = _readFromExpr();
|
||||
else if (MATCH("RTE", 3))
|
||||
return_value = _readRangeTblEntry();
|
||||
else if (MATCH("RANGETBLFUNCTION", 16))
|
||||
return_value = _readRangeTblFunction();
|
||||
else if (MATCH("NOTIFY", 6))
|
||||
return_value = _readNotifyStmt();
|
||||
else if (MATCH("DECLARECURSOR", 13))
|
||||
|
Reference in New Issue
Block a user