*** 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 ----