*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 352,358 ****
aggtransfnregprocpg_proc.oid
! Transition functionaggfinalfn
--- 352,358 ----
aggtransfnregprocpg_proc.oid
! Transition function (zero if none)aggfinalfn
***************
*** 370,376 ****
aggtranstypeoidpg_type.oid
! Data type of the aggregate function's internal transition (state) dataagginitval
--- 370,394 ----
aggtranstypeoidpg_type.oid
! Data type of the aggregate function's internal transition (state) data (zero if none)
!
!
! aggtranssortop
! oid
! pg_operator.oid
! An optional sort operator for the type "aggtranstype", used for some kinds of ordered set functions
!
!
! aggordnargs
! int4
!
! Number of direct arguments to ordered set function; -2 for hypothetical set functions; -1 for ordinary aggregates.
!
!
! aggisordsetfunc
! bool
!
! A flag to represent whether a function is ordered set or notagginitval
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 12197,12202 **** SELECT xmlagg(x) FROM (SELECT x FROM test ORDER BY y DESC) AS tab;
--- 12197,12453 ----
+
+ Ordered Set Functions
+
+
+ ordered set function
+ built-in
+
+
+
+ Ordered set functions compute a single result
+ from an ordered set of input values. The built-in ordered set functions
+ are listed in
+ and
+ .
+ The special syntax considerations for ordered set functions
+ are explained in .
+
+
+
+ Inverse Distribution Functions
+
+
+
+
+ Function
+ Direct Argument Type(s)
+ Ordered Argument Type(s)
+ Return Type
+ Description
+
+
+
+
+
+
+
+
+ percentile
+ discrete
+
+ percentile_disc(fraction) WITHIN GROUP (ORDER BY sort_expression)
+
+
+ double precision (must be [0..1])
+
+
+ any sortable type
+
+
+ same as sort expression
+
+
+ discrete percentile; returns the first result whose position in
+ the ordering equals or exceeds the specified fraction
+
+
+
+
+
+
+ percentile
+ discrete
+
+ percentile_disc(fractions) WITHIN GROUP (ORDER BY sort_expression)
+
+
+ double precision[] (all must be [0..1] or null)
+
+
+ any sortable type
+
+
+ array of input type
+
+
+ multiple discrete percentile; returns an array of results matching the
+ shape of the fractions parameter, with each
+ non-null element replaced by the input value at that percentile
+
+
+
+
+
+
+ percentile
+ continuous
+
+
+ median
+
+ percentile_cont(fraction) WITHIN GROUP (ORDER BY sort_expression)
+
+
+ double precision (must be [0..1])
+
+
+ double precision or interval
+
+
+ same as sort expression
+
+
+ continuous percentile; interpolates between adjacent items.
+
+
+
+
+
+
+ percentile
+ continuous
+
+ percentile_cont(fractions) WITHIN GROUP (ORDER BY sort_expression)
+
+
+ double precision[] (all must be [0..1] or null)
+
+
+ double precision or interval
+
+
+ array of input type
+
+
+ multiple continuous percentile; returns an array of results matching
+ the shape of the fractions parameter, with each
+ non-null element replaced by the value corresponding to that percentile
+
+
+
+
+
+
+ mode
+ statistical
+
+ mode() WITHIN GROUP (ORDER BY sort_expression)
+
+
+
+
+ any sortable type
+
+
+ same as sort expression
+
+
+ returns the most frequent input value (choosing one arbitrarily if
+ there are multiple equally good result)
+
+
+
+
+
+
+
+
+ All the inverse distribution functions ignore null values in their sorted
+ input. The fraction parameter must be between 0
+ and 1; an error is thrown if not. However, a null fraction simply produces
+ a null result.
+
+
+
+ Hypothetical Set Functions
+
+
+
+
+ Function
+ Return Type
+
+
+
+
+
+
+
+
+ rank
+ hypothetical
+
+ rank(args) WITHIN GROUP (ORDER BY sorted_args)
+
+
+ bigint
+
+
+
+
+
+
+ dense_rank
+ hypothetical
+
+ dense_rank(args) WITHIN GROUP (ORDER BY sorted_args)
+
+
+ bigint
+
+
+
+
+
+
+ percent_rank
+ hypothetical
+
+ percent_rank(args) WITHIN GROUP (ORDER BY sorted_args)
+
+
+ double precision
+
+
+
+
+
+
+ cume_dist
+ hypothetical
+
+ cume_dist(args) WITHIN GROUP (ORDER BY sorted_args)
+
+
+ double precision
+
+
+
+
+
+
+
+
+ For all hypothetical set functions, the list of arguments given
+ by args should match the number and types of
+ arguments given as sorted_args.
+
+
+
+ All of the functions listed in
+ are associated with a
+ window function defined in
+ . In each case, the function result
+ represents the value that the associated window function would have
+ returned, for the hypothetical row constructed from
+ args and included in the sorted group of
+ rows.
+
+
+
+
Window Functions
*** a/doc/src/sgml/ref/alter_aggregate.sgml
--- b/doc/src/sgml/ref/alter_aggregate.sgml
***************
*** 21,32 **** PostgreSQL documentation
! ALTER AGGREGATE name ( [ argmode ] [ arg_name ] arg_data_type [ , ... ] )
RENAME TO new_name
ALTER AGGREGATE name ( [ argmode ] [ arg_name ] arg_data_type [ , ... ] )
OWNER TO new_owner
ALTER AGGREGATE name ( [ argmode ] [ arg_name ] arg_data_type [ , ... ] )
SET SCHEMA new_schema
--- 21,37 ----
! ALTER AGGREGATE
RENAME TO new_name
ALTER AGGREGATE name ( [ argmode ] [ arg_name ] arg_data_type [ , ... ] )
OWNER TO new_owner
ALTER AGGREGATE name ( [ argmode ] [ arg_name ] arg_data_type [ , ... ] )
SET SCHEMA new_schema
+
+ where aggregate_signature is one of:
+
+ name ( * | [ argmode ] [ arg_name ] arg_data_type [ , ... ] )
+ name ( [ [ argmode ] [ arg_name ] arg_data_type [ , ... ] ] ) WITHIN GROUP ( * | [ argmode ] [ arg_name ] arg_data_type [ , ... ] )
***************
*** 148,157 **** ALTER AGGREGATE myavg(integer) OWNER TO joe;
! To move the aggregate function myavg for type
! integer into schema myschema:
! ALTER AGGREGATE myavg(integer) SET SCHEMA myschema;
--- 153,163 ----
! To move the ordered set function mypercentile with
! direct argument of type float8 taking groups
! of integer type into schema myschema:
! ALTER AGGREGATE mypercentile(float8) WITHIN GROUP (integer) SET SCHEMA myschema;
*** a/doc/src/sgml/ref/alter_extension.sgml
--- b/doc/src/sgml/ref/alter_extension.sgml
***************
*** 30,36 **** ALTER EXTENSION name DROP where member_object is:
! AGGREGATE agg_name ( [ argmode ] [ argname ] agg_type [, ...] ) |
CAST (source_type AS target_type) |
COLLATION object_name |
CONVERSION object_name |
--- 30,36 ----
where member_object is:
! AGGREGATE agg_name ( [ argmode ] [ argname ] agg_type [, ...] ) [ WITHIN GROUP ( * | [ argmode ] [ argname ] agg_type [, ...] ) ] |
CAST (source_type AS target_type) |
COLLATION object_name |
CONVERSION object_name |
*** a/doc/src/sgml/ref/comment.sgml
--- b/doc/src/sgml/ref/comment.sgml
***************
*** 23,29 **** PostgreSQL documentation
COMMENT ON
{
! AGGREGATE agg_name ( [ argmode ] [ argname ] agg_type [, ...] ) |
CAST (source_type AS target_type) |
COLLATION object_name |
COLUMN relation_name.column_name |
--- 23,29 ----
COMMENT ON
{
! AGGREGATE agg_name ( [ argmode ] [ argname ] agg_type [, ...] ) [ WITHIN GROUP ( * | [ argmode ] [ argname ] agg_type [, ...] ) ] |
CAST (source_type AS target_type) |
COLLATION object_name |
COLUMN relation_name.column_name |
*** a/doc/src/sgml/ref/create_aggregate.sgml
--- b/doc/src/sgml/ref/create_aggregate.sgml
***************
*** 29,34 **** CREATE AGGREGATE name ( [ sort_operator ]
)
+ CREATE AGGREGATE name ( [ argmode ] [ arg_name ] arg_data_type [ , ... ] ) WITHIN GROUP ( * | [ argmode ] [ arg_name ] arg_data_type [ , ... ] ) (
+ FINALFUNC = ffunc
+ [ , STRICT ]
+ [ , HYPOTHETICAL ]
+ [ , STYPE = state_data_type ]
+ [ , INITCOND = initial_condition ]
+ [ , TRANSSORTOP = state_sort_operator ]
+ )
+
or the old syntax
CREATE AGGREGATE name (
***************
*** 70,76 **** CREATE AGGREGATE name (
! An aggregate function is made from one or two ordinary
functions:
a state transition function
sfunc,
--- 79,85 ----
! An ordinary aggregate function is made from one or two ordinary
functions:
a state transition function
sfunc,
***************
*** 165,170 **** SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
--- 174,187 ----
+ The WITHIN GROUP syntax denotes a special subset of
+ aggregate functions collectively called ordered set
+ functions. These functions operate over groups of sorted values
+ in order-dependent ways. As such, they are constructed differently; there
+ is no state transition function, but the final function is required.
+
+
+
To be able to create an aggregate function, you must
have USAGE privilege on the argument types, the state
type, and the return type, as well as EXECUTE privilege
***************
*** 278,283 **** SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
--- 295,305 ----
aggregate's result, and the return type is state_data_type.
+
+ For ordered set functions, the function arguments must instead
+ correspond to the input arguments (both direct and grouped) plus
+ the state type if any.
+
***************
*** 305,310 **** SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
--- 327,365 ----
+
+
+ state_sort_operator
+
+
+ For ordered set functions only, this is a sort operator that can be
+ applied to
+ the state_data_type.
+ This is just an operator name (possibly schema-qualified).
+
+
+
+
+
+ STRICT
+
+
+ For ordered set functions only, this flag specifies that the function is
+ strict, i.e. that grouped rows containing nulls are skipped.
+
+
+
+
+
+ HYPOTHETICAL
+
+
+ For ordered set functions only, this flag specifies that the aggregate
+ parameters are to be processed according to the requirements for
+ hypothetical set functions.
+
+
+
*** a/doc/src/sgml/ref/drop_aggregate.sgml
--- b/doc/src/sgml/ref/drop_aggregate.sgml
***************
*** 21,29 **** PostgreSQL documentation
! DROP AGGREGATE [ IF EXISTS ]
! name ( [ argmode ] [ arg_name ] arg_data_type [ , ... ] )
[ CASCADE | RESTRICT ]
--- 21,33 ----
! DROP AGGREGATE [ IF EXISTS ] aggregate_signature
[ CASCADE | RESTRICT ]
+
+ where aggregate_signature is one of:
+
+ name ( * | [ argmode ] [ arg_name ] arg_data_type [ , ... ] )
+ name ( [ [ argmode ] [ arg_name ] arg_data_type [ , ... ] ] ) WITHIN GROUP ( * | [ argmode ] [ arg_name ] arg_data_type [ , ... ] )
*** a/doc/src/sgml/ref/security_label.sgml
--- b/doc/src/sgml/ref/security_label.sgml
***************
*** 25,31 **** SECURITY LABEL [ FOR provider ] ON
{
TABLE object_name |
COLUMN table_name.column_name |
! AGGREGATE agg_name ( [ argmode ] [ argname ] agg_type [, ...] ) |
DATABASE object_name |
DOMAIN object_name |
EVENT TRIGGER object_name |
--- 25,31 ----
{
TABLE object_name |
COLUMN table_name.column_name |
! AGGREGATE agg_name ( [ argmode ] [ argname ] agg_type [, ...] ) [ WITHIN GROUP ( * | [ argmode ] [ argname ] agg_type [, ...] ) ] |
DATABASE object_name |
DOMAIN object_name |
EVENT TRIGGER object_name |
*** a/doc/src/sgml/syntax.sgml
--- b/doc/src/sgml/syntax.sgml
***************
*** 1706,1711 **** SELECT string_agg(a ORDER BY a, ',') FROM table; -- incorrect
--- 1706,1759 ----
+
+ Ordered Set Functions
+
+
+ ordered set function
+
+
+
+ aggregate function
+ ordered set function
+
+
+
+ WITHIN GROUP
+
+
+
+ An ordered set function is a particular kind of
+ aggregate function which is applied to sorted groups of values and returns
+ a single result for each group which may be influenced by the sort
+ order. Like all aggregate functions, it reduces multiple inputs to a
+ single output value; typical ordered set functions return a percentile
+ extracted from the ordered group, or the rank a specified value would have
+ within that group. The syntax of an ordered set function is:
+
+
+ function_name ( [ expression [ , ... ] ] ) WITHIN GROUP ( order_by_clause ) [ FILTER ( WHERE filter_clause ) ]
+
+
+ where function_name is a previously
+ defined ordered set function (possibly qualified with a schema name) and
+ expression is any value expression that does
+ not itself contain an aggregate expression, a window function call, or any
+ reference to ungrouped columns of the source data. The
+ mandatory order_by_clause has the same syntax
+ as for a query-level ORDER BY> clause, as described
+ in , except that its expressions are always
+ just expressions and cannot be output-column names or numbers. The
+ expressions of the order_by_clause may, and
+ almost invariably do, refer to the ungrouped columns of the input; the
+ clause defines the grouped input to the function. The optional
+ filter_clause is identical to that for
+ aggregate functions (see , and is applied
+ to input rows prior to the sort operation.
+
+
+
+
Window Function Calls
*** a/doc/src/sgml/xaggr.sgml
--- b/doc/src/sgml/xaggr.sgml
***************
*** 9,28 ****
! Aggregate functions in PostgreSQL
! are expressed in terms of state values
! and state transition functions.
! That is, an aggregate operates using a state value that is updated
! as each successive input row is processed.
! To define a new aggregate
! function, one selects a data type for the state value,
! an initial value for the state, and a state transition
! function. The state transition function is just an
! ordinary function that could also be used outside the
! context of the aggregate. A final function
! can also be specified, in case the desired result of the aggregate
! is different from the data that needs to be kept in the running
! state value.
--- 9,25 ----
! Aggregate functions (other than ordered set functions)
! in PostgreSQL are expressed in terms
! of state values and state transition
! functions. That is, an aggregate operates using a state value
! that is updated as each successive input row is processed. To define a new
! aggregate function, one selects a data type for the state value, an initial
! value for the state, and a state transition function. The state transition
! function is just an ordinary function that could also be used outside the
! context of the aggregate. A final function can also
! be specified, in case the desired result of the aggregate is different from
! the data that needs to be kept in the running state value.
*** a/src/backend/catalog/pg_aggregate.c
--- b/src/backend/catalog/pg_aggregate.c
***************
*** 46,51 **** Oid
--- 46,52 ----
AggregateCreate(const char *aggName,
Oid aggNamespace,
int numArgs,
+ int numDirectArgs,
oidvector *parameterTypes,
Datum allParameterTypes,
Datum parameterModes,
***************
*** 54,77 **** AggregateCreate(const char *aggName,
List *aggtransfnName,
List *aggfinalfnName,
List *aggsortopName,
Oid aggTransType,
! const char *agginitval)
{
Relation aggdesc;
HeapTuple tup;
bool nulls[Natts_pg_aggregate];
Datum values[Natts_pg_aggregate];
Form_pg_proc proc;
! Oid transfn;
Oid finalfn = InvalidOid; /* can be omitted */
Oid sortop = InvalidOid; /* can be omitted */
Oid *aggArgTypes = parameterTypes->values;
bool hasPolyArg;
bool hasInternalArg;
Oid rettype;
Oid finaltype;
! Oid *fnArgs;
! int nargs_transfn;
Oid procOid;
TupleDesc tupDesc;
int i;
--- 55,83 ----
List *aggtransfnName,
List *aggfinalfnName,
List *aggsortopName,
+ List *aggtranssortopName,
Oid aggTransType,
! const char *agginitval,
! bool isStrict,
! bool isOrderedSet,
! bool isHypotheticalSet)
{
Relation aggdesc;
HeapTuple tup;
bool nulls[Natts_pg_aggregate];
Datum values[Natts_pg_aggregate];
Form_pg_proc proc;
! Oid transfn = InvalidOid; /* can be omitted */
Oid finalfn = InvalidOid; /* can be omitted */
Oid sortop = InvalidOid; /* can be omitted */
+ Oid transsortop = InvalidOid; /* Can be omitted */
Oid *aggArgTypes = parameterTypes->values;
bool hasPolyArg;
bool hasInternalArg;
+ Oid variadic_type = InvalidOid;
Oid rettype;
Oid finaltype;
! Oid *fnArgs = palloc((numArgs + 1) * sizeof(Oid));
Oid procOid;
TupleDesc tupDesc;
int i;
***************
*** 83,90 **** AggregateCreate(const char *aggName,
if (!aggName)
elog(ERROR, "no aggregate name supplied");
! if (!aggtransfnName)
! elog(ERROR, "aggregate must have a transition function");
/* check for polymorphic and INTERNAL arguments */
hasPolyArg = false;
--- 89,108 ----
if (!aggName)
elog(ERROR, "no aggregate name supplied");
! if (isOrderedSet)
! {
! if (aggtransfnName)
! elog(ERROR, "Ordered set functions cannot have transition functions");
! if (!aggfinalfnName)
! elog(ERROR, "Ordered set functions must have final functions");
! }
! else
! {
! if (!aggtransfnName)
! elog(ERROR, "aggregate must have a transition function");
! if (isStrict)
! elog(ERROR, "aggregate with transition function must not be explicitly STRICT");
! }
/* check for polymorphic and INTERNAL arguments */
hasPolyArg = false;
***************
*** 97,102 **** AggregateCreate(const char *aggName,
--- 115,250 ----
hasInternalArg = true;
}
+ /*-
+ * Argument mode checks. If there were no variadics, we should have been
+ * passed a NULL pointer for parameterModes, so we can skip this if so.
+ * Otherwise, the allowed cases are as follows:
+ *
+ * aggfn(..., variadic sometype) - normal agg with variadic arg last
+ * aggfn(..., variadic "any") - normal agg with "any" variadic
+ *
+ * ordfn(..., variadic "any") within group (*)
+ * - ordered set func with "any" variadic in direct args, which requires
+ * that the ordered args also be variadic any which we represent
+ * specially; this is the common case for hypothetical set functions.
+ * Note this is the only case where numDirectArgs == numArgs on input
+ * (implies finalfn(..., variadic "any"))
+ *
+ * ordfn(...) within group (..., variadic "any")
+ * - ordered set func with no variadic in direct args, but allowing any
+ * types of ordered args.
+ * (implies finalfn(..., ..., variadic "any"))
+ *
+ * We don't allow variadic ordered args other than "any"; we don't allow
+ * anything after variadic "any" except the special-case (*).
+ *
+ * We might like to support this one:
+ *
+ * ordfn(..., variadic sometype) within group (...)
+ * - ordered set func with variadic direct arg last, followed by ordered
+ * args, none of which are variadic
+ * (implies finalfn(..., sometype, ..., [transtype]))
+ *
+ * but currently it seems to be too intrusive to do so; the assumption
+ * that variadic args can only come last is quite widespread.
+ */
+
+ if (parameterModes != PointerGetDatum(NULL))
+ {
+ /*
+ * We expect the array to be a 1-D CHAR array; verify that. We don't
+ * need to use deconstruct_array() since the array data is just going
+ * to look like a C array of char values.
+ */
+ ArrayType *modesArray = (ArrayType *) DatumGetPointer(parameterModes);
+ char *paramModes;
+ int modesCount;
+ int i;
+
+ if (ARR_NDIM(modesArray) != 1 ||
+ ARR_HASNULL(modesArray) ||
+ ARR_ELEMTYPE(modesArray) != CHAROID)
+ elog(ERROR, "parameterModes is not a 1-D char array");
+
+ paramModes = (char *) ARR_DATA_PTR(modesArray);
+ modesCount = ARR_DIMS(modesArray)[0];
+
+ for (i = 0; i < modesCount; ++i)
+ {
+ switch (paramModes[i])
+ {
+ case PROARGMODE_VARIADIC:
+ if (OidIsValid(variadic_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("VARIADIC must not be specified more than once")));
+ variadic_type = aggArgTypes[i];
+
+ /* enforce restrictions on ordered args */
+
+ if (numDirectArgs >= 0
+ && i >= numDirectArgs
+ && variadic_type != ANYOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("VARIADIC ordered arguments must be of type ANY")));
+
+ break;
+
+ case PROARGMODE_IN:
+ if (OidIsValid(variadic_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("VARIADIC argument must be last")));
+ break;
+
+ default:
+ elog(ERROR, "invalid argument mode");
+ }
+ }
+ }
+
+ switch (variadic_type)
+ {
+ case InvalidOid:
+ case ANYARRAYOID:
+ case ANYOID:
+ /* okay */
+ break;
+ default:
+ if (!OidIsValid(get_element_type(variadic_type)))
+ elog(ERROR, "VARIADIC parameter must be an array");
+ break;
+ }
+
+ if (isHypotheticalSet)
+ {
+ if (numArgs != numDirectArgs
+ || variadic_type != ANYOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("Invalid argument types for hypothetical set function"),
+ errhint("Required declaration is (..., VARIADIC \"any\") WITHIN GROUP (*)")));
+
+ /* flag for special processing for hypothetical sets */
+ numDirectArgs = -2;
+ }
+ else if (numArgs == numDirectArgs)
+ {
+ if (variadic_type == ANYOID)
+ {
+ /*
+ * this case allows the number of direct args to be truly variable
+ */
+ numDirectArgs = -1;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("Invalid argument types for ordered set function"),
+ errhint("WITHIN GROUP (*) is not allowed without VARIADIC \"any\"")));
+ }
+
/*
* If transtype is polymorphic, must have polymorphic argument also; else
* we will have no way to deduce the actual transtype.
***************
*** 107,159 **** AggregateCreate(const char *aggName,
errmsg("cannot determine transition data type"),
errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument.")));
! /* find the transfn */
! nargs_transfn = numArgs + 1;
! fnArgs = (Oid *) palloc(nargs_transfn * sizeof(Oid));
! fnArgs[0] = aggTransType;
! memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
! transfn = lookup_agg_function(aggtransfnName, nargs_transfn, fnArgs,
! &rettype);
! /*
! * Return type of transfn (possibly after refinement by
! * enforce_generic_type_consistency, if transtype isn't polymorphic) must
! * exactly match declared transtype.
! *
! * In the non-polymorphic-transtype case, it might be okay to allow a
! * rettype that's binary-coercible to transtype, but I'm not quite
! * convinced that it's either safe or useful. When transtype is
! * polymorphic we *must* demand exact equality.
! */
! if (rettype != aggTransType)
! ereport(ERROR,
! (errcode(ERRCODE_DATATYPE_MISMATCH),
! errmsg("return type of transition function %s is not %s",
! NameListToString(aggtransfnName),
! format_type_be(aggTransType))));
! tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(transfn));
! if (!HeapTupleIsValid(tup))
! elog(ERROR, "cache lookup failed for function %u", transfn);
! proc = (Form_pg_proc) GETSTRUCT(tup);
! /*
! * If the transfn is strict and the initval is NULL, make sure first input
! * type and transtype are the same (or at least binary-compatible), so
! * that it's OK to use the first input value as the initial transValue.
! */
! if (proc->proisstrict && agginitval == NULL)
! {
! if (numArgs < 1 ||
! !IsBinaryCoercible(aggArgTypes[0], aggTransType))
ereport(ERROR,
! (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
! errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type")));
}
- ReleaseSysCache(tup);
/* handle finalfn, if supplied */
! if (aggfinalfnName)
{
fnArgs[0] = aggTransType;
finalfn = lookup_agg_function(aggfinalfnName, 1, fnArgs,
--- 255,340 ----
errmsg("cannot determine transition data type"),
errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument.")));
! if (!isOrderedSet)
! {
! /* find the transfn */
! fnArgs[0] = aggTransType;
! memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(Oid));
! transfn = lookup_agg_function(aggtransfnName, numArgs + 1, fnArgs,
! &rettype);
! /*
! * Return type of transfn (possibly after refinement by
! * enforce_generic_type_consistency, if transtype isn't polymorphic)
! * must exactly match declared transtype.
! *
! * In the non-polymorphic-transtype case, it might be okay to allow a
! * rettype that's binary-coercible to transtype, but I'm not quite
! * convinced that it's either safe or useful. When transtype is
! * polymorphic we *must* demand exact equality.
! */
! if (rettype != aggTransType)
ereport(ERROR,
! (errcode(ERRCODE_DATATYPE_MISMATCH),
! errmsg("return type of transition function %s is not %s",
! NameListToString(aggtransfnName),
! format_type_be(aggTransType))));
!
! tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(transfn));
! if (!HeapTupleIsValid(tup))
! elog(ERROR, "cache lookup failed for function %u", transfn);
! proc = (Form_pg_proc) GETSTRUCT(tup);
!
! /*
! * If the transfn is strict and the initval is NULL, make sure first
! * input type and transtype are the same (or at least
! * binary-compatible), so that it's OK to use the first input value as
! * the initial transValue.
! */
! if (proc->proisstrict && agginitval == NULL)
! {
! if (numArgs < 1 ||
! !IsBinaryCoercible(aggArgTypes[0], aggTransType))
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
! errmsg("must not omit initial value when transition function is strict and transition type is not compatible with input type")));
! }
! ReleaseSysCache(tup);
}
/* handle finalfn, if supplied */
! if (isOrderedSet)
! {
! int num_final_args = numArgs;
!
! memcpy(fnArgs, aggArgTypes, num_final_args * sizeof(Oid));
!
! /*
! * If there's a transtype, it becomes the last arg to the finalfn;
! * but if the agg (and hence the finalfn) is variadic "any", then
! * this contributes nothing to the signature.
! */
! if (aggTransType != InvalidOid && variadic_type != ANYOID)
! fnArgs[num_final_args++] = aggTransType;
!
! finalfn = lookup_agg_function(aggfinalfnName, num_final_args, fnArgs,
! &finaltype);
!
! /*
! * this is also checked at runtime for security reasons, but check
! * here too to provide a friendly error (the requirement is because
! * the finalfn will be passed null dummy args for type resolution
! * purposes)
! */
!
! if (func_strict(finalfn))
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
! errmsg("ordered set final functions must not be declared STRICT")));
! }
! else if (aggfinalfnName)
{
fnArgs[0] = aggTransType;
finalfn = lookup_agg_function(aggfinalfnName, 1, fnArgs,
***************
*** 166,171 **** AggregateCreate(const char *aggName,
--- 347,353 ----
*/
finaltype = aggTransType;
}
+
Assert(OidIsValid(finaltype));
/*
***************
*** 207,212 **** AggregateCreate(const char *aggName,
--- 389,406 ----
false, -1);
}
+ /* handle transsortop, if supplied */
+ if (aggtranssortopName)
+ {
+ if (!isOrderedSet || !OidIsValid(aggTransType))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ errmsg("transition sort operator can only be specified for ordered set functions with transition types")));
+ transsortop = LookupOperName(NULL, aggtranssortopName,
+ aggTransType, aggTransType,
+ false, -1);
+ }
+
/*
* permission checks on used types
*/
***************
*** 217,231 **** AggregateCreate(const char *aggName,
aclcheck_error_type(aclresult, aggArgTypes[i]);
}
! aclresult = pg_type_aclcheck(aggTransType, GetUserId(), ACL_USAGE);
! if (aclresult != ACLCHECK_OK)
! aclcheck_error_type(aclresult, aggTransType);
aclresult = pg_type_aclcheck(finaltype, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, finaltype);
-
/*
* Everything looks okay. Try to create the pg_proc entry for the
* aggregate. (This could fail if there's already a conflicting entry.)
--- 411,427 ----
aclcheck_error_type(aclresult, aggArgTypes[i]);
}
! if (OidIsValid(aggTransType))
! {
! aclresult = pg_type_aclcheck(aggTransType, GetUserId(), ACL_USAGE);
! if (aclresult != ACLCHECK_OK)
! aclcheck_error_type(aclresult, aggTransType);
! }
aclresult = pg_type_aclcheck(finaltype, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, finaltype);
/*
* Everything looks okay. Try to create the pg_proc entry for the
* aggregate. (This could fail if there's already a conflicting entry.)
***************
*** 246,252 **** AggregateCreate(const char *aggName,
false, /* security invoker (currently not
* definable for agg) */
false, /* isLeakProof */
! false, /* isStrict (not needed for agg) */
PROVOLATILE_IMMUTABLE, /* volatility (not
* needed for agg) */
parameterTypes, /* paramTypes */
--- 442,448 ----
false, /* security invoker (currently not
* definable for agg) */
false, /* isLeakProof */
! isStrict, /* isStrict (needed for ordered set funcs) */
PROVOLATILE_IMMUTABLE, /* volatility (not
* needed for agg) */
parameterTypes, /* paramTypes */
***************
*** 272,278 **** AggregateCreate(const char *aggName,
--- 468,478 ----
values[Anum_pg_aggregate_aggtransfn - 1] = ObjectIdGetDatum(transfn);
values[Anum_pg_aggregate_aggfinalfn - 1] = ObjectIdGetDatum(finalfn);
values[Anum_pg_aggregate_aggsortop - 1] = ObjectIdGetDatum(sortop);
+ values[Anum_pg_aggregate_aggtranssortop - 1] = ObjectIdGetDatum(transsortop);
values[Anum_pg_aggregate_aggtranstype - 1] = ObjectIdGetDatum(aggTransType);
+ values[Anum_pg_aggregate_aggordnargs - 1] = Int32GetDatum(numDirectArgs);
+ values[Anum_pg_aggregate_aggisordsetfunc - 1] = BoolGetDatum(isOrderedSet);
+
if (agginitval)
values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval);
else
***************
*** 290,307 **** AggregateCreate(const char *aggName,
/*
* Create dependencies for the aggregate (above and beyond those already
! * made by ProcedureCreate). Note: we don't need an explicit dependency
! * on aggTransType since we depend on it indirectly through transfn.
*/
myself.classId = ProcedureRelationId;
myself.objectId = procOid;
myself.objectSubId = 0;
/* Depends on transition function */
! referenced.classId = ProcedureRelationId;
! referenced.objectId = transfn;
! referenced.objectSubId = 0;
! recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
/* Depends on final function, if any */
if (OidIsValid(finalfn))
--- 490,512 ----
/*
* Create dependencies for the aggregate (above and beyond those already
! * made by ProcedureCreate). Normal aggs don't need an explicit
! * dependency on aggTransType since we depend on it indirectly through
! * transfn, but ordered set functions with variadic "any" do need one
! * (ordered set functions without variadic depend on it via the finalfn).
*/
myself.classId = ProcedureRelationId;
myself.objectId = procOid;
myself.objectSubId = 0;
/* Depends on transition function */
! if (OidIsValid(transfn))
! {
! referenced.classId = ProcedureRelationId;
! referenced.objectId = transfn;
! referenced.objectSubId = 0;
! recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
! }
/* Depends on final function, if any */
if (OidIsValid(finalfn))
***************
*** 321,326 **** AggregateCreate(const char *aggName,
--- 526,549 ----
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* Depends on transsort operator, if any */
+ if (OidIsValid(transsortop))
+ {
+ referenced.classId = OperatorRelationId;
+ referenced.objectId = transsortop;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+
+ /* May depend on aggTransType if any */
+ if (OidIsValid(aggTransType) && isOrderedSet && variadic_type == ANYOID)
+ {
+ referenced.classId = TypeRelationId;
+ referenced.objectId = aggTransType;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+
return procOid;
}
*** a/src/backend/commands/aggregatecmds.c
--- b/src/backend/commands/aggregatecmds.c
***************
*** 44,52 ****
* DefineAggregate
*
* "oldstyle" signals the old (pre-8.2) style where the aggregate input type
! * is specified by a BASETYPE element in the parameters. Otherwise,
! * "args" is a list of FunctionParameter structs defining the agg's arguments.
! * "parameters" is a list of DefElem representing the agg's definition clauses.
*/
Oid
DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
--- 44,55 ----
* DefineAggregate
*
* "oldstyle" signals the old (pre-8.2) style where the aggregate input type
! * is specified by a BASETYPE element in the parameters. Otherwise, "args" is
! * a pair, whose first element is a list of FunctionParameter structs defining
! * the agg's arguments (both direct and ordered), and whose second element is
! * an Integer node with the number of direct args, or -1 if this isn't an
! * ordered set func. "parameters" is a list of DefElem representing the agg's
! * definition clauses.
*/
Oid
DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
***************
*** 58,75 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
List *transfuncName = NIL;
List *finalfuncName = NIL;
List *sortoperatorName = NIL;
TypeName *baseType = NULL;
TypeName *transType = NULL;
char *initval = NULL;
int numArgs;
oidvector *parameterTypes;
ArrayType *allParameterTypes;
ArrayType *parameterModes;
ArrayType *parameterNames;
List *parameterDefaults;
- Oid transTypeId;
char transTypeType;
ListCell *pl;
/* Convert list of names to a name and namespace */
aggNamespace = QualifiedNameGetCreationNamespace(name, &aggName);
--- 61,83 ----
List *transfuncName = NIL;
List *finalfuncName = NIL;
List *sortoperatorName = NIL;
+ List *transsortoperatorName = NIL;
TypeName *baseType = NULL;
TypeName *transType = NULL;
char *initval = NULL;
int numArgs;
+ int numDirectArgs = -1;
+ Oid transTypeId = InvalidOid;
oidvector *parameterTypes;
ArrayType *allParameterTypes;
ArrayType *parameterModes;
ArrayType *parameterNames;
List *parameterDefaults;
char transTypeType;
ListCell *pl;
+ bool ishypothetical = false;
+ bool isOrderedSet = false;
+ bool isStrict = false;
/* Convert list of names to a name and namespace */
aggNamespace = QualifiedNameGetCreationNamespace(name, &aggName);
***************
*** 80,85 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
--- 88,101 ----
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(aggNamespace));
+ Assert(args == NIL || list_length(args) == 2);
+
+ if (list_length(args) == 2)
+ {
+ numDirectArgs = intVal(lsecond(args));
+ isOrderedSet = (numDirectArgs != -1);
+ }
+
foreach(pl, parameters)
{
DefElem *defel = (DefElem *) lfirst(pl);
***************
*** 106,111 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
--- 122,133 ----
initval = defGetString(defel);
else if (pg_strcasecmp(defel->defname, "initcond1") == 0)
initval = defGetString(defel);
+ else if (pg_strcasecmp(defel->defname, "hypothetical") == 0)
+ ishypothetical = true;
+ else if (pg_strcasecmp(defel->defname, "strict") == 0)
+ isStrict = true;
+ else if (pg_strcasecmp(defel->defname, "transsortop") == 0)
+ transsortoperatorName = defGetQualifiedName(defel);
else
ereport(WARNING,
(errcode(ERRCODE_SYNTAX_ERROR),
***************
*** 113,129 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
defel->defname)));
}
! /*
! * make sure we have our required definitions
! */
! if (transType == NULL)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
! errmsg("aggregate stype must be specified")));
! if (transfuncName == NIL)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
! errmsg("aggregate sfunc must be specified")));
/*
* look up the aggregate's input datatype(s).
--- 135,169 ----
defel->defname)));
}
! if (!isOrderedSet)
! {
! /*
! * make sure we have our required definitions
! */
! if (transType == NULL)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
! errmsg("aggregate stype must be specified")));
! if (transfuncName == NIL)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
! errmsg("aggregate sfunc must be specified")));
! if (isStrict)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
! errmsg("aggregate with sfunc must not be explicitly declared STRICT")));
! }
! else
! {
! if (transfuncName != NIL)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
! errmsg("sfunc must not be specified for ordered set functions")));
! if (finalfuncName == NIL)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
! errmsg("finalfunc must be specified for ordered set functions")));
! }
/*
* look up the aggregate's input datatype(s).
***************
*** 173,180 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("basetype is redundant with aggregate input type specification")));
! numArgs = list_length(args);
! interpret_function_parameter_list(args,
InvalidOid,
true, /* is an aggregate */
queryString,
--- 213,227 ----
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("basetype is redundant with aggregate input type specification")));
! /*
! * The grammar has already concatenated the direct and ordered
! * args (if any) for us. Note that error checking for position
! * and number of VARIADIC args is not done for us, we have to
! * do it ourselves later (in AggregateCreate)
! */
!
! numArgs = list_length(linitial(args));
! interpret_function_parameter_list(linitial(args),
InvalidOid,
true, /* is an aggregate */
queryString,
***************
*** 191,197 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
}
/*
! * look up the aggregate's transtype.
*
* transtype can't be a pseudo-type, since we need to be able to store
* values of the transtype. However, we can allow polymorphic transtype
--- 238,244 ----
}
/*
! * look up the aggregate's transtype, if specified.
*
* transtype can't be a pseudo-type, since we need to be able to store
* values of the transtype. However, we can allow polymorphic transtype
***************
*** 201,218 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
* worse) by connecting up incompatible internal-using functions in an
* aggregate.
*/
! transTypeId = typenameTypeId(NULL, transType);
! transTypeType = get_typtype(transTypeId);
! if (transTypeType == TYPTYPE_PSEUDO &&
! !IsPolymorphicType(transTypeId))
{
! if (transTypeId == INTERNALOID && superuser())
! /* okay */ ;
! else
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
! errmsg("aggregate transition data type cannot be %s",
! format_type_be(transTypeId))));
}
/*
--- 248,267 ----
* worse) by connecting up incompatible internal-using functions in an
* aggregate.
*/
! if (transType)
{
! transTypeId = typenameTypeId(NULL, transType);
! transTypeType = get_typtype(transTypeId);
! if (transTypeType == TYPTYPE_PSEUDO &&
! !IsPolymorphicType(transTypeId))
! {
! if (transTypeId != INTERNALOID || !superuser() || isOrderedSet)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
! errmsg("aggregate transition data type cannot be %s",
! format_type_be(transTypeId))));
! }
!
}
/*
***************
*** 224,236 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
* value. However, if it's an incorrect value it seems much more
* user-friendly to complain at CREATE AGGREGATE time.
*/
! if (initval && transTypeType != TYPTYPE_PSEUDO)
{
! Oid typinput,
! typioparam;
! getTypeInputInfo(transTypeId, &typinput, &typioparam);
! (void) OidInputFunctionCall(typinput, initval, typioparam, -1);
}
/*
--- 273,295 ----
* value. However, if it's an incorrect value it seems much more
* user-friendly to complain at CREATE AGGREGATE time.
*/
! if (transType)
{
! if (initval && transTypeType != TYPTYPE_PSEUDO)
! {
! Oid typinput,
! typioparam;
! getTypeInputInfo(transTypeId, &typinput, &typioparam);
! (void) OidInputFunctionCall(typinput, initval, typioparam, -1);
! }
! }
! else
! {
! if (initval)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
! errmsg("INITVAL must not be specified without STYPE")));
}
/*
***************
*** 239,244 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
--- 298,304 ----
return AggregateCreate(aggName, /* aggregate name */
aggNamespace, /* namespace */
numArgs,
+ numDirectArgs,
parameterTypes,
PointerGetDatum(allParameterTypes),
PointerGetDatum(parameterModes),
***************
*** 247,252 **** DefineAggregate(List *name, List *args, bool oldstyle, List *parameters,
transfuncName, /* step function name */
finalfuncName, /* final function name */
sortoperatorName, /* sort operator name */
transTypeId, /* transition data type */
! initval); /* initial condition */
}
--- 307,316 ----
transfuncName, /* step function name */
finalfuncName, /* final function name */
sortoperatorName, /* sort operator name */
+ transsortoperatorName, /* transsort operator name */
transTypeId, /* transition data type */
! initval, /* initial condition */
! isStrict, /* is explicitly STRICT */
! isOrderedSet, /* If the function is an ordered set */
! ishypothetical); /* If the function is a hypothetical set */
}
*** a/src/backend/commands/functioncmds.c
--- b/src/backend/commands/functioncmds.c
***************
*** 274,281 **** interpret_function_parameter_list(List *parameters,
/* handle input parameters */
if (fp->mode != FUNC_PARAM_OUT && fp->mode != FUNC_PARAM_TABLE)
{
! /* other input parameters can't follow a VARIADIC parameter */
! if (varCount > 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("VARIADIC parameter must be the last input parameter")));
--- 274,286 ----
/* handle input parameters */
if (fp->mode != FUNC_PARAM_OUT && fp->mode != FUNC_PARAM_TABLE)
{
! /*
! * For functions, other input parameters can't follow a VARIADIC
! * parameter; for aggregates, we might be dealing with an ordered
! * set function which have more complex rules for variadics, so
! * punt the error checking for that case to the caller.
! */
! if (varCount > 0 && !is_aggregate)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("VARIADIC parameter must be the last input parameter")));
*** 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
***************
*** 380,387 **** sql_fn_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var)
param = ParseFuncOrColumn(pstate,
list_make1(subfield),
list_make1(param),
! NIL, NULL, false, false, false,
! NULL, true, cref->location);
}
return param;
--- 380,387 ----
param = ParseFuncOrColumn(pstate,
list_make1(subfield),
list_make1(param),
! cref->location,
! NULL);
}
return param;
*** a/src/backend/executor/nodeAgg.c
--- b/src/backend/executor/nodeAgg.c
***************
*** 3,9 ****
* nodeAgg.c
* Routines to handle aggregate nodes.
*
! * ExecAgg evaluates each aggregate in the following steps:
*
* transvalue = initcond
* foreach input_tuple do
--- 3,9 ----
* nodeAgg.c
* Routines to handle aggregate nodes.
*
! * ExecAgg evaluates each normal aggregate in the following steps:
*
* transvalue = initcond
* foreach input_tuple do
***************
*** 66,71 ****
--- 66,91 ----
* AggState is available as context in earlier releases (back to 8.1),
* but direct examination of the node is needed to use it before 9.0.
*
+ *---
+ *
+ * Ordered set functions modify the above process in a number of ways.
+ * Most importantly, they do not have transfuncs at all; the same sort
+ * mechanism used for ORDER BY/DISTINCT as described above is used to
+ * process the input, but then the finalfunc is called without actually
+ * running the sort (the finalfunc is allowed to insert rows first).
+ * The finalfunc has access via a set of AggSet* API functions to the
+ * Tuplesortstate, row count in the group, and other ancillary info.
+ *
+ * Ordered set functions can, however, have a transvalue declared; this is
+ * treated as a constant, and added to the end of the sort fields.
+ * Hypothetical set functions use this to provide a flag that distinguishes
+ * the hypothetical row from the input data.
+ *
+ * Since they have no transfunc, ordered set functions have their own
+ * 'strict' flag stored in the aggregate's own pg_proc entry; this affects
+ * whether rows containing nulls are placed in the sorter. But since we
+ * pass dummy null arguments to the finalfunc for type resolution purposes,
+ * no ordered set finalfunc is allowed to be strict.
*
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
***************
*** 87,96 ****
--- 107,118 ----
#include "executor/nodeAgg.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
+ #include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/tlist.h"
#include "parser/parse_agg.h"
#include "parser/parse_coerce.h"
+ #include "parser/parse_clause.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
***************
*** 105,110 ****
--- 127,134 ----
*/
typedef struct AggStatePerAggData
{
+ NodeTag type;
+
/*
* These values are set up during ExecInitAgg() and do not change
* thereafter:
***************
*** 114,123 **** typedef struct AggStatePerAggData
AggrefExprState *aggrefstate;
Aggref *aggref;
! /* number of input arguments for aggregate function proper */
int numArguments;
! /* number of inputs including ORDER BY expressions */
int numInputs;
/* Oids of transfer functions */
--- 138,162 ----
AggrefExprState *aggrefstate;
Aggref *aggref;
! /* Pointer to parent AggState node */
! AggState *aggstate;
!
! /* copied from aggref */
! bool isOrderedSet;
!
! /*
! * number of arguments for aggregate function proper.
! * For ordered set functions, this includes the ORDER BY
! * columns, *except* in the case of hypothetical set functions.
! */
int numArguments;
! /*
! * number of inputs including ORDER BY expressions. For ordered
! * set functions, *only* the ORDER BY expressions are included
! * here, since the direct args to the function are not properly
! * "input" in the sense of being derived from the tuple group.
! */
int numInputs;
/* Oids of transfer functions */
***************
*** 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;
--- 165,187 ----
/*
* 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 are not allowed to have strict finalfns;
+ * other aggregates respect the finalfn strict flag in the
+ * FmgrInfo above.
+ */
+ int numStrict;
+
/* Input collation derived for aggregate */
Oid aggCollation;
***************
*** 148,153 **** typedef struct AggStatePerAggData
--- 198,206 ----
Oid *sortCollations;
bool *sortNullsFirst;
+ /* just for convenience of ordered set funcs, not used here */
+ Oid *sortEqOperators;
+
/*
* fmgr lookup data for input columns' equality operators --- only
* set/used when aggregate has DISTINCT flag. Note that these are in
***************
*** 204,209 **** typedef struct AggStatePerAggData
--- 257,265 ----
*/
Tuplesortstate *sortstate; /* sort object, if DISTINCT or ORDER BY */
+
+ int64 number_of_rows; /* number of rows */
+
} AggStatePerAggData;
/*
***************
*** 300,305 **** initialize_aggregates(AggState *aggstate,
--- 356,363 ----
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;
--- 441,457 ----
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;
}
--- 567,590 ----
if (peraggstate->numSortCols > 0)
{
+ int numStrict = peraggstate->numStrict;
+
/* DISTINCT and/or ORDER BY case */
Assert(slot->tts_nvalid == peraggstate->numInputs);
/*
! * If the aggregate 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)
--- 595,602 ----
slot->tts_isnull[0]);
else
tuplesort_puttupleslot(peraggstate->sortstate, slot);
+
+ peraggstate->number_of_rows++;
}
else
{
***************
*** 756,770 **** 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;
*resultIsNull = true;
}
--- 819,884 ----
if (OidIsValid(peraggstate->finalfn_oid))
{
FunctionCallInfoData fcinfo;
+ bool isnull = false;
+
+ if (!(peraggstate->isOrderedSet))
+ {
+ 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;
+ int numArguments = peraggstate->numArguments;
+
+ ExecClearTuple(peraggstate->evalslot);
+ ExecClearTuple(peraggstate->uniqslot);
+
+ InitFunctionCallInfoData(fcinfo,
+ &(peraggstate->finalfn),
+ peraggstate->numArguments,
+ 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;
! }
!
! for(; i < numArguments; i++)
! {
! fcinfo.arg[i] = (Datum) 0;
! fcinfo.argnull[i] = true;
! isnull = true;
! }
! }
!
! if (isnull && fcinfo.flinfo->fn_strict)
{
! /*
! * don't call a strict function with NULL inputs; for ordered set
! * functions this is paranoia, we already required that fn_strict
! * is false, but easy to check anyway
! */
*resultVal = (Datum) 0;
*resultIsNull = true;
}
***************
*** 1164,1169 **** agg_retrieve_direct(AggState *aggstate)
--- 1278,1294 ----
}
/*
+ * 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,
--- 1299,1315 ----
if (peraggstate->numSortCols > 0)
{
! if (!(peraggstate->isOrderedSet))
! {
! 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.
*/
--- 1317,1322 ----
***************
*** 1568,1577 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
--- 1687,1698 ----
int numInputs;
int numSortCols;
int numDistinctCols;
+ bool isOrderedSet = aggref->isordset;
List *sortlist;
HeapTuple aggTuple;
Form_pg_aggregate aggform;
Oid aggtranstype;
+ Oid aggtranstypecoll;
AclResult aclresult;
Oid transfn_oid,
finalfn_oid;
***************
*** 1580,1585 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
--- 1701,1710 ----
Datum textInitVal;
int i;
ListCell *lc;
+ bool is_strict;
+ Oid inputCollations[FUNC_MAX_ARGS];
+ List *argexprs;
+ List *argexprstate;
/* Planner should have assigned aggregate to correct level */
Assert(aggref->agglevelsup == 0);
***************
*** 1601,1631 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
/* Nope, so assign a new PerAgg record */
peraggstate = &peragg[++aggno];
- /* Mark Aggref state node with assigned index in the result array */
- aggrefstate->aggno = aggno;
-
/* Fill in the peraggstate data */
! peraggstate->aggrefstate = aggrefstate;
peraggstate->aggref = aggref;
! numInputs = list_length(aggref->args);
! peraggstate->numInputs = numInputs;
! peraggstate->sortstate = NULL;
! /*
! * Get actual datatypes of the inputs. These could be different from
! * the agg's declared input types, when the agg accepts ANY or a
! * polymorphic type.
! */
! numArguments = 0;
! foreach(lc, aggref->args)
! {
! TargetEntry *tle = (TargetEntry *) lfirst(lc);
! if (!tle->resjunk)
! inputTypes[numArguments++] = exprType((Node *) tle->expr);
! }
! peraggstate->numArguments = numArguments;
aggTuple = SearchSysCache1(AGGFNOID,
ObjectIdGetDatum(aggref->aggfnoid));
if (!HeapTupleIsValid(aggTuple))
--- 1726,1743 ----
/* Nope, so assign a new PerAgg record */
peraggstate = &peragg[++aggno];
/* Fill in the peraggstate data */
! peraggstate->type = T_AggStatePerAggData;
! peraggstate->aggstate = aggstate;
peraggstate->aggref = aggref;
! peraggstate->aggrefstate = aggrefstate;
! peraggstate->isOrderedSet = isOrderedSet;
! /* Mark Aggref state node with assigned index in the result array */
! aggrefstate->aggno = aggno;
+ /* Fetch the pg_aggregate row */
aggTuple = SearchSysCache1(AGGFNOID,
ObjectIdGetDatum(aggref->aggfnoid));
if (!HeapTupleIsValid(aggTuple))
***************
*** 1633,1638 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
--- 1745,1757 ----
aggref->aggfnoid);
aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
+ /*
+ * Check that the definition hasn't somehow changed incompatibly.
+ */
+ if (isOrderedSet != (aggform->aggisordsetfunc)
+ || (aggref->ishypothetical != (aggform->aggordnargs == -2)))
+ elog(ERROR, "Incompatible change to aggregate definition");
+
/* Check permission to call aggregate function */
aclresult = pg_proc_aclcheck(aggref->aggfnoid, GetUserId(),
ACL_EXECUTE);
***************
*** 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,
--- 1763,1799 ----
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,
***************
*** 1674,1690 **** 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;
! int agg_nargs;
(void) get_func_signature(aggref->aggfnoid,
! &declaredArgTypes, &agg_nargs);
! Assert(agg_nargs == numArguments);
aggtranstype = enforce_generic_type_consistency(inputTypes,
declaredArgTypes,
agg_nargs,
--- 1805,1841 ----
}
}
+ /*
+ * Get actual datatypes of the inputs. These could be different from
+ * the agg's declared input types, when the agg accepts ANY or a
+ * polymorphic type.
+ */
+
+ peraggstate->numInputs = numInputs = list_length(aggref->args);
+
+ numArguments = get_aggregate_argtypes(aggref,
+ inputTypes,
+ inputCollations);
+
/* 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;
! int agg_nargs;
(void) get_func_signature(aggref->aggfnoid,
! &declaredArgTypes,
! &agg_nargs);
!
! /*
! * if variadic "any", might be more actual args than declared
! * args, but these extra args can't influence the determination
! * of polymorphic transition or result type.
! */
! Assert(agg_nargs <= numArguments);
!
aggtranstype = enforce_generic_type_consistency(inputTypes,
declaredArgTypes,
agg_nargs,
***************
*** 1693,1727 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
pfree(declaredArgTypes);
}
/* build expression trees using actual argument & result types */
! build_aggregate_fnexprs(inputTypes,
! numArguments,
! aggref->aggvariadic,
! aggtranstype,
! aggref->aggtype,
! aggref->inputcollid,
! transfn_oid,
! finalfn_oid,
! &transfnexpr,
! &finalfnexpr);
!
! fmgr_info(transfn_oid, &peraggstate->transfn);
! fmgr_info_set_expr((Node *) transfnexpr, &peraggstate->transfn);
if (OidIsValid(finalfn_oid))
{
fmgr_info(finalfn_oid, &peraggstate->finalfn);
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
--- 1844,1925 ----
pfree(declaredArgTypes);
}
+ aggtranstypecoll = get_typcollation(aggtranstype);
+
/* build expression trees using actual argument & result types */
!
! if (!isOrderedSet)
! {
! build_aggregate_fnexprs(inputTypes,
! numArguments,
! aggref->aggvariadic,
! aggtranstype,
! aggref->aggtype,
! aggref->inputcollid,
! transfn_oid,
! finalfn_oid,
! &transfnexpr,
! &finalfnexpr);
! }
! else
! {
! /*
! * The transvalue counts as an argument, but not for hypothetical
! * set funcs.
! */
! if (OidIsValid(aggtranstype) && !(aggref->ishypothetical))
! {
! if (numArguments == FUNC_MAX_ARGS)
! elog(ERROR, "Too many arguments to ordered set function");
!
! inputTypes[numArguments++] = aggtranstype;
! inputCollations[numArguments++] = aggtranstypecoll;
! }
!
! build_orderedset_fnexprs(inputTypes,
! numArguments,
! aggref->aggvariadic,
! aggref->aggtype,
! aggref->inputcollid,
! inputCollations,
! finalfn_oid,
! &finalfnexpr);
! }
!
! peraggstate->numArguments = numArguments;
!
! 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))
{
fmgr_info(finalfn_oid, &peraggstate->finalfn);
fmgr_info_set_expr((Node *) finalfnexpr, &peraggstate->finalfn);
+ if (peraggstate->finalfn.fn_strict && isOrderedSet)
+ elog(ERROR, "Ordered set finalfns must not be strict");
}
+ if (is_strict)
+ peraggstate->numStrict = (isOrderedSet ? 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
***************
*** 1744,1750 **** 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))
--- 1942,1950 ----
* 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))
***************
*** 1754,1774 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
aggref->aggfnoid)));
}
! /*
! * Get a tupledesc corresponding to the inputs (including sort
! * expressions) of the agg.
! */
! peraggstate->evaldesc = ExecTypeFromTL(aggref->args, false);
!
! /* Create slot we're going to do argument evaluation in */
! peraggstate->evalslot = ExecInitExtraTupleSlot(estate);
! ExecSetSlotDescriptor(peraggstate->evalslot, peraggstate->evaldesc);
!
! /* Set up projection info for evaluation */
! peraggstate->evalproj = ExecBuildProjectionInfo(aggrefstate->args,
! aggstate->tmpcontext,
! peraggstate->evalslot,
! NULL);
/*
* If we're doing either DISTINCT or ORDER BY, then we have a list of
--- 1954,1961 ----
aggref->aggfnoid)));
}
! argexprs = aggref->args;
! argexprstate = aggrefstate->args;
/*
* If we're doing either DISTINCT or ORDER BY, then we have a list of
***************
*** 1777,1782 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
--- 1964,1974 ----
*
* Note that by construction, if there is a DISTINCT clause then the
* ORDER BY clause is a prefix of it (see transformDistinctClause).
+ *
+ * If we're doing an ordered set function, though, we want to do the
+ * initialization for DISTINCT since the ordered set finalfn might
+ * want it, and it's much easier to do it here. So set numDistinctCols
+ * and let the later initialization take care of it.
*/
if (aggref->aggdistinct)
{
***************
*** 1788,1798 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
{
sortlist = aggref->aggorder;
numSortCols = list_length(sortlist);
! numDistinctCols = 0;
}
peraggstate->numSortCols = numSortCols;
peraggstate->numDistinctCols = numDistinctCols;
if (numSortCols > 0)
{
--- 1980,2065 ----
{
sortlist = aggref->aggorder;
numSortCols = list_length(sortlist);
! numDistinctCols = isOrderedSet ? numSortCols : 0;
! }
!
! /*
! * If this is an ordered set function, and we have a transtype, then
! * it represents an extra column to be added to the sorter with a
! * fixed value. Plus, if aggtranssortop is valid, we have to include
! * a sort entry for the new column.
! *
! * I'd probably have done this in the planner if I'd seen any
! * possible place to put it; if there is one, it's very obscure.
! */
!
! if (OidIsValid(aggtranstype) && isOrderedSet)
! {
! Oid sortop = aggform->aggtranssortop;
! Const *node = makeNode(Const);
! TargetEntry *tle;
! SortGroupClause *sortcl = NULL;
!
! node->consttype = aggtranstype;
! node->consttypmod = -1;
! node->constcollid = aggtranstypecoll;
! node->constlen = peraggstate->transtypeLen;
! node->constvalue = peraggstate->initValue;
! node->constisnull = peraggstate->initValueIsNull;
! node->constbyval = peraggstate->transtypeByVal;
! node->location = -1;
!
! tle = makeTargetEntry((Expr *) node,
! ++numInputs,
! NULL,
! true);
!
! peraggstate->numInputs = numInputs;
!
! if (OidIsValid(sortop))
! {
! Assert(aggref->aggdistinct == NIL);
!
! sortcl = makeNode(SortGroupClause);
!
! sortcl->tleSortGroupRef = assignSortGroupRef(tle, argexprs);
!
! sortcl->sortop = sortop;
! sortcl->hashable = false;
! sortcl->eqop = get_equality_op_for_ordering_op(sortop,
! &sortcl->nulls_first);
!
! sortlist = lappend(list_copy(sortlist), sortcl);
! ++numSortCols;
! ++numDistinctCols;
! }
!
! /* shallow-copy the passed-in lists, which we must not scribble on. */
!
! argexprs = lappend(list_copy(argexprs), (Node *) tle);
! argexprstate = lappend(list_copy(argexprstate),
! ExecInitExpr((Expr *) tle, (PlanState *) aggstate));
}
peraggstate->numSortCols = numSortCols;
peraggstate->numDistinctCols = numDistinctCols;
+ peraggstate->sortstate = NULL;
+
+ /*
+ * Get a tupledesc corresponding to the inputs (including sort
+ * expressions) of the agg.
+ */
+ peraggstate->evaldesc = ExecTypeFromTL(argexprs, false);
+
+ /* Create slot we're going to do argument evaluation in */
+ peraggstate->evalslot = ExecInitExtraTupleSlot(estate);
+ ExecSetSlotDescriptor(peraggstate->evalslot, peraggstate->evaldesc);
+
+ /* Set up projection info for evaluation */
+ peraggstate->evalproj = ExecBuildProjectionInfo(argexprstate,
+ aggstate->tmpcontext,
+ peraggstate->evalslot,
+ NULL);
if (numSortCols > 0)
{
***************
*** 1805,1815 **** 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);
}
! else if (numDistinctCols > 0)
{
/* we will need an extra slot to store prior values */
peraggstate->uniqslot = ExecInitExtraTupleSlot(estate);
--- 2072,2083 ----
/* 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);
}
!
! if (numDistinctCols > 0 && (numInputs > 1 || isOrderedSet))
{
/* we will need an extra slot to store prior values */
peraggstate->uniqslot = ExecInitExtraTupleSlot(estate);
***************
*** 1822,1871 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
(AttrNumber *) palloc(numSortCols * sizeof(AttrNumber));
peraggstate->sortOperators =
(Oid *) palloc(numSortCols * sizeof(Oid));
peraggstate->sortCollations =
(Oid *) palloc(numSortCols * sizeof(Oid));
peraggstate->sortNullsFirst =
(bool *) palloc(numSortCols * sizeof(bool));
i = 0;
foreach(lc, sortlist)
{
SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc);
TargetEntry *tle = get_sortgroupclause_tle(sortcl,
! aggref->args);
/* the parser should have made sure of this */
Assert(OidIsValid(sortcl->sortop));
peraggstate->sortColIdx[i] = tle->resno;
peraggstate->sortOperators[i] = sortcl->sortop;
peraggstate->sortCollations[i] = exprCollation((Node *) tle->expr);
peraggstate->sortNullsFirst[i] = sortcl->nulls_first;
- i++;
- }
- Assert(i == numSortCols);
- }
! if (aggref->aggdistinct)
! {
! Assert(numArguments > 0);
!
! /*
! * We need the equal function for each DISTINCT comparison we will
! * make.
! */
! peraggstate->equalfns =
! (FmgrInfo *) palloc(numDistinctCols * sizeof(FmgrInfo));
!
! i = 0;
! foreach(lc, aggref->aggdistinct)
! {
! SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc);
- fmgr_info(get_opcode(sortcl->eqop), &peraggstate->equalfns[i]);
i++;
}
! Assert(i == numDistinctCols);
}
ReleaseSysCache(aggTuple);
--- 2090,2136 ----
(AttrNumber *) palloc(numSortCols * sizeof(AttrNumber));
peraggstate->sortOperators =
(Oid *) palloc(numSortCols * sizeof(Oid));
+ peraggstate->sortEqOperators =
+ (Oid *) palloc(numSortCols * sizeof(Oid));
peraggstate->sortCollations =
(Oid *) palloc(numSortCols * sizeof(Oid));
peraggstate->sortNullsFirst =
(bool *) palloc(numSortCols * sizeof(bool));
+ if (numDistinctCols > 0)
+ peraggstate->equalfns =
+ (FmgrInfo *) palloc(numDistinctCols * sizeof(FmgrInfo));
+ else
+ peraggstate->equalfns = NULL;
+
i = 0;
foreach(lc, sortlist)
{
SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc);
TargetEntry *tle = get_sortgroupclause_tle(sortcl,
! argexprs);
/* the parser should have made sure of this */
Assert(OidIsValid(sortcl->sortop));
peraggstate->sortColIdx[i] = tle->resno;
peraggstate->sortOperators[i] = sortcl->sortop;
+ peraggstate->sortEqOperators[i] = sortcl->eqop;
peraggstate->sortCollations[i] = exprCollation((Node *) tle->expr);
peraggstate->sortNullsFirst[i] = sortcl->nulls_first;
! /*
! * It's OK to get the equalfns here too, since we already
! * require that sortlist is aggref->aggdistinct for the normal
! * distinct case, and for ordered set functions using the
! * (possibly modified copy of) aggref->aggorder is correct
! */
! if (peraggstate->equalfns)
! fmgr_info(get_opcode(sortcl->eqop), &peraggstate->equalfns[i]);
i++;
}
! Assert(i == numSortCols);
}
ReleaseSysCache(aggTuple);
***************
*** 2023,2028 **** ExecReScanAgg(AggState *node)
--- 2288,2296 ----
* If aggcontext isn't NULL, the function also stores at *aggcontext the
* identity of the memory context that aggregate transition values are
* being stored in.
+ *
+ * We do NOT include AGG_CONTEXT_ORDERED as a possible return here, since
+ * that would open a security hole.
*/
int
AggCheckCallContext(FunctionCallInfo fcinfo, MemoryContext *aggcontext)
***************
*** 2063,2065 **** aggregate_dummy(PG_FUNCTION_ARGS)
--- 2331,2448 ----
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;
+ if (datumtype)
+ *datumtype = peraggstate->evaldesc->attrs[0]->atttypid;
+ }
+ else
+ {
+ if (tupdesc)
+ *tupdesc = peraggstate->evaldesc;
+ if (datumtype)
+ *datumtype = InvalidOid;
+ }
+
+ if (tupslot)
+ *tupslot = peraggstate->evalslot;
+ }
+ else
+ elog(ERROR, "AggSetSortInfo called on non ordered set function");
+ }
+
+ int
+ AggSetGetDistinctInfo(FunctionCallInfo fcinfo,
+ TupleTableSlot **uniqslot,
+ AttrNumber **sortColIdx,
+ FmgrInfo **equalfns)
+ {
+ if (fcinfo->context && IsA(fcinfo->context, AggStatePerAggData))
+ {
+ AggStatePerAggData *peraggstate = (AggStatePerAggData *) fcinfo->context;
+
+ if (uniqslot)
+ *uniqslot = peraggstate->uniqslot;
+ if (sortColIdx)
+ *sortColIdx = peraggstate->sortColIdx;
+ if (equalfns)
+ *equalfns = peraggstate->equalfns;
+
+ return peraggstate->numDistinctCols;
+ }
+ else
+ elog(ERROR, "AggSetGetDistinctOperators called on non ordered set function");
+ }
+
+ int
+ AggSetGetSortOperators(FunctionCallInfo fcinfo,
+ AttrNumber **sortColIdx,
+ Oid **sortOperators,
+ Oid **sortEqOperators,
+ Oid **sortCollations,
+ bool **sortNullsFirst)
+ {
+ if (fcinfo->context && IsA(fcinfo->context, AggStatePerAggData))
+ {
+ AggStatePerAggData *peraggstate = (AggStatePerAggData *) fcinfo->context;
+
+ if (sortColIdx)
+ *sortColIdx = peraggstate->sortColIdx;
+ if (sortOperators)
+ *sortOperators = peraggstate->sortOperators;
+ if (sortEqOperators)
+ *sortEqOperators = peraggstate->sortEqOperators;
+ if (sortCollations)
+ *sortCollations = peraggstate->sortCollations;
+ if (sortNullsFirst)
+ *sortNullsFirst = peraggstate->sortNullsFirst;
+
+ return peraggstate->numSortCols;
+ }
+ else
+ elog(ERROR, "AggSetGetSortOperators called on non ordered set function");
+ }
+
+ void
+ AggSetGetPerTupleContext(FunctionCallInfo fcinfo,
+ MemoryContext *memcontext)
+ {
+ if (fcinfo->context && IsA(fcinfo->context, AggStatePerAggData))
+ {
+ AggStatePerAggData *peraggstate = (AggStatePerAggData *) fcinfo->context;
+ *memcontext = peraggstate->aggstate->tmpcontext->ecxt_per_tuple_memory;
+ }
+ }
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 1139,1147 **** _copyAggref(const Aggref *from)
--- 1139,1150 ----
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(aggvariadic);
+ COPY_SCALAR_FIELD(isordset);
+ COPY_SCALAR_FIELD(ishypothetical);
COPY_SCALAR_FIELD(agglevelsup);
COPY_LOCATION_FIELD(location);
***************
*** 2174,2179 **** _copyFuncCall(const FuncCall *from)
--- 2177,2183 ----
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,204 **** _equalAggref(const Aggref *a, const Aggref *b)
--- 196,207 ----
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(aggvariadic);
+ COMPARE_SCALAR_FIELD(isordset);
+ COMPARE_SCALAR_FIELD(ishypothetical);
COMPARE_SCALAR_FIELD(agglevelsup);
COMPARE_LOCATION_FIELD(location);
***************
*** 2006,2011 **** _equalFuncCall(const FuncCall *a, const FuncCall *b)
--- 2009,2015 ----
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
***************
*** 558,563 **** makeFuncCall(List *name, List *args, int location)
--- 558,564 ----
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
***************
*** 1631,1636 **** expression_tree_walker(Node *node,
--- 1631,1641 ----
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;
}
***************
*** 2155,2161 **** expression_tree_mutator(Node *node,
--- 2160,2168 ----
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
***************
*** 960,968 **** _outAggref(StringInfo str, const Aggref *node)
--- 960,971 ----
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(aggvariadic);
+ WRITE_BOOL_FIELD(isordset);
+ WRITE_BOOL_FIELD(ishypothetical);
WRITE_UINT_FIELD(agglevelsup);
WRITE_LOCATION_FIELD(location);
}
***************
*** 2090,2095 **** _outFuncCall(StringInfo str, const FuncCall *node)
--- 2093,2099 ----
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,503 **** _readAggref(void)
--- 495,506 ----
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(aggvariadic);
+ READ_BOOL_FIELD(isordset);
+ READ_BOOL_FIELD(ishypothetical);
READ_UINT_FIELD(agglevelsup);
READ_LOCATION_FIELD(location);
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
***************
*** 39,44 ****
--- 39,45 ----
#include "parser/analyze.h"
#include "parser/parse_coerce.h"
#include "parser/parse_func.h"
+ #include "parser/parse_agg.h"
#include "rewrite/rewriteManip.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
***************
*** 464,470 **** count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
QualCost argcosts;
Oid *inputTypes;
int numArguments;
- ListCell *l;
Assert(aggref->agglevelsup == 0);
--- 465,470 ----
***************
*** 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;
***************
*** 504,575 **** count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
costs->transCost.startup += argcosts.startup;
costs->transCost.per_tuple += argcosts.per_tuple;
! /* extract argument types (ignoring any ORDER BY expressions) */
! inputTypes = (Oid *) palloc(sizeof(Oid) * list_length(aggref->args));
! numArguments = 0;
! foreach(l, aggref->args)
{
! TargetEntry *tle = (TargetEntry *) lfirst(l);
! if (!tle->resjunk)
! inputTypes[numArguments++] = exprType((Node *) tle->expr);
! }
! /* resolve actual type of transition state, if polymorphic */
! if (IsPolymorphicType(aggtranstype))
! {
! /* have to fetch the agg's declared input types... */
! Oid *declaredArgTypes;
! int agg_nargs;
!
! (void) get_func_signature(aggref->aggfnoid,
! &declaredArgTypes, &agg_nargs);
! Assert(agg_nargs == numArguments);
! aggtranstype = enforce_generic_type_consistency(inputTypes,
! declaredArgTypes,
! agg_nargs,
! aggtranstype,
! false);
! pfree(declaredArgTypes);
! }
! /*
! * If the transition type is pass-by-value then it doesn't add
! * anything to the required size of the hashtable. If it is
! * 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;
/*
! * If transition state is of same type as first input, assume it's
! * the same typmod (same width) as well. This works for cases
! * like MAX/MIN and is probably somewhat reasonable otherwise.
*/
! if (numArguments > 0 && aggtranstype == inputTypes[0])
! aggtranstypmod = exprTypmod((Node *) linitial(aggref->args));
! else
! aggtranstypmod = -1;
! avgwidth = get_typavgwidth(aggtranstype, aggtranstypmod);
! avgwidth = MAXALIGN(avgwidth);
! costs->transitionSpace += avgwidth + 2 * sizeof(void *);
}
! else if (aggtranstype == INTERNALOID)
{
! /*
! * INTERNAL transition type is a special case: although INTERNAL
! * is pass-by-value, it's almost certainly being used as a pointer
! * to some large data structure. We assume usage of
! * ALLOCSET_DEFAULT_INITSIZE, which is a good guess if the data is
! * being kept in a private memory context, as is done by
! * array_agg() for instance.
! */
! costs->transitionSpace += ALLOCSET_DEFAULT_INITSIZE;
}
/*
--- 505,595 ----
costs->transCost.startup += argcosts.startup;
costs->transCost.per_tuple += argcosts.per_tuple;
! /*
! * If we're doing a sorted agg, we can punt the entire
! * determination of transition element size since we're not
! * going to be using it to determine hashtable limits. This
! * simplifies the code for hypothetical set functions.
! */
!
! if (aggref->aggorder == NIL && aggref->aggdistinct == NIL)
{
! Assert(!aggref->isordset);
! /* extract argument types (ignoring any ORDER BY expressions) */
! inputTypes = (Oid *) palloc(sizeof(Oid) * FUNC_MAX_ARGS);
! numArguments = get_aggregate_argtypes(aggref, inputTypes, NULL);
! /* resolve actual type of transition state, if polymorphic */
! if (OidIsValid(aggtranstype) && IsPolymorphicType(aggtranstype))
! {
! /* have to fetch the agg's declared input types... */
! Oid *declaredArgTypes;
! int agg_nargs;
!
! (void) get_func_signature(aggref->aggfnoid,
! &declaredArgTypes, &agg_nargs);
!
! /*
! * if variadic "any", might be more actual args than declared
! * args, but these extra args can't influence the determination
! * of polymorphic transition or result type.
! */
! Assert(agg_nargs <= numArguments);
!
! aggtranstype = enforce_generic_type_consistency(inputTypes,
! declaredArgTypes,
! agg_nargs,
! aggtranstype,
! false);
! pfree(declaredArgTypes);
! }
/*
! * If the transition type is pass-by-value then it doesn't add
! * anything to the required size of the hashtable. If it is
! * 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;
!
! /*
! * If transition state is of same type as first input, assume it's
! * the same typmod (same width) as well. This works for cases
! * like MAX/MIN and is probably somewhat reasonable otherwise.
! */
! if (numArguments > 0 && aggtranstype == inputTypes[0])
! aggtranstypmod = exprTypmod((Node *) linitial(aggref->args));
! else
! aggtranstypmod = -1;
! avgwidth = get_typavgwidth(aggtranstype, aggtranstypmod);
! avgwidth = MAXALIGN(avgwidth);
! costs->transitionSpace += avgwidth + 2 * sizeof(void *);
! }
! else if (aggtranstype == INTERNALOID)
! {
! /*
! * INTERNAL transition type is a special case: although INTERNAL
! * is pass-by-value, it's almost certainly being used as a pointer
! * to some large data structure. We assume usage of
! * ALLOCSET_DEFAULT_INITSIZE, which is a good guess if the data is
! * being kept in a private memory context, as is done by
! * array_agg() for instance.
! */
! costs->transitionSpace += ALLOCSET_DEFAULT_INITSIZE;
! }
!
! pfree(inputTypes);
}
! else
{
! costs->transitionSpace = work_mem; /* just in case */
}
/*
***************
*** 3826,3832 **** 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);
}
/*
--- 3846,3852 ----
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, false);
}
/*
*** a/src/backend/parser/analyze.c
--- b/src/backend/parser/analyze.c
***************
*** 951,957 **** transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
&qry->targetList,
EXPR_KIND_ORDER_BY,
true /* fix unknowns */ ,
! false /* allow SQL92 rules */ );
qry->groupClause = transformGroupClause(pstate,
stmt->groupClause,
--- 951,958 ----
&qry->targetList,
EXPR_KIND_ORDER_BY,
true /* fix unknowns */ ,
! false /* allow SQL92 rules */,
! false /* don't add duplicates */);
qry->groupClause = transformGroupClause(pstate,
stmt->groupClause,
***************
*** 1211,1217 **** transformValuesClause(ParseState *pstate, SelectStmt *stmt)
&qry->targetList,
EXPR_KIND_ORDER_BY,
true /* fix unknowns */ ,
! false /* allow SQL92 rules */ );
qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset,
EXPR_KIND_OFFSET, "OFFSET");
--- 1212,1219 ----
&qry->targetList,
EXPR_KIND_ORDER_BY,
true /* fix unknowns */ ,
! false /* allow SQL92 rules */,
! false /* don't add duplicates */ );
qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset,
EXPR_KIND_OFFSET, "OFFSET");
***************
*** 1435,1441 **** transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
&qry->targetList,
EXPR_KIND_ORDER_BY,
false /* no unknowns expected */ ,
! false /* allow SQL92 rules */ );
/* restore namespace, remove jrte from rtable */
pstate->p_namespace = sv_namespace;
--- 1437,1444 ----
&qry->targetList,
EXPR_KIND_ORDER_BY,
false /* no unknowns expected */ ,
! false /* allow SQL92 rules */ ,
! false /* don't add duplicates */ );
/* restore namespace, remove jrte from rtable */
pstate->p_namespace = sv_namespace;
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 494,499 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
--- 494,500 ----
%type opt_existing_window_name
%type opt_if_not_exists
%type filter_clause
+ %type within_group_clause
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
***************
*** 596,602 **** 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
--- 597,603 ----
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
***************
*** 3660,3666 **** AlterExtensionContentsStmt:
n->action = $4;
n->objtype = OBJECT_AGGREGATE;
n->objname = $6;
! n->objargs = extractArgTypes($7);
$$ = (Node *)n;
}
| ALTER EXTENSION name add_drop CAST '(' Typename AS Typename ')'
--- 3661,3667 ----
n->action = $4;
n->objtype = OBJECT_AGGREGATE;
n->objname = $6;
! n->objargs = extractArgTypes(linitial($7));
$$ = (Node *)n;
}
| ALTER EXTENSION name add_drop CAST '(' Typename AS Typename ')'
***************
*** 5239,5245 **** CommentStmt:
CommentStmt *n = makeNode(CommentStmt);
n->objtype = OBJECT_AGGREGATE;
n->objname = $4;
! n->objargs = extractArgTypes($5);
n->comment = $7;
$$ = (Node *) n;
}
--- 5240,5246 ----
CommentStmt *n = makeNode(CommentStmt);
n->objtype = OBJECT_AGGREGATE;
n->objname = $4;
! n->objargs = extractArgTypes(linitial($5));
n->comment = $7;
$$ = (Node *) n;
}
***************
*** 5405,5411 **** SecLabelStmt:
n->provider = $3;
n->objtype = OBJECT_AGGREGATE;
n->objname = $6;
! n->objargs = extractArgTypes($7);
n->label = $9;
$$ = (Node *) n;
}
--- 5406,5412 ----
n->provider = $3;
n->objtype = OBJECT_AGGREGATE;
n->objname = $6;
! n->objargs = extractArgTypes(linitial($7));
n->label = $9;
$$ = (Node *) n;
}
***************
*** 6405,6413 **** aggr_arg: func_arg
}
;
! /* Zero-argument aggregates are named with * for consistency with COUNT(*) */
! aggr_args: '(' aggr_args_list ')' { $$ = $2; }
! | '(' '*' ')' { $$ = NIL; }
;
aggr_args_list:
--- 6406,6458 ----
}
;
! /*
! * Aggregate args (for create aggregate, etc.) are treated as follows:
! *
! * (*) - no args
! * (func_arg,func_arg,...) - normal agg with args
! * () within group (func_arg,...) - ordered set func with no direct args
! * (func_arg,...) within group (func_arg,...) - ordered set func with args
! * (func_arg,...) within group (*) - ordered set func variadic special case
! *
! * This doesn't correspond to anything in the spec because the spec doesn't
! * have any DDL to create or modify ordered set functions, so we're winging
! * it here.
! *
! * Almost everything we do with an ordered set function treats its arguments
! * as though they were a single list, with the direct and grouped arg types
! * concatenated. So for simplicity, we construct a single list here.
! *
! * But we still need to know when creating an agg (but not for referring to it
! * later) where the division between direct and ordered args is; so this
! * production returns a pair (arglist,num) where num is the number of direct
! * args, or -1 if no within group clause was used. Most users of aggr_args,
! * other than CREATE AGGREGATE, therefore only need to pay attention to
! * linitial($n).
! */
!
! aggr_args: '(' '*' ')'
! {
! $$ = list_make2(NIL, makeInteger(-1));
! }
! | '(' aggr_args_list ')'
! {
! $$ = list_make2($2, makeInteger(-1));
! }
! | '(' ')' WITHIN GROUP_P '(' aggr_args_list ')'
! {
! $$ = list_make2($6, makeInteger(0));
! }
! | '(' aggr_args_list ')' WITHIN GROUP_P '(' aggr_args_list ')'
! {
! int n = list_length($2);
! $$ = list_make2(list_concat($2,$7), makeInteger(n));
! }
! | '(' aggr_args_list ')' WITHIN GROUP_P '(' '*' ')'
! {
! int n = list_length($2);
! $$ = list_make2($2, makeInteger(n));
! }
;
aggr_args_list:
***************
*** 6613,6619 **** RemoveAggrStmt:
DropStmt *n = makeNode(DropStmt);
n->removeType = OBJECT_AGGREGATE;
n->objects = list_make1($3);
! n->arguments = list_make1(extractArgTypes($4));
n->behavior = $5;
n->missing_ok = false;
n->concurrent = false;
--- 6658,6664 ----
DropStmt *n = makeNode(DropStmt);
n->removeType = OBJECT_AGGREGATE;
n->objects = list_make1($3);
! n->arguments = list_make1(extractArgTypes(linitial($4)));
n->behavior = $5;
n->missing_ok = false;
n->concurrent = false;
***************
*** 6624,6630 **** RemoveAggrStmt:
DropStmt *n = makeNode(DropStmt);
n->removeType = OBJECT_AGGREGATE;
n->objects = list_make1($5);
! n->arguments = list_make1(extractArgTypes($6));
n->behavior = $7;
n->missing_ok = true;
n->concurrent = false;
--- 6669,6675 ----
DropStmt *n = makeNode(DropStmt);
n->removeType = OBJECT_AGGREGATE;
n->objects = list_make1($5);
! n->arguments = list_make1(extractArgTypes(linitial($6)));
n->behavior = $7;
n->missing_ok = true;
n->concurrent = false;
***************
*** 6840,6846 **** RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_AGGREGATE;
n->object = $3;
! n->objarg = extractArgTypes($4);
n->newname = $7;
n->missing_ok = false;
$$ = (Node *)n;
--- 6885,6891 ----
RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_AGGREGATE;
n->object = $3;
! n->objarg = extractArgTypes(linitial($4));
n->newname = $7;
n->missing_ok = false;
$$ = (Node *)n;
***************
*** 7314,7320 **** AlterObjectSchemaStmt:
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
n->objectType = OBJECT_AGGREGATE;
n->object = $3;
! n->objarg = extractArgTypes($4);
n->newschema = $7;
n->missing_ok = false;
$$ = (Node *)n;
--- 7359,7365 ----
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
n->objectType = OBJECT_AGGREGATE;
n->object = $3;
! n->objarg = extractArgTypes(linitial($4));
n->newschema = $7;
n->missing_ok = false;
$$ = (Node *)n;
***************
*** 7543,7549 **** AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
n->objectType = OBJECT_AGGREGATE;
n->object = $3;
! n->objarg = extractArgTypes($4);
n->newowner = $7;
$$ = (Node *)n;
}
--- 7588,7594 ----
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
n->objectType = OBJECT_AGGREGATE;
n->object = $3;
! n->objarg = extractArgTypes(linitial($4));
n->newowner = $7;
$$ = (Node *)n;
}
***************
*** 9431,9436 **** sortby: a_expr USING qual_all_Op opt_nulls_order
--- 9476,9486 ----
;
+ within_group_clause:
+ WITHIN GROUP_P '(' sort_clause ')' { $$ = $4; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
select_limit:
limit_clause offset_clause { $$ = list_make2($2, $1); }
| offset_clause limit_clause { $$ = list_make2($1, $2); }
***************
*** 11152,11163 **** 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
{ $$ = $1; }
--- 11202,11236 ----
* (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 != NIL)
! {
! if (n->agg_order != NIL)
! 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
{ $$ = $1; }
***************
*** 12716,12721 **** unreserved_keyword:
--- 12789,12795 ----
| 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,52 ----
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;
--- 77,84 ----
*/
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);
}
/*
--- 96,119 ----
*/
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;
}
/*
***************
*** 109,114 **** transformAggregateCall(ParseState *pstate, Aggref *agg,
--- 124,134 ----
*
* We need to mess with p_next_resno since it will be used to number any
* new targetlist entries.
+ *
+ * If and only if we're doing a WITHIN GROUP list, we preserve any
+ * duplicate expressions in the sort clause. This is needed because the
+ * sort clause of WITHIN GROUP is really an argument list, and we must
+ * keep the number and content of entries matching the specified input.
*/
save_next_resno = pstate->p_next_resno;
pstate->p_next_resno = attno;
***************
*** 118,124 **** transformAggregateCall(ParseState *pstate, Aggref *agg,
&tlist,
EXPR_KIND_ORDER_BY,
true /* fix unknowns */ ,
! true /* force SQL99 rules */ );
/*
* If we have DISTINCT, transform that to produce a distinctList.
--- 138,145 ----
&tlist,
EXPR_KIND_ORDER_BY,
true /* fix unknowns */ ,
! true /* force SQL99 rules */ ,
! agg_within_group /* keep duplicates? */ );
/*
* If we have DISTINCT, transform that to produce a distinctList.
***************
*** 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 */
--- 181,188 ----
* 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;
--- 334,340 ----
* 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)
--- 352,360 ----
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;
}
--- 378,385 ----
(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
--- 848,863 ----
* 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((Node*)(((Aggref *)node)->orddirectargs),
+ context);
+ }
/*
* If we have any GROUP BY items that are not simple Vars, check to see if
***************
*** 1042,1044 **** build_aggregate_fnexprs(Oid *agg_input_types,
--- 1075,1172 ----
agg_input_collation,
COERCE_EXPLICIT_CALL);
}
+
+ void
+ build_orderedset_fnexprs(Oid *agg_input_types,
+ int agg_num_inputs,
+ bool agg_variadic,
+ Oid agg_result_type,
+ Oid agg_input_collation,
+ Oid *agg_input_collation_array,
+ Oid finalfn_oid,
+ Expr **finalfnexpr)
+ {
+ FuncExpr *fexpr;
+ Param *argp;
+ List *args = NIL;
+ int i = 0;
+
+ /*
+ * Build arg list to use in the finalfn FuncExpr node. We really only care
+ * that finalfn can discover the actual argument types at runtime using
+ * get_fn_expr_argtype(), so it's okay to use Param nodes that don't
+ * correspond to any real Param.
+ */
+ for (i = 0; i < agg_num_inputs; i++)
+ {
+ argp = makeNode(Param);
+ argp->paramkind = PARAM_EXEC;
+ argp->paramid = -1;
+ argp->paramtype = agg_input_types[i];
+ argp->paramtypmod = -1;
+ argp->paramcollid = agg_input_collation_array[i];
+ argp->location = -1;
+
+ args = lappend(args, argp);
+ }
+
+ fexpr = makeFuncExpr(finalfn_oid,
+ agg_result_type,
+ args,
+ InvalidOid,
+ agg_input_collation,
+ COERCE_EXPLICIT_CALL);
+ fexpr->funcvariadic = agg_variadic;
+ *finalfnexpr = (Expr *) fexpr;
+ }
+
+ int get_aggregate_argtypes(Aggref *aggref, Oid *inputTypes, Oid *inputCollations)
+ {
+ int numArguments = 0;
+ ListCell *lc;
+
+ if (!(aggref->isordset))
+ {
+ foreach(lc, aggref->args)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+ if (!tle->resjunk)
+ {
+ inputTypes[numArguments] = exprType((Node *) tle->expr);
+ if (inputCollations != NULL)
+ inputCollations[numArguments] = exprCollation((Node *) tle->expr);
+ ++numArguments;
+ }
+ }
+ }
+ else
+ {
+ foreach(lc, aggref->orddirectargs)
+ {
+ Node *expr_orddirectargs = lfirst(lc);
+
+ inputTypes[numArguments] = exprType(expr_orddirectargs);
+ if (inputCollations != NULL)
+ inputCollations[numArguments] = exprCollation(expr_orddirectargs);
+
+ ++numArguments;
+ }
+
+ if (!(aggref->ishypothetical))
+ {
+ foreach(lc, aggref->args)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+ inputTypes[numArguments] = exprType((Node *) tle->expr);
+ if (inputCollations != NULL)
+ inputCollations[numArguments] = exprCollation((Node *) tle->expr);
+
+ ++numArguments;
+ }
+ }
+ }
+
+ return numArguments;
+ }
*** a/src/backend/parser/parse_clause.c
--- b/src/backend/parser/parse_clause.c
***************
*** 71,77 **** static void checkExprIsVarFree(ParseState *pstate, Node *n,
static TargetEntry *findTargetlistEntrySQL92(ParseState *pstate, Node *node,
List **tlist, ParseExprKind exprKind);
static TargetEntry *findTargetlistEntrySQL99(ParseState *pstate, Node *node,
! List **tlist, ParseExprKind exprKind);
static int get_matching_location(int sortgroupref,
List *sortgrouprefs, List *exprs);
static List *addTargetToSortList(ParseState *pstate, TargetEntry *tle,
--- 71,78 ----
static TargetEntry *findTargetlistEntrySQL92(ParseState *pstate, Node *node,
List **tlist, ParseExprKind exprKind);
static TargetEntry *findTargetlistEntrySQL99(ParseState *pstate, Node *node,
! List **tlist, ParseExprKind exprKind,
! bool keepDuplicates);
static int get_matching_location(int sortgroupref,
List *sortgrouprefs, List *exprs);
static List *addTargetToSortList(ParseState *pstate, TargetEntry *tle,
***************
*** 1476,1482 **** findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist,
/*
* Otherwise, we have an expression, so process it per SQL99 rules.
*/
! return findTargetlistEntrySQL99(pstate, node, tlist, exprKind);
}
/*
--- 1477,1483 ----
/*
* Otherwise, we have an expression, so process it per SQL99 rules.
*/
! return findTargetlistEntrySQL99(pstate, node, tlist, exprKind, false);
}
/*
***************
*** 1491,1500 **** findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist,
* node the ORDER BY, GROUP BY, etc expression to be matched
* tlist the target list (passed by reference so we can append to it)
* exprKind identifies clause type being processed
*/
static TargetEntry *
findTargetlistEntrySQL99(ParseState *pstate, Node *node, List **tlist,
! ParseExprKind exprKind)
{
TargetEntry *target_result;
ListCell *tl;
--- 1492,1502 ----
* node the ORDER BY, GROUP BY, etc expression to be matched
* tlist the target list (passed by reference so we can append to it)
* exprKind identifies clause type being processed
+ * keepDuplicates if true, don't try and match to any existing entry
*/
static TargetEntry *
findTargetlistEntrySQL99(ParseState *pstate, Node *node, List **tlist,
! ParseExprKind exprKind, bool keepDuplicates)
{
TargetEntry *target_result;
ListCell *tl;
***************
*** 1509,1532 **** findTargetlistEntrySQL99(ParseState *pstate, Node *node, List **tlist,
*/
expr = transformExpr(pstate, node, exprKind);
! foreach(tl, *tlist)
{
! TargetEntry *tle = (TargetEntry *) lfirst(tl);
! Node *texpr;
! /*
! * Ignore any implicit cast on the existing tlist expression.
! *
! * This essentially allows the ORDER/GROUP/etc item to adopt the same
! * datatype previously selected for a textually-equivalent tlist item.
! * There can't be any implicit cast at top level in an ordinary SELECT
! * tlist at this stage, but the case does arise with ORDER BY in an
! * aggregate function.
! */
! texpr = strip_implicit_coercions((Node *) tle->expr);
! if (equal(expr, texpr))
! return tle;
}
/*
--- 1511,1537 ----
*/
expr = transformExpr(pstate, node, exprKind);
! if (!keepDuplicates)
{
! foreach(tl, *tlist)
! {
! TargetEntry *tle = (TargetEntry *) lfirst(tl);
! Node *texpr;
! /*
! * Ignore any implicit cast on the existing tlist expression.
! *
! * This essentially allows the ORDER/GROUP/etc item to adopt the same
! * datatype previously selected for a textually-equivalent tlist item.
! * There can't be any implicit cast at top level in an ordinary SELECT
! * tlist at this stage, but the case does arise with ORDER BY in an
! * aggregate function.
! */
! texpr = strip_implicit_coercions((Node *) tle->expr);
! if (equal(expr, texpr))
! return tle;
! }
}
/*
***************
*** 1568,1574 **** transformGroupClause(ParseState *pstate, List *grouplist,
if (useSQL99)
tle = findTargetlistEntrySQL99(pstate, gexpr,
! targetlist, exprKind);
else
tle = findTargetlistEntrySQL92(pstate, gexpr,
targetlist, exprKind);
--- 1573,1579 ----
if (useSQL99)
tle = findTargetlistEntrySQL99(pstate, gexpr,
! targetlist, exprKind, false);
else
tle = findTargetlistEntrySQL92(pstate, gexpr,
targetlist, exprKind);
***************
*** 1635,1645 **** transformSortClause(ParseState *pstate,
List **targetlist,
ParseExprKind exprKind,
bool resolveUnknown,
! bool useSQL99)
{
List *sortlist = NIL;
ListCell *olitem;
foreach(olitem, orderlist)
{
SortBy *sortby = (SortBy *) lfirst(olitem);
--- 1640,1653 ----
List **targetlist,
ParseExprKind exprKind,
bool resolveUnknown,
! bool useSQL99,
! bool keepDuplicates)
{
List *sortlist = NIL;
ListCell *olitem;
+ Assert(useSQL99 || !keepDuplicates);
+
foreach(olitem, orderlist)
{
SortBy *sortby = (SortBy *) lfirst(olitem);
***************
*** 1647,1653 **** transformSortClause(ParseState *pstate,
if (useSQL99)
tle = findTargetlistEntrySQL99(pstate, sortby->node,
! targetlist, exprKind);
else
tle = findTargetlistEntrySQL92(pstate, sortby->node,
targetlist, exprKind);
--- 1655,1661 ----
if (useSQL99)
tle = findTargetlistEntrySQL99(pstate, sortby->node,
! targetlist, exprKind, keepDuplicates);
else
tle = findTargetlistEntrySQL92(pstate, sortby->node,
targetlist, exprKind);
***************
*** 1717,1723 **** transformWindowDefinitions(ParseState *pstate,
targetlist,
EXPR_KIND_WINDOW_ORDER,
true /* fix unknowns */ ,
! true /* force SQL99 rules */ );
partitionClause = transformGroupClause(pstate,
windef->partitionClause,
targetlist,
--- 1725,1732 ----
targetlist,
EXPR_KIND_WINDOW_ORDER,
true /* fix unknowns */ ,
! true /* force SQL99 rules */,
! false /* don't add duplicates */);
partitionClause = transformGroupClause(pstate,
windef->partitionClause,
targetlist,
*** a/src/backend/parser/parse_collate.c
--- b/src/backend/parser/parse_collate.c
***************
*** 73,79 **** typedef struct
static bool assign_query_collations_walker(Node *node, ParseState *pstate);
static bool assign_collations_walker(Node *node,
assign_collations_context *context);
!
/*
* assign_query_collations()
--- 73,81 ----
static bool assign_query_collations_walker(Node *node, ParseState *pstate);
static bool assign_collations_walker(Node *node,
assign_collations_context *context);
! static void assign_aggregate_collations(Aggref *aggref,
! assign_collations_context *context,
! assign_collations_context *loccontext);
/*
* assign_query_collations()
***************
*** 564,607 **** assign_collations_walker(Node *node, assign_collations_context *context)
case T_Aggref:
{
/*
! * Aggref is a special case because expressions
! * used only for ordering shouldn't be taken to
! * conflict with each other or with regular args.
! * So we apply assign_expr_collations() to them
! * rather than passing down our loccontext.
! *
! * Note that we recurse to each TargetEntry, not
! * directly to its contained expression, so that
! * the case above for T_TargetEntry will apply
! * appropriate checks to agg ORDER BY items.
! *
! * Likewise, we assign collations for the (bool)
! * expression in aggfilter, independently of any
! * other args.
! *
! * We need not recurse into the aggorder or
! * aggdistinct lists, because those contain only
! * SortGroupClause nodes which we need not
! * process.
*/
Aggref *aggref = (Aggref *) node;
- ListCell *lc;
! foreach(lc, aggref->args)
! {
! TargetEntry *tle = (TargetEntry *) lfirst(lc);
!
! Assert(IsA(tle, TargetEntry));
! if (tle->resjunk)
! assign_expr_collations(context->pstate,
! (Node *) tle);
! else
! (void) assign_collations_walker((Node *) tle,
! &loccontext);
! }
assign_expr_collations(context->pstate,
! (Node *) aggref->aggfilter);
}
break;
case T_WindowFunc:
--- 566,581 ----
case T_Aggref:
{
/*
! * Aggref is special enough that we give it its own
! * function. The FILTER clause is independent of the
! * rest of the aggregate, however.
*/
Aggref *aggref = (Aggref *) node;
! assign_aggregate_collations(aggref, context, &loccontext);
assign_expr_collations(context->pstate,
! (Node *) aggref->aggfilter);
}
break;
case T_WindowFunc:
***************
*** 802,804 **** assign_collations_walker(Node *node, assign_collations_context *context)
--- 776,934 ----
return false;
}
+
+
+ /*
+ * Aggref is a special case because expressions used only for ordering
+ * shouldn't be taken to conflict with each other or with regular args. So we
+ * apply assign_expr_collations() to them rather than passing down our
+ * loccontext.
+ *
+ * Note that we recurse to each TargetEntry, not directly to its contained
+ * expression, so that the case above for T_TargetEntry will apply appropriate
+ * checks to agg ORDER BY items.
+ *
+ * We need not recurse into the aggorder or aggdistinct lists, because those
+ * contain only SortGroupClause nodes which we need not process.
+ *
+ * For ordered set functions, it's unfortunately unclear how best to proceed.
+ * The spec-defined inverse distribution functions have only one sort column
+ * and don't allow collatable types, but this is clearly unsatisfactory in the
+ * general case. Compromise by taking the sort column as part of the collation
+ * determination if, and only if, there is only one such column, and force the
+ * final choice of input collation down into the sort column if need be; but
+ * don't error out unless actually necessary (leaving it up to the function to
+ * handle the issue at runtime). This ugly wart is justified by the fact that
+ * there seems to be no other good way to get a result collation for
+ * percentile_* applied to a collatable type.
+ *
+ * But hypothetical set functions are special; they must have
+ * pairwise-assigned collations for each matching pair of args, and again we
+ * need to force the final choice of collation down into the sort column to
+ * ensure that the sort happens on the chosen collation. If there are any
+ * additional args (not allowed in the spec, but a user-defined function might
+ * have some), those contribute to the result collation in the normal way.
+ * (The hypothetical paired args never contribute to the result collation at
+ * all.)
+ */
+
+ static Expr *
+ relabel_expr_collation(Expr *expr, Oid newcollation)
+ {
+ RelabelType *node = makeNode(RelabelType);
+ node->arg = expr;
+ node->resulttype = exprType((Node *)expr);
+ node->resulttypmod = exprTypmod((Node *)expr);
+ node->resultcollid = newcollation;
+ node->relabelformat = COERCE_IMPLICIT_CAST;
+ node->location = exprLocation((Node *)expr);
+ return (Expr *) node;
+ }
+
+ static void
+ assign_aggregate_collations(Aggref *aggref,
+ assign_collations_context *context,
+ assign_collations_context *loccontext)
+ {
+ ListCell *lc;
+
+ if (aggref->ishypothetical)
+ {
+ /*-
+ * Hypothetical set function, i.e.
+ * func(..., a,b,c,...) within group (p,q,r,...)
+ *
+ * Any initial set of direct args (before "a") contributes to the
+ * result collation in the usual way for function args. But none of
+ * a,b,c... or p,q,r... contribute at all; instead, they must be
+ * paired up (as though UNIONed) and the sorted col's collation forced
+ * to the chosen value (so that we sort it correctly).
+ */
+ int initial_args = list_length(aggref->orddirectargs) - list_length(aggref->args);
+ ListCell *h_arg = list_head(aggref->orddirectargs);
+ ListCell *s_arg = list_head(aggref->args);
+
+ Assert(initial_args >= 0);
+
+ while (initial_args-- > 0)
+ {
+ (void) assign_collations_walker((Node *) lfirst(h_arg), loccontext);
+ h_arg = lnext(h_arg);
+ }
+
+ for_each_cell(h_arg,h_arg)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(s_arg);
+ Oid coll = select_common_collation(context->pstate,
+ list_make2(lfirst(h_arg),lfirst(s_arg)),
+ false);
+
+ /*
+ * we can only get InvalidOid here if the type is not collatable,
+ * so no need to try and relabel in that case.
+ */
+
+ if (OidIsValid(coll)
+ && coll != exprCollation((Node *)(tle->expr)))
+ {
+ tle->expr = relabel_expr_collation(tle->expr, coll);
+ }
+
+ s_arg = lnext(s_arg);
+ }
+ }
+ else if (aggref->isordset && list_length(aggref->args) == 1)
+ {
+ /*
+ * Ordered set func with one sorted arg
+ */
+ TargetEntry *tle = (TargetEntry *) linitial(aggref->args);
+
+ /* do the TLE first so that it won't error out on conflicts */
+
+ (void) assign_collations_walker((Node *) tle,
+ loccontext);
+
+ (void) assign_collations_walker((Node *) aggref->orddirectargs,
+ loccontext);
+
+ /*
+ * If the sort col is a collatable type, and we chose a collation,
+ * and it's not the one the sort col already has, then force the
+ * sort col's collation (which can't have been explicit) to the
+ * chosen one. Otherwise leave it alone.
+ */
+ if (type_is_collatable(exprType((Node *)(tle->expr)))
+ && (loccontext->strength == COLLATE_IMPLICIT
+ || loccontext->strength == COLLATE_EXPLICIT)
+ && exprCollation((Node *)(tle->expr)) != loccontext->collation)
+ {
+ tle->expr = relabel_expr_collation(tle->expr, loccontext->collation);
+ }
+ }
+ else
+ {
+ /*
+ * For this case, we do the direct args (if any) together, as is
+ * normal for functions, but args which are either used only for
+ * sorting or are only part of a WITHIN GROUP are processed
+ * individually.
+ */
+
+ (void) assign_collations_walker((Node *) aggref->orddirectargs,
+ loccontext);
+
+ foreach(lc, aggref->args)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+ Assert(IsA(tle, TargetEntry));
+ if (tle->resjunk)
+ assign_expr_collations(context->pstate,
+ (Node *) tle);
+ else
+ (void) assign_collations_walker((Node *) tle,
+ loccontext);
+ }
+ }
+ }
*** a/src/backend/parser/parse_expr.c
--- b/src/backend/parser/parse_expr.c
***************
*** 463,470 **** transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
newresult = ParseFuncOrColumn(pstate,
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;
--- 463,470 ----
newresult = ParseFuncOrColumn(pstate,
list_make1(n),
list_make1(result),
! location,
! NULL);
if (newresult == NULL)
unknown_attribute(pstate, result, strVal(n), location);
result = newresult;
***************
*** 631,638 **** transformColumnRef(ParseState *pstate, ColumnRef *cref)
node = ParseFuncOrColumn(pstate,
list_make1(makeString(colname)),
list_make1(node),
! NIL, NULL, false, false, false,
! NULL, true, cref->location);
}
break;
}
--- 631,637 ----
node = ParseFuncOrColumn(pstate,
list_make1(makeString(colname)),
list_make1(node),
! cref->location, NULL);
}
break;
}
***************
*** 676,683 **** transformColumnRef(ParseState *pstate, ColumnRef *cref)
node = ParseFuncOrColumn(pstate,
list_make1(makeString(colname)),
list_make1(node),
! NIL, NULL, false, false, false,
! NULL, true, cref->location);
}
break;
}
--- 675,681 ----
node = ParseFuncOrColumn(pstate,
list_make1(makeString(colname)),
list_make1(node),
! cref->location, NULL);
}
break;
}
***************
*** 734,741 **** transformColumnRef(ParseState *pstate, ColumnRef *cref)
node = ParseFuncOrColumn(pstate,
list_make1(makeString(colname)),
list_make1(node),
! NIL, NULL, false, false, false,
! NULL, true, cref->location);
}
break;
}
--- 732,738 ----
node = ParseFuncOrColumn(pstate,
list_make1(makeString(colname)),
list_make1(node),
! cref->location, NULL);
}
break;
}
***************
*** 1242,1279 **** transformFuncCall(ParseState *pstate, FuncCall *fn)
{
List *targs;
ListCell *args;
- Expr *tagg_filter;
/* Transform the list of arguments ... */
targs = NIL;
foreach(args, fn->args)
{
! targs = lappend(targs, transformExprRecurse(pstate,
! (Node *) lfirst(args)));
}
- /*
- * Transform the aggregate filter using transformWhereClause(), to which
- * FILTER is virtually identical...
- */
- tagg_filter = NULL;
- if (fn->agg_filter != NULL)
- tagg_filter = (Expr *)
- transformWhereClause(pstate, (Node *) fn->agg_filter,
- EXPR_KIND_FILTER, "FILTER");
-
/* ... and hand off to ParseFuncOrColumn */
return ParseFuncOrColumn(pstate,
fn->funcname,
targs,
! fn->agg_order,
! tagg_filter,
! fn->agg_star,
! fn->agg_distinct,
! fn->func_variadic,
! fn->over,
! false,
! fn->location);
}
static Node *
--- 1239,1258 ----
{
List *targs;
ListCell *args;
/* Transform the list of arguments ... */
targs = NIL;
foreach(args, fn->args)
{
! targs = lappend(targs, transformExprRecurse(pstate, (Node *) lfirst(args)));
}
/* ... and hand off to ParseFuncOrColumn */
return ParseFuncOrColumn(pstate,
fn->funcname,
targs,
! fn->location,
! fn);
}
static Node *
*** a/src/backend/parser/parse_func.c
--- b/src/backend/parser/parse_func.c
***************
*** 17,32 ****
--- 17,35 ----
#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"
#include "nodes/nodeFuncs.h"
#include "parser/parse_agg.h"
+ #include "parser/parse_clause.h"
#include "parser/parse_coerce.h"
#include "parser/parse_func.h"
#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"
***************
*** 56,70 **** static Node *ParseComplexProjection(ParseState *pstate, char *funcname,
* Also, when is_column is true, we return NULL on failure rather than
* reporting a no-such-function error.
*
! * The argument expressions (in fargs) and filter must have been transformed
! * already. But the agg_order expressions, if any, have not been.
*/
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;
ListCell *l;
--- 59,79 ----
* Also, when is_column is true, we return NULL on failure rather than
* reporting a no-such-function error.
*
! * The argument expressions (in fargs) must have been transformed
! * already.
*/
Node *
ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
! int location, FuncCall *fn)
{
+ List *agg_order = (fn ? fn->agg_order : NIL);
+ Expr *agg_filter = NULL;
+ bool agg_star = (fn ? fn->agg_star : false);
+ bool agg_distinct = (fn ? fn->agg_distinct : false);
+ bool agg_within_group = (fn ? fn->has_within_group : false);
+ bool func_variadic = (fn ? fn->func_variadic : false);
+ WindowDef *over = (fn ? fn->over : NULL);
+ bool is_column = (fn == NULL);
Oid rettype;
Oid funcid;
ListCell *l;
***************
*** 81,86 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 90,101 ----
int nvargs;
Oid vatype;
FuncDetailCode fdresult;
+ int number_of_args = -1;
+ bool isordsetfunc = false;
+ bool ishypotheticalsetfunc = false;
+
+ /* 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
***************
*** 98,103 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 113,127 ----
parser_errposition(pstate, location)));
/*
+ * Transform the aggregate filter using transformWhereClause(), to which
+ * FILTER is virtually identical...
+ */
+ if (fn && fn->agg_filter != NULL)
+ agg_filter = (Expr *)
+ transformWhereClause(pstate, (Node *) fn->agg_filter,
+ EXPR_KIND_FILTER, "FILTER");
+
+ /*
* Extract arg type info in preparation for function lookup.
*
* If any arguments are Param markers of type VOID, we discard them from
***************
*** 163,168 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 187,198 ----
}
}
+ if (agg_within_group && argnames)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("ordered set functions cannot use named arguments"),
+ parser_errposition(pstate, location)));
+
if (fargs)
{
first_arg = linitial(fargs);
***************
*** 170,175 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 200,225 ----
}
/*
+ * 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 GROUP
+ * clause.
+ */
+ if (agg_within_group)
+ {
+ Assert(agg_order != NIL);
+
+ 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
* the "function call" could be a projection. We also check that there
***************
*** 247,252 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 297,308 ----
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,
--- 322,373 ----
NameListToString(funcname)),
parser_errposition(pstate, location)));
}
+ else if (fdresult == FUNCDETAIL_AGGREGATE)
+ {
+ HeapTuple tup;
+ 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);
+ isordsetfunc = classForm->aggisordsetfunc;
+
+ if (isordsetfunc)
+ {
+ if (classForm->aggordnargs == -2)
+ {
+ ishypotheticalsetfunc = true;
+
+ if (nvargs != 2*list_length(agg_order))
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("Incorrect number of arguments for hypothetical set function"),
+ parser_errposition(pstate, location)));
+ }
+ else
+ {
+ number_of_args = classForm->aggordnargs;
+ }
+
+ if (!agg_within_group)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("WITHIN GROUP is required for call to ordered set function %s",
+ NameListToString(funcname)),
+ parser_errposition(pstate, location)));
+
+ if (over)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("OVER clause not supported for call to ordered set function %s",
+ NameListToString(funcname)),
+ parser_errposition(pstate, location)));
+ }
+
+ ReleaseSysCache(tup);
+ }
else if (!(fdresult == FUNCDETAIL_AGGREGATE ||
fdresult == FUNCDETAIL_WINDOWFUNC))
{
***************
*** 351,363 **** 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
* into an array --- unless it's an "any" variadic.
*/
! if (nvargs > 0 && declared_arg_types[nargs - 1] != ANYOID)
{
ArrayExpr *newa = makeNode(ArrayExpr);
int non_var_args = nargs - nvargs;
--- 453,468 ----
false);
/* perform the necessary typecasting of arguments */
! make_fn_arguments(pstate, fargs, (isordsetfunc) ? agg_order : NIL,
! actual_arg_types,
! declared_arg_types,
! ishypotheticalsetfunc);
/*
* If it's a variadic function call, transform the last nvargs arguments
* into an array --- unless it's an "any" variadic.
*/
! if (nvargs > 0 && vatype != ANYOID)
{
ArrayExpr *newa = makeNode(ArrayExpr);
int non_var_args = nargs - nvargs;
***************
*** 388,403 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
* When function is called with an explicit VARIADIC labeled parameter,
* and the declared_arg_type is "any", then sanity check the actual
* parameter type now - it must be an array.
*/
if (nargs > 0 && vatype == ANYOID && func_variadic)
{
! Oid va_arr_typid = actual_arg_types[nargs - 1];
if (!OidIsValid(get_element_type(va_arr_typid)))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("VARIADIC argument must be an array"),
! parser_errposition(pstate, exprLocation((Node *) llast(fargs)))));
}
/* build the appropriate output structure */
--- 493,523 ----
* When function is called with an explicit VARIADIC labeled parameter,
* and the declared_arg_type is "any", then sanity check the actual
* parameter type now - it must be an array.
+ *
+ * Also, it can't be a hypothetical set function, and if it's an ordered
+ * set function, the variadic labeled parameter is the last _direct_ arg,
+ * not an ordered arg. (In practice we're unlikely to get this far for
+ * hypotheticals, since make_fn_arguments would probably fail to unify
+ * types, but we can't change the order of these.)
*/
if (nargs > 0 && vatype == ANYOID && func_variadic)
{
! int ignore_args = (agg_within_group ? list_length(agg_order) : 0);
! Oid va_arr_typid = actual_arg_types[nargs - 1 - ignore_args];
!
! if (ishypotheticalsetfunc)
! ereport(ERROR,
! (errcode(ERRCODE_SYNTAX_ERROR),
! errmsg("explicit VARIADIC argument not allowed for hypothetical set function"),
! parser_errposition(pstate,
! exprLocation((Node *) list_nth(fargs, nargs - 1 - ignore_args)))));
if (!OidIsValid(get_element_type(va_arr_typid)))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("VARIADIC argument must be an array"),
! parser_errposition(pstate,
! exprLocation((Node *) list_nth(fargs, nargs - 1 - ignore_args)))));
}
/* build the appropriate output structure */
***************
*** 421,426 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 541,552 ----
/* aggregate function */
Aggref *aggref = makeNode(Aggref);
+ if (agg_within_group && !isordsetfunc)
+ 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;
/* aggcollid and inputcollid will be set by parse_collate.c */
***************
*** 428,441 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
aggref->aggfilter = agg_filter;
aggref->aggstar = agg_star;
aggref->aggvariadic = func_variadic;
/* agglevelsup will be set by transformAggregateCall */
aggref->location = location;
/*
* Reject attempt to call a parameterless aggregate without (*)
* syntax. This is mere pedantry but some folks insisted ...
*/
! if (fargs == NIL && !agg_star)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("%s(*) must be used to call a parameterless aggregate function",
--- 554,577 ----
aggref->aggfilter = agg_filter;
aggref->aggstar = agg_star;
aggref->aggvariadic = func_variadic;
+ aggref->ishypothetical = ishypotheticalsetfunc;
/* agglevelsup will be set by transformAggregateCall */
aggref->location = location;
+ if (isordsetfunc
+ && number_of_args >= 0
+ && number_of_args != list_length(fargs))
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("Incorrect number of direct arguments to ordered set function %s",
+ NameListToString(funcname)),
+ parser_errposition(pstate, location)));
+
/*
* Reject attempt to call a parameterless aggregate without (*)
* syntax. This is mere pedantry but some folks insisted ...
*/
! if (fargs == NIL && !agg_star && !agg_within_group)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("%s(*) must be used to call a parameterless aggregate function",
***************
*** 464,470 **** 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;
}
--- 600,607 ----
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;
}
***************
*** 473,478 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 610,621 ----
/* 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.
*/
***************
*** 1374,1384 **** func_get_detail(List *funcname,
void
make_fn_arguments(ParseState *pstate,
List *fargs,
Oid *actual_arg_types,
! Oid *declared_arg_types)
{
ListCell *current_fargs;
int i = 0;
foreach(current_fargs, fargs)
{
--- 1517,1537 ----
void
make_fn_arguments(ParseState *pstate,
List *fargs,
+ List *agg_order,
Oid *actual_arg_types,
! Oid *declared_arg_types,
! bool requiresUnification)
{
ListCell *current_fargs;
+ ListCell *current_aoargs;
int i = 0;
+ int unify_offset = -1;
+
+ if (requiresUnification)
+ {
+ unify_offset = list_length(fargs) - list_length(agg_order);
+ Assert(unify_offset >= 0);
+ }
foreach(current_fargs, fargs)
{
***************
*** 1386,1391 **** make_fn_arguments(ParseState *pstate,
--- 1539,1545 ----
if (actual_arg_types[i] != declared_arg_types[i])
{
Node *node = (Node *) lfirst(current_fargs);
+ Node *temp = NULL;
/*
* If arg is a NamedArgExpr, coerce its input expr instead --- we
***************
*** 1406,1423 **** make_fn_arguments(ParseState *pstate,
}
else
{
! node = coerce_type(pstate,
! node,
! actual_arg_types[i],
! declared_arg_types[i], -1,
! COERCION_IMPLICIT,
! COERCE_IMPLICIT_CAST,
! -1);
! lfirst(current_fargs) = node;
}
}
i++;
}
}
/*
--- 1560,1625 ----
}
else
{
! /*
! * If we are dealing with a hypothetical set function, we
! * need to unify agg_order and fargs.
! */
!
! if (declared_arg_types[i] == ANYOID && requiresUnification)
! {
! Oid unification_oid;
! SortBy *unify_with = (SortBy *) list_nth(agg_order,i - unify_offset);
!
! unification_oid = select_common_type(pstate,
! list_make2(unify_with->node,node),
! "WITHIN GROUP",
! NULL);
!
! declared_arg_types[i + list_length(agg_order)] = unification_oid;
!
! temp = coerce_type(pstate,
! node,
! actual_arg_types[i],
! unification_oid, -1,
! COERCION_IMPLICIT,
! COERCE_IMPLICIT_CAST,
! -1);
! }
! else
! {
! temp = coerce_type(pstate,
! node,
! actual_arg_types[i],
! declared_arg_types[i], -1,
! COERCION_IMPLICIT,
! COERCE_IMPLICIT_CAST,
! -1);
! }
!
! lfirst(current_fargs) = temp;
}
}
i++;
}
+
+ foreach(current_aoargs, agg_order)
+ {
+ if (actual_arg_types[i] != declared_arg_types[i])
+ {
+ SortBy *node = (SortBy *) lfirst(current_aoargs);
+ Node *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, false);
/* 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, false);
/* and build the expression node */
result = makeNode(ScalarArrayOpExpr);
*** a/src/backend/utils/adt/Makefile
--- b/src/backend/utils/adt/Makefile
***************
*** 19,31 **** OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
array_userfuncs.o arrayutils.o bool.o \
cash.o char.o date.o datetime.o datum.o domains.o \
enum.o float.o format_type.o \
! geo_ops.o geo_selfuncs.o int.o int8.o json.o jsonfuncs.o like.o \
lockfuncs.o misc.o nabstime.o name.o numeric.o numutils.o \
oid.o oracle_compat.o pseudotypes.o rangetypes.o rangetypes_gist.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 \
--- 19,31 ----
array_userfuncs.o arrayutils.o bool.o \
cash.o char.o date.o datetime.o datum.o domains.o \
enum.o float.o format_type.o \
! geo_ops.o geo_selfuncs.o hypotheticalset.o int.o int8.o json.o jsonfuncs.o like.o \
lockfuncs.o misc.o nabstime.o name.o numeric.o numutils.o \
oid.o oracle_compat.o pseudotypes.o rangetypes.o rangetypes_gist.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 \
! 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/hypotheticalset.c
***************
*** 0 ****
--- 1,219 ----
+ /*-------------------------------------------------------------------------
+ *
+ * hypotheticalset.c
+ * Hypothetical set 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/hypotheticalset.c
+ *
+ *-------------------------------------------------------------------------
+ */
+ #include "postgres.h"
+ #include "fmgr.h"
+ #include
+ #include
+
+ #include "utils/tuplesort.h"
+ #include "catalog/pg_type.h"
+ #include "utils/datetime.h"
+ #include "utils/builtins.h"
+ #include "executor/executor.h"
+
+ Datum hypothetical_rank_final(PG_FUNCTION_ARGS);
+ Datum hypothetical_dense_rank_final(PG_FUNCTION_ARGS);
+ Datum hypothetical_percent_rank_final(PG_FUNCTION_ARGS);
+ Datum hypothetical_cume_dist_final(PG_FUNCTION_ARGS);
+
+
+ /*
+ * Common code to sanity-check args for hypothetical set functions. No need
+ * for friendly errors, these can only happen if someone's messing up the
+ * aggregate definitions. The checks are needed for security, however; but we
+ * only need them once per call site. Store a pointer to the tupdesc as a
+ * sentinel.
+ */
+
+ static void
+ hypothetical_check_argtypes(FunctionCallInfo fcinfo, int nargs, TupleDesc tupdesc)
+ {
+ int i;
+
+ if (!tupdesc
+ || (nargs + 1) != tupdesc->natts
+ || tupdesc->attrs[nargs]->atttypid != BOOLOID)
+ elog(ERROR, "type mismatch in hypothetical set function");
+
+ for (i = 0; i < nargs; ++i)
+ if (get_fn_expr_argtype(fcinfo->flinfo,i) != tupdesc->attrs[i]->atttypid)
+ elog(ERROR, "type mismatch in hypothetical set function");
+
+ fcinfo->flinfo->fn_extra = tupdesc;
+ }
+
+ /*
+ * rank(float8) - rank of hypothetical row
+ */
+ Datum
+ hypothetical_rank_final(PG_FUNCTION_ARGS)
+ {
+ Tuplesortstate *sorter = NULL;
+ TupleDesc tupdesc = NULL;
+ TupleTableSlot *slot = NULL;
+ Oid datumtype = InvalidOid;
+ int nargs = PG_NARGS();
+ int i;
+ int64 rank = 1;
+
+ AggSetGetSortInfo(fcinfo, &sorter, &tupdesc, &slot, &datumtype);
+
+ if (fcinfo->flinfo->fn_extra == NULL
+ || fcinfo->flinfo->fn_extra != tupdesc)
+ hypothetical_check_argtypes(fcinfo, nargs, tupdesc);
+
+ /* insert the hypothetical row into the sort */
+
+ ExecClearTuple(slot);
+ for (i = 0; i < nargs; ++i)
+ {
+ slot->tts_values[i] = PG_GETARG_DATUM(i);
+ slot->tts_isnull[i] = PG_ARGISNULL(i);
+ }
+ slot->tts_values[nargs] = BoolGetDatum(true);
+ slot->tts_isnull[nargs] = false;
+ ExecStoreVirtualTuple(slot);
+
+ tuplesort_puttupleslot(sorter, slot);
+
+ tuplesort_performsort(sorter);
+
+ while (tuplesort_gettupleslot(sorter, true, slot))
+ {
+ bool isnull;
+ Datum d = slot_getattr(slot, nargs + 1, &isnull);
+
+ if (!isnull && DatumGetBool(d))
+ break;
+
+ ++rank;
+ }
+
+ ExecClearTuple(slot);
+
+ PG_RETURN_INT64(rank);
+ }
+
+ /*
+ * dense_rank(float8) - rank of hypothetical row
+ * without gap in ranking
+ */
+ Datum
+ hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
+ {
+ Tuplesortstate *sorter = NULL;
+ TupleDesc tupdesc = NULL;
+ TupleTableSlot *slot = NULL;
+ Oid datumtype = InvalidOid;
+ int nargs = PG_NARGS();
+ int i;
+ int64 rank = 1;
+ int duplicate_count = 0;
+ TupleTableSlot *slot2 = NULL;
+ AttrNumber *colidx;
+ FmgrInfo *equalfns;
+ int numDistinctCol = 0;
+ MemoryContext memcontext;
+
+ AggSetGetSortInfo(fcinfo, &sorter, &tupdesc, &slot, &datumtype);
+
+ if (fcinfo->flinfo->fn_extra == NULL
+ || fcinfo->flinfo->fn_extra != tupdesc)
+ hypothetical_check_argtypes(fcinfo, nargs, tupdesc);
+
+ /* insert the hypothetical row into the sort */
+
+ ExecClearTuple(slot);
+ for (i = 0; i < nargs; ++i)
+ {
+ slot->tts_values[i] = PG_GETARG_DATUM(i);
+ slot->tts_isnull[i] = PG_ARGISNULL(i);
+ }
+ slot->tts_values[nargs] = BoolGetDatum(true);
+ slot->tts_isnull[nargs] = false;
+ ExecStoreVirtualTuple(slot);
+
+ tuplesort_puttupleslot(sorter, slot);
+
+ tuplesort_performsort(sorter);
+
+ numDistinctCol = AggSetGetDistinctInfo(fcinfo, &slot2, &colidx, &equalfns);
+
+ ExecClearTuple(slot2);
+
+ AggSetGetPerTupleContext(fcinfo, &memcontext);
+
+ while (tuplesort_gettupleslot(sorter, true, slot))
+ {
+ TupleTableSlot *tmpslot = slot2;
+ bool isnull;
+ Datum d = slot_getattr(slot, nargs + 1, &isnull);
+
+ if (!isnull && DatumGetBool(d))
+ break;
+
+ if (!TupIsNull(slot2)
+ && execTuplesMatch(slot, slot2,
+ (numDistinctCol - 1),
+ colidx,
+ equalfns,
+ memcontext))
+ ++duplicate_count;
+
+ slot2 = slot;
+ slot = tmpslot;
+
+ ++rank;
+ }
+
+ ExecClearTuple(slot);
+ ExecClearTuple(slot2);
+
+ rank = rank - duplicate_count;
+ PG_RETURN_INT64(rank);
+ }
+
+ /* percent_rank(float8)
+ * Calculates the relative ranking of hypothetical
+ * row within a group
+ */
+
+ Datum
+ hypothetical_percent_rank_final(PG_FUNCTION_ARGS)
+ {
+ Datum rank = hypothetical_rank_final(fcinfo);
+ int64 rank_val = DatumGetInt64(rank);
+ int64 rowcount = AggSetGetRowCount(fcinfo) + 1;
+
+ float8 result_val = (float8) (rank_val - 1) / (float8) (rowcount - 1);
+
+ PG_RETURN_FLOAT8(result_val);
+ }
+
+ /* cume_dist - cumulative distribution of hypothetical
+ * row in a group
+ */
+
+ Datum
+ hypothetical_cume_dist_final(PG_FUNCTION_ARGS)
+ {
+ Datum rank = hypothetical_rank_final(fcinfo);
+ int64 rank_val = DatumGetInt64(rank);
+ int64 rowcount = AggSetGetRowCount(fcinfo) + 1;
+
+ float8 result_val = (float8) (rank_val) / (float8) (rowcount);
+
+ PG_RETURN_FLOAT8(result_val);
+ }
*** /dev/null
--- b/src/backend/utils/adt/inversedistribution.c
***************
*** 0 ****
--- 1,662 ----
+ /*-------------------------------------------------------------------------
+ *
+ * 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
+ #include
+
+ #include "utils/tuplesort.h"
+ #include "catalog/pg_type.h"
+ #include "utils/datetime.h"
+ #include "utils/lsyscache.h"
+ #include "utils/array.h"
+
+ /*
+ * percentile_disc(float8) - discrete percentile
+ */
+
+ Datum percentile_disc_final(PG_FUNCTION_ARGS);
+
+ Datum
+ percentile_disc_final(PG_FUNCTION_ARGS)
+ {
+ float8 percentile;
+ Tuplesortstate *sorter;
+ Oid datumtype;
+ Datum val;
+ bool isnull;
+ int64 skiprows;
+ int64 rowcount = AggSetGetRowCount(fcinfo);
+
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+
+ percentile = PG_GETARG_FLOAT8(0);
+
+ AggSetGetSortInfo(fcinfo, &sorter, NULL, NULL, &datumtype);
+
+ if (percentile < 0 || percentile > 1 || isnan(percentile))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("percentile value %g must be 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.
+ *
+ * We don't actually expect to see nulls in the input, our strict flag
+ * should have filtered them out, but we're required to not crash if
+ * there is one.
+ */
+
+ 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 (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(val);
+ }
+
+
+ /*
+ * For percentile_cont, we need a way to interpolate between consecutive
+ * values. Use a helper function for that, so that we can share the rest
+ * of the code between types.
+ */
+
+ static Datum float8_lerp(Datum lo, Datum hi, float8 pct)
+ {
+ float8 loval = DatumGetFloat8(lo);
+ float8 hival = DatumGetFloat8(hi);
+ return Float8GetDatum(loval + (pct * (hival - loval)));
+ }
+
+ static Datum interval_lerp(Datum lo, Datum hi, float8 pct)
+ {
+ Datum diff_result = DirectFunctionCall2(interval_mi, hi, lo);
+ Datum mul_result = DirectFunctionCall2(interval_mul,
+ diff_result,
+ Float8GetDatumFast(pct));
+ return DirectFunctionCall2(interval_pl, mul_result, lo);
+ }
+
+ typedef Datum (*LerpFunc)(Datum lo, Datum hi, float8 pct);
+
+ static Datum
+ percentile_cont_final_common(FunctionCallInfo fcinfo,
+ Oid expect_type,
+ LerpFunc lerpfunc)
+ {
+ float8 percentile;
+ 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;
+
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+
+ percentile = PG_GETARG_FLOAT8(0);
+
+ AggSetGetSortInfo(fcinfo, &sorter, NULL, NULL, &datumtype);
+
+ Assert(datumtype == expect_type);
+
+ if (percentile < 0 || percentile > 1 || isnan(percentile))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("percentile value %g must be 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 (isnull)
+ PG_RETURN_NULL();
+
+ 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;
+ val = lerpfunc(first_row, second_row, proportion);
+ }
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(val);
+ }
+
+
+
+ /*
+ * percentile_cont(float8) - continuous percentile
+ */
+
+ Datum percentile_cont_float8_final(PG_FUNCTION_ARGS);
+ Datum percentile_cont_interval_final(PG_FUNCTION_ARGS);
+
+ Datum
+ percentile_cont_float8_final(PG_FUNCTION_ARGS)
+ {
+ return percentile_cont_final_common(fcinfo, FLOAT8OID, float8_lerp);
+ }
+
+ /*
+ * percentile_interval_cont(Interval) - continuous percentile for Interval
+ */
+
+ Datum
+ percentile_cont_interval_final(PG_FUNCTION_ARGS)
+ {
+ return percentile_cont_final_common(fcinfo, INTERVALOID, interval_lerp);
+ }
+
+
+ /*
+ * mode() - most common value
+ */
+
+ Datum mode_final(PG_FUNCTION_ARGS);
+
+ Datum
+ mode_final(PG_FUNCTION_ARGS)
+ {
+ Tuplesortstate *sorter;
+ Oid datumtype;
+ bool isnull;
+ Datum val;
+ Datum last_val;
+ bool last_val_is_mode = false;
+ int64 val_freq = 0;
+ Datum mode_val;
+ int64 mode_freq = 0;
+ FmgrInfo *equalfn;
+ bool shouldfree;
+
+ struct mode_type_info {
+ Oid typid;
+ int16 typLen;
+ bool typByVal;
+ } *typinfo = fcinfo->flinfo->fn_extra;
+
+ AggSetGetSortInfo(fcinfo, &sorter, NULL, NULL, &datumtype);
+ AggSetGetDistinctInfo(fcinfo, NULL, NULL, &equalfn);
+
+ if (!typinfo || typinfo->typid != datumtype)
+ {
+ if (typinfo)
+ pfree(typinfo);
+ typinfo = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(struct mode_type_info));
+ typinfo->typid = datumtype;
+ get_typlenbyval(datumtype, &typinfo->typLen, &typinfo->typByVal);
+ }
+
+ shouldfree = !(typinfo->typByVal);
+
+ tuplesort_performsort(sorter);
+
+ while (tuplesort_getdatum(sorter, true, &val, &isnull))
+ {
+ if (isnull)
+ continue;
+
+ if (val_freq == 0)
+ {
+ /* first value - assume modal until shown otherwise */
+ mode_val = last_val = val;
+ mode_freq = val_freq = 1;
+ last_val_is_mode = true;
+ }
+ else if (DatumGetBool(FunctionCall2(equalfn, val, last_val)))
+ {
+ /* value equal to previous value */
+ if (last_val_is_mode)
+ ++mode_freq;
+ else if (++val_freq > mode_freq)
+ {
+ if (shouldfree)
+ {
+ pfree(DatumGetPointer(mode_val));
+ pfree(DatumGetPointer(val));
+ }
+
+ mode_val = last_val;
+ mode_freq = val_freq;
+ last_val_is_mode = true;
+ }
+ else if (shouldfree)
+ pfree(DatumGetPointer(val));
+ }
+ else
+ {
+ if (shouldfree && !last_val_is_mode)
+ pfree(DatumGetPointer(last_val));
+
+ last_val_is_mode = false;
+ last_val = val;
+ val_freq = 1;
+ }
+ }
+
+ if (shouldfree && !last_val_is_mode)
+ pfree(DatumGetPointer(last_val));
+
+ if (mode_freq)
+ PG_RETURN_DATUM(mode_val);
+ else
+ PG_RETURN_NULL();
+ }
+
+
+
+ /*
+ * percentile_disc(float8[]) - discrete percentiles
+ */
+
+ Datum percentile_disc_multi_final(PG_FUNCTION_ARGS);
+
+ struct pct_info {
+ int64 first_row;
+ int64 second_row;
+ float8 proportion;
+ int idx;
+ };
+
+ static int pct_info_cmp(const void *pa, const void *pb)
+ {
+ const struct pct_info *a = pa;
+ const struct pct_info *b = pb;
+ if (a->first_row == b->first_row)
+ return (a->second_row < b->second_row) ? -1 : (a->second_row == b->second_row) ? 0 : 1;
+ else
+ return (a->first_row < b->first_row) ? -1 : 1;
+ }
+
+ static struct pct_info *setup_pct_info(int num_percentiles,
+ Datum *percentiles_datum,
+ bool *percentiles_null,
+ int64 rowcount,
+ bool continuous)
+ {
+ struct pct_info *pct_info = palloc(num_percentiles * sizeof(struct pct_info));
+ int i;
+
+ for (i = 0; i < num_percentiles; i++)
+ {
+ pct_info[i].idx = i;
+
+ if (percentiles_null[i])
+ {
+ pct_info[i].first_row = 0;
+ pct_info[i].second_row = 0;
+ pct_info[i].proportion = 0;
+ }
+ else
+ {
+ float8 p = DatumGetFloat8(percentiles_datum[i]);
+
+ if (p < 0 || p > 1 || isnan(p))
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("percentile value %g must be between 0 and 1", p)));
+
+ if (continuous)
+ {
+ pct_info[i].first_row = 1 + floor(p * (rowcount - 1));
+ pct_info[i].second_row = 1 + ceil(p * (rowcount - 1));
+ pct_info[i].proportion = (p * (rowcount-1)) - floor(p * (rowcount-1));
+ }
+ else
+ {
+ /*
+ * We need the smallest K such that (K/N) >= percentile. K starts at 1.
+ * Therefore K >= N*percentile
+ * Therefore K = ceil(N*percentile), minimum 1
+ */
+
+ pct_info[i].first_row = Max(1, (int64) ceil(rowcount * p));
+ pct_info[i].second_row = 0;
+ pct_info[i].proportion = 0;
+ }
+ }
+ }
+
+ qsort(pct_info, num_percentiles, sizeof(struct pct_info), pct_info_cmp);
+
+ return pct_info;
+ }
+
+ Datum
+ percentile_disc_multi_final(PG_FUNCTION_ARGS)
+ {
+ ArrayType *param;
+ Datum *percentiles_datum;
+ bool *percentiles_null;
+ int num_percentiles;
+ int64 rowcount = AggSetGetRowCount(fcinfo);
+ int64 rownum = 0;
+ Tuplesortstate *sorter;
+ Oid datumtype;
+ Datum val;
+ bool isnull;
+ Datum *result_datum;
+ bool *result_isnull;
+ int i;
+ struct pct_info *pct_info;
+
+ struct mode_type_info {
+ Oid typid;
+ int16 typLen;
+ bool typByVal;
+ char typAlign;
+ } *typinfo = fcinfo->flinfo->fn_extra;
+
+ if (PG_ARGISNULL(0) || rowcount < 1)
+ PG_RETURN_NULL();
+
+ AggSetGetSortInfo(fcinfo, &sorter, NULL, NULL, &datumtype);
+
+ if (!typinfo || typinfo->typid != datumtype)
+ {
+ if (typinfo)
+ pfree(typinfo);
+ typinfo = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(struct mode_type_info));
+ typinfo->typid = datumtype;
+ get_typlenbyvalalign(datumtype,
+ &typinfo->typLen,
+ &typinfo->typByVal,
+ &typinfo->typAlign);
+ }
+
+ param = PG_GETARG_ARRAYTYPE_P(0);
+
+ deconstruct_array(param, FLOAT8OID, 8, FLOAT8PASSBYVAL, 'd',
+ &percentiles_datum, &percentiles_null, &num_percentiles);
+
+ if (num_percentiles == 0)
+ PG_RETURN_POINTER(construct_empty_array(datumtype));
+
+ result_datum = palloc0(num_percentiles * sizeof(Datum));
+ result_isnull = palloc0(num_percentiles * sizeof(bool));
+
+ pct_info = setup_pct_info(num_percentiles,
+ percentiles_datum,
+ percentiles_null,
+ rowcount,
+ false);
+
+ /*
+ * Start by dealing with any nulls in the param array - those are
+ * sorted to the front on row=0, so set the corresponding result
+ * indexes to null
+ */
+ for (i = 0; i < num_percentiles; ++i)
+ {
+ int idx = pct_info[i].idx;
+
+ if (pct_info[i].first_row > 0)
+ break;
+
+ result_datum[idx] = (Datum) 0;
+ result_isnull[idx] = true;
+ }
+
+ /*
+ * If there's anything left after doing the nulls, then grind the
+ * input and extract the needed values
+ */
+ if (i < num_percentiles)
+ {
+ tuplesort_performsort(sorter);
+
+ for (; i < num_percentiles; ++i)
+ {
+ int64 target_row = pct_info[i].first_row;
+ int idx = pct_info[i].idx;
+
+ if (target_row > rownum)
+ {
+ while (target_row > ++rownum)
+ {
+ 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");
+ }
+
+ result_datum[idx] = val;
+ result_isnull[idx] = isnull;
+ }
+ }
+
+ /* We make the output array the same shape as the input */
+
+ PG_RETURN_POINTER(construct_md_array(result_datum, result_isnull,
+ ARR_NDIM(param),
+ ARR_DIMS(param), ARR_LBOUND(param),
+ datumtype,
+ typinfo->typLen,
+ typinfo->typByVal,
+ typinfo->typAlign));
+ }
+
+ static Datum
+ percentile_cont_multi_final_common(FunctionCallInfo fcinfo,
+ Oid expect_type,
+ int16 typLen, bool typByVal, char typAlign,
+ LerpFunc lerpfunc)
+ {
+ ArrayType *param;
+ Datum *percentiles_datum;
+ bool *percentiles_null;
+ int num_percentiles;
+ int64 rowcount = AggSetGetRowCount(fcinfo);
+ int64 rownum = 0;
+ int64 rownum_second = 0;
+ Tuplesortstate *sorter;
+ Oid datumtype;
+ Datum first_val;
+ Datum second_val;
+ bool isnull;
+ Datum *result_datum;
+ bool *result_isnull;
+ int i;
+ struct pct_info *pct_info;
+
+ if (PG_ARGISNULL(0) || rowcount < 1)
+ PG_RETURN_NULL();
+
+ AggSetGetSortInfo(fcinfo, &sorter, NULL, NULL, &datumtype);
+ Assert(datumtype == expect_type);
+
+ param = PG_GETARG_ARRAYTYPE_P(0);
+
+ deconstruct_array(param, FLOAT8OID, 8, FLOAT8PASSBYVAL, 'd',
+ &percentiles_datum, &percentiles_null, &num_percentiles);
+
+ if (num_percentiles == 0)
+ PG_RETURN_POINTER(construct_empty_array(datumtype));
+
+ result_datum = palloc0(num_percentiles * sizeof(Datum));
+ result_isnull = palloc0(num_percentiles * sizeof(bool));
+
+ pct_info = setup_pct_info(num_percentiles,
+ percentiles_datum,
+ percentiles_null,
+ rowcount,
+ true);
+
+ /*
+ * Start by dealing with any nulls in the param array - those are
+ * sorted to the front on row=0, so set the corresponding result
+ * indexes to null
+ */
+ for (i = 0; i < num_percentiles; ++i)
+ {
+ int idx = pct_info[i].idx;
+
+ if (pct_info[i].first_row > 0)
+ break;
+
+ result_datum[idx] = (Datum) 0;
+ result_isnull[idx] = true;
+ }
+
+ /*
+ * If there's anything left after doing the nulls, then grind the
+ * input and extract the needed values
+ */
+ if (i < num_percentiles)
+ {
+ tuplesort_performsort(sorter);
+
+ for (; i < num_percentiles; ++i)
+ {
+ int64 target_row = pct_info[i].first_row;
+ bool need_lerp = pct_info[i].second_row > target_row;
+ int idx = pct_info[i].idx;
+
+ if (target_row > rownum_second)
+ {
+ rownum = rownum_second;
+
+ while (target_row > ++rownum)
+ {
+ if (!tuplesort_getdatum(sorter, true, NULL, NULL))
+ elog(ERROR,"missing row in percentile_cont");
+ }
+
+ if (!tuplesort_getdatum(sorter, true, &first_val, &isnull) || isnull)
+ elog(ERROR,"missing row in percentile_cont");
+
+ rownum_second = rownum;
+
+ if (need_lerp)
+ {
+ if (!tuplesort_getdatum(sorter, true, &second_val, &isnull) || isnull)
+ elog(ERROR,"missing row in percentile_cont");
+ ++rownum_second;
+ }
+ }
+ else if (target_row == rownum_second)
+ {
+ first_val = second_val;
+ rownum = rownum_second;
+
+ if (need_lerp)
+ {
+ if (!tuplesort_getdatum(sorter, true, &second_val, &isnull) || isnull)
+ elog(ERROR,"missing row in percentile_cont");
+ ++rownum_second;
+ }
+ }
+
+ if (need_lerp)
+ {
+ result_datum[idx] = lerpfunc(first_val, second_val, pct_info[i].proportion);
+ }
+ else
+ result_datum[idx] = first_val;
+
+ result_isnull[idx] = false;
+ }
+ }
+
+ /* We make the output array the same shape as the input */
+
+ PG_RETURN_POINTER(construct_md_array(result_datum, result_isnull,
+ ARR_NDIM(param),
+ ARR_DIMS(param), ARR_LBOUND(param),
+ expect_type,
+ typLen,
+ typByVal,
+ typAlign));
+ }
+
+
+ /*
+ * percentile_cont(float8[]) within group (float8) - continuous percentiles
+ */
+
+ Datum percentile_cont_float8_multi_final(PG_FUNCTION_ARGS);
+ Datum percentile_cont_interval_multi_final(PG_FUNCTION_ARGS);
+
+ Datum
+ percentile_cont_float8_multi_final(PG_FUNCTION_ARGS)
+ {
+ return percentile_cont_multi_final_common(fcinfo,
+ FLOAT8OID, 8, FLOAT8PASSBYVAL, 'd',
+ float8_lerp);
+ }
+
+ /*
+ * percentile_cont(float8[]) within group (Interval) - continuous percentiles
+ */
+
+ Datum
+ percentile_cont_interval_multi_final(PG_FUNCTION_ARGS)
+ {
+ return percentile_cont_multi_final_common(fcinfo,
+ INTERVALOID, 16, false, 'd',
+ interval_lerp);
+ }
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
***************
*** 22,27 ****
--- 22,28 ----
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
+ #include "catalog/pg_aggregate.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
***************
*** 293,298 **** static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname,
--- 294,302 ----
static int print_function_arguments(StringInfo buf, HeapTuple proctup,
bool print_table_args, bool print_defaults);
static void print_function_rettype(StringInfo buf, HeapTuple proctup);
+ static void print_aggregate_arguments(StringInfo buf,
+ HeapTuple proctup, HeapTuple aggtup,
+ bool print_defaults);
static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
Bitmapset *rels_used);
static bool refname_is_unique(char *refname, deparse_namespace *dpns,
***************
*** 402,407 **** static char *generate_function_name(Oid funcid, int nargs,
--- 406,413 ----
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 ")
***************
*** 2267,2272 **** print_function_arguments(StringInfo buf, HeapTuple proctup,
--- 2273,2421 ----
/*
+ * pg_get_aggregate_arguments
+ * Get a nicely-formatted list of arguments for an aggregate.
+ * This is everything that would go after the function name
+ * in CREATE AGGREGATE, _including_ the parens, because in the
+ * case of ordered set funcs, we emit the WITHIN GROUP clause
+ * too.
+ */
+ Datum
+ pg_get_aggregate_arguments(PG_FUNCTION_ARGS)
+ {
+ Oid funcid = PG_GETARG_OID(0);
+ StringInfoData buf;
+ HeapTuple proctup;
+ HeapTuple aggtup;
+
+ initStringInfo(&buf);
+
+ proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+ if (!HeapTupleIsValid(proctup))
+ elog(ERROR, "cache lookup failed for function %u", funcid);
+
+ aggtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(funcid));
+ if (!HeapTupleIsValid(aggtup))
+ elog(ERROR, "function %u is not an aggregate function", funcid);
+
+ (void) print_aggregate_arguments(&buf, proctup, aggtup, true);
+
+ ReleaseSysCache(aggtup);
+ ReleaseSysCache(proctup);
+
+ PG_RETURN_TEXT_P(string_to_text(buf.data));
+ }
+
+ /*
+ * pg_get_aggregate_identity_arguments
+ * Get a formatted list of arguments for an aggregate.
+ * This is everything that would go after the function name in
+ * ALTER AGGREGATE, etc. In particular, don't print defaults.
+ * Currently, this is identical to pg_get_aggregate_arguments,
+ * but if we ever allow defaults that will change.
+ */
+ Datum
+ pg_get_aggregate_identity_arguments(PG_FUNCTION_ARGS)
+ {
+ Oid funcid = PG_GETARG_OID(0);
+ StringInfoData buf;
+ HeapTuple proctup;
+ HeapTuple aggtup;
+
+ initStringInfo(&buf);
+
+ proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+ if (!HeapTupleIsValid(proctup))
+ elog(ERROR, "cache lookup failed for function %u", funcid);
+
+ aggtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(funcid));
+ if (!HeapTupleIsValid(aggtup))
+ elog(ERROR, "function %u is not an aggregate function", funcid);
+
+ (void) print_aggregate_arguments(&buf, proctup, aggtup, false);
+
+ ReleaseSysCache(aggtup);
+ ReleaseSysCache(proctup);
+
+ PG_RETURN_TEXT_P(string_to_text(buf.data));
+ }
+
+
+ /*
+ * Common code for pg_get_aggregate_arguments
+ * We print argument defaults only if print_defaults is true.
+ */
+ static void
+ print_aggregate_arguments(StringInfo buf,
+ HeapTuple proctup, HeapTuple aggtup,
+ bool print_defaults)
+ {
+ Form_pg_aggregate agg = (Form_pg_aggregate) GETSTRUCT(aggtup);
+ int numargs;
+ bool ordsetfunc = agg->aggisordsetfunc;
+ int numdirectargs = agg->aggordnargs;
+ Oid *argtypes;
+ char **argnames;
+ char *argmodes;
+ int i;
+
+ /* defaults not supported at this time */
+ (void) print_defaults;
+
+ numargs = get_func_arg_info(proctup,
+ &argtypes, &argnames, &argmodes);
+
+ appendStringInfoChar(buf, '(');
+
+ for (i = 0; i < numargs; i++)
+ {
+ Oid argtype = argtypes[i];
+ char *argname = argnames ? argnames[i] : NULL;
+ char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
+ const char *modename;
+
+ switch (argmode)
+ {
+ case PROARGMODE_IN:
+ modename = "";
+ break;
+ case PROARGMODE_VARIADIC:
+ modename = "VARIADIC ";
+ break;
+ default:
+ elog(ERROR, "invalid parameter mode '%c'", argmode);
+ modename = NULL; /* keep compiler quiet */
+ break;
+ }
+
+ if (i == numdirectargs)
+ {
+ appendStringInfoString(buf, ") WITHIN GROUP (");
+ }
+ else if (i > 0)
+ appendStringInfoString(buf, ", ");
+
+ appendStringInfoString(buf, modename);
+
+ if (argname && argname[0])
+ appendStringInfo(buf, "%s ", quote_identifier(argname));
+
+ appendStringInfoString(buf, format_type_be(argtype));
+ }
+
+ if (ordsetfunc)
+ {
+ if (numdirectargs < 0 || numdirectargs == numargs)
+ appendStringInfoString(buf, ") WITHIN GROUP (*");
+ }
+ else if (numargs == 0)
+ appendStringInfoChar(buf, '*');
+
+ appendStringInfoChar(buf, ')');
+ }
+
+
+ /*
* deparse_expression - General utility for deparsing expressions
*
* calls deparse_expression_pretty with all prettyPrinting disabled
***************
*** 7388,7393 **** static void
--- 7537,7616 ----
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;
+
+ 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 (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;
***************
*** 7442,7455 **** 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, ')');
}
/*
--- 7665,7670 ----
*** 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/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 229,234 **** static void getTableData(TableInfo *tblinfo, int numTables, bool oids);
--- 229,235 ----
static void makeTableDataInfo(TableInfo *tbinfo, bool oids);
static void buildMatViewRefreshDependencies(Archive *fout);
static void getTableDataFKConstraints(void);
+ static char *format_aggregate_arguments(FuncInfo *finfo, char *funcargs);
static char *format_function_arguments(FuncInfo *finfo, char *funcargs,
bool is_agg);
static char *format_function_arguments_old(Archive *fout,
***************
*** 9363,9368 **** dumpProcLang(Archive *fout, ProcLangInfo *plang)
--- 9364,9385 ----
}
/*
+ * format_aggregate_arguments: generate function name and argument list
+ *
+ * This is used when we can rely on pg_get_aggregate_arguments to format
+ * the argument list.
+ */
+ static char *
+ format_aggregate_arguments(FuncInfo *finfo, char *funcargs)
+ {
+ PQExpBufferData fn;
+
+ initPQExpBuffer(&fn);
+ appendPQExpBuffer(&fn, "%s%s", fmtId(finfo->dobj.name), funcargs);
+ return fn.data;
+ }
+
+ /*
* format_function_arguments: generate function name and argument list
*
* This is used when we can rely on pg_get_function_arguments to format
***************
*** 11418,11432 **** dumpAgg(Archive *fout, AggInfo *agginfo)
--- 11435,11456 ----
int i_aggtransfn;
int i_aggfinalfn;
int i_aggsortop;
+ int i_aggtranssortop;
+ int i_hypothetical;
+ int i_isstrict;
int i_aggtranstype;
int i_agginitval;
int i_convertok;
const char *aggtransfn;
const char *aggfinalfn;
const char *aggsortop;
+ const char *aggtranssortop;
const char *aggtranstype;
const char *agginitval;
+ bool hypothetical;
+ bool isstrict;
bool convertok;
+ bool has_comma = false;
/* Skip if not to be dumped */
if (!agginfo->aggfn.dobj.dump || dataOnly)
***************
*** 11442,11452 **** dumpAgg(Archive *fout, AggInfo *agginfo)
selectSourceSchema(fout, agginfo->aggfn.dobj.namespace->dobj.name);
/* Get aggregate-specific details */
! if (fout->remoteVersion >= 80400)
{
appendPQExpBuffer(query, "SELECT aggtransfn, "
"aggfinalfn, aggtranstype::pg_catalog.regtype, "
"aggsortop::pg_catalog.regoperator, "
"agginitval, "
"'t'::boolean AS convertok, "
"pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, "
--- 11466,11496 ----
selectSourceSchema(fout, agginfo->aggfn.dobj.namespace->dobj.name);
/* Get aggregate-specific details */
! if (fout->remoteVersion >= 90400)
! {
! appendPQExpBuffer(query, "SELECT aggtransfn, "
! "aggfinalfn, aggtranstype::pg_catalog.regtype, "
! "aggsortop::pg_catalog.regoperator, "
! "aggtranssortop::pg_catalog.regoperator, "
! "(aggordnargs = -2) as hypothetical, "
! "p.proisstrict as isstrict, "
! "agginitval, "
! "'t'::boolean AS convertok, "
! "pg_catalog.pg_get_aggregate_arguments(p.oid) AS funcargs, "
! "pg_catalog.pg_get_aggregate_identity_arguments(p.oid) AS funciargs "
! "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
! "WHERE a.aggfnoid = p.oid "
! "AND p.oid = '%u'::pg_catalog.oid",
! agginfo->aggfn.dobj.catId.oid);
! }
! else if (fout->remoteVersion >= 80400)
{
appendPQExpBuffer(query, "SELECT aggtransfn, "
"aggfinalfn, aggtranstype::pg_catalog.regtype, "
"aggsortop::pg_catalog.regoperator, "
+ "0 as aggtranssortop, "
+ "false as hypothetical, "
+ "false as isstrict, "
"agginitval, "
"'t'::boolean AS convertok, "
"pg_catalog.pg_get_function_arguments(p.oid) AS funcargs, "
***************
*** 11461,11466 **** dumpAgg(Archive *fout, AggInfo *agginfo)
--- 11505,11513 ----
appendPQExpBuffer(query, "SELECT aggtransfn, "
"aggfinalfn, aggtranstype::pg_catalog.regtype, "
"aggsortop::pg_catalog.regoperator, "
+ "0 as aggtranssortop, "
+ "false as hypothetical, "
+ "false as isstrict, "
"agginitval, "
"'t'::boolean AS convertok "
"FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
***************
*** 11473,11478 **** dumpAgg(Archive *fout, AggInfo *agginfo)
--- 11520,11528 ----
appendPQExpBuffer(query, "SELECT aggtransfn, "
"aggfinalfn, aggtranstype::pg_catalog.regtype, "
"0 AS aggsortop, "
+ "0 as aggtranssortop, "
+ "'f'::boolean as hypothetical, "
+ "'f'::boolean as isstrict, "
"agginitval, "
"'t'::boolean AS convertok "
"FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
***************
*** 11485,11490 **** dumpAgg(Archive *fout, AggInfo *agginfo)
--- 11535,11543 ----
appendPQExpBuffer(query, "SELECT aggtransfn, aggfinalfn, "
"format_type(aggtranstype, NULL) AS aggtranstype, "
"0 AS aggsortop, "
+ "0 as aggtranssortop, "
+ "'f'::boolean as hypothetical, "
+ "'f'::boolean as isstrict, "
"agginitval, "
"'t'::boolean AS convertok "
"FROM pg_aggregate "
***************
*** 11497,11502 **** dumpAgg(Archive *fout, AggInfo *agginfo)
--- 11550,11558 ----
"aggfinalfn, "
"(SELECT typname FROM pg_type WHERE oid = aggtranstype1) AS aggtranstype, "
"0 AS aggsortop, "
+ "0 as aggtranssortop, "
+ "'f'::boolean as hypothetical, "
+ "'f'::boolean as isstrict, "
"agginitval1 AS agginitval, "
"(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) AS convertok "
"FROM pg_aggregate "
***************
*** 11509,11514 **** dumpAgg(Archive *fout, AggInfo *agginfo)
--- 11565,11573 ----
i_aggtransfn = PQfnumber(res, "aggtransfn");
i_aggfinalfn = PQfnumber(res, "aggfinalfn");
i_aggsortop = PQfnumber(res, "aggsortop");
+ i_aggtranssortop = PQfnumber(res, "aggtranssortop");
+ i_hypothetical = PQfnumber(res, "hypothetical");
+ i_isstrict = PQfnumber(res, "isstrict");
i_aggtranstype = PQfnumber(res, "aggtranstype");
i_agginitval = PQfnumber(res, "agginitval");
i_convertok = PQfnumber(res, "convertok");
***************
*** 11516,11526 **** dumpAgg(Archive *fout, AggInfo *agginfo)
aggtransfn = PQgetvalue(res, 0, i_aggtransfn);
aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn);
aggsortop = PQgetvalue(res, 0, i_aggsortop);
aggtranstype = PQgetvalue(res, 0, i_aggtranstype);
agginitval = PQgetvalue(res, 0, i_agginitval);
convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't');
! if (fout->remoteVersion >= 80400)
{
/* 8.4 or later; we rely on server-side code for most of the work */
char *funcargs;
--- 11575,11599 ----
aggtransfn = PQgetvalue(res, 0, i_aggtransfn);
aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn);
aggsortop = PQgetvalue(res, 0, i_aggsortop);
+ aggtranssortop = PQgetvalue(res, 0, i_aggtranssortop);
+ hypothetical = (PQgetvalue(res, 0, i_hypothetical)[0] == 't');
+ isstrict = (PQgetvalue(res, 0, i_isstrict)[0] == 't');
aggtranstype = PQgetvalue(res, 0, i_aggtranstype);
agginitval = PQgetvalue(res, 0, i_agginitval);
convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't');
! if (fout->remoteVersion >= 90400)
! {
! /* 9.4 or later; we rely on server-side code for almost all of the work */
! char *funcargs;
! char *funciargs;
!
! funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
! funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
! aggfullsig = format_aggregate_arguments(&agginfo->aggfn, funcargs);
! aggsig = format_aggregate_arguments(&agginfo->aggfn, funciargs);
! }
! else if (fout->remoteVersion >= 80400)
{
/* 8.4 or later; we rely on server-side code for most of the work */
char *funcargs;
***************
*** 11550,11585 **** dumpAgg(Archive *fout, AggInfo *agginfo)
if (fout->remoteVersion >= 70300)
{
/* If using 7.3's regproc or regtype, data is already quoted */
! appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
! aggtransfn,
! aggtranstype);
}
else if (fout->remoteVersion >= 70100)
{
/* format_type quotes, regproc does not */
! appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
fmtId(aggtransfn),
aggtranstype);
}
else
{
/* need quotes all around */
! appendPQExpBuffer(details, " SFUNC = %s,\n",
fmtId(aggtransfn));
appendPQExpBuffer(details, " STYPE = %s",
fmtId(aggtranstype));
}
! if (!PQgetisnull(res, 0, i_agginitval))
{
! appendPQExpBuffer(details, ",\n INITCOND = ");
! appendStringLiteralAH(details, agginitval, fout);
}
! if (strcmp(aggfinalfn, "-") != 0)
{
! appendPQExpBuffer(details, ",\n FINALFUNC = %s",
! aggfinalfn);
}
aggsortop = convertOperatorReference(fout, aggsortop);
--- 11623,11680 ----
if (fout->remoteVersion >= 70300)
{
/* If using 7.3's regproc or regtype, data is already quoted */
! /*
! * either or both of SFUNC and STYPE might be missing in >90400,
! * but if SFUNC is missing, then FINALFUNC will always be present,
! * and if SFUNC is present then STYPE must also be present; the
! * code below relies on these conditions to keep the commas in the
! * right places. STRICT must be forced to false if SFUNC is present.
! */
!
! if (strcmp(aggtransfn,"-") != 0)
! {
! appendPQExpBuffer(details, "\n SFUNC = %s,", aggtransfn);
! isstrict = false;
! }
!
! if (strcmp(aggtranstype,"-") != 0)
! appendPQExpBuffer(details, "\n STYPE = %s", aggtranstype);
! else
! has_comma = true;
}
else if (fout->remoteVersion >= 70100)
{
/* format_type quotes, regproc does not */
! appendPQExpBuffer(details, "\n SFUNC = %s,\n STYPE = %s",
fmtId(aggtransfn),
aggtranstype);
}
else
{
/* need quotes all around */
! appendPQExpBuffer(details, "\n SFUNC = %s,\n",
fmtId(aggtransfn));
appendPQExpBuffer(details, " STYPE = %s",
fmtId(aggtranstype));
}
! if (strcmp(aggfinalfn, "-") != 0)
{
! appendPQExpBuffer(details, "%s\n FINALFUNC = %s",
! (has_comma ? "" : ","),
! aggfinalfn);
}
! if (hypothetical)
! appendPQExpBuffer(details, ",\n HYPOTHETICAL");
!
! if (isstrict)
! appendPQExpBuffer(details, ",\n STRICT");
!
! if (!PQgetisnull(res, 0, i_agginitval))
{
! appendPQExpBuffer(details, ",\n INITCOND = ");
! appendStringLiteralAH(details, agginitval, fout);
}
aggsortop = convertOperatorReference(fout, aggsortop);
***************
*** 11589,11594 **** dumpAgg(Archive *fout, AggInfo *agginfo)
--- 11684,11696 ----
aggsortop);
}
+ aggtranssortop = convertOperatorReference(fout, aggtranssortop);
+ if (aggtranssortop)
+ {
+ appendPQExpBuffer(details, ",\n TRANSSORTOP = %s",
+ aggtranssortop);
+ }
+
/*
* DROP must be fully qualified in case same name appears in pg_catalog
*/
***************
*** 11596,11602 **** dumpAgg(Archive *fout, AggInfo *agginfo)
fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
aggsig);
! appendPQExpBuffer(q, "CREATE AGGREGATE %s (\n%s\n);\n",
aggfullsig, details->data);
appendPQExpBuffer(labelq, "AGGREGATE %s", aggsig);
--- 11698,11704 ----
fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
aggsig);
! appendPQExpBuffer(q, "CREATE AGGREGATE %s (%s\n);\n",
aggfullsig, details->data);
appendPQExpBuffer(labelq, "AGGREGATE %s", aggsig);
***************
*** 11625,11631 **** dumpAgg(Archive *fout, AggInfo *agginfo)
/*
* Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
* command look like a function's GRANT; in particular this affects the
! * syntax for zero-argument aggregates.
*/
free(aggsig);
free(aggsig_tag);
--- 11727,11733 ----
/*
* Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
* command look like a function's GRANT; in particular this affects the
! * syntax for zero-argument aggregates and ordered set functions.
*/
free(aggsig);
free(aggsig_tag);
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
***************
*** 72,78 **** describeAggregates(const char *pattern, bool verbose, bool showSystem)
gettext_noop("Name"),
gettext_noop("Result data type"));
! if (pset.sversion >= 80400)
appendPQExpBuffer(&buf,
" CASE WHEN p.pronargs = 0\n"
" THEN CAST('*' AS pg_catalog.text)\n"
--- 72,82 ----
gettext_noop("Name"),
gettext_noop("Result data type"));
! if (pset.sversion >= 90400)
! appendPQExpBuffer(&buf,
! " pg_catalog.pg_get_aggregate_arguments(p.oid) AS \"%s\",\n",
! gettext_noop("Argument data types"));
! else if (pset.sversion >= 80400)
appendPQExpBuffer(&buf,
" CASE WHEN p.pronargs = 0\n"
" THEN CAST('*' AS pg_catalog.text)\n"
***************
*** 254,260 **** describeFunctions(const char *functypes, const char *pattern, bool verbose, bool
gettext_noop("Schema"),
gettext_noop("Name"));
! if (pset.sversion >= 80400)
appendPQExpBuffer(&buf,
" pg_catalog.pg_get_function_result(p.oid) as \"%s\",\n"
" pg_catalog.pg_get_function_arguments(p.oid) as \"%s\",\n"
--- 258,283 ----
gettext_noop("Schema"),
gettext_noop("Name"));
! if (pset.sversion >= 90400)
! appendPQExpBuffer(&buf,
! " pg_catalog.pg_get_function_result(p.oid) as \"%s\",\n"
! " CASE WHEN p.proisagg THEN pg_catalog.pg_get_aggregate_arguments(p.oid)\n"
! " ELSE pg_catalog.pg_get_function_arguments(p.oid) END as \"%s\",\n"
! " CASE\n"
! " WHEN p.proisagg THEN '%s'\n"
! " WHEN p.proiswindow THEN '%s'\n"
! " WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN '%s'\n"
! " ELSE '%s'\n"
! " END as \"%s\"",
! gettext_noop("Result data type"),
! gettext_noop("Argument data types"),
! /* translator: "agg" is short for "aggregate" */
! gettext_noop("agg"),
! gettext_noop("window"),
! gettext_noop("trigger"),
! gettext_noop("normal"),
! gettext_noop("Type"));
! else if (pset.sversion >= 80400)
appendPQExpBuffer(&buf,
" pg_catalog.pg_get_function_result(p.oid) as \"%s\",\n"
" pg_catalog.pg_get_function_arguments(p.oid) as \"%s\",\n"
*** a/src/include/catalog/pg_aggregate.h
--- b/src/include/catalog/pg_aggregate.h
***************
*** 32,37 ****
--- 32,40 ----
* aggfinalfn final function (0 if none)
* aggsortop associated sort operator (0 if none)
* aggtranstype type of aggregate's transition (state) data
+ * aggtranssortop An optional sort operator for the type aggtranstype
+ * aggordnargs Number of direct arguments to aggregate.
+ * aggisordsetfunc A flag to represent whether a function is ordered set or not
* agginitval initial value for transition state (can be NULL)
* ----------------------------------------------------------------
*/
***************
*** 44,49 **** CATALOG(pg_aggregate,2600) BKI_WITHOUT_OIDS
--- 47,55 ----
regproc aggfinalfn;
Oid aggsortop;
Oid aggtranstype;
+ Oid aggtranssortop;
+ int32 aggordnargs;
+ 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
/* ----------------
--- 68,83 ----
* ----------------
*/
! #define Natts_pg_aggregate 9
#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_aggtranssortop 6
! #define Anum_pg_aggregate_aggordnargs 7
! #define Anum_pg_aggregate_aggisordsetfunc 8
! #define Anum_pg_aggregate_agginitval 9
/* ----------------
***************
*** 77,239 **** 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
--- 86,261 ----
*/
/* avg */
! DATA(insert ( 2100 int8_avg_accum numeric_avg 0 1231 0 -1 f "{0,0}" ));
! DATA(insert ( 2101 int4_avg_accum int8_avg 0 1016 0 -1 f "{0,0}" ));
! DATA(insert ( 2102 int2_avg_accum int8_avg 0 1016 0 -1 f "{0,0}" ));
! DATA(insert ( 2103 numeric_avg_accum numeric_avg 0 1231 0 -1 f "{0,0}" ));
! DATA(insert ( 2104 float4_accum float8_avg 0 1022 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2105 float8_accum float8_avg 0 1022 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2106 interval_accum interval_avg 0 1187 0 -1 f "{0 second,0 second}" ));
/* sum */
! DATA(insert ( 2107 int8_sum - 0 1700 0 -1 f _null_ ));
! DATA(insert ( 2108 int4_sum - 0 20 0 -1 f _null_ ));
! DATA(insert ( 2109 int2_sum - 0 20 0 -1 f _null_ ));
! DATA(insert ( 2110 float4pl - 0 700 0 -1 f _null_ ));
! DATA(insert ( 2111 float8pl - 0 701 0 -1 f _null_ ));
! DATA(insert ( 2112 cash_pl - 0 790 0 -1 f _null_ ));
! DATA(insert ( 2113 interval_pl - 0 1186 0 -1 f _null_ ));
! DATA(insert ( 2114 numeric_add - 0 1700 0 -1 f _null_ ));
/* max */
! DATA(insert ( 2115 int8larger - 413 20 0 -1 f _null_ ));
! DATA(insert ( 2116 int4larger - 521 23 0 -1 f _null_ ));
! DATA(insert ( 2117 int2larger - 520 21 0 -1 f _null_ ));
! DATA(insert ( 2118 oidlarger - 610 26 0 -1 f _null_ ));
! DATA(insert ( 2119 float4larger - 623 700 0 -1 f _null_ ));
! DATA(insert ( 2120 float8larger - 674 701 0 -1 f _null_ ));
! DATA(insert ( 2121 int4larger - 563 702 0 -1 f _null_ ));
! DATA(insert ( 2122 date_larger - 1097 1082 0 -1 f _null_ ));
! DATA(insert ( 2123 time_larger - 1112 1083 0 -1 f _null_ ));
! DATA(insert ( 2124 timetz_larger - 1554 1266 0 -1 f _null_ ));
! DATA(insert ( 2125 cashlarger - 903 790 0 -1 f _null_ ));
! DATA(insert ( 2126 timestamp_larger - 2064 1114 0 -1 f _null_ ));
! DATA(insert ( 2127 timestamptz_larger - 1324 1184 0 -1 f _null_ ));
! DATA(insert ( 2128 interval_larger - 1334 1186 0 -1 f _null_ ));
! DATA(insert ( 2129 text_larger - 666 25 0 -1 f _null_ ));
! DATA(insert ( 2130 numeric_larger - 1756 1700 0 -1 f _null_ ));
! DATA(insert ( 2050 array_larger - 1073 2277 0 -1 f _null_ ));
! DATA(insert ( 2244 bpchar_larger - 1060 1042 0 -1 f _null_ ));
! DATA(insert ( 2797 tidlarger - 2800 27 0 -1 f _null_ ));
! DATA(insert ( 3526 enum_larger - 3519 3500 0 -1 f _null_ ));
/* min */
! DATA(insert ( 2131 int8smaller - 412 20 0 -1 f _null_ ));
! DATA(insert ( 2132 int4smaller - 97 23 0 -1 f _null_ ));
! DATA(insert ( 2133 int2smaller - 95 21 0 -1 f _null_ ));
! DATA(insert ( 2134 oidsmaller - 609 26 0 -1 f _null_ ));
! DATA(insert ( 2135 float4smaller - 622 700 0 -1 f _null_ ));
! DATA(insert ( 2136 float8smaller - 672 701 0 -1 f _null_ ));
! DATA(insert ( 2137 int4smaller - 562 702 0 -1 f _null_ ));
! DATA(insert ( 2138 date_smaller - 1095 1082 0 -1 f _null_ ));
! DATA(insert ( 2139 time_smaller - 1110 1083 0 -1 f _null_ ));
! DATA(insert ( 2140 timetz_smaller - 1552 1266 0 -1 f _null_ ));
! DATA(insert ( 2141 cashsmaller - 902 790 0 -1 f _null_ ));
! DATA(insert ( 2142 timestamp_smaller - 2062 1114 0 -1 f _null_ ));
! DATA(insert ( 2143 timestamptz_smaller - 1322 1184 0 -1 f _null_ ));
! DATA(insert ( 2144 interval_smaller - 1332 1186 0 -1 f _null_ ));
! DATA(insert ( 2145 text_smaller - 664 25 0 -1 f _null_ ));
! DATA(insert ( 2146 numeric_smaller - 1754 1700 0 -1 f _null_ ));
! DATA(insert ( 2051 array_smaller - 1072 2277 0 -1 f _null_ ));
! DATA(insert ( 2245 bpchar_smaller - 1058 1042 0 -1 f _null_ ));
! DATA(insert ( 2798 tidsmaller - 2799 27 0 -1 f _null_ ));
! DATA(insert ( 3527 enum_smaller - 3518 3500 0 -1 f _null_ ));
/* count */
! DATA(insert ( 2147 int8inc_any - 0 20 0 -1 f "0" ));
! DATA(insert ( 2803 int8inc - 0 20 0 -1 f "0" ));
/* var_pop */
! DATA(insert ( 2718 int8_accum numeric_var_pop 0 1231 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2719 int4_accum numeric_var_pop 0 1231 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2720 int2_accum numeric_var_pop 0 1231 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2721 float4_accum float8_var_pop 0 1022 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2722 float8_accum float8_var_pop 0 1022 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2723 numeric_accum numeric_var_pop 0 1231 0 -1 f "{0,0,0}" ));
/* var_samp */
! DATA(insert ( 2641 int8_accum numeric_var_samp 0 1231 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2642 int4_accum numeric_var_samp 0 1231 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2643 int2_accum numeric_var_samp 0 1231 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2644 float4_accum float8_var_samp 0 1022 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2645 float8_accum float8_var_samp 0 1022 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2646 numeric_accum numeric_var_samp 0 1231 0 -1 f "{0,0,0}" ));
/* variance: historical Postgres syntax for var_samp */
! DATA(insert ( 2148 int8_accum numeric_var_samp 0 1231 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2149 int4_accum numeric_var_samp 0 1231 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2150 int2_accum numeric_var_samp 0 1231 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2151 float4_accum float8_var_samp 0 1022 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2152 float8_accum float8_var_samp 0 1022 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2153 numeric_accum numeric_var_samp 0 1231 0 -1 f "{0,0,0}" ));
/* stddev_pop */
! DATA(insert ( 2724 int8_accum numeric_stddev_pop 0 1231 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2725 int4_accum numeric_stddev_pop 0 1231 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2726 int2_accum numeric_stddev_pop 0 1231 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2727 float4_accum float8_stddev_pop 0 1022 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2728 float8_accum float8_stddev_pop 0 1022 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2729 numeric_accum numeric_stddev_pop 0 1231 0 -1 f "{0,0,0}" ));
/* stddev_samp */
! DATA(insert ( 2712 int8_accum numeric_stddev_samp 0 1231 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2713 int4_accum numeric_stddev_samp 0 1231 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2714 int2_accum numeric_stddev_samp 0 1231 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2715 float4_accum float8_stddev_samp 0 1022 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2716 float8_accum float8_stddev_samp 0 1022 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2717 numeric_accum numeric_stddev_samp 0 1231 0 -1 f "{0,0,0}" ));
/* stddev: historical Postgres syntax for stddev_samp */
! DATA(insert ( 2154 int8_accum numeric_stddev_samp 0 1231 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2155 int4_accum numeric_stddev_samp 0 1231 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2156 int2_accum numeric_stddev_samp 0 1231 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2157 float4_accum float8_stddev_samp 0 1022 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2158 float8_accum float8_stddev_samp 0 1022 0 -1 f "{0,0,0}" ));
! DATA(insert ( 2159 numeric_accum numeric_stddev_samp 0 1231 0 -1 f "{0,0,0}" ));
/* SQL2003 binary regression aggregates */
! DATA(insert ( 2818 int8inc_float8_float8 - 0 20 0 -1 f "0" ));
! DATA(insert ( 2819 float8_regr_accum float8_regr_sxx 0 1022 0 -1 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2820 float8_regr_accum float8_regr_syy 0 1022 0 -1 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2821 float8_regr_accum float8_regr_sxy 0 1022 0 -1 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2822 float8_regr_accum float8_regr_avgx 0 1022 0 -1 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2823 float8_regr_accum float8_regr_avgy 0 1022 0 -1 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2824 float8_regr_accum float8_regr_r2 0 1022 0 -1 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2825 float8_regr_accum float8_regr_slope 0 1022 0 -1 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2826 float8_regr_accum float8_regr_intercept 0 1022 0 -1 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2827 float8_regr_accum float8_covar_pop 0 1022 0 -1 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2828 float8_regr_accum float8_covar_samp 0 1022 0 -1 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2829 float8_regr_accum float8_corr 0 1022 0 -1 f "{0,0,0,0,0,0}" ));
/* boolean-and and boolean-or */
! DATA(insert ( 2517 booland_statefunc - 58 16 0 -1 f _null_ ));
! DATA(insert ( 2518 boolor_statefunc - 59 16 0 -1 f _null_ ));
! DATA(insert ( 2519 booland_statefunc - 58 16 0 -1 f _null_ ));
/* bitwise integer */
! DATA(insert ( 2236 int2and - 0 21 0 -1 f _null_ ));
! DATA(insert ( 2237 int2or - 0 21 0 -1 f _null_ ));
! DATA(insert ( 2238 int4and - 0 23 0 -1 f _null_ ));
! DATA(insert ( 2239 int4or - 0 23 0 -1 f _null_ ));
! DATA(insert ( 2240 int8and - 0 20 0 -1 f _null_ ));
! DATA(insert ( 2241 int8or - 0 20 0 -1 f _null_ ));
! DATA(insert ( 2242 bitand - 0 1560 0 -1 f _null_ ));
! DATA(insert ( 2243 bitor - 0 1560 0 -1 f _null_ ));
/* xml */
! DATA(insert ( 2901 xmlconcat2 - 0 142 0 -1 f _null_ ));
/* array */
! DATA(insert ( 2335 array_agg_transfn array_agg_finalfn 0 2281 0 -1 f _null_ ));
/* text */
! DATA(insert ( 3538 string_agg_transfn string_agg_finalfn 0 2281 0 -1 f _null_ ));
/* bytea */
! DATA(insert ( 3545 bytea_string_agg_transfn bytea_string_agg_finalfn 0 2281 0 -1 f _null_ ));
/* json */
! DATA(insert ( 3175 json_agg_transfn json_agg_finalfn 0 2281 0 -1 f _null_ ));
!
! /* ordered set functions */
! DATA(insert ( 3931 - percentile_disc_final 0 0 0 1 t _null_));
! DATA(insert ( 3935 - percentile_cont_float8_final 0 0 0 1 t _null_));
! DATA(insert ( 3939 - percentile_cont_interval_final 0 0 0 1 t _null_));
! DATA(insert ( 3968 - rank_final 0 16 59 -2 t "f"));
! DATA(insert ( 3970 - dense_rank_final 0 16 59 -2 t "f"));
! DATA(insert ( 3972 - percent_rank_final 0 16 59 -2 t "f"));
! DATA(insert ( 3974 - cume_dist_final 0 16 58 -2 t "f"));
! DATA(insert ( 3976 - mode_final 0 0 0 0 t _null_));
! DATA(insert ( 3978 - percentile_disc_multi_final 0 0 0 1 t _null_));
! DATA(insert ( 3980 - percentile_cont_float8_multi_final 0 0 0 1 t _null_));
! DATA(insert ( 3982 - percentile_cont_interval_multi_final 0 0 0 1 t _null_));
/*
* prototypes for functions in pg_aggregate.c
***************
*** 241,246 **** DATA(insert ( 3175 json_agg_transfn json_agg_finalfn 0 2281 _null_ ));
--- 263,269 ----
extern Oid AggregateCreate(const char *aggName,
Oid aggNamespace,
int numArgs,
+ int numDirectArgs,
oidvector *parameterTypes,
Datum allParameterTypes,
Datum parameterModes,
***************
*** 249,255 **** extern Oid AggregateCreate(const char *aggName,
List *aggtransfnName,
List *aggfinalfnName,
List *aggsortopName,
Oid aggTransType,
! const char *agginitval);
#endif /* PG_AGGREGATE_H */
--- 272,282 ----
List *aggtransfnName,
List *aggfinalfnName,
List *aggsortopName,
+ List *aggtranssortopName,
Oid aggTransType,
! const char *agginitval,
! bool isStrict,
! bool isOrderedSetFunc,
! bool isHypotheticalSet);
#endif /* PG_AGGREGATE_H */
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 1964,1969 **** DATA(insert OID = 2232 ( pg_get_function_identity_arguments PGNSP PGUID 12 1
--- 1964,1973 ----
DESCR("identity argument list of a function");
DATA(insert OID = 2165 ( pg_get_function_result PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 25 "26" _null_ _null_ _null_ _null_ pg_get_function_result _null_ _null_ _null_ ));
DESCR("result type of a function");
+ DATA(insert OID = 3178 ( pg_get_aggregate_arguments PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 25 "26" _null_ _null_ _null_ _null_ pg_get_aggregate_arguments _null_ _null_ _null_ ));
+ DESCR("argument list of an aggregate function");
+ DATA(insert OID = 3179 ( pg_get_aggregate_identity_arguments PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 25 "26" _null_ _null_ _null_ _null_ pg_get_aggregate_identity_arguments _null_ _null_ _null_ ));
+ DESCR("identity argument list of an aggregate function");
DATA(insert OID = 1686 ( pg_get_keywords PGNSP PGUID 12 10 400 0 0 f f f f t t s 0 0 2249 "" "{25,18,25}" "{o,o,o}" "{word,catcode,catdesc}" _null_ pg_get_keywords _null_ _null_ _null_ ));
DESCR("list of SQL keywords");
***************
*** 4729,4734 **** DESCR("SP-GiST support for quad tree over range");
--- 4733,4806 ----
/* 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 2283 "701 2283" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("discrete percentile");
+
+ DATA(insert OID = 3932 ( percentile_disc_final PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2283 "701 2283" _null_ _null_ _null_ _null_ percentile_disc_final _null_ _null_ _null_ ));
+ DESCR("ordered set final function");
+
+ 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("continous distribution percentile for float8");
+
+ DATA(insert OID = 3936 ( percentile_cont_float8_final PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 701 "701 701" _null_ _null_ _null_ _null_ percentile_cont_float8_final _null_ _null_ _null_ ));
+ DESCR("ordered set final function");
+
+ 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("continous distribution percentile for interval");
+
+ DATA(insert OID = 3940 ( percentile_cont_interval_final PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 1186 "701 1186" _null_ _null_ _null_ _null_ percentile_cont_interval_final _null_ _null_ _null_ ));
+ DESCR("ordered set final function");
+
+ /* hypothetical set functions */
+ DATA(insert OID = 3968 ( rank PGNSP PGUID 12 1 0 2276 0 t f f f f f i 1 0 20 2276 "{2276}" "{v}" _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("hypothetical rank");
+
+ DATA(insert OID = 3969 ( rank_final PGNSP PGUID 12 1 0 2276 0 f f f f f f i 1 0 20 2276 "{2276}" "{v}" _null_ _null_ hypothetical_rank_final _null_ _null_ _null_ ));
+ DESCR("ordered set final function");
+
+ DATA(insert OID = 3970 ( dense_rank PGNSP PGUID 12 1 0 2276 0 t f f f f f i 1 0 20 2276 "{2276}" "{v}" _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("rank of hypothetical row without gaps");
+
+ DATA(insert OID = 3971 ( dense_rank_final PGNSP PGUID 12 1 0 2276 0 f f f f f f i 1 0 20 2276 "{2276}" "{v}" _null_ _null_ hypothetical_dense_rank_final _null_ _null_ _null_ ));
+ DESCR("ordered set final function");
+
+ DATA(insert OID = 3972 ( percent_rank PGNSP PGUID 12 1 0 2276 0 t f f f f f i 1 0 701 2276 "{2276}" "{v}" _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("fractional ranking of hypothetical row within a group");
+
+ DATA(insert OID = 3973 ( percent_rank_final PGNSP PGUID 12 1 0 2276 0 f f f f f f i 1 0 701 2276 "{2276}" "{v}" _null_ _null_ hypothetical_percent_rank_final _null_ _null_ _null_ ));
+ DESCR("ordered set final function");
+
+ DATA(insert OID = 3974 ( cume_dist PGNSP PGUID 12 1 0 2276 0 t f f f f f i 1 0 701 2276 "{2276}" "{v}" _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("cumulative distribution of hypothetical row in a group");
+
+ DATA(insert OID = 3975 ( cume_dist_final PGNSP PGUID 12 1 0 2276 0 f f f f f f i 1 0 701 2276 "{2276}" "{v}" _null_ _null_ hypothetical_cume_dist_final _null_ _null_ _null_ ));
+ DESCR("ordered set final function");
+
+ DATA(insert OID = 3976 ( mode PGNSP PGUID 12 1 0 0 0 t f f f t f i 1 0 2283 2283 _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("most common value in group");
+ DATA(insert OID = 3977 ( mode_final PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 2283 2283 _null_ _null_ _null_ _null_ mode_final _null_ _null_ _null_ ));
+ DESCR("ordered set final function");
+
+ DATA(insert OID = 3978 ( percentile_disc PGNSP PGUID 12 1 0 0 0 t f f f t f i 2 0 2277 "1022 2283" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("multiple discrete percentiles");
+
+ DATA(insert OID = 3979 ( percentile_disc_multi_final PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "1022 2283" _null_ _null_ _null_ _null_ percentile_disc_multi_final _null_ _null_ _null_ ));
+ DESCR("ordered set final function");
+
+ DATA(insert OID = 3980 ( percentile_cont PGNSP PGUID 12 1 0 0 0 t f f f t f i 2 0 1022 "1022 701" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("multiple continuous percentiles of float8 values");
+
+ DATA(insert OID = 3981 ( percentile_cont_float8_multi_final PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 1022 "1022 701" _null_ _null_ _null_ _null_ percentile_cont_float8_multi_final _null_ _null_ _null_ ));
+ DESCR("ordered set final function");
+
+ DATA(insert OID = 3982 ( percentile_cont PGNSP PGUID 12 1 0 0 0 t f f f t f i 2 0 1187 "1022 1186" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("multiple continuous percentiles of interval values");
+
+ DATA(insert OID = 3983 ( percentile_cont_interval_multi_final PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 1187 "1022 1186" _null_ _null_ _null_ _null_ percentile_cont_interval_multi_final _null_ _null_ _null_ ));
+ DESCR("ordered set final function");
+
/*
* 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/fmgr.h
--- b/src/include/fmgr.h
***************
*** 651,656 **** extern void **find_rendezvous_variable(const char *varName);
--- 651,685 ----
extern int AggCheckCallContext(FunctionCallInfo fcinfo,
MemoryContext *aggcontext);
+ typedef struct Tuplesortstate fmTuplesortstate;
+ typedef struct tupleDesc *fmTupleDesc;
+ typedef struct TupleTableSlot fmTupleTableSlot;
+
+ extern int64 AggSetGetRowCount(FunctionCallInfo fcinfo);
+
+ extern void AggSetGetSortInfo(FunctionCallInfo fcinfo,
+ fmTuplesortstate **sortstate,
+ fmTupleDesc *tupdesc,
+ fmTupleTableSlot **tupslot,
+ Oid *datumtype);
+
+ /* int16 rather than AttrNumber here to avoid includes */
+ extern int AggSetGetDistinctInfo(FunctionCallInfo fcinfo,
+ fmTupleTableSlot **tupslot,
+ int16 **sortColIdx,
+ FmgrInfo **equalfns);
+
+ /* int16 rather than AttrNumber here to avoid includes */
+ extern int AggSetGetSortOperators(FunctionCallInfo fcinfo,
+ int16 **sortColIdx,
+ Oid **sortOperators,
+ Oid **sortEqOperators,
+ Oid **sortCollations,
+ bool **sortNullsFirst);
+
+ extern void AggSetGetPerTupleContext(FunctionCallInfo fcinfo,
+ MemoryContext *memcontext);
+
/*
* We allow plugin modules to hook function entry/exit. This is intended
* as support for loadable security policy modules, which may want to
*** 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,255 **** typedef struct Aggref
--- 247,258 ----
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 aggvariadic; /* TRUE if VARIADIC was used in call */
+ bool isordset; /* If node is from an ordered set function */
+ bool ishypothetical; /* If node is from a hypothetical 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
***************
*** 412,417 **** PG_KEYWORD("where", WHERE, RESERVED_KEYWORD)
--- 412,418 ----
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);
***************
*** 34,37 **** extern void build_aggregate_fnexprs(Oid *agg_input_types,
--- 34,51 ----
Expr **transfnexpr,
Expr **finalfnexpr);
+ void
+ build_orderedset_fnexprs(Oid *agg_input_types,
+ int agg_num_inputs,
+ bool agg_variadic,
+ Oid agg_result_type,
+ Oid agg_input_collation,
+ Oid *agg_input_collation_array,
+ Oid finalfn_oid,
+ Expr **finalfnexpr);
+
+ int get_aggregate_argtypes(Aggref *aggref,
+ Oid *inputTypes,
+ Oid *inputCollations);
+
#endif /* PARSE_AGG_H */
*** a/src/include/parser/parse_clause.h
--- b/src/include/parser/parse_clause.h
***************
*** 31,37 **** extern List *transformGroupClause(ParseState *pstate, List *grouplist,
ParseExprKind exprKind, bool useSQL99);
extern List *transformSortClause(ParseState *pstate, List *orderlist,
List **targetlist, ParseExprKind exprKind,
! bool resolveUnknown, bool useSQL99);
extern List *transformWindowDefinitions(ParseState *pstate,
List *windowdefs,
--- 31,37 ----
ParseExprKind exprKind, bool useSQL99);
extern List *transformSortClause(ParseState *pstate, List *orderlist,
List **targetlist, ParseExprKind exprKind,
! bool resolveUnknown, bool useSQL99, bool keepDuplicates);
extern List *transformWindowDefinitions(ParseState *pstate,
List *windowdefs,
*** 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,48 ----
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,
! int location, FuncCall *fn);
extern FuncDetailCode func_get_detail(List *funcname,
List *fargs, List *fargnames,
***************
*** 66,73 **** extern FuncCandidateList func_select_candidate(int nargs,
extern void make_fn_arguments(ParseState *pstate,
List *fargs,
Oid *actual_arg_types,
! Oid *declared_arg_types);
extern const char *funcname_signature_string(const char *funcname, int nargs,
List *argnames, const Oid *argtypes);
--- 63,72 ----
extern void make_fn_arguments(ParseState *pstate,
List *fargs,
+ List *agg_order,
Oid *actual_arg_types,
! Oid *declared_arg_types,
! bool requiresUnification);
extern const char *funcname_signature_string(const char *funcname, int nargs,
List *argnames, const Oid *argtypes);
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 658,663 **** extern Datum pg_get_functiondef(PG_FUNCTION_ARGS);
--- 658,665 ----
extern Datum pg_get_function_arguments(PG_FUNCTION_ARGS);
extern Datum pg_get_function_identity_arguments(PG_FUNCTION_ARGS);
extern Datum pg_get_function_result(PG_FUNCTION_ARGS);
+ extern Datum pg_get_aggregate_arguments(PG_FUNCTION_ARGS);
+ extern Datum pg_get_aggregate_identity_arguments(PG_FUNCTION_ARGS);
extern char *deparse_expression(Node *expr, List *dpcontext,
bool forceprefix, bool showimplicit);
extern List *deparse_context_for(const char *aliasname, Oid relid);
*** a/src/test/regress/expected/aggregates.out
--- b/src/test/regress/expected/aggregates.out
***************
*** 1249,1254 **** select aggfns(distinct a,b,c order by a,c using ~<~,b) filter (where a > 1)
--- 1249,1461 ----
{"(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: WITHIN GROUP is required for call to ordered set function percentile_cont
+ 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)
+
+ select rank(3) within group (order by x) from (values (1),(1),(2),(2),(3),(3),(4)) v(x);
+ rank
+ ------
+ 5
+ (1 row)
+
+ select cume_dist(3) within group (order by x) from (values (1),(1),(2),(2),(3),(3),(4)) v(x);
+ cume_dist
+ -----------
+ 0.875
+ (1 row)
+
+ select percent_rank(3) within group (order by x) from (values (1),(1),(2),(2),(3),(3),(4),(5)) v(x);
+ percent_rank
+ --------------
+ 0.5
+ (1 row)
+
+ select dense_rank(3) within group (order by x) from (values (1),(1),(2),(2),(3),(3),(4)) v(x);
+ dense_rank
+ ------------
+ 3
+ (1 row)
+
+ select percentile_disc(array[0,0.1,0.25,0.5,0.75,0.9,1]) within group (order by thousand) from tenk1;
+ percentile_disc
+ ----------------------------
+ {0,99,249,499,749,899,999}
+ (1 row)
+
+ select percentile_cont(array[0,0.25,0.5,0.75,1]) within group (order by thousand) from tenk1;
+ percentile_cont
+ -----------------------------
+ {0,249.75,499.5,749.25,999}
+ (1 row)
+
+ select percentile_disc(array[[null,1,0.5],[0.75,0.25,null]]) within group (order by thousand) from tenk1;
+ percentile_disc
+ ---------------------------------
+ {{NULL,999,499},{749,249,NULL}}
+ (1 row)
+
+ select percentile_cont(array[0,1,0.25,0.75,0.5,1]) within group (order by x)
+ from generate_series(1,6) x;
+ percentile_cont
+ -----------------------
+ {1,6,2.25,4.75,3.5,5}
+ (1 row)
+
+ select ten, mode() within group (order by string4) from tenk1 group by ten;
+ ten | mode
+ -----+--------
+ 0 | HHHHxx
+ 1 | OOOOxx
+ 2 | VVVVxx
+ 3 | OOOOxx
+ 4 | HHHHxx
+ 5 | HHHHxx
+ 6 | OOOOxx
+ 7 | AAAAxx
+ 8 | VVVVxx
+ 9 | VVVVxx
+ (10 rows)
+
+ select percentile_disc(array[0.25,0.5,0.75]) within group (order by x)
+ from unnest('{fred,jim,fred,jack,jill,fred,jill,jim,jim,sheila,jim,sheila}'::text[]) u(x);
+ percentile_disc
+ -----------------
+ {fred,jill,jim}
+ (1 row)
+
+ -- ordered set funcs can't use ungrouped direct args:
+ select rank(x) within group (order by x) from generate_series(1,5) x;
+ ERROR: column "x.x" must appear in the GROUP BY clause or be used in an aggregate function
+ LINE 1: select rank(x) within group (order by x) from generate_serie...
+ ^
+ -- collation:
+ select pg_collation_for(percentile_disc(1) within group (order by x collate "POSIX"))
+ from (values ('fred'),('jim')) v(x);
+ pg_collation_for
+ ------------------
+ "POSIX"
+ (1 row)
+
+ -- hypothetical type unification and argument failures:
+ select rank(3) within group (order by x) from (values ('fred'),('jim')) v(x);
+ ERROR: WITHIN GROUP types text and integer cannot be matched
+ LINE 1: select rank(3) within group (order by x) from (values ('fred...
+ ^
+ select rank(3) within group (order by stringu1,stringu2) from tenk1;
+ ERROR: Incorrect number of arguments for hypothetical set function
+ LINE 1: select rank(3) within group (order by stringu1,stringu2) fro...
+ ^
+ select rank('fred') within group (order by x) from generate_series(1,5) x;
+ ERROR: invalid input syntax for integer: "fred"
+ LINE 1: select rank('fred') within group (order by x) from generate_...
+ ^
+ select rank('adam'::text collate "C") within group (order by x collate "POSIX")
+ from (values ('fred'),('jim')) v(x);
+ ERROR: collation mismatch between explicit collations "C" and "POSIX"
+ LINE 1: ...adam'::text collate "C") within group (order by x collate "P...
+ ^
+ -- hypothetical type unification successes:
+ select rank('adam'::varchar) within group (order by x) from (values ('fred'),('jim')) v(x);
+ rank
+ ------
+ 1
+ (1 row)
+
+ select rank('3') within group (order by x) from generate_series(1,5) x;
+ rank
+ ------
+ 3
+ (1 row)
+
+ -- deparse and multiple features:
+ create view aggordview1 as
+ select ten,
+ percentile_disc(0.5) within group (order by thousand) as p50,
+ percentile_disc(0.5) within group (order by thousand) filter (where hundred=1) as px,
+ rank(5,'AZZZZ',50) within group (order by hundred, string4 desc, hundred)
+ from tenk1
+ group by ten order by ten;
+ select pg_get_viewdef('aggordview1');
+ pg_get_viewdef
+ --------------------------------------------------------------------------------------------------------------------------------
+ SELECT tenk1.ten, +
+ percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) AS p50, +
+ percentile_disc((0.5)::double precision) WITHIN GROUP (ORDER BY tenk1.thousand) FILTER (WHERE (tenk1.hundred = 1)) AS px, +
+ rank(5, 'AZZZZ'::name, 50) WITHIN GROUP (ORDER BY tenk1.hundred, tenk1.string4 DESC, tenk1.hundred) AS rank +
+ FROM tenk1 +
+ GROUP BY tenk1.ten +
+ ORDER BY tenk1.ten;
+ (1 row)
+
+ select * from aggordview1 order by ten;
+ ten | p50 | px | rank
+ -----+-----+-----+------
+ 0 | 490 | | 101
+ 1 | 491 | 401 | 101
+ 2 | 492 | | 101
+ 3 | 493 | | 101
+ 4 | 494 | | 101
+ 5 | 495 | | 67
+ 6 | 496 | | 1
+ 7 | 497 | | 1
+ 8 | 498 | | 1
+ 9 | 499 | | 1
+ (10 rows)
+
+ drop view aggordview1;
-- variadic aggregates
select least_agg(q1,q2) from int8_tbl;
least_agg
*** 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)
***************
*** 857,866 **** ORDER BY 1;
count("any") | count()
(1 row)
! -- For the same reason, we avoid creating built-in variadic aggregates.
! SELECT oid, proname
! FROM pg_proc AS p
! WHERE proisagg AND provariadic != 0;
oid | proname
-----+---------
(0 rows)
--- 866,877 ----
count("any") | count()
(1 row)
! -- For the same reason, we avoid creating built-in variadic aggregates, except
! -- ordered set functions (which have their own syntax and are not subject to
! -- the misplaced ORDER BY issue).
! SELECT p.oid, proname
! FROM pg_proc AS p JOIN pg_aggregate AS a ON (a.aggfnoid=p.oid)
! WHERE proisagg AND provariadic != 0 AND NOT a.aggisordsetfunc;
oid | proname
-----+---------
(0 rows)
*** a/src/test/regress/sql/aggregates.sql
--- b/src/test/regress/sql/aggregates.sql
***************
*** 481,486 **** select aggfns(distinct a,b,c order by a,c using ~<~,b) filter (where a > 1)
--- 481,549 ----
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;
+ select rank(3) within group (order by x) from (values (1),(1),(2),(2),(3),(3),(4)) v(x);
+ select cume_dist(3) within group (order by x) from (values (1),(1),(2),(2),(3),(3),(4)) v(x);
+ select percent_rank(3) within group (order by x) from (values (1),(1),(2),(2),(3),(3),(4),(5)) v(x);
+ select dense_rank(3) within group (order by x) from (values (1),(1),(2),(2),(3),(3),(4)) v(x);
+
+ select percentile_disc(array[0,0.1,0.25,0.5,0.75,0.9,1]) within group (order by thousand) from tenk1;
+ select percentile_cont(array[0,0.25,0.5,0.75,1]) within group (order by thousand) from tenk1;
+ select percentile_disc(array[[null,1,0.5],[0.75,0.25,null]]) within group (order by thousand) from tenk1;
+ select percentile_cont(array[0,1,0.25,0.75,0.5,1]) within group (order by x)
+ from generate_series(1,6) x;
+
+ select ten, mode() within group (order by string4) from tenk1 group by ten;
+
+ select percentile_disc(array[0.25,0.5,0.75]) within group (order by x)
+ from unnest('{fred,jim,fred,jack,jill,fred,jill,jim,jim,sheila,jim,sheila}'::text[]) u(x);
+
+ -- ordered set funcs can't use ungrouped direct args:
+ select rank(x) within group (order by x) from generate_series(1,5) x;
+
+ -- collation:
+ select pg_collation_for(percentile_disc(1) within group (order by x collate "POSIX"))
+ from (values ('fred'),('jim')) v(x);
+
+ -- hypothetical type unification and argument failures:
+ select rank(3) within group (order by x) from (values ('fred'),('jim')) v(x);
+ select rank(3) within group (order by stringu1,stringu2) from tenk1;
+ select rank('fred') within group (order by x) from generate_series(1,5) x;
+ select rank('adam'::text collate "C") within group (order by x collate "POSIX")
+ from (values ('fred'),('jim')) v(x);
+ -- hypothetical type unification successes:
+ select rank('adam'::varchar) within group (order by x) from (values ('fred'),('jim')) v(x);
+ select rank('3') within group (order by x) from generate_series(1,5) x;
+
+ -- deparse and multiple features:
+ create view aggordview1 as
+ select ten,
+ percentile_disc(0.5) within group (order by thousand) as p50,
+ percentile_disc(0.5) within group (order by thousand) filter (where hundred=1) as px,
+ rank(5,'AZZZZ',50) within group (order by hundred, string4 desc, hundred)
+ from tenk1
+ group by ten order by ten;
+
+ select pg_get_viewdef('aggordview1');
+ select * from aggordview1 order by ten;
+ drop view aggordview1;
+
-- variadic aggregates
select least_agg(q1,q2) from int8_tbl;
select least_agg(variadic array[q1,q2]) from int8_tbl;
*** 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
***************
*** 685,695 **** WHERE p1.oid < p2.oid AND p1.proname = p2.proname AND
array_dims(p1.proargtypes) != array_dims(p2.proargtypes)
ORDER BY 1;
! -- For the same reason, we avoid creating built-in variadic aggregates.
! SELECT oid, proname
! FROM pg_proc AS p
! WHERE proisagg AND provariadic != 0;
-- For the same reason, built-in aggregates with default arguments are no good.
--- 694,706 ----
array_dims(p1.proargtypes) != array_dims(p2.proargtypes)
ORDER BY 1;
! -- For the same reason, we avoid creating built-in variadic aggregates, except
! -- ordered set functions (which have their own syntax and are not subject to
! -- the misplaced ORDER BY issue).
! SELECT p.oid, proname
! FROM pg_proc AS p JOIN pg_aggregate AS a ON (a.aggfnoid=p.oid)
! WHERE proisagg AND provariadic != 0 AND NOT a.aggisordsetfunc;
-- For the same reason, built-in aggregates with default arguments are no good.