Commit c62736cc authored by Greg Stark's avatar Greg Stark

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

Author: Andrew Gierth, David Fetter
Reviewers: Dean Rasheed, Jeevan Chalke, Stephen Frost
parent 55cbfa53
......@@ -13278,7 +13278,7 @@ select $1[i][j]
generate_subscripts($1,2) g2(j);
$$ LANGUAGE sql IMMUTABLE;
CREATE FUNCTION
postgres=# SELECT * FROM unnest2(ARRAY[[1,2],[3,4]]);
SELECT * FROM unnest2(ARRAY[[1,2],[3,4]]);
unnest2
---------
1
......@@ -13286,6 +13286,48 @@ postgres=# SELECT * FROM unnest2(ARRAY[[1,2],[3,4]]);
3
4
(4 rows)
</programlisting>
</para>
<indexterm>
<primary>ordinality</primary>
</indexterm>
<para>
When a function in the <literal>FROM</literal> clause is suffixed by
<literal>WITH ORDINALITY</literal>, a <type>bigint</type> column is appended
to the output which starts from 1 and increments by 1 for each row of the
function's output. This is most useful in the case of set returning functions
such as UNNEST(). This functionality is available for functions returning
composite types or using <literal>OUT</literal> parameters, but not when using
a function returning <literal>RECORD</literal> with an explicit column
definition list.
<programlisting>
-- set returning function WITH ORDINALITY
SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n);
ls | n
-----------------+----
pg_serial | 1
pg_twophase | 2
postmaster.opts | 3
pg_notify | 4
postgresql.conf | 5
pg_tblspc | 6
logfile | 7
base | 8
postmaster.pid | 9
pg_ident.conf | 10
global | 11
pg_clog | 12
pg_snapshots | 13
pg_multixact | 14
PG_VERSION | 15
pg_xlog | 16
pg_hba.conf | 17
pg_stat_tmp | 18
pg_subtrans | 19
(19 rows)
</programlisting>
</para>
......
......@@ -52,7 +52,8 @@ SELECT [ ALL | DISTINCT [ ON ( <replaceable class="parameter">expression</replac
[ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [ [ AS ] <replaceable class="parameter">alias</replaceable> [ ( <replaceable class="parameter">column_alias</replaceable> [, ...] ) ] ]
[ LATERAL ] ( <replaceable class="parameter">select</replaceable> ) [ AS ] <replaceable class="parameter">alias</replaceable> [ ( <replaceable class="parameter">column_alias</replaceable> [, ...] ) ]
<replaceable class="parameter">with_query_name</replaceable> [ [ AS ] <replaceable class="parameter">alias</replaceable> [ ( <replaceable class="parameter">column_alias</replaceable> [, ...] ) ] ]
[ LATERAL ] <replaceable class="parameter">function_name</replaceable> ( [ <replaceable class="parameter">argument</replaceable> [, ...] ] ) [ AS ] <replaceable class="parameter">alias</replaceable> [ ( <replaceable class="parameter">column_alias</replaceable> [, ...] | <replaceable class="parameter">column_definition</replaceable> [, ...] ) ]
[ LATERAL ] <replaceable class="parameter">function_name</replaceable> ( [ <replaceable class="parameter">argument</replaceable> [, ...] ] ) [ WITH ORDINALITY ] [ [ AS ] <replaceable class="parameter">alias</replaceable> [ ( <replaceable class="parameter">column_alias</replaceable> [, ...] ) ] ]
[ LATERAL ] <replaceable class="parameter">function_name</replaceable> ( [ <replaceable class="parameter">argument</replaceable> [, ...] ] ) [ AS ] <replaceable class="parameter">alias</replaceable> ( <replaceable class="parameter">column_definition</replaceable> [, ...] )
[ LATERAL ] <replaceable class="parameter">function_name</replaceable> ( [ <replaceable class="parameter">argument</replaceable> [, ...] ] ) AS ( <replaceable class="parameter">column_definition</replaceable> [, ...] )
<replaceable class="parameter">from_item</replaceable> [ NATURAL ] <replaceable class="parameter">join_type</replaceable> <replaceable class="parameter">from_item</replaceable> [ ON <replaceable class="parameter">join_condition</replaceable> | USING ( <replaceable class="parameter">join_column</replaceable> [, ...] ) ]
......@@ -368,18 +369,40 @@ TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
clause. (This is especially useful for functions that return
result sets, but any function can be used.) This acts as
though its output were created as a temporary table for the
duration of this single <command>SELECT</command> command. An
alias can also be used. If an alias is written, a column alias
list can also be written to provide substitute names for one
or more attributes of the function's composite return type. If
the function has been defined as returning the <type>record</>
data type, then an alias or the key word <literal>AS</> must
be present, followed by a column definition list in the form
<literal>( <replaceable
duration of this single <command>SELECT</command> command.
When the optional <command>WITH ORDINALITY</command> is
appended to the function call, a new column is appended after
all the function call's columns with numbering for each row.
For example:
<programlisting>
SELECT * FROM unnest(ARRAY['a','b','c','d','e','f']) WITH ORDINALITY;
unnest | ordinality
--------+----------
a | 1
b | 2
c | 3
d | 4
e | 5
f | 6
(6 rows)
</programlisting>
An alias can also be used. If an alias is written, a column
alias list can also be written to provide substitute names for
one or more attributes of the function's composite return
type, including the column added by <literal>ORDINALITY</literal>
if present.
</para>
<para>
If the function has been defined as returning the
<type>record</> data type, then an alias or the key word
<literal>AS</> must be present, followed by a column
definition list in the form <literal>( <replaceable
class="parameter">column_name</replaceable> <replaceable
class="parameter">data_type</replaceable> <optional>, ... </>
)</literal>. The column definition list must match the actual
number and types of columns returned by the function.
class="parameter">data_type</replaceable> <optional>, ...
</>)</literal>. The column definition list must match the
actual number and types of columns returned by the function.
<literal>ORDINALITY</literal> does not work in this case.
</para>
</listitem>
</varlistentry>
......
......@@ -157,6 +157,40 @@ CreateTupleDescCopy(TupleDesc tupdesc)
return desc;
}
/*
* CreateTupleDescCopyExtend
* This function creates a new TupleDesc by copying from an existing
* TupleDesc, but adding space for more columns. The new tupdesc is
* not regarded as the same record type as the old one (and therefore
* does not inherit its typeid/typmod, which instead are left as an
* anonymous record type).
*
* The additional column slots are not initialized in any way;
* callers must do their own TupleDescInitEntry on each.
*
* !!! Constraints and defaults are not copied !!!
*/
TupleDesc
CreateTupleDescCopyExtend(TupleDesc tupdesc, int moreatts)
{
TupleDesc desc;
int i;
int src_natts = tupdesc->natts;
Assert(moreatts >= 0);
desc = CreateTemplateTupleDesc(src_natts + moreatts, tupdesc->tdhasoid);
for (i = 0; i < src_natts; i++)
{
memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_FIXED_PART_SIZE);
desc->attrs[i]->attnotnull = false;
desc->attrs[i]->atthasdef = false;
}
return desc;
}
/*
* CreateTupleDescCopyConstr
* This function creates a new TupleDesc by copying from an existing
......
......@@ -25,7 +25,7 @@
#include "executor/nodeFunctionscan.h"
#include "funcapi.h"
#include "nodes/nodeFuncs.h"
#include "catalog/pg_type.h"
static TupleTableSlot *FunctionNext(FunctionScanState *node);
......@@ -42,10 +42,37 @@ static TupleTableSlot *FunctionNext(FunctionScanState *node);
static TupleTableSlot *
FunctionNext(FunctionScanState *node)
{
TupleTableSlot *slot;
EState *estate;
ScanDirection direction;
Tuplestorestate *tuplestorestate;
TupleTableSlot *scanslot;
TupleTableSlot *funcslot;
if (node->func_slot)
{
/*
* ORDINALITY case:
*
* We fetch the function result into FUNCSLOT (which matches the
* function return type), and then copy the values to SCANSLOT
* (which matches the scan result type), setting the ordinal
* column in the process.
*/
funcslot = node->func_slot;
scanslot = node->ss.ss_ScanTupleSlot;
}
else
{
/*
* non-ORDINALITY case: the function return type and scan result
* type are the same, so we fetch the function result straight
* into the scan result slot.
*/
funcslot = node->ss.ss_ScanTupleSlot;
scanslot = NULL;
}
/*
* get information from the estate and scan state
......@@ -64,19 +91,62 @@ FunctionNext(FunctionScanState *node)
node->tuplestorestate = tuplestorestate =
ExecMakeTableFunctionResult(node->funcexpr,
node->ss.ps.ps_ExprContext,
node->tupdesc,
node->func_tupdesc,
node->eflags & EXEC_FLAG_BACKWARD);
}
/*
* Get the next tuple from tuplestore. Return NULL if no more tuples.
*/
slot = node->ss.ss_ScanTupleSlot;
(void) tuplestore_gettupleslot(tuplestorestate,
ScanDirectionIsForward(direction),
false,
slot);
return slot;
funcslot);
if (!scanslot)
return funcslot;
/*
* we're doing ordinality, so we copy the values from the function return
* slot to the (distinct) scan slot. We can do this because the lifetimes
* of the values in each slot are the same; until we reset the scan or
* fetch the next tuple, both will be valid.
*/
ExecClearTuple(scanslot);
/*
* increment or decrement before checking for end-of-data, so that we can
* move off either end of the result by 1 (and no more than 1) without
* losing correct count. See PortalRunSelect for why we assume that we
* won't be called repeatedly in the end-of-data state.
*/
if (ScanDirectionIsForward(direction))
node->ordinal++;
else
node->ordinal--;
if (!TupIsNull(funcslot))
{
int natts = funcslot->tts_tupleDescriptor->natts;
int i;
slot_getallattrs(funcslot);
for (i = 0; i < natts; ++i)
{
scanslot->tts_values[i] = funcslot->tts_values[i];
scanslot->tts_isnull[i] = funcslot->tts_isnull[i];
}
scanslot->tts_values[natts] = Int64GetDatumFast(node->ordinal);
scanslot->tts_isnull[natts] = false;
ExecStoreVirtualTuple(scanslot);
}
return scanslot;
}
/*
......@@ -116,7 +186,8 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
FunctionScanState *scanstate;
Oid funcrettype;
TypeFuncClass functypclass;
TupleDesc tupdesc = NULL;
TupleDesc func_tupdesc = NULL;
TupleDesc scan_tupdesc = NULL;
/* check for unsupported flags */
Assert(!(eflags & EXEC_FLAG_MARK));
......@@ -148,6 +219,16 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
ExecInitScanTupleSlot(estate, &scanstate->ss);
/*
* We only need a separate slot for the function result if we are doing
* ordinality; otherwise, we fetch function results directly into the
* scan slot.
*/
if (node->funcordinality)
scanstate->func_slot = ExecInitExtraTupleSlot(estate);
else
scanstate->func_slot = NULL;
/*
* initialize child expressions
*/
......@@ -159,39 +240,52 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
(PlanState *) scanstate);
/*
* Now determine if the function returns a simple or composite type, and
* build an appropriate tupdesc.
* Now determine if the function returns a simple or composite
* type, and build an appropriate tupdesc. This tupdesc
* (func_tupdesc) is the one that matches the shape of the
* function result, no extra columns.
*/
functypclass = get_expr_result_type(node->funcexpr,
&funcrettype,
&tupdesc);
&func_tupdesc);
if (functypclass == TYPEFUNC_COMPOSITE)
{
/* Composite data type, e.g. a table's row type */
Assert(tupdesc);
Assert(func_tupdesc);
/*
* XXX
* Existing behaviour is a bit inconsistent with regard to aliases and
* whole-row Vars of the function result. If the function returns a
* composite type, then the whole-row Var will refer to this tupdesc,
* which has the type's own column names rather than the alias column
* names given in the query. This affects the output of constructs like
* row_to_json which read the column names from the passed-in values.
*/
/* Must copy it out of typcache for safety */
tupdesc = CreateTupleDescCopy(tupdesc);
func_tupdesc = CreateTupleDescCopy(func_tupdesc);
}
else if (functypclass == TYPEFUNC_SCALAR)
{
/* Base data type, i.e. scalar */
char *attname = strVal(linitial(node->funccolnames));
tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(tupdesc,
func_tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(func_tupdesc,
(AttrNumber) 1,
attname,
funcrettype,
-1,
0);
TupleDescInitEntryCollation(tupdesc,
TupleDescInitEntryCollation(func_tupdesc,
(AttrNumber) 1,
exprCollation(node->funcexpr));
}
else if (functypclass == TYPEFUNC_RECORD)
{
tupdesc = BuildDescFromLists(node->funccolnames,
func_tupdesc = BuildDescFromLists(node->funccolnames,
node->funccoltypes,
node->funccoltypmods,
node->funccolcollations);
......@@ -207,15 +301,47 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
* function should do this for itself, but let's cover things in case it
* doesn't.)
*/
BlessTupleDesc(tupdesc);
BlessTupleDesc(func_tupdesc);
/*
* If doing ordinality, we need a new tupdesc with one additional column
* tacked on, always of type "bigint". The name to use has already been
* recorded by the parser as the last element of funccolnames.
*
* Without ordinality, the scan result tupdesc is the same as the
* function result tupdesc. (No need to make a copy.)
*/
if (node->funcordinality)
{
int natts = func_tupdesc->natts;
scan_tupdesc = CreateTupleDescCopyExtend(func_tupdesc, 1);
TupleDescInitEntry(scan_tupdesc,
natts + 1,
strVal(llast(node->funccolnames)),
INT8OID,
-1,
0);
BlessTupleDesc(scan_tupdesc);
}
else
scan_tupdesc = func_tupdesc;
scanstate->tupdesc = tupdesc;
ExecAssignScanType(&scanstate->ss, tupdesc);
scanstate->scan_tupdesc = scan_tupdesc;
scanstate->func_tupdesc = func_tupdesc;
ExecAssignScanType(&scanstate->ss, scan_tupdesc);
if (scanstate->func_slot)
ExecSetSlotDescriptor(scanstate->func_slot, func_tupdesc);
/*
* Other node-specific setup
*/
scanstate->ordinal = 0;
scanstate->tuplestorestate = NULL;
scanstate->funcexpr = ExecInitExpr((Expr *) node->funcexpr,
(PlanState *) scanstate);
......@@ -249,6 +375,8 @@ ExecEndFunctionScan(FunctionScanState *node)
*/
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
ExecClearTuple(node->ss.ss_ScanTupleSlot);
if (node->func_slot)
ExecClearTuple(node->func_slot);
/*
* Release tuplestore resources
......@@ -268,9 +396,13 @@ void
ExecReScanFunctionScan(FunctionScanState *node)
{
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
if (node->func_slot)
ExecClearTuple(node->func_slot);
ExecScanReScan(&node->ss);
node->ordinal = 0;
/*
* If we haven't materialized yet, just return.
*/
......
......@@ -509,6 +509,7 @@ _copyFunctionScan(const FunctionScan *from)
COPY_NODE_FIELD(funccoltypes);
COPY_NODE_FIELD(funccoltypmods);
COPY_NODE_FIELD(funccolcollations);
COPY_SCALAR_FIELD(funcordinality);
return newnode;
}
......@@ -1983,6 +1984,7 @@ _copyRangeTblEntry(const RangeTblEntry *from)
COPY_NODE_FIELD(funccoltypes);
COPY_NODE_FIELD(funccoltypmods);
COPY_NODE_FIELD(funccolcollations);
COPY_SCALAR_FIELD(funcordinality);
COPY_NODE_FIELD(values_lists);
COPY_NODE_FIELD(values_collations);
COPY_STRING_FIELD(ctename);
......@@ -2296,6 +2298,7 @@ _copyRangeFunction(const RangeFunction *from)
{
RangeFunction *newnode = makeNode(RangeFunction);
COPY_SCALAR_FIELD(ordinality);
COPY_SCALAR_FIELD(lateral);
COPY_NODE_FIELD(funccallnode);
COPY_NODE_FIELD(alias);
......
......@@ -2126,6 +2126,7 @@ _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_NODE_FIELD(alias);
......@@ -2234,6 +2235,7 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b)
COMPARE_NODE_FIELD(funccoltypes);
COMPARE_NODE_FIELD(funccoltypmods);
COMPARE_NODE_FIELD(funccolcollations);
COMPARE_SCALAR_FIELD(funcordinality);
COMPARE_NODE_FIELD(values_lists);
COMPARE_NODE_FIELD(values_collations);
COMPARE_STRING_FIELD(ctename);
......
......@@ -126,6 +126,10 @@ makeVarFromTargetEntry(Index varno,
* 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,
......@@ -151,9 +155,33 @@ makeWholeRowVar(RangeTblEntry *rte,
InvalidOid,
varlevelsup);
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 (type_is_rowtype(toid))
if (rte->funcordinality)
{
/* ORDINALITY always produces an anonymous RECORD result */
result = makeVar(varno,
InvalidAttrNumber,
RECORDOID,
-1,
InvalidOid,
varlevelsup);
}
else if (type_is_rowtype(toid))
{
/* func returns composite; same as relation case */
result = makeVar(varno,
......@@ -184,8 +212,8 @@ makeWholeRowVar(RangeTblEntry *rte,
varlevelsup);
}
break;
default:
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
......
......@@ -521,6 +521,7 @@ _outFunctionScan(StringInfo str, const FunctionScan *node)
WRITE_NODE_FIELD(funccoltypes);
WRITE_NODE_FIELD(funccoltypmods);
WRITE_NODE_FIELD(funccolcollations);
WRITE_BOOL_FIELD(funcordinality);
}
static void
......@@ -2382,6 +2383,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
WRITE_NODE_FIELD(funccoltypes);
WRITE_NODE_FIELD(funccoltypmods);
WRITE_NODE_FIELD(funccolcollations);
WRITE_BOOL_FIELD(funcordinality);
break;
case RTE_VALUES:
WRITE_NODE_FIELD(values_lists);
......@@ -2614,6 +2616,7 @@ _outRangeFunction(StringInfo str, const RangeFunction *node)
{
WRITE_NODE_TYPE("RANGEFUNCTION");
WRITE_BOOL_FIELD(ordinality);
WRITE_BOOL_FIELD(lateral);
WRITE_NODE_FIELD(funccallnode);
WRITE_NODE_FIELD(alias);
......
......@@ -1223,6 +1223,7 @@ _readRangeTblEntry(void)
READ_NODE_FIELD(funccoltypes);
READ_NODE_FIELD(funccoltypmods);
READ_NODE_FIELD(funccolcollations);
READ_BOOL_FIELD(funcordinality);
break;
case RTE_VALUES:
READ_NODE_FIELD(values_lists);
......
......@@ -115,8 +115,8 @@ static BitmapHeapScan *make_bitmap_heapscan(List *qptlist,
static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
List *tidquals);
static FunctionScan *make_functionscan(List *qptlist, List *qpqual,
Index scanrelid, Node *funcexpr, List *funccolnames,
List *funccoltypes, List *funccoltypmods,
Index scanrelid, Node *funcexpr, bool ordinality,
List *funccolnames, List *funccoltypes, List *funccoltypmods,
List *funccolcollations);
static ValuesScan *make_valuesscan(List *qptlist, List *qpqual,
Index scanrelid, List *values_lists);
......@@ -1733,6 +1733,7 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path,
scan_plan = make_functionscan(tlist, scan_clauses, scan_relid,
funcexpr,
rte->funcordinality,
rte->eref->colnames,
rte->funccoltypes,
rte->funccoltypmods,
......@@ -3366,6 +3367,7 @@ make_functionscan(List *qptlist,
List *qpqual,
Index scanrelid,
Node *funcexpr,
bool ordinality,
List *funccolnames,
List *funccoltypes,
List *funccoltypmods,
......@@ -3381,6 +3383,7 @@ make_functionscan(List *qptlist,
plan->righttree = NULL;
node->scan.scanrelid = scanrelid;
node->funcexpr = funcexpr;
node->funcordinality = ordinality;
node->funccolnames = funccolnames;
node->funccoltypes = funccoltypes;
node->funccoltypmods = funccoltypmods;
......
......@@ -4452,10 +4452,15 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
*/
check_stack_depth();
/* Fail if the caller wanted ORDINALITY - we don't implement that here. */
if (rte->funcordinality)
return NULL;
/* Fail if FROM item isn't a simple FuncExpr */
fexpr = (FuncExpr *) rte->funcexpr;
if (fexpr == NULL || !IsA(fexpr, FuncExpr))
return NULL;
func_oid = fexpr->funcid;
/*
......
......@@ -566,7 +566,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
NULLS_P NUMERIC
OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTION OPTIONS OR
ORDER OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER
ORDER ORDINALITY OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER
PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS POSITION
PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
......@@ -609,8 +609,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
* list and so can never be entered directly. The filter in parser.c
* creates these tokens when required.
*/
%token NULLS_FIRST NULLS_LAST WITH_TIME
%token NULLS_FIRST NULLS_LAST WITH_ORDINALITY WITH_TIME
/* Precedence: lowest to highest */
%nonassoc SET /* see relation_expr_opt_alias */
......@@ -9588,20 +9587,42 @@ table_ref: relation_expr opt_alias_clause
{
RangeFunction *n = makeNode(RangeFunction);
n->lateral = false;
n->ordinality = false;
n->funccallnode = $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);
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);
......@@ -12575,6 +12596,7 @@ unreserved_keyword:
| OPERATOR
| OPTION
| OPTIONS
| ORDINALITY
| OVER
| OWNED
| OWNER
......
This diff is collapsed.
......@@ -133,7 +133,7 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
case WITH:
/*
* WITH TIME must be reduced to one token
* WITH TIME and WITH ORDINALITY must each be reduced to one token
*/
cur_yylval = lvalp->core_yystype;
cur_yylloc = *llocp;
......@@ -143,6 +143,9 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
case TIME:
cur_token = WITH_TIME;
break;
case ORDINALITY:
cur_token = WITH_ORDINALITY;
break;
default:
/* save the lookahead token for next time */
yyextra->lookahead_token = next_token;
......
......@@ -8004,6 +8004,8 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
case RTE_FUNCTION:
/* Function RTE */
get_rule_expr(rte->funcexpr, context, true);
if (rte->funcordinality)
appendStringInfoString(buf, " WITH ORDINALITY");
break;
case RTE_VALUES:
/* Values list RTE */
......
......@@ -87,6 +87,7 @@ extern TupleDesc CreateTupleDesc(int natts, bool hasoid,
Form_pg_attribute *attrs);
extern TupleDesc CreateTupleDescCopy(TupleDesc tupdesc);
extern TupleDesc CreateTupleDescCopyExtend(TupleDesc tupdesc, int moreatts);
extern TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc);
......
......@@ -1395,7 +1395,10 @@ typedef struct SubqueryScanState
* function appearing in FROM (typically a function returning set).
*
* eflags node's capability flags
* tupdesc expected return tuple description
* ordinal column value for WITH ORDINALITY
* scan_tupdesc scan tuple descriptor
* func_tupdesc function tuple descriptor
* func_slot function result slot, or null
* tuplestorestate private state of tuplestore.c
* funcexpr state for function expression being evaluated
* ----------------
......@@ -1404,7 +1407,10 @@ typedef struct FunctionScanState
{
ScanState ss; /* its first field is NodeTag */
int eflags;
TupleDesc tupdesc;
int64 ordinal;
TupleDesc scan_tupdesc;
TupleDesc func_tupdesc;
TupleTableSlot *func_slot;
Tuplestorestate *tuplestorestate;
ExprState *funcexpr;
} FunctionScanState;
......
......@@ -471,6 +471,7 @@ typedef struct RangeFunction
{
NodeTag type;
bool lateral; /* does it have LATERAL prefix? */
bool ordinality; /* does it have WITH ORDINALITY suffix? */
Node *funccallnode; /* untransformed function call tree */
Alias *alias; /* table alias & optional column aliases */
List *coldeflist; /* list of ColumnDef nodes to describe result
......@@ -651,8 +652,13 @@ typedef struct XmlSerialize
* dropped columns. Note however that a stored rule may have nonempty
* colnames for columns dropped since the rule was created (and for that
* matter the colnames might be out of date due to column renamings).
*
* The same comments apply to FUNCTION RTEs when the function's return type
* is a named composite type.
* is a named composite type. In addition, for all return types, FUNCTION
* RTEs with ORDINALITY must always have the last colname entry being the
* one for the ordinal column; this is enforced when constructing the RTE.
* Thus when ORDINALITY is used, there will be exactly one more colname
* than would have been present otherwise.
*
* In JOIN RTEs, the colnames in both alias and eref are one-to-one with
* joinaliasvars entries. A JOIN RTE will omit columns of its inputs when
......@@ -751,15 +757,21 @@ typedef struct RangeTblEntry
/*
* Fields valid for a function RTE (else NULL):
*
* If the function returns RECORD, funccoltypes lists the column types
* declared in the RTE's column type specification, funccoltypmods lists
* their declared typmods, funccolcollations their collations. Otherwise,
* those fields are NIL.
* If the function returns an otherwise-unspecified RECORD, funccoltypes
* lists the column types declared in the RTE's column type specification,
* funccoltypmods lists their declared typmods, funccolcollations their
* collations. Note that in this case, ORDINALITY is not permitted, so
* there is no extra ordinal column to be allowed for.
*
* Otherwise, those fields are NIL, and the result column types must be
* derived from the funcexpr while treating the ordinal column, if
* present, as a special case. (see get_rte_attribute_*)
*/
Node *funcexpr; /* expression tree for func call */
List *funccoltypes; /* OID list of column type OIDs */
List *funccoltypmods; /* integer list of column typmods */
List *funccolcollations; /* OID list of column collation OIDs */
bool funcordinality; /* is this called WITH ORDINALITY? */
/*
* Fields valid for a values RTE (else NIL):
......
......@@ -425,6 +425,7 @@ typedef struct FunctionScan
{
Scan scan;
Node *funcexpr; /* expression tree for func call */
bool funcordinality; /* WITH ORDINALITY */
List *funccolnames; /* output column names (string Value nodes) */
List *funccoltypes; /* OID list of column type OIDs */
List *funccoltypmods; /* integer list of column typmods */
......
......@@ -269,6 +269,7 @@ PG_KEYWORD("option", OPTION, UNRESERVED_KEYWORD)
PG_KEYWORD("options", OPTIONS, UNRESERVED_KEYWORD)
PG_KEYWORD("or", OR, RESERVED_KEYWORD)
PG_KEYWORD("order", ORDER, RESERVED_KEYWORD)
PG_KEYWORD("ordinality", ORDINALITY, UNRESERVED_KEYWORD)
PG_KEYWORD("out", OUT_P, COL_NAME_KEYWORD)
PG_KEYWORD("outer", OUTER_P, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("over", OVER, UNRESERVED_KEYWORD)
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment