*** a/src/backend/executor/execQual.c
--- b/src/backend/executor/execQual.c
***************
*** 4410,4415 **** ExecInitExpr(Expr *node, PlanState *parent)
--- 4410,4416 ----
  
  					astate->args = (List *) ExecInitExpr((Expr *) aggref->args,
  														 parent);
+ 					astate->orddirectargs = (List *) ExecInitExpr((Expr *) aggref->orddirectargs, parent);
  					astate->aggfilter = ExecInitExpr(aggref->aggfilter,
  													 parent);
  
*** a/src/backend/executor/functions.c
--- b/src/backend/executor/functions.c
***************
*** 381,387 **** sql_fn_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var)
  								  list_make1(subfield),
  								  list_make1(param),
  								  NIL, NULL, false, false, false,
! 								  NULL, true, cref->location);
  	}
  
  	return param;
--- 381,387 ----
  								  list_make1(subfield),
  								  list_make1(param),
  								  NIL, NULL, false, false, false,
! 								  false, NULL, true, cref->location);
  	}
  
  	return param;
*** a/src/backend/executor/nodeAgg.c
--- b/src/backend/executor/nodeAgg.c
***************
*** 105,110 ****
--- 105,112 ----
   */
  typedef struct AggStatePerAggData
  {
+ 	NodeTag type;
+ 	
  	/*
  	 * These values are set up during ExecInitAgg() and do not change
  	 * thereafter:
***************
*** 114,119 **** typedef struct AggStatePerAggData
--- 116,124 ----
  	AggrefExprState *aggrefstate;
  	Aggref	   *aggref;
  
+ 	/* Pointer to parent AggState node */
+ 	AggState   *aggstate;
+ 
  	/* number of input arguments for aggregate function proper */
  	int			numArguments;
  
***************
*** 126,137 **** typedef struct AggStatePerAggData
  
  	/*
  	 * fmgr lookup data for transfer functions --- only valid when
! 	 * corresponding oid is not InvalidOid.  Note in particular that fn_strict
! 	 * flags are kept here.
  	 */
  	FmgrInfo	transfn;
  	FmgrInfo	finalfn;
  
  	/* Input collation derived for aggregate */
  	Oid			aggCollation;
  
--- 131,152 ----
  
  	/*
  	 * fmgr lookup data for transfer functions --- only valid when
! 	 * corresponding oid is not InvalidOid.
  	 */
  	FmgrInfo	transfn;
  	FmgrInfo	finalfn;
  
+ 	/*
+ 	 * If >0, aggregate as a whole is strict (skips null input)
+ 	 * The value specifies how many columns to check; normal aggs
+ 	 * only check numArguments, while ordered set functions check
+ 	 * numInputs.
+ 	 *
+ 	 * Ordered set functions have a separate strictness state for
+ 	 * the finalfn, which is tested separately.
+ 	 */
+ 	int         numStrict;
+ 
  	/* Input collation derived for aggregate */
  	Oid			aggCollation;
  
***************
*** 204,209 **** typedef struct AggStatePerAggData
--- 219,227 ----
  	 */
  
  	Tuplesortstate *sortstate;	/* sort object, if DISTINCT or ORDER BY */
+ 
+ 	int64 number_of_rows;		/* number of rows */
+ 
  }	AggStatePerAggData;
  
  /*
***************
*** 300,305 **** initialize_aggregates(AggState *aggstate,
--- 318,325 ----
  		AggStatePerAgg peraggstate = &peragg[aggno];
  		AggStatePerGroup pergroupstate = &pergroup[aggno];
  
+ 		peraggstate->number_of_rows = 0;
+ 
  		/*
  		 * Start a fresh sort operation for each DISTINCT/ORDER BY aggregate.
  		 */
***************
*** 383,396 **** advance_transition_function(AggState *aggstate,
  	MemoryContext oldContext;
  	Datum		newVal;
  	int			i;
  
! 	if (peraggstate->transfn.fn_strict)
  	{
  		/*
  		 * For a strict transfn, nothing happens when there's a NULL input; we
  		 * just keep the prior transValue.
  		 */
! 		for (i = 1; i <= numArguments; i++)
  		{
  			if (fcinfo->argnull[i])
  				return;
--- 403,419 ----
  	MemoryContext oldContext;
  	Datum		newVal;
  	int			i;
+ 	int         numStrict = peraggstate->numStrict;
  
! 	Assert(OidIsValid(peraggstate->transfn_oid));
! 
! 	if (numStrict > 0)
  	{
  		/*
  		 * For a strict transfn, nothing happens when there's a NULL input; we
  		 * just keep the prior transValue.
  		 */
! 		for (i = 1; i <= numStrict; i++)
  		{
  			if (fcinfo->argnull[i])
  				return;
***************
*** 506,529 **** advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup)
  
  		if (peraggstate->numSortCols > 0)
  		{
  			/* DISTINCT and/or ORDER BY case */
  			Assert(slot->tts_nvalid == peraggstate->numInputs);
  
  			/*
  			 * If the transfn is strict, we want to check for nullity before
  			 * storing the row in the sorter, to save space if there are a lot
! 			 * of nulls.  Note that we must only check numArguments columns,
! 			 * not numInputs, since nullity in columns used only for sorting
! 			 * is not relevant here.
  			 */
! 			if (peraggstate->transfn.fn_strict)
  			{
! 				for (i = 0; i < nargs; i++)
  				{
  					if (slot->tts_isnull[i])
  						break;
  				}
! 				if (i < nargs)
  					continue;
  			}
  
--- 529,552 ----
  
  		if (peraggstate->numSortCols > 0)
  		{
+ 			int numStrict = peraggstate->numStrict;
+ 
  			/* DISTINCT and/or ORDER BY case */
  			Assert(slot->tts_nvalid == peraggstate->numInputs);
  
  			/*
  			 * If the transfn is strict, we want to check for nullity before
  			 * storing the row in the sorter, to save space if there are a lot
! 			 * of nulls.
  			 */
! 			if (numStrict > 0)
  			{
! 				for (i = 0; i < numStrict; i++)
  				{
  					if (slot->tts_isnull[i])
  						break;
  				}
! 				if (i < numStrict)
  					continue;
  			}
  
***************
*** 534,539 **** advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup)
--- 557,564 ----
  								   slot->tts_isnull[0]);
  			else
  				tuplesort_puttupleslot(peraggstate->sortstate, slot);
+ 
+ 			++peraggstate->number_of_rows;
  		}
  		else
  		{
***************
*** 756,768 **** finalize_aggregate(AggState *aggstate,
  	if (OidIsValid(peraggstate->finalfn_oid))
  	{
  		FunctionCallInfoData fcinfo;
  
! 		InitFunctionCallInfoData(fcinfo, &(peraggstate->finalfn), 1,
! 								 peraggstate->aggCollation,
! 								 (void *) aggstate, NULL);
! 		fcinfo.arg[0] = pergroupstate->transValue;
! 		fcinfo.argnull[0] = pergroupstate->transValueIsNull;
! 		if (fcinfo.flinfo->fn_strict && pergroupstate->transValueIsNull)
  		{
  			/* don't call a strict function with NULL inputs */
  			*resultVal = (Datum) 0;
--- 781,820 ----
  	if (OidIsValid(peraggstate->finalfn_oid))
  	{
  		FunctionCallInfoData fcinfo;
+ 		bool isnull = false;
+ 
+ 		if (!(peraggstate->aggref->isordset))
+ 		{
+ 			InitFunctionCallInfoData(fcinfo, &(peraggstate->finalfn), 1,
+ 								 	peraggstate->aggCollation,
+ 								 	(void *) aggstate, NULL);
+ 
+ 			fcinfo.arg[0] = pergroupstate->transValue;
+ 			fcinfo.argnull[0] = isnull = pergroupstate->transValueIsNull;
+ 		}
+ 		else
+ 		{
+ 			List     *args = peraggstate->aggrefstate->orddirectargs;
+ 			ListCell *lc;
+ 			int i = 0;
+ 
+ 			InitFunctionCallInfoData(fcinfo, &(peraggstate->finalfn), list_length(args),
+ 								 	peraggstate->aggCollation,
+ 								 	(void *) peraggstate, NULL);
+ 
+ 			foreach (lc, args)
+ 			{
+ 				ExprState *expr = (ExprState *) lfirst(lc);
+ 
+ 				fcinfo.arg[i] = ExecEvalExpr(expr, aggstate->ss.ps.ps_ExprContext,  &fcinfo.argnull[i], NULL);
+ 				if (fcinfo.argnull[i])
+ 					isnull = true;
  
! 				++i;
! 			}
! 		}
! 
! 		if (isnull && fcinfo.flinfo->fn_strict)
  		{
  			/* don't call a strict function with NULL inputs */
  			*resultVal = (Datum) 0;
***************
*** 1164,1169 **** agg_retrieve_direct(AggState *aggstate)
--- 1216,1232 ----
  		}
  
  		/*
+ 		 * Use the representative input tuple for any references to
+ 		 * non-aggregated input columns in the qual and tlist.	(If we are not
+ 		 * grouping, and there are no input rows at all, we will come here
+ 		 * with an empty firstSlot ... but if not grouping, there can't be any
+ 		 * references to non-aggregated input columns, so no problem.)
+ 		 * We do this before finalizing because for ordered set functions,
+ 		 * finalize_aggregates can evaluate arguments referencing the tuple.
+ 		 */
+ 		econtext->ecxt_outertuple = firstSlot;
+ 
+ 		/*
  		 * Done scanning input tuple group. Finalize each aggregate
  		 * calculation, and stash results in the per-output-tuple context.
  		 */
***************
*** 1174,1187 **** agg_retrieve_direct(AggState *aggstate)
  
  			if (peraggstate->numSortCols > 0)
  			{
! 				if (peraggstate->numInputs == 1)
! 					process_ordered_aggregate_single(aggstate,
! 													 peraggstate,
! 													 pergroupstate);
! 				else
! 					process_ordered_aggregate_multi(aggstate,
! 													peraggstate,
! 													pergroupstate);
  			}
  
  			finalize_aggregate(aggstate, peraggstate, pergroupstate,
--- 1237,1253 ----
  
  			if (peraggstate->numSortCols > 0)
  			{
! 				if (!(peraggstate->aggref->isordset))
! 				{
! 					if (peraggstate->numInputs == 1)
! 						process_ordered_aggregate_single(aggstate,
! 														 peraggstate,
! 														 pergroupstate);
! 					else
! 						process_ordered_aggregate_multi(aggstate,
! 														peraggstate,
! 														pergroupstate);
! 				}
  			}
  
  			finalize_aggregate(aggstate, peraggstate, pergroupstate,
***************
*** 1189,1203 **** agg_retrieve_direct(AggState *aggstate)
  		}
  
  		/*
- 		 * Use the representative input tuple for any references to
- 		 * non-aggregated input columns in the qual and tlist.	(If we are not
- 		 * grouping, and there are no input rows at all, we will come here
- 		 * with an empty firstSlot ... but if not grouping, there can't be any
- 		 * references to non-aggregated input columns, so no problem.)
- 		 */
- 		econtext->ecxt_outertuple = firstSlot;
- 
- 		/*
  		 * Check the qual (HAVING clause); if the group does not match, ignore
  		 * it and loop back to try to process another group.
  		 */
--- 1255,1260 ----
***************
*** 1580,1585 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
--- 1637,1643 ----
  		Datum		textInitVal;
  		int			i;
  		ListCell   *lc;
+         bool        is_strict;
  
  		/* Planner should have assigned aggregate to correct level */
  		Assert(aggref->agglevelsup == 0);
***************
*** 1601,1606 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
--- 1659,1667 ----
  		/* Nope, so assign a new PerAgg record */
  		peraggstate = &peragg[++aggno];
  
+ 		peraggstate->type = T_AggStatePerAggData;
+ 		peraggstate->aggstate = aggstate;
+ 
  		/* Mark Aggref state node with assigned index in the result array */
  		aggrefstate->aggno = aggno;
  
***************
*** 1644,1668 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
  		peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
  		peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
  
! 		/* Check that aggregate owner has permission to call component fns */
  		{
  			HeapTuple	procTuple;
  			Oid			aggOwner;
  
  			procTuple = SearchSysCache1(PROCOID,
  										ObjectIdGetDatum(aggref->aggfnoid));
  			if (!HeapTupleIsValid(procTuple))
  				elog(ERROR, "cache lookup failed for function %u",
  					 aggref->aggfnoid);
! 			aggOwner = ((Form_pg_proc) GETSTRUCT(procTuple))->proowner;
  			ReleaseSysCache(procTuple);
  
! 			aclresult = pg_proc_aclcheck(transfn_oid, aggOwner,
! 										 ACL_EXECUTE);
! 			if (aclresult != ACLCHECK_OK)
  				aclcheck_error(aclresult, ACL_KIND_PROC,
  							   get_func_name(transfn_oid));
! 			InvokeFunctionExecuteHook(transfn_oid);
  			if (OidIsValid(finalfn_oid))
  			{
  				aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner,
--- 1705,1741 ----
  		peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
  		peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
  
! 		/*
! 		 * Check that aggregate owner has permission to call component fns
! 		 * In passing, fetch the proisstrict flag for the aggregate proper,
! 		 * which subs for the transfn's strictness flag in cases where there
! 		 * is no transfn.
! 		 */
  		{
  			HeapTuple	procTuple;
  			Oid			aggOwner;
+ 			Form_pg_proc procp;
  
  			procTuple = SearchSysCache1(PROCOID,
  										ObjectIdGetDatum(aggref->aggfnoid));
  			if (!HeapTupleIsValid(procTuple))
  				elog(ERROR, "cache lookup failed for function %u",
  					 aggref->aggfnoid);
! 			procp = (Form_pg_proc) GETSTRUCT(procTuple);
! 			aggOwner = procp->proowner;
! 			is_strict = procp->proisstrict;
  			ReleaseSysCache(procTuple);
  
! 			if (OidIsValid(transfn_oid))
! 			{
! 				aclresult = pg_proc_aclcheck(transfn_oid, aggOwner,
! 									         ACL_EXECUTE);
! 				if (aclresult != ACLCHECK_OK && OidIsValid(transfn_oid))
  				aclcheck_error(aclresult, ACL_KIND_PROC,
  							   get_func_name(transfn_oid));
! 				InvokeFunctionExecuteHook(transfn_oid);
! 			}
! 
  			if (OidIsValid(finalfn_oid))
  			{
  				aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner,
***************
*** 1676,1682 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
  
  		/* resolve actual type of transition state, if polymorphic */
  		aggtranstype = aggform->aggtranstype;
! 		if (IsPolymorphicType(aggtranstype))
  		{
  			/* have to fetch the agg's declared input types... */
  			Oid		   *declaredArgTypes;
--- 1749,1755 ----
  
  		/* resolve actual type of transition state, if polymorphic */
  		aggtranstype = aggform->aggtranstype;
! 		if (OidIsValid(aggtranstype) && IsPolymorphicType(aggtranstype))
  		{
  			/* have to fetch the agg's declared input types... */
  			Oid		   *declaredArgTypes;
***************
*** 1704,1711 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
  								&transfnexpr,
  								&finalfnexpr);
  
! 		fmgr_info(transfn_oid, &peraggstate->transfn);
! 		fmgr_info_set_expr((Node *) transfnexpr, &peraggstate->transfn);
  
  		if (OidIsValid(finalfn_oid))
  		{
--- 1777,1789 ----
  								&transfnexpr,
  								&finalfnexpr);
  
! 		if (OidIsValid(transfn_oid))
! 		{
! 			fmgr_info(transfn_oid, &peraggstate->transfn);
! 			fmgr_info_set_expr((Node *) transfnexpr, &peraggstate->transfn);
! 
! 			is_strict = peraggstate->transfn.fn_strict;
! 		}
  
  		if (OidIsValid(finalfn_oid))
  		{
***************
*** 1713,1726 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
  			fmgr_info_set_expr((Node *) finalfnexpr, &peraggstate->finalfn);
  		}
  
  		peraggstate->aggCollation = aggref->inputcollid;
  
  		get_typlenbyval(aggref->aggtype,
  						&peraggstate->resulttypeLen,
  						&peraggstate->resulttypeByVal);
! 		get_typlenbyval(aggtranstype,
! 						&peraggstate->transtypeLen,
! 						&peraggstate->transtypeByVal);
  
  		/*
  		 * initval is potentially null, so don't try to access it as a struct
--- 1791,1814 ----
  			fmgr_info_set_expr((Node *) finalfnexpr, &peraggstate->finalfn);
  		}
  
+ 		if (is_strict)
+ 		{
+ 			peraggstate->numStrict = (peraggstate->aggref->isordset ? numInputs : numArguments);
+ 		}
+ 		else
+ 			peraggstate->numStrict = 0;
+ 
  		peraggstate->aggCollation = aggref->inputcollid;
  
  		get_typlenbyval(aggref->aggtype,
  						&peraggstate->resulttypeLen,
  						&peraggstate->resulttypeByVal);
! 		if (OidIsValid(aggtranstype))
! 		{
! 			get_typlenbyval(aggtranstype,
! 							&peraggstate->transtypeLen,
! 							&peraggstate->transtypeByVal);
! 		}
  
  		/*
  		 * initval is potentially null, so don't try to access it as a struct
***************
*** 1743,1749 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
  		 * transValue.	This should have been checked at agg definition time,
  		 * but just in case...
  		 */
! 		if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull)
  		{
  			if (numArguments < 1 ||
  				!IsBinaryCoercible(inputTypes[0], aggtranstype))
--- 1831,1839 ----
  		 * transValue.	This should have been checked at agg definition time,
  		 * but just in case...
  		 */
! 		if (OidIsValid(peraggstate->transfn_oid)
! 			&& peraggstate->transfn.fn_strict
! 			&& peraggstate->initValueIsNull)
  		{
  			if (numArguments < 1 ||
  				!IsBinaryCoercible(inputTypes[0], aggtranstype))
***************
*** 1804,1810 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
  			/* If we have only one input, we need its len/byval info. */
  			if (numInputs == 1)
  			{
! 				get_typlenbyval(inputTypes[0],
  								&peraggstate->inputtypeLen,
  								&peraggstate->inputtypeByVal);
  			}
--- 1894,1900 ----
  			/* If we have only one input, we need its len/byval info. */
  			if (numInputs == 1)
  			{
! 				get_typlenbyval(peraggstate->evaldesc->attrs[0]->atttypid,
  								&peraggstate->inputtypeLen,
  								&peraggstate->inputtypeByVal);
  			}
***************
*** 2039,2044 **** AggCheckCallContext(FunctionCallInfo fcinfo, MemoryContext *aggcontext)
--- 2129,2141 ----
  		return AGG_CONTEXT_WINDOW;
  	}
  
+ 	if (fcinfo->context && IsA(fcinfo->context, AggStatePerAggData))
+ 	{
+ 		if (aggcontext)
+ 			*aggcontext = ((AggStatePerAggData *) fcinfo->context)->aggstate->aggcontext;
+ 		return AGG_CONTEXT_ORDERED;
+ 	}
+ 
  	/* this is just to prevent "uninitialized variable" warnings */
  	if (aggcontext)
  		*aggcontext = NULL;
***************
*** 2062,2064 **** aggregate_dummy(PG_FUNCTION_ARGS)
--- 2159,2209 ----
  		 fcinfo->flinfo->fn_oid);
  	return (Datum) 0;			/* keep compiler quiet */
  }
+ 
+ /* AggSetGetRowCount - Get the number of rows in case of ordered set
+  * functions.
+  */
+ int64
+ AggSetGetRowCount(FunctionCallInfo fcinfo)
+ {
+ 	if (fcinfo->context && IsA(fcinfo->context, AggStatePerAggData))
+ 	{
+ 		return ((AggStatePerAggData *)fcinfo->context)->number_of_rows;
+ 	}
+ 
+ 	elog(ERROR, "Called AggSetGetRowCount on non ordered set function");
+ 	return -1;
+ }
+ 
+ /* AggSetGetSortInfo - Get the sort state in the case of 
+  * ordered set functions.
+  */
+ void
+ AggSetGetSortInfo(FunctionCallInfo fcinfo, Tuplesortstate **sortstate, TupleDesc *tupdesc, TupleTableSlot **tupslot, Oid *datumtype)
+ {
+ 	if (fcinfo->context && IsA(fcinfo->context, AggStatePerAggData))
+ 	{
+ 		AggStatePerAggData *peraggstate = (AggStatePerAggData *) fcinfo->context;
+ 
+ 		*sortstate = peraggstate->sortstate;
+ 		if (peraggstate->numInputs == 1)
+ 		{
+ 			if (tupdesc)
+ 				*tupdesc = NULL;
+ 			*datumtype = peraggstate->evaldesc->attrs[0]->atttypid;
+ 		}
+ 		else
+ 		{
+ 			if (tupdesc)
+ 				*tupdesc = peraggstate->evaldesc;
+ 			*datumtype = InvalidOid;
+ 		}
+ 		
+ 		if (tupslot)
+ 			*tupslot = peraggstate->evalslot;
+ 	}
+ 	else
+ 	{
+ 		elog(ERROR, "AggSetSortInfo called on non ordered set function");
+ 	}
+ }
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 1138,1145 **** _copyAggref(const Aggref *from)
--- 1138,1147 ----
  	COPY_NODE_FIELD(args);
  	COPY_NODE_FIELD(aggorder);
  	COPY_NODE_FIELD(aggdistinct);
+ 	COPY_NODE_FIELD(orddirectargs);
  	COPY_NODE_FIELD(aggfilter);
  	COPY_SCALAR_FIELD(aggstar);
+ 	COPY_SCALAR_FIELD(isordset);
  	COPY_SCALAR_FIELD(agglevelsup);
  	COPY_LOCATION_FIELD(location);
  
***************
*** 2171,2176 **** _copyFuncCall(const FuncCall *from)
--- 2173,2179 ----
  	COPY_SCALAR_FIELD(agg_star);
  	COPY_SCALAR_FIELD(agg_distinct);
  	COPY_SCALAR_FIELD(func_variadic);
+ 	COPY_SCALAR_FIELD(has_within_group);
  	COPY_NODE_FIELD(over);
  	COPY_LOCATION_FIELD(location);
  
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 196,203 **** _equalAggref(const Aggref *a, const Aggref *b)
--- 196,205 ----
  	COMPARE_NODE_FIELD(args);
  	COMPARE_NODE_FIELD(aggorder);
  	COMPARE_NODE_FIELD(aggdistinct);
+ 	COMPARE_NODE_FIELD(orddirectargs);
  	COMPARE_NODE_FIELD(aggfilter);
  	COMPARE_SCALAR_FIELD(aggstar);
+ 	COMPARE_SCALAR_FIELD(isordset);
  	COMPARE_SCALAR_FIELD(agglevelsup);
  	COMPARE_LOCATION_FIELD(location);
  
***************
*** 2001,2006 **** _equalFuncCall(const FuncCall *a, const FuncCall *b)
--- 2003,2009 ----
  	COMPARE_SCALAR_FIELD(agg_star);
  	COMPARE_SCALAR_FIELD(agg_distinct);
  	COMPARE_SCALAR_FIELD(func_variadic);
+ 	COMPARE_SCALAR_FIELD(has_within_group);
  	COMPARE_NODE_FIELD(over);
  	COMPARE_LOCATION_FIELD(location);
  
*** a/src/backend/nodes/makefuncs.c
--- b/src/backend/nodes/makefuncs.c
***************
*** 530,535 **** makeFuncCall(List *name, List *args, int location)
--- 530,536 ----
  	n->agg_star = FALSE;
  	n->agg_distinct = FALSE;
  	n->func_variadic = FALSE;
+ 	n->has_within_group = FALSE;
  	n->over = NULL;
  	return n;
  }
*** a/src/backend/nodes/nodeFuncs.c
--- b/src/backend/nodes/nodeFuncs.c
***************
*** 1572,1577 **** expression_tree_walker(Node *node,
--- 1572,1582 ----
  				if (expression_tree_walker((Node *) expr->aggdistinct,
  										   walker, context))
  					return true;
+ 
+ 				if (expression_tree_walker((Node *) expr->orddirectargs,
+ 										   walker, context))
+ 					return true;
+ 
  				if (walker((Node *) expr->aggfilter, context))
  					return true;
  			}
***************
*** 2096,2102 **** expression_tree_mutator(Node *node,
--- 2101,2109 ----
  				MUTATE(newnode->args, aggref->args, List *);
  				MUTATE(newnode->aggorder, aggref->aggorder, List *);
  				MUTATE(newnode->aggdistinct, aggref->aggdistinct, List *);
+ 				MUTATE(newnode->orddirectargs, aggref->orddirectargs, List *);
  				MUTATE(newnode->aggfilter, aggref->aggfilter, Expr *);
+ 
  				return (Node *) newnode;
  			}
  			break;
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
***************
*** 959,966 **** _outAggref(StringInfo str, const Aggref *node)
--- 959,968 ----
  	WRITE_NODE_FIELD(args);
  	WRITE_NODE_FIELD(aggorder);
  	WRITE_NODE_FIELD(aggdistinct);
+ 	WRITE_NODE_FIELD(orddirectargs);
  	WRITE_NODE_FIELD(aggfilter);
  	WRITE_BOOL_FIELD(aggstar);
+ 	WRITE_BOOL_FIELD(isordset);
  	WRITE_UINT_FIELD(agglevelsup);
  	WRITE_LOCATION_FIELD(location);
  }
***************
*** 2087,2092 **** _outFuncCall(StringInfo str, const FuncCall *node)
--- 2089,2095 ----
  	WRITE_BOOL_FIELD(agg_star);
  	WRITE_BOOL_FIELD(agg_distinct);
  	WRITE_BOOL_FIELD(func_variadic);
+ 	WRITE_BOOL_FIELD(has_within_group);
  	WRITE_NODE_FIELD(over);
  	WRITE_LOCATION_FIELD(location);
  }
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
***************
*** 495,502 **** _readAggref(void)
--- 495,504 ----
  	READ_NODE_FIELD(args);
  	READ_NODE_FIELD(aggorder);
  	READ_NODE_FIELD(aggdistinct);
+ 	READ_NODE_FIELD(orddirectargs);
  	READ_NODE_FIELD(aggfilter);
  	READ_BOOL_FIELD(aggstar);
+ 	READ_BOOL_FIELD(isordset);
  	READ_UINT_FIELD(agglevelsup);
  	READ_LOCATION_FIELD(location);
  
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
***************
*** 486,492 **** count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
  			costs->numOrderedAggs++;
  
  		/* add component function execution costs to appropriate totals */
! 		costs->transCost.per_tuple += get_func_cost(aggtransfn) * cpu_operator_cost;
  		if (OidIsValid(aggfinalfn))
  			costs->finalCost += get_func_cost(aggfinalfn) * cpu_operator_cost;
  
--- 486,493 ----
  			costs->numOrderedAggs++;
  
  		/* add component function execution costs to appropriate totals */
! 		if (OidIsValid(aggtransfn))
! 			costs->transCost.per_tuple += get_func_cost(aggtransfn) * cpu_operator_cost;
  		if (OidIsValid(aggfinalfn))
  			costs->finalCost += get_func_cost(aggfinalfn) * cpu_operator_cost;
  
***************
*** 516,522 **** count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
  		}
  
  		/* resolve actual type of transition state, if polymorphic */
! 		if (IsPolymorphicType(aggtranstype))
  		{
  			/* have to fetch the agg's declared input types... */
  			Oid		   *declaredArgTypes;
--- 517,523 ----
  		}
  
  		/* resolve actual type of transition state, if polymorphic */
! 		if (OidIsValid(aggtranstype) && IsPolymorphicType(aggtranstype))
  		{
  			/* have to fetch the agg's declared input types... */
  			Oid		   *declaredArgTypes;
***************
*** 539,545 **** count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
  		 * pass-by-reference then we have to add the estimated size of the
  		 * value itself, plus palloc overhead.
  		 */
! 		if (!get_typbyval(aggtranstype))
  		{
  			int32		aggtranstypmod;
  			int32		avgwidth;
--- 540,546 ----
  		 * pass-by-reference then we have to add the estimated size of the
  		 * value itself, plus palloc overhead.
  		 */
! 		if (OidIsValid(aggtranstype) && !get_typbyval(aggtranstype))
  		{
  			int32		aggtranstypmod;
  			int32		avgwidth;
***************
*** 3882,3888 **** recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple)
  		elog(ERROR, "function's resolved result type changed during planning");
  
  	/* perform any necessary typecasting of arguments */
! 	make_fn_arguments(NULL, args, actual_arg_types, declared_arg_types);
  }
  
  /*
--- 3883,3889 ----
  		elog(ERROR, "function's resolved result type changed during planning");
  
  	/* perform any necessary typecasting of arguments */
! 	make_fn_arguments(NULL, args, NULL, actual_arg_types, declared_arg_types);
  }
  
  /*
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 493,498 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
--- 493,499 ----
  %type <str>		opt_existing_window_name
  %type <boolean> opt_if_not_exists
  %type <node>    filter_clause
+ %type <list> 	within_group_clause
  
  /*
   * Non-keyword token types.  These are hard-wired into the "flex" lexer.
***************
*** 595,601 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  	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
--- 596,602 ----
  	VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
  	VERBOSE VERSION_P VIEW VOLATILE
  
! 	WHEN WHERE WITHIN WHITESPACE_P WINDOW WITH WITHOUT WORK WRAPPER WRITE
  
  	XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLEXISTS XMLFOREST XMLPARSE
  	XMLPI XMLROOT XMLSERIALIZE
***************
*** 9413,9418 **** sortby:		a_expr USING qual_all_Op opt_nulls_order
--- 9414,9424 ----
  		;
  
  
+ within_group_clause:
+ 			WITHIN GROUP_P '(' sort_clause ')'  { $$ = $4; }
+ 			| /*EMPTY*/                         { $$ = NULL; }
+          ;
+ 
  select_limit:
  			limit_clause offset_clause			{ $$ = list_make2($2, $1); }
  			| offset_clause limit_clause		{ $$ = list_make2($1, $2); }
***************
*** 11112,11122 **** func_application: func_name '(' ')'
   * (Note that many of the special SQL functions wouldn't actually make any
   * sense as functional index entries, but we ignore that consideration here.)
   */
! func_expr: func_application filter_clause over_clause 
  				{
                		FuncCall *n = (FuncCall*)$1;
! 					n->agg_filter = $2;
! 					n->over = $3;
  					$$ = (Node*)n;
  				} 
  			| func_expr_common_subexpr
--- 11118,11151 ----
   * (Note that many of the special SQL functions wouldn't actually make any
   * sense as functional index entries, but we ignore that consideration here.)
   */
! func_expr: func_application within_group_clause filter_clause over_clause 
  				{
                		FuncCall *n = (FuncCall*)$1;
! 					/*
! 					 * the order clause for WITHIN GROUP and the one
! 					 * for aggregate ORDER BY share a field, so we
! 					 * have to check here that at most one is present.
! 					 * We check for DISTINCT here to give a better
! 					 * error position.  Other consistency checks are
! 					 * deferred to parse_func.c or parse_agg.c
! 					 */
! 					if ($2)
! 					{
! 						if (n->agg_order)
! 							ereport(ERROR,
! 									(errcode(ERRCODE_SYNTAX_ERROR),
! 									 errmsg("Cannot have multiple ORDER BY clauses with WITHIN GROUP"),
! 									 parser_errposition(@2)));
! 						if (n->agg_distinct)
! 							ereport(ERROR,
! 									(errcode(ERRCODE_SYNTAX_ERROR),
! 									 errmsg("Cannot have DISTINCT and WITHIN GROUP together"),
! 									 parser_errposition(@2)));
! 						n->agg_order = $2;
! 						n->has_within_group = TRUE;
! 					}
! 					n->agg_filter = $3;
! 					n->over = $4;
  					$$ = (Node*)n;
  				} 
  			| func_expr_common_subexpr
***************
*** 12675,12680 **** unreserved_keyword:
--- 12704,12710 ----
  			| VIEW
  			| VOLATILE
  			| WHITESPACE_P
+ 			| WITHIN
  			| WITHOUT
  			| WORK
  			| WRAPPER
*** a/src/backend/parser/parse_agg.c
--- b/src/backend/parser/parse_agg.c
***************
*** 44,50 **** typedef struct
  	int			sublevels_up;
  } check_ungrouped_columns_context;
  
! static int	check_agg_arguments(ParseState *pstate, List *args, Expr *filter);
  static bool check_agg_arguments_walker(Node *node,
  						   check_agg_arguments_context *context);
  static void check_ungrouped_columns(Node *node, ParseState *pstate, Query *qry,
--- 44,50 ----
  	int			sublevels_up;
  } check_ungrouped_columns_context;
  
! static int	check_agg_arguments(ParseState *pstate, List *args, List *agg_ordset, Expr *filter);
  static bool check_agg_arguments_walker(Node *node,
  						   check_agg_arguments_context *context);
  static void check_ungrouped_columns(Node *node, ParseState *pstate, Query *qry,
***************
*** 75,81 **** static bool check_ungrouped_columns_walker(Node *node,
   */
  void
  transformAggregateCall(ParseState *pstate, Aggref *agg,
! 					   List *args, List *aggorder, bool agg_distinct)
  {
  	List	   *tlist;
  	List	   *torder;
--- 75,81 ----
   */
  void
  transformAggregateCall(ParseState *pstate, Aggref *agg,
! 					   List *args, List *aggorder, bool agg_distinct, bool agg_within_group)
  {
  	List	   *tlist;
  	List	   *torder;
***************
*** 93,104 **** transformAggregateCall(ParseState *pstate, Aggref *agg,
  	 */
  	tlist = NIL;
  	attno = 1;
! 	foreach(lc, args)
  	{
! 		Expr	   *arg = (Expr *) lfirst(lc);
! 		TargetEntry *tle = makeTargetEntry(arg, attno++, NULL, false);
  
! 		tlist = lappend(tlist, tle);
  	}
  
  	/*
--- 93,116 ----
  	 */
  	tlist = NIL;
  	attno = 1;
! 
! 	if (agg_within_group)
! 	{
! 		agg->isordset = TRUE;
! 		agg->orddirectargs = args;
! 	}
! 	else
  	{
! 		foreach(lc, args)
! 		{
! 			Expr	   *arg = (Expr *) lfirst(lc);
! 			TargetEntry *tle = makeTargetEntry(arg, attno++, NULL, false);
  
! 			tlist = lappend(tlist, tle);
! 		}
! 
! 		agg->isordset = FALSE;
! 		agg->orddirectargs = NIL;
  	}
  
  	/*
***************
*** 160,166 **** transformAggregateCall(ParseState *pstate, Aggref *agg,
  	 * Check the arguments to compute the aggregate's level and detect
  	 * improper nesting.
  	 */
! 	min_varlevel = check_agg_arguments(pstate, agg->args, agg->aggfilter);
  	agg->agglevelsup = min_varlevel;
  
  	/* Mark the correct pstate level as having aggregates */
--- 172,178 ----
  	 * Check the arguments to compute the aggregate's level and detect
  	 * improper nesting.
  	 */
! 	min_varlevel = check_agg_arguments(pstate, agg->args, agg->orddirectargs, agg->aggfilter);
  	agg->agglevelsup = min_varlevel;
  
  	/* Mark the correct pstate level as having aggregates */
***************
*** 312,318 **** transformAggregateCall(ParseState *pstate, Aggref *agg,
   * which we can't know until we finish scanning the arguments.
   */
  static int
! check_agg_arguments(ParseState *pstate, List *args, Expr *filter)
  {
  	int			agglevel;
  	check_agg_arguments_context context;
--- 324,330 ----
   * which we can't know until we finish scanning the arguments.
   */
  static int
! check_agg_arguments(ParseState *pstate, List *args, List *agg_ordset, Expr *filter)
  {
  	int			agglevel;
  	check_agg_arguments_context context;
***************
*** 330,335 **** check_agg_arguments(ParseState *pstate, List *args, Expr *filter)
--- 342,350 ----
  								  check_agg_arguments_walker,
  								  (void *) &context);
  
+ 	(void) expression_tree_walker((Node *) agg_ordset,	  check_agg_arguments_walker,
+ 								  (void *) &context);
+ 
  	/*
  	 * If we found no vars nor aggs at all, it's a level-zero aggregate;
  	 * otherwise, its level is the minimum of vars or aggs.
***************
*** 353,360 **** check_agg_arguments(ParseState *pstate, List *args, Expr *filter)
  				(errcode(ERRCODE_GROUPING_ERROR),
  				 errmsg("aggregate function calls cannot be nested"),
  				 parser_errposition(pstate,
! 									locate_agg_of_level((Node *) args,
! 														agglevel))));
  
  	return agglevel;
  }
--- 368,375 ----
  				(errcode(ERRCODE_GROUPING_ERROR),
  				 errmsg("aggregate function calls cannot be nested"),
  				 parser_errposition(pstate,
! 								locate_agg_of_level((Node *) args,
! 								agglevel))));
  
  	return agglevel;
  }
***************
*** 823,830 **** check_ungrouped_columns_walker(Node *node,
  	 * We do need to look at aggregates of lower levels, however.
  	 */
  	if (IsA(node, Aggref) &&
! 		(int) ((Aggref *) node)->agglevelsup >= context->sublevels_up)
  		return false;
  
  	/*
  	 * If we have any GROUP BY items that are not simple Vars, check to see if
--- 838,852 ----
  	 * We do need to look at aggregates of lower levels, however.
  	 */
  	if (IsA(node, Aggref) &&
! 		(int) ((Aggref *) node)->agglevelsup > context->sublevels_up)
! 	{
  		return false;
+ 	}
+ 	else if (IsA(node, Aggref) &&
+ 			(int) ((Aggref *) node)->agglevelsup == context->sublevels_up)
+ 	{
+ 		return check_ungrouped_columns_walker(((Aggref *)node)->orddirectargs, context);
+ 	}
  
  	/*
  	 * If we have any GROUP BY items that are not simple Vars, check to see if
***************
*** 1005,1016 **** build_aggregate_fnexprs(Oid *agg_input_types,
  		args = lappend(args, argp);
  	}
  
! 	*transfnexpr = (Expr *) makeFuncExpr(transfn_oid,
! 										 agg_state_type,
! 										 args,
! 										 InvalidOid,
! 										 agg_input_collation,
! 										 COERCE_EXPLICIT_CALL);
  
  	/* see if we have a final function */
  	if (!OidIsValid(finalfn_oid))
--- 1027,1045 ----
  		args = lappend(args, argp);
  	}
  
! 	if (OidIsValid(transfn_oid))
! 	{
! 		*transfnexpr = (Expr *) makeFuncExpr(transfn_oid,
! 										agg_state_type,
! 										args,
! 										InvalidOid,
! 										agg_input_collation,
! 										COERCE_EXPLICIT_CALL);
! 	}
! 	else
! 	{
! 		transfnexpr = NULL;
! 	}
  
  	/* see if we have a final function */
  	if (!OidIsValid(finalfn_oid))
*** a/src/backend/parser/parse_expr.c
--- b/src/backend/parser/parse_expr.c
***************
*** 464,470 **** transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
  										  list_make1(n),
  										  list_make1(result),
  										  NIL, NULL, false, false, false,
! 										  NULL, true, location);
  			if (newresult == NULL)
  				unknown_attribute(pstate, result, strVal(n), location);
  			result = newresult;
--- 464,470 ----
  										  list_make1(n),
  										  list_make1(result),
  										  NIL, NULL, false, false, false,
! 										  false, NULL, true, location);
  			if (newresult == NULL)
  				unknown_attribute(pstate, result, strVal(n), location);
  			result = newresult;
***************
*** 632,638 **** transformColumnRef(ParseState *pstate, ColumnRef *cref)
  											 list_make1(makeString(colname)),
  											 list_make1(node),
  											 NIL, NULL, false, false, false,
! 											 NULL, true, cref->location);
  				}
  				break;
  			}
--- 632,638 ----
  											 list_make1(makeString(colname)),
  											 list_make1(node),
  											 NIL, NULL, false, false, false,
! 											 false, NULL, true, cref->location);
  				}
  				break;
  			}
***************
*** 677,683 **** transformColumnRef(ParseState *pstate, ColumnRef *cref)
  											 list_make1(makeString(colname)),
  											 list_make1(node),
  											 NIL, NULL, false, false, false,
! 											 NULL, true, cref->location);
  				}
  				break;
  			}
--- 677,683 ----
  											 list_make1(makeString(colname)),
  											 list_make1(node),
  											 NIL, NULL, false, false, false,
! 											 false, NULL, true, cref->location);
  				}
  				break;
  			}
***************
*** 735,741 **** transformColumnRef(ParseState *pstate, ColumnRef *cref)
  											 list_make1(makeString(colname)),
  											 list_make1(node),
  											 NIL, NULL, false, false, false,
! 											 NULL, true, cref->location);
  				}
  				break;
  			}
--- 735,741 ----
  											 list_make1(makeString(colname)),
  											 list_make1(node),
  											 NIL, NULL, false, false, false,
! 											 false, NULL, true, cref->location);
  				}
  				break;
  			}
***************
*** 1248,1255 **** transformFuncCall(ParseState *pstate, FuncCall *fn)
  	targs = NIL;
  	foreach(args, fn->args)
  	{
! 		targs = lappend(targs, transformExprRecurse(pstate,
! 													(Node *) lfirst(args)));
  	}
  
  	/*
--- 1248,1254 ----
  	targs = NIL;
  	foreach(args, fn->args)
  	{
! 		targs = lappend(targs, transformExprRecurse(pstate, (Node *) lfirst(args)));
  	}
  
  	/*
***************
*** 1271,1276 **** transformFuncCall(ParseState *pstate, FuncCall *fn)
--- 1270,1276 ----
  							 fn->agg_star,
  							 fn->agg_distinct,
  							 fn->func_variadic,
+ 							 fn->has_within_group,
  							 fn->over,
  							 false,
  							 fn->location);
*** a/src/backend/parser/parse_func.c
--- b/src/backend/parser/parse_func.c
***************
*** 17,22 ****
--- 17,23 ----
  #include "access/htup_details.h"
  #include "catalog/pg_proc.h"
  #include "catalog/pg_type.h"
+ #include "catalog/pg_aggregate.h"
  #include "funcapi.h"
  #include "lib/stringinfo.h"
  #include "nodes/makefuncs.h"
***************
*** 27,32 ****
--- 28,34 ----
  #include "parser/parse_relation.h"
  #include "parser/parse_target.h"
  #include "parser/parse_type.h"
+ #include "parser/parse_expr.h"
  #include "utils/builtins.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"
***************
*** 63,69 **** Node *
  ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
  				  List *agg_order, Expr *agg_filter,
  				  bool agg_star, bool agg_distinct, bool func_variadic,
! 				  WindowDef *over, bool is_column, int location)
  {
  	Oid			rettype;
  	Oid			funcid;
--- 65,71 ----
  ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
  				  List *agg_order, Expr *agg_filter,
  				  bool agg_star, bool agg_distinct, bool func_variadic,
! 				  bool agg_within_group, WindowDef *over, bool is_column, int location)
  {
  	Oid			rettype;
  	Oid			funcid;
***************
*** 81,86 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 83,94 ----
  	int			nvargs;
  	Oid			vatype;
  	FuncDetailCode fdresult;
+ 	HeapTuple	tup;
+ 	Oid		aggfinalfn;
+ 	int		number_of_args;
+ 
+ 	/* Check if the function has WITHIN GROUP as well as distinct. */
+ 	Assert(!(agg_within_group && agg_distinct));
  
  	/*
  	 * Most of the rest of the parser just assumes that functions do not have
***************
*** 163,174 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 171,208 ----
  		}
  	}
  
+ 	if(agg_within_group && argnames)
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_SYNTAX_ERROR),
+ 			errmsg("Named arguments not allowed in WITHIN GROUP")));
+ 	}
+ 
  	if (fargs)
  	{
  		first_arg = linitial(fargs);
  		Assert(first_arg != NULL);
  	}
  
+ 	/* If WITHIN GROUP is present, we need to call transformExpr on each
+ 	 * SortBy node in agg_order, then call exprType and append to
+ 	 * actual_arg_types, in order to get the types of values in
+ 	 * WITHIN BY clause.
+ 	 */
+ 	if(agg_within_group)
+ 	{
+ 		Assert(agg_order != NULL);
+ 
+ 		foreach(l, agg_order)
+ 		{
+ 			SortBy	   *arg = (SortBy *) lfirst(l);
+ 
+ 			arg->node = transformExpr(pstate, arg->node, EXPR_KIND_ORDER_BY);
+ 
+ 			actual_arg_types[nargs++] = exprType(arg->node);
+ 		}
+ 	}
+ 
  	/*
  	 * Check for column projection: if function has one argument, and that
  	 * argument is of complex type, and function name is not qualified, then
***************
*** 247,252 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 281,292 ----
  			errmsg("DISTINCT specified, but %s is not an aggregate function",
  				   NameListToString(funcname)),
  					 parser_errposition(pstate, location)));
+ 		if (agg_within_group)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 			errmsg("WITHIN GROUP specified, but %s is not an ordered set function",
+ 				   NameListToString(funcname)),
+ 					 parser_errposition(pstate, location)));
  		if (agg_order != NIL)
  			ereport(ERROR,
  					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
***************
*** 266,271 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 306,329 ----
  							NameListToString(funcname)),
  					 parser_errposition(pstate, location)));
  	}
+ 	else if (fdresult == FUNCDETAIL_ORDERED)
+ 	{
+ 		if(!agg_within_group)
+ 		{
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 					 errmsg("Ordered set function specified, but WITHIN GROUP not present"),
+ 					 parser_errposition(pstate, location)));
+ 		}
+ 		
+ 		if(over)
+ 		{
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 					 errmsg("OVER clause in ordered set function not supported"),
+ 					 parser_errposition(pstate, location)));
+ 		}
+ 	}
  	else if (!(fdresult == FUNCDETAIL_AGGREGATE ||
  			   fdresult == FUNCDETAIL_WINDOWFUNC))
  	{
***************
*** 351,357 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
  											   false);
  
  	/* perform the necessary typecasting of arguments */
