ordinality_20130122_2220.patch

text/plain

Filename: ordinality_20130122_2220.patch
Type: text/plain
Part: 0
Message: Patch: UNNEST (and other functions) WITH ORDINALITY

Patch

Format: context
File+
doc/src/sgml/func.sgml 25 0
doc/src/sgml/ref/select.sgml 30 0
src/backend/access/common/tupdesc.c 29 0
src/backend/executor/nodeFunctionscan.c 115 1
src/backend/nodes/copyfuncs.c 3 0
src/backend/nodes/equalfuncs.c 2 0
src/backend/nodes/makefuncs.c 2 0
src/backend/nodes/outfuncs.c 3 0
src/backend/nodes/readfuncs.c 1 0
src/backend/optimizer/plan/createplan.c 5 0
src/backend/parser/gram.y 25 0
src/backend/parser/parse_clause.c 1 0
src/backend/parser/parser.c 4 0
src/backend/parser/parse_relation.c 75 0
src/backend/utils/adt/ruleutils.c 2 0
src/include/access/tupdesc.h 1 0
src/include/nodes/execnodes.h 4 0
src/include/nodes/parsenodes.h 2 0
src/include/nodes/plannodes.h 1 0
src/include/parser/kwlist.h 1 0
src/include/parser/parse_relation.h 0 0
*** 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)
  </programlisting>
    </para>
  
*** a/doc/src/sgml/ref/select.sgml
--- b/doc/src/sgml/ref/select.sgml
***************
*** 52,58 **** 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> [, ...] ] ) 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> [, ...] ) ]
  
--- 52,59 ----
      [ 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> [, ...] ] ) [ 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> [, ...] ) ]
  
***************
*** 366,383 **** 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
          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.
         </para>
        </listitem>
       </varlistentry>
--- 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 <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 | ?column? 
! --------+----------
!  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. 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.
! 
         </para>
        </listitem>
       </varlistentry>
*** 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 ----