Skip to content

Commit 3379fae

Browse files
committed
array_agg aggregate function, as per SQL:2008, but without ORDER BY clause
Rearrange the documentation a bit now that array_agg and xmlagg have similar semantics and issues. best of Robert Haas, Jeff Davis, Peter Eisentraut
1 parent 69a0e2f commit 3379fae

File tree

9 files changed

+164
-23
lines changed

9 files changed

+164
-23
lines changed

doc/src/sgml/func.sgml

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.457 2008/11/12 13:09:27 petere Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.458 2008/11/13 15:59:50 petere Exp $ -->
22

33
<chapter id="functions">
44
<title>Functions and Operators</title>
@@ -8353,7 +8353,7 @@ SELECT xmlroot(xmlparse(document '<?xml version="1.1"?><content>abc</content>'),
83538353
</para>
83548354
</sect3>
83558355

8356-
<sect3>
8356+
<sect3 id="functions-xml-xmlagg">
83578357
<title><literal>xmlagg</literal></title>
83588358

83598359
<indexterm>
@@ -8366,10 +8366,10 @@ SELECT xmlroot(xmlparse(document '<?xml version="1.1"?><content>abc</content>'),
83668366

83678367
<para>
83688368
The function <function>xmlagg</function> is, unlike the other
8369-
functions below, an aggregate function. It concatenates the
8369+
functions described here, an aggregate function. It concatenates the
83708370
input values to the aggregate function call,
83718371
like <function>xmlconcat</function> does.
8372-
See <xref linkend="functions-aggregate"> for general information
8372+
See <xref linkend="functions-aggregate"> for additional information
83738373
about aggregate functions.
83748374
</para>
83758375

@@ -8387,23 +8387,18 @@ SELECT xmlagg(x) FROM test;
83878387
</para>
83888388

83898389
<para>
8390-
Note that in the current implementation, the order of the
8391-
concatenation is in principle undefined. Making the input values
8392-
to be sorted in some other way will usually work, however. For
8393-
instance, in the above example, one could influence the order
8394-
like so:
8390+
The influence the order of the concatenation, something like the
8391+
following approach to sort the input values can be used:
8392+
83958393
<screen><![CDATA[
83968394
SELECT xmlagg(x) FROM (SELECT * FROM test ORDER BY y DESC) AS tab;
83978395
xmlagg
83988396
----------------------
83998397
<bar/><foo>abc</foo>
84008398
]]></screen>
84018399

8402-
But this approach is not guaranteed to work in all situations and
8403-
in all versions of PostgreSQL. A future version of PostgreSQL
8404-
will probably provide an additional feature to control the order
8405-
in a proper way (<literal>xmlagg(expr ORDER BY expr, expr,
8406-
...</literal>).
8400+
Again, see <xref linkend="functions-aggregate"> for additional
8401+
information.
84078402
</para>
84088403
</sect3>
84098404

@@ -9490,6 +9485,11 @@ SELECT NULLIF(value, '(none)') ...
94909485
</tbody>
94919486
</tgroup>
94929487
</table>
9488+
9489+
<para>
9490+
See also <xref linkend="functions-aggregate"> about the aggregate
9491+
function <function>array_agg</function> for use with arrays.
9492+
</para>
94939493
</sect1>
94949494

94959495
<sect1 id="functions-aggregate">
@@ -9526,6 +9526,22 @@ SELECT NULLIF(value, '(none)') ...
95269526
</thead>
95279527

95289528
<tbody>
9529+
<row>
9530+
<entry>
9531+
<indexterm>
9532+
<primary>array_agg</primary>
9533+
</indexterm>
9534+
<function>array_agg(<replaceable class="parameter">expression</replaceable>)</function>
9535+
</entry>
9536+
<entry>
9537+
any
9538+
</entry>
9539+
<entry>
9540+
array of the argument type
9541+
</entry>
9542+
<entry>input values concatenated into an array</entry>
9543+
</row>
9544+
95299545
<row>
95309546
<entry>
95319547
<indexterm>
@@ -9684,6 +9700,22 @@ SELECT NULLIF(value, '(none)') ...
96849700
</entry>
96859701
<entry>sum of <replaceable class="parameter">expression</replaceable> across all input values</entry>
96869702
</row>
9703+
9704+
<row>
9705+
<entry>
9706+
<indexterm>
9707+
<primary>xmlagg</primary>
9708+
</indexterm>
9709+
<function>xmlagg(<replaceable class="parameter">expression</replaceable>)</function>
9710+
</entry>
9711+
<entry>
9712+
<type>xml</type>
9713+
</entry>
9714+
<entry>
9715+
<type>xml</type>
9716+
</entry>
9717+
<entry>concatenation of XML values (see also <xref linkend="functions-xml-xmlagg">)</entry>
9718+
</row>
96879719
</tbody>
96889720
</tgroup>
96899721
</table>
@@ -9692,8 +9724,10 @@ SELECT NULLIF(value, '(none)') ...
96929724
It should be noted that except for <function>count</function>,
96939725
these functions return a null value when no rows are selected. In
96949726
particular, <function>sum</function> of no rows returns null, not
9695-
zero as one might expect. The <function>coalesce</function> function can be
9696-
used to substitute zero for null when necessary.
9727+
zero as one might expect, and <function>array_agg</function>
9728+
returns null rather than an empty array when there are no input
9729+
rows. The <function>coalesce</function> function can be used to
9730+
substitute zero or an empty array for null when necessary.
96979731
</para>
96989732

96999733
<note>
@@ -9733,6 +9767,25 @@ SELECT count(*) FROM sometable;
97339767
</para>
97349768
</note>
97359769

9770+
<para>
9771+
The aggregate functions <function>array_agg</function>
9772+
and <function>xmlagg</function>, as well as similar user-defined
9773+
aggregate functions, produce meaningfully different result values
9774+
depending on the order of the input values. In the current
9775+
implementation, the order of the concatenation is in principle
9776+
undefined. Making the input values to be sorted in some other way
9777+
will usually work, however. For example:
9778+
9779+
<screen><![CDATA[
9780+
SELECT xmlagg(x) FROM (SELECT * FROM test ORDER BY y DESC) AS tab;
9781+
]]></screen>
9782+
9783+
But this approach is not guaranteed to work in all situations, and
9784+
it is not strictly SQL-conforming. A future version of PostgreSQL
9785+
might provide an additional feature to control the order in a
9786+
better-defined way (<literal>xmlagg(expr ORDER BY expr, expr,
9787+
...</literal>).
9788+
</para>
97369789

97379790
<para>
97389791
<xref linkend="functions-aggregate-statistics-table"> shows

src/backend/catalog/sql_features.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ S094 Arrays of reference types NO
350350
S095 Array constructors by query YES
351351
S096 Optional array bounds YES
352352
S097 Array element assignment NO
353-
S098 ARRAY_AGG NO
353+
S098 ARRAY_AGG NO ORDER BY clause not supported
354354
S111 ONLY in query expressions YES
355355
S151 Type predicate NO
356356
S161 Subtype treatment NO

src/backend/utils/adt/array_userfuncs.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
* Copyright (c) 2003-2008, PostgreSQL Global Development Group
77
*
88
* IDENTIFICATION
9-
* $PostgreSQL: pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.23 2008/01/01 19:45:52 momjian Exp $
9+
* $PostgreSQL: pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.24 2008/11/13 15:59:50 petere Exp $
1010
*
1111
*-------------------------------------------------------------------------
1212
*/
1313
#include "postgres.h"
1414

15+
#include "nodes/execnodes.h"
1516
#include "utils/array.h"
1617
#include "utils/builtins.h"
1718
#include "utils/lsyscache.h"
@@ -465,3 +466,34 @@ create_singleton_array(FunctionCallInfo fcinfo,
465466
return construct_md_array(dvalues, NULL, ndims, dims, lbs, element_type,
466467
typlen, typbyval, typalign);
467468
}
469+
470+
Datum
471+
array_agg_transfn(PG_FUNCTION_ARGS)
472+
{
473+
Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
474+
475+
if (arg1_typeid == InvalidOid)
476+
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
477+
errmsg("could not determine input data type")));
478+
479+
/* cannot be called directly because of internal-type argument */
480+
Assert(fcinfo->context && IsA(fcinfo->context, AggState));
481+
482+
PG_RETURN_POINTER(accumArrayResult(PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0),
483+
PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1),
484+
PG_ARGISNULL(1),
485+
arg1_typeid,
486+
((AggState *) fcinfo->context)->aggcontext));
487+
}
488+
489+
Datum
490+
array_agg_finalfn(PG_FUNCTION_ARGS)
491+
{
492+
/* cannot be called directly because of internal-type argument */
493+
Assert(fcinfo->context && IsA(fcinfo->context, AggState));
494+
495+
if (PG_ARGISNULL(0))
496+
PG_RETURN_NULL(); /* returns null iff no input values */
497+
498+
PG_RETURN_ARRAYTYPE_P(makeArrayResult((ArrayBuildState *) PG_GETARG_POINTER(0), CurrentMemoryContext));
499+
}