! 	make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types);
  
  	/*
  	 * If it's a variadic function call, transform the last nvargs arguments
--- 409,417 ----
  											   false);
  
  	/* perform the necessary typecasting of arguments */
! 	make_fn_arguments(pstate, fargs, (fdresult==FUNCDETAIL_ORDERED) ? agg_order : NIL, 
! 				actual_arg_types, 
! 				declared_arg_types);
  
  	/*
  	 * If it's a variadic function call, transform the last nvargs arguments
***************
*** 416,425 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
  
  		retval = (Node *) funcexpr;
  	}
! 	else if (fdresult == FUNCDETAIL_AGGREGATE && !over)
  	{
  		/* aggregate function */
  		Aggref	   *aggref = makeNode(Aggref);
  
  		aggref->aggfnoid = funcid;
  		aggref->aggtype = rettype;
--- 476,492 ----
  
  		retval = (Node *) funcexpr;
  	}
! 	else if ((fdresult == FUNCDETAIL_AGGREGATE && !over) || fdresult == FUNCDETAIL_ORDERED)
  	{
  		/* aggregate function */
  		Aggref	   *aggref = makeNode(Aggref);
+ 		if(agg_within_group && fdresult == FUNCDETAIL_AGGREGATE)
+ 		{
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 					 errmsg("%s is not an ordered set function",func_signature_string(funcname, nargs,
+ 											  NIL, actual_arg_types))));
+ 		}
  
  		aggref->aggfnoid = funcid;
  		aggref->aggtype = rettype;
***************
*** 430,435 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 497,522 ----
  		/* agglevelsup will be set by transformAggregateCall */
  		aggref->location = location;
  
+ 		if (fdresult == FUNCDETAIL_ORDERED)
+ 		{
+ 			Form_pg_aggregate classForm;
+ 			tup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(funcid));
+ 			if (!HeapTupleIsValid(tup)) /* should not happen */
+ 				elog(ERROR, "cache lookup failed for aggregate %u", funcid);
+ 
+ 			classForm = (Form_pg_aggregate) GETSTRUCT(tup);
+ 			aggfinalfn = classForm->aggfinalfn;
+ 			number_of_args = get_func_nargs(aggfinalfn);
+ 			if (number_of_args != list_length(fargs))
+ 			{
+ 				ereport(ERROR,
+ 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 					 errmsg("Number of arguments does not match fargs")));
+ 			}
+ 
+ 			ReleaseSysCache(tup);
+ 		}
+ 
  		/*
  		 * Reject attempt to call a parameterless aggregate without (*)
  		 * syntax.	This is mere pedantry but some folks insisted ...
***************
*** 460,466 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
  					 parser_errposition(pstate, location)));
  
  		/* parse_agg.c does additional aggregate-specific processing */
! 		transformAggregateCall(pstate, aggref, fargs, agg_order, agg_distinct);
  
  		retval = (Node *) aggref;
  	}
--- 547,553 ----
  					 parser_errposition(pstate, location)));
  
  		/* parse_agg.c does additional aggregate-specific processing */
! 		transformAggregateCall(pstate, aggref, fargs, agg_order, agg_distinct, agg_within_group);
  
  		retval = (Node *) aggref;
  	}
***************
*** 469,474 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 556,569 ----
  		/* window function */
  		WindowFunc *wfunc = makeNode(WindowFunc);
  
+ 		if(agg_within_group)
+ 		{
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 					 errmsg("WITHIN GROUP not allowed in window functions"),
+ 					 parser_errposition(pstate, location)));
+ 		}
+ 
  		/*
  		 * True window functions must be called with a window definition.
  		 */
***************
*** 1264,1269 **** func_get_detail(List *funcname,
--- 1359,1366 ----
  
  		ftup = SearchSysCache1(PROCOID,
  							   ObjectIdGetDatum(best_candidate->oid));
+ 
+ 		
  		if (!HeapTupleIsValid(ftup))	/* should not happen */
  			elog(ERROR, "cache lookup failed for function %u",
  				 best_candidate->oid);
***************
*** 1341,1347 **** func_get_detail(List *funcname,
  			}
  		}
  		if (pform->proisagg)
! 			result = FUNCDETAIL_AGGREGATE;
  		else if (pform->proiswindow)
  			result = FUNCDETAIL_WINDOWFUNC;
  		else
--- 1438,1460 ----
  			}
  		}
  		if (pform->proisagg)
! 		{
! 			Form_pg_aggregate pfagg;
! 			HeapTuple	ftup_agg;
! 
! 			ftup_agg = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(best_candidate->oid));
! 			pfagg = (Form_pg_aggregate) GETSTRUCT(ftup_agg);
! 			if (pfagg->aggisordsetfunc)
! 			{
! 				result = FUNCDETAIL_ORDERED;
! 			}
! 			else
! 			{
! 				result = FUNCDETAIL_AGGREGATE;
! 			}
! 
! 			ReleaseSysCache(ftup_agg);
! 		}
  		else if (pform->proiswindow)
  			result = FUNCDETAIL_WINDOWFUNC;
  		else
***************
*** 1370,1379 **** func_get_detail(List *funcname,
--- 1483,1494 ----
  void
  make_fn_arguments(ParseState *pstate,
  				  List *fargs,
+ 				  List *agg_order,
  				  Oid *actual_arg_types,
  				  Oid *declared_arg_types)
  {
  	ListCell   *current_fargs;
+ 	ListCell   *current_aoargs;
  	int			i = 0;
  
  	foreach(current_fargs, fargs)
***************
*** 1414,1419 **** make_fn_arguments(ParseState *pstate,
--- 1529,1553 ----
  		}
  		i++;
  	}
+ 
+ 	foreach(current_aoargs, agg_order)
+ 	{
+ 		if (actual_arg_types[i] != declared_arg_types[i])
+ 		{
+ 			SortBy	   *node = (SortBy *) lfirst(current_aoargs);
+ 			SortBy     *temp = NULL;
+ 
+ 			temp = coerce_type(pstate,
+ 							node->node,
+ 							actual_arg_types[i],
+ 							declared_arg_types[i], -1,
+ 							COERCION_IMPLICIT,
+ 							COERCE_IMPLICIT_CAST,
+ 							-1);
+ 			node->node = temp;
+ 		}
+ 		i++;
+ 	}
  }
  
  /*
*** a/src/backend/parser/parse_oper.c
--- b/src/backend/parser/parse_oper.c
***************
*** 823,829 **** make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
  											   false);
  
  	/* perform the necessary typecasting of arguments */
! 	make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
  
  	/* and build the expression node */
  	result = makeNode(OpExpr);
--- 823,829 ----
  											   false);
  
  	/* perform the necessary typecasting of arguments */
! 	make_fn_arguments(pstate, args, NULL, actual_arg_types, declared_arg_types);
  
  	/* and build the expression node */
  	result = makeNode(OpExpr);
***************
*** 953,959 **** make_scalar_array_op(ParseState *pstate, List *opname,
  	declared_arg_types[1] = res_atypeId;
  
  	/* perform the necessary typecasting of arguments */
! 	make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
  
  	/* and build the expression node */
  	result = makeNode(ScalarArrayOpExpr);
--- 953,959 ----
  	declared_arg_types[1] = res_atypeId;
  
  	/* perform the necessary typecasting of arguments */
! 	make_fn_arguments(pstate, args, NULL, actual_arg_types, declared_arg_types);
  
  	/* and build the expression node */
  	result = makeNode(ScalarArrayOpExpr);
*** a/src/backend/utils/adt/Makefile
--- b/src/backend/utils/adt/Makefile
***************
*** 25,31 **** OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
  	rowtypes.o regexp.o regproc.o ruleutils.o selfuncs.o \
  	tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \
  	network.o mac.o inet_cidr_ntop.o inet_net_pton.o \
! 	ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o \
  	ascii.o quote.o pgstatfuncs.o encode.o dbsize.o genfile.o trigfuncs.o \
  	tsginidx.o tsgistidx.o tsquery.o tsquery_cleanup.o tsquery_gist.o \
  	tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \
--- 25,31 ----
  	rowtypes.o regexp.o regproc.o ruleutils.o selfuncs.o \
  	tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \
  	network.o mac.o inet_cidr_ntop.o inet_net_pton.o \
! 	inversedistribution.o ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o \
  	ascii.o quote.o pgstatfuncs.o encode.o dbsize.o genfile.o trigfuncs.o \
  	tsginidx.o tsgistidx.o tsquery.o tsquery_cleanup.o tsquery_gist.o \
  	tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \
*** /dev/null
--- b/src/backend/utils/adt/inversedistribution.c
***************
*** 0 ****
--- 1,237 ----
+ /*-------------------------------------------------------------------------
+  *
+  * inversedistribution.c
+  *	  Inverse distribution functions.
+  *
+  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/backend/utils/adt/inversedistribution.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ #include "fmgr.h"
+ #include <string.h>
+ #include <math.h>
+ 
+ #include "utils/tuplesort.h"
+ #include "catalog/pg_type.h"
+ #include "utils/datetime.h"
+ 
+ /*
+  * percentile_disc(float8)  - discrete (nearest) percentile
+  */
+ 
+ Datum percentile_disc_final(PG_FUNCTION_ARGS);
+ 
+ Datum
+ percentile_disc_final(PG_FUNCTION_ARGS)
+ {
+ 	float8 percentile = PG_GETARG_FLOAT8(0);
+ 	int64 rowcount = AggSetGetRowCount(fcinfo);
+ 	Tuplesortstate *sorter;
+ 	Oid datumtype;
+ 	Datum val;
+ 	bool isnull;
+ 	int64 skiprows;
+ 
+ 	AggSetGetSortInfo(fcinfo, &sorter, NULL, NULL, &datumtype);
+ 
+ 	Assert(datumtype == FLOAT8OID);
+ 
+ 	if (percentile < 0 || percentile > 1)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ 				 errmsg("percentile value %g is not between 0 and 1", percentile)));
+ 
+ 	if (rowcount < 1)
+ 		PG_RETURN_NULL();
+ 
+ 	tuplesort_performsort(sorter);
+ 
+ 	/*
+ 	 * We need the smallest K such that (K/N) >= percentile. K starts at 1.
+ 	 * Therefore K >= N*percentile
+ 	 * Therefore K = ceil(N*percentile)
+ 	 * So we skip K-1 rows (if K>0) and return the next row fetched.
+ 	 */
+ 
+ 	skiprows = (int64) ceil(percentile * rowcount);
+ 	Assert(skiprows <= rowcount);
+ 
+ 	while (--skiprows > 0)
+ 		if (!tuplesort_getdatum(sorter, true, NULL, NULL))
+ 			elog(ERROR,"missing row in percentile_disc");
+ 
+ 	if (!tuplesort_getdatum(sorter, true, &val, &isnull))
+ 		elog(ERROR,"missing row in percentile_disc");
+ 
+ 	/* if float8 is by-ref, "val" already points to palloced space in
+ 	 * our memory context. No need to do more.
+ 	 */
+ 
+ 	if (isnull)
+ 		PG_RETURN_NULL();
+ 	else
+ 		PG_RETURN_DATUM(val);
+ }
+ 
+ 
+ 
+ /*
+  * percentile_cont(float8)  - continuous (nearest) percentile
+  */
+ 
+ Datum percentile_cont_final(PG_FUNCTION_ARGS);
+ 
+ Datum
+ percentile_cont_final(PG_FUNCTION_ARGS)
+ {
+ 	float8 percentile = PG_GETARG_FLOAT8(0);
+ 	int64 rowcount = AggSetGetRowCount(fcinfo);
+ 	Tuplesortstate *sorter;
+ 	Oid datumtype;
+ 	Datum val;
+ 	Datum first_row;
+ 	Datum second_row;
+ 	float8 first_row_val;
+ 	float8 second_row_val;
+ 	float8 proportion;
+ 	bool isnull;
+ 	int64 skiprows;
+ 	int64 lower_row = 0;
+ 	int64 higher_row = 0;
+ 
+ 	AggSetGetSortInfo(fcinfo, &sorter, NULL, NULL, &datumtype);
+ 
+ 	Assert(datumtype == FLOAT8OID);
+ 
+ 	if (percentile < 0 || percentile > 1)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ 				 errmsg("percentile value %g is not between 0 and 1", percentile)));
+ 
+ 	if (rowcount < 1)
+ 		PG_RETURN_NULL();
+ 
+ 	tuplesort_performsort(sorter);
+ 
+ 	lower_row = floor(percentile * (rowcount -1));
+ 	higher_row = ceil(percentile * (rowcount -1));
+ 
+ 	Assert(lower_row < rowcount);
+ 
+ 	for (skiprows = lower_row; skiprows > 0; --skiprows)
+ 		if (!tuplesort_getdatum(sorter, true, NULL, NULL))
+ 			elog(ERROR,"missing row in percentile_cont");
+ 
+ 	if (!tuplesort_getdatum(sorter, true, &first_row, &isnull))
+ 			elog(ERROR,"missing row in percentile_cont");
+ 
+ 	if (lower_row == higher_row)
+ 	{
+ 
+ 		val = first_row;
+ 	}
+ 	else
+ 	{
+ 		if (!tuplesort_getdatum(sorter, true, &second_row, &isnull))
+ 			elog(ERROR,"missing row in percentile_cont");
+ 
+ 		if (isnull)
+ 			PG_RETURN_NULL();
+ 
+ 		proportion = (percentile * (rowcount-1)) - lower_row;
+ 		first_row_val = DatumGetFloat8(first_row);
+ 		second_row_val = DatumGetFloat8(second_row); 
+ 		val = Float8GetDatum(first_row_val + (proportion *(second_row_val - first_row_val)));
+ 	}
+ 
+ 	if (isnull)
+ 		PG_RETURN_NULL();
+ 	else
+ 		PG_RETURN_DATUM(val);
+ }
+ 
+ /*
+  * percentile_interval_cont(Interval)  - continuous (nearest) percentile for Interval
+  */
+ 
+ Datum percentile_interval_cont_final(PG_FUNCTION_ARGS);
+ 
+ Datum
+ percentile_interval_cont_final(PG_FUNCTION_ARGS)
+ {
+ 	float8 percentile = PG_GETARG_FLOAT8(0);
+ 	int64 rowcount = AggSetGetRowCount(fcinfo);
+ 	Tuplesortstate *sorter;
+ 	Oid datumtype;
+ 	Datum val;
+ 	Datum first_row;
+ 	Datum second_row;
+ 	float8 proportion;
+ 	bool isnull;
+ 	int64 skiprows;
+ 	int64 lower_row = 0;
+ 	int64 higher_row = 0;
+ 	Datum diff_result;
+ 	Datum mul_result;
+ 	Datum add_result;
+ 
+ 	AggSetGetSortInfo(fcinfo, &sorter, NULL, NULL, &datumtype);
+ 
+ 	Assert(datumtype == INTERVALOID);
+ 
+ 	if (percentile < 0 || percentile > 1)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ 				 errmsg("percentile value %g is not between 0 and 1", percentile)));
+ 
+ 	if (rowcount < 1)
+ 		PG_RETURN_NULL();
+ 
+ 	tuplesort_performsort(sorter);
+ 
+ 	lower_row = floor(percentile * (rowcount -1));
+ 	higher_row = ceil(percentile * (rowcount -1));
+ 
+ 	Assert(lower_row < rowcount);
+ 
+ 	for (skiprows = lower_row; skiprows > 0; --skiprows)
+ 		if (!tuplesort_getdatum(sorter, true, NULL, NULL))
+ 			elog(ERROR,"missing row in percentile_cont");
+ 
+ 	if (!tuplesort_getdatum(sorter, true, &first_row, &isnull))
+ 			elog(ERROR,"missing row in percentile_cont");
+ 
+ 	if (lower_row == higher_row)
+ 	{
+ 
+ 		val = first_row;
+ 	}
+ 	else
+ 	{
+ 		if (!tuplesort_getdatum(sorter, true, &second_row, &isnull))
+ 			elog(ERROR,"missing row in percentile_cont");
+ 
+ 		if (isnull)
+ 			PG_RETURN_NULL();
+ 
+ 		proportion = (percentile * (rowcount-1)) - lower_row;
+ 
+ 		diff_result = DirectFunctionCall2(interval_mi, second_row, first_row);
+ 		mul_result = DirectFunctionCall2(interval_mul, diff_result, Float8GetDatumFast(proportion));
+ 		add_result = DirectFunctionCall2(interval_pl, mul_result, first_row);
+ 		val = add_result;
+ 	}
+ 
+ 	if (isnull)
+ 		PG_RETURN_NULL();
+ 	else
+ 		PG_RETURN_DATUM(val);
+ }
+ 
+ 
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
***************
*** 402,407 **** static char *generate_function_name(Oid funcid, int nargs,
--- 402,409 ----
  static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
  static text *string_to_text(char *str);
  static char *flatten_reloptions(Oid relid);
+ static void get_aggstd_expr(Aggref *aggref, deparse_context *context);
+ static void get_ordset_expr(Aggref *aggref, deparse_context *context);
  
  #define only_marker(rte)  ((rte)->inh ? "" : "ONLY ")
  
***************
*** 7384,7389 **** static void
--- 7386,7468 ----
  get_agg_expr(Aggref *aggref, deparse_context *context)
  {
  	StringInfo	buf = context->buf;
+ 
+ 	if (aggref->isordset)
+ 	{
+ 		get_ordset_expr(aggref, context);
+ 	}
+ 	else
+ 	{
+ 		get_aggstd_expr(aggref, context);
+ 	}
+ 
+ 	if (aggref->aggfilter != NULL)
+ 	{
+ 		appendStringInfoString(buf, ") FILTER (WHERE ");
+ 		get_rule_expr((Node *)aggref->aggfilter, context, false);
+ 	}
+ 
+ 	appendStringInfoString(buf, ")");
+ }
+ 
+ static void
+ get_ordset_expr(Aggref *aggref, deparse_context *context)
+ {
+ 	StringInfo	buf = context->buf;
+ 	Oid			argtypes[FUNC_MAX_ARGS];
+ 	List	   *arglist;
+ 	int			nargs;
+ 	ListCell   *l;
+ 
+ 	/* Extract the regular arguments, ignoring resjunk stuff for the moment */
+ 	arglist = NIL;
+ 	nargs = 0;
+ 
+ 	foreach(l, aggref->orddirectargs)
+ 	{
+ 		Node	   *arg = (Node *) lfirst(l);
+ 
+ 		Assert(!IsA(arg, NamedArgExpr));
+ 		if (nargs >= FUNC_MAX_ARGS)		/* paranoia */
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+ 					 errmsg("too many arguments")));
+ 		argtypes[nargs] = exprType(arg);
+ 		nargs++;
+ 	}
+ 
+ 	/* For direct arguments in case of ordered set functions */
+ 	foreach(l, aggref->args)
+ 	{
+ 		TargetEntry *tle = (TargetEntry *) lfirst(l);
+ 		Node	   *arg = (Node *) tle->expr;
+ 
+ 		Assert(!IsA(arg, NamedArgExpr));
+ 		if (tle->resjunk)
+ 			continue;
+ 		if (nargs >= FUNC_MAX_ARGS)		/* paranoia */
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+ 					 errmsg("too many arguments")));
+ 		argtypes[nargs] = exprType(arg);
+ 		arglist = lappend(arglist, arg);
+ 		nargs++;
+ 	}
+ 
+ 	appendStringInfo(buf, "%s(",
+ 					 generate_function_name(aggref->aggfnoid, nargs,
+ 											NIL, argtypes,
+ 											false, NULL));
+ 
+ 	get_rule_expr((Node *)aggref->orddirectargs, context, true);
+ 	appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY ");
+ 	get_rule_orderby(aggref->aggorder, aggref->args, false, context);
+ 	
+ }
+ static void
+ get_aggstd_expr(Aggref *aggref, deparse_context *context)
+ {
+ 	StringInfo	buf = context->buf;
  	Oid			argtypes[FUNC_MAX_ARGS];
  	List	   *arglist;
  	int			nargs;
***************
*** 7424,7437 **** get_agg_expr(Aggref *aggref, deparse_context *context)
  		appendStringInfoString(buf, " ORDER BY ");
  		get_rule_orderby(aggref->aggorder, aggref->args, false, context);
  	}
- 
- 	if (aggref->aggfilter != NULL)
- 	{
- 		appendStringInfoString(buf, ") FILTER (WHERE ");
- 		get_rule_expr((Node *) aggref->aggfilter, context, false);
- 	}
- 
- 	appendStringInfoChar(buf, ')');
  }
  
  /*
--- 7503,7508 ----
***************
*** 7472,7478 **** get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
  	if (wfunc->aggfilter != NULL)
  	{
  		appendStringInfoString(buf, ") FILTER (WHERE ");
! 		get_rule_expr((Node *) wfunc->aggfilter, context, false);
  	}
  
  	appendStringInfoString(buf, ") OVER ");
--- 7543,7549 ----
  	if (wfunc->aggfilter != NULL)
  	{
  		appendStringInfoString(buf, ") FILTER (WHERE ");
! 		get_rule_expr((Node *)wfunc->aggfilter, context, false);
  	}
  
  	appendStringInfoString(buf, ") OVER ");
*** a/src/backend/utils/sort/tuplesort.c
--- b/src/backend/utils/sort/tuplesort.c
***************
*** 1411,1433 **** tuplesort_performsort(Tuplesortstate *state)
   * Internal routine to fetch the next tuple in either forward or back
   * direction into *stup.  Returns FALSE if no more tuples.
   * If *should_free is set, the caller must pfree stup.tuple when done with it.
   */
  static bool
  tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
  						  SortTuple *stup, bool *should_free)
  {
  	unsigned int tuplen;
  
  	switch (state->status)
  	{
  		case TSS_SORTEDINMEM:
  			Assert(forward || state->randomAccess);
! 			*should_free = false;
  			if (forward)
  			{
  				if (state->current < state->memtupcount)
  				{
! 					*stup = state->memtuples[state->current++];
  					return true;
  				}
  				state->eof_reached = true;
--- 1411,1439 ----
   * Internal routine to fetch the next tuple in either forward or back
   * direction into *stup.  Returns FALSE if no more tuples.
   * If *should_free is set, the caller must pfree stup.tuple when done with it.
+  * stup may be null to move without fetching.
   */
  static bool
  tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
  						  SortTuple *stup, bool *should_free)
  {
  	unsigned int tuplen;
+ 	SortTuple dummy;
+ 	SortTuple *ptup = stup ? stup : &dummy;
  
  	switch (state->status)
  	{
  		case TSS_SORTEDINMEM:
  			Assert(forward || state->randomAccess);
! 			if (should_free)
! 				*should_free = false;
  			if (forward)
  			{
  				if (state->current < state->memtupcount)
  				{
! 					if (stup)
! 						*stup = state->memtuples[state->current];
! 					state->current++;
  					return true;
  				}
  				state->eof_reached = true;
***************
*** 1459,1479 **** tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
  					if (state->current <= 0)
  						return false;
  				}
! 				*stup = state->memtuples[state->current - 1];
  				return true;
  			}
  			break;
  
  		case TSS_SORTEDONTAPE:
  			Assert(forward || state->randomAccess);
! 			*should_free = true;
  			if (forward)
  			{
  				if (state->eof_reached)
  					return false;
  				if ((tuplen = getlen(state, state->result_tape, true)) != 0)
  				{
! 					READTUP(state, stup, state->result_tape, tuplen);
  					return true;
  				}
  				else
--- 1465,1489 ----
  					if (state->current <= 0)
  						return false;
  				}
! 				if (stup)
! 					*stup = state->memtuples[state->current - 1];
  				return true;
  			}
  			break;
  
  		case TSS_SORTEDONTAPE:
  			Assert(forward || state->randomAccess);
! 			if (should_free)
! 				*should_free = true;
  			if (forward)
  			{
  				if (state->eof_reached)
  					return false;
  				if ((tuplen = getlen(state, state->result_tape, true)) != 0)
  				{
! 					READTUP(state, ptup, state->result_tape, tuplen);
! 					if (!stup && dummy.tuple)
! 						pfree(dummy.tuple);
  					return true;
  				}
  				else
***************
*** 1546,1557 **** tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
  									  state->result_tape,
  									  tuplen))
  				elog(ERROR, "bogus tuple length in backward scan");
! 			READTUP(state, stup, state->result_tape, tuplen);
  			return true;
  
  		case TSS_FINALMERGE:
  			Assert(forward);
! 			*should_free = true;
  
  			/*
  			 * This code should match the inner loop of mergeonerun().
--- 1556,1570 ----
  									  state->result_tape,
  									  tuplen))
  				elog(ERROR, "bogus tuple length in backward scan");
! 			READTUP(state, ptup, state->result_tape, tuplen);
! 			if (!stup && dummy.tuple)
! 				pfree(dummy.tuple);
  			return true;
  
  		case TSS_FINALMERGE:
  			Assert(forward);
! 			if (should_free)
! 				*should_free = true;
  
  			/*
  			 * This code should match the inner loop of mergeonerun().
***************
*** 1563,1573 **** tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
  				int			tupIndex;
  				SortTuple  *newtup;
  
! 				*stup = state->memtuples[0];
  				/* returned tuple is no longer counted in our memory space */
! 				if (stup->tuple)
  				{
! 					tuplen = GetMemoryChunkSpace(stup->tuple);
  					state->availMem += tuplen;
  					state->mergeavailmem[srcTape] += tuplen;
  				}
--- 1576,1586 ----
  				int			tupIndex;
  				SortTuple  *newtup;
  
! 				*ptup = state->memtuples[0];
  				/* returned tuple is no longer counted in our memory space */
! 				if (ptup->tuple)
  				{
! 					tuplen = GetMemoryChunkSpace(ptup->tuple);
  					state->availMem += tuplen;
  					state->mergeavailmem[srcTape] += tuplen;
  				}
***************
*** 1598,1603 **** tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
--- 1611,1618 ----
  				newtup->tupindex = state->mergefreelist;
  				state->mergefreelist = tupIndex;
  				state->mergeavailslots[srcTape]++;
+ 				if (!stup && dummy.tuple)
+ 					pfree(dummy.tuple);
  				return true;
  			}
  			return false;
***************
*** 1620,1639 **** tuplesort_gettupleslot(Tuplesortstate *state, bool forward,
  	MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext);
  	SortTuple	stup;
  	bool		should_free;
  
! 	if (!tuplesort_gettuple_common(state, forward, &stup, &should_free))
! 		stup.tuple = NULL;
  
  	MemoryContextSwitchTo(oldcontext);
  
! 	if (stup.tuple)
  	{
! 		ExecStoreMinimalTuple((MinimalTuple) stup.tuple, slot, should_free);
  		return true;
  	}
  	else
  	{
! 		ExecClearTuple(slot);
  		return false;
  	}
  }
--- 1635,1656 ----
  	MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext);
  	SortTuple	stup;
  	bool		should_free;
+ 	bool        found;
  
! 	found = tuplesort_gettuple_common(state, forward, (slot ? &stup : NULL), &should_free);
  
  	MemoryContextSwitchTo(oldcontext);
  
! 	if (found)
  	{
! 		if (slot)
! 			ExecStoreMinimalTuple((MinimalTuple) stup.tuple, slot, should_free);
  		return true;
  	}
  	else
  	{
! 		if (slot)
! 			ExecClearTuple(slot);
  		return false;
  	}
  }
***************
*** 1692,1715 **** tuplesort_getdatum(Tuplesortstate *state, bool forward,
  	SortTuple	stup;
  	bool		should_free;
  
! 	if (!tuplesort_gettuple_common(state, forward, &stup, &should_free))
  	{
  		MemoryContextSwitchTo(oldcontext);
  		return false;
  	}
  
! 	if (stup.isnull1 || state->datumTypeByVal)
  	{
! 		*val = stup.datum1;
! 		*isNull = stup.isnull1;
! 	}
! 	else
! 	{
! 		if (should_free)
  			*val = stup.datum1;
  		else
! 			*val = datumCopy(stup.datum1, false, state->datumTypeLen);
! 		*isNull = false;
  	}
  
  	MemoryContextSwitchTo(oldcontext);
--- 1709,1735 ----
  	SortTuple	stup;
  	bool		should_free;
  
! 	if (!tuplesort_gettuple_common(state, forward, (val ? &stup : NULL), &should_free))
  	{
  		MemoryContextSwitchTo(oldcontext);
  		return false;
  	}
  
! 	if (val)
  	{
! 		if (stup.isnull1 || state->datumTypeByVal)
! 		{
  			*val = stup.datum1;
+ 			*isNull = stup.isnull1;
+ 		}
  		else
! 		{
! 			if (should_free)
! 				*val = stup.datum1;
! 			else
! 				*val = datumCopy(stup.datum1, false, state->datumTypeLen);
! 			*isNull = false;
! 		}
  	}
  
  	MemoryContextSwitchTo(oldcontext);
*** a/src/include/catalog/pg_aggregate.h
--- b/src/include/catalog/pg_aggregate.h
***************
*** 44,49 **** CATALOG(pg_aggregate,2600) BKI_WITHOUT_OIDS
--- 44,50 ----
  	regproc		aggfinalfn;
  	Oid			aggsortop;
  	Oid			aggtranstype;
+ 	bool		aggisordsetfunc;
  
  #ifdef CATALOG_VARLEN			/* variable-length fields start here */
  	text		agginitval;
***************
*** 62,74 **** typedef FormData_pg_aggregate *Form_pg_aggregate;
   * ----------------
   */
  
! #define Natts_pg_aggregate				6
  #define Anum_pg_aggregate_aggfnoid		1
  #define Anum_pg_aggregate_aggtransfn	2
  #define Anum_pg_aggregate_aggfinalfn	3
  #define Anum_pg_aggregate_aggsortop		4
  #define Anum_pg_aggregate_aggtranstype	5
! #define Anum_pg_aggregate_agginitval	6
  
  
  /* ----------------
--- 63,76 ----
   * ----------------
   */
  
! #define Natts_pg_aggregate				7
  #define Anum_pg_aggregate_aggfnoid		1
  #define Anum_pg_aggregate_aggtransfn	2
  #define Anum_pg_aggregate_aggfinalfn	3
  #define Anum_pg_aggregate_aggsortop		4
  #define Anum_pg_aggregate_aggtranstype	5
! #define Anum_pg_aggregate_aggisordsetfunc	6
! #define Anum_pg_aggregate_agginitval	7
  
  
  /* ----------------
***************
*** 77,240 **** typedef FormData_pg_aggregate *Form_pg_aggregate;
   */
  
  /* avg */
! DATA(insert ( 2100	int8_avg_accum	numeric_avg		0	1231	"{0,0}" ));
! DATA(insert ( 2101	int4_avg_accum	int8_avg		0	1016	"{0,0}" ));
! DATA(insert ( 2102	int2_avg_accum	int8_avg		0	1016	"{0,0}" ));
! DATA(insert ( 2103	numeric_avg_accum	numeric_avg		0	1231	"{0,0}" ));
! DATA(insert ( 2104	float4_accum	float8_avg		0	1022	"{0,0,0}" ));
! DATA(insert ( 2105	float8_accum	float8_avg		0	1022	"{0,0,0}" ));
! DATA(insert ( 2106	interval_accum	interval_avg	0	1187	"{0 second,0 second}" ));
  
  /* sum */
! DATA(insert ( 2107	int8_sum		-				0	1700	_null_ ));
! DATA(insert ( 2108	int4_sum		-				0	20		_null_ ));
! DATA(insert ( 2109	int2_sum		-				0	20		_null_ ));
! DATA(insert ( 2110	float4pl		-				0	700		_null_ ));
! DATA(insert ( 2111	float8pl		-				0	701		_null_ ));
! DATA(insert ( 2112	cash_pl			-				0	790		_null_ ));
! DATA(insert ( 2113	interval_pl		-				0	1186	_null_ ));
! DATA(insert ( 2114	numeric_add		-				0	1700	_null_ ));
  
  /* max */
! DATA(insert ( 2115	int8larger		-				413		20		_null_ ));
! DATA(insert ( 2116	int4larger		-				521		23		_null_ ));
! DATA(insert ( 2117	int2larger		-				520		21		_null_ ));
! DATA(insert ( 2118	oidlarger		-				610		26		_null_ ));
! DATA(insert ( 2119	float4larger	-				623		700		_null_ ));
! DATA(insert ( 2120	float8larger	-				674		701		_null_ ));
! DATA(insert ( 2121	int4larger		-				563		702		_null_ ));
! DATA(insert ( 2122	date_larger		-				1097	1082	_null_ ));
! DATA(insert ( 2123	time_larger		-				1112	1083	_null_ ));
! DATA(insert ( 2124	timetz_larger	-				1554	1266	_null_ ));
! DATA(insert ( 2125	cashlarger		-				903		790		_null_ ));
! DATA(insert ( 2126	timestamp_larger	-			2064	1114	_null_ ));
! DATA(insert ( 2127	timestamptz_larger	-			1324	1184	_null_ ));
! DATA(insert ( 2128	interval_larger -				1334	1186	_null_ ));
! DATA(insert ( 2129	text_larger		-				666		25		_null_ ));
! DATA(insert ( 2130	numeric_larger	-				1756	1700	_null_ ));
! DATA(insert ( 2050	array_larger	-				1073	2277	_null_ ));
! DATA(insert ( 2244	bpchar_larger	-				1060	1042	_null_ ));
! DATA(insert ( 2797	tidlarger		-				2800	27		_null_ ));
! DATA(insert ( 3526	enum_larger		-				3519	3500	_null_ ));
  
  /* min */
! DATA(insert ( 2131	int8smaller		-				412		20		_null_ ));
! DATA(insert ( 2132	int4smaller		-				97		23		_null_ ));
! DATA(insert ( 2133	int2smaller		-				95		21		_null_ ));
! DATA(insert ( 2134	oidsmaller		-				609		26		_null_ ));
! DATA(insert ( 2135	float4smaller	-				622		700		_null_ ));
! DATA(insert ( 2136	float8smaller	-				672		701		_null_ ));
! DATA(insert ( 2137	int4smaller		-				562		702		_null_ ));
! DATA(insert ( 2138	date_smaller	-				1095	1082	_null_ ));
! DATA(insert ( 2139	time_smaller	-				1110	1083	_null_ ));
! DATA(insert ( 2140	timetz_smaller	-				1552	1266	_null_ ));
! DATA(insert ( 2141	cashsmaller		-				902		790		_null_ ));
! DATA(insert ( 2142	timestamp_smaller	-			2062	1114	_null_ ));
! DATA(insert ( 2143	timestamptz_smaller -			1322	1184	_null_ ));
! DATA(insert ( 2144	interval_smaller	-			1332	1186	_null_ ));
! DATA(insert ( 2145	text_smaller	-				664		25		_null_ ));
! DATA(insert ( 2146	numeric_smaller -				1754	1700	_null_ ));
! DATA(insert ( 2051	array_smaller	-				1072	2277	_null_ ));
! DATA(insert ( 2245	bpchar_smaller	-				1058	1042	_null_ ));
! DATA(insert ( 2798	tidsmaller		-				2799	27		_null_ ));
! DATA(insert ( 3527	enum_smaller	-				3518	3500	_null_ ));
  
  /* count */
! DATA(insert ( 2147	int8inc_any		-				0		20		"0" ));
! DATA(insert ( 2803	int8inc			-				0		20		"0" ));
  
  /* var_pop */
! DATA(insert ( 2718	int8_accum	numeric_var_pop 0	1231	"{0,0,0}" ));
! DATA(insert ( 2719	int4_accum	numeric_var_pop 0	1231	"{0,0,0}" ));
! DATA(insert ( 2720	int2_accum	numeric_var_pop 0	1231	"{0,0,0}" ));
! DATA(insert ( 2721	float4_accum	float8_var_pop 0	1022	"{0,0,0}" ));
! DATA(insert ( 2722	float8_accum	float8_var_pop 0	1022	"{0,0,0}" ));
! DATA(insert ( 2723	numeric_accum  numeric_var_pop 0	1231	"{0,0,0}" ));
  
  /* var_samp */
! DATA(insert ( 2641	int8_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
! DATA(insert ( 2642	int4_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
! DATA(insert ( 2643	int2_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
! DATA(insert ( 2644	float4_accum	float8_var_samp 0	1022	"{0,0,0}" ));
! DATA(insert ( 2645	float8_accum	float8_var_samp 0	1022	"{0,0,0}" ));
! DATA(insert ( 2646	numeric_accum  numeric_var_samp 0	1231	"{0,0,0}" ));
  
  /* variance: historical Postgres syntax for var_samp */
! DATA(insert ( 2148	int8_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
! DATA(insert ( 2149	int4_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
! DATA(insert ( 2150	int2_accum	numeric_var_samp	0	1231	"{0,0,0}" ));
! DATA(insert ( 2151	float4_accum	float8_var_samp 0	1022	"{0,0,0}" ));
! DATA(insert ( 2152	float8_accum	float8_var_samp 0	1022	"{0,0,0}" ));
! DATA(insert ( 2153	numeric_accum  numeric_var_samp 0	1231	"{0,0,0}" ));
  
  /* stddev_pop */
! DATA(insert ( 2724	int8_accum	numeric_stddev_pop		0	1231	"{0,0,0}" ));
! DATA(insert ( 2725	int4_accum	numeric_stddev_pop		0	1231	"{0,0,0}" ));
! DATA(insert ( 2726	int2_accum	numeric_stddev_pop		0	1231	"{0,0,0}" ));
! DATA(insert ( 2727	float4_accum	float8_stddev_pop	0	1022	"{0,0,0}" ));
! DATA(insert ( 2728	float8_accum	float8_stddev_pop	0	1022	"{0,0,0}" ));
! DATA(insert ( 2729	numeric_accum	numeric_stddev_pop	0	1231	"{0,0,0}" ));
  
  /* stddev_samp */
! DATA(insert ( 2712	int8_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
! DATA(insert ( 2713	int4_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
! DATA(insert ( 2714	int2_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
! DATA(insert ( 2715	float4_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
! DATA(insert ( 2716	float8_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
! DATA(insert ( 2717	numeric_accum	numeric_stddev_samp 0	1231	"{0,0,0}" ));
  
  /* stddev: historical Postgres syntax for stddev_samp */
! DATA(insert ( 2154	int8_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
! DATA(insert ( 2155	int4_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
! DATA(insert ( 2156	int2_accum	numeric_stddev_samp		0	1231	"{0,0,0}" ));
! DATA(insert ( 2157	float4_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
! DATA(insert ( 2158	float8_accum	float8_stddev_samp	0	1022	"{0,0,0}" ));
! DATA(insert ( 2159	numeric_accum	numeric_stddev_samp 0	1231	"{0,0,0}" ));
  
  /* SQL2003 binary regression aggregates */
! DATA(insert ( 2818	int8inc_float8_float8		-				0	20		"0" ));
! DATA(insert ( 2819	float8_regr_accum	float8_regr_sxx			0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2820	float8_regr_accum	float8_regr_syy			0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2821	float8_regr_accum	float8_regr_sxy			0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2822	float8_regr_accum	float8_regr_avgx		0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2823	float8_regr_accum	float8_regr_avgy		0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2824	float8_regr_accum	float8_regr_r2			0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2825	float8_regr_accum	float8_regr_slope		0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2826	float8_regr_accum	float8_regr_intercept	0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2827	float8_regr_accum	float8_covar_pop		0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2828	float8_regr_accum	float8_covar_samp		0	1022	"{0,0,0,0,0,0}" ));
! DATA(insert ( 2829	float8_regr_accum	float8_corr				0	1022	"{0,0,0,0,0,0}" ));
  
  /* boolean-and and boolean-or */
! DATA(insert ( 2517	booland_statefunc	-			58	16		_null_ ));
! DATA(insert ( 2518	boolor_statefunc	-			59	16		_null_ ));
! DATA(insert ( 2519	booland_statefunc	-			58	16		_null_ ));
  
  /* bitwise integer */
! DATA(insert ( 2236 int2and		  -					0	21		_null_ ));
! DATA(insert ( 2237 int2or		  -					0	21		_null_ ));
! DATA(insert ( 2238 int4and		  -					0	23		_null_ ));
! DATA(insert ( 2239 int4or		  -					0	23		_null_ ));
! DATA(insert ( 2240 int8and		  -					0	20		_null_ ));
! DATA(insert ( 2241 int8or		  -					0	20		_null_ ));
! DATA(insert ( 2242 bitand		  -					0	1560	_null_ ));
! DATA(insert ( 2243 bitor		  -					0	1560	_null_ ));
  
  /* xml */
! DATA(insert ( 2901 xmlconcat2	  -					0	142		_null_ ));
  
  /* array */
! DATA(insert ( 2335	array_agg_transfn	array_agg_finalfn		0	2281	_null_ ));
  
  /* text */
! DATA(insert ( 3538	string_agg_transfn	string_agg_finalfn		0	2281	_null_ ));
  
  /* bytea */
! DATA(insert ( 3545	bytea_string_agg_transfn	bytea_string_agg_finalfn		0	2281	_null_ ));
  
  /* json */
! DATA(insert ( 3175	json_agg_transfn	json_agg_finalfn		0	2281	_null_ ));
  
  /*
   * prototypes for functions in pg_aggregate.c
   */
--- 79,246 ----
   */
  
  /* avg */
! DATA(insert ( 2100	int8_avg_accum	numeric_avg		0	1231	f "{0,0}" ));
! DATA(insert ( 2101	int4_avg_accum	int8_avg		0	1016	f "{0,0}" ));
! DATA(insert ( 2102	int2_avg_accum	int8_avg		0	1016	f "{0,0}" ));
! DATA(insert ( 2103	numeric_avg_accum	numeric_avg		0 1231	f "{0,0}" ));
! DATA(insert ( 2104	float4_accum	float8_avg		0	1022	f "{0,0,0}" ));
! DATA(insert ( 2105	float8_accum	float8_avg		0	1022	f "{0,0,0}" ));
! DATA(insert ( 2106	interval_accum	interval_avg	0	1187	f "{0 second,0 second}" ));
  
  /* sum */
! DATA(insert ( 2107	int8_sum		-				0	1700	f _null_ ));
! DATA(insert ( 2108	int4_sum		-				0	20		f _null_ ));
! DATA(insert ( 2109	int2_sum		-				0	20		f _null_ ));
! DATA(insert ( 2110	float4pl		-				0	700		f _null_ ));
! DATA(insert ( 2111	float8pl		-				0	701		f _null_ ));
! DATA(insert ( 2112	cash_pl			-				0	790		f _null_ ));
! DATA(insert ( 2113	interval_pl		-				0	1186	f _null_ ));
! DATA(insert ( 2114	numeric_add		-				0	1700	f _null_ ));
  
  /* max */
! DATA(insert ( 2115	int8larger		-				413		20		f _null_ ));
! DATA(insert ( 2116	int4larger		-				521		23		f _null_ ));
! DATA(insert ( 2117	int2larger		-				520		21		f _null_ ));
! DATA(insert ( 2118	oidlarger		-				610		26		f _null_ ));
! DATA(insert ( 2119	float4larger	-				623		700		f _null_ ));
! DATA(insert ( 2120	float8larger	-				674		701		f _null_ ));
! DATA(insert ( 2121	int4larger		-				563		702		f _null_ ));
! DATA(insert ( 2122	date_larger		-				1097	1082	f _null_ ));
! DATA(insert ( 2123	time_larger		-				1112	1083	f _null_ ));
! DATA(insert ( 2124	timetz_larger	-				1554	1266	f _null_ ));
! DATA(insert ( 2125	cashlarger		-				903		790		f _null_ ));
! DATA(insert ( 2126	timestamp_larger	-			2064	1114	f _null_ ));
! DATA(insert ( 2127	timestamptz_larger	-			1324	1184	f _null_ ));
! DATA(insert ( 2128	interval_larger -				1334	1186	f _null_ ));
! DATA(insert ( 2129	text_larger		-				666		25		f _null_ ));
! DATA(insert ( 2130	numeric_larger	-				1756	1700	f _null_ ));
! DATA(insert ( 2050	array_larger	-				1073	2277	f _null_ ));
! DATA(insert ( 2244	bpchar_larger	-				1060	1042	f _null_ ));
! DATA(insert ( 2797	tidlarger		-				2800	27		f _null_ ));
! DATA(insert ( 3526	enum_larger		-				3519	3500	f _null_ ));
  
  /* min */
! DATA(insert ( 2131	int8smaller		-				412		20		f _null_ ));
! DATA(insert ( 2132	int4smaller		-				97		23		f _null_ ));
! DATA(insert ( 2133	int2smaller		-				95		21		f _null_ ));
! DATA(insert ( 2134	oidsmaller		-				609		26		f _null_ ));
! DATA(insert ( 2135	float4smaller	-				622		700		f _null_ ));
! DATA(insert ( 2136	float8smaller	-				672		701		f _null_ ));
! DATA(insert ( 2137	int4smaller		-				562		702		f _null_ ));
! DATA(insert ( 2138	date_smaller	-				1095	1082	f _null_ ));
! DATA(insert ( 2139	time_smaller	-				1110	1083	f _null_ ));
! DATA(insert ( 2140	timetz_smaller	-				1552	1266	f _null_ ));
! DATA(insert ( 2141	cashsmaller		-				902		790		f _null_ ));
! DATA(insert ( 2142	timestamp_smaller	-			2062	1114	f _null_ ));
! DATA(insert ( 2143	timestamptz_smaller -			1322	1184	f _null_ ));
! DATA(insert ( 2144	interval_smaller	-			1332	1186	f _null_ ));
! DATA(insert ( 2145	text_smaller	-				664		25		f _null_ ));
! DATA(insert ( 2146	numeric_smaller -				1754	1700	f _null_ ));
! DATA(insert ( 2051	array_smaller	-				1072	2277	f _null_ ));
! DATA(insert ( 2245	bpchar_smaller	-				1058	1042	f _null_ ));
! DATA(insert ( 2798	tidsmaller		-				2799	27		f _null_ ));
! DATA(insert ( 3527	enum_smaller	-				3518	3500	f _null_ ));
  
  /* count */
! DATA(insert ( 2147	int8inc_any		-				0		20		f "0" ));
! DATA(insert ( 2803	int8inc			-				0		20		f "0" ));
  
  /* var_pop */
! DATA(insert ( 2718	int8_accum	numeric_var_pop 0	1231	f "{0,0,0}" ));
! DATA(insert ( 2719	int4_accum	numeric_var_pop 0	1231	f "{0,0,0}" ));
! DATA(insert ( 2720	int2_accum	numeric_var_pop 0	1231	f "{0,0,0}" ));
! DATA(insert ( 2721	float4_accum	float8_var_pop 0	1022	f "{0,0,0}" ));
! DATA(insert ( 2722	float8_accum	float8_var_pop 0	1022	f "{0,0,0}" ));
! DATA(insert ( 2723	numeric_accum  numeric_var_pop 0	1231	f "{0,0,0}" ));
  
  /* var_samp */
! DATA(insert ( 2641	int8_accum	numeric_var_samp	0	1231	f "{0,0,0}" ));
! DATA(insert ( 2642	int4_accum	numeric_var_samp	0	1231	f "{0,0,0}" ));
! DATA(insert ( 2643	int2_accum	numeric_var_samp	0	1231	f "{0,0,0}" ));
! DATA(insert ( 2644	float4_accum	float8_var_samp 0	1022	f "{0,0,0}" ));
! DATA(insert ( 2645	float8_accum	float8_var_samp 0	1022	f "{0,0,0}" ));
! DATA(insert ( 2646	numeric_accum  numeric_var_samp 0	1231	f "{0,0,0}" ));
  
  /* variance: historical Postgres syntax for var_samp */
! DATA(insert ( 2148	int8_accum	numeric_var_samp	0	1231	f "{0,0,0}" ));
! DATA(insert ( 2149	int4_accum	numeric_var_samp	0	1231	f "{0,0,0}" ));
! DATA(insert ( 2150	int2_accum	numeric_var_samp	0	1231	f "{0,0,0}" ));
! DATA(insert ( 2151	float4_accum	float8_var_samp 0	1022	f "{0,0,0}" ));
! DATA(insert ( 2152	float8_accum	float8_var_samp 0	1022	f "{0,0,0}" ));
! DATA(insert ( 2153	numeric_accum  numeric_var_samp 0	1231	f "{0,0,0}" ));
  
  /* stddev_pop */
! DATA(insert ( 2724	int8_accum	numeric_stddev_pop		0	1231	f "{0,0,0}" ));
! DATA(insert ( 2725	int4_accum	numeric_stddev_pop		0	1231	f "{0,0,0}" ));
! DATA(insert ( 2726	int2_accum	numeric_stddev_pop		0	1231	f "{0,0,0}" ));
! DATA(insert ( 2727	float4_accum	float8_stddev_pop	0	1022	f "{0,0,0}" ));
! DATA(insert ( 2728	float8_accum	float8_stddev_pop	0	1022	f "{0,0,0}" ));
! DATA(insert ( 2729	numeric_accum	numeric_stddev_pop	0	1231	f "{0,0,0}" ));
  
  /* stddev_samp */
! DATA(insert ( 2712	int8_accum	numeric_stddev_samp		0	1231	f "{0,0,0}" ));
! DATA(insert ( 2713	int4_accum	numeric_stddev_samp		0	1231	f "{0,0,0}" ));
! DATA(insert ( 2714	int2_accum	numeric_stddev_samp		0	1231	f "{0,0,0}" ));
! DATA(insert ( 2715	float4_accum	float8_stddev_samp	0	1022	f "{0,0,0}" ));
! DATA(insert ( 2716	float8_accum	float8_stddev_samp	0	1022	f "{0,0,0}" ));
! DATA(insert ( 2717	numeric_accum	numeric_stddev_samp 0	1231	f "{0,0,0}" ));
  
  /* stddev: historical Postgres syntax for stddev_samp */
! DATA(insert ( 2154	int8_accum	numeric_stddev_samp		0	1231	f "{0,0,0}" ));
! DATA(insert ( 2155	int4_accum	numeric_stddev_samp		0	1231	f "{0,0,0}" ));
! DATA(insert ( 2156	int2_accum	numeric_stddev_samp		0	1231	f "{0,0,0}" ));
! DATA(insert ( 2157	float4_accum	float8_stddev_samp	0	1022	f "{0,0,0}" ));
! DATA(insert ( 2158	float8_accum	float8_stddev_samp	0	1022	f "{0,0,0}" ));
! DATA(insert ( 2159	numeric_accum	numeric_stddev_samp 0	1231	f "{0,0,0}" ));
  
  /* SQL2003 binary regression aggregates */
! DATA(insert ( 2818	int8inc_float8_float8		-				0	20		f "0" ));
! DATA(insert ( 2819	float8_regr_accum	float8_regr_sxx			0	1022	f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2820	float8_regr_accum	float8_regr_syy			0	1022	f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2821	float8_regr_accum	float8_regr_sxy			0	1022	f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2822	float8_regr_accum	float8_regr_avgx		0	1022	f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2823	float8_regr_accum	float8_regr_avgy		0	1022	f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2824	float8_regr_accum	float8_regr_r2			0	1022	f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2825	float8_regr_accum	float8_regr_slope		0	1022	f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2826	float8_regr_accum	float8_regr_intercept	0	1022	f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2827	float8_regr_accum	float8_covar_pop		0	1022	f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2828	float8_regr_accum	float8_covar_samp		0	1022	f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2829	float8_regr_accum	float8_corr				0	1022	f "{0,0,0,0,0,0}" ));
  
  /* boolean-and and boolean-or */
! DATA(insert ( 2517	booland_statefunc	-			58	16		f _null_ ));
! DATA(insert ( 2518	boolor_statefunc	-			59	16		f _null_ ));
! DATA(insert ( 2519	booland_statefunc	-			58	16		f _null_ ));
  
  /* bitwise integer */
! DATA(insert ( 2236 int2and		  -					0	21		f _null_ ));
! DATA(insert ( 2237 int2or		  -					0	21		f _null_ ));
! DATA(insert ( 2238 int4and		  -					0	23		f _null_ ));
! DATA(insert ( 2239 int4or		  -					0	23		f _null_ ));
! DATA(insert ( 2240 int8and		  -					0	20		f _null_ ));
! DATA(insert ( 2241 int8or		  -					0	20		f _null_ ));
! DATA(insert ( 2242 bitand		  -					0	1560	f _null_ ));
! DATA(insert ( 2243 bitor		  -					0	1560	f _null_ ));
  
  /* xml */
! DATA(insert ( 2901 xmlconcat2	  -					0	142		f _null_ ));
  
  /* array */
! DATA(insert ( 2335	array_agg_transfn	array_agg_finalfn		0	2281	f _null_ ));
  
  /* text */
! DATA(insert ( 3538	string_agg_transfn	string_agg_finalfn		0	2281	f _null_ ));
  
  /* bytea */
! DATA(insert ( 3545	bytea_string_agg_transfn	bytea_string_agg_finalfn		0	2281	f _null_ ));
  
  /* json */
! DATA(insert ( 3175	json_agg_transfn	json_agg_finalfn		0	2281	f _null_ ));
  
+ /* ordered set function */
+ DATA(insert ( 3931 	- 			percentile_disc_final		0 	0 	t _null_));
+ DATA(insert ( 3935 	- 			percentile_cont_final		0 	0 	t _null_));
+ DATA(insert ( 3939 	- 			percentile_interval_cont_final	0 	0 	t _null_));
  /*
   * prototypes for functions in pg_aggregate.c
   */
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 4727,4732 **** DESCR("SP-GiST support for quad tree over range");
--- 4727,4752 ----
  /* event triggers */
  DATA(insert OID = 3566 (  pg_event_trigger_dropped_objects		PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,25,25,25,25}" "{o,o,o,o,o,o,o}" "{classid, objid, objsubid, object_type, schema_name, object_name, object_identity}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
  DESCR("list objects dropped by the current command");
+ 
+ /* inverse distribution functions */
+ DATA(insert OID = 3931 ( percentile_disc	PGNSP PGUID 12 1 0 0 0 t f f f t f i 2 0 701 "701 701" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("percentile_disc");
+ 
+ DATA(insert OID = 3932 ( percentile_disc_final	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 701 "701" _null_ _null_ _null_ _null_ percentile_disc_final _null_ _null_ _null_ ));
+ DESCR("percentile_disc_final");
+ 
+ DATA(insert OID = 3935 ( percentile_cont	PGNSP PGUID 12 1 0 0 0 t f f f t f i 2 0 701 "701 701" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("percentile_cont");
+ 
+ DATA(insert OID = 3936 ( percentile_cont_final	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 701 "701" _null_ _null_ _null_ _null_ percentile_cont_final _null_ _null_ _null_ ));
+ DESCR("percentile_cont_final");
+ 
+ DATA(insert OID = 3939 ( percentile_cont	PGNSP PGUID 12 1 0 0 0 t f f f t f i 2 0 1186 "701 1186" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("percentile_interval_cont");
+ 
+ DATA(insert OID = 3940 ( percentile_interval_cont_final	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1186 "701" _null_ _null_ _null_ _null_ percentile_interval_cont_final _null_ _null_ _null_ ));
+ DESCR("percentile_interval_cont_final");
+ 
  /*
   * Symbolic values for provolatile column: these indicate whether the result
   * of a function is dependent *only* on the values of its explicit arguments,
*** a/src/include/executor/nodeAgg.h
--- b/src/include/executor/nodeAgg.h
***************
*** 25,28 **** extern Size hash_agg_entry_size(int numAggs);
--- 25,31 ----
  
  extern Datum aggregate_dummy(PG_FUNCTION_ARGS);
  
+ extern int64 AggSetGetRowCount(FunctionCallInfo fcinfo);
+ 
+ 
  #endif   /* NODEAGG_H */
*** a/src/include/fmgr.h
--- b/src/include/fmgr.h
***************
*** 647,655 **** extern void **find_rendezvous_variable(const char *varName);
--- 647,668 ----
  /* AggCheckCallContext can return one of the following codes, or 0: */
  #define AGG_CONTEXT_AGGREGATE	1		/* regular aggregate */
  #define AGG_CONTEXT_WINDOW		2		/* window function */
+ #define AGG_CONTEXT_ORDERED		3		/* ordered set function */
  
  extern int AggCheckCallContext(FunctionCallInfo fcinfo,
  					MemoryContext *aggcontext);
+ extern int64 AggSetGetRowCount(FunctionCallInfo fcinfo);
+ 
+ typedef struct Tuplesortstate fmTuplesortstate;
+ typedef struct tupleDesc *fmTupleDesc;
+ typedef struct TupleTableSlot fmTupleTableSlot;
+ 
+ extern void AggSetGetSortInfo(FunctionCallInfo fcinfo,
+ 							  fmTuplesortstate **sortstate,
+ 							  fmTupleDesc *tupdesc,
+ 							  fmTupleTableSlot **tupslot,
+ 							  Oid *datumtype);
+ 
  
  /*
   * We allow plugin modules to hook function entry/exit.  This is intended
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
***************
*** 588,593 **** typedef struct AggrefExprState
--- 588,594 ----
  {
  	ExprState	xprstate;
  	List	   *args;			/* states of argument expressions */
+ 	List	   *orddirectargs;	/* Ordered direct arguments */
  	ExprState  *aggfilter;		/* FILTER expression */
  	int			aggno;			/* ID number for agg within its plan node */
  } AggrefExprState;
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 425,431 **** typedef enum NodeTag
  	T_WindowObjectData,			/* private in nodeWindowAgg.c */
  	T_TIDBitmap,				/* in nodes/tidbitmap.h */
  	T_InlineCodeBlock,			/* in nodes/parsenodes.h */
! 	T_FdwRoutine				/* in foreign/fdwapi.h */
  } NodeTag;
  
  /*
--- 425,432 ----
  	T_WindowObjectData,			/* private in nodeWindowAgg.c */
  	T_TIDBitmap,				/* in nodes/tidbitmap.h */
  	T_InlineCodeBlock,			/* in nodes/parsenodes.h */
! 	T_FdwRoutine,				/* in foreign/fdwapi.h */
! 	T_AggStatePerAggData			/* private in nodeAgg.c */
  } NodeTag;
  
  /*
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 303,308 **** typedef struct FuncCall
--- 303,309 ----
  	bool		agg_star;		/* argument was really '*' */
  	bool		agg_distinct;	/* arguments were labeled DISTINCT */
  	bool		func_variadic;	/* last argument was labeled VARIADIC */
+ 	bool		has_within_group;	/* WITHIN GROUP clause,if any */
  	struct WindowDef *over;		/* OVER clause, if any */
  	int			location;		/* token location, or -1 if unknown */
  } FuncCall;
*** a/src/include/nodes/primnodes.h
--- b/src/include/nodes/primnodes.h
***************
*** 247,254 **** typedef struct Aggref
--- 247,256 ----
  	List	   *args;			/* arguments and sort expressions */
  	List	   *aggorder;		/* ORDER BY (list of SortGroupClause) */
  	List	   *aggdistinct;	/* DISTINCT (list of SortGroupClause) */
+ 	List	   *orddirectargs;	/* Direct arguments for ordered set functions */
  	Expr	   *aggfilter;		/* FILTER expression */
  	bool		aggstar;		/* TRUE if argument list was really '*' */
+ 	bool		isordset;	/* If node is from an ordered set function */
  	Index		agglevelsup;	/* > 0 if agg belongs to outer query */
  	int			location;		/* token location, or -1 if unknown */
  } Aggref;
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
***************
*** 411,416 **** PG_KEYWORD("where", WHERE, RESERVED_KEYWORD)
--- 411,417 ----
  PG_KEYWORD("whitespace", WHITESPACE_P, UNRESERVED_KEYWORD)
  PG_KEYWORD("window", WINDOW, RESERVED_KEYWORD)
  PG_KEYWORD("with", WITH, RESERVED_KEYWORD)
+ PG_KEYWORD("within", WITHIN, UNRESERVED_KEYWORD)
  PG_KEYWORD("without", WITHOUT, UNRESERVED_KEYWORD)
  PG_KEYWORD("work", WORK, UNRESERVED_KEYWORD)
  PG_KEYWORD("wrapper", WRAPPER, UNRESERVED_KEYWORD)
*** a/src/include/parser/parse_agg.h
--- b/src/include/parser/parse_agg.h
***************
*** 17,23 ****
  
  extern void transformAggregateCall(ParseState *pstate, Aggref *agg,
  					   List *args, List *aggorder,
! 					   bool agg_distinct);
  extern void transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
  						WindowDef *windef);
  
--- 17,23 ----
  
  extern void transformAggregateCall(ParseState *pstate, Aggref *agg,
  					   List *args, List *aggorder,
! 					   bool agg_distinct, bool agg_within_group);
  extern void transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
  						WindowDef *windef);
  
*** a/src/include/parser/parse_func.h
--- b/src/include/parser/parse_func.h
***************
*** 38,51 **** typedef enum
  	FUNCDETAIL_NORMAL,			/* found a matching regular function */
  	FUNCDETAIL_AGGREGATE,		/* found a matching aggregate function */
  	FUNCDETAIL_WINDOWFUNC,		/* found a matching window function */
! 	FUNCDETAIL_COERCION			/* it's a type coercion request */
  } FuncDetailCode;
  
- 
  extern Node *ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
  				  List *agg_order, Expr *agg_filter,
  				  bool agg_star, bool agg_distinct, bool func_variadic,
! 				  WindowDef *over, bool is_column, int location);
  
  extern FuncDetailCode func_get_detail(List *funcname,
  				List *fargs, List *fargnames,
--- 38,51 ----
  	FUNCDETAIL_NORMAL,			/* found a matching regular function */
  	FUNCDETAIL_AGGREGATE,		/* found a matching aggregate function */
  	FUNCDETAIL_WINDOWFUNC,		/* found a matching window function */
! 	FUNCDETAIL_COERCION,			/* it's a type coercion request */
! 	FUNCDETAIL_ORDERED		/* ordered function */
  } FuncDetailCode;
  
  extern Node *ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
  				  List *agg_order, Expr *agg_filter,
  				  bool agg_star, bool agg_distinct, bool func_variadic,
! 				  bool agg_within_group, WindowDef *over, bool is_column, int location);
  
  extern FuncDetailCode func_get_detail(List *funcname,
  				List *fargs, List *fargnames,
***************
*** 66,71 **** extern FuncCandidateList func_select_candidate(int nargs,
--- 66,72 ----
  
  extern void make_fn_arguments(ParseState *pstate,
  				  List *fargs,
+ 				  List *agg_order,
  				  Oid *actual_arg_types,
  				  Oid *declared_arg_types);
  
*** a/src/test/regress/expected/aggregates.out
--- b/src/test/regress/expected/aggregates.out
***************
*** 1249,1251 **** select aggfns(distinct a,b,c order by a,c using ~<~,b) filter (where a > 1)
--- 1249,1307 ----
   {"(2,2,bar)","(3,1,baz)"}
  (1 row)
  
+ -- ordered set functions
+ select p, percentile_cont(p) within group (order by x::float8) from generate_series(1,5) x, 
+ 	(values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) 
+ 	group by p order by p;
+   p   | percentile_cont 
+ ------+-----------------
+     0 |               1
+   0.1 |             1.4
+  0.25 |               2
+   0.4 |             2.6
+   0.5 |               3
+   0.6 |             3.4
+  0.75 |               4
+   0.9 |             4.6
+     1 |               5
+ (9 rows)
+ 
+ select p, percentile_cont(p order by p) within group (order by x::float8) 
+ 	from generate_series(1,5) x, (values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) 
+ 	group by p order by x;
+ ERROR:  Cannot have multiple ORDER BY clauses with WITHIN GROUP
+ LINE 1: select p, percentile_cont(p order by p) within group (order ...
+                                                 ^
+ select p, sum() within group (order by x::float8) 
+ 	from generate_series(1,5) x, 
+ 	(values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) group by p order by p;
+ ERROR:  sum(double precision) is not an ordered set function
+ select p, percentile_cont(p,p) from generate_series(1,5) x, 
+ 	(values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) group by p order by p;
+ ERROR:  Ordered set function specified, but WITHIN GROUP not present
+ LINE 1: select p, percentile_cont(p,p) from generate_series(1,5) x, 
+                   ^
+ select percentile_cont(0.5) within group (order by b) from aggtest;
+  percentile_cont  
+ ------------------
+  53.4485001564026
+ (1 row)
+ 
+ select percentile_cont(0.5) within group (order by b),sum(b) from aggtest;
+  percentile_cont  |   sum   
+ ------------------+---------
+  53.4485001564026 | 431.773
+ (1 row)
+ 
+ select percentile_cont(0.5) within group (order by thousand) from tenk1;
+  percentile_cont 
+ -----------------
+            499.5
+ (1 row)
+ 
+ select percentile_disc(0.5) within group (order by thousand) from tenk1;
+  percentile_disc 
+ -----------------
+              499
+ (1 row)
+ 
*** a/src/test/regress/expected/opr_sanity.out
--- b/src/test/regress/expected/opr_sanity.out
***************
*** 700,708 **** SELECT * FROM funcdescs
  
  -- **************** pg_aggregate ****************
  -- Look for illegal values in pg_aggregate fields.
  SELECT ctid, aggfnoid::oid
  FROM pg_aggregate as p1
! WHERE aggfnoid = 0 OR aggtransfn = 0 OR aggtranstype = 0;
   ctid | aggfnoid 
  ------+----------
  (0 rows)
--- 700,716 ----
  
  -- **************** pg_aggregate ****************
  -- Look for illegal values in pg_aggregate fields.
+ -- ordered set functions can't have transfns, and must
+ -- have finalfns, but may or may not have transtypes.
+ -- other aggs must have transfns and transtypes with
+ -- optional finalfns.
  SELECT ctid, aggfnoid::oid
  FROM pg_aggregate as p1
! WHERE aggfnoid = 0
!    OR CASE WHEN aggisordsetfunc
!            THEN aggtransfn <> 0 OR aggfinalfn = 0
!            ELSE aggtransfn = 0 OR aggtranstype = 0
!       END;
   ctid | aggfnoid 
  ------+----------
  (0 rows)
***************
*** 764,771 **** WHERE a.aggfnoid = p.oid AND
      a.aggfinalfn = pfn.oid AND
      (pfn.proretset
       OR NOT binary_coercible(pfn.prorettype, p.prorettype)
!      OR pfn.pronargs != 1
!      OR NOT binary_coercible(a.aggtranstype, pfn.proargtypes[0]));
   aggfnoid | proname | oid | proname 
  ----------+---------+-----+---------
  (0 rows)
--- 772,780 ----
      a.aggfinalfn = pfn.oid AND
      (pfn.proretset
       OR NOT binary_coercible(pfn.prorettype, p.prorettype)
!      OR (aggisordsetfunc IS FALSE
!          AND (pfn.pronargs != 1
!               OR NOT binary_coercible(a.aggtranstype, pfn.proargtypes[0]))));
   aggfnoid | proname | oid | proname 
  ----------+---------+-----+---------
  (0 rows)
*** a/src/test/regress/sql/aggregates.sql
--- b/src/test/regress/sql/aggregates.sql
***************
*** 480,482 **** select sum(unique1) FILTER (WHERE
--- 480,501 ----
  select aggfns(distinct a,b,c order by a,c using ~<~,b) filter (where a > 1)
      from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c),
      generate_series(1,2) i;
+ 
+ -- ordered set functions
+ 
+ select p, percentile_cont(p) within group (order by x::float8) from generate_series(1,5) x, 
+ 	(values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) 
+ 	group by p order by p;
+ select p, percentile_cont(p order by p) within group (order by x::float8) 
+ 	from generate_series(1,5) x, (values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) 
+ 	group by p order by x;
+ select p, sum() within group (order by x::float8) 
+ 	from generate_series(1,5) x, 
+ 	(values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) group by p order by p;
+ select p, percentile_cont(p,p) from generate_series(1,5) x, 
+ 	(values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) group by p order by p;
+ select percentile_cont(0.5) within group (order by b) from aggtest;
+ select percentile_cont(0.5) within group (order by b),sum(b) from aggtest;
+ select percentile_cont(0.5) within group (order by thousand) from tenk1;
+ select percentile_disc(0.5) within group (order by thousand) from tenk1;
+ 
*** a/src/test/regress/sql/opr_sanity.sql
--- b/src/test/regress/sql/opr_sanity.sql
***************
*** 564,573 **** SELECT * FROM funcdescs
  -- **************** pg_aggregate ****************
  
  -- Look for illegal values in pg_aggregate fields.
  
  SELECT ctid, aggfnoid::oid
  FROM pg_aggregate as p1
! WHERE aggfnoid = 0 OR aggtransfn = 0 OR aggtranstype = 0;
  
  -- Make sure the matching pg_proc entry is sensible, too.
  
--- 564,581 ----
  -- **************** pg_aggregate ****************
  
  -- Look for illegal values in pg_aggregate fields.
+ -- ordered set functions can't have transfns, and must
+ -- have finalfns, but may or may not have transtypes.
+ -- other aggs must have transfns and transtypes with
+ -- optional finalfns.
  
  SELECT ctid, aggfnoid::oid
  FROM pg_aggregate as p1
! WHERE aggfnoid = 0
!    OR CASE WHEN aggisordsetfunc
!            THEN aggtransfn <> 0 OR aggfinalfn = 0
!            ELSE aggtransfn = 0 OR aggtranstype = 0
!       END;
  
  -- Make sure the matching pg_proc entry is sensible, too.
  
***************
*** 618,625 **** WHERE a.aggfnoid = p.oid AND
      a.aggfinalfn = pfn.oid AND
      (pfn.proretset
       OR NOT binary_coercible(pfn.prorettype, p.prorettype)
!      OR pfn.pronargs != 1
!      OR NOT binary_coercible(a.aggtranstype, pfn.proargtypes[0]));
  
  -- If transfn is strict then either initval should be non-NULL, or
  -- input type should match transtype so that the first non-null input
--- 626,634 ----
      a.aggfinalfn = pfn.oid AND
      (pfn.proretset
       OR NOT binary_coercible(pfn.prorettype, p.prorettype)
!      OR (aggisordsetfunc IS FALSE
!          AND (pfn.pronargs != 1
!               OR NOT binary_coercible(a.aggtranstype, pfn.proargtypes[0]))));
  
  -- If transfn is strict then either initval should be non-NULL, or
  -- input type should match transtype so that the first non-null input
