*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 12748,12753 **** postgres=# SELECT * FROM unnest2(ARRAY[[1,2],[3,4]]);
--- 12748,12778 ----
3
4
(4 rows)
+
+ -- unnest WITH ORDINALITY
+ postgres@postgres:5493=# 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)
*** a/doc/src/sgml/ref/select.sgml
--- b/doc/src/sgml/ref/select.sgml
***************
*** 52,58 **** SELECT [ ALL | DISTINCT [ ON ( expressiontable_name [ * ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
[ LATERAL ] ( select ) [ AS ] alias [ ( column_alias [, ...] ) ]
with_query_name [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
! [ LATERAL ] function_name ( [ argument [, ...] ] ) [ AS ] alias [ ( column_alias [, ...] | column_definition [, ...] ) ]
[ LATERAL ] function_name ( [ argument [, ...] ] ) AS ( column_definition [, ...] )
from_item [ NATURAL ] join_type from_item [ ON join_condition | USING ( join_column [, ...] ) ]
--- 52,59 ----
[ ONLY ] table_name [ * ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
[ LATERAL ] ( select ) [ AS ] alias [ ( column_alias [, ...] ) ]
with_query_name [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
! [ LATERAL ] function_name ( [ argument [, ...] ] ) [ WITH ORDINALITY ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ]
! [ LATERAL ] function_name ( [ argument [, ...] ] ) [ AS ] alias ( column_definition [, ...] )
[ LATERAL ] function_name ( [ argument [, ...] ] ) AS ( column_definition [, ...] )
from_item [ NATURAL ] join_type from_item [ ON join_condition | USING ( join_column [, ...] ) ]
***************
*** 366,383 **** TABLE [ ONLY ] table_name [ * ]
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 SELECT 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 record>
! data type, then an alias or the key word AS> must
! be present, followed by a column definition list in the form
! ( column_name data_type , ... >
! ). The column definition list must match the actual
! number and types of columns returned by the function.
--- 367,401 ----
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 SELECT command.
! When the optional WITH ORDINALITY 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:
!
! SELECT * FROM unnest(ARRAY['a','b','c','d','e','f']) WITH ORDINALITY;
! unnest | ?column?
! --------+----------
! a | 1
! b | 2
! c | 3
! d | 4
! e | 5
! f | 6
! (6 rows)
!
! 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
! record> data type, then an alias or the key word
! AS> must be present, followed by a column
! definition list in the form ( column_name data_type , ...
! >). The column definition list must match the
! actual number and types of columns returned by the function.
!
*** a/src/backend/access/common/tupdesc.c
--- b/src/backend/access/common/tupdesc.c
***************
*** 158,163 **** CreateTupleDescCopy(TupleDesc tupdesc)
--- 158,192 ----
}
/*
+ * 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.
+ *
+ * !!! 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
* TupleDesc (including its constraints and defaults).
*** a/src/backend/executor/nodeFunctionscan.c
--- b/src/backend/executor/nodeFunctionscan.c
***************
*** 25,31 ****
#include "executor/nodeFunctionscan.h"
#include "funcapi.h"
#include "nodes/nodeFuncs.h"
!
static TupleTableSlot *FunctionNext(FunctionScanState *node);
--- 25,31 ----
#include "executor/nodeFunctionscan.h"
#include "funcapi.h"
#include "nodes/nodeFuncs.h"
! #include "catalog/pg_type.h"
static TupleTableSlot *FunctionNext(FunctionScanState *node);
***************
*** 42,51 **** static TupleTableSlot *FunctionNext(FunctionScanState *node);
static TupleTableSlot *
FunctionNext(FunctionScanState *node)
{
- TupleTableSlot *slot;
EState *estate;
ScanDirection direction;
Tuplestorestate *tuplestorestate;
/*
* get information from the estate and scan state
--- 42,68 ----
static TupleTableSlot *
FunctionNext(FunctionScanState *node)
{
EState *estate;
ScanDirection direction;
Tuplestorestate *tuplestorestate;
+ TupleTableSlot *scanslot;
+ TupleTableSlot *funcslot;
+
+ if (node->func_slot)
+ {
+ /*
+ * ORDINALITY case: FUNCSLOT is the function return,
+ * SCANSLOT the scan result
+ */
+
+ funcslot = node->func_slot;
+ scanslot = node->ss.ss_ScanTupleSlot;
+ }
+ else
+ {
+ funcslot = node->ss.ss_ScanTupleSlot;
+ scanslot = NULL;
+ }
/*
* get information from the estate and scan state
***************
*** 64,82 **** FunctionNext(FunctionScanState *node)
node->tuplestorestate = tuplestorestate =
ExecMakeTableFunctionResult(node->funcexpr,
node->ss.ps.ps_ExprContext,
! node->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;
}
/*
--- 81,132 ----
node->tuplestorestate = tuplestorestate =
ExecMakeTableFunctionResult(node->funcexpr,
node->ss.ps.ps_ExprContext,
! node->func_tupdesc,
node->eflags & EXEC_FLAG_BACKWARD);
}
/*
* Get the next tuple from tuplestore. Return NULL if no more tuples.
*/
(void) tuplestore_gettupleslot(tuplestorestate,
ScanDirectionIsForward(direction),
false,
! 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);
!
! 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];
! }
!
! node->ordinal++;
!
! scanslot->tts_values[natts] = Int64GetDatumFast(node->ordinal);
! scanslot->tts_isnull[natts] = false;
!
! ExecStoreVirtualTuple(scanslot);
! }
!
! return scanslot;
}
/*
***************
*** 116,122 **** ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
FunctionScanState *scanstate;
Oid funcrettype;
TypeFuncClass functypclass;
! TupleDesc tupdesc = NULL;
/* check for unsupported flags */
Assert(!(eflags & EXEC_FLAG_MARK));
--- 166,173 ----
FunctionScanState *scanstate;
Oid funcrettype;
TypeFuncClass functypclass;
! TupleDesc func_tupdesc = NULL;
! TupleDesc scan_tupdesc = NULL;
/* check for unsupported flags */
Assert(!(eflags & EXEC_FLAG_MARK));
***************
*** 148,153 **** ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
--- 199,209 ----
ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
ExecInitScanTupleSlot(estate, &scanstate->ss);
+ if (node->funcordinality)
+ scanstate->func_slot = ExecInitExtraTupleSlot(estate);
+ else
+ scanstate->func_slot = NULL;
+
/*
* initialize child expressions
*/
***************
*** 164,200 **** ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
*/
functypclass = get_expr_result_type(node->funcexpr,
&funcrettype,
! &tupdesc);
if (functypclass == TYPEFUNC_COMPOSITE)
{
/* Composite data type, e.g. a table's row type */
! Assert(tupdesc);
/* Must copy it out of typcache for safety */
! tupdesc = CreateTupleDescCopy(tupdesc);
}
else if (functypclass == TYPEFUNC_SCALAR)
{
/* Base data type, i.e. scalar */
char *attname = strVal(linitial(node->funccolnames));
! tupdesc = CreateTemplateTupleDesc(1, false);
! TupleDescInitEntry(tupdesc,
(AttrNumber) 1,
attname,
funcrettype,
-1,
0);
! TupleDescInitEntryCollation(tupdesc,
(AttrNumber) 1,
exprCollation(node->funcexpr));
}
else if (functypclass == TYPEFUNC_RECORD)
{
! tupdesc = BuildDescFromLists(node->funccolnames,
! node->funccoltypes,
! node->funccoltypmods,
! node->funccolcollations);
}
else
{
--- 220,267 ----
*/
functypclass = get_expr_result_type(node->funcexpr,
&funcrettype,
! &func_tupdesc);
if (functypclass == TYPEFUNC_COMPOSITE)
{
/* Composite data type, e.g. a table's row type */
! 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 */
! func_tupdesc = CreateTupleDescCopy(func_tupdesc);
}
else if (functypclass == TYPEFUNC_SCALAR)
{
/* Base data type, i.e. scalar */
char *attname = strVal(linitial(node->funccolnames));
! func_tupdesc = CreateTemplateTupleDesc(1, false);
! TupleDescInitEntry(func_tupdesc,
(AttrNumber) 1,
attname,
funcrettype,
-1,
0);
! TupleDescInitEntryCollation(func_tupdesc,
(AttrNumber) 1,
exprCollation(node->funcexpr));
}
else if (functypclass == TYPEFUNC_RECORD)
{
! func_tupdesc = BuildDescFromLists(node->funccolnames,
! node->funccoltypes,
! node->funccoltypmods,
! node->funccolcollations);
}
else
{
***************
*** 207,220 **** 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);
! scanstate->tupdesc = tupdesc;
! ExecAssignScanType(&scanstate->ss, tupdesc);
/*
* Other node-specific setup
*/
scanstate->tuplestorestate = NULL;
scanstate->funcexpr = ExecInitExpr((Expr *) node->funcexpr,
(PlanState *) scanstate);
--- 274,309 ----
* function should do this for itself, but let's cover things in case it
* doesn't.)
*/
! BlessTupleDesc(func_tupdesc);
! 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);
!
! ExecSetSlotDescriptor(scanstate->func_slot, func_tupdesc);
! }
! else
! scan_tupdesc = func_tupdesc;
!
! scanstate->scan_tupdesc = scan_tupdesc;
! scanstate->func_tupdesc = func_tupdesc;
! ExecAssignScanType(&scanstate->ss, scan_tupdesc);
/*
* Other node-specific setup
*/
+ scanstate->ordinal = 0;
scanstate->tuplestorestate = NULL;
scanstate->funcexpr = ExecInitExpr((Expr *) node->funcexpr,
(PlanState *) scanstate);
***************
*** 249,254 **** ExecEndFunctionScan(FunctionScanState *node)
--- 338,345 ----
*/
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
ExecClearTuple(node->ss.ss_ScanTupleSlot);
+ if (node->func_slot)
+ ExecClearTuple(node->func_slot);
/*
* Release tuplestore resources
***************
*** 268,276 **** void
--- 359,371 ----
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.
*/
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 503,508 **** _copyFunctionScan(const FunctionScan *from)
--- 503,509 ----
* copy remainder of node
*/
COPY_NODE_FIELD(funcexpr);
+ COPY_SCALAR_FIELD(funcordinality);
COPY_NODE_FIELD(funccolnames);
COPY_NODE_FIELD(funccoltypes);
COPY_NODE_FIELD(funccoltypmods);
***************
*** 1989,1994 **** _copyRangeTblEntry(const RangeTblEntry *from)
--- 1990,1996 ----
COPY_NODE_FIELD(alias);
COPY_NODE_FIELD(eref);
COPY_SCALAR_FIELD(lateral);
+ COPY_SCALAR_FIELD(ordinality);
COPY_SCALAR_FIELD(inh);
COPY_SCALAR_FIELD(inFromCl);
COPY_SCALAR_FIELD(requiredPerms);
***************
*** 2279,2284 **** _copyRangeFunction(const RangeFunction *from)
--- 2281,2287 ----
RangeFunction *newnode = makeNode(RangeFunction);
COPY_SCALAR_FIELD(lateral);
+ COPY_SCALAR_FIELD(ordinality);
COPY_NODE_FIELD(funccallnode);
COPY_NODE_FIELD(alias);
COPY_NODE_FIELD(coldeflist);
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 2123,2128 **** static bool
--- 2123,2129 ----
_equalRangeFunction(const RangeFunction *a, const RangeFunction *b)
{
COMPARE_SCALAR_FIELD(lateral);
+ COMPARE_SCALAR_FIELD(ordinality);
COMPARE_NODE_FIELD(funccallnode);
COMPARE_NODE_FIELD(alias);
COMPARE_NODE_FIELD(coldeflist);
***************
*** 2241,2246 **** _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b)
--- 2242,2248 ----
COMPARE_NODE_FIELD(alias);
COMPARE_NODE_FIELD(eref);
COMPARE_SCALAR_FIELD(lateral);
+ COMPARE_SCALAR_FIELD(ordinality);
COMPARE_SCALAR_FIELD(inh);
COMPARE_SCALAR_FIELD(inFromCl);
COMPARE_SCALAR_FIELD(requiredPerms);
*** a/src/backend/nodes/makefuncs.c
--- b/src/backend/nodes/makefuncs.c
***************
*** 153,164 **** makeWholeRowVar(RangeTblEntry *rte,
break;
case RTE_FUNCTION:
toid = exprType(rte->funcexpr);
! if (type_is_rowtype(toid))
{
/* func returns composite; same as relation case */
result = makeVar(varno,
InvalidAttrNumber,
! toid,
-1,
InvalidOid,
varlevelsup);
--- 153,164 ----
break;
case RTE_FUNCTION:
toid = exprType(rte->funcexpr);
! if (type_is_rowtype(toid) || rte->ordinality)
{
/* func returns composite; same as relation case */
result = makeVar(varno,
InvalidAttrNumber,
! (rte->ordinality) ? RECORDOID : toid,
-1,
InvalidOid,
varlevelsup);
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
***************
*** 515,520 **** _outFunctionScan(StringInfo str, const FunctionScan *node)
--- 515,521 ----
_outScanInfo(str, (const Scan *) node);
WRITE_NODE_FIELD(funcexpr);
+ WRITE_BOOL_FIELD(funcordinality);
WRITE_NODE_FIELD(funccolnames);
WRITE_NODE_FIELD(funccoltypes);
WRITE_NODE_FIELD(funccoltypmods);
***************
*** 2384,2389 **** _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
--- 2385,2391 ----
}
WRITE_BOOL_FIELD(lateral);
+ WRITE_BOOL_FIELD(ordinality);
WRITE_BOOL_FIELD(inh);
WRITE_BOOL_FIELD(inFromCl);
WRITE_UINT_FIELD(requiredPerms);
***************
*** 2598,2603 **** _outRangeFunction(StringInfo str, const RangeFunction *node)
--- 2600,2606 ----
WRITE_NODE_TYPE("RANGEFUNCTION");
WRITE_BOOL_FIELD(lateral);
+ WRITE_BOOL_FIELD(ordinality);
WRITE_NODE_FIELD(funccallnode);
WRITE_NODE_FIELD(alias);
WRITE_NODE_FIELD(coldeflist);
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
***************
*** 1224,1229 **** _readRangeTblEntry(void)
--- 1224,1230 ----
}
READ_BOOL_FIELD(lateral);
+ READ_BOOL_FIELD(ordinality);
READ_BOOL_FIELD(inh);
READ_BOOL_FIELD(inFromCl);
READ_UINT_FIELD(requiredPerms);
*** a/src/backend/optimizer/plan/createplan.c
--- b/src/backend/optimizer/plan/createplan.c
***************
*** 114,121 **** 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,
List *funccolcollations);
static ValuesScan *make_valuesscan(List *qptlist, List *qpqual,
Index scanrelid, List *values_lists);
--- 114,121 ----
static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
List *tidquals);
static FunctionScan *make_functionscan(List *qptlist, List *qpqual,
! 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);
***************
*** 1723,1728 **** create_functionscan_plan(PlannerInfo *root, Path *best_path,
--- 1723,1729 ----
scan_plan = make_functionscan(tlist, scan_clauses, scan_relid,
funcexpr,
+ rte->ordinality,
rte->eref->colnames,
rte->funccoltypes,
rte->funccoltypmods,
***************
*** 3356,3361 **** make_functionscan(List *qptlist,
--- 3357,3363 ----
List *qpqual,
Index scanrelid,
Node *funcexpr,
+ bool ordinality,
List *funccolnames,
List *funccoltypes,
List *funccoltypmods,
***************
*** 3371,3376 **** make_functionscan(List *qptlist,
--- 3373,3379 ----
plan->righttree = NULL;
node->scan.scanrelid = scanrelid;
node->funcexpr = funcexpr;
+ node->funcordinality = ordinality;
node->funccolnames = funccolnames;
node->funccoltypes = funccoltypes;
node->funccoltypmods = funccoltypmods;
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 562,568 **** static void processCASbits(int cas_bits, int location, const char *constrType,
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
PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS POSITION
PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
--- 562,568 ----
NULLS_P NUMERIC
OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTION OPTIONS OR
! 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
***************
*** 591,597 **** static void processCASbits(int cas_bits, int location, const char *constrType,
VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
VERBOSE VERSION_P VIEW VOLATILE
! WHEN WHERE WHITESPACE_P WINDOW WITH WITHOUT WORK WRAPPER WRITE
XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLEXISTS XMLFOREST XMLPARSE
XMLPI XMLROOT XMLSERIALIZE
--- 591,597 ----
VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
VERBOSE VERSION_P VIEW VOLATILE
! WHEN WHERE WHITESPACE_P WINDOW WITH WITH_ORDINALITY WITHOUT WORK WRAPPER WRITE
XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLEXISTS XMLFOREST XMLPARSE
XMLPI XMLROOT XMLSERIALIZE
***************
*** 9391,9410 **** table_ref: relation_expr opt_alias_clause
--- 9391,9432 ----
{
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);
***************
*** 12583,12588 **** unreserved_keyword:
--- 12605,12611 ----
| OPERATOR
| OPTION
| OPTIONS
+ | ORDINALITY
| OWNED
| OWNER
| PARSER
*** a/src/backend/parser/parse_clause.c
--- b/src/backend/parser/parse_clause.c
***************
*** 536,543 **** transformRangeFunction(ParseState *pstate, RangeFunction *r)
/*
* OK, build an RTE for the function.
*/
! rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr,
! r, r->lateral, true);
/*
* If a coldeflist was supplied, ensure it defines a legal set of names
--- 536,542 ----
/*
* OK, build an RTE for the function.
*/
! rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr, r, true);
/*
* If a coldeflist was supplied, ensure it defines a legal set of names
*** a/src/backend/parser/parse_relation.c
--- b/src/backend/parser/parse_relation.c
***************
*** 796,802 **** markVarForSelectPriv(ParseState *pstate, Var *var, RangeTblEntry *rte)
* physical column numbers.
*/
static void
! buildRelationAliases(TupleDesc tupdesc, Alias *alias, Alias *eref)
{
int maxattrs = tupdesc->natts;
ListCell *aliaslc;
--- 796,802 ----
* physical column numbers.
*/
static void
! buildRelationAliases(TupleDesc tupdesc, Alias *alias, Alias *eref, bool ordinality)
{
int maxattrs = tupdesc->natts;
ListCell *aliaslc;
***************
*** 848,853 **** buildRelationAliases(TupleDesc tupdesc, Alias *alias, Alias *eref)
--- 848,872 ----
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("?column?"));
+ }
+
+ eref->colnames = lappend(eref->colnames, attrname);
+ }
+
/* Too many user-supplied aliases? */
if (aliaslc)
ereport(ERROR,
***************
*** 870,912 **** buildRelationAliases(TupleDesc tupdesc, Alias *alias, Alias *eref)
*/
static void
buildScalarFunctionAlias(Node *funcexpr, char *funcname,
! Alias *alias, Alias *eref)
{
- 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)
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))
{
! pname = get_func_result_name(((FuncExpr *) funcexpr)->funcid);
! if (pname)
! {
! eref->colnames = list_make1(makeString(pname));
! return;
! }
}
! /*
! * Otherwise use the previously-determined alias (not necessarily the
! * function name!)
! */
! eref->colnames = list_make1(makeString(eref->aliasname));
}
/*
--- 889,930 ----
*/
static void
buildScalarFunctionAlias(Node *funcexpr, char *funcname,
! Alias *alias, Alias *eref, bool ordinality)
{
Assert(eref->colnames == NIL);
/* 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
{
! 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);
!
! if (!pname)
! pname = eref->aliasname;
!
! eref->colnames = list_make1(makeString(pname));
}
! if (ordinality && list_length(eref->colnames) < 2)
! eref->colnames = lappend(eref->colnames, makeString(pstrdup("?column?")));
!
! return;
}
/*
***************
*** 1002,1008 **** addRangeTableEntry(ParseState *pstate,
* and/or actual column names.
*/
rte->eref = makeAlias(refname, NIL);
! buildRelationAliases(rel->rd_att, alias, rte->eref);
/*
* Drop the rel refcount, but keep the access lock till end of transaction
--- 1020,1026 ----
* and/or actual column names.
*/
rte->eref = makeAlias(refname, NIL);
! buildRelationAliases(rel->rd_att, alias, rte->eref, false);
/*
* Drop the rel refcount, but keep the access lock till end of transaction
***************
*** 1062,1068 **** addRangeTableEntryForRelation(ParseState *pstate,
* and/or actual column names.
*/
rte->eref = makeAlias(refname, NIL);
! buildRelationAliases(rel->rd_att, alias, rte->eref);
/*
* Set flags and access permissions.
--- 1080,1086 ----
* and/or actual column names.
*/
rte->eref = makeAlias(refname, NIL);
! buildRelationAliases(rel->rd_att, alias, rte->eref, false);
/*
* Set flags and access permissions.
***************
*** 1177,1183 **** addRangeTableEntryForFunction(ParseState *pstate,
char *funcname,
Node *funcexpr,
RangeFunction *rangefunc,
- bool lateral,
bool inFromCl)
{
RangeTblEntry *rte = makeNode(RangeTblEntry);
--- 1195,1200 ----
***************
*** 1233,1249 **** addRangeTableEntryForFunction(ParseState *pstate,
/* Composite data type, e.g. a table's row type */
Assert(tupdesc);
/* Build the column alias list */
! buildRelationAliases(tupdesc, alias, eref);
}
else if (functypclass == TYPEFUNC_SCALAR)
{
/* Base data type, i.e. scalar */
! buildScalarFunctionAlias(funcexpr, funcname, alias, eref);
}
else if (functypclass == TYPEFUNC_RECORD)
{
ListCell *col;
/*
* Use the column definition list to form the alias list and
* funccoltypes/funccoltypmods/funccolcollations lists.
--- 1250,1272 ----
/* 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);
}
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.
***************
*** 1285,1291 **** addRangeTableEntryForFunction(ParseState *pstate,
* Functions are never checked for access rights (at least, not by the RTE
* permissions mechanism).
*/
! rte->lateral = lateral;
rte->inh = false; /* never true for functions */
rte->inFromCl = inFromCl;
--- 1308,1315 ----
* Functions are never checked for access rights (at least, not by the RTE
* permissions mechanism).
*/
! rte->lateral = rangefunc->lateral;
! rte->ordinality = rangefunc->ordinality;
rte->inh = false; /* never true for functions */
rte->inFromCl = inFromCl;
***************
*** 1709,1714 **** expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
--- 1733,1739 ----
TypeFuncClass functypclass;
Oid funcrettype;
TupleDesc tupdesc;
+ int ordattno = 0;
functypclass = get_expr_result_type(rte->funcexpr,
&funcrettype,
***************
*** 1720,1725 **** expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
--- 1745,1751 ----
expandTupleDesc(tupdesc, rte->eref,
rtindex, sublevels_up, location,
include_dropped, colnames, colvars);
+ ordattno = tupdesc->natts + 1;
}
else if (functypclass == TYPEFUNC_SCALAR)
{
***************
*** 1740,1745 **** expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
--- 1766,1773 ----
*colvars = lappend(*colvars, varnode);
}
+
+ ordattno = 2;
}
else if (functypclass == TYPEFUNC_RECORD)
{
***************
*** 1778,1783 **** expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
--- 1806,1829 ----
/* addRangeTableEntryForFunction should've caught this */
elog(ERROR, "function in FROM has unsupported return type");
}
+
+ /* tack on the extra ordinality column if present */
+ if (rte->ordinality)
+ {
+ if (colnames)
+ *colnames = lappend(*colnames, llast(rte->eref->colnames));
+
+ if (colvars)
+ {
+ Var *varnode = makeVar(rtindex,
+ ordattno,
+ INT8OID,
+ -1,
+ InvalidOid,
+ sublevels_up);
+ *colvars = lappend(*colvars, varnode);
+ }
+ }
}
break;
case RTE_VALUES:
*** a/src/backend/parser/parser.c
--- b/src/backend/parser/parser.c
***************
*** 133,139 **** base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
case WITH:
/*
! * WITH TIME must be reduced to one token
*/
cur_yylval = lvalp->core_yystype;
cur_yylloc = *llocp;
--- 133,139 ----
case WITH:
/*
! * WITH TIME and WITH ORDINALITY must each be reduced to one token
*/
cur_yylval = lvalp->core_yystype;
cur_yylloc = *llocp;
***************
*** 143,148 **** base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
--- 143,151 ----
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;
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
***************
*** 7938,7943 **** get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
--- 7938,7945 ----
case RTE_FUNCTION:
/* Function RTE */
get_rule_expr(rte->funcexpr, context, true);
+ if (rte->ordinality)
+ appendStringInfoString(buf, " WITH ORDINALITY");
break;
case RTE_VALUES:
/* Values list RTE */
*** a/src/include/access/tupdesc.h
--- b/src/include/access/tupdesc.h
***************
*** 87,92 **** extern TupleDesc CreateTupleDesc(int natts, bool hasoid,
--- 87,93 ----
Form_pg_attribute *attrs);
extern TupleDesc CreateTupleDescCopy(TupleDesc tupdesc);
+ extern TupleDesc CreateTupleDescCopyExtend(TupleDesc tupdesc, int moreatts);
extern TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc);
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
***************
*** 1393,1399 **** typedef struct FunctionScanState
{
ScanState ss; /* its first field is NodeTag */
int eflags;
! TupleDesc tupdesc;
Tuplestorestate *tuplestorestate;
ExprState *funcexpr;
} FunctionScanState;
--- 1393,1402 ----
{
ScanState ss; /* its first field is NodeTag */
int eflags;
! int64 ordinal;
! TupleDesc scan_tupdesc;
! TupleDesc func_tupdesc;
! TupleTableSlot *func_slot;
Tuplestorestate *tuplestorestate;
ExprState *funcexpr;
} FunctionScanState;
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 463,468 **** typedef struct RangeFunction
--- 463,469 ----
{
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
***************
*** 759,764 **** typedef struct RangeTblEntry
--- 760,766 ----
Alias *alias; /* user-written alias clause, if any */
Alias *eref; /* expanded reference names */
bool lateral; /* subquery, function, or values is LATERAL? */
+ bool ordinality; /* function WITH ORDINALITY? */
bool inh; /* inheritance requested? */
bool inFromCl; /* present in FROM clause? */
AclMode requiredPerms; /* bitmask of required access permissions */
*** a/src/include/nodes/plannodes.h
--- b/src/include/nodes/plannodes.h
***************
*** 423,428 **** typedef struct FunctionScan
--- 423,429 ----
{
Scan scan;
Node *funcexpr; /* expression tree for func call */
+ bool funcordinality;
List *funccolnames; /* output column names (string Value nodes) */
List *funccoltypes; /* OID list of column type OIDs */
List *funccoltypmods; /* integer list of column typmods */
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
***************
*** 267,272 **** PG_KEYWORD("option", OPTION, UNRESERVED_KEYWORD)
--- 267,273 ----
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, TYPE_FUNC_NAME_KEYWORD)
*** a/src/include/parser/parse_relation.h
--- b/src/include/parser/parse_relation.h
***************
*** 61,67 **** extern RangeTblEntry *addRangeTableEntryForFunction(ParseState *pstate,
char *funcname,
Node *funcexpr,
RangeFunction *rangefunc,
- bool lateral,
bool inFromCl);
extern RangeTblEntry *addRangeTableEntryForValues(ParseState *pstate,
List *exprs,
--- 61,66 ----