src/include/catalog/catversion.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
3838
* Portions Copyright (c) 1994, Regents of the University of California
3939
*
40-
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.504 2008/11/12 13:09:27 petere Exp $
40+
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.505 2008/11/13 15:59:50 petere Exp $
4141
*
4242
*-------------------------------------------------------------------------
4343
*/
@@ -53,6 +53,6 @@
5353
*/
5454

5555
/* yyyymmddN */
56-
#define CATALOG_VERSION_NO 200811121
56+
#define CATALOG_VERSION_NO 200811131
5757

5858
#endif

src/include/catalog/pg_aggregate.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
99
* Portions Copyright (c) 1994, Regents of the University of California
1010
*
11-
* $PostgreSQL: pgsql/src/include/catalog/pg_aggregate.h,v 1.66 2008/03/27 03:57:34 tgl Exp $
11+
* $PostgreSQL: pgsql/src/include/catalog/pg_aggregate.h,v 1.67 2008/11/13 15:59:50 petere Exp $
1212
*
1313
* NOTES
1414
* the genbki.sh script reads this file and generates .bki
@@ -220,6 +220,9 @@ DATA(insert ( 2243 bitor - 0 1560 _null_ ));
220220
/* xml */
221221
DATA(insert ( 2901 xmlconcat2 - 0 142 _null_ ));
222222

223+
/* array */
224+
DATA(insert ( 2335 array_agg_transfn array_agg_finalfn 0 2281 _null_ ));
225+
223226
/*
224227
* prototypes for functions in pg_aggregate.c
225228
*/

src/include/catalog/pg_proc.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.526 2008/11/12 13:38:04 tgl Exp $
10+
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.527 2008/11/13 15:59:50 petere Exp $
1111
*
1212
* NOTES
1313
* The script catalog/genbki.sh reads this file and generates .bki
@@ -1022,6 +1022,13 @@ DATA(insert OID = 1193 ( array_fill PGNSP PGUID 12 1 0 0 f f f f i 2 2277 "2283
10221022
DESCR("array constructor with value");
10231023
DATA(insert OID = 1286 ( array_fill PGNSP PGUID 12 1 0 0 f f f f i 3 2277 "2283 1007 1007" _null_ _null_ _null_ array_fill_with_lower_bounds _null_ _null_ _null_ ));
10241024
DESCR("array constructor with value");
1025+
DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 f f f f i 2 2281 "2281 2283" _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
1026+
DESCR("array_agg transition function");
1027+
DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 f f f f i 1 2277 "2281" _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
1028+
DESCR("array_agg final function");
1029+
DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 t f f f i 1 2277 "2283" _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
1030+
DESCR("concatenate aggregate input into an array");
1031+
10251032
DATA(insert OID = 760 ( smgrin PGNSP PGUID 12 1 0 0 f f t f s 1 210 "2275" _null_ _null_ _null_ smgrin _null_ _null_ _null_ ));
10261033
DESCR("I/O");
10271034
DATA(insert OID = 761 ( smgrout PGNSP PGUID 12 1 0 0 f f t f s 1 2275 "210" _null_ _null_ _null_ smgrout _null_ _null_ _null_ ));

src/include/utils/array.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
5050
* Portions Copyright (c) 1994, Regents of the University of California
5151
*
52-
* $PostgreSQL: pgsql/src/include/utils/array.h,v 1.70 2008/11/12 13:09:28 petere Exp $
52+
* $PostgreSQL: pgsql/src/include/utils/array.h,v 1.71 2008/11/13 15:59:50 petere Exp $
5353
*
5454
*-------------------------------------------------------------------------
5555
*/
@@ -275,4 +275,7 @@ extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
275275
Datum element,
276276
int ndims);
277277

278+
extern Datum array_agg_transfn(PG_FUNCTION_ARGS);
279+
extern Datum array_agg_finalfn(PG_FUNCTION_ARGS);
280+
278281
#endif /* ARRAY_H */

src/test/regress/expected/arrays.out

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,3 +1125,39 @@ select c, cardinality(c), d, cardinality(d) from arrtest;
11251125
{foo,new_word} | 2 | {bar,foo} | 2
11261126
(3 rows)
11271127

1128+
select array_agg(unique1) from tenk1 where unique1 < 15;
1129+
array_agg
1130+
--------------------------------------
1131+
{4,2,1,6,14,9,8,5,3,13,12,11,7,10,0}
1132+
(1 row)
1133+
1134+
select array_agg(ten) from tenk1 where unique1 < 15;
1135+
array_agg
1136+
---------------------------------
1137+
{4,2,1,6,4,9,8,5,3,3,2,1,7,0,0}
1138+
(1 row)
1139+
1140+
select array_agg(nullif(ten, 4)) from tenk1 where unique1 < 15;
1141+
array_agg
1142+
---------------------------------------
1143+
{NULL,2,1,6,NULL,9,8,5,3,3,2,1,7,0,0}
1144+
(1 row)
1145+
1146+
select cardinality(array_agg(unique1)) from tenk1 where unique1 < 15;
1147+
cardinality
1148+
-------------
1149+
15
1150+
(1 row)
1151+
1152+
select array_agg(unique1) from (select * from tenk1 order by unique1 asc) as tab where unique1 < 15;
1153+
array_agg
1154+
--------------------------------------
1155+
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14}
1156+
(1 row)
1157+
1158+
select array_agg(unique1) from tenk1 where unique1 < -15;
1159+
array_agg
1160+
-----------
1161+
1162+
(1 row)
1163+

src/test/regress/sql/arrays.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,3 +395,10 @@ select array_length(array[[1,2,3], [4,5,6]], 3);
395395
select cardinality(array[1,2,3]);
396396
select cardinality(array[[1,2,3], [4,5,6]]);
397397
select c, cardinality(c), d, cardinality(d) from arrtest;
398+
399+
select array_agg(unique1) from tenk1 where unique1 < 15;
400+
select array_agg(ten) from tenk1 where unique1 < 15;
401+
select array_agg(nullif(ten, 4)) from tenk1 where unique1 < 15;
402+
select cardinality(array_agg(unique1)) from tenk1 where unique1 < 15;
403+
select array_agg(unique1) from (select * from tenk1 order by unique1 asc) as tab where unique1 < 15;
404+
select array_agg(unique1) from tenk1 where unique1 < -15;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy