Proposal/design feedback needed: WITHIN GROUP (sql standard ordered set aggregate functions)
The spec defines two types of aggregate function classed as "ordered set
function", as follows:
1. An "inverse distribution function" taking one argument (which must be
a grouped column or otherwise constant within groups) plus a sorted
group with exactly one column:
=# SELECT (func(p) WITHIN GROUP (ORDER BY q)) from ...
The motivating example for this (and the only ones in the spec) are
percentile_cont and percentile_disc, to return a percentile result
from a continuous or discrete distribution. (Thus
percentile_cont(0.5) within group (order by x) is the spec's version
of a median(x) function.)
2. A "hypothetical set function" taking N arguments of arbitrary types
(a la VARIADIC "any", rather than a fixed list) plus a sorted group
with N columns of matching types:
=# SELECT (func(p1,p2,...) WITHIN GROUP (ORDER BY q1,q2,...)) from ...
(where typeof(p1)==typeof(q1) and so on, at least up to trivial
conversions)
The motivating example here is to be able to do rank(p1,p2,...) to
return the rank that the specified values would have had if they were
added to the group.
As usual, we do not want to constrain ourselves to supporting only the
specific cases in the spec, but would prefer a general solution.
We (meaning myself and Atri) have an implementation that basically
works, though it is not yet complete, but before taking it any further
we need to resolve the design question of how to represent these two
types of function in the system catalogs. The fact that there are in
effect two parts to the parameter list, which are either independent
(for inverse distribution funcs) or closely related (for hypothetical
set functions), doesn't seem to point to an obvious way to represent
this in pg_proc/pg_aggregate.
I'm not yet satisfied with the method used in our implementation, so
we're throwing this open for suggestions. We will post the
work-in-progress patch along with a description of its current
implementation shortly.
One of the major complications is that we ideally want to be able to do
polymorphism based on the type of the sorted group, specifically in
order to be able to do
percentile_disc(float8) within group (order by anyelement)
returning anyelement. (i.e. we should be able to get a discrete
percentile from any type that is orderable.) The question here is how to
resolve the return type both of the aggregate itself and of the finalfn.
We've also had an expression of interest in extending this to allow
percentile_disc(float8[]) and percentile_cont(float8[]) returning
arrays; e.g. percentile_cont(array[0, 0.25, 0.5, 0.75, 1]) to return an
array containing the bounds, median and quartiles in one go. This is an
extension to the spec but it seems sufficiently obviously useful to be
worth supporting.
Comments?
--
Andrew (irc:RhodiumToad)
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Jul 18, 2013 at 03:15:14AM +0000, Andrew Gierth wrote:
The spec defines two types of aggregate function classed as "ordered set
function", as follows:1. An "inverse distribution function" taking one argument (which must be
a grouped column or otherwise constant within groups) plus a sorted
group with exactly one column:=# SELECT (func(p) WITHIN GROUP (ORDER BY q)) from ...
The motivating example for this (and the only ones in the spec) are
percentile_cont and percentile_disc, to return a percentile result
from a continuous or discrete distribution. (Thus
percentile_cont(0.5) within group (order by x) is the spec's version
of a median(x) function.)2. A "hypothetical set function" taking N arguments of arbitrary types
(a la VARIADIC "any", rather than a fixed list) plus a sorted group
with N columns of matching types:=# SELECT (func(p1,p2,...) WITHIN GROUP (ORDER BY q1,q2,...)) from ...
(where typeof(p1)==typeof(q1) and so on, at least up to trivial
conversions)The motivating example here is to be able to do rank(p1,p2,...) to
return the rank that the specified values would have had if they were
added to the group.As usual, we do not want to constrain ourselves to supporting only the
specific cases in the spec, but would prefer a general solution.We (meaning myself and Atri) have an implementation that basically
works, though it is not yet complete, but before taking it any further
we need to resolve the design question of how to represent these two
types of function in the system catalogs. The fact that there are in
effect two parts to the parameter list, which are either independent
(for inverse distribution funcs) or closely related (for hypothetical
set functions), doesn't seem to point to an obvious way to represent
this in pg_proc/pg_aggregate.I'm not yet satisfied with the method used in our implementation,
What is that method?
so we're throwing this open for suggestions. We will post the
work-in-progress patch along with a description of its current
implementation shortly.One of the major complications is that we ideally want to be able to
do polymorphism based on the type of the sorted group, specifically
in order to be able to dopercentile_disc(float8) within group (order by anyelement)
returning anyelement. (i.e. we should be able to get a discrete
percentile from any type that is orderable.) The question here is
how to resolve the return type both of the aggregate itself and of
the finalfn.We've also had an expression of interest in extending this to allow
percentile_disc(float8[]) and percentile_cont(float8[]) returning
arrays; e.g. percentile_cont(array[0, 0.25, 0.5, 0.75, 1]) to return
an array containing the bounds, median and quartiles in one go. This
is an extension to the spec but it seems sufficiently obviously
useful to be worth supporting.Comments?
I'm really happy to see PostgreSQL come into its own when it comes to
the analytics side of the house :)
Cheers,
David.
--
David Fetter <david@fetter.org> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: david.fetter@gmail.com
iCal: webcal://www.tripit.com/feed/ical/people/david74/tripit.ics
Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Jul 18, 2013 at 10:02 AM, David Fetter <david@fetter.org> wrote:
On Thu, Jul 18, 2013 at 03:15:14AM +0000, Andrew Gierth wrote:
The spec defines two types of aggregate function classed as "ordered set
function", as follows:1. An "inverse distribution function" taking one argument (which must be
a grouped column or otherwise constant within groups) plus a sorted
group with exactly one column:=# SELECT (func(p) WITHIN GROUP (ORDER BY q)) from ...
The motivating example for this (and the only ones in the spec) are
percentile_cont and percentile_disc, to return a percentile result
from a continuous or discrete distribution. (Thus
percentile_cont(0.5) within group (order by x) is the spec's version
of a median(x) function.)2. A "hypothetical set function" taking N arguments of arbitrary types
(a la VARIADIC "any", rather than a fixed list) plus a sorted group
with N columns of matching types:=# SELECT (func(p1,p2,...) WITHIN GROUP (ORDER BY q1,q2,...)) from ...
(where typeof(p1)==typeof(q1) and so on, at least up to trivial
conversions)The motivating example here is to be able to do rank(p1,p2,...) to
return the rank that the specified values would have had if they were
added to the group.As usual, we do not want to constrain ourselves to supporting only the
specific cases in the spec, but would prefer a general solution.We (meaning myself and Atri) have an implementation that basically
works, though it is not yet complete, but before taking it any further
we need to resolve the design question of how to represent these two
types of function in the system catalogs. The fact that there are in
effect two parts to the parameter list, which are either independent
(for inverse distribution funcs) or closely related (for hypothetical
set functions), doesn't seem to point to an obvious way to represent
this in pg_proc/pg_aggregate.I'm not yet satisfied with the method used in our implementation,
What is that method?
We currently represent ordered set functions with a new bool flag in
pg_aggregate. The flag is set to true for ordered set
functions(obviously) and false for all others. The currently
implemented functions i.e. percentile_disc, percentile_cont and
percentile_cont for intervals have their finalfns present in
pg_aggregate.
The aggregate functions take in two arguments, one for the percentile
value and other for the input row set. So, percentile_cont's entry in
pg_proc has float8 and float8 as its parameters and another entry of
percentile_cont (with the interval version as the finalfn) has float8
and interval as its parameter types.
As you can see, there isn't a way right now to resolve the return type
of the aggregate for polymorphic cases. This is something we wish to
resolve.
Regards,
Atri
--
Regards,
Atri
l'apprenant
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 07/17/2013 08:15 PM, Andrew Gierth wrote:
The spec defines two types of aggregate function classed as "ordered set
function", as follows:1. An "inverse distribution function" taking one argument (which must be
a grouped column or otherwise constant within groups) plus a sorted
group with exactly one column:=# SELECT (func(p) WITHIN GROUP (ORDER BY q)) from ...
The motivating example for this (and the only ones in the spec) are
percentile_cont and percentile_disc, to return a percentile result
from a continuous or discrete distribution. (Thus
percentile_cont(0.5) within group (order by x) is the spec's version
of a median(x) function.)
One question is how this relates to the existing
SELECT agg_func(x order by y)
... syntax. Clearly there's some extra functionality here, but the two
are very similar conceptually.
2. A "hypothetical set function" taking N arguments of arbitrary types
(a la VARIADIC "any", rather than a fixed list) plus a sorted group
with N columns of matching types:=# SELECT (func(p1,p2,...) WITHIN GROUP (ORDER BY q1,q2,...)) from ...
(where typeof(p1)==typeof(q1) and so on, at least up to trivial
conversions)The motivating example here is to be able to do rank(p1,p2,...) to
return the rank that the specified values would have had if they were
added to the group.
Wow, I can't possibly grasp the purpose of this. Maybe a practical example?
We've also had an expression of interest in extending this to allow
percentile_disc(float8[]) and percentile_cont(float8[]) returning
arrays; e.g. percentile_cont(array[0, 0.25, 0.5, 0.75, 1]) to return an
array containing the bounds, median and quartiles in one go. This is an
extension to the spec but it seems sufficiently obviously useful to be
worth supporting.
To be specific, I asked for this because it's already something I do
using PL/R, although in PL/R it's pretty much limited to floats.
Anyway, for anyone who isn't following why we want this: statitical
summary reports. For example, I'd love to be able to do a quartile
distribution of query execution times without resorting to R.
--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Import Notes
Reply to msg id not found: WMc8a7b9b3a1849d20c463dd5b78c2251eda3c4f7bae1d4e9774002e49479d2e22ae471ca243f062215387e04ab5ff7d84@asav-2.01.com
Josh Berkus wrote:
On 07/17/2013 08:15 PM, Andrew Gierth wrote:
The spec defines two types of aggregate function classed as "ordered set
function", as follows:1. An "inverse distribution function" taking one argument (which must be
a grouped column or otherwise constant within groups) plus a sorted
group with exactly one column:=# SELECT (func(p) WITHIN GROUP (ORDER BY q)) from ...
The motivating example for this (and the only ones in the spec) are
percentile_cont and percentile_disc, to return a percentile result
from a continuous or discrete distribution. (Thus
percentile_cont(0.5) within group (order by x) is the spec's version
of a median(x) function.)One question is how this relates to the existing
SELECT agg_func(x order by y)
... syntax. Clearly there's some extra functionality here, but the two
are very similar conceptually.
Well, as you probably know, the spec is a whole pile of random
special-case syntax and any similarities are probably more accidental
than anything else.
A major difference is that in agg(x order by y), the values of y are
not passed to the aggregate function - they serve no purpose other
than controlling the order of the "x" values. Whereas in WITHIN GROUP,
the values in the ORDER BY ... clause are in some sense the primary
input to the aggregate, and the "p" argument is secondary and can't
vary between rows of the group.
Our implementation does heavily reuse the existing executor mechanics
for ORDER BY in aggregates, and it also reuses a fair chunk of the
parser code for it, but there are significant differences.
[of hypothetical set functions]
Wow, I can't possibly grasp the purpose of this. Maybe a practical
example?
=# select rank(123) within group (order by x)
from (values (10),(50),(100),(200),(500)) v(x);
would return 1 row containing the value 4, because if you added the
value 123 to the grouped values, it would have been ranked 4th.
Any time you want to calculate what the rank, dense_rank or cume_dist
would be of a specific row within a group without actually adding the
row to the group, this is how it's done.
I don't have any practical examples to hand, but this beast seems to
be implemented in at least Oracle and MSSQL so I guess it has uses.
[on supporting arrays of percentiles]
To be specific, I asked for this because it's already something I do
using PL/R, although in PL/R it's pretty much limited to floats.
percentile_cont is limited to floats and intervals in the spec; to be
precise, it's limited to taking args of either interval or any numeric
type, and returns interval for interval args, and "approximate numeric
with implementation-defined precision", i.e. some form of float, for
numeric-type args. The definition requires interpolation between values,
so it's not clear that there's any point in trying to allow other types.
percentile_disc is also limited to floats and intervals in the spec, but
I see absolutely no reason whatsoever for this, since the definition
given is valid for any type with ordering operators; there is no reason
not to make it fully polymorphic. (The requirement for ordering will be
enforced in parse-analysis anyway, by the ORDER BY transformations, and
the function simply returns one of the input values unaltered.)
--
Andrew (irc:RhodiumToad)
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Andrew,
Well, as you probably know, the spec is a whole pile of random
special-case syntax and any similarities are probably more accidental
than anything else.
Hah, I didn't realize that our ordered aggregate syntax even *was* spec.
A major difference is that in agg(x order by y), the values of y are
not passed to the aggregate function - they serve no purpose other
than controlling the order of the "x" values. Whereas in WITHIN GROUP,
the values in the ORDER BY ... clause are in some sense the primary
input to the aggregate, and the "p" argument is secondary and can't
vary between rows of the group.Our implementation does heavily reuse the existing executor mechanics
for ORDER BY in aggregates, and it also reuses a fair chunk of the
parser code for it, but there are significant differences.
Well, seems like it would work the same as
agg_func(constx,coly,colz ORDER BY coly, colz)
... which means you could reuse a LOT of the internal plumbing. Or am I
missing something?
Also, what would a CREATE AGGREGATE and state function definition for
custom WITHIN GROUP aggregates look like?
Any time you want to calculate what the rank, dense_rank or cume_dist
would be of a specific row within a group without actually adding the
row to the group, this is how it's done.I don't have any practical examples to hand, but this beast seems to
be implemented in at least Oracle and MSSQL so I guess it has uses.
Well, I still can't imagine a practical use for it, at least based on
RANK. I certainly have no objections if you have the code, though.
I'll also point out that mode() requires ordered input as well, so add
that to the set of functions we'll want to eventually support.
One thing I find myself wanting with ordered aggregates is the ability
to exclude NULLs. Thoughts?
--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Import Notes
Reply to msg id not found: WM7d7bef525069df0820d9da6c86675806c4e0a08b778b8b013a5cf60b0db9682722331f9faf9b4d926818488a4c44a408@asav-3.01.com
Josh Berkus wrote:
Hah, I didn't realize that our ordered aggregate syntax even *was* spec.
The spec defines agg(x order by y) only for array_agg and xmlagg; the
generalization to arbitrary other aggregates is our extension. (But
kind of obvious really.)
Our implementation does heavily reuse the existing executor mechanics
for ORDER BY in aggregates, and it also reuses a fair chunk of the
parser code for it, but there are significant differences.Well, seems like it would work the same as
agg_func(constx,coly,colz ORDER BY coly, colz)
... which means you could reuse a LOT of the internal plumbing. Or am I
missing something?
You missed the part above which said "...does heavily reuse..." :-)
i.e. we are in fact reusing a lot of the internal plumbing.
Also, what would a CREATE AGGREGATE and state function definition for
custom WITHIN GROUP aggregates look like?
Now this is exactly the part we haven't nailed down yet and want ideas
for.
The problem is, given that the parser is looking at:
foo(p1,p2,...) within group (order by q1,q2,...)
how do we best represent the possible matching functions in pg_proc
and pg_aggregate? Our partial solution so far does not allow
polymorphism to work properly, so we need a better way; I'm hoping for
some independent suggestions before I post my own ideas.
--
Andrew (irc:RhodiumToad)
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
The problem is, given that the parser is looking at:
foo(p1,p2,...) within group (order by q1,q2,...)
how do we best represent the possible matching functions in pg_proc
and pg_aggregate? Our partial solution so far does not allow
polymorphism to work properly, so we need a better way; I'm hoping for
some independent suggestions before I post my own ideas.
Yeah, you'd need to extend VARIADIC somehow. That is, I should be able
to define a function as:
percentile_state (
pctl float,
ordercols VARIADIC ANY )
returns VARIADIC ANY
... so that it can handle the sorting. Another way to look at it would be:
percentile_state (
pctl float,
orderedset ANONYMOUS ROW )
returns ANONYMOUS ROW as ...
... because really, what you're handing the state function is an
anonymous row type constructed of the order by phrase. Of course, then
we have to have some way to manipulate the anonymous row from within the
function; at the very least, an equality operator.
--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Import Notes
Reply to msg id not found: WM092e7ea8e373fa7a34dad9e877c6a24db3f7471aefebd505323a9162c01949052c050430edcca44099daf6657695a154@asav-3.01.com
On Thu, Jul 18, 2013 at 10:33:15PM +0000, Andrew Gierth wrote:
Josh Berkus wrote:
Well, seems like it would work the same as
agg_func(constx,coly,colz ORDER BY coly, colz)
I'd try transforming WITHIN GROUP into the above during parse analysis. The
default would be the transformation for hypothetical set functions:
agg(x,y,z) WITHIN GROUP (ORDER BY a,b,c) -> agg(x,y,z ORDER BY a,b,c)
Add a CREATE AGGREGATE option, say SQL_INVERSE_DISTRIBUTION_FUNCTION =
{true|false} or SQLIDF, that chooses the IDF transformation:
agg(x) WITHIN GROUP (ORDER BY y) -> agg(x, y ORDER BY y)
Then there's perhaps no new core aggregation or function candidacy machinery.
(I don't know whether VARIADIC transition functions work today, but that would
become an orthogonal project.) Compare how we handle standard interval typmod
syntax; only the parser and deparser know about it.
Atri's description upthread sounded pretty similar to that.
Also, what would a CREATE AGGREGATE and state function definition for
custom WITHIN GROUP aggregates look like?Now this is exactly the part we haven't nailed down yet and want ideas
for.
PERCENTILE_DISC would be declared as (float8, anyelement) with that SQLIDF
option. To diagnose nonsensical calls made through nonstandard syntax, it
could dig into its AggState to verify that its second argument is equal() to
its first ORDER BY expression.
There would be a question of whether to accept the WITHIN GROUP syntax for any
aggregate or just for those for which the standard indicates it. Then follows
the question of when to deparse as WITHIN GROUP and when to deparse as the
internal syntax. I'd lean toward accepting WITHIN GROUP for any aggregate but
deparsing that way only SQLIDF aggregates and aggregates with names matching
the standard hypothetical set function names. Or you could add a second
CREATE AGGREGATE option requesting hypothetical-set-function deparse style.
--
Noah Misch
EnterpriseDB http://www.enterprisedb.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Noah Misch <noah@leadboat.com> writes:
(I don't know whether VARIADIC transition functions work today, but that would
become an orthogonal project.)
Coincidentally enough, some Salesforce folk were asking me about allowing
VARIADIC aggregates just a few days ago. I experimented enough to find
out that if you make an array-accepting transition function, and then
force the aggregate's pg_proc entry to look like it's variadic (by
manually setting provariadic and some other fields), then everything
seems to Just Work: the parser and executor are both fine with it.
So I think all that's needed here is to add some syntax support to
CREATE AGGREGATE, and probably make some tweaks in pg_dump. I was
planning to go work on that sometime soon.
Having said that, though, what Andrew seemed to want was VARIADIC ANY,
which is a *completely* different kettle of fish, since the actual
parameters can't be converted to an array. I'm not sure if that's
as easy to support.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi all,
This is our current work-in-progress patch for WITHIN GROUP.
What mostly works:
- percentile_cont(float8) within group (order by float8)
- percentile_cont(float8) within group (order by interval)
- percentile_disc(float8) within group (order by float8)
What doesn't work:
- supporting other types in percentile_disc (want polymorphism to
work first)
- no commands yet to add new ordered set functions (want to nail
down the catalog representation first)
- no hypothetical set functions yet (need to resolve the above two
points first)
- some rough edges
- probably some bugs
- docs
Implementation details:
For execution, we repurpose the existing aggregate-orderby mechanics.
Given func(directargs) WITHIN GROUP (ORDER BY args), we process the
(ORDER BY args) into a tuplesort in the same way currently done for
agg(args ORDER BY args). Rather than using a transfn, we then call the
finalfn as finalfn(directargs), providing an API by which the finalfn
can access the tuplesort. (This is somewhat inspired by the window
function API, but unfortunately has nothing in common with it in terms
of requirements, so we couldn't just reuse it.)
func(p1,p2,...) WITHIN GROUP (ORDER BY q1,q2,...) is represented in the
catalog with two pg_proc rows:
func(p1,p2,...,q1,q2,...) (proisagg=true)
func_final(p1,p2,...)
with the usual pg_aggregate row linking them, though aggtransfn is set
to InvalidOid (as is aggtranstype) and an additional flag indicates
that this is an ordered set function.
(This representation is inadequate for a number of reasons; it does not
handle polymorphism well and would require special-case coding for
hypothetical set functions, which we have not yet tackled. See our other
post.)
Regards,
Atri
--
Regards,
Atri
l'apprenant
Attachments:
patch19713context.patchapplication/octet-stream; name=patch19713context.patchDownload
*** a/src/backend/executor/execQual.c
--- b/src/backend/executor/execQual.c
***************
*** 4410,4415 **** ExecInitExpr(Expr *node, PlanState *parent)
--- 4410,4416 ----
astate->args = (List *) ExecInitExpr((Expr *) aggref->args,
parent);
+ astate->orddirectargs = (List *) ExecInitExpr((Expr *) aggref->orddirectargs, parent);
astate->aggfilter = ExecInitExpr(aggref->aggfilter,
parent);
*** a/src/backend/executor/functions.c
--- b/src/backend/executor/functions.c
***************
*** 381,387 **** sql_fn_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var)
list_make1(subfield),
list_make1(param),
NIL, NULL, false, false, false,
! NULL, true, cref->location);
}
return param;
--- 381,387 ----
list_make1(subfield),
list_make1(param),
NIL, NULL, false, false, false,
! false, NULL, true, cref->location);
}
return param;
*** a/src/backend/executor/nodeAgg.c
--- b/src/backend/executor/nodeAgg.c
***************
*** 105,110 ****
--- 105,112 ----
*/
typedef struct AggStatePerAggData
{
+ NodeTag type;
+
/*
* These values are set up during ExecInitAgg() and do not change
* thereafter:
***************
*** 114,119 **** typedef struct AggStatePerAggData
--- 116,124 ----
AggrefExprState *aggrefstate;
Aggref *aggref;
+ /* Pointer to parent AggState node */
+ AggState *aggstate;
+
/* number of input arguments for aggregate function proper */
int numArguments;
***************
*** 126,137 **** typedef struct AggStatePerAggData
/*
* fmgr lookup data for transfer functions --- only valid when
! * corresponding oid is not InvalidOid. Note in particular that fn_strict
! * flags are kept here.
*/
FmgrInfo transfn;
FmgrInfo finalfn;
/* Input collation derived for aggregate */
Oid aggCollation;
--- 131,152 ----
/*
* fmgr lookup data for transfer functions --- only valid when
! * corresponding oid is not InvalidOid.
*/
FmgrInfo transfn;
FmgrInfo finalfn;
+ /*
+ * If >0, aggregate as a whole is strict (skips null input)
+ * The value specifies how many columns to check; normal aggs
+ * only check numArguments, while ordered set functions check
+ * numInputs.
+ *
+ * Ordered set functions have a separate strictness state for
+ * the finalfn, which is tested separately.
+ */
+ int numStrict;
+
/* Input collation derived for aggregate */
Oid aggCollation;
***************
*** 204,209 **** typedef struct AggStatePerAggData
--- 219,227 ----
*/
Tuplesortstate *sortstate; /* sort object, if DISTINCT or ORDER BY */
+
+ int64 number_of_rows; /* number of rows */
+
} AggStatePerAggData;
/*
***************
*** 300,305 **** initialize_aggregates(AggState *aggstate,
--- 318,325 ----
AggStatePerAgg peraggstate = &peragg[aggno];
AggStatePerGroup pergroupstate = &pergroup[aggno];
+ peraggstate->number_of_rows = 0;
+
/*
* Start a fresh sort operation for each DISTINCT/ORDER BY aggregate.
*/
***************
*** 383,396 **** advance_transition_function(AggState *aggstate,
MemoryContext oldContext;
Datum newVal;
int i;
! if (peraggstate->transfn.fn_strict)
{
/*
* For a strict transfn, nothing happens when there's a NULL input; we
* just keep the prior transValue.
*/
! for (i = 1; i <= numArguments; i++)
{
if (fcinfo->argnull[i])
return;
--- 403,419 ----
MemoryContext oldContext;
Datum newVal;
int i;
+ int numStrict = peraggstate->numStrict;
! Assert(OidIsValid(peraggstate->transfn_oid));
!
! if (numStrict > 0)
{
/*
* For a strict transfn, nothing happens when there's a NULL input; we
* just keep the prior transValue.
*/
! for (i = 1; i <= numStrict; i++)
{
if (fcinfo->argnull[i])
return;
***************
*** 506,529 **** advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup)
if (peraggstate->numSortCols > 0)
{
/* DISTINCT and/or ORDER BY case */
Assert(slot->tts_nvalid == peraggstate->numInputs);
/*
* If the transfn is strict, we want to check for nullity before
* storing the row in the sorter, to save space if there are a lot
! * of nulls. Note that we must only check numArguments columns,
! * not numInputs, since nullity in columns used only for sorting
! * is not relevant here.
*/
! if (peraggstate->transfn.fn_strict)
{
! for (i = 0; i < nargs; i++)
{
if (slot->tts_isnull[i])
break;
}
! if (i < nargs)
continue;
}
--- 529,552 ----
if (peraggstate->numSortCols > 0)
{
+ int numStrict = peraggstate->numStrict;
+
/* DISTINCT and/or ORDER BY case */
Assert(slot->tts_nvalid == peraggstate->numInputs);
/*
* If the transfn is strict, we want to check for nullity before
* storing the row in the sorter, to save space if there are a lot
! * of nulls.
*/
! if (numStrict > 0)
{
! for (i = 0; i < numStrict; i++)
{
if (slot->tts_isnull[i])
break;
}
! if (i < numStrict)
continue;
}
***************
*** 534,539 **** advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup)
--- 557,564 ----
slot->tts_isnull[0]);
else
tuplesort_puttupleslot(peraggstate->sortstate, slot);
+
+ ++peraggstate->number_of_rows;
}
else
{
***************
*** 756,768 **** finalize_aggregate(AggState *aggstate,
if (OidIsValid(peraggstate->finalfn_oid))
{
FunctionCallInfoData fcinfo;
! InitFunctionCallInfoData(fcinfo, &(peraggstate->finalfn), 1,
! peraggstate->aggCollation,
! (void *) aggstate, NULL);
! fcinfo.arg[0] = pergroupstate->transValue;
! fcinfo.argnull[0] = pergroupstate->transValueIsNull;
! if (fcinfo.flinfo->fn_strict && pergroupstate->transValueIsNull)
{
/* don't call a strict function with NULL inputs */
*resultVal = (Datum) 0;
--- 781,820 ----
if (OidIsValid(peraggstate->finalfn_oid))
{
FunctionCallInfoData fcinfo;
+ bool isnull = false;
+
+ if (!(peraggstate->aggref->isordset))
+ {
+ InitFunctionCallInfoData(fcinfo, &(peraggstate->finalfn), 1,
+ peraggstate->aggCollation,
+ (void *) aggstate, NULL);
+
+ fcinfo.arg[0] = pergroupstate->transValue;
+ fcinfo.argnull[0] = isnull = pergroupstate->transValueIsNull;
+ }
+ else
+ {
+ List *args = peraggstate->aggrefstate->orddirectargs;
+ ListCell *lc;
+ int i = 0;
+
+ InitFunctionCallInfoData(fcinfo, &(peraggstate->finalfn), list_length(args),
+ peraggstate->aggCollation,
+ (void *) peraggstate, NULL);
+
+ foreach (lc, args)
+ {
+ ExprState *expr = (ExprState *) lfirst(lc);
+
+ fcinfo.arg[i] = ExecEvalExpr(expr, aggstate->ss.ps.ps_ExprContext, &fcinfo.argnull[i], NULL);
+ if (fcinfo.argnull[i])
+ isnull = true;
! ++i;
! }
! }
!
! if (isnull && fcinfo.flinfo->fn_strict)
{
/* don't call a strict function with NULL inputs */
*resultVal = (Datum) 0;
***************
*** 1164,1169 **** agg_retrieve_direct(AggState *aggstate)
--- 1216,1232 ----
}
/*
+ * Use the representative input tuple for any references to
+ * non-aggregated input columns in the qual and tlist. (If we are not
+ * grouping, and there are no input rows at all, we will come here
+ * with an empty firstSlot ... but if not grouping, there can't be any
+ * references to non-aggregated input columns, so no problem.)
+ * We do this before finalizing because for ordered set functions,
+ * finalize_aggregates can evaluate arguments referencing the tuple.
+ */
+ econtext->ecxt_outertuple = firstSlot;
+
+ /*
* Done scanning input tuple group. Finalize each aggregate
* calculation, and stash results in the per-output-tuple context.
*/
***************
*** 1174,1187 **** agg_retrieve_direct(AggState *aggstate)
if (peraggstate->numSortCols > 0)
{
! if (peraggstate->numInputs == 1)
! process_ordered_aggregate_single(aggstate,
! peraggstate,
! pergroupstate);
! else
! process_ordered_aggregate_multi(aggstate,
! peraggstate,
! pergroupstate);
}
finalize_aggregate(aggstate, peraggstate, pergroupstate,
--- 1237,1253 ----
if (peraggstate->numSortCols > 0)
{
! if (!(peraggstate->aggref->isordset))
! {
! if (peraggstate->numInputs == 1)
! process_ordered_aggregate_single(aggstate,
! peraggstate,
! pergroupstate);
! else
! process_ordered_aggregate_multi(aggstate,
! peraggstate,
! pergroupstate);
! }
}
finalize_aggregate(aggstate, peraggstate, pergroupstate,
***************
*** 1189,1203 **** agg_retrieve_direct(AggState *aggstate)
}
/*
- * Use the representative input tuple for any references to
- * non-aggregated input columns in the qual and tlist. (If we are not
- * grouping, and there are no input rows at all, we will come here
- * with an empty firstSlot ... but if not grouping, there can't be any
- * references to non-aggregated input columns, so no problem.)
- */
- econtext->ecxt_outertuple = firstSlot;
-
- /*
* Check the qual (HAVING clause); if the group does not match, ignore
* it and loop back to try to process another group.
*/
--- 1255,1260 ----
***************
*** 1580,1585 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
--- 1637,1643 ----
Datum textInitVal;
int i;
ListCell *lc;
+ bool is_strict;
/* Planner should have assigned aggregate to correct level */
Assert(aggref->agglevelsup == 0);
***************
*** 1601,1606 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
--- 1659,1667 ----
/* Nope, so assign a new PerAgg record */
peraggstate = &peragg[++aggno];
+ peraggstate->type = T_AggStatePerAggData;
+ peraggstate->aggstate = aggstate;
+
/* Mark Aggref state node with assigned index in the result array */
aggrefstate->aggno = aggno;
***************
*** 1644,1668 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
! /* Check that aggregate owner has permission to call component fns */
{
HeapTuple procTuple;
Oid aggOwner;
procTuple = SearchSysCache1(PROCOID,
ObjectIdGetDatum(aggref->aggfnoid));
if (!HeapTupleIsValid(procTuple))
elog(ERROR, "cache lookup failed for function %u",
aggref->aggfnoid);
! aggOwner = ((Form_pg_proc) GETSTRUCT(procTuple))->proowner;
ReleaseSysCache(procTuple);
! aclresult = pg_proc_aclcheck(transfn_oid, aggOwner,
! ACL_EXECUTE);
! if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_PROC,
get_func_name(transfn_oid));
! InvokeFunctionExecuteHook(transfn_oid);
if (OidIsValid(finalfn_oid))
{
aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner,
--- 1705,1741 ----
peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
! /*
! * Check that aggregate owner has permission to call component fns
! * In passing, fetch the proisstrict flag for the aggregate proper,
! * which subs for the transfn's strictness flag in cases where there
! * is no transfn.
! */
{
HeapTuple procTuple;
Oid aggOwner;
+ Form_pg_proc procp;
procTuple = SearchSysCache1(PROCOID,
ObjectIdGetDatum(aggref->aggfnoid));
if (!HeapTupleIsValid(procTuple))
elog(ERROR, "cache lookup failed for function %u",
aggref->aggfnoid);
! procp = (Form_pg_proc) GETSTRUCT(procTuple);
! aggOwner = procp->proowner;
! is_strict = procp->proisstrict;
ReleaseSysCache(procTuple);
! if (OidIsValid(transfn_oid))
! {
! aclresult = pg_proc_aclcheck(transfn_oid, aggOwner,
! ACL_EXECUTE);
! if (aclresult != ACLCHECK_OK && OidIsValid(transfn_oid))
aclcheck_error(aclresult, ACL_KIND_PROC,
get_func_name(transfn_oid));
! InvokeFunctionExecuteHook(transfn_oid);
! }
!
if (OidIsValid(finalfn_oid))
{
aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner,
***************
*** 1676,1682 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
/* resolve actual type of transition state, if polymorphic */
aggtranstype = aggform->aggtranstype;
! if (IsPolymorphicType(aggtranstype))
{
/* have to fetch the agg's declared input types... */
Oid *declaredArgTypes;
--- 1749,1755 ----
/* resolve actual type of transition state, if polymorphic */
aggtranstype = aggform->aggtranstype;
! if (OidIsValid(aggtranstype) && IsPolymorphicType(aggtranstype))
{
/* have to fetch the agg's declared input types... */
Oid *declaredArgTypes;
***************
*** 1704,1711 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
&transfnexpr,
&finalfnexpr);
! fmgr_info(transfn_oid, &peraggstate->transfn);
! fmgr_info_set_expr((Node *) transfnexpr, &peraggstate->transfn);
if (OidIsValid(finalfn_oid))
{
--- 1777,1789 ----
&transfnexpr,
&finalfnexpr);
! if (OidIsValid(transfn_oid))
! {
! fmgr_info(transfn_oid, &peraggstate->transfn);
! fmgr_info_set_expr((Node *) transfnexpr, &peraggstate->transfn);
!
! is_strict = peraggstate->transfn.fn_strict;
! }
if (OidIsValid(finalfn_oid))
{
***************
*** 1713,1726 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
fmgr_info_set_expr((Node *) finalfnexpr, &peraggstate->finalfn);
}
peraggstate->aggCollation = aggref->inputcollid;
get_typlenbyval(aggref->aggtype,
&peraggstate->resulttypeLen,
&peraggstate->resulttypeByVal);
! get_typlenbyval(aggtranstype,
! &peraggstate->transtypeLen,
! &peraggstate->transtypeByVal);
/*
* initval is potentially null, so don't try to access it as a struct
--- 1791,1814 ----
fmgr_info_set_expr((Node *) finalfnexpr, &peraggstate->finalfn);
}
+ if (is_strict)
+ {
+ peraggstate->numStrict = (peraggstate->aggref->isordset ? numInputs : numArguments);
+ }
+ else
+ peraggstate->numStrict = 0;
+
peraggstate->aggCollation = aggref->inputcollid;
get_typlenbyval(aggref->aggtype,
&peraggstate->resulttypeLen,
&peraggstate->resulttypeByVal);
! if (OidIsValid(aggtranstype))
! {
! get_typlenbyval(aggtranstype,
! &peraggstate->transtypeLen,
! &peraggstate->transtypeByVal);
! }
/*
* initval is potentially null, so don't try to access it as a struct
***************
*** 1743,1749 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
* transValue. This should have been checked at agg definition time,
* but just in case...
*/
! if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull)
{
if (numArguments < 1 ||
!IsBinaryCoercible(inputTypes[0], aggtranstype))
--- 1831,1839 ----
* transValue. This should have been checked at agg definition time,
* but just in case...
*/
! if (OidIsValid(peraggstate->transfn_oid)
! && peraggstate->transfn.fn_strict
! && peraggstate->initValueIsNull)
{
if (numArguments < 1 ||
!IsBinaryCoercible(inputTypes[0], aggtranstype))
***************
*** 1804,1810 **** ExecInitAgg(Agg *node, EState *estate, int eflags)
/* If we have only one input, we need its len/byval info. */
if (numInputs == 1)
{
! get_typlenbyval(inputTypes[0],
&peraggstate->inputtypeLen,
&peraggstate->inputtypeByVal);
}
--- 1894,1900 ----
/* If we have only one input, we need its len/byval info. */
if (numInputs == 1)
{
! get_typlenbyval(peraggstate->evaldesc->attrs[0]->atttypid,
&peraggstate->inputtypeLen,
&peraggstate->inputtypeByVal);
}
***************
*** 2039,2044 **** AggCheckCallContext(FunctionCallInfo fcinfo, MemoryContext *aggcontext)
--- 2129,2141 ----
return AGG_CONTEXT_WINDOW;
}
+ if (fcinfo->context && IsA(fcinfo->context, AggStatePerAggData))
+ {
+ if (aggcontext)
+ *aggcontext = ((AggStatePerAggData *) fcinfo->context)->aggstate->aggcontext;
+ return AGG_CONTEXT_ORDERED;
+ }
+
/* this is just to prevent "uninitialized variable" warnings */
if (aggcontext)
*aggcontext = NULL;
***************
*** 2062,2064 **** aggregate_dummy(PG_FUNCTION_ARGS)
--- 2159,2209 ----
fcinfo->flinfo->fn_oid);
return (Datum) 0; /* keep compiler quiet */
}
+
+ /* AggSetGetRowCount - Get the number of rows in case of ordered set
+ * functions.
+ */
+ int64
+ AggSetGetRowCount(FunctionCallInfo fcinfo)
+ {
+ if (fcinfo->context && IsA(fcinfo->context, AggStatePerAggData))
+ {
+ return ((AggStatePerAggData *)fcinfo->context)->number_of_rows;
+ }
+
+ elog(ERROR, "Called AggSetGetRowCount on non ordered set function");
+ return -1;
+ }
+
+ /* AggSetGetSortInfo - Get the sort state in the case of
+ * ordered set functions.
+ */
+ void
+ AggSetGetSortInfo(FunctionCallInfo fcinfo, Tuplesortstate **sortstate, TupleDesc *tupdesc, TupleTableSlot **tupslot, Oid *datumtype)
+ {
+ if (fcinfo->context && IsA(fcinfo->context, AggStatePerAggData))
+ {
+ AggStatePerAggData *peraggstate = (AggStatePerAggData *) fcinfo->context;
+
+ *sortstate = peraggstate->sortstate;
+ if (peraggstate->numInputs == 1)
+ {
+ if (tupdesc)
+ *tupdesc = NULL;
+ *datumtype = peraggstate->evaldesc->attrs[0]->atttypid;
+ }
+ else
+ {
+ if (tupdesc)
+ *tupdesc = peraggstate->evaldesc;
+ *datumtype = InvalidOid;
+ }
+
+ if (tupslot)
+ *tupslot = peraggstate->evalslot;
+ }
+ else
+ {
+ elog(ERROR, "AggSetSortInfo called on non ordered set function");
+ }
+ }
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 1138,1145 **** _copyAggref(const Aggref *from)
--- 1138,1147 ----
COPY_NODE_FIELD(args);
COPY_NODE_FIELD(aggorder);
COPY_NODE_FIELD(aggdistinct);
+ COPY_NODE_FIELD(orddirectargs);
COPY_NODE_FIELD(aggfilter);
COPY_SCALAR_FIELD(aggstar);
+ COPY_SCALAR_FIELD(isordset);
COPY_SCALAR_FIELD(agglevelsup);
COPY_LOCATION_FIELD(location);
***************
*** 2171,2176 **** _copyFuncCall(const FuncCall *from)
--- 2173,2179 ----
COPY_SCALAR_FIELD(agg_star);
COPY_SCALAR_FIELD(agg_distinct);
COPY_SCALAR_FIELD(func_variadic);
+ COPY_SCALAR_FIELD(has_within_group);
COPY_NODE_FIELD(over);
COPY_LOCATION_FIELD(location);
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 196,203 **** _equalAggref(const Aggref *a, const Aggref *b)
--- 196,205 ----
COMPARE_NODE_FIELD(args);
COMPARE_NODE_FIELD(aggorder);
COMPARE_NODE_FIELD(aggdistinct);
+ COMPARE_NODE_FIELD(orddirectargs);
COMPARE_NODE_FIELD(aggfilter);
COMPARE_SCALAR_FIELD(aggstar);
+ COMPARE_SCALAR_FIELD(isordset);
COMPARE_SCALAR_FIELD(agglevelsup);
COMPARE_LOCATION_FIELD(location);
***************
*** 2001,2006 **** _equalFuncCall(const FuncCall *a, const FuncCall *b)
--- 2003,2009 ----
COMPARE_SCALAR_FIELD(agg_star);
COMPARE_SCALAR_FIELD(agg_distinct);
COMPARE_SCALAR_FIELD(func_variadic);
+ COMPARE_SCALAR_FIELD(has_within_group);
COMPARE_NODE_FIELD(over);
COMPARE_LOCATION_FIELD(location);
*** a/src/backend/nodes/makefuncs.c
--- b/src/backend/nodes/makefuncs.c
***************
*** 530,535 **** makeFuncCall(List *name, List *args, int location)
--- 530,536 ----
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->func_variadic = FALSE;
+ n->has_within_group = FALSE;
n->over = NULL;
return n;
}
*** a/src/backend/nodes/nodeFuncs.c
--- b/src/backend/nodes/nodeFuncs.c
***************
*** 1572,1577 **** expression_tree_walker(Node *node,
--- 1572,1582 ----
if (expression_tree_walker((Node *) expr->aggdistinct,
walker, context))
return true;
+
+ if (expression_tree_walker((Node *) expr->orddirectargs,
+ walker, context))
+ return true;
+
if (walker((Node *) expr->aggfilter, context))
return true;
}
***************
*** 2096,2102 **** expression_tree_mutator(Node *node,
--- 2101,2109 ----
MUTATE(newnode->args, aggref->args, List *);
MUTATE(newnode->aggorder, aggref->aggorder, List *);
MUTATE(newnode->aggdistinct, aggref->aggdistinct, List *);
+ MUTATE(newnode->orddirectargs, aggref->orddirectargs, List *);
MUTATE(newnode->aggfilter, aggref->aggfilter, Expr *);
+
return (Node *) newnode;
}
break;
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
***************
*** 959,966 **** _outAggref(StringInfo str, const Aggref *node)
--- 959,968 ----
WRITE_NODE_FIELD(args);
WRITE_NODE_FIELD(aggorder);
WRITE_NODE_FIELD(aggdistinct);
+ WRITE_NODE_FIELD(orddirectargs);
WRITE_NODE_FIELD(aggfilter);
WRITE_BOOL_FIELD(aggstar);
+ WRITE_BOOL_FIELD(isordset);
WRITE_UINT_FIELD(agglevelsup);
WRITE_LOCATION_FIELD(location);
}
***************
*** 2087,2092 **** _outFuncCall(StringInfo str, const FuncCall *node)
--- 2089,2095 ----
WRITE_BOOL_FIELD(agg_star);
WRITE_BOOL_FIELD(agg_distinct);
WRITE_BOOL_FIELD(func_variadic);
+ WRITE_BOOL_FIELD(has_within_group);
WRITE_NODE_FIELD(over);
WRITE_LOCATION_FIELD(location);
}
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
***************
*** 495,502 **** _readAggref(void)
--- 495,504 ----
READ_NODE_FIELD(args);
READ_NODE_FIELD(aggorder);
READ_NODE_FIELD(aggdistinct);
+ READ_NODE_FIELD(orddirectargs);
READ_NODE_FIELD(aggfilter);
READ_BOOL_FIELD(aggstar);
+ READ_BOOL_FIELD(isordset);
READ_UINT_FIELD(agglevelsup);
READ_LOCATION_FIELD(location);
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
***************
*** 486,492 **** count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
costs->numOrderedAggs++;
/* add component function execution costs to appropriate totals */
! costs->transCost.per_tuple += get_func_cost(aggtransfn) * cpu_operator_cost;
if (OidIsValid(aggfinalfn))
costs->finalCost += get_func_cost(aggfinalfn) * cpu_operator_cost;
--- 486,493 ----
costs->numOrderedAggs++;
/* add component function execution costs to appropriate totals */
! if (OidIsValid(aggtransfn))
! costs->transCost.per_tuple += get_func_cost(aggtransfn) * cpu_operator_cost;
if (OidIsValid(aggfinalfn))
costs->finalCost += get_func_cost(aggfinalfn) * cpu_operator_cost;
***************
*** 516,522 **** count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
}
/* resolve actual type of transition state, if polymorphic */
! if (IsPolymorphicType(aggtranstype))
{
/* have to fetch the agg's declared input types... */
Oid *declaredArgTypes;
--- 517,523 ----
}
/* resolve actual type of transition state, if polymorphic */
! if (OidIsValid(aggtranstype) && IsPolymorphicType(aggtranstype))
{
/* have to fetch the agg's declared input types... */
Oid *declaredArgTypes;
***************
*** 539,545 **** count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
* pass-by-reference then we have to add the estimated size of the
* value itself, plus palloc overhead.
*/
! if (!get_typbyval(aggtranstype))
{
int32 aggtranstypmod;
int32 avgwidth;
--- 540,546 ----
* pass-by-reference then we have to add the estimated size of the
* value itself, plus palloc overhead.
*/
! if (OidIsValid(aggtranstype) && !get_typbyval(aggtranstype))
{
int32 aggtranstypmod;
int32 avgwidth;
***************
*** 3882,3888 **** recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple)
elog(ERROR, "function's resolved result type changed during planning");
/* perform any necessary typecasting of arguments */
! make_fn_arguments(NULL, args, actual_arg_types, declared_arg_types);
}
/*
--- 3883,3889 ----
elog(ERROR, "function's resolved result type changed during planning");
/* perform any necessary typecasting of arguments */
! make_fn_arguments(NULL, args, NULL, actual_arg_types, declared_arg_types);
}
/*
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 493,498 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
--- 493,499 ----
%type <str> opt_existing_window_name
%type <boolean> opt_if_not_exists
%type <node> filter_clause
+ %type <list> within_group_clause
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
***************
*** 595,601 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
VERBOSE VERSION_P VIEW VOLATILE
! WHEN WHERE WHITESPACE_P WINDOW WITH WITHOUT WORK WRAPPER WRITE
XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLEXISTS XMLFOREST XMLPARSE
XMLPI XMLROOT XMLSERIALIZE
--- 596,602 ----
VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
VERBOSE VERSION_P VIEW VOLATILE
! WHEN WHERE WITHIN WHITESPACE_P WINDOW WITH WITHOUT WORK WRAPPER WRITE
XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLEXISTS XMLFOREST XMLPARSE
XMLPI XMLROOT XMLSERIALIZE
***************
*** 9413,9418 **** sortby: a_expr USING qual_all_Op opt_nulls_order
--- 9414,9424 ----
;
+ within_group_clause:
+ WITHIN GROUP_P '(' sort_clause ')' { $$ = $4; }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
select_limit:
limit_clause offset_clause { $$ = list_make2($2, $1); }
| offset_clause limit_clause { $$ = list_make2($1, $2); }
***************
*** 11112,11122 **** func_application: func_name '(' ')'
* (Note that many of the special SQL functions wouldn't actually make any
* sense as functional index entries, but we ignore that consideration here.)
*/
! func_expr: func_application filter_clause over_clause
{
FuncCall *n = (FuncCall*)$1;
! n->agg_filter = $2;
! n->over = $3;
$$ = (Node*)n;
}
| func_expr_common_subexpr
--- 11118,11151 ----
* (Note that many of the special SQL functions wouldn't actually make any
* sense as functional index entries, but we ignore that consideration here.)
*/
! func_expr: func_application within_group_clause filter_clause over_clause
{
FuncCall *n = (FuncCall*)$1;
! /*
! * the order clause for WITHIN GROUP and the one
! * for aggregate ORDER BY share a field, so we
! * have to check here that at most one is present.
! * We check for DISTINCT here to give a better
! * error position. Other consistency checks are
! * deferred to parse_func.c or parse_agg.c
! */
! if ($2)
! {
! if (n->agg_order)
! ereport(ERROR,
! (errcode(ERRCODE_SYNTAX_ERROR),
! errmsg("Cannot have multiple ORDER BY clauses with WITHIN GROUP"),
! parser_errposition(@2)));
! if (n->agg_distinct)
! ereport(ERROR,
! (errcode(ERRCODE_SYNTAX_ERROR),
! errmsg("Cannot have DISTINCT and WITHIN GROUP together"),
! parser_errposition(@2)));
! n->agg_order = $2;
! n->has_within_group = TRUE;
! }
! n->agg_filter = $3;
! n->over = $4;
$$ = (Node*)n;
}
| func_expr_common_subexpr
***************
*** 12675,12680 **** unreserved_keyword:
--- 12704,12710 ----
| VIEW
| VOLATILE
| WHITESPACE_P
+ | WITHIN
| WITHOUT
| WORK
| WRAPPER
*** a/src/backend/parser/parse_agg.c
--- b/src/backend/parser/parse_agg.c
***************
*** 44,50 **** typedef struct
int sublevels_up;
} check_ungrouped_columns_context;
! static int check_agg_arguments(ParseState *pstate, List *args, Expr *filter);
static bool check_agg_arguments_walker(Node *node,
check_agg_arguments_context *context);
static void check_ungrouped_columns(Node *node, ParseState *pstate, Query *qry,
--- 44,50 ----
int sublevels_up;
} check_ungrouped_columns_context;
! static int check_agg_arguments(ParseState *pstate, List *args, List *agg_ordset, Expr *filter);
static bool check_agg_arguments_walker(Node *node,
check_agg_arguments_context *context);
static void check_ungrouped_columns(Node *node, ParseState *pstate, Query *qry,
***************
*** 75,81 **** static bool check_ungrouped_columns_walker(Node *node,
*/
void
transformAggregateCall(ParseState *pstate, Aggref *agg,
! List *args, List *aggorder, bool agg_distinct)
{
List *tlist;
List *torder;
--- 75,81 ----
*/
void
transformAggregateCall(ParseState *pstate, Aggref *agg,
! List *args, List *aggorder, bool agg_distinct, bool agg_within_group)
{
List *tlist;
List *torder;
***************
*** 93,104 **** transformAggregateCall(ParseState *pstate, Aggref *agg,
*/
tlist = NIL;
attno = 1;
! foreach(lc, args)
{
! Expr *arg = (Expr *) lfirst(lc);
! TargetEntry *tle = makeTargetEntry(arg, attno++, NULL, false);
! tlist = lappend(tlist, tle);
}
/*
--- 93,116 ----
*/
tlist = NIL;
attno = 1;
!
! if (agg_within_group)
! {
! agg->isordset = TRUE;
! agg->orddirectargs = args;
! }
! else
{
! foreach(lc, args)
! {
! Expr *arg = (Expr *) lfirst(lc);
! TargetEntry *tle = makeTargetEntry(arg, attno++, NULL, false);
! tlist = lappend(tlist, tle);
! }
!
! agg->isordset = FALSE;
! agg->orddirectargs = NIL;
}
/*
***************
*** 160,166 **** transformAggregateCall(ParseState *pstate, Aggref *agg,
* Check the arguments to compute the aggregate's level and detect
* improper nesting.
*/
! min_varlevel = check_agg_arguments(pstate, agg->args, agg->aggfilter);
agg->agglevelsup = min_varlevel;
/* Mark the correct pstate level as having aggregates */
--- 172,178 ----
* Check the arguments to compute the aggregate's level and detect
* improper nesting.
*/
! min_varlevel = check_agg_arguments(pstate, agg->args, agg->orddirectargs, agg->aggfilter);
agg->agglevelsup = min_varlevel;
/* Mark the correct pstate level as having aggregates */
***************
*** 312,318 **** transformAggregateCall(ParseState *pstate, Aggref *agg,
* which we can't know until we finish scanning the arguments.
*/
static int
! check_agg_arguments(ParseState *pstate, List *args, Expr *filter)
{
int agglevel;
check_agg_arguments_context context;
--- 324,330 ----
* which we can't know until we finish scanning the arguments.
*/
static int
! check_agg_arguments(ParseState *pstate, List *args, List *agg_ordset, Expr *filter)
{
int agglevel;
check_agg_arguments_context context;
***************
*** 330,335 **** check_agg_arguments(ParseState *pstate, List *args, Expr *filter)
--- 342,350 ----
check_agg_arguments_walker,
(void *) &context);
+ (void) expression_tree_walker((Node *) agg_ordset, check_agg_arguments_walker,
+ (void *) &context);
+
/*
* If we found no vars nor aggs at all, it's a level-zero aggregate;
* otherwise, its level is the minimum of vars or aggs.
***************
*** 353,360 **** check_agg_arguments(ParseState *pstate, List *args, Expr *filter)
(errcode(ERRCODE_GROUPING_ERROR),
errmsg("aggregate function calls cannot be nested"),
parser_errposition(pstate,
! locate_agg_of_level((Node *) args,
! agglevel))));
return agglevel;
}
--- 368,375 ----
(errcode(ERRCODE_GROUPING_ERROR),
errmsg("aggregate function calls cannot be nested"),
parser_errposition(pstate,
! locate_agg_of_level((Node *) args,
! agglevel))));
return agglevel;
}
***************
*** 823,830 **** check_ungrouped_columns_walker(Node *node,
* We do need to look at aggregates of lower levels, however.
*/
if (IsA(node, Aggref) &&
! (int) ((Aggref *) node)->agglevelsup >= context->sublevels_up)
return false;
/*
* If we have any GROUP BY items that are not simple Vars, check to see if
--- 838,852 ----
* We do need to look at aggregates of lower levels, however.
*/
if (IsA(node, Aggref) &&
! (int) ((Aggref *) node)->agglevelsup > context->sublevels_up)
! {
return false;
+ }
+ else if (IsA(node, Aggref) &&
+ (int) ((Aggref *) node)->agglevelsup == context->sublevels_up)
+ {
+ return check_ungrouped_columns_walker(((Aggref *)node)->orddirectargs, context);
+ }
/*
* If we have any GROUP BY items that are not simple Vars, check to see if
***************
*** 1005,1016 **** build_aggregate_fnexprs(Oid *agg_input_types,
args = lappend(args, argp);
}
! *transfnexpr = (Expr *) makeFuncExpr(transfn_oid,
! agg_state_type,
! args,
! InvalidOid,
! agg_input_collation,
! COERCE_EXPLICIT_CALL);
/* see if we have a final function */
if (!OidIsValid(finalfn_oid))
--- 1027,1045 ----
args = lappend(args, argp);
}
! if (OidIsValid(transfn_oid))
! {
! *transfnexpr = (Expr *) makeFuncExpr(transfn_oid,
! agg_state_type,
! args,
! InvalidOid,
! agg_input_collation,
! COERCE_EXPLICIT_CALL);
! }
! else
! {
! transfnexpr = NULL;
! }
/* see if we have a final function */
if (!OidIsValid(finalfn_oid))
*** a/src/backend/parser/parse_expr.c
--- b/src/backend/parser/parse_expr.c
***************
*** 464,470 **** transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
list_make1(n),
list_make1(result),
NIL, NULL, false, false, false,
! NULL, true, location);
if (newresult == NULL)
unknown_attribute(pstate, result, strVal(n), location);
result = newresult;
--- 464,470 ----
list_make1(n),
list_make1(result),
NIL, NULL, false, false, false,
! false, NULL, true, location);
if (newresult == NULL)
unknown_attribute(pstate, result, strVal(n), location);
result = newresult;
***************
*** 632,638 **** transformColumnRef(ParseState *pstate, ColumnRef *cref)
list_make1(makeString(colname)),
list_make1(node),
NIL, NULL, false, false, false,
! NULL, true, cref->location);
}
break;
}
--- 632,638 ----
list_make1(makeString(colname)),
list_make1(node),
NIL, NULL, false, false, false,
! false, NULL, true, cref->location);
}
break;
}
***************
*** 677,683 **** transformColumnRef(ParseState *pstate, ColumnRef *cref)
list_make1(makeString(colname)),
list_make1(node),
NIL, NULL, false, false, false,
! NULL, true, cref->location);
}
break;
}
--- 677,683 ----
list_make1(makeString(colname)),
list_make1(node),
NIL, NULL, false, false, false,
! false, NULL, true, cref->location);
}
break;
}
***************
*** 735,741 **** transformColumnRef(ParseState *pstate, ColumnRef *cref)
list_make1(makeString(colname)),
list_make1(node),
NIL, NULL, false, false, false,
! NULL, true, cref->location);
}
break;
}
--- 735,741 ----
list_make1(makeString(colname)),
list_make1(node),
NIL, NULL, false, false, false,
! false, NULL, true, cref->location);
}
break;
}
***************
*** 1248,1255 **** transformFuncCall(ParseState *pstate, FuncCall *fn)
targs = NIL;
foreach(args, fn->args)
{
! targs = lappend(targs, transformExprRecurse(pstate,
! (Node *) lfirst(args)));
}
/*
--- 1248,1254 ----
targs = NIL;
foreach(args, fn->args)
{
! targs = lappend(targs, transformExprRecurse(pstate, (Node *) lfirst(args)));
}
/*
***************
*** 1271,1276 **** transformFuncCall(ParseState *pstate, FuncCall *fn)
--- 1270,1276 ----
fn->agg_star,
fn->agg_distinct,
fn->func_variadic,
+ fn->has_within_group,
fn->over,
false,
fn->location);
*** a/src/backend/parser/parse_func.c
--- b/src/backend/parser/parse_func.c
***************
*** 17,22 ****
--- 17,23 ----
#include "access/htup_details.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
+ #include "catalog/pg_aggregate.h"
#include "funcapi.h"
#include "lib/stringinfo.h"
#include "nodes/makefuncs.h"
***************
*** 27,32 ****
--- 28,34 ----
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parse_type.h"
+ #include "parser/parse_expr.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
***************
*** 63,69 **** Node *
ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
List *agg_order, Expr *agg_filter,
bool agg_star, bool agg_distinct, bool func_variadic,
! WindowDef *over, bool is_column, int location)
{
Oid rettype;
Oid funcid;
--- 65,71 ----
ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
List *agg_order, Expr *agg_filter,
bool agg_star, bool agg_distinct, bool func_variadic,
! bool agg_within_group, WindowDef *over, bool is_column, int location)
{
Oid rettype;
Oid funcid;
***************
*** 81,86 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 83,94 ----
int nvargs;
Oid vatype;
FuncDetailCode fdresult;
+ HeapTuple tup;
+ Oid aggfinalfn;
+ int number_of_args;
+
+ /* Check if the function has WITHIN GROUP as well as distinct. */
+ Assert(!(agg_within_group && agg_distinct));
/*
* Most of the rest of the parser just assumes that functions do not have
***************
*** 163,174 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 171,208 ----
}
}
+ if(agg_within_group && argnames)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("Named arguments not allowed in WITHIN GROUP")));
+ }
+
if (fargs)
{
first_arg = linitial(fargs);
Assert(first_arg != NULL);
}
+ /* If WITHIN GROUP is present, we need to call transformExpr on each
+ * SortBy node in agg_order, then call exprType and append to
+ * actual_arg_types, in order to get the types of values in
+ * WITHIN BY clause.
+ */
+ if(agg_within_group)
+ {
+ Assert(agg_order != NULL);
+
+ foreach(l, agg_order)
+ {
+ SortBy *arg = (SortBy *) lfirst(l);
+
+ arg->node = transformExpr(pstate, arg->node, EXPR_KIND_ORDER_BY);
+
+ actual_arg_types[nargs++] = exprType(arg->node);
+ }
+ }
+
/*
* Check for column projection: if function has one argument, and that
* argument is of complex type, and function name is not qualified, then
***************
*** 247,252 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 281,292 ----
errmsg("DISTINCT specified, but %s is not an aggregate function",
NameListToString(funcname)),
parser_errposition(pstate, location)));
+ if (agg_within_group)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("WITHIN GROUP specified, but %s is not an ordered set function",
+ NameListToString(funcname)),
+ parser_errposition(pstate, location)));
if (agg_order != NIL)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
***************
*** 266,271 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 306,329 ----
NameListToString(funcname)),
parser_errposition(pstate, location)));
}
+ else if (fdresult == FUNCDETAIL_ORDERED)
+ {
+ if(!agg_within_group)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("Ordered set function specified, but WITHIN GROUP not present"),
+ parser_errposition(pstate, location)));
+ }
+
+ if(over)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("OVER clause in ordered set function not supported"),
+ parser_errposition(pstate, location)));
+ }
+ }
else if (!(fdresult == FUNCDETAIL_AGGREGATE ||
fdresult == FUNCDETAIL_WINDOWFUNC))
{
***************
*** 351,357 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
false);
/* perform the necessary typecasting of arguments */
! make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types);
/*
* If it's a variadic function call, transform the last nvargs arguments
--- 409,417 ----
false);
/* perform the necessary typecasting of arguments */
! make_fn_arguments(pstate, fargs, (fdresult==FUNCDETAIL_ORDERED) ? agg_order : NIL,
! actual_arg_types,
! declared_arg_types);
/*
* If it's a variadic function call, transform the last nvargs arguments
***************
*** 416,425 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
retval = (Node *) funcexpr;
}
! else if (fdresult == FUNCDETAIL_AGGREGATE && !over)
{
/* aggregate function */
Aggref *aggref = makeNode(Aggref);
aggref->aggfnoid = funcid;
aggref->aggtype = rettype;
--- 476,492 ----
retval = (Node *) funcexpr;
}
! else if ((fdresult == FUNCDETAIL_AGGREGATE && !over) || fdresult == FUNCDETAIL_ORDERED)
{
/* aggregate function */
Aggref *aggref = makeNode(Aggref);
+ if(agg_within_group && fdresult == FUNCDETAIL_AGGREGATE)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("%s is not an ordered set function",func_signature_string(funcname, nargs,
+ NIL, actual_arg_types))));
+ }
aggref->aggfnoid = funcid;
aggref->aggtype = rettype;
***************
*** 430,435 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 497,522 ----
/* agglevelsup will be set by transformAggregateCall */
aggref->location = location;
+ if (fdresult == FUNCDETAIL_ORDERED)
+ {
+ Form_pg_aggregate classForm;
+ tup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(funcid));
+ if (!HeapTupleIsValid(tup)) /* should not happen */
+ elog(ERROR, "cache lookup failed for aggregate %u", funcid);
+
+ classForm = (Form_pg_aggregate) GETSTRUCT(tup);
+ aggfinalfn = classForm->aggfinalfn;
+ number_of_args = get_func_nargs(aggfinalfn);
+ if (number_of_args != list_length(fargs))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("Number of arguments does not match fargs")));
+ }
+
+ ReleaseSysCache(tup);
+ }
+
/*
* Reject attempt to call a parameterless aggregate without (*)
* syntax. This is mere pedantry but some folks insisted ...
***************
*** 460,466 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
parser_errposition(pstate, location)));
/* parse_agg.c does additional aggregate-specific processing */
! transformAggregateCall(pstate, aggref, fargs, agg_order, agg_distinct);
retval = (Node *) aggref;
}
--- 547,553 ----
parser_errposition(pstate, location)));
/* parse_agg.c does additional aggregate-specific processing */
! transformAggregateCall(pstate, aggref, fargs, agg_order, agg_distinct, agg_within_group);
retval = (Node *) aggref;
}
***************
*** 469,474 **** ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
--- 556,569 ----
/* window function */
WindowFunc *wfunc = makeNode(WindowFunc);
+ if(agg_within_group)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("WITHIN GROUP not allowed in window functions"),
+ parser_errposition(pstate, location)));
+ }
+
/*
* True window functions must be called with a window definition.
*/
***************
*** 1264,1269 **** func_get_detail(List *funcname,
--- 1359,1366 ----
ftup = SearchSysCache1(PROCOID,
ObjectIdGetDatum(best_candidate->oid));
+
+
if (!HeapTupleIsValid(ftup)) /* should not happen */
elog(ERROR, "cache lookup failed for function %u",
best_candidate->oid);
***************
*** 1341,1347 **** func_get_detail(List *funcname,
}
}
if (pform->proisagg)
! result = FUNCDETAIL_AGGREGATE;
else if (pform->proiswindow)
result = FUNCDETAIL_WINDOWFUNC;
else
--- 1438,1460 ----
}
}
if (pform->proisagg)
! {
! Form_pg_aggregate pfagg;
! HeapTuple ftup_agg;
!
! ftup_agg = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(best_candidate->oid));
! pfagg = (Form_pg_aggregate) GETSTRUCT(ftup_agg);
! if (pfagg->aggisordsetfunc)
! {
! result = FUNCDETAIL_ORDERED;
! }
! else
! {
! result = FUNCDETAIL_AGGREGATE;
! }
!
! ReleaseSysCache(ftup_agg);
! }
else if (pform->proiswindow)
result = FUNCDETAIL_WINDOWFUNC;
else
***************
*** 1370,1379 **** func_get_detail(List *funcname,
--- 1483,1494 ----
void
make_fn_arguments(ParseState *pstate,
List *fargs,
+ List *agg_order,
Oid *actual_arg_types,
Oid *declared_arg_types)
{
ListCell *current_fargs;
+ ListCell *current_aoargs;
int i = 0;
foreach(current_fargs, fargs)
***************
*** 1414,1419 **** make_fn_arguments(ParseState *pstate,
--- 1529,1553 ----
}
i++;
}
+
+ foreach(current_aoargs, agg_order)
+ {
+ if (actual_arg_types[i] != declared_arg_types[i])
+ {
+ SortBy *node = (SortBy *) lfirst(current_aoargs);
+ SortBy *temp = NULL;
+
+ temp = coerce_type(pstate,
+ node->node,
+ actual_arg_types[i],
+ declared_arg_types[i], -1,
+ COERCION_IMPLICIT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ node->node = temp;
+ }
+ i++;
+ }
}
/*
*** a/src/backend/parser/parse_oper.c
--- b/src/backend/parser/parse_oper.c
***************
*** 823,829 **** make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
false);
/* perform the necessary typecasting of arguments */
! make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
/* and build the expression node */
result = makeNode(OpExpr);
--- 823,829 ----
false);
/* perform the necessary typecasting of arguments */
! make_fn_arguments(pstate, args, NULL, actual_arg_types, declared_arg_types);
/* and build the expression node */
result = makeNode(OpExpr);
***************
*** 953,959 **** make_scalar_array_op(ParseState *pstate, List *opname,
declared_arg_types[1] = res_atypeId;
/* perform the necessary typecasting of arguments */
! make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
/* and build the expression node */
result = makeNode(ScalarArrayOpExpr);
--- 953,959 ----
declared_arg_types[1] = res_atypeId;
/* perform the necessary typecasting of arguments */
! make_fn_arguments(pstate, args, NULL, actual_arg_types, declared_arg_types);
/* and build the expression node */
result = makeNode(ScalarArrayOpExpr);
*** a/src/backend/utils/adt/Makefile
--- b/src/backend/utils/adt/Makefile
***************
*** 25,31 **** OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
rowtypes.o regexp.o regproc.o ruleutils.o selfuncs.o \
tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \
network.o mac.o inet_cidr_ntop.o inet_net_pton.o \
! ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o \
ascii.o quote.o pgstatfuncs.o encode.o dbsize.o genfile.o trigfuncs.o \
tsginidx.o tsgistidx.o tsquery.o tsquery_cleanup.o tsquery_gist.o \
tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \
--- 25,31 ----
rowtypes.o regexp.o regproc.o ruleutils.o selfuncs.o \
tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \
network.o mac.o inet_cidr_ntop.o inet_net_pton.o \
! inversedistribution.o ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o \
ascii.o quote.o pgstatfuncs.o encode.o dbsize.o genfile.o trigfuncs.o \
tsginidx.o tsgistidx.o tsquery.o tsquery_cleanup.o tsquery_gist.o \
tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \
*** /dev/null
--- b/src/backend/utils/adt/inversedistribution.c
***************
*** 0 ****
--- 1,237 ----
+ /*-------------------------------------------------------------------------
+ *
+ * inversedistribution.c
+ * Inverse distribution functions.
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/inversedistribution.c
+ *
+ *-------------------------------------------------------------------------
+ */
+ #include "postgres.h"
+ #include "fmgr.h"
+ #include <string.h>
+ #include <math.h>
+
+ #include "utils/tuplesort.h"
+ #include "catalog/pg_type.h"
+ #include "utils/datetime.h"
+
+ /*
+ * percentile_disc(float8) - discrete (nearest) percentile
+ */
+
+ Datum percentile_disc_final(PG_FUNCTION_ARGS);
+
+ Datum
+ percentile_disc_final(PG_FUNCTION_ARGS)
+ {
+ float8 percentile = PG_GETARG_FLOAT8(0);
+ int64 rowcount = AggSetGetRowCount(fcinfo);
+ Tuplesortstate *sorter;
+ Oid datumtype;
+ Datum val;
+ bool isnull;
+ int64 skiprows;
+
+ AggSetGetSortInfo(fcinfo, &sorter, NULL, NULL, &datumtype);
+
+ Assert(datumtype == FLOAT8OID);
+
+ if (percentile < 0 || percentile > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("percentile value %g is not between 0 and 1", percentile)));
+
+ if (rowcount < 1)
+ PG_RETURN_NULL();
+
+ tuplesort_performsort(sorter);
+
+ /*
+ * We need the smallest K such that (K/N) >= percentile. K starts at 1.
+ * Therefore K >= N*percentile
+ * Therefore K = ceil(N*percentile)
+ * So we skip K-1 rows (if K>0) and return the next row fetched.
+ */
+
+ skiprows = (int64) ceil(percentile * rowcount);
+ Assert(skiprows <= rowcount);
+
+ while (--skiprows > 0)
+ if (!tuplesort_getdatum(sorter, true, NULL, NULL))
+ elog(ERROR,"missing row in percentile_disc");
+
+ if (!tuplesort_getdatum(sorter, true, &val, &isnull))
+ elog(ERROR,"missing row in percentile_disc");
+
+ /* if float8 is by-ref, "val" already points to palloced space in
+ * our memory context. No need to do more.
+ */
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(val);
+ }
+
+
+
+ /*
+ * percentile_cont(float8) - continuous (nearest) percentile
+ */
+
+ Datum percentile_cont_final(PG_FUNCTION_ARGS);
+
+ Datum
+ percentile_cont_final(PG_FUNCTION_ARGS)
+ {
+ float8 percentile = PG_GETARG_FLOAT8(0);
+ int64 rowcount = AggSetGetRowCount(fcinfo);
+ Tuplesortstate *sorter;
+ Oid datumtype;
+ Datum val;
+ Datum first_row;
+ Datum second_row;
+ float8 first_row_val;
+ float8 second_row_val;
+ float8 proportion;
+ bool isnull;
+ int64 skiprows;
+ int64 lower_row = 0;
+ int64 higher_row = 0;
+
+ AggSetGetSortInfo(fcinfo, &sorter, NULL, NULL, &datumtype);
+
+ Assert(datumtype == FLOAT8OID);
+
+ if (percentile < 0 || percentile > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("percentile value %g is not between 0 and 1", percentile)));
+
+ if (rowcount < 1)
+ PG_RETURN_NULL();
+
+ tuplesort_performsort(sorter);
+
+ lower_row = floor(percentile * (rowcount -1));
+ higher_row = ceil(percentile * (rowcount -1));
+
+ Assert(lower_row < rowcount);
+
+ for (skiprows = lower_row; skiprows > 0; --skiprows)
+ if (!tuplesort_getdatum(sorter, true, NULL, NULL))
+ elog(ERROR,"missing row in percentile_cont");
+
+ if (!tuplesort_getdatum(sorter, true, &first_row, &isnull))
+ elog(ERROR,"missing row in percentile_cont");
+
+ if (lower_row == higher_row)
+ {
+
+ val = first_row;
+ }
+ else
+ {
+ if (!tuplesort_getdatum(sorter, true, &second_row, &isnull))
+ elog(ERROR,"missing row in percentile_cont");
+
+ if (isnull)
+ PG_RETURN_NULL();
+
+ proportion = (percentile * (rowcount-1)) - lower_row;
+ first_row_val = DatumGetFloat8(first_row);
+ second_row_val = DatumGetFloat8(second_row);
+ val = Float8GetDatum(first_row_val + (proportion *(second_row_val - first_row_val)));
+ }
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(val);
+ }
+
+ /*
+ * percentile_interval_cont(Interval) - continuous (nearest) percentile for Interval
+ */
+
+ Datum percentile_interval_cont_final(PG_FUNCTION_ARGS);
+
+ Datum
+ percentile_interval_cont_final(PG_FUNCTION_ARGS)
+ {
+ float8 percentile = PG_GETARG_FLOAT8(0);
+ int64 rowcount = AggSetGetRowCount(fcinfo);
+ Tuplesortstate *sorter;
+ Oid datumtype;
+ Datum val;
+ Datum first_row;
+ Datum second_row;
+ float8 proportion;
+ bool isnull;
+ int64 skiprows;
+ int64 lower_row = 0;
+ int64 higher_row = 0;
+ Datum diff_result;
+ Datum mul_result;
+ Datum add_result;
+
+ AggSetGetSortInfo(fcinfo, &sorter, NULL, NULL, &datumtype);
+
+ Assert(datumtype == INTERVALOID);
+
+ if (percentile < 0 || percentile > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("percentile value %g is not between 0 and 1", percentile)));
+
+ if (rowcount < 1)
+ PG_RETURN_NULL();
+
+ tuplesort_performsort(sorter);
+
+ lower_row = floor(percentile * (rowcount -1));
+ higher_row = ceil(percentile * (rowcount -1));
+
+ Assert(lower_row < rowcount);
+
+ for (skiprows = lower_row; skiprows > 0; --skiprows)
+ if (!tuplesort_getdatum(sorter, true, NULL, NULL))
+ elog(ERROR,"missing row in percentile_cont");
+
+ if (!tuplesort_getdatum(sorter, true, &first_row, &isnull))
+ elog(ERROR,"missing row in percentile_cont");
+
+ if (lower_row == higher_row)
+ {
+
+ val = first_row;
+ }
+ else
+ {
+ if (!tuplesort_getdatum(sorter, true, &second_row, &isnull))
+ elog(ERROR,"missing row in percentile_cont");
+
+ if (isnull)
+ PG_RETURN_NULL();
+
+ proportion = (percentile * (rowcount-1)) - lower_row;
+
+ diff_result = DirectFunctionCall2(interval_mi, second_row, first_row);
+ mul_result = DirectFunctionCall2(interval_mul, diff_result, Float8GetDatumFast(proportion));
+ add_result = DirectFunctionCall2(interval_pl, mul_result, first_row);
+ val = add_result;
+ }
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(val);
+ }
+
+
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
***************
*** 402,407 **** static char *generate_function_name(Oid funcid, int nargs,
--- 402,409 ----
static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
static text *string_to_text(char *str);
static char *flatten_reloptions(Oid relid);
+ static void get_aggstd_expr(Aggref *aggref, deparse_context *context);
+ static void get_ordset_expr(Aggref *aggref, deparse_context *context);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
***************
*** 7384,7389 **** static void
--- 7386,7468 ----
get_agg_expr(Aggref *aggref, deparse_context *context)
{
StringInfo buf = context->buf;
+
+ if (aggref->isordset)
+ {
+ get_ordset_expr(aggref, context);
+ }
+ else
+ {
+ get_aggstd_expr(aggref, context);
+ }
+
+ if (aggref->aggfilter != NULL)
+ {
+ appendStringInfoString(buf, ") FILTER (WHERE ");
+ get_rule_expr((Node *)aggref->aggfilter, context, false);
+ }
+
+ appendStringInfoString(buf, ")");
+ }
+
+ static void
+ get_ordset_expr(Aggref *aggref, deparse_context *context)
+ {
+ StringInfo buf = context->buf;
+ Oid argtypes[FUNC_MAX_ARGS];
+ List *arglist;
+ int nargs;
+ ListCell *l;
+
+ /* Extract the regular arguments, ignoring resjunk stuff for the moment */
+ arglist = NIL;
+ nargs = 0;
+
+ foreach(l, aggref->orddirectargs)
+ {
+ Node *arg = (Node *) lfirst(l);
+
+ Assert(!IsA(arg, NamedArgExpr));
+ if (nargs >= FUNC_MAX_ARGS) /* paranoia */
+ ereport(ERROR,
+ (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+ errmsg("too many arguments")));
+ argtypes[nargs] = exprType(arg);
+ nargs++;
+ }
+
+ /* For direct arguments in case of ordered set functions */
+ foreach(l, aggref->args)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(l);
+ Node *arg = (Node *) tle->expr;
+
+ Assert(!IsA(arg, NamedArgExpr));
+ if (tle->resjunk)
+ continue;
+ if (nargs >= FUNC_MAX_ARGS) /* paranoia */
+ ereport(ERROR,
+ (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+ errmsg("too many arguments")));
+ argtypes[nargs] = exprType(arg);
+ arglist = lappend(arglist, arg);
+ nargs++;
+ }
+
+ appendStringInfo(buf, "%s(",
+ generate_function_name(aggref->aggfnoid, nargs,
+ NIL, argtypes,
+ false, NULL));
+
+ get_rule_expr((Node *)aggref->orddirectargs, context, true);
+ appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY ");
+ get_rule_orderby(aggref->aggorder, aggref->args, false, context);
+
+ }
+ static void
+ get_aggstd_expr(Aggref *aggref, deparse_context *context)
+ {
+ StringInfo buf = context->buf;
Oid argtypes[FUNC_MAX_ARGS];
List *arglist;
int nargs;
***************
*** 7424,7437 **** get_agg_expr(Aggref *aggref, deparse_context *context)
appendStringInfoString(buf, " ORDER BY ");
get_rule_orderby(aggref->aggorder, aggref->args, false, context);
}
-
- if (aggref->aggfilter != NULL)
- {
- appendStringInfoString(buf, ") FILTER (WHERE ");
- get_rule_expr((Node *) aggref->aggfilter, context, false);
- }
-
- appendStringInfoChar(buf, ')');
}
/*
--- 7503,7508 ----
***************
*** 7472,7478 **** get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
if (wfunc->aggfilter != NULL)
{
appendStringInfoString(buf, ") FILTER (WHERE ");
! get_rule_expr((Node *) wfunc->aggfilter, context, false);
}
appendStringInfoString(buf, ") OVER ");
--- 7543,7549 ----
if (wfunc->aggfilter != NULL)
{
appendStringInfoString(buf, ") FILTER (WHERE ");
! get_rule_expr((Node *)wfunc->aggfilter, context, false);
}
appendStringInfoString(buf, ") OVER ");
*** a/src/backend/utils/sort/tuplesort.c
--- b/src/backend/utils/sort/tuplesort.c
***************
*** 1411,1433 **** tuplesort_performsort(Tuplesortstate *state)
* Internal routine to fetch the next tuple in either forward or back
* direction into *stup. Returns FALSE if no more tuples.
* If *should_free is set, the caller must pfree stup.tuple when done with it.
*/
static bool
tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
SortTuple *stup, bool *should_free)
{
unsigned int tuplen;
switch (state->status)
{
case TSS_SORTEDINMEM:
Assert(forward || state->randomAccess);
! *should_free = false;
if (forward)
{
if (state->current < state->memtupcount)
{
! *stup = state->memtuples[state->current++];
return true;
}
state->eof_reached = true;
--- 1411,1439 ----
* Internal routine to fetch the next tuple in either forward or back
* direction into *stup. Returns FALSE if no more tuples.
* If *should_free is set, the caller must pfree stup.tuple when done with it.
+ * stup may be null to move without fetching.
*/
static bool
tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
SortTuple *stup, bool *should_free)
{
unsigned int tuplen;
+ SortTuple dummy;
+ SortTuple *ptup = stup ? stup : &dummy;
switch (state->status)
{
case TSS_SORTEDINMEM:
Assert(forward || state->randomAccess);
! if (should_free)
! *should_free = false;
if (forward)
{
if (state->current < state->memtupcount)
{
! if (stup)
! *stup = state->memtuples[state->current];
! state->current++;
return true;
}
state->eof_reached = true;
***************
*** 1459,1479 **** tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
if (state->current <= 0)
return false;
}
! *stup = state->memtuples[state->current - 1];
return true;
}
break;
case TSS_SORTEDONTAPE:
Assert(forward || state->randomAccess);
! *should_free = true;
if (forward)
{
if (state->eof_reached)
return false;
if ((tuplen = getlen(state, state->result_tape, true)) != 0)
{
! READTUP(state, stup, state->result_tape, tuplen);
return true;
}
else
--- 1465,1489 ----
if (state->current <= 0)
return false;
}
! if (stup)
! *stup = state->memtuples[state->current - 1];
return true;
}
break;
case TSS_SORTEDONTAPE:
Assert(forward || state->randomAccess);
! if (should_free)
! *should_free = true;
if (forward)
{
if (state->eof_reached)
return false;
if ((tuplen = getlen(state, state->result_tape, true)) != 0)
{
! READTUP(state, ptup, state->result_tape, tuplen);
! if (!stup && dummy.tuple)
! pfree(dummy.tuple);
return true;
}
else
***************
*** 1546,1557 **** tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
state->result_tape,
tuplen))
elog(ERROR, "bogus tuple length in backward scan");
! READTUP(state, stup, state->result_tape, tuplen);
return true;
case TSS_FINALMERGE:
Assert(forward);
! *should_free = true;
/*
* This code should match the inner loop of mergeonerun().
--- 1556,1570 ----
state->result_tape,
tuplen))
elog(ERROR, "bogus tuple length in backward scan");
! READTUP(state, ptup, state->result_tape, tuplen);
! if (!stup && dummy.tuple)
! pfree(dummy.tuple);
return true;
case TSS_FINALMERGE:
Assert(forward);
! if (should_free)
! *should_free = true;
/*
* This code should match the inner loop of mergeonerun().
***************
*** 1563,1573 **** tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
int tupIndex;
SortTuple *newtup;
! *stup = state->memtuples[0];
/* returned tuple is no longer counted in our memory space */
! if (stup->tuple)
{
! tuplen = GetMemoryChunkSpace(stup->tuple);
state->availMem += tuplen;
state->mergeavailmem[srcTape] += tuplen;
}
--- 1576,1586 ----
int tupIndex;
SortTuple *newtup;
! *ptup = state->memtuples[0];
/* returned tuple is no longer counted in our memory space */
! if (ptup->tuple)
{
! tuplen = GetMemoryChunkSpace(ptup->tuple);
state->availMem += tuplen;
state->mergeavailmem[srcTape] += tuplen;
}
***************
*** 1598,1603 **** tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
--- 1611,1618 ----
newtup->tupindex = state->mergefreelist;
state->mergefreelist = tupIndex;
state->mergeavailslots[srcTape]++;
+ if (!stup && dummy.tuple)
+ pfree(dummy.tuple);
return true;
}
return false;
***************
*** 1620,1639 **** tuplesort_gettupleslot(Tuplesortstate *state, bool forward,
MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext);
SortTuple stup;
bool should_free;
! if (!tuplesort_gettuple_common(state, forward, &stup, &should_free))
! stup.tuple = NULL;
MemoryContextSwitchTo(oldcontext);
! if (stup.tuple)
{
! ExecStoreMinimalTuple((MinimalTuple) stup.tuple, slot, should_free);
return true;
}
else
{
! ExecClearTuple(slot);
return false;
}
}
--- 1635,1656 ----
MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext);
SortTuple stup;
bool should_free;
+ bool found;
! found = tuplesort_gettuple_common(state, forward, (slot ? &stup : NULL), &should_free);
MemoryContextSwitchTo(oldcontext);
! if (found)
{
! if (slot)
! ExecStoreMinimalTuple((MinimalTuple) stup.tuple, slot, should_free);
return true;
}
else
{
! if (slot)
! ExecClearTuple(slot);
return false;
}
}
***************
*** 1692,1715 **** tuplesort_getdatum(Tuplesortstate *state, bool forward,
SortTuple stup;
bool should_free;
! if (!tuplesort_gettuple_common(state, forward, &stup, &should_free))
{
MemoryContextSwitchTo(oldcontext);
return false;
}
! if (stup.isnull1 || state->datumTypeByVal)
{
! *val = stup.datum1;
! *isNull = stup.isnull1;
! }
! else
! {
! if (should_free)
*val = stup.datum1;
else
! *val = datumCopy(stup.datum1, false, state->datumTypeLen);
! *isNull = false;
}
MemoryContextSwitchTo(oldcontext);
--- 1709,1735 ----
SortTuple stup;
bool should_free;
! if (!tuplesort_gettuple_common(state, forward, (val ? &stup : NULL), &should_free))
{
MemoryContextSwitchTo(oldcontext);
return false;
}
! if (val)
{
! if (stup.isnull1 || state->datumTypeByVal)
! {
*val = stup.datum1;
+ *isNull = stup.isnull1;
+ }
else
! {
! if (should_free)
! *val = stup.datum1;
! else
! *val = datumCopy(stup.datum1, false, state->datumTypeLen);
! *isNull = false;
! }
}
MemoryContextSwitchTo(oldcontext);
*** a/src/include/catalog/pg_aggregate.h
--- b/src/include/catalog/pg_aggregate.h
***************
*** 44,49 **** CATALOG(pg_aggregate,2600) BKI_WITHOUT_OIDS
--- 44,50 ----
regproc aggfinalfn;
Oid aggsortop;
Oid aggtranstype;
+ bool aggisordsetfunc;
#ifdef CATALOG_VARLEN /* variable-length fields start here */
text agginitval;
***************
*** 62,74 **** typedef FormData_pg_aggregate *Form_pg_aggregate;
* ----------------
*/
! #define Natts_pg_aggregate 6
#define Anum_pg_aggregate_aggfnoid 1
#define Anum_pg_aggregate_aggtransfn 2
#define Anum_pg_aggregate_aggfinalfn 3
#define Anum_pg_aggregate_aggsortop 4
#define Anum_pg_aggregate_aggtranstype 5
! #define Anum_pg_aggregate_agginitval 6
/* ----------------
--- 63,76 ----
* ----------------
*/
! #define Natts_pg_aggregate 7
#define Anum_pg_aggregate_aggfnoid 1
#define Anum_pg_aggregate_aggtransfn 2
#define Anum_pg_aggregate_aggfinalfn 3
#define Anum_pg_aggregate_aggsortop 4
#define Anum_pg_aggregate_aggtranstype 5
! #define Anum_pg_aggregate_aggisordsetfunc 6
! #define Anum_pg_aggregate_agginitval 7
/* ----------------
***************
*** 77,240 **** typedef FormData_pg_aggregate *Form_pg_aggregate;
*/
/* avg */
! DATA(insert ( 2100 int8_avg_accum numeric_avg 0 1231 "{0,0}" ));
! DATA(insert ( 2101 int4_avg_accum int8_avg 0 1016 "{0,0}" ));
! DATA(insert ( 2102 int2_avg_accum int8_avg 0 1016 "{0,0}" ));
! DATA(insert ( 2103 numeric_avg_accum numeric_avg 0 1231 "{0,0}" ));
! DATA(insert ( 2104 float4_accum float8_avg 0 1022 "{0,0,0}" ));
! DATA(insert ( 2105 float8_accum float8_avg 0 1022 "{0,0,0}" ));
! DATA(insert ( 2106 interval_accum interval_avg 0 1187 "{0 second,0 second}" ));
/* sum */
! DATA(insert ( 2107 int8_sum - 0 1700 _null_ ));
! DATA(insert ( 2108 int4_sum - 0 20 _null_ ));
! DATA(insert ( 2109 int2_sum - 0 20 _null_ ));
! DATA(insert ( 2110 float4pl - 0 700 _null_ ));
! DATA(insert ( 2111 float8pl - 0 701 _null_ ));
! DATA(insert ( 2112 cash_pl - 0 790 _null_ ));
! DATA(insert ( 2113 interval_pl - 0 1186 _null_ ));
! DATA(insert ( 2114 numeric_add - 0 1700 _null_ ));
/* max */
! DATA(insert ( 2115 int8larger - 413 20 _null_ ));
! DATA(insert ( 2116 int4larger - 521 23 _null_ ));
! DATA(insert ( 2117 int2larger - 520 21 _null_ ));
! DATA(insert ( 2118 oidlarger - 610 26 _null_ ));
! DATA(insert ( 2119 float4larger - 623 700 _null_ ));
! DATA(insert ( 2120 float8larger - 674 701 _null_ ));
! DATA(insert ( 2121 int4larger - 563 702 _null_ ));
! DATA(insert ( 2122 date_larger - 1097 1082 _null_ ));
! DATA(insert ( 2123 time_larger - 1112 1083 _null_ ));
! DATA(insert ( 2124 timetz_larger - 1554 1266 _null_ ));
! DATA(insert ( 2125 cashlarger - 903 790 _null_ ));
! DATA(insert ( 2126 timestamp_larger - 2064 1114 _null_ ));
! DATA(insert ( 2127 timestamptz_larger - 1324 1184 _null_ ));
! DATA(insert ( 2128 interval_larger - 1334 1186 _null_ ));
! DATA(insert ( 2129 text_larger - 666 25 _null_ ));
! DATA(insert ( 2130 numeric_larger - 1756 1700 _null_ ));
! DATA(insert ( 2050 array_larger - 1073 2277 _null_ ));
! DATA(insert ( 2244 bpchar_larger - 1060 1042 _null_ ));
! DATA(insert ( 2797 tidlarger - 2800 27 _null_ ));
! DATA(insert ( 3526 enum_larger - 3519 3500 _null_ ));
/* min */
! DATA(insert ( 2131 int8smaller - 412 20 _null_ ));
! DATA(insert ( 2132 int4smaller - 97 23 _null_ ));
! DATA(insert ( 2133 int2smaller - 95 21 _null_ ));
! DATA(insert ( 2134 oidsmaller - 609 26 _null_ ));
! DATA(insert ( 2135 float4smaller - 622 700 _null_ ));
! DATA(insert ( 2136 float8smaller - 672 701 _null_ ));
! DATA(insert ( 2137 int4smaller - 562 702 _null_ ));
! DATA(insert ( 2138 date_smaller - 1095 1082 _null_ ));
! DATA(insert ( 2139 time_smaller - 1110 1083 _null_ ));
! DATA(insert ( 2140 timetz_smaller - 1552 1266 _null_ ));
! DATA(insert ( 2141 cashsmaller - 902 790 _null_ ));
! DATA(insert ( 2142 timestamp_smaller - 2062 1114 _null_ ));
! DATA(insert ( 2143 timestamptz_smaller - 1322 1184 _null_ ));
! DATA(insert ( 2144 interval_smaller - 1332 1186 _null_ ));
! DATA(insert ( 2145 text_smaller - 664 25 _null_ ));
! DATA(insert ( 2146 numeric_smaller - 1754 1700 _null_ ));
! DATA(insert ( 2051 array_smaller - 1072 2277 _null_ ));
! DATA(insert ( 2245 bpchar_smaller - 1058 1042 _null_ ));
! DATA(insert ( 2798 tidsmaller - 2799 27 _null_ ));
! DATA(insert ( 3527 enum_smaller - 3518 3500 _null_ ));
/* count */
! DATA(insert ( 2147 int8inc_any - 0 20 "0" ));
! DATA(insert ( 2803 int8inc - 0 20 "0" ));
/* var_pop */
! DATA(insert ( 2718 int8_accum numeric_var_pop 0 1231 "{0,0,0}" ));
! DATA(insert ( 2719 int4_accum numeric_var_pop 0 1231 "{0,0,0}" ));
! DATA(insert ( 2720 int2_accum numeric_var_pop 0 1231 "{0,0,0}" ));
! DATA(insert ( 2721 float4_accum float8_var_pop 0 1022 "{0,0,0}" ));
! DATA(insert ( 2722 float8_accum float8_var_pop 0 1022 "{0,0,0}" ));
! DATA(insert ( 2723 numeric_accum numeric_var_pop 0 1231 "{0,0,0}" ));
/* var_samp */
! DATA(insert ( 2641 int8_accum numeric_var_samp 0 1231 "{0,0,0}" ));
! DATA(insert ( 2642 int4_accum numeric_var_samp 0 1231 "{0,0,0}" ));
! DATA(insert ( 2643 int2_accum numeric_var_samp 0 1231 "{0,0,0}" ));
! DATA(insert ( 2644 float4_accum float8_var_samp 0 1022 "{0,0,0}" ));
! DATA(insert ( 2645 float8_accum float8_var_samp 0 1022 "{0,0,0}" ));
! DATA(insert ( 2646 numeric_accum numeric_var_samp 0 1231 "{0,0,0}" ));
/* variance: historical Postgres syntax for var_samp */
! DATA(insert ( 2148 int8_accum numeric_var_samp 0 1231 "{0,0,0}" ));
! DATA(insert ( 2149 int4_accum numeric_var_samp 0 1231 "{0,0,0}" ));
! DATA(insert ( 2150 int2_accum numeric_var_samp 0 1231 "{0,0,0}" ));
! DATA(insert ( 2151 float4_accum float8_var_samp 0 1022 "{0,0,0}" ));
! DATA(insert ( 2152 float8_accum float8_var_samp 0 1022 "{0,0,0}" ));
! DATA(insert ( 2153 numeric_accum numeric_var_samp 0 1231 "{0,0,0}" ));
/* stddev_pop */
! DATA(insert ( 2724 int8_accum numeric_stddev_pop 0 1231 "{0,0,0}" ));
! DATA(insert ( 2725 int4_accum numeric_stddev_pop 0 1231 "{0,0,0}" ));
! DATA(insert ( 2726 int2_accum numeric_stddev_pop 0 1231 "{0,0,0}" ));
! DATA(insert ( 2727 float4_accum float8_stddev_pop 0 1022 "{0,0,0}" ));
! DATA(insert ( 2728 float8_accum float8_stddev_pop 0 1022 "{0,0,0}" ));
! DATA(insert ( 2729 numeric_accum numeric_stddev_pop 0 1231 "{0,0,0}" ));
/* stddev_samp */
! DATA(insert ( 2712 int8_accum numeric_stddev_samp 0 1231 "{0,0,0}" ));
! DATA(insert ( 2713 int4_accum numeric_stddev_samp 0 1231 "{0,0,0}" ));
! DATA(insert ( 2714 int2_accum numeric_stddev_samp 0 1231 "{0,0,0}" ));
! DATA(insert ( 2715 float4_accum float8_stddev_samp 0 1022 "{0,0,0}" ));
! DATA(insert ( 2716 float8_accum float8_stddev_samp 0 1022 "{0,0,0}" ));
! DATA(insert ( 2717 numeric_accum numeric_stddev_samp 0 1231 "{0,0,0}" ));
/* stddev: historical Postgres syntax for stddev_samp */
! DATA(insert ( 2154 int8_accum numeric_stddev_samp 0 1231 "{0,0,0}" ));
! DATA(insert ( 2155 int4_accum numeric_stddev_samp 0 1231 "{0,0,0}" ));
! DATA(insert ( 2156 int2_accum numeric_stddev_samp 0 1231 "{0,0,0}" ));
! DATA(insert ( 2157 float4_accum float8_stddev_samp 0 1022 "{0,0,0}" ));
! DATA(insert ( 2158 float8_accum float8_stddev_samp 0 1022 "{0,0,0}" ));
! DATA(insert ( 2159 numeric_accum numeric_stddev_samp 0 1231 "{0,0,0}" ));
/* SQL2003 binary regression aggregates */
! DATA(insert ( 2818 int8inc_float8_float8 - 0 20 "0" ));
! DATA(insert ( 2819 float8_regr_accum float8_regr_sxx 0 1022 "{0,0,0,0,0,0}" ));
! DATA(insert ( 2820 float8_regr_accum float8_regr_syy 0 1022 "{0,0,0,0,0,0}" ));
! DATA(insert ( 2821 float8_regr_accum float8_regr_sxy 0 1022 "{0,0,0,0,0,0}" ));
! DATA(insert ( 2822 float8_regr_accum float8_regr_avgx 0 1022 "{0,0,0,0,0,0}" ));
! DATA(insert ( 2823 float8_regr_accum float8_regr_avgy 0 1022 "{0,0,0,0,0,0}" ));
! DATA(insert ( 2824 float8_regr_accum float8_regr_r2 0 1022 "{0,0,0,0,0,0}" ));
! DATA(insert ( 2825 float8_regr_accum float8_regr_slope 0 1022 "{0,0,0,0,0,0}" ));
! DATA(insert ( 2826 float8_regr_accum float8_regr_intercept 0 1022 "{0,0,0,0,0,0}" ));
! DATA(insert ( 2827 float8_regr_accum float8_covar_pop 0 1022 "{0,0,0,0,0,0}" ));
! DATA(insert ( 2828 float8_regr_accum float8_covar_samp 0 1022 "{0,0,0,0,0,0}" ));
! DATA(insert ( 2829 float8_regr_accum float8_corr 0 1022 "{0,0,0,0,0,0}" ));
/* boolean-and and boolean-or */
! DATA(insert ( 2517 booland_statefunc - 58 16 _null_ ));
! DATA(insert ( 2518 boolor_statefunc - 59 16 _null_ ));
! DATA(insert ( 2519 booland_statefunc - 58 16 _null_ ));
/* bitwise integer */
! DATA(insert ( 2236 int2and - 0 21 _null_ ));
! DATA(insert ( 2237 int2or - 0 21 _null_ ));
! DATA(insert ( 2238 int4and - 0 23 _null_ ));
! DATA(insert ( 2239 int4or - 0 23 _null_ ));
! DATA(insert ( 2240 int8and - 0 20 _null_ ));
! DATA(insert ( 2241 int8or - 0 20 _null_ ));
! DATA(insert ( 2242 bitand - 0 1560 _null_ ));
! DATA(insert ( 2243 bitor - 0 1560 _null_ ));
/* xml */
! DATA(insert ( 2901 xmlconcat2 - 0 142 _null_ ));
/* array */
! DATA(insert ( 2335 array_agg_transfn array_agg_finalfn 0 2281 _null_ ));
/* text */
! DATA(insert ( 3538 string_agg_transfn string_agg_finalfn 0 2281 _null_ ));
/* bytea */
! DATA(insert ( 3545 bytea_string_agg_transfn bytea_string_agg_finalfn 0 2281 _null_ ));
/* json */
! DATA(insert ( 3175 json_agg_transfn json_agg_finalfn 0 2281 _null_ ));
/*
* prototypes for functions in pg_aggregate.c
*/
--- 79,246 ----
*/
/* avg */
! DATA(insert ( 2100 int8_avg_accum numeric_avg 0 1231 f "{0,0}" ));
! DATA(insert ( 2101 int4_avg_accum int8_avg 0 1016 f "{0,0}" ));
! DATA(insert ( 2102 int2_avg_accum int8_avg 0 1016 f "{0,0}" ));
! DATA(insert ( 2103 numeric_avg_accum numeric_avg 0 1231 f "{0,0}" ));
! DATA(insert ( 2104 float4_accum float8_avg 0 1022 f "{0,0,0}" ));
! DATA(insert ( 2105 float8_accum float8_avg 0 1022 f "{0,0,0}" ));
! DATA(insert ( 2106 interval_accum interval_avg 0 1187 f "{0 second,0 second}" ));
/* sum */
! DATA(insert ( 2107 int8_sum - 0 1700 f _null_ ));
! DATA(insert ( 2108 int4_sum - 0 20 f _null_ ));
! DATA(insert ( 2109 int2_sum - 0 20 f _null_ ));
! DATA(insert ( 2110 float4pl - 0 700 f _null_ ));
! DATA(insert ( 2111 float8pl - 0 701 f _null_ ));
! DATA(insert ( 2112 cash_pl - 0 790 f _null_ ));
! DATA(insert ( 2113 interval_pl - 0 1186 f _null_ ));
! DATA(insert ( 2114 numeric_add - 0 1700 f _null_ ));
/* max */
! DATA(insert ( 2115 int8larger - 413 20 f _null_ ));
! DATA(insert ( 2116 int4larger - 521 23 f _null_ ));
! DATA(insert ( 2117 int2larger - 520 21 f _null_ ));
! DATA(insert ( 2118 oidlarger - 610 26 f _null_ ));
! DATA(insert ( 2119 float4larger - 623 700 f _null_ ));
! DATA(insert ( 2120 float8larger - 674 701 f _null_ ));
! DATA(insert ( 2121 int4larger - 563 702 f _null_ ));
! DATA(insert ( 2122 date_larger - 1097 1082 f _null_ ));
! DATA(insert ( 2123 time_larger - 1112 1083 f _null_ ));
! DATA(insert ( 2124 timetz_larger - 1554 1266 f _null_ ));
! DATA(insert ( 2125 cashlarger - 903 790 f _null_ ));
! DATA(insert ( 2126 timestamp_larger - 2064 1114 f _null_ ));
! DATA(insert ( 2127 timestamptz_larger - 1324 1184 f _null_ ));
! DATA(insert ( 2128 interval_larger - 1334 1186 f _null_ ));
! DATA(insert ( 2129 text_larger - 666 25 f _null_ ));
! DATA(insert ( 2130 numeric_larger - 1756 1700 f _null_ ));
! DATA(insert ( 2050 array_larger - 1073 2277 f _null_ ));
! DATA(insert ( 2244 bpchar_larger - 1060 1042 f _null_ ));
! DATA(insert ( 2797 tidlarger - 2800 27 f _null_ ));
! DATA(insert ( 3526 enum_larger - 3519 3500 f _null_ ));
/* min */
! DATA(insert ( 2131 int8smaller - 412 20 f _null_ ));
! DATA(insert ( 2132 int4smaller - 97 23 f _null_ ));
! DATA(insert ( 2133 int2smaller - 95 21 f _null_ ));
! DATA(insert ( 2134 oidsmaller - 609 26 f _null_ ));
! DATA(insert ( 2135 float4smaller - 622 700 f _null_ ));
! DATA(insert ( 2136 float8smaller - 672 701 f _null_ ));
! DATA(insert ( 2137 int4smaller - 562 702 f _null_ ));
! DATA(insert ( 2138 date_smaller - 1095 1082 f _null_ ));
! DATA(insert ( 2139 time_smaller - 1110 1083 f _null_ ));
! DATA(insert ( 2140 timetz_smaller - 1552 1266 f _null_ ));
! DATA(insert ( 2141 cashsmaller - 902 790 f _null_ ));
! DATA(insert ( 2142 timestamp_smaller - 2062 1114 f _null_ ));
! DATA(insert ( 2143 timestamptz_smaller - 1322 1184 f _null_ ));
! DATA(insert ( 2144 interval_smaller - 1332 1186 f _null_ ));
! DATA(insert ( 2145 text_smaller - 664 25 f _null_ ));
! DATA(insert ( 2146 numeric_smaller - 1754 1700 f _null_ ));
! DATA(insert ( 2051 array_smaller - 1072 2277 f _null_ ));
! DATA(insert ( 2245 bpchar_smaller - 1058 1042 f _null_ ));
! DATA(insert ( 2798 tidsmaller - 2799 27 f _null_ ));
! DATA(insert ( 3527 enum_smaller - 3518 3500 f _null_ ));
/* count */
! DATA(insert ( 2147 int8inc_any - 0 20 f "0" ));
! DATA(insert ( 2803 int8inc - 0 20 f "0" ));
/* var_pop */
! DATA(insert ( 2718 int8_accum numeric_var_pop 0 1231 f "{0,0,0}" ));
! DATA(insert ( 2719 int4_accum numeric_var_pop 0 1231 f "{0,0,0}" ));
! DATA(insert ( 2720 int2_accum numeric_var_pop 0 1231 f "{0,0,0}" ));
! DATA(insert ( 2721 float4_accum float8_var_pop 0 1022 f "{0,0,0}" ));
! DATA(insert ( 2722 float8_accum float8_var_pop 0 1022 f "{0,0,0}" ));
! DATA(insert ( 2723 numeric_accum numeric_var_pop 0 1231 f "{0,0,0}" ));
/* var_samp */
! DATA(insert ( 2641 int8_accum numeric_var_samp 0 1231 f "{0,0,0}" ));
! DATA(insert ( 2642 int4_accum numeric_var_samp 0 1231 f "{0,0,0}" ));
! DATA(insert ( 2643 int2_accum numeric_var_samp 0 1231 f "{0,0,0}" ));
! DATA(insert ( 2644 float4_accum float8_var_samp 0 1022 f "{0,0,0}" ));
! DATA(insert ( 2645 float8_accum float8_var_samp 0 1022 f "{0,0,0}" ));
! DATA(insert ( 2646 numeric_accum numeric_var_samp 0 1231 f "{0,0,0}" ));
/* variance: historical Postgres syntax for var_samp */
! DATA(insert ( 2148 int8_accum numeric_var_samp 0 1231 f "{0,0,0}" ));
! DATA(insert ( 2149 int4_accum numeric_var_samp 0 1231 f "{0,0,0}" ));
! DATA(insert ( 2150 int2_accum numeric_var_samp 0 1231 f "{0,0,0}" ));
! DATA(insert ( 2151 float4_accum float8_var_samp 0 1022 f "{0,0,0}" ));
! DATA(insert ( 2152 float8_accum float8_var_samp 0 1022 f "{0,0,0}" ));
! DATA(insert ( 2153 numeric_accum numeric_var_samp 0 1231 f "{0,0,0}" ));
/* stddev_pop */
! DATA(insert ( 2724 int8_accum numeric_stddev_pop 0 1231 f "{0,0,0}" ));
! DATA(insert ( 2725 int4_accum numeric_stddev_pop 0 1231 f "{0,0,0}" ));
! DATA(insert ( 2726 int2_accum numeric_stddev_pop 0 1231 f "{0,0,0}" ));
! DATA(insert ( 2727 float4_accum float8_stddev_pop 0 1022 f "{0,0,0}" ));
! DATA(insert ( 2728 float8_accum float8_stddev_pop 0 1022 f "{0,0,0}" ));
! DATA(insert ( 2729 numeric_accum numeric_stddev_pop 0 1231 f "{0,0,0}" ));
/* stddev_samp */
! DATA(insert ( 2712 int8_accum numeric_stddev_samp 0 1231 f "{0,0,0}" ));
! DATA(insert ( 2713 int4_accum numeric_stddev_samp 0 1231 f "{0,0,0}" ));
! DATA(insert ( 2714 int2_accum numeric_stddev_samp 0 1231 f "{0,0,0}" ));
! DATA(insert ( 2715 float4_accum float8_stddev_samp 0 1022 f "{0,0,0}" ));
! DATA(insert ( 2716 float8_accum float8_stddev_samp 0 1022 f "{0,0,0}" ));
! DATA(insert ( 2717 numeric_accum numeric_stddev_samp 0 1231 f "{0,0,0}" ));
/* stddev: historical Postgres syntax for stddev_samp */
! DATA(insert ( 2154 int8_accum numeric_stddev_samp 0 1231 f "{0,0,0}" ));
! DATA(insert ( 2155 int4_accum numeric_stddev_samp 0 1231 f "{0,0,0}" ));
! DATA(insert ( 2156 int2_accum numeric_stddev_samp 0 1231 f "{0,0,0}" ));
! DATA(insert ( 2157 float4_accum float8_stddev_samp 0 1022 f "{0,0,0}" ));
! DATA(insert ( 2158 float8_accum float8_stddev_samp 0 1022 f "{0,0,0}" ));
! DATA(insert ( 2159 numeric_accum numeric_stddev_samp 0 1231 f "{0,0,0}" ));
/* SQL2003 binary regression aggregates */
! DATA(insert ( 2818 int8inc_float8_float8 - 0 20 f "0" ));
! DATA(insert ( 2819 float8_regr_accum float8_regr_sxx 0 1022 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2820 float8_regr_accum float8_regr_syy 0 1022 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2821 float8_regr_accum float8_regr_sxy 0 1022 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2822 float8_regr_accum float8_regr_avgx 0 1022 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2823 float8_regr_accum float8_regr_avgy 0 1022 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2824 float8_regr_accum float8_regr_r2 0 1022 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2825 float8_regr_accum float8_regr_slope 0 1022 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2826 float8_regr_accum float8_regr_intercept 0 1022 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2827 float8_regr_accum float8_covar_pop 0 1022 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2828 float8_regr_accum float8_covar_samp 0 1022 f "{0,0,0,0,0,0}" ));
! DATA(insert ( 2829 float8_regr_accum float8_corr 0 1022 f "{0,0,0,0,0,0}" ));
/* boolean-and and boolean-or */
! DATA(insert ( 2517 booland_statefunc - 58 16 f _null_ ));
! DATA(insert ( 2518 boolor_statefunc - 59 16 f _null_ ));
! DATA(insert ( 2519 booland_statefunc - 58 16 f _null_ ));
/* bitwise integer */
! DATA(insert ( 2236 int2and - 0 21 f _null_ ));
! DATA(insert ( 2237 int2or - 0 21 f _null_ ));
! DATA(insert ( 2238 int4and - 0 23 f _null_ ));
! DATA(insert ( 2239 int4or - 0 23 f _null_ ));
! DATA(insert ( 2240 int8and - 0 20 f _null_ ));
! DATA(insert ( 2241 int8or - 0 20 f _null_ ));
! DATA(insert ( 2242 bitand - 0 1560 f _null_ ));
! DATA(insert ( 2243 bitor - 0 1560 f _null_ ));
/* xml */
! DATA(insert ( 2901 xmlconcat2 - 0 142 f _null_ ));
/* array */
! DATA(insert ( 2335 array_agg_transfn array_agg_finalfn 0 2281 f _null_ ));
/* text */
! DATA(insert ( 3538 string_agg_transfn string_agg_finalfn 0 2281 f _null_ ));
/* bytea */
! DATA(insert ( 3545 bytea_string_agg_transfn bytea_string_agg_finalfn 0 2281 f _null_ ));
/* json */
! DATA(insert ( 3175 json_agg_transfn json_agg_finalfn 0 2281 f _null_ ));
+ /* ordered set function */
+ DATA(insert ( 3931 - percentile_disc_final 0 0 t _null_));
+ DATA(insert ( 3935 - percentile_cont_final 0 0 t _null_));
+ DATA(insert ( 3939 - percentile_interval_cont_final 0 0 t _null_));
/*
* prototypes for functions in pg_aggregate.c
*/
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 4727,4732 **** DESCR("SP-GiST support for quad tree over range");
--- 4727,4752 ----
/* event triggers */
DATA(insert OID = 3566 ( pg_event_trigger_dropped_objects PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,25,25,25,25}" "{o,o,o,o,o,o,o}" "{classid, objid, objsubid, object_type, schema_name, object_name, object_identity}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
DESCR("list objects dropped by the current command");
+
+ /* inverse distribution functions */
+ DATA(insert OID = 3931 ( percentile_disc PGNSP PGUID 12 1 0 0 0 t f f f t f i 2 0 701 "701 701" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("percentile_disc");
+
+ DATA(insert OID = 3932 ( percentile_disc_final PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 701 "701" _null_ _null_ _null_ _null_ percentile_disc_final _null_ _null_ _null_ ));
+ DESCR("percentile_disc_final");
+
+ DATA(insert OID = 3935 ( percentile_cont PGNSP PGUID 12 1 0 0 0 t f f f t f i 2 0 701 "701 701" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("percentile_cont");
+
+ DATA(insert OID = 3936 ( percentile_cont_final PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 701 "701" _null_ _null_ _null_ _null_ percentile_cont_final _null_ _null_ _null_ ));
+ DESCR("percentile_cont_final");
+
+ DATA(insert OID = 3939 ( percentile_cont PGNSP PGUID 12 1 0 0 0 t f f f t f i 2 0 1186 "701 1186" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("percentile_interval_cont");
+
+ DATA(insert OID = 3940 ( percentile_interval_cont_final PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1186 "701" _null_ _null_ _null_ _null_ percentile_interval_cont_final _null_ _null_ _null_ ));
+ DESCR("percentile_interval_cont_final");
+
/*
* Symbolic values for provolatile column: these indicate whether the result
* of a function is dependent *only* on the values of its explicit arguments,
*** a/src/include/executor/nodeAgg.h
--- b/src/include/executor/nodeAgg.h
***************
*** 25,28 **** extern Size hash_agg_entry_size(int numAggs);
--- 25,31 ----
extern Datum aggregate_dummy(PG_FUNCTION_ARGS);
+ extern int64 AggSetGetRowCount(FunctionCallInfo fcinfo);
+
+
#endif /* NODEAGG_H */
*** a/src/include/fmgr.h
--- b/src/include/fmgr.h
***************
*** 647,655 **** extern void **find_rendezvous_variable(const char *varName);
--- 647,668 ----
/* AggCheckCallContext can return one of the following codes, or 0: */
#define AGG_CONTEXT_AGGREGATE 1 /* regular aggregate */
#define AGG_CONTEXT_WINDOW 2 /* window function */
+ #define AGG_CONTEXT_ORDERED 3 /* ordered set function */
extern int AggCheckCallContext(FunctionCallInfo fcinfo,
MemoryContext *aggcontext);
+ extern int64 AggSetGetRowCount(FunctionCallInfo fcinfo);
+
+ typedef struct Tuplesortstate fmTuplesortstate;
+ typedef struct tupleDesc *fmTupleDesc;
+ typedef struct TupleTableSlot fmTupleTableSlot;
+
+ extern void AggSetGetSortInfo(FunctionCallInfo fcinfo,
+ fmTuplesortstate **sortstate,
+ fmTupleDesc *tupdesc,
+ fmTupleTableSlot **tupslot,
+ Oid *datumtype);
+
/*
* We allow plugin modules to hook function entry/exit. This is intended
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
***************
*** 588,593 **** typedef struct AggrefExprState
--- 588,594 ----
{
ExprState xprstate;
List *args; /* states of argument expressions */
+ List *orddirectargs; /* Ordered direct arguments */
ExprState *aggfilter; /* FILTER expression */
int aggno; /* ID number for agg within its plan node */
} AggrefExprState;
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 425,431 **** typedef enum NodeTag
T_WindowObjectData, /* private in nodeWindowAgg.c */
T_TIDBitmap, /* in nodes/tidbitmap.h */
T_InlineCodeBlock, /* in nodes/parsenodes.h */
! T_FdwRoutine /* in foreign/fdwapi.h */
} NodeTag;
/*
--- 425,432 ----
T_WindowObjectData, /* private in nodeWindowAgg.c */
T_TIDBitmap, /* in nodes/tidbitmap.h */
T_InlineCodeBlock, /* in nodes/parsenodes.h */
! T_FdwRoutine, /* in foreign/fdwapi.h */
! T_AggStatePerAggData /* private in nodeAgg.c */
} NodeTag;
/*
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 303,308 **** typedef struct FuncCall
--- 303,309 ----
bool agg_star; /* argument was really '*' */
bool agg_distinct; /* arguments were labeled DISTINCT */
bool func_variadic; /* last argument was labeled VARIADIC */
+ bool has_within_group; /* WITHIN GROUP clause,if any */
struct WindowDef *over; /* OVER clause, if any */
int location; /* token location, or -1 if unknown */
} FuncCall;
*** a/src/include/nodes/primnodes.h
--- b/src/include/nodes/primnodes.h
***************
*** 247,254 **** typedef struct Aggref
--- 247,256 ----
List *args; /* arguments and sort expressions */
List *aggorder; /* ORDER BY (list of SortGroupClause) */
List *aggdistinct; /* DISTINCT (list of SortGroupClause) */
+ List *orddirectargs; /* Direct arguments for ordered set functions */
Expr *aggfilter; /* FILTER expression */
bool aggstar; /* TRUE if argument list was really '*' */
+ bool isordset; /* If node is from an ordered set function */
Index agglevelsup; /* > 0 if agg belongs to outer query */
int location; /* token location, or -1 if unknown */
} Aggref;
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
***************
*** 411,416 **** PG_KEYWORD("where", WHERE, RESERVED_KEYWORD)
--- 411,417 ----
PG_KEYWORD("whitespace", WHITESPACE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("window", WINDOW, RESERVED_KEYWORD)
PG_KEYWORD("with", WITH, RESERVED_KEYWORD)
+ PG_KEYWORD("within", WITHIN, UNRESERVED_KEYWORD)
PG_KEYWORD("without", WITHOUT, UNRESERVED_KEYWORD)
PG_KEYWORD("work", WORK, UNRESERVED_KEYWORD)
PG_KEYWORD("wrapper", WRAPPER, UNRESERVED_KEYWORD)
*** a/src/include/parser/parse_agg.h
--- b/src/include/parser/parse_agg.h
***************
*** 17,23 ****
extern void transformAggregateCall(ParseState *pstate, Aggref *agg,
List *args, List *aggorder,
! bool agg_distinct);
extern void transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
WindowDef *windef);
--- 17,23 ----
extern void transformAggregateCall(ParseState *pstate, Aggref *agg,
List *args, List *aggorder,
! bool agg_distinct, bool agg_within_group);
extern void transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
WindowDef *windef);
*** a/src/include/parser/parse_func.h
--- b/src/include/parser/parse_func.h
***************
*** 38,51 **** typedef enum
FUNCDETAIL_NORMAL, /* found a matching regular function */
FUNCDETAIL_AGGREGATE, /* found a matching aggregate function */
FUNCDETAIL_WINDOWFUNC, /* found a matching window function */
! FUNCDETAIL_COERCION /* it's a type coercion request */
} FuncDetailCode;
-
extern Node *ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
List *agg_order, Expr *agg_filter,
bool agg_star, bool agg_distinct, bool func_variadic,
! WindowDef *over, bool is_column, int location);
extern FuncDetailCode func_get_detail(List *funcname,
List *fargs, List *fargnames,
--- 38,51 ----
FUNCDETAIL_NORMAL, /* found a matching regular function */
FUNCDETAIL_AGGREGATE, /* found a matching aggregate function */
FUNCDETAIL_WINDOWFUNC, /* found a matching window function */
! FUNCDETAIL_COERCION, /* it's a type coercion request */
! FUNCDETAIL_ORDERED /* ordered function */
} FuncDetailCode;
extern Node *ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
List *agg_order, Expr *agg_filter,
bool agg_star, bool agg_distinct, bool func_variadic,
! bool agg_within_group, WindowDef *over, bool is_column, int location);
extern FuncDetailCode func_get_detail(List *funcname,
List *fargs, List *fargnames,
***************
*** 66,71 **** extern FuncCandidateList func_select_candidate(int nargs,
--- 66,72 ----
extern void make_fn_arguments(ParseState *pstate,
List *fargs,
+ List *agg_order,
Oid *actual_arg_types,
Oid *declared_arg_types);
*** a/src/test/regress/expected/aggregates.out
--- b/src/test/regress/expected/aggregates.out
***************
*** 1249,1251 **** select aggfns(distinct a,b,c order by a,c using ~<~,b) filter (where a > 1)
--- 1249,1307 ----
{"(2,2,bar)","(3,1,baz)"}
(1 row)
+ -- ordered set functions
+ select p, percentile_cont(p) within group (order by x::float8) from generate_series(1,5) x,
+ (values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p)
+ group by p order by p;
+ p | percentile_cont
+ ------+-----------------
+ 0 | 1
+ 0.1 | 1.4
+ 0.25 | 2
+ 0.4 | 2.6
+ 0.5 | 3
+ 0.6 | 3.4
+ 0.75 | 4
+ 0.9 | 4.6
+ 1 | 5
+ (9 rows)
+
+ select p, percentile_cont(p order by p) within group (order by x::float8)
+ from generate_series(1,5) x, (values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p)
+ group by p order by x;
+ ERROR: Cannot have multiple ORDER BY clauses with WITHIN GROUP
+ LINE 1: select p, percentile_cont(p order by p) within group (order ...
+ ^
+ select p, sum() within group (order by x::float8)
+ from generate_series(1,5) x,
+ (values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) group by p order by p;
+ ERROR: sum(double precision) is not an ordered set function
+ select p, percentile_cont(p,p) from generate_series(1,5) x,
+ (values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) group by p order by p;
+ ERROR: Ordered set function specified, but WITHIN GROUP not present
+ LINE 1: select p, percentile_cont(p,p) from generate_series(1,5) x,
+ ^
+ select percentile_cont(0.5) within group (order by b) from aggtest;
+ percentile_cont
+ ------------------
+ 53.4485001564026
+ (1 row)
+
+ select percentile_cont(0.5) within group (order by b),sum(b) from aggtest;
+ percentile_cont | sum
+ ------------------+---------
+ 53.4485001564026 | 431.773
+ (1 row)
+
+ select percentile_cont(0.5) within group (order by thousand) from tenk1;
+ percentile_cont
+ -----------------
+ 499.5
+ (1 row)
+
+ select percentile_disc(0.5) within group (order by thousand) from tenk1;
+ percentile_disc
+ -----------------
+ 499
+ (1 row)
+
*** a/src/test/regress/expected/opr_sanity.out
--- b/src/test/regress/expected/opr_sanity.out
***************
*** 700,708 **** SELECT * FROM funcdescs
-- **************** pg_aggregate ****************
-- Look for illegal values in pg_aggregate fields.
SELECT ctid, aggfnoid::oid
FROM pg_aggregate as p1
! WHERE aggfnoid = 0 OR aggtransfn = 0 OR aggtranstype = 0;
ctid | aggfnoid
------+----------
(0 rows)
--- 700,716 ----
-- **************** pg_aggregate ****************
-- Look for illegal values in pg_aggregate fields.
+ -- ordered set functions can't have transfns, and must
+ -- have finalfns, but may or may not have transtypes.
+ -- other aggs must have transfns and transtypes with
+ -- optional finalfns.
SELECT ctid, aggfnoid::oid
FROM pg_aggregate as p1
! WHERE aggfnoid = 0
! OR CASE WHEN aggisordsetfunc
! THEN aggtransfn <> 0 OR aggfinalfn = 0
! ELSE aggtransfn = 0 OR aggtranstype = 0
! END;
ctid | aggfnoid
------+----------
(0 rows)
***************
*** 764,771 **** WHERE a.aggfnoid = p.oid AND
a.aggfinalfn = pfn.oid AND
(pfn.proretset
OR NOT binary_coercible(pfn.prorettype, p.prorettype)
! OR pfn.pronargs != 1
! OR NOT binary_coercible(a.aggtranstype, pfn.proargtypes[0]));
aggfnoid | proname | oid | proname
----------+---------+-----+---------
(0 rows)
--- 772,780 ----
a.aggfinalfn = pfn.oid AND
(pfn.proretset
OR NOT binary_coercible(pfn.prorettype, p.prorettype)
! OR (aggisordsetfunc IS FALSE
! AND (pfn.pronargs != 1
! OR NOT binary_coercible(a.aggtranstype, pfn.proargtypes[0]))));
aggfnoid | proname | oid | proname
----------+---------+-----+---------
(0 rows)
*** a/src/test/regress/sql/aggregates.sql
--- b/src/test/regress/sql/aggregates.sql
***************
*** 480,482 **** select sum(unique1) FILTER (WHERE
--- 480,501 ----
select aggfns(distinct a,b,c order by a,c using ~<~,b) filter (where a > 1)
from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c),
generate_series(1,2) i;
+
+ -- ordered set functions
+
+ select p, percentile_cont(p) within group (order by x::float8) from generate_series(1,5) x,
+ (values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p)
+ group by p order by p;
+ select p, percentile_cont(p order by p) within group (order by x::float8)
+ from generate_series(1,5) x, (values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p)
+ group by p order by x;
+ select p, sum() within group (order by x::float8)
+ from generate_series(1,5) x,
+ (values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) group by p order by p;
+ select p, percentile_cont(p,p) from generate_series(1,5) x,
+ (values (0::float8),(0.1),(0.25),(0.4),(0.5),(0.6),(0.75),(0.9),(1)) v(p) group by p order by p;
+ select percentile_cont(0.5) within group (order by b) from aggtest;
+ select percentile_cont(0.5) within group (order by b),sum(b) from aggtest;
+ select percentile_cont(0.5) within group (order by thousand) from tenk1;
+ select percentile_disc(0.5) within group (order by thousand) from tenk1;
+
*** a/src/test/regress/sql/opr_sanity.sql
--- b/src/test/regress/sql/opr_sanity.sql
***************
*** 564,573 **** SELECT * FROM funcdescs
-- **************** pg_aggregate ****************
-- Look for illegal values in pg_aggregate fields.
SELECT ctid, aggfnoid::oid
FROM pg_aggregate as p1
! WHERE aggfnoid = 0 OR aggtransfn = 0 OR aggtranstype = 0;
-- Make sure the matching pg_proc entry is sensible, too.
--- 564,581 ----
-- **************** pg_aggregate ****************
-- Look for illegal values in pg_aggregate fields.
+ -- ordered set functions can't have transfns, and must
+ -- have finalfns, but may or may not have transtypes.
+ -- other aggs must have transfns and transtypes with
+ -- optional finalfns.
SELECT ctid, aggfnoid::oid
FROM pg_aggregate as p1
! WHERE aggfnoid = 0
! OR CASE WHEN aggisordsetfunc
! THEN aggtransfn <> 0 OR aggfinalfn = 0
! ELSE aggtransfn = 0 OR aggtranstype = 0
! END;
-- Make sure the matching pg_proc entry is sensible, too.
***************
*** 618,625 **** WHERE a.aggfnoid = p.oid AND
a.aggfinalfn = pfn.oid AND
(pfn.proretset
OR NOT binary_coercible(pfn.prorettype, p.prorettype)
! OR pfn.pronargs != 1
! OR NOT binary_coercible(a.aggtranstype, pfn.proargtypes[0]));
-- If transfn is strict then either initval should be non-NULL, or
-- input type should match transtype so that the first non-null input
--- 626,634 ----
a.aggfinalfn = pfn.oid AND
(pfn.proretset
OR NOT binary_coercible(pfn.prorettype, p.prorettype)
! OR (aggisordsetfunc IS FALSE
! AND (pfn.pronargs != 1
! OR NOT binary_coercible(a.aggtranstype, pfn.proargtypes[0]))));
-- If transfn is strict then either initval should be non-NULL, or
-- input type should match transtype so that the first non-null input
"some Salesforce folks" that would be me! It looks like I didn't quite
communicate to Tom just what I was looking for as I do indeed want to have
a variable number of "any" types, as:
CREATE AGGREGATE FOO ( ANYELEMENT, <more types>, VARIADIC "any") (
...
STYPE = ANYARRAY
...)
so the corresponding transition function would be
CREATE FUNCTION FOO_sfunc( ANYARRAY, ANYELEMENT, <more types>, VARIADIC
"any") RETURNS ANYARRAY
and the final func is
CREATE FUNCTION FOO_ffunc( ANYARRAY ) RETURNS ANYELEMENT
The functions are in C, and I cheat and actually use the ANYARRAY
transition variable as a struct just keeping the varlena length correct
(thanks to Tom for that idea). Currently I just support a fixed number of
"any" args but really need to have that be variable.
So supporting VARIADIC "any" for user defined aggregates would be most
useful.
On Thu, Jul 18, 2013 at 7:09 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Show quoted text
Noah Misch <noah@leadboat.com> writes:
(I don't know whether VARIADIC transition functions work today, but that
would
become an orthogonal project.)
Coincidentally enough, some Salesforce folk were asking me about allowing
VARIADIC aggregates just a few days ago. I experimented enough to find
out that if you make an array-accepting transition function, and then
force the aggregate's pg_proc entry to look like it's variadic (by
manually setting provariadic and some other fields), then everything
seems to Just Work: the parser and executor are both fine with it.
So I think all that's needed here is to add some syntax support to
CREATE AGGREGATE, and probably make some tweaks in pg_dump. I was
planning to go work on that sometime soon.Having said that, though, what Andrew seemed to want was VARIADIC ANY,
which is a *completely* different kettle of fish, since the actual
parameters can't be converted to an array. I'm not sure if that's
as easy to support.regards, tom lane
Ok, since Atri posted our work-so-far and there's not been much
comment, I'll outline here my proposed plan of attack.
Rather than, as in the WIP patch, using the agg finalfn to validate
the split between normal args and ORDER BY args, I propose this:
Firstly, as in the WIP patch,
func(a) within group (order by b)
is looked up as though it were func(a,b). The result must be marked as
an ordered set function. A new pg_aggregate integer column,
aggordnargs (?), must be equal to the number of normal args (i.e. (a)
in this case). Note that this may be 0; one can see a legitimate use
case for mode() within group (order by anyelement) for example.
The finalfn must be defined to have the same signature, so its args
are processed as if it were func_final(a,b) - but only a dummy arg is
passed for b. (Similar to the case for window functions.) Resolution
of polymorphic parameters and result types therefore works as normal.
For hypothetical set functions we add a special case, aggordnargs=-1,
for which both the aggregate and the finalfn must be defined as
(variadic "any") and parse analysis detects this case and unifies the
types of the normal args with those of the ORDER BY args.
I propose this new syntax:
create aggregate func(argtypes...) within group (argtypes...) (
[ STYPE = ... , ]
[ SORTOP = ... , ]
[ INITCOND = ... , ]
FINALFUNC = func_final
);
Ordered set functions will typically not need STYPE etc., but
hypothetical set functions will be declared as, e.g.:
create aggregate rank(variadic "any") within group (variadic "any") (
STYPE = boolean,
INITCOND = 'f',
SORTOP = >,
FINALFUNC = rank_hypothetical_final
);
(I'm open to comment as to whether to simply overload the aggsortop
column in pg_aggregate or add a new one. I'm inclined to do the latter.)
The idea here is that a column of type STYPE will be appended to the
list of columns to be sorted, using SORTOP as sort operator, and all
input rows will have this column initialized to the INITCOND value.
This is to make it easy to implement rank() and friends by simply
inserting the hypothetical row into the sort, with a true value to
flag it, and finding its position in the sort result. (Better that
than comparing the hypothetical row against the whole group.)
(Security caveat: it will be necessary for the finalfn in such cases
to validate that the additional column exists with the right
type. Producing the wrong result is acceptable if the values in it are
unexpected; crashing is not.)
Any comment before we get back to coding?
--
Andrew (irc:RhodiumToad)
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Tue, Jul 23, 2013 at 01:21:52AM +0000, Andrew Gierth wrote:
For hypothetical set functions we add a special case, aggordnargs=-1,
for which both the aggregate and the finalfn must be defined as
(variadic "any") and parse analysis detects this case and unifies the
types of the normal args with those of the ORDER BY args.
Other aggregates based on this syntax might not desire such type unification.
Having parse analysis do that distorts the character of an "any" argument. I
think the proper place for such processing is the first call to a transition
function. The transition functions could certainly call a new API exposed
under src/backend/parser to do the heavy lifting. But let's not make the
parser presume that an aggordnargs=-1 aggregate always wants its "any"
arguments handled in the manner of the standard hypothetical set functions.
The rest of the plan looks good so far.
--
Noah Misch
EnterpriseDB http://www.enterprisedb.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Noah Misch said:
Other aggregates based on this syntax might not desire such type unification.
Then there would have to be some way to distinguish that. Maybe those could
have -1 and the standard hypothetical set functions -2, with some flag in
CREATE AGGREGATE to sort it out.
Having parse analysis do that distorts the character of an "any" argument. I
think the proper place for such processing is the first call to a transition
function.
Except there isn't one.
But let's not make the
parser presume that an aggordnargs=-1 aggregate always wants its "any"
arguments handled in the manner of the standard hypothetical set functions.
This has to happen in the parser because these are errors that should be
caught before execution:
rank(foo) within group (order by bar,baz)
rank(integercol) within group (order by textcol)
And collations have to be resolved (pairwise) before sorting can happen:
rank(textval COLLATE "C") within group (order by foo) -- sorts in "C"
rank(textval COLLATE "C") within group (order by bar COLLATE "en_US") -- error
(basically, in rank(x) within group (order by y) where x and y are
collatable, the collation rules apply exactly as though you were doing
(x < y), with all the implicit vs. explicit stuff included)
And this:
rank(1.1) within group (order by intcol)
should become rank(1.1) within group (order by intcol::numeric)
--
Andrew (irc:RhodiumToad)
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Jul 24, 2013 at 04:16:28AM +0000, Andrew Gierth wrote:
Noah Misch said:
Other aggregates based on this syntax might not desire such type unification.
Then there would have to be some way to distinguish that. Maybe those could
have -1 and the standard hypothetical set functions -2, with some flag in
CREATE AGGREGATE to sort it out.
Sure.
But let's not make the
parser presume that an aggordnargs=-1 aggregate always wants its "any"
arguments handled in the manner of the standard hypothetical set functions.This has to happen in the parser because these are errors that should be
caught before execution:rank(foo) within group (order by bar,baz)
rank(integercol) within group (order by textcol)
Would be nice to have, but not an overriding concern.
And collations have to be resolved (pairwise) before sorting can happen:
rank(textval COLLATE "C") within group (order by foo) -- sorts in "C"
rank(textval COLLATE "C") within group (order by bar COLLATE "en_US") -- error(basically, in rank(x) within group (order by y) where x and y are
collatable, the collation rules apply exactly as though you were doing
(x < y), with all the implicit vs. explicit stuff included)
This, though, makes direct parser support nigh inevitable.
The issue to resolve here is whether and to what extent we should implement
the SQL-standard hypothetical set functions as special cases of some
more-generic concept. Here's the declaration you proposed for the rank() HSF:
create aggregate rank(variadic "any") within group (variadic "any") (
This would be the first place to my knowledge where "any" doesn't mean
unrestricted type acceptance at the parser level. If we need bespoke syntax
declaring a function as an HSF with the entailed call behavior nuances, fine.
Treating a declaration with this particular mix of "any" as a secret handshake
requesting those nuances is not the way to go.
STYPE = boolean,
INITCOND = 'f',
SORTOP = >,
FINALFUNC = rank_hypothetical_final
);
--
Noah Misch
EnterpriseDB http://www.enterprisedb.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Fri, 2013-07-19 at 21:29 +0530, Atri Sharma wrote:
Hi all,
This is our current work-in-progress patch for WITHIN GROUP.
Please fix these compiler warnings:
parse_agg.c: In function ‘check_ungrouped_columns_walker’:
parse_agg.c:848:3: warning: passing argument 1 of ‘check_ungrouped_columns_walker’ from incompatible pointer type [enabled by default]
parse_agg.c:822:1: note: expected ‘struct Node *’ but argument is of type ‘struct List *’
parse_func.c: In function ‘make_fn_arguments’:
parse_func.c:1540:9: warning: assignment from incompatible pointer type [enabled by default]
parse_func.c:1547:15: warning: assignment from incompatible pointer type [enabled by default]
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 7/19/13 11:59 AM, Atri Sharma wrote:
Hi all,
This is our current work-in-progress patch for WITHIN GROUP.
This patch needs to be rebased.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Sent from my iPad
On 04-Sep-2013, at 21:38, Peter Eisentraut <peter_e@gmx.net> wrote:
On 7/19/13 11:59 AM, Atri Sharma wrote:
Hi all,
This is our current work-in-progress patch for WITHIN GROUP.
This patch needs to be rebased.
This version of patch is quite old.We will be sending an updated patch before the start of September commitfest, with all the points you mentioned taken care of.
Thanks for the points.
Regards,
Atri
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Sep 4, 2013 at 10:01 PM, Atri Sharma <atri.jiit@gmail.com> wrote:
Sent from my iPad
On 04-Sep-2013, at 21:38, Peter Eisentraut <peter_e@gmx.net> wrote:
On 7/19/13 11:59 AM, Atri Sharma wrote:
Hi all,
This is our current work-in-progress patch for WITHIN GROUP.
This patch needs to be rebased.
Hi All,
This is our complete patch for implementation of WITHIN GROUP.
Functions supported:
percentile_disc
percentile_cont for float8 and intervals
percentile_disc and percentile_cont support arrays of percentiles as well.
mode
rank
dense_rank
percent_rank
cume_dist
The patch also adds support for user defined ordered set functions
with CREATE AGGREGATE.
Polymorphism is now supported, with the original gripes about it now
solved. Essentially, we have added
a new field in pg_aggregate, aggordnargs, which we use it to verify,
having looked up the function, that it is being called correctly.
aggordnargs holds the number of direct args to the aggregate.
Hypothetical set functions build over the extension of VARIADIC, and
all of the hypothetical set functions
have variadic 'any' as their parameter types.
Need review:
1) psql /df and /dfa output.
2) Handling of non hypothetical collations.
3) Need of mode(), and the name.
Feedback/Comments?
Regards,
Atri
Attachments:
withingrouppatchcontextv1.patchapplication/octet-stream; name=withingrouppatchcontextv1.patchDownload
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 352,358 ****
<entry><structfield>aggtransfn</structfield></entry>
<entry><type>regproc</type></entry>
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
! <entry>Transition function</entry>
</row>
<row>
<entry><structfield>aggfinalfn</structfield></entry>
--- 352,358 ----
<entry><structfield>aggtransfn</structfield></entry>
<entry><type>regproc</type></entry>
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
! <entry>Transition function (zero if none)</entry>
</row>
<row>
<entry><structfield>aggfinalfn</structfield></entry>
***************
*** 370,376 ****
<entry><structfield>aggtranstype</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
! <entry>Data type of the aggregate function's internal transition (state) data</entry>
</row>
<row>
<entry><structfield>agginitval</structfield></entry>
--- 370,394 ----
<entry><structfield>aggtranstype</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
! <entry>Data type of the aggregate function's internal transition (state) data (zero if none)</entry>
! </row>
! <row>
! <entry><structfield>aggtranssortop</structfield></entry>
! <entry><type>oid</type></entry>
! <entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</literal></entry>
! <entry>An optional sort operator for the type "aggtranstype", used for some kinds of ordered set functions</entry>
! </row>
! <row>
! <entry><structfield>aggordnargs</structfield></entry>
! <entry><type>int4</type></entry>
! <entry></entry>
! <entry>Number of direct arguments to ordered set function; -2 for hypothetical set functions; -1 for ordinary aggregates.</entry>
! </row>
! <row>
! <entry><structfield>aggisordsetfunc</structfield></entry>
! <entry><type>bool</type></entry>
! <entry></entry>
! <entry>A flag to represent whether a function is ordered set or not</entry>
</row>
<row>
<entry><structfield>agginitval</structfield></entry>
*** 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 ----
</sect1>
+ <sect1 id="functions-ordered">
+ <title>Ordered Set Functions</title>
+
+ <indexterm zone="functions-ordered">
+ <primary>ordered set function</primary>
+ <secondary>built-in</secondary>
+ </indexterm>
+
+ <para>
+ <firstterm>Ordered set functions</firstterm> compute a single result
+ from an ordered set of input values. The built-in ordered set functions
+ are listed in
+ <xref linkend="functions-inversedist-table"> and
+ <xref linkend="functions-hypothetical-table">.
+ The special syntax considerations for ordered set functions
+ are explained in <xref linkend="syntax-orderedset">.
+ </para>
+
+ <table id="functions-inversedist-table">
+ <title>Inverse Distribution Functions</title>
+
+ <tgroup cols="5">
+ <thead>
+ <row>
+ <entry>Function</entry>
+ <entry>Direct Argument Type(s)</entry>
+ <entry>Ordered Argument Type(s)</entry>
+ <entry>Return Type</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>percentile</primary>
+ <secondary>discrete</secondary>
+ </indexterm>
+ <function>percentile_disc(<replaceable class="parameter">fraction</replaceable>) WITHIN GROUP (ORDER BY <replaceable class="parameter">sort_expression</replaceable>)</function>
+ </entry>
+ <entry>
+ <type>double precision</type> (must be [0..1])
+ </entry>
+ <entry>
+ any sortable type
+ </entry>
+ <entry>
+ same as sort expression
+ </entry>
+ <entry>
+ discrete percentile; returns the first result whose position in
+ the ordering equals or exceeds the specified fraction
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>percentile</primary>
+ <secondary>discrete</secondary>
+ </indexterm>
+ <function>percentile_disc(<replaceable class="parameter">fractions</replaceable>) WITHIN GROUP (ORDER BY <replaceable class="parameter">sort_expression</replaceable>)</function>
+ </entry>
+ <entry>
+ <type>double precision[]</type> (all must be [0..1] or null)
+ </entry>
+ <entry>
+ any sortable type
+ </entry>
+ <entry>
+ array of input type
+ </entry>
+ <entry>
+ multiple discrete percentile; returns an array of results matching the
+ shape of the <literal>fractions</literal> parameter, with each
+ non-null element replaced by the input value at that percentile
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>percentile</primary>
+ <secondary>continuous</secondary>
+ </indexterm>
+ <indexterm>
+ <primary>median</primary>
+ </indexterm>
+ <function>percentile_cont(<replaceable class="parameter">fraction</replaceable>) WITHIN GROUP (ORDER BY <replaceable class="parameter">sort_expression</replaceable>)</function>
+ </entry>
+ <entry>
+ <type>double precision</type> (must be [0..1])
+ </entry>
+ <entry>
+ <type>double precision</type> or <type>interval</type>
+ </entry>
+ <entry>
+ same as sort expression
+ </entry>
+ <entry>
+ continuous percentile; interpolates between adjacent items.
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>percentile</primary>
+ <secondary>continuous</secondary>
+ </indexterm>
+ <function>percentile_cont(<replaceable class="parameter">fractions</replaceable>) WITHIN GROUP (ORDER BY <replaceable class="parameter">sort_expression</replaceable>)</function>
+ </entry>
+ <entry>
+ <type>double precision[]</type> (all must be [0..1] or null)
+ </entry>
+ <entry>
+ <type>double precision</type> or <type>interval</type>
+ </entry>
+ <entry>
+ array of input type
+ </entry>
+ <entry>
+ multiple continuous percentile; returns an array of results matching
+ the shape of the <literal>fractions</literal> parameter, with each
+ non-null element replaced by the value corresponding to that percentile
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>mode</primary>
+ <secondary>statistical</secondary>
+ </indexterm>
+ <function>mode() WITHIN GROUP (ORDER BY <replaceable class="parameter">sort_expression</replaceable>)</function>
+ </entry>
+ <entry>
+ </entry>
+ <entry>
+ any sortable type
+ </entry>
+ <entry>
+ same as sort expression
+ </entry>
+ <entry>
+ returns the most frequent input value (choosing one arbitrarily if
+ there are multiple equally good result)
+ </entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ All the inverse distribution functions ignore null values in their sorted
+ input. The <replaceable>fraction</replaceable> parameter must be between 0
+ and 1; an error is thrown if not. However, a null fraction simply produces
+ a null result.
+ </para>
+
+ <table id="functions-hypothetical-table">
+ <title>Hypothetical Set Functions</title>
+
+ <tgroup cols="3">
+ <thead>
+ <row>
+ <entry>Function</entry>
+ <entry>Return Type</entry>
+ </row>
+ </thead>
+
+ <tbody>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>rank</primary>
+ <secondary>hypothetical</secondary>
+ </indexterm>
+ <function>rank(<replaceable class="parameter">args</replaceable>) WITHIN GROUP (ORDER BY <replaceable class="parameter">sorted_args</replaceable>)</function>
+ </entry>
+ <entry>
+ <type>bigint</type>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>dense_rank</primary>
+ <secondary>hypothetical</secondary>
+ </indexterm>
+ <function>dense_rank(<replaceable class="parameter">args</replaceable>) WITHIN GROUP (ORDER BY <replaceable class="parameter">sorted_args</replaceable>)</function>
+ </entry>
+ <entry>
+ <type>bigint</type>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>percent_rank</primary>
+ <secondary>hypothetical</secondary>
+ </indexterm>
+ <function>percent_rank(<replaceable class="parameter">args</replaceable>) WITHIN GROUP (ORDER BY <replaceable class="parameter">sorted_args</replaceable>)</function>
+ </entry>
+ <entry>
+ <type>double precision</type>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>cume_dist</primary>
+ <secondary>hypothetical</secondary>
+ </indexterm>
+ <function>cume_dist(<replaceable class="parameter">args</replaceable>) WITHIN GROUP (ORDER BY <replaceable class="parameter">sorted_args</replaceable>)</function>
+ </entry>
+ <entry>
+ <type>double precision</type>
+ </entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ For all hypothetical set functions, the list of arguments given
+ by <replaceable>args</replaceable> should match the number and types of
+ arguments given as <replaceable>sorted_args</replaceable>.
+ </para>
+
+ <para>
+ All of the functions listed in
+ <xref linkend="functions-hypothetical-table"> are associated with a
+ window function defined in
+ <xref linkend="functions-window">. In each case, the function result
+ represents the value that the associated window function would have
+ returned, for the hypothetical row constructed from
+ <replaceable>args</replaceable> and included in the sorted group of
+ rows.
+ </para>
+
+ </sect1>
+
<sect1 id="functions-window">
<title>Window Functions</title>
*** a/doc/src/sgml/ref/alter_aggregate.sgml
--- b/doc/src/sgml/ref/alter_aggregate.sgml
***************
*** 21,32 **** PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
! ALTER AGGREGATE <replaceable>name</replaceable> ( [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
RENAME TO <replaceable>new_name</replaceable>
ALTER AGGREGATE <replaceable>name</replaceable> ( [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
OWNER TO <replaceable>new_owner</replaceable>
ALTER AGGREGATE <replaceable>name</replaceable> ( [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
SET SCHEMA <replaceable>new_schema</replaceable>
</synopsis>
</refsynopsisdiv>
--- 21,37 ----
<refsynopsisdiv>
<synopsis>
! ALTER AGGREGATE
RENAME TO <replaceable>new_name</replaceable>
ALTER AGGREGATE <replaceable>name</replaceable> ( [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
OWNER TO <replaceable>new_owner</replaceable>
ALTER AGGREGATE <replaceable>name</replaceable> ( [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
SET SCHEMA <replaceable>new_schema</replaceable>
+
+ <phrase>where <replaceable>aggregate_signature</replaceable> is one of:</phrase>
+
+ <replaceable>name</replaceable> ( * | [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
+ <replaceable>name</replaceable> ( [ [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] ] ) WITHIN GROUP ( * | [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
</synopsis>
</refsynopsisdiv>
***************
*** 148,157 **** ALTER AGGREGATE myavg(integer) OWNER TO joe;
</para>
<para>
! To move the aggregate function <literal>myavg</literal> for type
! <type>integer</type> into schema <literal>myschema</literal>:
<programlisting>
! ALTER AGGREGATE myavg(integer) SET SCHEMA myschema;
</programlisting></para>
</refsect1>
--- 153,163 ----
</para>
<para>
! To move the ordered set function <literal>mypercentile</literal> with
! direct argument of type <type>float8</type> taking groups
! of <type>integer</type> type into schema <literal>myschema</literal>:
<programlisting>
! ALTER AGGREGATE mypercentile(float8) WITHIN GROUP (integer) SET SCHEMA myschema;
</programlisting></para>
</refsect1>
*** a/doc/src/sgml/ref/alter_extension.sgml
--- b/doc/src/sgml/ref/alter_extension.sgml
***************
*** 30,36 **** ALTER EXTENSION <replaceable class="PARAMETER">name</replaceable> DROP <replacea
<phrase>where <replaceable class="PARAMETER">member_object</replaceable> is:</phrase>
! AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) |
CAST (<replaceable>source_type</replaceable> AS <replaceable>target_type</replaceable>) |
COLLATION <replaceable class="PARAMETER">object_name</replaceable> |
CONVERSION <replaceable class="PARAMETER">object_name</replaceable> |
--- 30,36 ----
<phrase>where <replaceable class="PARAMETER">member_object</replaceable> is:</phrase>
! AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) [ WITHIN GROUP ( * | [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) ] |
CAST (<replaceable>source_type</replaceable> AS <replaceable>target_type</replaceable>) |
COLLATION <replaceable class="PARAMETER">object_name</replaceable> |
CONVERSION <replaceable class="PARAMETER">object_name</replaceable> |
*** a/doc/src/sgml/ref/comment.sgml
--- b/doc/src/sgml/ref/comment.sgml
***************
*** 23,29 **** PostgreSQL documentation
<synopsis>
COMMENT ON
{
! AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) |
CAST (<replaceable>source_type</replaceable> AS <replaceable>target_type</replaceable>) |
COLLATION <replaceable class="PARAMETER">object_name</replaceable> |
COLUMN <replaceable class="PARAMETER">relation_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
--- 23,29 ----
<synopsis>
COMMENT ON
{
! AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) [ WITHIN GROUP ( * | [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) ] |
CAST (<replaceable>source_type</replaceable> AS <replaceable>target_type</replaceable>) |
COLLATION <replaceable class="PARAMETER">object_name</replaceable> |
COLUMN <replaceable class="PARAMETER">relation_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
*** a/doc/src/sgml/ref/create_aggregate.sgml
--- b/doc/src/sgml/ref/create_aggregate.sgml
***************
*** 29,34 **** CREATE AGGREGATE <replaceable class="parameter">name</replaceable> ( [ <replacea
--- 29,43 ----
[ , SORTOP = <replaceable class="PARAMETER">sort_operator</replaceable> ]
)
+ CREATE AGGREGATE <replaceable class="parameter">name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">arg_name</replaceable> ] <replaceable class="parameter">arg_data_type</replaceable> [ , ... ] ) WITHIN GROUP ( * | [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">arg_name</replaceable> ] <replaceable class="parameter">arg_data_type</replaceable> [ , ... ] ) (
+ FINALFUNC = <replaceable class="PARAMETER">ffunc</replaceable>
+ [ , STRICT ]
+ [ , HYPOTHETICAL ]
+ [ , STYPE = <replaceable class="PARAMETER">state_data_type</replaceable> ]
+ [ , INITCOND = <replaceable class="PARAMETER">initial_condition</replaceable> ]
+ [ , TRANSSORTOP = <replaceable class="PARAMETER">state_sort_operator</replaceable> ]
+ )
+
<phrase>or the old syntax</phrase>
CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> (
***************
*** 70,76 **** CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> (
</para>
<para>
! An aggregate function is made from one or two ordinary
functions:
a state transition function
<replaceable class="PARAMETER">sfunc</replaceable>,
--- 79,85 ----
</para>
<para>
! An ordinary aggregate function is made from one or two ordinary
functions:
a state transition function
<replaceable class="PARAMETER">sfunc</replaceable>,
***************
*** 165,170 **** SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
--- 174,187 ----
</para>
<para>
+ The <literal>WITHIN GROUP</literal> syntax denotes a special subset of
+ aggregate functions collectively called <quote>ordered set
+ functions</quote>. 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.
+ </para>
+
+ <para>
To be able to create an aggregate function, you must
have <literal>USAGE</literal> privilege on the argument types, the state
type, and the return type, as well as <literal>EXECUTE</literal> privilege
***************
*** 278,283 **** SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
--- 295,305 ----
aggregate's result, and the return type is <replaceable
class="PARAMETER">state_data_type</replaceable>.
</para>
+ <para>
+ For ordered set functions, the function arguments must instead
+ correspond to the input arguments (both direct and grouped) plus
+ the state type if any.
+ </para>
</listitem>
</varlistentry>
***************
*** 305,310 **** SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
--- 327,365 ----
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">state_sort_operator</replaceable></term>
+ <listitem>
+ <para>
+ For ordered set functions only, this is a sort operator that can be
+ applied to
+ the <replaceable class="PARAMETER">state_data_type</replaceable>.
+ This is just an operator name (possibly schema-qualified).
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>STRICT</literal></term>
+ <listitem>
+ <para>
+ For ordered set functions only, this flag specifies that the function is
+ strict, i.e. that grouped rows containing nulls are skipped.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>HYPOTHETICAL</literal></term>
+ <listitem>
+ <para>
+ For ordered set functions only, this flag specifies that the aggregate
+ parameters are to be processed according to the requirements for
+ hypothetical set functions.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
<para>
*** a/doc/src/sgml/ref/drop_aggregate.sgml
--- b/doc/src/sgml/ref/drop_aggregate.sgml
***************
*** 21,29 **** PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
! DROP AGGREGATE [ IF EXISTS ]
! <replaceable class="parameter">name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">arg_name</replaceable> ] <replaceable class="parameter">arg_data_type</replaceable> [ , ... ] )
[ CASCADE | RESTRICT ]
</synopsis>
</refsynopsisdiv>
--- 21,33 ----
<refsynopsisdiv>
<synopsis>
! DROP AGGREGATE [ IF EXISTS ] <replaceable class="parameter">aggregate_signature</replaceable>
[ CASCADE | RESTRICT ]
+
+ <phrase>where <replaceable>aggregate_signature</replaceable> is one of:</phrase>
+
+ <replaceable>name</replaceable> ( * | [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
+ <replaceable>name</replaceable> ( [ [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] ] ) WITHIN GROUP ( * | [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
</synopsis>
</refsynopsisdiv>
*** a/doc/src/sgml/ref/security_label.sgml
--- b/doc/src/sgml/ref/security_label.sgml
***************
*** 25,31 **** SECURITY LABEL [ FOR <replaceable class="PARAMETER">provider</replaceable> ] ON
{
TABLE <replaceable class="PARAMETER">object_name</replaceable> |
COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
! AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) |
DATABASE <replaceable class="PARAMETER">object_name</replaceable> |
DOMAIN <replaceable class="PARAMETER">object_name</replaceable> |
EVENT TRIGGER <replaceable class="PARAMETER">object_name</replaceable> |
--- 25,31 ----
{
TABLE <replaceable class="PARAMETER">object_name</replaceable> |
COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
! AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) [ WITHIN GROUP ( * | [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) ] |
DATABASE <replaceable class="PARAMETER">object_name</replaceable> |
DOMAIN <replaceable class="PARAMETER">object_name</replaceable> |
EVENT TRIGGER <replaceable class="PARAMETER">object_name</replaceable> |
*** 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 ----
</para>
</sect2>
+ <sect2 id="syntax-orderedset">
+ <title>Ordered Set Functions</title>
+
+ <indexterm zone="syntax-orderedset">
+ <primary>ordered set function</primary>
+ </indexterm>
+
+ <indexterm zone="syntax-orderedset">
+ <primary>aggregate function</primary>
+ <secondary>ordered set function</secondary>
+ </indexterm>
+
+ <indexterm zone="syntax-orderedset">
+ <primary>WITHIN GROUP</primary>
+ </indexterm>
+
+ <para>
+ An <firstterm>ordered set function</firstterm> 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:
+
+ <synopsis>
+ <replaceable>function_name</replaceable> ( [ <replaceable>expression</replaceable> [ , ... ] ] ) WITHIN GROUP ( <replaceable>order_by_clause</replaceable> ) [ FILTER ( WHERE <replaceable>filter_clause</replaceable> ) ]
+ </synopsis>
+
+ where <replaceable>function_name</replaceable> is a previously
+ defined ordered set function (possibly qualified with a schema name) and
+ <replaceable>expression</replaceable> 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 <replaceable>order_by_clause</replaceable> has the same syntax
+ as for a query-level <literal>ORDER BY</> clause, as described
+ in <xref linkend="queries-order">, except that its expressions are always
+ just expressions and cannot be output-column names or numbers. The
+ expressions of the <replaceable>order_by_clause</replaceable> may, and
+ almost invariably do, refer to the ungrouped columns of the input; the
+ clause defines the grouped input to the function. The optional
+ <replaceable>filter_clause</replaceable> is identical to that for
+ aggregate functions (see <xref linkend="syntax-aggregates">, and is applied
+ to input rows prior to the sort operation.
+ </para>
+
+ </sect2>
+
<sect2 id="syntax-window-functions">
<title>Window Function Calls</title>
*** a/doc/src/sgml/xaggr.sgml
--- b/doc/src/sgml/xaggr.sgml
***************
*** 9,28 ****
</indexterm>
<para>
! Aggregate functions in <productname>PostgreSQL</productname>
! are expressed in terms of <firstterm>state values</firstterm>
! and <firstterm>state transition functions</firstterm>.
! 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 <firstterm>final function</firstterm>
! 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.
</para>
<para>
--- 9,25 ----
</indexterm>
<para>
! Aggregate functions (other than ordered set functions)
! in <productname>PostgreSQL</productname> are expressed in terms
! of <firstterm>state values</firstterm> and <firstterm>state transition
! functions</firstterm>. 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 <firstterm>final function</firstterm> 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.
</para>
<para>
*** 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 <str> opt_existing_window_name
%type <boolean> opt_if_not_exists
%type <node> filter_clause
+ %type <list> within_group_clause
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
***************
*** 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 <string.h>
+ #include <math.h>
+
+ #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 <string.h>
+ #include <math.h>
+
+ #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.
On Fri, 2013-09-13 at 14:56 +0530, Atri Sharma wrote:
This is our complete patch for implementation of WITHIN GROUP.
Please fix compiler warnings:
inversedistribution.c: In function ‘mode_final’:
inversedistribution.c:276:11: warning: ‘mode_val’ may be used uninitialized in this function [-Wmaybe-uninitialized]
inversedistribution.c:299:8: warning: ‘last_val’ may be used uninitialized in this function [-Wmaybe-uninitialized]
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
"Peter" == Peter Eisentraut <peter_e@gmx.net> writes:
Peter> Please fix compiler warnings:
Someone should do the same in WaitForBackgroundWorkerStartup so that
building with -Werror works.
New patch coming shortly.
--
Andrew.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
"Peter" == Peter Eisentraut <peter_e@gmx.net> writes:
Peter> Please fix compiler warnings:
Done.
--
Andrew (irc:RhodiumToad)
Attachments:
ordset-20130914.patchtext/x-patchDownload
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 352,358 ****
<entry><structfield>aggtransfn</structfield></entry>
<entry><type>regproc</type></entry>
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
! <entry>Transition function</entry>
</row>
<row>
<entry><structfield>aggfinalfn</structfield></entry>
--- 352,358 ----
<entry><structfield>aggtransfn</structfield></entry>
<entry><type>regproc</type></entry>
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
! <entry>Transition function (zero if none)</entry>
</row>
<row>
<entry><structfield>aggfinalfn</structfield></entry>
***************
*** 370,376 ****
<entry><structfield>aggtranstype</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
! <entry>Data type of the aggregate function's internal transition (state) data</entry>
</row>
<row>
<entry><structfield>agginitval</structfield></entry>
--- 370,394 ----
<entry><structfield>aggtranstype</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
! <entry>Data type of the aggregate function's internal transition (state) data (zero if none)</entry>
! </row>
! <row>
! <entry><structfield>aggtranssortop</structfield></entry>
! <entry><type>oid</type></entry>
! <entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</literal></entry>
! <entry>An optional sort operator for the type "aggtranstype", used for some kinds of ordered set functions</entry>
! </row>
! <row>
! <entry><structfield>aggordnargs</structfield></entry>
! <entry><type>int4</type></entry>
! <entry></entry>
! <entry>Number of direct arguments to ordered set function; -2 for hypothetical set functions; -1 for ordinary aggregates.</entry>
! </row>
! <row>
! <entry><structfield>aggisordsetfunc</structfield></entry>
! <entry><type>bool</type></entry>
! <entry></entry>
! <entry>A flag to represent whether a function is ordered set or not</entry>
</row>
<row>
<entry><structfield>agginitval</structfield></entry>
*** 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 ----
</sect1>
+ <sect1 id="functions-ordered">
+ <title>Ordered Set Functions</title>
+
+ <indexterm zone="functions-ordered">
+ <primary>ordered set function</primary>
+ <secondary>built-in</secondary>
+ </indexterm>
+
+ <para>
+ <firstterm>Ordered set functions</firstterm> compute a single result
+ from an ordered set of input values. The built-in ordered set functions
+ are listed in
+ <xref linkend="functions-inversedist-table"> and
+ <xref linkend="functions-hypothetical-table">.
+ The special syntax considerations for ordered set functions
+ are explained in <xref linkend="syntax-orderedset">.
+ </para>
+
+ <table id="functions-inversedist-table">
+ <title>Inverse Distribution Functions</title>
+
+ <tgroup cols="5">
+ <thead>
+ <row>
+ <entry>Function</entry>
+ <entry>Direct Argument Type(s)</entry>
+ <entry>Ordered Argument Type(s)</entry>
+ <entry>Return Type</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>percentile</primary>
+ <secondary>discrete</secondary>
+ </indexterm>
+ <function>percentile_disc(<replaceable class="parameter">fraction</replaceable>) WITHIN GROUP (ORDER BY <replaceable class="parameter">sort_expression</replaceable>)</function>
+ </entry>
+ <entry>
+ <type>double precision</type> (must be [0..1])
+ </entry>
+ <entry>
+ any sortable type
+ </entry>
+ <entry>
+ same as sort expression
+ </entry>
+ <entry>
+ discrete percentile; returns the first result whose position in
+ the ordering equals or exceeds the specified fraction
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>percentile</primary>
+ <secondary>discrete</secondary>
+ </indexterm>
+ <function>percentile_disc(<replaceable class="parameter">fractions</replaceable>) WITHIN GROUP (ORDER BY <replaceable class="parameter">sort_expression</replaceable>)</function>
+ </entry>
+ <entry>
+ <type>double precision[]</type> (all must be [0..1] or null)
+ </entry>
+ <entry>
+ any sortable type
+ </entry>
+ <entry>
+ array of input type
+ </entry>
+ <entry>
+ multiple discrete percentile; returns an array of results matching the
+ shape of the <literal>fractions</literal> parameter, with each
+ non-null element replaced by the input value at that percentile
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>percentile</primary>
+ <secondary>continuous</secondary>
+ </indexterm>
+ <indexterm>
+ <primary>median</primary>
+ </indexterm>
+ <function>percentile_cont(<replaceable class="parameter">fraction</replaceable>) WITHIN GROUP (ORDER BY <replaceable class="parameter">sort_expression</replaceable>)</function>
+ </entry>
+ <entry>
+ <type>double precision</type> (must be [0..1])
+ </entry>
+ <entry>
+ <type>double precision</type> or <type>interval</type>
+ </entry>
+ <entry>
+ same as sort expression
+ </entry>
+ <entry>
+ continuous percentile; interpolates between adjacent items.
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>percentile</primary>
+ <secondary>continuous</secondary>
+ </indexterm>
+ <function>percentile_cont(<replaceable class="parameter">fractions</replaceable>) WITHIN GROUP (ORDER BY <replaceable class="parameter">sort_expression</replaceable>)</function>
+ </entry>
+ <entry>
+ <type>double precision[]</type> (all must be [0..1] or null)
+ </entry>
+ <entry>
+ <type>double precision</type> or <type>interval</type>
+ </entry>
+ <entry>
+ array of input type
+ </entry>
+ <entry>
+ multiple continuous percentile; returns an array of results matching
+ the shape of the <literal>fractions</literal> parameter, with each
+ non-null element replaced by the value corresponding to that percentile
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>mode</primary>
+ <secondary>statistical</secondary>
+ </indexterm>
+ <function>mode() WITHIN GROUP (ORDER BY <replaceable class="parameter">sort_expression</replaceable>)</function>
+ </entry>
+ <entry>
+ </entry>
+ <entry>
+ any sortable type
+ </entry>
+ <entry>
+ same as sort expression
+ </entry>
+ <entry>
+ returns the most frequent input value (choosing one arbitrarily if
+ there are multiple equally good result)
+ </entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ All the inverse distribution functions ignore null values in their sorted
+ input. The <replaceable>fraction</replaceable> parameter must be between 0
+ and 1; an error is thrown if not. However, a null fraction simply produces
+ a null result.
+ </para>
+
+ <table id="functions-hypothetical-table">
+ <title>Hypothetical Set Functions</title>
+
+ <tgroup cols="3">
+ <thead>
+ <row>
+ <entry>Function</entry>
+ <entry>Return Type</entry>
+ </row>
+ </thead>
+
+ <tbody>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>rank</primary>
+ <secondary>hypothetical</secondary>
+ </indexterm>
+ <function>rank(<replaceable class="parameter">args</replaceable>) WITHIN GROUP (ORDER BY <replaceable class="parameter">sorted_args</replaceable>)</function>
+ </entry>
+ <entry>
+ <type>bigint</type>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>dense_rank</primary>
+ <secondary>hypothetical</secondary>
+ </indexterm>
+ <function>dense_rank(<replaceable class="parameter">args</replaceable>) WITHIN GROUP (ORDER BY <replaceable class="parameter">sorted_args</replaceable>)</function>
+ </entry>
+ <entry>
+ <type>bigint</type>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>percent_rank</primary>
+ <secondary>hypothetical</secondary>
+ </indexterm>
+ <function>percent_rank(<replaceable class="parameter">args</replaceable>) WITHIN GROUP (ORDER BY <replaceable class="parameter">sorted_args</replaceable>)</function>
+ </entry>
+ <entry>
+ <type>double precision</type>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>cume_dist</primary>
+ <secondary>hypothetical</secondary>
+ </indexterm>
+ <function>cume_dist(<replaceable class="parameter">args</replaceable>) WITHIN GROUP (ORDER BY <replaceable class="parameter">sorted_args</replaceable>)</function>
+ </entry>
+ <entry>
+ <type>double precision</type>
+ </entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ For all hypothetical set functions, the list of arguments given
+ by <replaceable>args</replaceable> should match the number and types of
+ arguments given as <replaceable>sorted_args</replaceable>.
+ </para>
+
+ <para>
+ All of the functions listed in
+ <xref linkend="functions-hypothetical-table"> are associated with a
+ window function defined in
+ <xref linkend="functions-window">. In each case, the function result
+ represents the value that the associated window function would have
+ returned, for the hypothetical row constructed from
+ <replaceable>args</replaceable> and included in the sorted group of
+ rows.
+ </para>
+
+ </sect1>
+
<sect1 id="functions-window">
<title>Window Functions</title>
*** a/doc/src/sgml/ref/alter_aggregate.sgml
--- b/doc/src/sgml/ref/alter_aggregate.sgml
***************
*** 21,32 **** PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
! ALTER AGGREGATE <replaceable>name</replaceable> ( [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
RENAME TO <replaceable>new_name</replaceable>
ALTER AGGREGATE <replaceable>name</replaceable> ( [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
OWNER TO <replaceable>new_owner</replaceable>
ALTER AGGREGATE <replaceable>name</replaceable> ( [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
SET SCHEMA <replaceable>new_schema</replaceable>
</synopsis>
</refsynopsisdiv>
--- 21,37 ----
<refsynopsisdiv>
<synopsis>
! ALTER AGGREGATE
RENAME TO <replaceable>new_name</replaceable>
ALTER AGGREGATE <replaceable>name</replaceable> ( [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
OWNER TO <replaceable>new_owner</replaceable>
ALTER AGGREGATE <replaceable>name</replaceable> ( [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
SET SCHEMA <replaceable>new_schema</replaceable>
+
+ <phrase>where <replaceable>aggregate_signature</replaceable> is one of:</phrase>
+
+ <replaceable>name</replaceable> ( * | [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
+ <replaceable>name</replaceable> ( [ [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] ] ) WITHIN GROUP ( * | [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
</synopsis>
</refsynopsisdiv>
***************
*** 148,157 **** ALTER AGGREGATE myavg(integer) OWNER TO joe;
</para>
<para>
! To move the aggregate function <literal>myavg</literal> for type
! <type>integer</type> into schema <literal>myschema</literal>:
<programlisting>
! ALTER AGGREGATE myavg(integer) SET SCHEMA myschema;
</programlisting></para>
</refsect1>
--- 153,163 ----
</para>
<para>
! To move the ordered set function <literal>mypercentile</literal> with
! direct argument of type <type>float8</type> taking groups
! of <type>integer</type> type into schema <literal>myschema</literal>:
<programlisting>
! ALTER AGGREGATE mypercentile(float8) WITHIN GROUP (integer) SET SCHEMA myschema;
</programlisting></para>
</refsect1>
*** a/doc/src/sgml/ref/alter_extension.sgml
--- b/doc/src/sgml/ref/alter_extension.sgml
***************
*** 30,36 **** ALTER EXTENSION <replaceable class="PARAMETER">name</replaceable> DROP <replacea
<phrase>where <replaceable class="PARAMETER">member_object</replaceable> is:</phrase>
! AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) |
CAST (<replaceable>source_type</replaceable> AS <replaceable>target_type</replaceable>) |
COLLATION <replaceable class="PARAMETER">object_name</replaceable> |
CONVERSION <replaceable class="PARAMETER">object_name</replaceable> |
--- 30,36 ----
<phrase>where <replaceable class="PARAMETER">member_object</replaceable> is:</phrase>
! AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) [ WITHIN GROUP ( * | [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) ] |
CAST (<replaceable>source_type</replaceable> AS <replaceable>target_type</replaceable>) |
COLLATION <replaceable class="PARAMETER">object_name</replaceable> |
CONVERSION <replaceable class="PARAMETER">object_name</replaceable> |
*** a/doc/src/sgml/ref/comment.sgml
--- b/doc/src/sgml/ref/comment.sgml
***************
*** 23,29 **** PostgreSQL documentation
<synopsis>
COMMENT ON
{
! AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) |
CAST (<replaceable>source_type</replaceable> AS <replaceable>target_type</replaceable>) |
COLLATION <replaceable class="PARAMETER">object_name</replaceable> |
COLUMN <replaceable class="PARAMETER">relation_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
--- 23,29 ----
<synopsis>
COMMENT ON
{
! AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) [ WITHIN GROUP ( * | [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) ] |
CAST (<replaceable>source_type</replaceable> AS <replaceable>target_type</replaceable>) |
COLLATION <replaceable class="PARAMETER">object_name</replaceable> |
COLUMN <replaceable class="PARAMETER">relation_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
*** a/doc/src/sgml/ref/create_aggregate.sgml
--- b/doc/src/sgml/ref/create_aggregate.sgml
***************
*** 29,34 **** CREATE AGGREGATE <replaceable class="parameter">name</replaceable> ( [ <replacea
--- 29,43 ----
[ , SORTOP = <replaceable class="PARAMETER">sort_operator</replaceable> ]
)
+ CREATE AGGREGATE <replaceable class="parameter">name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">arg_name</replaceable> ] <replaceable class="parameter">arg_data_type</replaceable> [ , ... ] ) WITHIN GROUP ( * | [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">arg_name</replaceable> ] <replaceable class="parameter">arg_data_type</replaceable> [ , ... ] ) (
+ FINALFUNC = <replaceable class="PARAMETER">ffunc</replaceable>
+ [ , STRICT ]
+ [ , HYPOTHETICAL ]
+ [ , STYPE = <replaceable class="PARAMETER">state_data_type</replaceable> ]
+ [ , INITCOND = <replaceable class="PARAMETER">initial_condition</replaceable> ]
+ [ , TRANSSORTOP = <replaceable class="PARAMETER">state_sort_operator</replaceable> ]
+ )
+
<phrase>or the old syntax</phrase>
CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> (
***************
*** 70,76 **** CREATE AGGREGATE <replaceable class="PARAMETER">name</replaceable> (
</para>
<para>
! An aggregate function is made from one or two ordinary
functions:
a state transition function
<replaceable class="PARAMETER">sfunc</replaceable>,
--- 79,85 ----
</para>
<para>
! An ordinary aggregate function is made from one or two ordinary
functions:
a state transition function
<replaceable class="PARAMETER">sfunc</replaceable>,
***************
*** 165,170 **** SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
--- 174,187 ----
</para>
<para>
+ The <literal>WITHIN GROUP</literal> syntax denotes a special subset of
+ aggregate functions collectively called <quote>ordered set
+ functions</quote>. 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.
+ </para>
+
+ <para>
To be able to create an aggregate function, you must
have <literal>USAGE</literal> privilege on the argument types, the state
type, and the return type, as well as <literal>EXECUTE</literal> privilege
***************
*** 278,283 **** SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
--- 295,305 ----
aggregate's result, and the return type is <replaceable
class="PARAMETER">state_data_type</replaceable>.
</para>
+ <para>
+ For ordered set functions, the function arguments must instead
+ correspond to the input arguments (both direct and grouped) plus
+ the state type if any.
+ </para>
</listitem>
</varlistentry>
***************
*** 305,310 **** SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
--- 327,365 ----
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="PARAMETER">state_sort_operator</replaceable></term>
+ <listitem>
+ <para>
+ For ordered set functions only, this is a sort operator that can be
+ applied to
+ the <replaceable class="PARAMETER">state_data_type</replaceable>.
+ This is just an operator name (possibly schema-qualified).
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>STRICT</literal></term>
+ <listitem>
+ <para>
+ For ordered set functions only, this flag specifies that the function is
+ strict, i.e. that grouped rows containing nulls are skipped.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>HYPOTHETICAL</literal></term>
+ <listitem>
+ <para>
+ For ordered set functions only, this flag specifies that the aggregate
+ parameters are to be processed according to the requirements for
+ hypothetical set functions.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
<para>
*** a/doc/src/sgml/ref/drop_aggregate.sgml
--- b/doc/src/sgml/ref/drop_aggregate.sgml
***************
*** 21,29 **** PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
! DROP AGGREGATE [ IF EXISTS ]
! <replaceable class="parameter">name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">arg_name</replaceable> ] <replaceable class="parameter">arg_data_type</replaceable> [ , ... ] )
[ CASCADE | RESTRICT ]
</synopsis>
</refsynopsisdiv>
--- 21,33 ----
<refsynopsisdiv>
<synopsis>
! DROP AGGREGATE [ IF EXISTS ] <replaceable class="parameter">aggregate_signature</replaceable>
[ CASCADE | RESTRICT ]
+
+ <phrase>where <replaceable>aggregate_signature</replaceable> is one of:</phrase>
+
+ <replaceable>name</replaceable> ( * | [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
+ <replaceable>name</replaceable> ( [ [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] ] ) WITHIN GROUP ( * | [ <replaceable>argmode</replaceable> ] [ <replaceable>arg_name</replaceable> ] <replaceable>arg_data_type</replaceable> [ , ... ] )
</synopsis>
</refsynopsisdiv>
*** a/doc/src/sgml/ref/security_label.sgml
--- b/doc/src/sgml/ref/security_label.sgml
***************
*** 25,31 **** SECURITY LABEL [ FOR <replaceable class="PARAMETER">provider</replaceable> ] ON
{
TABLE <replaceable class="PARAMETER">object_name</replaceable> |
COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
! AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) |
DATABASE <replaceable class="PARAMETER">object_name</replaceable> |
DOMAIN <replaceable class="PARAMETER">object_name</replaceable> |
EVENT TRIGGER <replaceable class="PARAMETER">object_name</replaceable> |
--- 25,31 ----
{
TABLE <replaceable class="PARAMETER">object_name</replaceable> |
COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
! AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> ( [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) [ WITHIN GROUP ( * | [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">agg_type</replaceable> [, ...] ) ] |
DATABASE <replaceable class="PARAMETER">object_name</replaceable> |
DOMAIN <replaceable class="PARAMETER">object_name</replaceable> |
EVENT TRIGGER <replaceable class="PARAMETER">object_name</replaceable> |
*** 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 ----
</para>
</sect2>
+ <sect2 id="syntax-orderedset">
+ <title>Ordered Set Functions</title>
+
+ <indexterm zone="syntax-orderedset">
+ <primary>ordered set function</primary>
+ </indexterm>
+
+ <indexterm zone="syntax-orderedset">
+ <primary>aggregate function</primary>
+ <secondary>ordered set function</secondary>
+ </indexterm>
+
+ <indexterm zone="syntax-orderedset">
+ <primary>WITHIN GROUP</primary>
+ </indexterm>
+
+ <para>
+ An <firstterm>ordered set function</firstterm> 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:
+
+ <synopsis>
+ <replaceable>function_name</replaceable> ( [ <replaceable>expression</replaceable> [ , ... ] ] ) WITHIN GROUP ( <replaceable>order_by_clause</replaceable> ) [ FILTER ( WHERE <replaceable>filter_clause</replaceable> ) ]
+ </synopsis>
+
+ where <replaceable>function_name</replaceable> is a previously
+ defined ordered set function (possibly qualified with a schema name) and
+ <replaceable>expression</replaceable> 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 <replaceable>order_by_clause</replaceable> has the same syntax
+ as for a query-level <literal>ORDER BY</> clause, as described
+ in <xref linkend="queries-order">, except that its expressions are always
+ just expressions and cannot be output-column names or numbers. The
+ expressions of the <replaceable>order_by_clause</replaceable> may, and
+ almost invariably do, refer to the ungrouped columns of the input; the
+ clause defines the grouped input to the function. The optional
+ <replaceable>filter_clause</replaceable> is identical to that for
+ aggregate functions (see <xref linkend="syntax-aggregates">, and is applied
+ to input rows prior to the sort operation.
+ </para>
+
+ </sect2>
+
<sect2 id="syntax-window-functions">
<title>Window Function Calls</title>
*** a/doc/src/sgml/xaggr.sgml
--- b/doc/src/sgml/xaggr.sgml
***************
*** 9,28 ****
</indexterm>
<para>
! Aggregate functions in <productname>PostgreSQL</productname>
! are expressed in terms of <firstterm>state values</firstterm>
! and <firstterm>state transition functions</firstterm>.
! 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 <firstterm>final function</firstterm>
! 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.
</para>
<para>
--- 9,25 ----
</indexterm>
<para>
! Aggregate functions (other than ordered set functions)
! in <productname>PostgreSQL</productname> are expressed in terms
! of <firstterm>state values</firstterm> and <firstterm>state transition
! functions</firstterm>. 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 <firstterm>final function</firstterm> 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.
</para>
<para>
*** 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 <str> opt_existing_window_name
%type <boolean> opt_if_not_exists
%type <node> filter_clause
+ %type <list> within_group_clause
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
***************
*** 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 <string.h>
+ #include <math.h>
+
+ #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 <string.h>
+ #include <math.h>
+
+ #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 = (Datum) 0;
+ bool last_val_is_mode = false;
+ int64 val_freq = 0;
+ Datum mode_val = (Datum) 0;
+ 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.
On Sat, Sep 14, 2013 at 7:23 AM, Andrew Gierth
<andrew@tao11.riddles.org.uk> wrote:
Peter> Please fix compiler warnings:
Someone should do the same in WaitForBackgroundWorkerStartup so that
building with -Werror works.
I don't get a warning there. Can you be more specific about the problem?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
"Robert" == Robert Haas <robertmhaas@gmail.com> writes:
Someone should do the same in WaitForBackgroundWorkerStartup so
that building with -Werror works.
Robert> I don't get a warning there. Can you be more specific about
Robert> the problem?
bgworker.c: In function 'WaitForBackgroundWorkerStartup':
bgworker.c:866: warning: 'pid' may be used uninitialized in this function
gcc 4.2.2 / freebsd 8.2
--
Andrew (irc:RhodiumToad)
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Tue, Sep 17, 2013 at 2:27 PM, Andrew Gierth
<andrew@tao11.riddles.org.uk> wrote:
"Robert" == Robert Haas <robertmhaas@gmail.com> writes:
Someone should do the same in WaitForBackgroundWorkerStartup so
that building with -Werror works.Robert> I don't get a warning there. Can you be more specific about
Robert> the problem?bgworker.c: In function 'WaitForBackgroundWorkerStartup':
bgworker.c:866: warning: 'pid' may be used uninitialized in this function
Does the attached patch fix it for you?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Attachments:
bgworker-wait-fix.patchapplication/octet-stream; name=bgworker-wait-fix.patchDownload
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 25e6156..0e7a4a5 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -863,7 +863,6 @@ BgwHandleStatus
WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
{
BgwHandleStatus status;
- pid_t pid;
int rc;
bool save_set_latch_on_sigusr1;
@@ -874,9 +873,13 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
{
for (;;)
{
+ pid_t pid;
+
CHECK_FOR_INTERRUPTS();
status = GetBackgroundWorkerPid(handle, &pid);
+ if (status == BGWH_STARTED)
+ *pidp = pid;
if (status != BGWH_NOT_YET_STARTED)
break;
@@ -900,6 +903,5 @@ WaitForBackgroundWorkerStartup(BackgroundWorkerHandle *handle, pid_t *pidp)
PG_END_TRY();
set_latch_on_sigusr1 = save_set_latch_on_sigusr1;
- *pidp = pid;
return status;
}
"Robert" == Robert Haas <robertmhaas@gmail.com> writes:
bgworker.c: In function 'WaitForBackgroundWorkerStartup':
bgworker.c:866: warning: 'pid' may be used uninitialized in this function
Robert> Does the attached patch fix it for you?
It compiles without error and looks ok...
--
Andrew (irc:RhodiumToad)
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Sep 19, 2013 at 12:52 PM, Andrew Gierth
<andrew@tao11.riddles.org.uk> wrote:
It compiles without error and looks ok...
Thanks for checking. Committed.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers