Function array_agg(array)
Greetings,
While looking for easier items in PostgreSQL Wiki's Todo List (this will be
my 3rd patch), i found this TODO:
Add a built-in array_agg(anyarray) or similar, that can aggregate
1-dimensional arrays into a 2-dimensional array.
I've stumbled by this lack of array_agg(anyarray) sometimes ago in my work,
so i decided to explore this.
Currently, if we try array_agg(anyarray), PostgreSQL behaves like this:
# select array_agg('{1,2}'::int[]);
ERROR: could not find array type for data type integer[]
Reading implementation of array_agg, it looks like the array_agg function
is generic, and can process any input. The error comes from PostgreSQL not
finding array type for int[] (_int4 in pg_proc).
In PostgreSQL, any array is multidimensional, array type for any array is
the same:
- the type of {1,2} is int[]
- {{1,2}, {3,4}} is int[]
- {{{1},{2}, {3} ,{4}}} is still int[]
So, can't we just set the typarray of array types to its self oid? (patch
attached). So far:
- the array_agg is working and returning correct types:
backend> select array_agg('{1,2}'::int[]);
1: array_agg (typeid = 1007, len = -1, typmod = -1, byval = f)
----
1: array_agg = "{"{1,2}"}" (typeid = 1007, len = -1, typmod = -1,
byval = f)
----
select array_agg('{''a'',''b''}'::varchar[]);
1: array_agg (typeid = 1015, len = -1, typmod = -1, byval = f)
----
1: array_agg = "{"{'a','b'}"}" (typeid = 1015, len = -1, typmod =
-1, byval = f)
----
- Regression tests passed except for the pg_type sanity check while
checking typelem relation with typarray:
SELECT p1.oid, p1.typname as basetype, p2.typname as arraytype,
p2.typelem, p2.typlen
FROM pg_type p1 LEFT JOIN pg_type p2 ON (p1.typarray = p2.oid)
WHERE p1.typarray <> 0 AND
(p2.oid IS NULL OR p2.typelem <> p1.oid OR p2.typlen <> -1);
! oid | basetype | arraytype | typelem | typlen
! ------+----------------+----------------+---------+--------
! 143 | _xml | _xml | 142 | -1
! 199 | _json | _json | 114 | -1
! 629 | _line | _line | 628 | -1
! 719 | _circle | _circle | 718 | -1
... (cut)
Aside from the sanity check complaints, I don't see any problems in the
resulting array operations.
So, back to the question: Can't we just set the typarray of array types to
its self oid?
Regards,
--
Ali Akbar
Attachments:
array_agg_anyarray-1.patchtext/x-diff; charset=US-ASCII; name=array_agg_anyarray-1.patchDownload
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index b7d9256..7934874 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -357,8 +357,8 @@ DATA(insert OID = 114 ( json PGNSP PGUID -1 f b U f t \054 0 0 199 json_in j
DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
-DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 143 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 199 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
DESCR("string representing an internal node tree");
@@ -395,7 +395,7 @@ DESCR("geometric polygon '(pt1,...)'");
DATA(insert OID = 628 ( line PGNSP PGUID 24 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("geometric line");
#define LINEOID 628
-DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 629 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
/* OIDS 700 - 799 */
@@ -421,11 +421,11 @@ DESCR("");
DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
-DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 719 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
-DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 791 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
/* OIDS 800 - 899 */
DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
@@ -441,46 +441,46 @@ DESCR("network IP address/netmask, network address");
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
-DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 1000 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 1001 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 1002 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 1003 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 1005 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
#define INT2ARRAYOID 1005
-DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 1006 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 1007 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
#define INT4ARRAYOID 1007
-DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 1008 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 1009 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
#define TEXTARRAYOID 1009
-DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 1028 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
#define OIDARRAYOID 1028
-DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
-DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
-DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 1010 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 1011 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 1012 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 1013 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 1014 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 1015 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 1016 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 1017 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 1018 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 1019 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 1020 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 1021 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
-DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 1022 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 1023 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 1024 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 1025 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 1027 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
-DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 1034 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 1040 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 1041 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 651 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 1263 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
#define CSTRINGARRAYOID 1263
DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 _null_ _null_ _null_ ));
@@ -501,34 +501,34 @@ DESCR("time of day");
DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
-DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 1115 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 1182 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 1183 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
-DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 1185 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
-DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 1187 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
/* OIDS 1200 - 1299 */
-DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 1231 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
-DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 1270 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
/* OIDS 1500 - 1599 */
DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
-DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 1561 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
-DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 1563 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
/* OIDS 1600 - 1699 */
@@ -542,7 +542,7 @@ DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
-DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 2201 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("registered procedure (with args)");
@@ -564,24 +564,24 @@ DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regty
DESCR("registered type");
#define REGTYPEOID 2206
-DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 2207 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 2208 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 2209 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 2210 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 2211 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
#define REGTYPEARRAYOID 2211
/* uuid */
DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("UUID datatype");
#define UUIDOID 2950
-DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 2951 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
/* pg_lsn */
DATA(insert OID = 3220 ( pg_lsn PGNSP PGUID 8 FLOAT8PASSBYVAL b U f t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("PostgreSQL LSN datatype");
#define LSNOID 3220
-DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 3221 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
/* text search */
DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
@@ -600,42 +600,42 @@ DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 reg
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
-DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 3643 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 3644 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 3645 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 3735 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 3770 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
/* jsonb */
DATA(insert OID = 3802 ( jsonb PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("Binary JSON");
#define JSONBOID 3802
-DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 3807 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("txid snapshot");
-DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 2949 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
/* range types */
DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("range of integers");
#define INT4RANGEOID 3904
-DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 3905 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("range of numerics");
-DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 3907 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("range of timestamps without time zone");
-DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 3909 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("range of timestamps with time zone");
-DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 3911 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("range of dates");
-DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 3913 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
DESCR("range of bigints");
-DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 3927 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
/*
* pseudo-types
@@ -652,7 +652,7 @@ DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 arr
*/
DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
#define RECORDOID 2249
-DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 2287 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
#define RECORDARRAYOID 2287
DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
#define CSTRINGOID 2275
Ali Akbar <the.apaan@gmail.com> writes:
So, can't we just set the typarray of array types to its self oid?
Seems dangerous as heck; certainly it would have side-effects far more
wide-ranging than just making this particular function work.
A safer answer is to split array_agg into two functions,
array_agg(anynonarray) -> anyarray
array_agg(anyarray) -> anyarray
I rather imagine you should do that anyway, because I really doubt
that this hack is operating quite as intended. I suspect you are
producing arrays containing arrays as elements, not true 2-D arrays.
That's not a direction we want to go in I think; certainly there are
no other operations that produce such things.
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
2014-10-11 22:28 GMT+07:00 Tom Lane <tgl@sss.pgh.pa.us>:
Seems dangerous as heck; certainly it would have side-effects far more
wide-ranging than just making this particular function work.A safer answer is to split array_agg into two functions,
array_agg(anynonarray) -> anyarray
array_agg(anyarray) -> anyarrayI rather imagine you should do that anyway, because I really doubt
that this hack is operating quite as intended. I suspect you are
producing arrays containing arrays as elements, not true 2-D arrays.
That's not a direction we want to go in I think; certainly there are
no other operations that produce such things.
Thanks for the review. Yes, it looks like the patch produced array as the
elements. So, all array operations behaves wierdly.
In this quick & dirty patch, I am trying to implement the
array_agg(anyarray), introducing two new functions:
- array_agg_anyarray_transfn
- array_agg_anyarray_finalfn
At first, i want to use accumArrayResult and makeMdArrayResult, but it's
complicated to work with multi-dimensional arrays with those two functions.
So i combined array_cat with those function.
Currently, it cannot handle NULL arrays:
backend> select array_agg(a) from (values(null::int[])) a(a);
1: array_agg (typeid = 1007, len = -1, typmod = -1, byval = f)
----
ERROR: cannot aggregate null arrays
Regards,
--
Ali Akbar
Attachments:
array_agg_anyarray-2.patchtext/x-diff; charset=US-ASCII; name=array_agg_anyarray-2.patchDownload
*** a/src/backend/utils/adt/array_userfuncs.c
--- b/src/backend/utils/adt/array_userfuncs.c
***************
*** 16,21 ****
--- 16,51 ----
#include "utils/builtins.h"
#include "utils/lsyscache.h"
+ #include "utils/memutils.h"
+
+ /*-------------------------------------------------------------------------
+ * ArrayAggAnyArrayState:
+ * aggregate state for array_agg(anyarray)
+ *-------------------------------------------------------------------------
+ */
+ typedef struct
+ {
+ MemoryContext mcontext; /* where all the temp stuff is kept */
+ char *data; /* array of accumulated data */
+ bits8 *nullbitmap; /* bitmap of is-null flags for data */
+
+ int abytes; /* allocated length of above arrays */
+ int aitems; /* allocated length of above arrays */
+ int nbytes; /* number of used bytes in above arrays */
+ int nitems; /* number of elements in above arrays */
+ int narray; /* number of array accumulated */
+ Oid element_type; /* data type of the Datums */
+ int16 typlen; /* needed info about datatype */
+ bool typbyval;
+ char typalign;
+
+ int ndims; /* element dimensions */
+ int *dims;
+ int *lbs;
+
+ bool hasnull; /* any element has null */
+ } ArrayAggAnyArrayState;
+
/*-----------------------------------------------------------------------------
* array_push :
***************
*** 544,546 **** array_agg_finalfn(PG_FUNCTION_ARGS)
--- 574,814 ----
PG_RETURN_DATUM(result);
}
+
+ /*
+ * ARRAY_AGG(anyarray) aggregate function
+ */
+ Datum
+ array_agg_anyarray_transfn(PG_FUNCTION_ARGS)
+ {
+ MemoryContext aggcontext,
+ arr_context,
+ oldcontext;
+ ArrayAggAnyArrayState *astate;
+
+ Oid arg_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ Oid arg_elemtype = get_element_type(arg_typeid);
+ ArrayType *arg;
+ int *dims,
+ *lbs,
+ ndims,
+ nitems,
+ ndatabytes;
+ char *data;
+
+ int i;
+
+ if (arg_elemtype == InvalidOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("could not determine input data type")));
+
+ if (!AggCheckCallContext(fcinfo, &aggcontext))
+ elog(ERROR, "array_agg_anyarray_transfn called in non-aggregate context");
+
+ if (PG_ARGISNULL(1))
+ elog(ERROR, "cannot aggregate null arrays");
+
+ astate = PG_ARGISNULL(0) ? NULL : (ArrayAggAnyArrayState *) PG_GETARG_POINTER(0);
+
+ if (astate == NULL)
+ {
+ arr_context = AllocSetContextCreate(aggcontext,
+ "array_agg_anyarray_transfn",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ oldcontext = MemoryContextSwitchTo(arr_context);
+ astate = (ArrayAggAnyArrayState *) palloc(sizeof(ArrayAggAnyArrayState));
+
+ astate->mcontext = arr_context;
+ astate->abytes = 0;
+ astate->aitems = 0;
+ astate->data = NULL;
+ astate->nullbitmap = NULL;
+ astate->nitems = 0;
+ astate->narray = 0;
+ astate->element_type = arg_elemtype;
+ get_typlenbyvalalign(arg_elemtype,
+ &astate->typlen,
+ &astate->typbyval,
+ &astate->typalign);
+ }
+ else
+ {
+ oldcontext = MemoryContextSwitchTo(astate->mcontext);
+ Assert(astate->element_type == arg_elemtype);
+ }
+
+ arg = PG_GETARG_ARRAYTYPE_P(1);
+
+ ndims = ARR_NDIM(arg);
+ dims = ARR_DIMS(arg);
+ lbs = ARR_LBOUND(arg);
+ data = ARR_DATA_PTR(arg);
+ nitems = ArrayGetNItems(ndims, dims);
+
+ ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
+
+ if (astate->data == NULL)
+ {
+ if (ndims + 1 > MAXDIM)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ ndims + 1, MAXDIM)));
+
+ astate->ndims = ndims;
+ astate->dims = (int *) palloc(ndims * sizeof(int));
+ astate->lbs = (int *) palloc(ndims * sizeof(int));
+ memcpy(astate->dims, dims, ndims * sizeof(int));
+ memcpy(astate->lbs, lbs, ndims * sizeof(int));
+
+ astate->abytes = 64 * (ndatabytes == 0 ? 1 : ndatabytes);
+ astate->aitems = 64 * nitems;
+ astate->data = (char *) palloc(astate->abytes);
+
+ memcpy(astate->data, data, ndatabytes);
+ astate->nbytes = ndatabytes;
+ astate->nitems = nitems;
+
+ if (ARR_HASNULL(arg))
+ {
+ astate->hasnull = true;
+ astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
+ array_bitmap_copy(astate->nullbitmap, 0,
+ ARR_NULLBITMAP(arg), 0,
+ nitems);
+ }
+ else
+ astate->hasnull = false;
+ }
+ else
+ {
+ if (astate->ndims != ndims)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot aggregate incompatible arrays"),
+ errdetail("Arrays of %d and %d dimensions are not "
+ "compatible for concatenation.",
+ astate->ndims, ndims)));
+
+ for (i = 0; i < ndims; i++)
+ if (astate->dims[i] != dims[i] || astate->lbs[i] != lbs[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot aggregate incompatible arrays"),
+ errdetail("Arrays with differing element dimensions are "
+ "not compatible for concatenation.")));
+
+ if (astate->nbytes + ndatabytes >= astate->abytes)
+ {
+ astate->nbytes *= 2;
+ astate->data = (char *)
+ repalloc(astate->data, astate->nbytes);
+ }
+ if (astate->nitems + nitems >= astate->aitems)
+ {
+ astate->aitems *= 2;
+ astate->nullbitmap = (bits8 *)
+ repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
+ }
+
+ memcpy(astate->data + astate->nbytes, data, ndatabytes);
+ astate->nbytes += ndatabytes;
+
+ if (ARR_HASNULL(arg) || astate->hasnull)
+ {
+ if (!astate->hasnull)
+ {
+ astate->hasnull = true;
+ astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
+ array_bitmap_copy(astate->nullbitmap, 0,
+ NULL, 0,
+ astate->nitems);
+ }
+ array_bitmap_copy(astate->nullbitmap, astate->nitems,
+ ARR_NULLBITMAP(arg), 0,
+ nitems);
+ astate->nitems += nitems;
+ }
+ }
+ astate->narray += 1;
+
+ MemoryContextSwitchTo(oldcontext);
+
+ /*
+ * The transition type for array_agg() is declared to be "internal", which
+ * is a pass-by-value type the same size as a pointer. So we can safely
+ * pass the pointer through nodeAgg.c's machinations.
+ */
+ PG_RETURN_POINTER(astate);
+ }
+
+ Datum
+ array_agg_anyarray_finalfn(PG_FUNCTION_ARGS)
+ {
+ MemoryContext oldcontext;
+
+ ArrayType *result;
+ ArrayAggAnyArrayState *astate;
+
+ int dataoffset,
+ nbytes,
+ ndims;
+
+ /*
+ * Test for null before Asserting we are in right context. This is to
+ * avoid possible Assert failure in 8.4beta installations, where it is
+ * possible for users to create NULL constants of type internal.
+ */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL(); /* returns null iff no input values */
+
+ /* cannot be called directly because of internal-type argument */
+ Assert(AggCheckCallContext(fcinfo, NULL));
+
+ oldcontext = MemoryContextSwitchTo(CurrentMemoryContext);
+
+ astate = (ArrayAggAnyArrayState *) PG_GETARG_POINTER(0);
+
+ ndims = astate->ndims + 1;
+ nbytes = astate->nbytes;
+ /* compute required space */
+ if (astate->hasnull)
+ {
+ dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, astate->nitems);
+ nbytes += dataoffset;
+ }
+ else
+ {
+ dataoffset = 0;
+ nbytes += ARR_OVERHEAD_NONULLS(ndims);
+ }
+
+ result = (ArrayType *) palloc0(nbytes);
+ SET_VARSIZE(result, nbytes);
+ result->ndim = astate->ndims + 1;
+ result->dataoffset = dataoffset;
+ result->elemtype = astate->element_type;
+
+ ARR_DIMS(result)[0] = astate->narray;
+ ARR_LBOUND(result)[0] = 1;
+ memcpy(&(ARR_DIMS(result)[1]), astate->dims, (ndims - 1) * sizeof(int));
+ memcpy(&(ARR_LBOUND(result)[1]), astate->lbs, (ndims - 1) * sizeof(int));
+
+ memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
+ if (astate->hasnull)
+ array_bitmap_copy(ARR_NULLBITMAP(result), 0,
+ astate->nullbitmap, 0,
+ astate->nitems);
+
+ MemoryContextSwitchTo(oldcontext);
+
+ /* we cannot release ArrayAggAnyArrayState because sometimes
+ * aggregate final functions are re-executed. Rather, it is nodeAgg.c's
+ * responsibility to reset aggcontext when it's safe to do so
+ */
+
+ PG_RETURN_DATUM(PointerGetDatum(result));
+ }
*** a/src/include/catalog/pg_aggregate.h
--- b/src/include/catalog/pg_aggregate.h
***************
*** 275,280 **** DATA(insert ( 2901 n 0 xmlconcat2 - - - - f f 0 142 0 0 0 _null_
--- 275,281 ----
/* array */
DATA(insert ( 2335 n 0 array_agg_transfn array_agg_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
+ DATA(insert ( 6005 n 0 array_agg_anyarray_transfn array_agg_anyarray_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
/* text */
DATA(insert ( 3538 n 0 string_agg_transfn string_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ ));
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 879,889 **** DATA(insert OID = 3167 ( array_remove PGNSP PGUID 12 1 0 0 0 f f f f f f i 2
DESCR("remove any occurrences of an element from an array");
DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ ));
DESCR("replace any occurrences of an element in an array");
! DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2283" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function");
! DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2283" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
DESCR("aggregate final function");
! DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2283" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into an array");
DATA(insert OID = 3218 ( width_bucket PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "2283 2277" _null_ _null_ _null_ _null_ width_bucket_array _null_ _null_ _null_ ));
DESCR("bucket number of operand given a sorted array of bucket lower bounds");
--- 879,895 ----
DESCR("remove any occurrences of an element from an array");
DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ ));
DESCR("replace any occurrences of an element in an array");
! DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2776" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function");
! DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2776" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
DESCR("aggregate final function");
! DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2776" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
! DESCR("concatenate aggregate input into an array");
! DATA(insert OID = 6003 ( array_agg_anyarray_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_transfn _null_ _null_ _null_ ));
! DESCR("aggregate transition function");
! DATA(insert OID = 6004 ( array_agg_anyarray_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_finalfn _null_ _null_ _null_ ));
! DESCR("aggregate final function");
! DATA(insert OID = 6005 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2277" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into an array");
DATA(insert OID = 3218 ( width_bucket PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "2283 2277" _null_ _null_ _null_ _null_ width_bucket_array _null_ _null_ _null_ ));
DESCR("bucket number of operand given a sorted array of bucket lower bounds");
*** a/src/include/utils/array.h
--- b/src/include/utils/array.h
***************
*** 293,298 **** extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
--- 293,301 ----
extern Datum array_agg_transfn(PG_FUNCTION_ARGS);
extern Datum array_agg_finalfn(PG_FUNCTION_ARGS);
+ extern Datum array_agg_anyarray_transfn(PG_FUNCTION_ARGS);
+ extern Datum array_agg_anyarray_finalfn(PG_FUNCTION_ARGS);
+
/*
* prototypes for functions defined in array_typanalyze.c
*/
2014-10-12 19:37 GMT+07:00 Ali Akbar <the.apaan@gmail.com>:
Currently, it cannot handle NULL arrays:
backend> select array_agg(a) from (values(null::int[])) a(a);
1: array_agg (typeid = 1007, len = -1, typmod = -1, byval = f)
----
ERROR: cannot aggregate null arrays
While thinking about the function behavior if its input is NULL array (e.g:
NULL:int[]), i've found:
- currentpatch doesn't handle empty array correctly:
- when there is only one array to aggregate, the resulting array is
wrong
- when the first array is empty array, and the second array is also
empty array, it segfaulted
- if we see NULL array as NULL values, the resulting array cannot be
differentiated from array of null ints:
- SELECT array_agg(NULL::int[]) FROM generate_series(1,2); ---> {NULL,
NULL} with type int[]
- SELECT array_agg(NULL::int) FROM generate_series(1,2); --> {NULL,
NULL} with type int[]
Also i've found that handling NULL array is listed as BUG in TODO. The
discussion in the thread is still not finished, with last email from Tom
Lane (/messages/by-id/18866.1226025853@sss.pgh.pa.us):
array_lower raise exception if array is empty (there are no dimensions
to inquire about)
array_upper raise exception if array is empty (there are no dimensions
to inquire about)Well, these beg the question: is an empty array zero-dimensional, or
is it a one-dimensional array of no elements, or perhaps both of those
as well as higher-dimensional cases where any axis has zero elements,
or ???It's really all kind of messy ... we need to trade off simplicity of
definition, ease of use, backwards compatibility, and standards
compliance (though the standard has only 1-D arrays so it's of just
limited help here).
So, is there any idea how we will handle NULL and empty array in
array_agg(anyarray)?
I propose we just reject those input because the output will make no sense:
- array_agg(NULL::int[]) --> the result will be indistinguished from
array_agg of NULL ints.
- array_agg('{}'::int[]) --> how we determine the dimension of the result?
is it 0? Or the result will be just an empty array {} ?
Regards,
--
Ali Akbar
So, is there any idea how we will handle NULL and empty array in
array_agg(anyarray)?
I propose we just reject those input because the output will make no sense:
- array_agg(NULL::int[]) --> the result will be indistinguished from
array_agg of NULL ints.
- array_agg('{}'::int[]) --> how we determine the dimension of the result?
is it 0? Or the result will be just an empty array {} ?
This updated patch rejects NULL and {} arrays as noted above.
Regards,
--
Ali Akbar
Attachments:
array_agg_anyarray-3.patchtext/x-patch; charset=US-ASCII; name=array_agg_anyarray-3.patchDownload
*** a/src/backend/utils/adt/array_userfuncs.c
--- b/src/backend/utils/adt/array_userfuncs.c
***************
*** 16,21 ****
--- 16,51 ----
#include "utils/builtins.h"
#include "utils/lsyscache.h"
+ #include "utils/memutils.h"
+
+ /*-------------------------------------------------------------------------
+ * ArrayAggAnyArrayState:
+ * aggregate state for array_agg(anyarray)
+ *-------------------------------------------------------------------------
+ */
+ typedef struct
+ {
+ MemoryContext mcontext; /* where all the temp stuff is kept */
+ char *data; /* array of accumulated data */
+ bits8 *nullbitmap; /* bitmap of is-null flags for data */
+
+ int abytes; /* allocated length of above arrays */
+ int aitems; /* allocated length of above arrays */
+ int nbytes; /* number of used bytes in above arrays */
+ int nitems; /* number of elements in above arrays */
+ int narray; /* number of array accumulated */
+ Oid element_type; /* data type of the Datums */
+ int16 typlen; /* needed info about datatype */
+ bool typbyval;
+ char typalign;
+
+ int ndims; /* element dimensions */
+ int *dims;
+ int *lbs;
+
+ bool hasnull; /* any element has null */
+ } ArrayAggAnyArrayState;
+
/*-----------------------------------------------------------------------------
* array_push :
***************
*** 544,546 **** array_agg_finalfn(PG_FUNCTION_ARGS)
--- 574,821 ----
PG_RETURN_DATUM(result);
}
+
+ /*
+ * ARRAY_AGG(anyarray) aggregate function
+ */
+ Datum
+ array_agg_anyarray_transfn(PG_FUNCTION_ARGS)
+ {
+ MemoryContext aggcontext,
+ arr_context,
+ oldcontext;
+ ArrayAggAnyArrayState *astate;
+
+ Oid arg_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ Oid arg_elemtype = get_element_type(arg_typeid);
+ ArrayType *arg;
+ int *dims,
+ *lbs,
+ ndims,
+ nitems,
+ ndatabytes;
+ char *data;
+
+ int i;
+
+ if (arg_elemtype == InvalidOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("could not determine input data type")));
+
+ if (!AggCheckCallContext(fcinfo, &aggcontext))
+ elog(ERROR, "array_agg_anyarray_transfn called in non-aggregate context");
+
+ if (PG_ARGISNULL(1))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot aggregate null arrays")));
+
+ astate = PG_ARGISNULL(0) ? NULL : (ArrayAggAnyArrayState *) PG_GETARG_POINTER(0);
+
+ if (astate == NULL)
+ {
+ arr_context = AllocSetContextCreate(aggcontext,
+ "array_agg_anyarray_transfn",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ oldcontext = MemoryContextSwitchTo(arr_context);
+ astate = (ArrayAggAnyArrayState *) palloc(sizeof(ArrayAggAnyArrayState));
+
+ astate->mcontext = arr_context;
+ astate->abytes = 0;
+ astate->aitems = 0;
+ astate->data = NULL;
+ astate->nullbitmap = NULL;
+ astate->nitems = 0;
+ astate->narray = 0;
+ astate->element_type = arg_elemtype;
+ get_typlenbyvalalign(arg_elemtype,
+ &astate->typlen,
+ &astate->typbyval,
+ &astate->typalign);
+ }
+ else
+ {
+ oldcontext = MemoryContextSwitchTo(astate->mcontext);
+ Assert(astate->element_type == arg_elemtype);
+ }
+
+ arg = PG_GETARG_ARRAYTYPE_P(1);
+
+ ndims = ARR_NDIM(arg);
+ dims = ARR_DIMS(arg);
+ lbs = ARR_LBOUND(arg);
+ data = ARR_DATA_PTR(arg);
+ nitems = ArrayGetNItems(ndims, dims);
+
+ ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
+
+ if (astate->data == NULL)
+ {
+ if (ndims == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot aggregate empty arrays")));
+
+ if (ndims + 1 > MAXDIM)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ ndims + 1, MAXDIM)));
+
+ astate->ndims = ndims;
+ astate->dims = (int *) palloc(ndims * sizeof(int));
+ astate->lbs = (int *) palloc(ndims * sizeof(int));
+ memcpy(astate->dims, dims, ndims * sizeof(int));
+ memcpy(astate->lbs, lbs, ndims * sizeof(int));
+
+ astate->abytes = 64 * (ndatabytes == 0 ? 1 : ndatabytes);
+ astate->aitems = 64 * nitems;
+ astate->data = (char *) palloc(astate->abytes);
+
+ memcpy(astate->data, data, ndatabytes);
+ astate->nbytes = ndatabytes;
+ astate->nitems = nitems;
+
+ if (ARR_HASNULL(arg))
+ {
+ astate->hasnull = true;
+ astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
+ array_bitmap_copy(astate->nullbitmap, 0,
+ ARR_NULLBITMAP(arg), 0,
+ nitems);
+ }
+ else
+ astate->hasnull = false;
+ }
+ else
+ {
+ if (astate->ndims != ndims)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot aggregate incompatible arrays"),
+ errdetail("Arrays of %d and %d dimensions are not "
+ "compatible for concatenation.",
+ astate->ndims, ndims)));
+
+ for (i = 0; i < ndims; i++)
+ if (astate->dims[i] != dims[i] || astate->lbs[i] != lbs[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot aggregate incompatible arrays"),
+ errdetail("Arrays with differing element dimensions are "
+ "not compatible for concatenation.")));
+
+ if (astate->nbytes + ndatabytes >= astate->abytes)
+ {
+ astate->nbytes *= 2;
+ astate->data = (char *)
+ repalloc(astate->data, astate->nbytes);
+ }
+ if (astate->nitems + nitems >= astate->aitems)
+ {
+ astate->aitems *= 2;
+ astate->nullbitmap = (bits8 *)
+ repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
+ }
+
+ memcpy(astate->data + astate->nbytes, data, ndatabytes);
+ astate->nbytes += ndatabytes;
+
+ if (ARR_HASNULL(arg) || astate->hasnull)
+ {
+ if (!astate->hasnull)
+ {
+ astate->hasnull = true;
+ astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
+ array_bitmap_copy(astate->nullbitmap, 0,
+ NULL, 0,
+ astate->nitems);
+ }
+ array_bitmap_copy(astate->nullbitmap, astate->nitems,
+ ARR_NULLBITMAP(arg), 0,
+ nitems);
+ astate->nitems += nitems;
+ }
+ }
+ astate->narray += 1;
+
+ MemoryContextSwitchTo(oldcontext);
+
+ /*
+ * The transition type for array_agg() is declared to be "internal", which
+ * is a pass-by-value type the same size as a pointer. So we can safely
+ * pass the pointer through nodeAgg.c's machinations.
+ */
+ PG_RETURN_POINTER(astate);
+ }
+
+ Datum
+ array_agg_anyarray_finalfn(PG_FUNCTION_ARGS)
+ {
+ MemoryContext oldcontext;
+
+ ArrayType *result;
+ ArrayAggAnyArrayState *astate;
+
+ int dataoffset,
+ nbytes,
+ ndims;
+
+ /*
+ * Test for null before Asserting we are in right context. This is to
+ * avoid possible Assert failure in 8.4beta installations, where it is
+ * possible for users to create NULL constants of type internal.
+ */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL(); /* returns null iff no input values */
+
+ /* cannot be called directly because of internal-type argument */
+ Assert(AggCheckCallContext(fcinfo, NULL));
+
+ oldcontext = MemoryContextSwitchTo(CurrentMemoryContext);
+
+ astate = (ArrayAggAnyArrayState *) PG_GETARG_POINTER(0);
+
+ ndims = astate->ndims + 1;
+ nbytes = astate->nbytes;
+ /* compute required space */
+ if (astate->hasnull)
+ {
+ dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, astate->nitems);
+ nbytes += dataoffset;
+ }
+ else
+ {
+ dataoffset = 0;
+ nbytes += ARR_OVERHEAD_NONULLS(ndims);
+ }
+
+ result = (ArrayType *) palloc0(nbytes);
+ SET_VARSIZE(result, nbytes);
+ result->ndim = astate->ndims + 1;
+ result->dataoffset = dataoffset;
+ result->elemtype = astate->element_type;
+
+ ARR_DIMS(result)[0] = astate->narray;
+ ARR_LBOUND(result)[0] = 1;
+ memcpy(&(ARR_DIMS(result)[1]), astate->dims, (ndims - 1) * sizeof(int));
+ memcpy(&(ARR_LBOUND(result)[1]), astate->lbs, (ndims - 1) * sizeof(int));
+
+ memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
+ if (astate->hasnull)
+ array_bitmap_copy(ARR_NULLBITMAP(result), 0,
+ astate->nullbitmap, 0,
+ astate->nitems);
+
+ MemoryContextSwitchTo(oldcontext);
+
+ /* we cannot release ArrayAggAnyArrayState because sometimes
+ * aggregate final functions are re-executed. Rather, it is nodeAgg.c's
+ * responsibility to reset aggcontext when it's safe to do so
+ */
+
+ PG_RETURN_DATUM(PointerGetDatum(result));
+ }
*** a/src/include/catalog/pg_aggregate.h
--- b/src/include/catalog/pg_aggregate.h
***************
*** 275,280 **** DATA(insert ( 2901 n 0 xmlconcat2 - - - - f f 0 142 0 0 0 _null_
--- 275,281 ----
/* array */
DATA(insert ( 2335 n 0 array_agg_transfn array_agg_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
+ DATA(insert ( 6005 n 0 array_agg_anyarray_transfn array_agg_anyarray_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
/* text */
DATA(insert ( 3538 n 0 string_agg_transfn string_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ ));
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 879,889 **** DATA(insert OID = 3167 ( array_remove PGNSP PGUID 12 1 0 0 0 f f f f f f i 2
DESCR("remove any occurrences of an element from an array");
DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ ));
DESCR("replace any occurrences of an element in an array");
! DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2283" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function");
! DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2283" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
DESCR("aggregate final function");
! DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2283" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into an array");
DATA(insert OID = 3218 ( width_bucket PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "2283 2277" _null_ _null_ _null_ _null_ width_bucket_array _null_ _null_ _null_ ));
DESCR("bucket number of operand given a sorted array of bucket lower bounds");
--- 879,895 ----
DESCR("remove any occurrences of an element from an array");
DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ ));
DESCR("replace any occurrences of an element in an array");
! DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2776" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function");
! DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2776" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
DESCR("aggregate final function");
! DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2776" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
! DESCR("concatenate aggregate input into an array");
! DATA(insert OID = 6003 ( array_agg_anyarray_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_transfn _null_ _null_ _null_ ));
! DESCR("aggregate transition function");
! DATA(insert OID = 6004 ( array_agg_anyarray_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_finalfn _null_ _null_ _null_ ));
! DESCR("aggregate final function");
! DATA(insert OID = 6005 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2277" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into an array");
DATA(insert OID = 3218 ( width_bucket PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "2283 2277" _null_ _null_ _null_ _null_ width_bucket_array _null_ _null_ _null_ ));
DESCR("bucket number of operand given a sorted array of bucket lower bounds");
*** a/src/include/utils/array.h
--- b/src/include/utils/array.h
***************
*** 293,298 **** extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
--- 293,301 ----
extern Datum array_agg_transfn(PG_FUNCTION_ARGS);
extern Datum array_agg_finalfn(PG_FUNCTION_ARGS);
+ extern Datum array_agg_anyarray_transfn(PG_FUNCTION_ARGS);
+ extern Datum array_agg_anyarray_finalfn(PG_FUNCTION_ARGS);
+
/*
* prototypes for functions defined in array_typanalyze.c
*/
Hi
2014-10-19 8:02 GMT+02:00 Ali Akbar <the.apaan@gmail.com>:
So, is there any idea how we will handle NULL and empty array in
array_agg(anyarray)?
I propose we just reject those input because the output will make no
sense:
- array_agg(NULL::int[]) --> the result will be indistinguished from
array_agg of NULL ints.
- array_agg('{}'::int[]) --> how we determine the dimension of the
result? is it 0? Or the result will be just an empty array {} ?This updated patch rejects NULL and {} arrays as noted above.
I agree with your proposal. I have a few comments to design:
1. patch doesn't hold documentation and regress tests, please append it.
2. this functionality (multidimensional aggregation) can be interesting
more times, so maybe some interface like array builder should be preferred.
3. array_agg was consistent with array(subselect), so it should be fixed too
postgres=# select array_agg(a) from test;
array_agg
-----------------------
{{1,2,3,4},{1,2,3,4}}
(1 row)
postgres=# select array(select a from test);
ERROR: could not find array type for data type integer[]
4. why you use a magic constant (64) there?
+ astate->abytes = 64 * (ndatabytes == 0 ? 1 : ndatabytes);
+ astate->aitems = 64 * nitems;
+ astate->nullbitmap = (bits8 *)
+ repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
Regards
Pavel
Show quoted text
Regards,
--
Ali Akbar--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Thanks for the review
2014-10-22 20:51 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
I agree with your proposal. I have a few comments to design:
1. patch doesn't hold documentation and regress tests, please append it.
OK, i'll add the documentation and regression test
2. this functionality (multidimensional aggregation) can be interesting
more times, so maybe some interface like array builder should be preferred.
We already have accumArrayResult and makeArrayResult/makeMdArrayResult,
haven't we?
Actually array_agg(anyarray) can be implemented by using accumArrayResult
and makeMdArrayResult, but in the process we will deconstruct the array
data and null bit-flags into ArrayBuildState->dvalues and dnulls. And we
will reconstruct it through makeMdArrayResult. I think this way it's not
efficient, so i decided to reimplement it and memcpy the array data and
null flags as-is.
In other places, i think it's clearer if we just use accumArrayResult and
makeArrayResult/makeMdArrayResult as API for building (multidimensional)
arrays.
3. array_agg was consistent with array(subselect), so it should be fixed
toopostgres=# select array_agg(a) from test;
array_agg
-----------------------
{{1,2,3,4},{1,2,3,4}}
(1 row)postgres=# select array(select a from test);
ERROR: could not find array type for data type integer[]
I'm pretty new in postgresql development. Can you point out where is
array(subselect) implemented?
4. why you use a magic constant (64) there?
+ astate->abytes = 64 * (ndatabytes == 0 ? 1 : ndatabytes); + astate->aitems = 64 * nitems;+ astate->nullbitmap = (bits8 *) + repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
just follow the arbitrary size choosen in accumArrayResult (arrayfuncs.c):
astate->alen = 64; /* arbitrary starting array size */
it can be any number not too small and too big. Too small, and we will
realloc shortly. Too big, we will end up wasting memory.
Regards,
--
Ali Akbar
2014-10-22 16:58 GMT+02:00 Ali Akbar <the.apaan@gmail.com>:
Thanks for the review
2014-10-22 20:51 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
I agree with your proposal. I have a few comments to design:
1. patch doesn't hold documentation and regress tests, please append it.
OK, i'll add the documentation and regression test
2. this functionality (multidimensional aggregation) can be interesting
more times, so maybe some interface like array builder should be preferred.We already have accumArrayResult and makeArrayResult/makeMdArrayResult,
haven't we?Actually array_agg(anyarray) can be implemented by using accumArrayResult
and makeMdArrayResult, but in the process we will deconstruct the array
data and null bit-flags into ArrayBuildState->dvalues and dnulls. And we
will reconstruct it through makeMdArrayResult. I think this way it's not
efficient, so i decided to reimplement it and memcpy the array data and
null flags as-is.
aha, so isn't better to fix a performance for accumArrayResult ?
In other places, i think it's clearer if we just use accumArrayResult and
makeArrayResult/makeMdArrayResult as API for building (multidimensional)
arrays.3. array_agg was consistent with array(subselect), so it should be fixed
toopostgres=# select array_agg(a) from test;
array_agg
-----------------------
{{1,2,3,4},{1,2,3,4}}
(1 row)postgres=# select array(select a from test);
ERROR: could not find array type for data type integer[]I'm pretty new in postgresql development. Can you point out where is
array(subselect) implemented?
where you can start?
postgres=# explain select array(select a from test);
ERROR: 42704: could not find array type for data type integer[]
LOCATION: exprType, nodeFuncs.c:116
look to code ... your magic keyword is a ARRAY_SUBLINK .. search in
postgresql sources this keyword
4. why you use a magic constant (64) there?
+ astate->abytes = 64 * (ndatabytes == 0 ? 1 : ndatabytes); + astate->aitems = 64 * nitems;+ astate->nullbitmap = (bits8 *) + repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);just follow the arbitrary size choosen in accumArrayResult (arrayfuncs.c):
astate->alen = 64; /* arbitrary starting array size */it can be any number not too small and too big. Too small, and we will
realloc shortly. Too big, we will end up wasting memory.
you can try to alloc 1KB instead as start -- it is used more times in
Postgres. Then a overhead is max 1KB per agg call - what is acceptable.
You take this value from accumArrayResult, but it is targeted for shorted
scalars - you should to expect so any array will be much larger.
Show quoted text
Regards,
--
Ali Akbar
2014-10-22 22:48 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
2014-10-22 16:58 GMT+02:00 Ali Akbar <the.apaan@gmail.com>:
Thanks for the review
2014-10-22 20:51 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
I agree with your proposal. I have a few comments to design:
1. patch doesn't hold documentation and regress tests, please append it.
OK, i'll add the documentation and regression test
2. this functionality (multidimensional aggregation) can be interesting
more times, so maybe some interface like array builder should be preferred.We already have accumArrayResult and makeArrayResult/makeMdArrayResult,
haven't we?Actually array_agg(anyarray) can be implemented by using accumArrayResult
and makeMdArrayResult, but in the process we will deconstruct the array
data and null bit-flags into ArrayBuildState->dvalues and dnulls. And we
will reconstruct it through makeMdArrayResult. I think this way it's not
efficient, so i decided to reimplement it and memcpy the array data and
null flags as-is.aha, so isn't better to fix a performance for accumArrayResult ?
Ok, i'll go this route. While reading the code of array(subselect) as you
pointed below, i think the easiest way to implement support for both
array_agg(anyarray) and array(subselect) is to change accumArrayResult so
it operates both with scalar datum(s) and array datums, with performance
optimization for the latter.
In other places, i think it's clearer if we just use accumArrayResult and
makeArrayResult/makeMdArrayResult as API for building (multidimensional)
arrays.3. array_agg was consistent with array(subselect), so it should be fixed
toopostgres=# select array_agg(a) from test;
array_agg
-----------------------
{{1,2,3,4},{1,2,3,4}}
(1 row)postgres=# select array(select a from test);
ERROR: could not find array type for data type integer[]I'm pretty new in postgresql development. Can you point out where is
array(subselect) implemented?where you can start?
postgres=# explain select array(select a from test);
ERROR: 42704: could not find array type for data type integer[]
LOCATION: exprType, nodeFuncs.c:116look to code ... your magic keyword is a ARRAY_SUBLINK .. search in
postgresql sources this keyword
Found it. Thanks.
On a side note in postgres array type for integer[] is still integer[], but
in pg_type, integer[] has no array type. I propose we consider to change it
so array type of anyarray is itself (not in this patch, of course, because
it is a big change). Consider the following code in nodeFuncs.c:109
if (sublink->subLinkType == ARRAY_SUBLINK)
{
type = get_array_type(type);
if (!OidIsValid(type))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find array type for data type %s",
format_type_be(exprType((Node *) tent->expr)))));
}
to implement array(subselect) you pointed above, we must specially checks
for array types. Quick search on get_array_type returns 10 places.
I'll think more about this later. For this patch, i'll go without changes
in pg_type.h.
4. why you use a magic constant (64) there?
+ astate->abytes = 64 * (ndatabytes == 0 ? 1 : ndatabytes); + astate->aitems = 64 * nitems;+ astate->nullbitmap = (bits8 *) + repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);just follow the arbitrary size choosen in accumArrayResult (arrayfuncs.c):
astate->alen = 64; /* arbitrary starting array size */it can be any number not too small and too big. Too small, and we will
realloc shortly. Too big, we will end up wasting memory.you can try to alloc 1KB instead as start -- it is used more times in
Postgres. Then a overhead is max 1KB per agg call - what is acceptable.You take this value from accumArrayResult, but it is targeted for shorted
scalars - you should to expect so any array will be much larger.
Ok.
Regards,
--
Ali Akbar
2014-10-23 4:00 GMT+02:00 Ali Akbar <the.apaan@gmail.com>:
2014-10-22 22:48 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
2014-10-22 16:58 GMT+02:00 Ali Akbar <the.apaan@gmail.com>:
Thanks for the review
2014-10-22 20:51 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
I agree with your proposal. I have a few comments to design:
1. patch doesn't hold documentation and regress tests, please append it.
OK, i'll add the documentation and regression test
2. this functionality (multidimensional aggregation) can be interesting
more times, so maybe some interface like array builder should be preferred.We already have accumArrayResult and makeArrayResult/makeMdArrayResult,
haven't we?Actually array_agg(anyarray) can be implemented by using
accumArrayResult and makeMdArrayResult, but in the process we will
deconstruct the array data and null bit-flags into ArrayBuildState->dvalues
and dnulls. And we will reconstruct it through makeMdArrayResult. I think
this way it's not efficient, so i decided to reimplement it and memcpy the
array data and null flags as-is.aha, so isn't better to fix a performance for accumArrayResult ?
Ok, i'll go this route. While reading the code of array(subselect) as you
pointed below, i think the easiest way to implement support for both
array_agg(anyarray) and array(subselect) is to change accumArrayResult so
it operates both with scalar datum(s) and array datums, with performance
optimization for the latter.In other places, i think it's clearer if we just use accumArrayResult and
makeArrayResult/makeMdArrayResult as API for building (multidimensional)
arrays.3. array_agg was consistent with array(subselect), so it should be
fixed toopostgres=# select array_agg(a) from test;
array_agg
-----------------------
{{1,2,3,4},{1,2,3,4}}
(1 row)postgres=# select array(select a from test);
ERROR: could not find array type for data type integer[]I'm pretty new in postgresql development. Can you point out where is
array(subselect) implemented?where you can start?
postgres=# explain select array(select a from test);
ERROR: 42704: could not find array type for data type integer[]
LOCATION: exprType, nodeFuncs.c:116look to code ... your magic keyword is a ARRAY_SUBLINK .. search in
postgresql sources this keywordFound it. Thanks.
On a side note in postgres array type for integer[] is still integer[],
but in pg_type, integer[] has no array type. I propose we consider to
change it so array type of anyarray is itself (not in this patch, of
course, because it is a big change). Consider the following code in
nodeFuncs.c:109
yes, it is true - this is really big change and maybe needs separate
discuss - ***if we allow cycle there. I am not sure about possible side
effects***.
Maybe this change is not necessary, you can fix a check only ... "if type
is not array or if get_array_type is null raise a error"
if (sublink->subLinkType == ARRAY_SUBLINK)
{
type = get_array_type(type);
if (!OidIsValid(type))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find array type for data type %s",
format_type_be(exprType((Node *) tent->expr)))));
}to implement array(subselect) you pointed above, we must specially checks
for array types. Quick search on get_array_type returns 10 places.
attention: probably we don't would to allow arrays everywhere.
Show quoted text
I'll think more about this later. For this patch, i'll go without changes
in pg_type.h.4. why you use a magic constant (64) there?
+ astate->abytes = 64 * (ndatabytes == 0 ? 1 : ndatabytes); + astate->aitems = 64 * nitems;+ astate->nullbitmap = (bits8 *) + repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);just follow the arbitrary size choosen in accumArrayResult
(arrayfuncs.c):
astate->alen = 64; /* arbitrary starting array size */it can be any number not too small and too big. Too small, and we will
realloc shortly. Too big, we will end up wasting memory.you can try to alloc 1KB instead as start -- it is used more times in
Postgres. Then a overhead is max 1KB per agg call - what is acceptable.You take this value from accumArrayResult, but it is targeted for shorted
scalars - you should to expect so any array will be much larger.Ok.
Regards,
--
Ali Akbar
Updated patch attached.
2014-10-22 20:51 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
I agree with your proposal. I have a few comments to design:
1. patch doesn't hold documentation and regress tests, please append
it.
i've added some regression tests in arrays.sql and aggregate.sql.
I've only found the documentation of array_agg. Where is the doc for
array(subselect) defined?
2. this functionality (multidimensional aggregation) can be interesting
more times, so maybe some interface like array builder should be preferred.
We already have accumArrayResult and makeArrayResult/makeMdArrayResult,
haven't we?Actually array_agg(anyarray) can be implemented by using
accumArrayResult and makeMdArrayResult, but in the process we will
deconstruct the array data and null bit-flags into ArrayBuildState->dvalues
and dnulls. And we will reconstruct it through makeMdArrayResult. I think
this way it's not efficient, so i decided to reimplement it and memcpy the
array data and null flags as-is.aha, so isn't better to fix a performance for accumArrayResult ?
Ok, i'll go this route. While reading the code of array(subselect) as you
pointed below, i think the easiest way to implement support for both
array_agg(anyarray) and array(subselect) is to change accumArrayResult so
it operates both with scalar datum(s) and array datums, with performance
optimization for the latter.
implemented it by modifying ArrayBuildState to ArrayBuildStateArray and
ArrayBuildStateScalar (do you have any idea for better struct naming?)
In other places, i think it's clearer if we just use accumArrayResult and
makeArrayResult/makeMdArrayResult as API for building (multidimensional)
arrays.3. array_agg was consistent with array(subselect), so it should be
fixed toopostgres=# select array_agg(a) from test;
array_agg
-----------------------
{{1,2,3,4},{1,2,3,4}}
(1 row)postgres=# select array(select a from test);
ERROR: could not find array type for data type integer[]I'm pretty new in postgresql development. Can you point out where is
array(subselect) implemented?where you can start?
postgres=# explain select array(select a from test);
ERROR: 42704: could not find array type for data type integer[]
LOCATION: exprType, nodeFuncs.c:116look to code ... your magic keyword is a ARRAY_SUBLINK .. search in
postgresql sources this keywordattention: probably we don't would to allow arrays everywhere.
I've changed the places where i think it's appropriate.
4. why you use a magic constant (64) there?
+ astate->abytes = 64 * (ndatabytes == 0 ? 1 : ndatabytes); + astate->aitems = 64 * nitems;+ astate->nullbitmap = (bits8 *) + repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);just follow the arbitrary size choosen in accumArrayResult
(arrayfuncs.c):
astate->alen = 64; /* arbitrary starting array size */it can be any number not too small and too big. Too small, and we will
realloc shortly. Too big, we will end up wasting memory.you can try to alloc 1KB instead as start -- it is used more times in
Postgres. Then a overhead is max 1KB per agg call - what is acceptable.You take this value from accumArrayResult, but it is targeted for
shorted scalars - you should to expect so any array will be much larger.
this patch allocates 1KB (1024 bytes) if the ndatabytes is < 512bytes. If
it is larger, it allocates 4 * size. For nullbitmap, it allocates 4 *
number of items in array.
Regards,
--
Ali Akbar
Attachments:
array_agg_anyarray-4.patchtext/x-patch; charset=US-ASCII; name=array_agg_anyarray-4.patchDownload
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 12046,12051 **** NULL baz</literallayout>(3 rows)</entry>
--- 12046,12067 ----
<row>
<entry>
<indexterm>
+ <primary>array_agg</primary>
+ </indexterm>
+ <function>array_agg(<replaceable class="parameter">anyarray</replaceable>)</function>
+ </entry>
+ <entry>
+ any
+ </entry>
+ <entry>
+ the same array type as input type
+ </entry>
+ <entry>input arrays, aggregated into higher-order multidimesional array. Rejects NULL and empty array as input.</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>average</primary>
</indexterm>
<indexterm>
*** a/src/backend/nodes/nodeFuncs.c
--- b/src/backend/nodes/nodeFuncs.c
***************
*** 108,119 **** exprType(const Node *expr)
type = exprType((Node *) tent->expr);
if (sublink->subLinkType == ARRAY_SUBLINK)
{
! type = get_array_type(type);
! if (!OidIsValid(type))
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_OBJECT),
! errmsg("could not find array type for data type %s",
! format_type_be(exprType((Node *) tent->expr)))));
}
}
else if (sublink->subLinkType == MULTIEXPR_SUBLINK)
--- 108,123 ----
type = exprType((Node *) tent->expr);
if (sublink->subLinkType == ARRAY_SUBLINK)
{
! if (!OidIsValid(get_element_type(type)))
! {
! /* not array, so check for its array type */
! type = get_array_type(type);
! if (!OidIsValid(type))
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_OBJECT),
! errmsg("could not find array type for data type %s",
! format_type_be(exprType((Node *) tent->expr)))));
! }
}
}
else if (sublink->subLinkType == MULTIEXPR_SUBLINK)
***************
*** 139,150 **** exprType(const Node *expr)
type = subplan->firstColType;
if (subplan->subLinkType == ARRAY_SUBLINK)
{
! type = get_array_type(type);
! if (!OidIsValid(type))
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_OBJECT),
! errmsg("could not find array type for data type %s",
! format_type_be(subplan->firstColType))));
}
}
else if (subplan->subLinkType == MULTIEXPR_SUBLINK)
--- 143,158 ----
type = subplan->firstColType;
if (subplan->subLinkType == ARRAY_SUBLINK)
{
! if (!OidIsValid(get_element_type(type)))
! {
! /* not array, so check for its array type */
! type = get_array_type(type);
! if (!OidIsValid(type))
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_OBJECT),
! errmsg("could not find array type for data type %s",
! format_type_be(subplan->firstColType))));
! }
}
}
else if (subplan->subLinkType == MULTIEXPR_SUBLINK)
*** a/src/backend/optimizer/plan/subselect.c
--- b/src/backend/optimizer/plan/subselect.c
***************
*** 668,677 **** build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
Assert(!te->resjunk);
Assert(testexpr == NULL);
! arraytype = get_array_type(exprType((Node *) te->expr));
! if (!OidIsValid(arraytype))
! elog(ERROR, "could not find array type for datatype %s",
! format_type_be(exprType((Node *) te->expr)));
prm = generate_new_param(root,
arraytype,
exprTypmod((Node *) te->expr),
--- 668,683 ----
Assert(!te->resjunk);
Assert(testexpr == NULL);
!
! arraytype = exprType((Node *) te->expr);
! if (!OidIsValid(get_element_type(arraytype)))
! {
! /* not array, so get the array type */
! arraytype = get_array_type(exprType((Node *) te->expr));
! if (!OidIsValid(arraytype))
! elog(ERROR, "could not find array type for datatype %s",
! format_type_be(exprType((Node *) te->expr)));
! }
prm = generate_new_param(root,
arraytype,
exprTypmod((Node *) te->expr),
*** a/src/backend/utils/adt/array_userfuncs.c
--- b/src/backend/utils/adt/array_userfuncs.c
***************
*** 16,22 ****
#include "utils/builtins.h"
#include "utils/lsyscache.h"
-
/*-----------------------------------------------------------------------------
* array_push :
* push an element onto either end of a one-dimensional array
--- 16,21 ----
*** a/src/backend/utils/adt/arrayfuncs.c
--- b/src/backend/utils/adt/arrayfuncs.c
***************
*** 145,151 **** static int width_bucket_array_variable(Datum operand,
Oid collation,
TypeCacheEntry *typentry);
-
/*
* array_in :
* converts an array from the external format in "string" to
--- 145,150 ----
*** a/src/include/catalog/pg_aggregate.h
--- b/src/include/catalog/pg_aggregate.h
***************
*** 275,280 **** DATA(insert ( 2901 n 0 xmlconcat2 - - - - f f 0 142 0 0 0 _null_
--- 275,281 ----
/* array */
DATA(insert ( 2335 n 0 array_agg_transfn array_agg_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
+ DATA(insert ( 6005 n 0 array_agg_anyarray_transfn array_agg_anyarray_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
/* text */
DATA(insert ( 3538 n 0 string_agg_transfn string_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ ));
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 879,889 **** DATA(insert OID = 3167 ( array_remove PGNSP PGUID 12 1 0 0 0 f f f f f f i 2
DESCR("remove any occurrences of an element from an array");
DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ ));
DESCR("replace any occurrences of an element in an array");
! DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2283" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function");
! DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2283" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
DESCR("aggregate final function");
! DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2283" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into an array");
DATA(insert OID = 3218 ( width_bucket PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "2283 2277" _null_ _null_ _null_ _null_ width_bucket_array _null_ _null_ _null_ ));
DESCR("bucket number of operand given a sorted array of bucket lower bounds");
--- 879,895 ----
DESCR("remove any occurrences of an element from an array");
DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ ));
DESCR("replace any occurrences of an element in an array");
! DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2776" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function");
! DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2776" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
DESCR("aggregate final function");
! DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2776" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
! DESCR("concatenate aggregate input into an array");
! DATA(insert OID = 6003 ( array_agg_anyarray_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_transfn _null_ _null_ _null_ ));
! DESCR("aggregate transition function");
! DATA(insert OID = 6004 ( array_agg_anyarray_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_finalfn _null_ _null_ _null_ ));
! DESCR("aggregate final function");
! DATA(insert OID = 6005 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2277" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into an array");
DATA(insert OID = 3218 ( width_bucket PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "2283 2277" _null_ _null_ _null_ _null_ width_bucket_array _null_ _null_ _null_ ));
DESCR("bucket number of operand given a sorted array of bucket lower bounds");
*** a/src/include/utils/array.h
--- b/src/include/utils/array.h
***************
*** 76,94 **** typedef struct
/*
* working state for accumArrayResult() and friends
*/
typedef struct ArrayBuildState
{
MemoryContext mcontext; /* where all the temp stuff is kept */
Datum *dvalues; /* array of accumulated Datums */
bool *dnulls; /* array of is-null flags for Datums */
int alen; /* allocated length of above arrays */
int nelems; /* number of valid entries in above arrays */
! Oid element_type; /* data type of the Datums */
! int16 typlen; /* needed info about datatype */
! bool typbyval;
! char typalign;
! } ArrayBuildState;
/*
* structure to cache type metadata needed for array manipulation
--- 76,132 ----
/*
* working state for accumArrayResult() and friends
+ *
+ * is_array_accum: whether accumulating array values.
+ * (if true must be casted to ArrayBuildStateArray, else
+ * cast to ArrayBuildStateScalar)
*/
typedef struct ArrayBuildState
{
+ bool is_array_accum;
MemoryContext mcontext; /* where all the temp stuff is kept */
+ Oid element_type; /* data type of the Datums */
+ int16 typlen; /* needed info about datatype */
+ bool typbyval;
+ char typalign;
+ } ArrayBuildState;
+
+ /*
+ * array build state for array accumulation of scalar datums
+ */
+ typedef struct ArrayBuildStateScalar
+ {
+ ArrayBuildState astate;
+
Datum *dvalues; /* array of accumulated Datums */
bool *dnulls; /* array of is-null flags for Datums */
int alen; /* allocated length of above arrays */
int nelems; /* number of valid entries in above arrays */
! } ArrayBuildStateScalar;
!
!
! /*
! * array build state for array accumulation of array datums
! */
! typedef struct
! {
! ArrayBuildState astate;
!
! char *data; /* array of accumulated data */
! bits8 *nullbitmap; /* bitmap of is-null flags for data */
!
! int abytes; /* allocated length of above arrays */
! int aitems; /* allocated length of above arrays */
! int nbytes; /* number of used bytes in above arrays */
! int nitems; /* number of elements in above arrays */
! int narray; /* number of array accumulated */
!
! int ndims; /* element dimensions */
! int *dims;
! int *lbs;
!
! bool hasnull; /* any element has null */
! } ArrayBuildStateArray;
/*
* structure to cache type metadata needed for array manipulation
***************
*** 260,266 **** extern Datum makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext);
extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
int *dims, int *lbs, MemoryContext rcontext, bool release);
!
extern ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim);
extern bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull);
extern void array_free_iterator(ArrayIterator iterator);
--- 298,305 ----
MemoryContext rcontext);
extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
int *dims, int *lbs, MemoryContext rcontext, bool release);
! extern Datum makeArrayResultArray(ArrayBuildStateArray *astate,
! MemoryContext rcontext, bool release);
extern ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim);
extern bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull);
extern void array_free_iterator(ArrayIterator iterator);
***************
*** 293,298 **** extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
--- 332,340 ----
extern Datum array_agg_transfn(PG_FUNCTION_ARGS);
extern Datum array_agg_finalfn(PG_FUNCTION_ARGS);
+ extern Datum array_agg_anyarray_transfn(PG_FUNCTION_ARGS);
+ extern Datum array_agg_anyarray_finalfn(PG_FUNCTION_ARGS);
+
/*
* prototypes for functions defined in array_typanalyze.c
*/
*** a/src/test/regress/expected/aggregates.out
--- b/src/test/regress/expected/aggregates.out
***************
*** 914,919 **** select array_agg(distinct a order by a desc nulls last)
--- 914,946 ----
{3,2,1,NULL}
(1 row)
+ -- array_agg(anyarray)
+ select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar);
+ array_agg
+ ---------------
+ {{1,2},{3,4}}
+ (1 row)
+
+ select array_agg(distinct ar order by ar desc)
+ from (select array[i / 2] from generate_series(1,10) a(i)) b(ar);
+ array_agg
+ ---------------------------
+ {{5},{4},{3},{2},{1},{0}}
+ (1 row)
+
+ select array_agg(ar)
+ from (select array_agg(array[i, i+1, i-1])
+ from generate_series(1,2) a(i)) b(ar);
+ array_agg
+ ---------------------
+ {{{1,2,0},{2,3,1}}}
+ (1 row)
+
+ select array_agg('{}'::int[]) from generate_series(1,2);
+ ERROR: cannot accumulate empty arrays
+ select array_agg(null::int[]) from generate_series(1,2);
+ ERROR: cannot accumulate null arrays
-- multi-arg aggs, strict/nonstrict, distinct/order by
select aggfstr(a,b,c)
from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c);
*** a/src/test/regress/expected/arrays.out
--- b/src/test/regress/expected/arrays.out
***************
*** 1521,1526 **** select array_agg(unique1) from tenk1 where unique1 < -15;
--- 1521,1543 ----
(1 row)
+ select array(select unique1 from tenk1 where unique1 < 15 order by unique1);
+ array
+ --------------------------------------
+ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14}
+ (1 row)
+
+ select array(select array[i,i/2] from generate_series(1,5) a(i));
+ array
+ ---------------------------------
+ {{1,0},{2,1},{3,1},{4,2},{5,2}}
+ (1 row)
+
+ -- cannot accumulate null arrays and empty arrays
+ select array(select null::int[]);
+ ERROR: cannot accumulate null arrays
+ select array(select '{}'::int[]);
+ ERROR: cannot accumulate empty arrays
select unnest(array[1,2,3]);
unnest
--------
*** a/src/test/regress/sql/aggregates.sql
--- b/src/test/regress/sql/aggregates.sql
***************
*** 322,327 **** select array_agg(distinct a order by a desc)
--- 322,339 ----
select array_agg(distinct a order by a desc nulls last)
from (values (1),(2),(1),(3),(null),(2)) v(a);
+ -- array_agg(anyarray)
+ select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar);
+ select array_agg(distinct ar order by ar desc)
+ from (select array[i / 2] from generate_series(1,10) a(i)) b(ar);
+ select array_agg(ar)
+ from (select array_agg(array[i, i+1, i-1])
+ from generate_series(1,2) a(i)) b(ar);
+
+ select array_agg('{}'::int[]) from generate_series(1,2);
+ select array_agg(null::int[]) from generate_series(1,2);
+
-- multi-arg aggs, strict/nonstrict, distinct/order by
select aggfstr(a,b,c)
*** a/src/test/regress/sql/arrays.sql
--- b/src/test/regress/sql/arrays.sql
***************
*** 432,437 **** select array_agg(ten) from (select ten from tenk1 where unique1 < 15 order by un
--- 432,443 ----
select array_agg(nullif(ten, 4)) from (select ten from tenk1 where unique1 < 15 order by unique1) ss;
select array_agg(unique1) from tenk1 where unique1 < -15;
+ select array(select unique1 from tenk1 where unique1 < 15 order by unique1);
+ select array(select array[i,i/2] from generate_series(1,5) a(i));
+ -- cannot accumulate null arrays and empty arrays
+ select array(select null::int[]);
+ select array(select '{}'::int[]);
+
select unnest(array[1,2,3]);
select * from unnest(array[1,2,3]);
select unnest(array[1,2,3,4.5]::float8[]);
Hi
it looks well
doc:
http://www.postgresql.org/docs/9.4/static/sql-expressions.html#SQL-SYNTAX-ARRAY-CONSTRUCTORS
it should be fixed too
Regards
Pavel
2014-10-24 10:24 GMT+02:00 Ali Akbar <the.apaan@gmail.com>:
Show quoted text
Updated patch attached.
2014-10-22 20:51 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
I agree with your proposal. I have a few comments to design:
1. patch doesn't hold documentation and regress tests, please append
it.i've added some regression tests in arrays.sql and aggregate.sql.
I've only found the documentation of array_agg. Where is the doc for
array(subselect) defined?2. this functionality (multidimensional aggregation) can be interesting
more times, so maybe some interface like array builder should be preferred.
We already have accumArrayResult and
makeArrayResult/makeMdArrayResult, haven't we?Actually array_agg(anyarray) can be implemented by using
accumArrayResult and makeMdArrayResult, but in the process we will
deconstruct the array data and null bit-flags into ArrayBuildState->dvalues
and dnulls. And we will reconstruct it through makeMdArrayResult. I think
this way it's not efficient, so i decided to reimplement it and memcpy the
array data and null flags as-is.aha, so isn't better to fix a performance for accumArrayResult ?
Ok, i'll go this route. While reading the code of array(subselect) as
you pointed below, i think the easiest way to implement support for both
array_agg(anyarray) and array(subselect) is to change accumArrayResult so
it operates both with scalar datum(s) and array datums, with performance
optimization for the latter.implemented it by modifying ArrayBuildState to ArrayBuildStateArray and
ArrayBuildStateScalar (do you have any idea for better struct naming?)In other places, i think it's clearer if we just use accumArrayResult and
makeArrayResult/makeMdArrayResult as API for building (multidimensional)
arrays.3. array_agg was consistent with array(subselect), so it should be
fixed toopostgres=# select array_agg(a) from test;
array_agg
-----------------------
{{1,2,3,4},{1,2,3,4}}
(1 row)postgres=# select array(select a from test);
ERROR: could not find array type for data type integer[]I'm pretty new in postgresql development. Can you point out where is
array(subselect) implemented?where you can start?
postgres=# explain select array(select a from test);
ERROR: 42704: could not find array type for data type integer[]
LOCATION: exprType, nodeFuncs.c:116look to code ... your magic keyword is a ARRAY_SUBLINK .. search in
postgresql sources this keywordattention: probably we don't would to allow arrays everywhere.
I've changed the places where i think it's appropriate.
4. why you use a magic constant (64) there?
+ astate->abytes = 64 * (ndatabytes == 0 ? 1 : ndatabytes); + astate->aitems = 64 * nitems;+ astate->nullbitmap = (bits8 *) + repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);just follow the arbitrary size choosen in accumArrayResult
(arrayfuncs.c):
astate->alen = 64; /* arbitrary starting array size */it can be any number not too small and too big. Too small, and we will
realloc shortly. Too big, we will end up wasting memory.you can try to alloc 1KB instead as start -- it is used more times in
Postgres. Then a overhead is max 1KB per agg call - what is acceptable.You take this value from accumArrayResult, but it is targeted for
shorted scalars - you should to expect so any array will be much larger.this patch allocates 1KB (1024 bytes) if the ndatabytes is < 512bytes. If
it is larger, it allocates 4 * size. For nullbitmap, it allocates 4 *
number of items in array.Regards,
--
Ali Akbar
2014-10-24 15:48 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
Hi
it looks well
doc:
http://www.postgresql.org/docs/9.4/static/sql-expressions.html#SQL-SYNTAX-ARRAY-CONSTRUCTORS
it should be fixed tooRegards
Pavel
doc updated with additional example for array(subselect). patch attached.
Regards,
--
Ali Akbar
Attachments:
array_agg_anyarray-5.patchtext/x-patch; charset=US-ASCII; name=array_agg_anyarray-5.patchDownload
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 12046,12051 **** NULL baz</literallayout>(3 rows)</entry>
--- 12046,12067 ----
<row>
<entry>
<indexterm>
+ <primary>array_agg</primary>
+ </indexterm>
+ <function>array_agg(<replaceable class="parameter">anyarray</replaceable>)</function>
+ </entry>
+ <entry>
+ any
+ </entry>
+ <entry>
+ the same array type as input type
+ </entry>
+ <entry>input arrays, aggregated into higher-order multidimesional array. Rejects NULL and empty array as input.</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>average</primary>
</indexterm>
<indexterm>
*** a/doc/src/sgml/syntax.sgml
--- b/doc/src/sgml/syntax.sgml
***************
*** 2238,2243 **** SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
--- 2238,2248 ----
array
-----------------------------------------------------------------------
{2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31,2412,2413}
+
+ SELECT ARRAY(SELECT array(select i) FROM generate_series(1,5) a(i));
+ array
+ -----------------------
+ {{1},{2},{3},{4},{5}}
(1 row)
</programlisting>
The subquery must return a single column. The resulting
*** a/src/backend/nodes/nodeFuncs.c
--- b/src/backend/nodes/nodeFuncs.c
***************
*** 108,119 **** exprType(const Node *expr)
type = exprType((Node *) tent->expr);
if (sublink->subLinkType == ARRAY_SUBLINK)
{
! type = get_array_type(type);
! if (!OidIsValid(type))
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_OBJECT),
! errmsg("could not find array type for data type %s",
! format_type_be(exprType((Node *) tent->expr)))));
}
}
else if (sublink->subLinkType == MULTIEXPR_SUBLINK)
--- 108,123 ----
type = exprType((Node *) tent->expr);
if (sublink->subLinkType == ARRAY_SUBLINK)
{
! if (!OidIsValid(get_element_type(type)))
! {
! /* not array, so check for its array type */
! type = get_array_type(type);
! if (!OidIsValid(type))
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_OBJECT),
! errmsg("could not find array type for data type %s",
! format_type_be(exprType((Node *) tent->expr)))));
! }
}
}
else if (sublink->subLinkType == MULTIEXPR_SUBLINK)
***************
*** 139,150 **** exprType(const Node *expr)
type = subplan->firstColType;
if (subplan->subLinkType == ARRAY_SUBLINK)
{
! type = get_array_type(type);
! if (!OidIsValid(type))
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_OBJECT),
! errmsg("could not find array type for data type %s",
! format_type_be(subplan->firstColType))));
}
}
else if (subplan->subLinkType == MULTIEXPR_SUBLINK)
--- 143,158 ----
type = subplan->firstColType;
if (subplan->subLinkType == ARRAY_SUBLINK)
{
! if (!OidIsValid(get_element_type(type)))
! {
! /* not array, so check for its array type */
! type = get_array_type(type);
! if (!OidIsValid(type))
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_OBJECT),
! errmsg("could not find array type for data type %s",
! format_type_be(subplan->firstColType))));
! }
}
}
else if (subplan->subLinkType == MULTIEXPR_SUBLINK)
*** a/src/backend/optimizer/plan/subselect.c
--- b/src/backend/optimizer/plan/subselect.c
***************
*** 668,677 **** build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
Assert(!te->resjunk);
Assert(testexpr == NULL);
! arraytype = get_array_type(exprType((Node *) te->expr));
! if (!OidIsValid(arraytype))
! elog(ERROR, "could not find array type for datatype %s",
! format_type_be(exprType((Node *) te->expr)));
prm = generate_new_param(root,
arraytype,
exprTypmod((Node *) te->expr),
--- 668,683 ----
Assert(!te->resjunk);
Assert(testexpr == NULL);
!
! arraytype = exprType((Node *) te->expr);
! if (!OidIsValid(get_element_type(arraytype)))
! {
! /* not array, so get the array type */
! arraytype = get_array_type(exprType((Node *) te->expr));
! if (!OidIsValid(arraytype))
! elog(ERROR, "could not find array type for datatype %s",
! format_type_be(exprType((Node *) te->expr)));
! }
prm = generate_new_param(root,
arraytype,
exprTypmod((Node *) te->expr),
*** a/src/backend/utils/adt/array_userfuncs.c
--- b/src/backend/utils/adt/array_userfuncs.c
***************
*** 16,22 ****
#include "utils/builtins.h"
#include "utils/lsyscache.h"
-
/*-----------------------------------------------------------------------------
* array_push :
* push an element onto either end of a one-dimensional array
--- 16,21 ----
*** a/src/backend/utils/adt/arrayfuncs.c
--- b/src/backend/utils/adt/arrayfuncs.c
***************
*** 145,151 **** static int width_bucket_array_variable(Datum operand,
Oid collation,
TypeCacheEntry *typentry);
-
/*
* array_in :
* converts an array from the external format in "string" to
--- 145,150 ----
*** a/src/include/catalog/pg_aggregate.h
--- b/src/include/catalog/pg_aggregate.h
***************
*** 275,280 **** DATA(insert ( 2901 n 0 xmlconcat2 - - - - f f 0 142 0 0 0 _null_
--- 275,281 ----
/* array */
DATA(insert ( 2335 n 0 array_agg_transfn array_agg_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
+ DATA(insert ( 6005 n 0 array_agg_anyarray_transfn array_agg_anyarray_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
/* text */
DATA(insert ( 3538 n 0 string_agg_transfn string_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ ));
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 879,889 **** DATA(insert OID = 3167 ( array_remove PGNSP PGUID 12 1 0 0 0 f f f f f f i 2
DESCR("remove any occurrences of an element from an array");
DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ ));
DESCR("replace any occurrences of an element in an array");
! DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2283" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function");
! DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2283" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
DESCR("aggregate final function");
! DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2283" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into an array");
DATA(insert OID = 3218 ( width_bucket PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "2283 2277" _null_ _null_ _null_ _null_ width_bucket_array _null_ _null_ _null_ ));
DESCR("bucket number of operand given a sorted array of bucket lower bounds");
--- 879,895 ----
DESCR("remove any occurrences of an element from an array");
DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ ));
DESCR("replace any occurrences of an element in an array");
! DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2776" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function");
! DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2776" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
DESCR("aggregate final function");
! DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2776" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
! DESCR("concatenate aggregate input into an array");
! DATA(insert OID = 6003 ( array_agg_anyarray_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_transfn _null_ _null_ _null_ ));
! DESCR("aggregate transition function");
! DATA(insert OID = 6004 ( array_agg_anyarray_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_finalfn _null_ _null_ _null_ ));
! DESCR("aggregate final function");
! DATA(insert OID = 6005 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2277" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into an array");
DATA(insert OID = 3218 ( width_bucket PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "2283 2277" _null_ _null_ _null_ _null_ width_bucket_array _null_ _null_ _null_ ));
DESCR("bucket number of operand given a sorted array of bucket lower bounds");
*** a/src/include/utils/array.h
--- b/src/include/utils/array.h
***************
*** 76,94 **** typedef struct
/*
* working state for accumArrayResult() and friends
*/
typedef struct ArrayBuildState
{
MemoryContext mcontext; /* where all the temp stuff is kept */
Datum *dvalues; /* array of accumulated Datums */
bool *dnulls; /* array of is-null flags for Datums */
int alen; /* allocated length of above arrays */
int nelems; /* number of valid entries in above arrays */
! Oid element_type; /* data type of the Datums */
! int16 typlen; /* needed info about datatype */
! bool typbyval;
! char typalign;
! } ArrayBuildState;
/*
* structure to cache type metadata needed for array manipulation
--- 76,132 ----
/*
* working state for accumArrayResult() and friends
+ *
+ * is_array_accum: whether accumulating array values.
+ * (if true must be casted to ArrayBuildStateArray, else
+ * cast to ArrayBuildStateScalar)
*/
typedef struct ArrayBuildState
{
+ bool is_array_accum;
MemoryContext mcontext; /* where all the temp stuff is kept */
+ Oid element_type; /* data type of the Datums */
+ int16 typlen; /* needed info about datatype */
+ bool typbyval;
+ char typalign;
+ } ArrayBuildState;
+
+ /*
+ * array build state for array accumulation of scalar datums
+ */
+ typedef struct ArrayBuildStateScalar
+ {
+ ArrayBuildState astate;
+
Datum *dvalues; /* array of accumulated Datums */
bool *dnulls; /* array of is-null flags for Datums */
int alen; /* allocated length of above arrays */
int nelems; /* number of valid entries in above arrays */
! } ArrayBuildStateScalar;
!
!
! /*
! * array build state for array accumulation of array datums
! */
! typedef struct
! {
! ArrayBuildState astate;
!
! char *data; /* array of accumulated data */
! bits8 *nullbitmap; /* bitmap of is-null flags for data */
!
! int abytes; /* allocated length of above arrays */
! int aitems; /* allocated length of above arrays */
! int nbytes; /* number of used bytes in above arrays */
! int nitems; /* number of elements in above arrays */
! int narray; /* number of array accumulated */
!
! int ndims; /* element dimensions */
! int *dims;
! int *lbs;
!
! bool hasnull; /* any element has null */
! } ArrayBuildStateArray;
/*
* structure to cache type metadata needed for array manipulation
***************
*** 260,266 **** extern Datum makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext);
extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
int *dims, int *lbs, MemoryContext rcontext, bool release);
!
extern ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim);
extern bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull);
extern void array_free_iterator(ArrayIterator iterator);
--- 298,305 ----
MemoryContext rcontext);
extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
int *dims, int *lbs, MemoryContext rcontext, bool release);
! extern Datum makeArrayResultArray(ArrayBuildStateArray *astate,
! MemoryContext rcontext, bool release);
extern ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim);
extern bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull);
extern void array_free_iterator(ArrayIterator iterator);
***************
*** 293,298 **** extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
--- 332,340 ----
extern Datum array_agg_transfn(PG_FUNCTION_ARGS);
extern Datum array_agg_finalfn(PG_FUNCTION_ARGS);
+ extern Datum array_agg_anyarray_transfn(PG_FUNCTION_ARGS);
+ extern Datum array_agg_anyarray_finalfn(PG_FUNCTION_ARGS);
+
/*
* prototypes for functions defined in array_typanalyze.c
*/
*** a/src/test/regress/expected/aggregates.out
--- b/src/test/regress/expected/aggregates.out
***************
*** 914,919 **** select array_agg(distinct a order by a desc nulls last)
--- 914,946 ----
{3,2,1,NULL}
(1 row)
+ -- array_agg(anyarray)
+ select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar);
+ array_agg
+ ---------------
+ {{1,2},{3,4}}
+ (1 row)
+
+ select array_agg(distinct ar order by ar desc)
+ from (select array[i / 2] from generate_series(1,10) a(i)) b(ar);
+ array_agg
+ ---------------------------
+ {{5},{4},{3},{2},{1},{0}}
+ (1 row)
+
+ select array_agg(ar)
+ from (select array_agg(array[i, i+1, i-1])
+ from generate_series(1,2) a(i)) b(ar);
+ array_agg
+ ---------------------
+ {{{1,2,0},{2,3,1}}}
+ (1 row)
+
+ select array_agg('{}'::int[]) from generate_series(1,2);
+ ERROR: cannot accumulate empty arrays
+ select array_agg(null::int[]) from generate_series(1,2);
+ ERROR: cannot accumulate null arrays
-- multi-arg aggs, strict/nonstrict, distinct/order by
select aggfstr(a,b,c)
from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c);
*** a/src/test/regress/expected/arrays.out
--- b/src/test/regress/expected/arrays.out
***************
*** 1521,1526 **** select array_agg(unique1) from tenk1 where unique1 < -15;
--- 1521,1543 ----
(1 row)
+ select array(select unique1 from tenk1 where unique1 < 15 order by unique1);
+ array
+ --------------------------------------
+ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14}
+ (1 row)
+
+ select array(select array[i,i/2] from generate_series(1,5) a(i));
+ array
+ ---------------------------------
+ {{1,0},{2,1},{3,1},{4,2},{5,2}}
+ (1 row)
+
+ -- cannot accumulate null arrays and empty arrays
+ select array(select null::int[]);
+ ERROR: cannot accumulate null arrays
+ select array(select '{}'::int[]);
+ ERROR: cannot accumulate empty arrays
select unnest(array[1,2,3]);
unnest
--------
*** a/src/test/regress/sql/aggregates.sql
--- b/src/test/regress/sql/aggregates.sql
***************
*** 322,327 **** select array_agg(distinct a order by a desc)
--- 322,339 ----
select array_agg(distinct a order by a desc nulls last)
from (values (1),(2),(1),(3),(null),(2)) v(a);
+ -- array_agg(anyarray)
+ select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar);
+ select array_agg(distinct ar order by ar desc)
+ from (select array[i / 2] from generate_series(1,10) a(i)) b(ar);
+ select array_agg(ar)
+ from (select array_agg(array[i, i+1, i-1])
+ from generate_series(1,2) a(i)) b(ar);
+
+ select array_agg('{}'::int[]) from generate_series(1,2);
+ select array_agg(null::int[]) from generate_series(1,2);
+
-- multi-arg aggs, strict/nonstrict, distinct/order by
select aggfstr(a,b,c)
*** a/src/test/regress/sql/arrays.sql
--- b/src/test/regress/sql/arrays.sql
***************
*** 432,437 **** select array_agg(ten) from (select ten from tenk1 where unique1 < 15 order by un
--- 432,443 ----
select array_agg(nullif(ten, 4)) from (select ten from tenk1 where unique1 < 15 order by unique1) ss;
select array_agg(unique1) from tenk1 where unique1 < -15;
+ select array(select unique1 from tenk1 where unique1 < 15 order by unique1);
+ select array(select array[i,i/2] from generate_series(1,5) a(i));
+ -- cannot accumulate null arrays and empty arrays
+ select array(select null::int[]);
+ select array(select '{}'::int[]);
+
select unnest(array[1,2,3]);
select * from unnest(array[1,2,3]);
select unnest(array[1,2,3,4.5]::float8[]);
Hi
some in last patch is wrong, I cannot to compile it:
arrayfuncs.c: In function ‘accumArrayResult’:
arrayfuncs.c:4603:9: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->alen = 64; /* arbitrary starting array size */
^
arrayfuncs.c:4604:9: error: ‘ArrayBuildState’ has no member named ‘dvalues’
astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
^
arrayfuncs.c:4604:44: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
^
arrayfuncs.c:4605:9: error: ‘ArrayBuildState’ has no member named ‘dnulls’
astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
^
arrayfuncs.c:4605:42: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
^
arrayfuncs.c:4606:9: error: ‘ArrayBuildState’ has no member named ‘nelems’
astate->nelems = 0;
^
arrayfuncs.c:4618:13: error: ‘ArrayBuildState’ has no member named ‘nelems’
if (astate->nelems >= astate->alen)
^
arrayfuncs.c:4618:31: error: ‘ArrayBuildState’ has no member named ‘alen’
if (astate->nelems >= astate->alen)
^
arrayfuncs.c:4620:10: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->alen *= 2;
2014-10-24 11:24 GMT+02:00 Ali Akbar <the.apaan@gmail.com>:
Show quoted text
2014-10-24 15:48 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
Hi
it looks well
doc:
http://www.postgresql.org/docs/9.4/static/sql-expressions.html#SQL-SYNTAX-ARRAY-CONSTRUCTORS
it should be fixed tooRegards
Pavel
doc updated with additional example for array(subselect). patch attached.
Regards,
--
Ali Akbar
2014-10-24 16:26 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
Hi
some in last patch is wrong, I cannot to compile it:
arrayfuncs.c: In function ‘accumArrayResult’:
arrayfuncs.c:4603:9: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->alen = 64; /* arbitrary starting array size */
^
arrayfuncs.c:4604:9: error: ‘ArrayBuildState’ has no member named ‘dvalues’
astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
^
arrayfuncs.c:4604:44: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
^
arrayfuncs.c:4605:9: error: ‘ArrayBuildState’ has no member named ‘dnulls’
astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
^
arrayfuncs.c:4605:42: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
^
arrayfuncs.c:4606:9: error: ‘ArrayBuildState’ has no member named ‘nelems’
astate->nelems = 0;
^
arrayfuncs.c:4618:13: error: ‘ArrayBuildState’ has no member named ‘nelems’
if (astate->nelems >= astate->alen)
^
arrayfuncs.c:4618:31: error: ‘ArrayBuildState’ has no member named ‘alen’
if (astate->nelems >= astate->alen)
^
arrayfuncs.c:4620:10: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->alen *= 2;
Sorry, correct patch attached.
This patch is in patience format (git --patience ..). In previous patches,
i use context format (git --patience ... | filterdiff --format=context),
but it turns out that some modification is lost.
--
Ali Akbar
Attachments:
array_agg_anyarray-6.patchtext/x-patch; charset=US-ASCII; name=array_agg_anyarray-6.patchDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 7e5bcd9..f59738a 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -12046,6 +12046,22 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<indexterm>
+ <primary>array_agg</primary>
+ </indexterm>
+ <function>array_agg(<replaceable class="parameter">anyarray</replaceable>)</function>
+ </entry>
+ <entry>
+ any
+ </entry>
+ <entry>
+ the same array type as input type
+ </entry>
+ <entry>input arrays, aggregated into higher-order multidimesional array. Rejects NULL and empty array as input.</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>average</primary>
</indexterm>
<indexterm>
diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml
index 2f0680f..8c182a4 100644
--- a/doc/src/sgml/syntax.sgml
+++ b/doc/src/sgml/syntax.sgml
@@ -2238,6 +2238,11 @@ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
array
-----------------------------------------------------------------------
{2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31,2412,2413}
+
+SELECT ARRAY(SELECT array(select i) FROM generate_series(1,5) a(i));
+ array
+-----------------------
+ {{1},{2},{3},{4},{5}}
(1 row)
</programlisting>
The subquery must return a single column. The resulting
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 41e973b..0261fcb 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -108,12 +108,16 @@ exprType(const Node *expr)
type = exprType((Node *) tent->expr);
if (sublink->subLinkType == ARRAY_SUBLINK)
{
- type = get_array_type(type);
- if (!OidIsValid(type))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("could not find array type for data type %s",
- format_type_be(exprType((Node *) tent->expr)))));
+ if (!OidIsValid(get_element_type(type)))
+ {
+ /* not array, so check for its array type */
+ type = get_array_type(type);
+ if (!OidIsValid(type))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(exprType((Node *) tent->expr)))));
+ }
}
}
else if (sublink->subLinkType == MULTIEXPR_SUBLINK)
@@ -139,12 +143,16 @@ exprType(const Node *expr)
type = subplan->firstColType;
if (subplan->subLinkType == ARRAY_SUBLINK)
{
- type = get_array_type(type);
- if (!OidIsValid(type))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("could not find array type for data type %s",
- format_type_be(subplan->firstColType))));
+ if (!OidIsValid(get_element_type(type)))
+ {
+ /* not array, so check for its array type */
+ type = get_array_type(type);
+ if (!OidIsValid(type))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(subplan->firstColType))));
+ }
}
}
else if (subplan->subLinkType == MULTIEXPR_SUBLINK)
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 3e7dc85..8fc8b49 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -668,10 +668,16 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
Assert(!te->resjunk);
Assert(testexpr == NULL);
- arraytype = get_array_type(exprType((Node *) te->expr));
- if (!OidIsValid(arraytype))
- elog(ERROR, "could not find array type for datatype %s",
- format_type_be(exprType((Node *) te->expr)));
+
+ arraytype = exprType((Node *) te->expr);
+ if (!OidIsValid(get_element_type(arraytype)))
+ {
+ /* not array, so get the array type */
+ arraytype = get_array_type(exprType((Node *) te->expr));
+ if (!OidIsValid(arraytype))
+ elog(ERROR, "could not find array type for datatype %s",
+ format_type_be(exprType((Node *) te->expr)));
+ }
prm = generate_new_param(root,
arraytype,
exprTypmod((Node *) te->expr),
diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c
index 831466d..9fdb25b 100644
--- a/src/backend/utils/adt/array_userfuncs.c
+++ b/src/backend/utils/adt/array_userfuncs.c
@@ -16,7 +16,6 @@
#include "utils/builtins.h"
#include "utils/lsyscache.h"
-
/*-----------------------------------------------------------------------------
* array_push :
* push an element onto either end of a one-dimensional array
@@ -513,8 +512,6 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
{
Datum result;
ArrayBuildState *state;
- int dims[1];
- int lbs[1];
/*
* Test for null before Asserting we are in right context. This is to
@@ -529,18 +526,43 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
state = (ArrayBuildState *) PG_GETARG_POINTER(0);
- dims[0] = state->nelems;
- lbs[0] = 1;
-
/*
* Make the result. We cannot release the ArrayBuildState because
* sometimes aggregate final functions are re-executed. Rather, it is
* nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
* so.
*/
- result = makeMdArrayResult(state, 1, dims, lbs,
- CurrentMemoryContext,
- false);
+ if (!state->is_array_accum)
+ {
+ int dims[1];
+ int lbs[1];
+
+ dims[0] = ((ArrayBuildStateScalar *) state)->nelems;
+ lbs[0] = 1;
+
+ result = makeMdArrayResult(state, 1, dims, lbs,
+ CurrentMemoryContext,
+ false);
+ }
+ else
+ result = makeArrayResultArray((ArrayBuildStateArray *) state,
+ CurrentMemoryContext,
+ false);
PG_RETURN_DATUM(result);
}
+
+/*
+ * ARRAY_AGG(anyarray) aggregate function
+ */
+Datum
+array_agg_anyarray_transfn(PG_FUNCTION_ARGS)
+{
+ return array_agg_transfn(fcinfo);
+}
+
+Datum
+array_agg_anyarray_finalfn(PG_FUNCTION_ARGS)
+{
+ return array_agg_finalfn(fcinfo);
+}
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 6c8b41d..582912e 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -145,7 +145,6 @@ static int width_bucket_array_variable(Datum operand,
Oid collation,
TypeCacheEntry *typentry);
-
/*
* array_in :
* converts an array from the external format in "string" to
@@ -4588,9 +4587,13 @@ accumArrayResult(ArrayBuildState *astate,
MemoryContext arr_context,
oldcontext;
+ ArrayBuildStateScalar *astate_scalar = NULL; /* for scalar datum accumulation */
+ ArrayBuildStateArray *astate_array = NULL; /* for array datum accumulation */
+
if (astate == NULL)
{
/* First time through --- initialize */
+ Oid subelement_type = get_element_type(element_type);
/* Make a temporary context to hold all the junk */
arr_context = AllocSetContextCreate(rcontext,
@@ -4599,60 +4602,224 @@ accumArrayResult(ArrayBuildState *astate,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
oldcontext = MemoryContextSwitchTo(arr_context);
- astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
- astate->mcontext = arr_context;
- astate->alen = 64; /* arbitrary starting array size */
- astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
- astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
- astate->nelems = 0;
- astate->element_type = element_type;
- get_typlenbyvalalign(element_type,
- &astate->typlen,
- &astate->typbyval,
- &astate->typalign);
+
+ if (subelement_type == InvalidOid)
+ {
+ /* scalar accumulate */
+ astate_scalar = (ArrayBuildStateScalar *) palloc(sizeof(ArrayBuildStateScalar));
+ astate = (ArrayBuildState *) astate_scalar;
+ astate->is_array_accum = false;
+ astate->mcontext = arr_context;
+
+ astate_scalar->alen = 64; /* arbitrary starting array size */
+ astate_scalar->dvalues = (Datum *) palloc(astate_scalar->alen * sizeof(Datum));
+ astate_scalar->dnulls = (bool *) palloc(astate_scalar->alen * sizeof(bool));
+ astate_scalar->nelems = 0;
+
+ astate->element_type = element_type;
+ get_typlenbyvalalign(element_type,
+ &astate->typlen,
+ &astate->typbyval,
+ &astate->typalign);
+ }
+ else
+ {
+ /* array accumulate */
+ astate_array = (ArrayBuildStateArray *) palloc(sizeof(ArrayBuildStateArray));
+ astate = (ArrayBuildState *) astate_array;
+ astate->is_array_accum = true;
+ astate->mcontext = arr_context;
+
+ astate_array->abytes = 0;
+ astate_array->aitems = 0;
+ astate_array->data = NULL;
+ astate_array->nullbitmap = NULL;
+ astate_array->nitems = 0;
+ astate_array->narray = 0;
+
+ astate->element_type = subelement_type;
+ get_typlenbyvalalign(subelement_type,
+ &astate->typlen,
+ &astate->typbyval,
+ &astate->typalign);
+ }
}
else
{
oldcontext = MemoryContextSwitchTo(astate->mcontext);
- Assert(astate->element_type == element_type);
- /* enlarge dvalues[]/dnulls[] if needed */
- if (astate->nelems >= astate->alen)
+ if (!astate->is_array_accum)
{
- astate->alen *= 2;
- astate->dvalues = (Datum *)
- repalloc(astate->dvalues, astate->alen * sizeof(Datum));
- astate->dnulls = (bool *)
- repalloc(astate->dnulls, astate->alen * sizeof(bool));
+ Assert(astate->element_type == element_type);
+ astate_scalar = (ArrayBuildStateScalar *) astate;
+
+ /* enlarge dvalues[]/dnulls[] if needed */
+ if (astate_scalar->nelems >= astate_scalar->alen)
+ {
+ astate_scalar->alen *= 2;
+ astate_scalar->dvalues = (Datum *)
+ repalloc(astate_scalar->dvalues, astate_scalar->alen * sizeof(Datum));
+ astate_scalar->dnulls = (bool *)
+ repalloc(astate_scalar->dnulls, astate_scalar->alen * sizeof(bool));
+ }
+ }
+ else
+ {
+ Assert(astate->element_type == get_element_type(element_type));
+ astate_array = (ArrayBuildStateArray *) astate;
+ /*
+ * array accumulate alloc & realloc placed below because
+ * we need to know its length first
+ */
}
}
- /*
- * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
- * it's varlena. (You might think that detoasting is not needed here
- * because construct_md_array can detoast the array elements later.
- * However, we must not let construct_md_array modify the ArrayBuildState
- * because that would mean array_agg_finalfn damages its input, which is
- * verboten. Also, this way frequently saves one copying step.)
- */
- if (!disnull && !astate->typbyval)
+ if (!astate->is_array_accum)
{
- if (astate->typlen == -1)
- dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
- else
- dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
+ /*
+ * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
+ * it's varlena. (You might think that detoasting is not needed here
+ * because construct_md_array can detoast the array elements later.
+ * However, we must not let construct_md_array modify the ArrayBuildState
+ * because that would mean array_agg_finalfn damages its input, which is
+ * verboten. Also, this way frequently saves one copying step.)
+ */
+ if (!disnull && !astate->typbyval)
+ {
+ if (astate->typlen == -1)
+ dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
+ else
+ dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
+ }
+
+ astate_scalar->dvalues[astate_scalar->nelems] = dvalue;
+ astate_scalar->dnulls[astate_scalar->nelems] = disnull;
+ astate_scalar->nelems++;
}
+ else
+ {
+ ArrayType *arg;
+ int *dims,
+ *lbs,
+ ndims,
+ nitems,
+ ndatabytes;
+ char *data;
+ int i;
- astate->dvalues[astate->nelems] = dvalue;
- astate->dnulls[astate->nelems] = disnull;
- astate->nelems++;
+ if (disnull)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot accumulate null arrays")));
+
+ arg = DatumGetArrayTypeP(dvalue);
+
+ ndims = ARR_NDIM(arg);
+ dims = ARR_DIMS(arg);
+ lbs = ARR_LBOUND(arg);
+ data = ARR_DATA_PTR(arg);
+ nitems = ArrayGetNItems(ndims, dims);
+
+ ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
+
+ if (astate_array->data == NULL)
+ {
+ /* first allocation */
+ if (ndims == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot accumulate empty arrays")));
+
+ if (ndims + 1 > MAXDIM)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ ndims + 1, MAXDIM)));
+
+ astate_array->ndims = ndims;
+ astate_array->dims = (int *) palloc(ndims * sizeof(int));
+ astate_array->lbs = (int *) palloc(ndims * sizeof(int));
+ memcpy(astate_array->dims, dims, ndims * sizeof(int));
+ memcpy(astate_array->lbs, lbs, ndims * sizeof(int));
+
+ astate_array->abytes = ndatabytes >= 512 ? 4 * ndatabytes : 1024;
+ astate_array->aitems = 4 * nitems;
+ astate_array->data = (char *) palloc(astate_array->abytes);
+
+ memcpy(astate_array->data, data, ndatabytes);
+ astate_array->nbytes = ndatabytes;
+ astate_array->nitems = nitems;
+ if (ARR_HASNULL(arg))
+ {
+ astate_array->hasnull = true;
+ astate_array->nullbitmap = (bits8 *) palloc((astate_array->aitems + 7) / 8);
+ array_bitmap_copy(astate_array->nullbitmap, 0,
+ ARR_NULLBITMAP(arg), 0,
+ nitems);
+ }
+ else
+ astate_array->hasnull = false;
+ }
+ else
+ {
+ if (astate_array->ndims != ndims)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot aggregate incompatible arrays"),
+ errdetail("Arrays of %d and %d dimensions are not "
+ "compatible for concatenation.",
+ astate_array->ndims, ndims)));
+
+ for (i = 0; i < ndims; i++)
+ if (astate_array->dims[i] != dims[i] || astate_array->lbs[i] != lbs[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot aggregate incompatible arrays"),
+ errdetail("Arrays with differing element dimensions are "
+ "not compatible for concatenation.")));
+
+ if (astate_array->nbytes + ndatabytes >= astate_array->abytes)
+ {
+ astate_array->nbytes *= 2;
+ astate_array->data = (char *)
+ repalloc(astate_array->data, astate_array->nbytes);
+ }
+ if (astate_array->nitems + nitems >= astate_array->aitems)
+ {
+ astate_array->aitems *= 2;
+ astate_array->nullbitmap = (bits8 *)
+ repalloc(astate_array->nullbitmap, (astate_array->aitems + 7) / 8);
+ }
+
+ memcpy(astate_array->data + astate_array->nbytes, data, ndatabytes);
+ astate_array->nbytes += ndatabytes;
+
+ if (ARR_HASNULL(arg) || astate_array->hasnull)
+ {
+ if (!astate_array->hasnull)
+ {
+ astate_array->hasnull = true;
+ astate_array->nullbitmap = (bits8 *) palloc((astate_array->aitems + 7) / 8);
+ array_bitmap_copy(astate_array->nullbitmap, 0,
+ NULL, 0,
+ astate_array->nitems);
+ }
+ array_bitmap_copy(astate_array->nullbitmap, astate_array->nitems,
+ ARR_NULLBITMAP(arg), 0,
+ nitems);
+ astate_array->nitems += nitems;
+ }
+ }
+ astate_array->narray += 1;
+ }
MemoryContextSwitchTo(oldcontext);
return astate;
}
/*
- * makeArrayResult - produce 1-D final result of accumArrayResult
+ * makeArrayResult - produce 1-D final result of scalar accumArrayResult
+ * - produce N+1-D final result of array accumArrayResult
*
* astate is working state (not NULL)
* rcontext is where to construct result
@@ -4661,17 +4828,22 @@ Datum
makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext)
{
- int dims[1];
- int lbs[1];
+ if (!astate->is_array_accum)
+ {
+ int dims[1];
+ int lbs[1];
- dims[0] = astate->nelems;
- lbs[0] = 1;
+ dims[0] = ((ArrayBuildStateScalar *) astate)->nelems;
+ lbs[0] = 1;
- return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true);
+ return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true);
+ }
+ else
+ return makeArrayResultArray((ArrayBuildStateArray *) astate, rcontext, true);
}
/*
- * makeMdArrayResult - produce multi-D final result of accumArrayResult
+ * makeMdArrayResult - produce multi-D final result of scalar accumArrayResult
*
* beware: no check that specified dimensions match the number of values
* accumulated.
@@ -4690,12 +4862,17 @@ makeMdArrayResult(ArrayBuildState *astate,
{
ArrayType *result;
MemoryContext oldcontext;
+ ArrayBuildStateScalar *astate_scalar;
+
+ Assert(!astate->is_array_accum);
+
+ astate_scalar = (ArrayBuildStateScalar *) astate;
/* Build the final array result in rcontext */
oldcontext = MemoryContextSwitchTo(rcontext);
- result = construct_md_array(astate->dvalues,
- astate->dnulls,
+ result = construct_md_array(astate_scalar->dvalues,
+ astate_scalar->dnulls,
ndims,
dims,
lbs,
@@ -4713,6 +4890,68 @@ makeMdArrayResult(ArrayBuildState *astate,
return PointerGetDatum(result);
}
+/*
+ * makeMdArrayResultArray - produce N+1-D final result of array accumArrayResult
+ *
+ * astate is working state (not NULL)
+ * rcontext is where to construct result
+ * release is true if okay to release working state
+ */
+Datum
+makeArrayResultArray(ArrayBuildStateArray *astate,
+ MemoryContext rcontext,
+ bool release)
+{
+ MemoryContext oldcontext;
+
+ ArrayType *result;
+
+ int dataoffset,
+ nbytes,
+ ndims;
+
+ oldcontext = MemoryContextSwitchTo(rcontext);
+
+ ndims = astate->ndims + 1;
+ nbytes = astate->nbytes;
+ /* compute required space */
+ if (astate->hasnull)
+ {
+ dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, astate->nitems);
+ nbytes += dataoffset;
+ }
+ else
+ {
+ dataoffset = 0;
+ nbytes += ARR_OVERHEAD_NONULLS(ndims);
+ }
+
+ result = (ArrayType *) palloc0(nbytes);
+ SET_VARSIZE(result, nbytes);
+ result->ndim = astate->ndims + 1;
+ result->dataoffset = dataoffset;
+ result->elemtype = astate->astate.element_type;
+
+ ARR_DIMS(result)[0] = astate->narray;
+ ARR_LBOUND(result)[0] = 1;
+ memcpy(&(ARR_DIMS(result)[1]), astate->dims, (ndims - 1) * sizeof(int));
+ memcpy(&(ARR_LBOUND(result)[1]), astate->lbs, (ndims - 1) * sizeof(int));
+
+ memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
+ if (astate->hasnull)
+ array_bitmap_copy(ARR_NULLBITMAP(result), 0,
+ astate->nullbitmap, 0,
+ astate->nitems);
+
+ MemoryContextSwitchTo(oldcontext);
+
+ /* Clean up all the junk */
+ if (release)
+ MemoryContextDelete(astate->astate.mcontext);
+
+ PG_RETURN_DATUM(PointerGetDatum(result));
+}
+
Datum
array_larger(PG_FUNCTION_ARGS)
{
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 3ba9e5e..2005199 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -275,6 +275,7 @@ DATA(insert ( 2901 n 0 xmlconcat2 - - - - f f 0 142 0 0 0 _null_
/* array */
DATA(insert ( 2335 n 0 array_agg_transfn array_agg_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
+DATA(insert ( 6005 n 0 array_agg_anyarray_transfn array_agg_anyarray_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
/* text */
DATA(insert ( 3538 n 0 string_agg_transfn string_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index b6dc1b8..9273c1f 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -879,11 +879,17 @@ DATA(insert OID = 3167 ( array_remove PGNSP PGUID 12 1 0 0 0 f f f f f f i 2
DESCR("remove any occurrences of an element from an array");
DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ ));
DESCR("replace any occurrences of an element in an array");
-DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2283" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
+DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2776" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function");
-DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2283" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
+DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2776" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
DESCR("aggregate final function");
-DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2283" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2776" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+DESCR("concatenate aggregate input into an array");
+DATA(insert OID = 6003 ( array_agg_anyarray_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_transfn _null_ _null_ _null_ ));
+DESCR("aggregate transition function");
+DATA(insert OID = 6004 ( array_agg_anyarray_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_finalfn _null_ _null_ _null_ ));
+DESCR("aggregate final function");
+DATA(insert OID = 6005 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2277" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into an array");
DATA(insert OID = 3218 ( width_bucket PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "2283 2277" _null_ _null_ _null_ _null_ width_bucket_array _null_ _null_ _null_ ));
DESCR("bucket number of operand given a sorted array of bucket lower bounds");
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index e744314..c6fbc7f 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -76,14 +76,15 @@ typedef struct
/*
* working state for accumArrayResult() and friends
+ *
+ * is_array_accum: whether accumulating array values.
+ * (if true must be casted to ArrayBuildStateArray, else
+ * cast to ArrayBuildStateScalar)
*/
typedef struct ArrayBuildState
{
+ bool is_array_accum;
MemoryContext mcontext; /* where all the temp stuff is kept */
- Datum *dvalues; /* array of accumulated Datums */
- bool *dnulls; /* array of is-null flags for Datums */
- int alen; /* allocated length of above arrays */
- int nelems; /* number of valid entries in above arrays */
Oid element_type; /* data type of the Datums */
int16 typlen; /* needed info about datatype */
bool typbyval;
@@ -91,6 +92,43 @@ typedef struct ArrayBuildState
} ArrayBuildState;
/*
+ * array build state for array accumulation of scalar datums
+ */
+typedef struct ArrayBuildStateScalar
+{
+ ArrayBuildState astate;
+
+ Datum *dvalues; /* array of accumulated Datums */
+ bool *dnulls; /* array of is-null flags for Datums */
+ int alen; /* allocated length of above arrays */
+ int nelems; /* number of valid entries in above arrays */
+} ArrayBuildStateScalar;
+
+
+/*
+ * array build state for array accumulation of array datums
+ */
+typedef struct
+{
+ ArrayBuildState astate;
+
+ char *data; /* array of accumulated data */
+ bits8 *nullbitmap; /* bitmap of is-null flags for data */
+
+ int abytes; /* allocated length of above arrays */
+ int aitems; /* allocated length of above arrays */
+ int nbytes; /* number of used bytes in above arrays */
+ int nitems; /* number of elements in above arrays */
+ int narray; /* number of array accumulated */
+
+ int ndims; /* element dimensions */
+ int *dims;
+ int *lbs;
+
+ bool hasnull; /* any element has null */
+} ArrayBuildStateArray;
+
+/*
* structure to cache type metadata needed for array manipulation
*/
typedef struct ArrayMetaState
@@ -260,7 +298,8 @@ extern Datum makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext);
extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
int *dims, int *lbs, MemoryContext rcontext, bool release);
-
+extern Datum makeArrayResultArray(ArrayBuildStateArray *astate,
+ MemoryContext rcontext, bool release);
extern ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim);
extern bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull);
extern void array_free_iterator(ArrayIterator iterator);
@@ -293,6 +332,9 @@ extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
extern Datum array_agg_transfn(PG_FUNCTION_ARGS);
extern Datum array_agg_finalfn(PG_FUNCTION_ARGS);
+extern Datum array_agg_anyarray_transfn(PG_FUNCTION_ARGS);
+extern Datum array_agg_anyarray_finalfn(PG_FUNCTION_ARGS);
+
/*
* prototypes for functions defined in array_typanalyze.c
*/
diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out
index 58df854..eed1d5b 100644
--- a/src/test/regress/expected/aggregates.out
+++ b/src/test/regress/expected/aggregates.out
@@ -914,6 +914,33 @@ select array_agg(distinct a order by a desc nulls last)
{3,2,1,NULL}
(1 row)
+-- array_agg(anyarray)
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar);
+ array_agg
+---------------
+ {{1,2},{3,4}}
+(1 row)
+
+select array_agg(distinct ar order by ar desc)
+ from (select array[i / 2] from generate_series(1,10) a(i)) b(ar);
+ array_agg
+---------------------------
+ {{5},{4},{3},{2},{1},{0}}
+(1 row)
+
+select array_agg(ar)
+ from (select array_agg(array[i, i+1, i-1])
+ from generate_series(1,2) a(i)) b(ar);
+ array_agg
+---------------------
+ {{{1,2,0},{2,3,1}}}
+(1 row)
+
+select array_agg('{}'::int[]) from generate_series(1,2);
+ERROR: cannot accumulate empty arrays
+select array_agg(null::int[]) from generate_series(1,2);
+ERROR: cannot accumulate null arrays
-- multi-arg aggs, strict/nonstrict, distinct/order by
select aggfstr(a,b,c)
from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c);
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index 46eff67..e80ebec 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -1521,6 +1521,23 @@ select array_agg(unique1) from tenk1 where unique1 < -15;
(1 row)
+select array(select unique1 from tenk1 where unique1 < 15 order by unique1);
+ array
+--------------------------------------
+ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14}
+(1 row)
+
+select array(select array[i,i/2] from generate_series(1,5) a(i));
+ array
+---------------------------------
+ {{1,0},{2,1},{3,1},{4,2},{5,2}}
+(1 row)
+
+-- cannot accumulate null arrays and empty arrays
+select array(select null::int[]);
+ERROR: cannot accumulate null arrays
+select array(select '{}'::int[]);
+ERROR: cannot accumulate empty arrays
select unnest(array[1,2,3]);
unnest
--------
diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql
index 8096a6f..2296337 100644
--- a/src/test/regress/sql/aggregates.sql
+++ b/src/test/regress/sql/aggregates.sql
@@ -322,6 +322,18 @@ select array_agg(distinct a order by a desc)
select array_agg(distinct a order by a desc nulls last)
from (values (1),(2),(1),(3),(null),(2)) v(a);
+-- array_agg(anyarray)
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar);
+select array_agg(distinct ar order by ar desc)
+ from (select array[i / 2] from generate_series(1,10) a(i)) b(ar);
+select array_agg(ar)
+ from (select array_agg(array[i, i+1, i-1])
+ from generate_series(1,2) a(i)) b(ar);
+
+select array_agg('{}'::int[]) from generate_series(1,2);
+select array_agg(null::int[]) from generate_series(1,2);
+
-- multi-arg aggs, strict/nonstrict, distinct/order by
select aggfstr(a,b,c)
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index fa8a20a..cb00f5f 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -432,6 +432,12 @@ select array_agg(ten) from (select ten from tenk1 where unique1 < 15 order by un
select array_agg(nullif(ten, 4)) from (select ten from tenk1 where unique1 < 15 order by unique1) ss;
select array_agg(unique1) from tenk1 where unique1 < -15;
+select array(select unique1 from tenk1 where unique1 < 15 order by unique1);
+select array(select array[i,i/2] from generate_series(1,5) a(i));
+-- cannot accumulate null arrays and empty arrays
+select array(select null::int[]);
+select array(select '{}'::int[]);
+
select unnest(array[1,2,3]);
select * from unnest(array[1,2,3]);
select unnest(array[1,2,3,4.5]::float8[]);
On Fri, Oct 24, 2014 at 11:43 AM, Ali Akbar <the.apaan@gmail.com> wrote:
2014-10-24 16:26 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
Hi
some in last patch is wrong, I cannot to compile it:
arrayfuncs.c: In function 'accumArrayResult':
arrayfuncs.c:4603:9: error: 'ArrayBuildState' has no member named 'alen'
astate->alen = 64; /* arbitrary starting array size */
^
arrayfuncs.c:4604:9: error: 'ArrayBuildState' has no member named
'dvalues'
astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
^
arrayfuncs.c:4604:44: error: 'ArrayBuildState' has no member named 'alen'
astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
^
arrayfuncs.c:4605:9: error: 'ArrayBuildState' has no member named 'dnulls'
astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
^
arrayfuncs.c:4605:42: error: 'ArrayBuildState' has no member named 'alen'
astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
^
arrayfuncs.c:4606:9: error: 'ArrayBuildState' has no member named 'nelems'
astate->nelems = 0;
^
arrayfuncs.c:4618:13: error: 'ArrayBuildState' has no member named
'nelems'
if (astate->nelems >= astate->alen)
^
arrayfuncs.c:4618:31: error: 'ArrayBuildState' has no member named 'alen'
if (astate->nelems >= astate->alen)
^
arrayfuncs.c:4620:10: error: 'ArrayBuildState' has no member named 'alen'
astate->alen *= 2;Sorry, correct patch attached.
This patch is in patience format (git --patience ..). In previous patches,
i use context format (git --patience ... | filterdiff --format=context),
but it turns out that some modification is lost.
That's not surprising, sometimes filterdiff misses the shot.
--
Michael
2014-10-24 11:43 GMT+02:00 Ali Akbar <the.apaan@gmail.com>:
2014-10-24 16:26 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
Hi
some in last patch is wrong, I cannot to compile it:
arrayfuncs.c: In function ‘accumArrayResult’:
arrayfuncs.c:4603:9: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->alen = 64; /* arbitrary starting array size */
^
arrayfuncs.c:4604:9: error: ‘ArrayBuildState’ has no member named
‘dvalues’
astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
^
arrayfuncs.c:4604:44: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
^
arrayfuncs.c:4605:9: error: ‘ArrayBuildState’ has no member named ‘dnulls’
astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
^
arrayfuncs.c:4605:42: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
^
arrayfuncs.c:4606:9: error: ‘ArrayBuildState’ has no member named ‘nelems’
astate->nelems = 0;
^
arrayfuncs.c:4618:13: error: ‘ArrayBuildState’ has no member named
‘nelems’
if (astate->nelems >= astate->alen)
^
arrayfuncs.c:4618:31: error: ‘ArrayBuildState’ has no member named ‘alen’
if (astate->nelems >= astate->alen)
^
arrayfuncs.c:4620:10: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->alen *= 2;Sorry, correct patch attached.
This patch is in patience format (git --patience ..). In previous patches,
i use context format (git --patience ... | filterdiff --format=context),
but it turns out that some modification is lost.
last version is compileable, but some is still broken
postgres=# select array_agg(array[i, i+1, i-1])
from generate_series(1,2) a(i);
ERROR: could not find array type for data type integer[]
but array(subselect) works
postgres=# select array(select a from xx);
array
-------------------
{{1,2,3},{1,2,3}}
(1 row)
Regards
Pavel
Show quoted text
--
Ali Akbar
Hi
I did some performance tests and it is interesting:
it is about 15% faster than original implementation.
Regards
Pavel
2014-10-24 13:58 GMT+02:00 Pavel Stehule <pavel.stehule@gmail.com>:
Show quoted text
2014-10-24 11:43 GMT+02:00 Ali Akbar <the.apaan@gmail.com>:
2014-10-24 16:26 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
Hi
some in last patch is wrong, I cannot to compile it:
arrayfuncs.c: In function ‘accumArrayResult’:
arrayfuncs.c:4603:9: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->alen = 64; /* arbitrary starting array size */
^
arrayfuncs.c:4604:9: error: ‘ArrayBuildState’ has no member named
‘dvalues’
astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
^
arrayfuncs.c:4604:44: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
^
arrayfuncs.c:4605:9: error: ‘ArrayBuildState’ has no member named
‘dnulls’
astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
^
arrayfuncs.c:4605:42: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
^
arrayfuncs.c:4606:9: error: ‘ArrayBuildState’ has no member named
‘nelems’
astate->nelems = 0;
^
arrayfuncs.c:4618:13: error: ‘ArrayBuildState’ has no member named
‘nelems’
if (astate->nelems >= astate->alen)
^
arrayfuncs.c:4618:31: error: ‘ArrayBuildState’ has no member named ‘alen’
if (astate->nelems >= astate->alen)
^
arrayfuncs.c:4620:10: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->alen *= 2;Sorry, correct patch attached.
This patch is in patience format (git --patience ..). In previous
patches, i use context format (git --patience ... | filterdiff
--format=context), but it turns out that some modification is lost.last version is compileable, but some is still broken
postgres=# select array_agg(array[i, i+1, i-1])
from generate_series(1,2) a(i);
ERROR: could not find array type for data type integer[]but array(subselect) works
postgres=# select array(select a from xx);
array
-------------------
{{1,2,3},{1,2,3}}
(1 row)Regards
Pavel
--
Ali Akbar
Michael Paquier wrote:
That's not surprising, sometimes filterdiff misses the shot.
Really? Wow, that's bad news. I've been using it to submit patches
from time to time ...
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
2014-10-24 13:58 GMT+02:00 Pavel Stehule <pavel.stehule@gmail.com>:
2014-10-24 11:43 GMT+02:00 Ali Akbar <the.apaan@gmail.com>:
2014-10-24 16:26 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
Hi
some in last patch is wrong, I cannot to compile it:
arrayfuncs.c: In function ‘accumArrayResult’:
arrayfuncs.c:4603:9: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->alen = 64; /* arbitrary starting array size */
^
arrayfuncs.c:4604:9: error: ‘ArrayBuildState’ has no member named
‘dvalues’
astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
^
arrayfuncs.c:4604:44: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
^
arrayfuncs.c:4605:9: error: ‘ArrayBuildState’ has no member named
‘dnulls’
astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
^
arrayfuncs.c:4605:42: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
^
arrayfuncs.c:4606:9: error: ‘ArrayBuildState’ has no member named
‘nelems’
astate->nelems = 0;
^
arrayfuncs.c:4618:13: error: ‘ArrayBuildState’ has no member named
‘nelems’
if (astate->nelems >= astate->alen)
^
arrayfuncs.c:4618:31: error: ‘ArrayBuildState’ has no member named ‘alen’
if (astate->nelems >= astate->alen)
^
arrayfuncs.c:4620:10: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->alen *= 2;Sorry, correct patch attached.
This patch is in patience format (git --patience ..). In previous
patches, i use context format (git --patience ... | filterdiff
--format=context), but it turns out that some modification is lost.last version is compileable, but some is still broken
postgres=# select array_agg(array[i, i+1, i-1])
from generate_series(1,2) a(i);
ERROR: could not find array type for data type integer[]
I am sorry, it works - I had a problem with broken database
I fixed small issue in regress tests and I enhanced tests for varlena types
and null values.
Regards
Pavel
Show quoted text
but array(subselect) works
postgres=# select array(select a from xx);
array
-------------------
{{1,2,3},{1,2,3}}
(1 row)Regards
Pavel
--
Ali Akbar
Attachments:
array_agg_anyarray-7.patchtext/x-patch; charset=US-ASCII; name=array_agg_anyarray-7.patchDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
new file mode 100644
index 7e5bcd9..f59738a
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
*************** NULL baz</literallayout>(3 rows)</entry>
*** 12046,12051 ****
--- 12046,12067 ----
<row>
<entry>
<indexterm>
+ <primary>array_agg</primary>
+ </indexterm>
+ <function>array_agg(<replaceable class="parameter">anyarray</replaceable>)</function>
+ </entry>
+ <entry>
+ any
+ </entry>
+ <entry>
+ the same array type as input type
+ </entry>
+ <entry>input arrays, aggregated into higher-order multidimesional array. Rejects NULL and empty array as input.</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>average</primary>
</indexterm>
<indexterm>
diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml
new file mode 100644
index 2f0680f..8c182a4
*** a/doc/src/sgml/syntax.sgml
--- b/doc/src/sgml/syntax.sgml
*************** SELECT ARRAY(SELECT oid FROM pg_proc WHE
*** 2238,2243 ****
--- 2238,2248 ----
array
-----------------------------------------------------------------------
{2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31,2412,2413}
+
+ SELECT ARRAY(SELECT array(select i) FROM generate_series(1,5) a(i));
+ array
+ -----------------------
+ {{1},{2},{3},{4},{5}}
(1 row)
</programlisting>
The subquery must return a single column. The resulting
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
new file mode 100644
index 41e973b..0261fcb
*** a/src/backend/nodes/nodeFuncs.c
--- b/src/backend/nodes/nodeFuncs.c
*************** exprType(const Node *expr)
*** 108,119 ****
type = exprType((Node *) tent->expr);
if (sublink->subLinkType == ARRAY_SUBLINK)
{
! type = get_array_type(type);
! if (!OidIsValid(type))
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_OBJECT),
! errmsg("could not find array type for data type %s",
! format_type_be(exprType((Node *) tent->expr)))));
}
}
else if (sublink->subLinkType == MULTIEXPR_SUBLINK)
--- 108,123 ----
type = exprType((Node *) tent->expr);
if (sublink->subLinkType == ARRAY_SUBLINK)
{
! if (!OidIsValid(get_element_type(type)))
! {
! /* not array, so check for its array type */
! type = get_array_type(type);
! if (!OidIsValid(type))
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_OBJECT),
! errmsg("could not find array type for data type %s",
! format_type_be(exprType((Node *) tent->expr)))));
! }
}
}
else if (sublink->subLinkType == MULTIEXPR_SUBLINK)
*************** exprType(const Node *expr)
*** 139,150 ****
type = subplan->firstColType;
if (subplan->subLinkType == ARRAY_SUBLINK)
{
! type = get_array_type(type);
! if (!OidIsValid(type))
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_OBJECT),
! errmsg("could not find array type for data type %s",
! format_type_be(subplan->firstColType))));
}
}
else if (subplan->subLinkType == MULTIEXPR_SUBLINK)
--- 143,158 ----
type = subplan->firstColType;
if (subplan->subLinkType == ARRAY_SUBLINK)
{
! if (!OidIsValid(get_element_type(type)))
! {
! /* not array, so check for its array type */
! type = get_array_type(type);
! if (!OidIsValid(type))
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_OBJECT),
! errmsg("could not find array type for data type %s",
! format_type_be(subplan->firstColType))));
! }
}
}
else if (subplan->subLinkType == MULTIEXPR_SUBLINK)
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
new file mode 100644
index 3e7dc85..8fc8b49
*** a/src/backend/optimizer/plan/subselect.c
--- b/src/backend/optimizer/plan/subselect.c
*************** build_subplan(PlannerInfo *root, Plan *p
*** 668,677 ****
Assert(!te->resjunk);
Assert(testexpr == NULL);
! arraytype = get_array_type(exprType((Node *) te->expr));
! if (!OidIsValid(arraytype))
! elog(ERROR, "could not find array type for datatype %s",
! format_type_be(exprType((Node *) te->expr)));
prm = generate_new_param(root,
arraytype,
exprTypmod((Node *) te->expr),
--- 668,683 ----
Assert(!te->resjunk);
Assert(testexpr == NULL);
!
! arraytype = exprType((Node *) te->expr);
! if (!OidIsValid(get_element_type(arraytype)))
! {
! /* not array, so get the array type */
! arraytype = get_array_type(exprType((Node *) te->expr));
! if (!OidIsValid(arraytype))
! elog(ERROR, "could not find array type for datatype %s",
! format_type_be(exprType((Node *) te->expr)));
! }
prm = generate_new_param(root,
arraytype,
exprTypmod((Node *) te->expr),
diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c
new file mode 100644
index 831466d..9fdb25b
*** a/src/backend/utils/adt/array_userfuncs.c
--- b/src/backend/utils/adt/array_userfuncs.c
***************
*** 16,22 ****
#include "utils/builtins.h"
#include "utils/lsyscache.h"
-
/*-----------------------------------------------------------------------------
* array_push :
* push an element onto either end of a one-dimensional array
--- 16,21 ----
*************** array_agg_finalfn(PG_FUNCTION_ARGS)
*** 513,520 ****
{
Datum result;
ArrayBuildState *state;
- int dims[1];
- int lbs[1];
/*
* Test for null before Asserting we are in right context. This is to
--- 512,517 ----
*************** array_agg_finalfn(PG_FUNCTION_ARGS)
*** 529,546 ****
state = (ArrayBuildState *) PG_GETARG_POINTER(0);
- dims[0] = state->nelems;
- lbs[0] = 1;
-
/*
* Make the result. We cannot release the ArrayBuildState because
* sometimes aggregate final functions are re-executed. Rather, it is
* nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
* so.
*/
! result = makeMdArrayResult(state, 1, dims, lbs,
! CurrentMemoryContext,
! false);
PG_RETURN_DATUM(result);
}
--- 526,568 ----
state = (ArrayBuildState *) PG_GETARG_POINTER(0);
/*
* Make the result. We cannot release the ArrayBuildState because
* sometimes aggregate final functions are re-executed. Rather, it is
* nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
* so.
*/
! if (!state->is_array_accum)
! {
! int dims[1];
! int lbs[1];
!
! dims[0] = ((ArrayBuildStateScalar *) state)->nelems;
! lbs[0] = 1;
!
! result = makeMdArrayResult(state, 1, dims, lbs,
! CurrentMemoryContext,
! false);
! }
! else
! result = makeArrayResultArray((ArrayBuildStateArray *) state,
! CurrentMemoryContext,
! false);
PG_RETURN_DATUM(result);
}
+
+ /*
+ * ARRAY_AGG(anyarray) aggregate function
+ */
+ Datum
+ array_agg_anyarray_transfn(PG_FUNCTION_ARGS)
+ {
+ return array_agg_transfn(fcinfo);
+ }
+
+ Datum
+ array_agg_anyarray_finalfn(PG_FUNCTION_ARGS)
+ {
+ return array_agg_finalfn(fcinfo);
+ }
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
new file mode 100644
index 6c8b41d..582912e
*** a/src/backend/utils/adt/arrayfuncs.c
--- b/src/backend/utils/adt/arrayfuncs.c
*************** static int width_bucket_array_variable(D
*** 145,151 ****
Oid collation,
TypeCacheEntry *typentry);
-
/*
* array_in :
* converts an array from the external format in "string" to
--- 145,150 ----
*************** accumArrayResult(ArrayBuildState *astate
*** 4588,4596 ****
--- 4587,4599 ----
MemoryContext arr_context,
oldcontext;
+ ArrayBuildStateScalar *astate_scalar = NULL; /* for scalar datum accumulation */
+ ArrayBuildStateArray *astate_array = NULL; /* for array datum accumulation */
+
if (astate == NULL)
{
/* First time through --- initialize */
+ Oid subelement_type = get_element_type(element_type);
/* Make a temporary context to hold all the junk */
arr_context = AllocSetContextCreate(rcontext,
*************** accumArrayResult(ArrayBuildState *astate
*** 4599,4658 ****
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
oldcontext = MemoryContextSwitchTo(arr_context);
! astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
! astate->mcontext = arr_context;
! astate->alen = 64; /* arbitrary starting array size */
! astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
! astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
! astate->nelems = 0;
! astate->element_type = element_type;
! get_typlenbyvalalign(element_type,
! &astate->typlen,
! &astate->typbyval,
! &astate->typalign);
}
else
{
oldcontext = MemoryContextSwitchTo(astate->mcontext);
! Assert(astate->element_type == element_type);
! /* enlarge dvalues[]/dnulls[] if needed */
! if (astate->nelems >= astate->alen)
{
! astate->alen *= 2;
! astate->dvalues = (Datum *)
! repalloc(astate->dvalues, astate->alen * sizeof(Datum));
! astate->dnulls = (bool *)
! repalloc(astate->dnulls, astate->alen * sizeof(bool));
}
}
! /*
! * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
! * it's varlena. (You might think that detoasting is not needed here
! * because construct_md_array can detoast the array elements later.
! * However, we must not let construct_md_array modify the ArrayBuildState
! * because that would mean array_agg_finalfn damages its input, which is
! * verboten. Also, this way frequently saves one copying step.)
! */
! if (!disnull && !astate->typbyval)
{
! if (astate->typlen == -1)
! dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
! else
! dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
}
! astate->dvalues[astate->nelems] = dvalue;
! astate->dnulls[astate->nelems] = disnull;
! astate->nelems++;
MemoryContextSwitchTo(oldcontext);
return astate;
}
/*
! * makeArrayResult - produce 1-D final result of accumArrayResult
*
* astate is working state (not NULL)
* rcontext is where to construct result
--- 4602,4825 ----
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
oldcontext = MemoryContextSwitchTo(arr_context);
!
! if (subelement_type == InvalidOid)
! {
! /* scalar accumulate */
! astate_scalar = (ArrayBuildStateScalar *) palloc(sizeof(ArrayBuildStateScalar));
! astate = (ArrayBuildState *) astate_scalar;
! astate->is_array_accum = false;
! astate->mcontext = arr_context;
!
! astate_scalar->alen = 64; /* arbitrary starting array size */
! astate_scalar->dvalues = (Datum *) palloc(astate_scalar->alen * sizeof(Datum));
! astate_scalar->dnulls = (bool *) palloc(astate_scalar->alen * sizeof(bool));
! astate_scalar->nelems = 0;
!
! astate->element_type = element_type;
! get_typlenbyvalalign(element_type,
! &astate->typlen,
! &astate->typbyval,
! &astate->typalign);
! }
! else
! {
! /* array accumulate */
! astate_array = (ArrayBuildStateArray *) palloc(sizeof(ArrayBuildStateArray));
! astate = (ArrayBuildState *) astate_array;
! astate->is_array_accum = true;
! astate->mcontext = arr_context;
!
! astate_array->abytes = 0;
! astate_array->aitems = 0;
! astate_array->data = NULL;
! astate_array->nullbitmap = NULL;
! astate_array->nitems = 0;
! astate_array->narray = 0;
!
! astate->element_type = subelement_type;
! get_typlenbyvalalign(subelement_type,
! &astate->typlen,
! &astate->typbyval,
! &astate->typalign);
! }
}
else
{
oldcontext = MemoryContextSwitchTo(astate->mcontext);
! if (!astate->is_array_accum)
{
! Assert(astate->element_type == element_type);
! astate_scalar = (ArrayBuildStateScalar *) astate;
!
! /* enlarge dvalues[]/dnulls[] if needed */
! if (astate_scalar->nelems >= astate_scalar->alen)
! {
! astate_scalar->alen *= 2;
! astate_scalar->dvalues = (Datum *)
! repalloc(astate_scalar->dvalues, astate_scalar->alen * sizeof(Datum));
! astate_scalar->dnulls = (bool *)
! repalloc(astate_scalar->dnulls, astate_scalar->alen * sizeof(bool));
! }
! }
! else
! {
! Assert(astate->element_type == get_element_type(element_type));
! astate_array = (ArrayBuildStateArray *) astate;
! /*
! * array accumulate alloc & realloc placed below because
! * we need to know its length first
! */
}
}
! if (!astate->is_array_accum)
{
! /*
! * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
! * it's varlena. (You might think that detoasting is not needed here
! * because construct_md_array can detoast the array elements later.
! * However, we must not let construct_md_array modify the ArrayBuildState
! * because that would mean array_agg_finalfn damages its input, which is
! * verboten. Also, this way frequently saves one copying step.)
! */
! if (!disnull && !astate->typbyval)
! {
! if (astate->typlen == -1)
! dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
! else
! dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
! }
!
! astate_scalar->dvalues[astate_scalar->nelems] = dvalue;
! astate_scalar->dnulls[astate_scalar->nelems] = disnull;
! astate_scalar->nelems++;
}
+ else
+ {
+ ArrayType *arg;
+ int *dims,
+ *lbs,
+ ndims,
+ nitems,
+ ndatabytes;
+ char *data;
+ int i;
! if (disnull)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("cannot accumulate null arrays")));
!
! arg = DatumGetArrayTypeP(dvalue);
!
! ndims = ARR_NDIM(arg);
! dims = ARR_DIMS(arg);
! lbs = ARR_LBOUND(arg);
! data = ARR_DATA_PTR(arg);
! nitems = ArrayGetNItems(ndims, dims);
+ ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
+
+ if (astate_array->data == NULL)
+ {
+ /* first allocation */
+ if (ndims == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot accumulate empty arrays")));
+
+ if (ndims + 1 > MAXDIM)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ ndims + 1, MAXDIM)));
+
+ astate_array->ndims = ndims;
+ astate_array->dims = (int *) palloc(ndims * sizeof(int));
+ astate_array->lbs = (int *) palloc(ndims * sizeof(int));
+ memcpy(astate_array->dims, dims, ndims * sizeof(int));
+ memcpy(astate_array->lbs, lbs, ndims * sizeof(int));
+
+ astate_array->abytes = ndatabytes >= 512 ? 4 * ndatabytes : 1024;
+ astate_array->aitems = 4 * nitems;
+ astate_array->data = (char *) palloc(astate_array->abytes);
+
+ memcpy(astate_array->data, data, ndatabytes);
+ astate_array->nbytes = ndatabytes;
+ astate_array->nitems = nitems;
+
+ if (ARR_HASNULL(arg))
+ {
+ astate_array->hasnull = true;
+ astate_array->nullbitmap = (bits8 *) palloc((astate_array->aitems + 7) / 8);
+ array_bitmap_copy(astate_array->nullbitmap, 0,
+ ARR_NULLBITMAP(arg), 0,
+ nitems);
+ }
+ else
+ astate_array->hasnull = false;
+ }
+ else
+ {
+ if (astate_array->ndims != ndims)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot aggregate incompatible arrays"),
+ errdetail("Arrays of %d and %d dimensions are not "
+ "compatible for concatenation.",
+ astate_array->ndims, ndims)));
+
+ for (i = 0; i < ndims; i++)
+ if (astate_array->dims[i] != dims[i] || astate_array->lbs[i] != lbs[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot aggregate incompatible arrays"),
+ errdetail("Arrays with differing element dimensions are "
+ "not compatible for concatenation.")));
+
+ if (astate_array->nbytes + ndatabytes >= astate_array->abytes)
+ {
+ astate_array->nbytes *= 2;
+ astate_array->data = (char *)
+ repalloc(astate_array->data, astate_array->nbytes);
+ }
+ if (astate_array->nitems + nitems >= astate_array->aitems)
+ {
+ astate_array->aitems *= 2;
+ astate_array->nullbitmap = (bits8 *)
+ repalloc(astate_array->nullbitmap, (astate_array->aitems + 7) / 8);
+ }
+
+ memcpy(astate_array->data + astate_array->nbytes, data, ndatabytes);
+ astate_array->nbytes += ndatabytes;
+
+ if (ARR_HASNULL(arg) || astate_array->hasnull)
+ {
+ if (!astate_array->hasnull)
+ {
+ astate_array->hasnull = true;
+ astate_array->nullbitmap = (bits8 *) palloc((astate_array->aitems + 7) / 8);
+ array_bitmap_copy(astate_array->nullbitmap, 0,
+ NULL, 0,
+ astate_array->nitems);
+ }
+ array_bitmap_copy(astate_array->nullbitmap, astate_array->nitems,
+ ARR_NULLBITMAP(arg), 0,
+ nitems);
+ astate_array->nitems += nitems;
+ }
+ }
+ astate_array->narray += 1;
+ }
MemoryContextSwitchTo(oldcontext);
return astate;
}
/*
! * makeArrayResult - produce 1-D final result of scalar accumArrayResult
! * - produce N+1-D final result of array accumArrayResult
*
* astate is working state (not NULL)
* rcontext is where to construct result
*************** Datum
*** 4661,4677 ****
makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext)
{
! int dims[1];
! int lbs[1];
! dims[0] = astate->nelems;
! lbs[0] = 1;
! return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true);
}
/*
! * makeMdArrayResult - produce multi-D final result of accumArrayResult
*
* beware: no check that specified dimensions match the number of values
* accumulated.
--- 4828,4849 ----
makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext)
{
! if (!astate->is_array_accum)
! {
! int dims[1];
! int lbs[1];
! dims[0] = ((ArrayBuildStateScalar *) astate)->nelems;
! lbs[0] = 1;
! return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true);
! }
! else
! return makeArrayResultArray((ArrayBuildStateArray *) astate, rcontext, true);
}
/*
! * makeMdArrayResult - produce multi-D final result of scalar accumArrayResult
*
* beware: no check that specified dimensions match the number of values
* accumulated.
*************** makeMdArrayResult(ArrayBuildState *astat
*** 4690,4701 ****
{
ArrayType *result;
MemoryContext oldcontext;
/* Build the final array result in rcontext */
oldcontext = MemoryContextSwitchTo(rcontext);
! result = construct_md_array(astate->dvalues,
! astate->dnulls,
ndims,
dims,
lbs,
--- 4862,4878 ----
{
ArrayType *result;
MemoryContext oldcontext;
+ ArrayBuildStateScalar *astate_scalar;
+
+ Assert(!astate->is_array_accum);
+
+ astate_scalar = (ArrayBuildStateScalar *) astate;
/* Build the final array result in rcontext */
oldcontext = MemoryContextSwitchTo(rcontext);
! result = construct_md_array(astate_scalar->dvalues,
! astate_scalar->dnulls,
ndims,
dims,
lbs,
*************** makeMdArrayResult(ArrayBuildState *astat
*** 4713,4718 ****
--- 4890,4957 ----
return PointerGetDatum(result);
}
+ /*
+ * makeMdArrayResultArray - produce N+1-D final result of array accumArrayResult
+ *
+ * astate is working state (not NULL)
+ * rcontext is where to construct result
+ * release is true if okay to release working state
+ */
+ Datum
+ makeArrayResultArray(ArrayBuildStateArray *astate,
+ MemoryContext rcontext,
+ bool release)
+ {
+ MemoryContext oldcontext;
+
+ ArrayType *result;
+
+ int dataoffset,
+ nbytes,
+ ndims;
+
+ oldcontext = MemoryContextSwitchTo(rcontext);
+
+ ndims = astate->ndims + 1;
+ nbytes = astate->nbytes;
+ /* compute required space */
+ if (astate->hasnull)
+ {
+ dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, astate->nitems);
+ nbytes += dataoffset;
+ }
+ else
+ {
+ dataoffset = 0;
+ nbytes += ARR_OVERHEAD_NONULLS(ndims);
+ }
+
+ result = (ArrayType *) palloc0(nbytes);
+ SET_VARSIZE(result, nbytes);
+ result->ndim = astate->ndims + 1;
+ result->dataoffset = dataoffset;
+ result->elemtype = astate->astate.element_type;
+
+ ARR_DIMS(result)[0] = astate->narray;
+ ARR_LBOUND(result)[0] = 1;
+ memcpy(&(ARR_DIMS(result)[1]), astate->dims, (ndims - 1) * sizeof(int));
+ memcpy(&(ARR_LBOUND(result)[1]), astate->lbs, (ndims - 1) * sizeof(int));
+
+ memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
+ if (astate->hasnull)
+ array_bitmap_copy(ARR_NULLBITMAP(result), 0,
+ astate->nullbitmap, 0,
+ astate->nitems);
+
+ MemoryContextSwitchTo(oldcontext);
+
+ /* Clean up all the junk */
+ if (release)
+ MemoryContextDelete(astate->astate.mcontext);
+
+ PG_RETURN_DATUM(PointerGetDatum(result));
+ }
+
Datum
array_larger(PG_FUNCTION_ARGS)
{
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
new file mode 100644
index 3ba9e5e..2005199
*** a/src/include/catalog/pg_aggregate.h
--- b/src/include/catalog/pg_aggregate.h
*************** DATA(insert ( 2901 n 0 xmlconcat2 -
*** 275,280 ****
--- 275,281 ----
/* array */
DATA(insert ( 2335 n 0 array_agg_transfn array_agg_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
+ DATA(insert ( 6005 n 0 array_agg_anyarray_transfn array_agg_anyarray_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
/* text */
DATA(insert ( 3538 n 0 string_agg_transfn string_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
new file mode 100644
index b6dc1b8..9273c1f
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DATA(insert OID = 3167 ( array_remove
*** 879,889 ****
DESCR("remove any occurrences of an element from an array");
DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ ));
DESCR("replace any occurrences of an element in an array");
! DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2283" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function");
! DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2283" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
DESCR("aggregate final function");
! DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2283" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into an array");
DATA(insert OID = 3218 ( width_bucket PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "2283 2277" _null_ _null_ _null_ _null_ width_bucket_array _null_ _null_ _null_ ));
DESCR("bucket number of operand given a sorted array of bucket lower bounds");
--- 879,895 ----
DESCR("remove any occurrences of an element from an array");
DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ ));
DESCR("replace any occurrences of an element in an array");
! DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2776" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function");
! DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2776" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
DESCR("aggregate final function");
! DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2776" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
! DESCR("concatenate aggregate input into an array");
! DATA(insert OID = 6003 ( array_agg_anyarray_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_transfn _null_ _null_ _null_ ));
! DESCR("aggregate transition function");
! DATA(insert OID = 6004 ( array_agg_anyarray_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_finalfn _null_ _null_ _null_ ));
! DESCR("aggregate final function");
! DATA(insert OID = 6005 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2277" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into an array");
DATA(insert OID = 3218 ( width_bucket PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "2283 2277" _null_ _null_ _null_ _null_ width_bucket_array _null_ _null_ _null_ ));
DESCR("bucket number of operand given a sorted array of bucket lower bounds");
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
new file mode 100644
index e744314..c6fbc7f
*** a/src/include/utils/array.h
--- b/src/include/utils/array.h
*************** typedef struct
*** 76,89 ****
/*
* working state for accumArrayResult() and friends
*/
typedef struct ArrayBuildState
{
MemoryContext mcontext; /* where all the temp stuff is kept */
- Datum *dvalues; /* array of accumulated Datums */
- bool *dnulls; /* array of is-null flags for Datums */
- int alen; /* allocated length of above arrays */
- int nelems; /* number of valid entries in above arrays */
Oid element_type; /* data type of the Datums */
int16 typlen; /* needed info about datatype */
bool typbyval;
--- 76,90 ----
/*
* working state for accumArrayResult() and friends
+ *
+ * is_array_accum: whether accumulating array values.
+ * (if true must be casted to ArrayBuildStateArray, else
+ * cast to ArrayBuildStateScalar)
*/
typedef struct ArrayBuildState
{
+ bool is_array_accum;
MemoryContext mcontext; /* where all the temp stuff is kept */
Oid element_type; /* data type of the Datums */
int16 typlen; /* needed info about datatype */
bool typbyval;
*************** typedef struct ArrayBuildState
*** 91,96 ****
--- 92,134 ----
} ArrayBuildState;
/*
+ * array build state for array accumulation of scalar datums
+ */
+ typedef struct ArrayBuildStateScalar
+ {
+ ArrayBuildState astate;
+
+ Datum *dvalues; /* array of accumulated Datums */
+ bool *dnulls; /* array of is-null flags for Datums */
+ int alen; /* allocated length of above arrays */
+ int nelems; /* number of valid entries in above arrays */
+ } ArrayBuildStateScalar;
+
+
+ /*
+ * array build state for array accumulation of array datums
+ */
+ typedef struct
+ {
+ ArrayBuildState astate;
+
+ char *data; /* array of accumulated data */
+ bits8 *nullbitmap; /* bitmap of is-null flags for data */
+
+ int abytes; /* allocated length of above arrays */
+ int aitems; /* allocated length of above arrays */
+ int nbytes; /* number of used bytes in above arrays */
+ int nitems; /* number of elements in above arrays */
+ int narray; /* number of array accumulated */
+
+ int ndims; /* element dimensions */
+ int *dims;
+ int *lbs;
+
+ bool hasnull; /* any element has null */
+ } ArrayBuildStateArray;
+
+ /*
* structure to cache type metadata needed for array manipulation
*/
typedef struct ArrayMetaState
*************** extern Datum makeArrayResult(ArrayBuildS
*** 260,266 ****
MemoryContext rcontext);
extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
int *dims, int *lbs, MemoryContext rcontext, bool release);
!
extern ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim);
extern bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull);
extern void array_free_iterator(ArrayIterator iterator);
--- 298,305 ----
MemoryContext rcontext);
extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
int *dims, int *lbs, MemoryContext rcontext, bool release);
! extern Datum makeArrayResultArray(ArrayBuildStateArray *astate,
! MemoryContext rcontext, bool release);
extern ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim);
extern bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull);
extern void array_free_iterator(ArrayIterator iterator);
*************** extern ArrayType *create_singleton_array
*** 293,298 ****
--- 332,340 ----
extern Datum array_agg_transfn(PG_FUNCTION_ARGS);
extern Datum array_agg_finalfn(PG_FUNCTION_ARGS);
+ extern Datum array_agg_anyarray_transfn(PG_FUNCTION_ARGS);
+ extern Datum array_agg_anyarray_finalfn(PG_FUNCTION_ARGS);
+
/*
* prototypes for functions defined in array_typanalyze.c
*/
diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out
new file mode 100644
index 58df854..7568724
*** a/src/test/regress/expected/aggregates.out
--- b/src/test/regress/expected/aggregates.out
*************** select array_agg(distinct a order by a d
*** 914,919 ****
--- 914,984 ----
{3,2,1,NULL}
(1 row)
+ -- array_agg(anyarray)
+ select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar);
+ array_agg
+ ---------------
+ {{1,2},{3,4}}
+ (1 row)
+
+ select array_agg(distinct ar order by ar desc)
+ from (select array[i / 2] from generate_series(1,10) a(i)) b(ar);
+ array_agg
+ ---------------------------
+ {{5},{4},{3},{2},{1},{0}}
+ (1 row)
+
+ select array_agg(ar)
+ from (select array_agg(array[i, i+1, i-1])
+ from generate_series(1,2) a(i)) b(ar);
+ array_agg
+ ---------------------
+ {{{1,2,0},{2,3,1}}}
+ (1 row)
+
+ -- array_agg(anyarray), varlena types
+ select array_agg(array[1.2,1.3,1.4]) from generate_series(1,3);
+ array_agg
+ ---------------------------------------------
+ {{1.2,1.3,1.4},{1.2,1.3,1.4},{1.2,1.3,1.4}}
+ (1 row)
+
+ select array_agg(array['Hello','Hohoho','Hi']) from generate_series(1,3);
+ array_agg
+ ---------------------------------------------------------
+ {{Hello,Hohoho,Hi},{Hello,Hohoho,Hi},{Hello,Hohoho,Hi}}
+ (1 row)
+
+ -- array_agg(anyarray), arrays with nulls
+ select array_agg(array[i, null, i+1, null, i+2]) from generate_series(1,3) g(i);
+ array_agg
+ ---------------------------------------------------------
+ {{1,NULL,2,NULL,3},{2,NULL,3,NULL,4},{3,NULL,4,NULL,5}}
+ (1 row)
+
+ select array_agg(array[1.1+ i, null, 1.1+i+1, null, 1.1+i+2]) from generate_series(1,3) g(i);
+ array_agg
+ ---------------------------------------------------------------------------
+ {{2.1,NULL,3.1,NULL,4.1},{3.1,NULL,4.1,NULL,5.1},{4.1,NULL,5.1,NULL,6.1}}
+ (1 row)
+
+ select array_agg(array[null, 'Hello','Hohoho', null,'Hi']) from generate_series(1,3);
+ array_agg
+ ---------------------------------------------------------------------------------------
+ {{NULL,Hello,Hohoho,NULL,Hi},{NULL,Hello,Hohoho,NULL,Hi},{NULL,Hello,Hohoho,NULL,Hi}}
+ (1 row)
+
+ select array_agg(array[[null, 'Hello', null, 'Hi'],['Hello', null, 'Hi', null]]) from generate_series(1,2);
+ array_agg
+ -------------------------------------------------------------------------------------------
+ {{{NULL,Hello,NULL,Hi},{Hello,NULL,Hi,NULL}},{{NULL,Hello,NULL,Hi},{Hello,NULL,Hi,NULL}}}
+ (1 row)
+
+ select array_agg('{}'::int[]) from generate_series(1,2);
+ ERROR: cannot accumulate empty arrays
+ select array_agg(null::int[]) from generate_series(1,2);
+ ERROR: cannot accumulate null arrays
-- multi-arg aggs, strict/nonstrict, distinct/order by
select aggfstr(a,b,c)
from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c);
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
new file mode 100644
index 46eff67..e80ebec
*** a/src/test/regress/expected/arrays.out
--- b/src/test/regress/expected/arrays.out
*************** select array_agg(unique1) from tenk1 whe
*** 1521,1526 ****
--- 1521,1543 ----
(1 row)
+ select array(select unique1 from tenk1 where unique1 < 15 order by unique1);
+ array
+ --------------------------------------
+ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14}
+ (1 row)
+
+ select array(select array[i,i/2] from generate_series(1,5) a(i));
+ array
+ ---------------------------------
+ {{1,0},{2,1},{3,1},{4,2},{5,2}}
+ (1 row)
+
+ -- cannot accumulate null arrays and empty arrays
+ select array(select null::int[]);
+ ERROR: cannot accumulate null arrays
+ select array(select '{}'::int[]);
+ ERROR: cannot accumulate empty arrays
select unnest(array[1,2,3]);
unnest
--------
diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql
new file mode 100644
index 8096a6f..165f248
*** a/src/test/regress/sql/aggregates.sql
--- b/src/test/regress/sql/aggregates.sql
*************** select array_agg(distinct a order by a d
*** 322,327 ****
--- 322,350 ----
select array_agg(distinct a order by a desc nulls last)
from (values (1),(2),(1),(3),(null),(2)) v(a);
+ -- array_agg(anyarray)
+ select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar);
+ select array_agg(distinct ar order by ar desc)
+ from (select array[i / 2] from generate_series(1,10) a(i)) b(ar);
+ select array_agg(ar)
+ from (select array_agg(array[i, i+1, i-1])
+ from generate_series(1,2) a(i)) b(ar);
+
+ -- array_agg(anyarray), varlena types
+ select array_agg(array[1.2,1.3,1.4]) from generate_series(1,3);
+ select array_agg(array['Hello','Hohoho','Hi']) from generate_series(1,3);
+
+ -- array_agg(anyarray), arrays with nulls
+ select array_agg(array[i, null, i+1, null, i+2]) from generate_series(1,3) g(i);
+ select array_agg(array[1.1+ i, null, 1.1+i+1, null, 1.1+i+2]) from generate_series(1,3) g(i);
+ select array_agg(array[null, 'Hello','Hohoho', null,'Hi']) from generate_series(1,3);
+
+ select array_agg(array[[null, 'Hello', null, 'Hi'],['Hello', null, 'Hi', null]]) from generate_series(1,2);
+
+ select array_agg('{}'::int[]) from generate_series(1,2);
+ select array_agg(null::int[]) from generate_series(1,2);
+
-- multi-arg aggs, strict/nonstrict, distinct/order by
select aggfstr(a,b,c)
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
new file mode 100644
index fa8a20a..cb00f5f
*** a/src/test/regress/sql/arrays.sql
--- b/src/test/regress/sql/arrays.sql
*************** select array_agg(ten) from (select ten f
*** 432,437 ****
--- 432,443 ----
select array_agg(nullif(ten, 4)) from (select ten from tenk1 where unique1 < 15 order by unique1) ss;
select array_agg(unique1) from tenk1 where unique1 < -15;
+ select array(select unique1 from tenk1 where unique1 < 15 order by unique1);
+ select array(select array[i,i/2] from generate_series(1,5) a(i));
+ -- cannot accumulate null arrays and empty arrays
+ select array(select null::int[]);
+ select array(select '{}'::int[]);
+
select unnest(array[1,2,3]);
select * from unnest(array[1,2,3]);
select unnest(array[1,2,3,4.5]::float8[]);
Hi Ali
I checked a code. I am thinking so code organization is not good.
accumArrayResult is too long now. makeMdArrayResult will not work, when
arrays was joined (it is not consistent now). I don't like a usage of
state->is_array_accum in array_userfunc.c -- it is signal of wrong
wrapping.
next question: there is function array_append(anyarray, anyelement). Isn't
time to define array_append(anyarray, anyarray) now?
Regards
Pavel
2014-10-24 15:05 GMT+02:00 Pavel Stehule <pavel.stehule@gmail.com>:
Show quoted text
2014-10-24 13:58 GMT+02:00 Pavel Stehule <pavel.stehule@gmail.com>:
2014-10-24 11:43 GMT+02:00 Ali Akbar <the.apaan@gmail.com>:
2014-10-24 16:26 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
Hi
some in last patch is wrong, I cannot to compile it:
arrayfuncs.c: In function ‘accumArrayResult’:
arrayfuncs.c:4603:9: error: ‘ArrayBuildState’ has no member named ‘alen’
astate->alen = 64; /* arbitrary starting array size */
^
arrayfuncs.c:4604:9: error: ‘ArrayBuildState’ has no member named
‘dvalues’
astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
^
arrayfuncs.c:4604:44: error: ‘ArrayBuildState’ has no member named
‘alen’
astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
^
arrayfuncs.c:4605:9: error: ‘ArrayBuildState’ has no member named
‘dnulls’
astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
^
arrayfuncs.c:4605:42: error: ‘ArrayBuildState’ has no member named
‘alen’
astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
^
arrayfuncs.c:4606:9: error: ‘ArrayBuildState’ has no member named
‘nelems’
astate->nelems = 0;
^
arrayfuncs.c:4618:13: error: ‘ArrayBuildState’ has no member named
‘nelems’
if (astate->nelems >= astate->alen)
^
arrayfuncs.c:4618:31: error: ‘ArrayBuildState’ has no member named
‘alen’
if (astate->nelems >= astate->alen)
^
arrayfuncs.c:4620:10: error: ‘ArrayBuildState’ has no member named
‘alen’
astate->alen *= 2;Sorry, correct patch attached.
This patch is in patience format (git --patience ..). In previous
patches, i use context format (git --patience ... | filterdiff
--format=context), but it turns out that some modification is lost.last version is compileable, but some is still broken
postgres=# select array_agg(array[i, i+1, i-1])
from generate_series(1,2) a(i);
ERROR: could not find array type for data type integer[]I am sorry, it works - I had a problem with broken database
I fixed small issue in regress tests and I enhanced tests for varlena
types and null values.Regards
Pavel
but array(subselect) works
postgres=# select array(select a from xx);
array
-------------------
{{1,2,3},{1,2,3}}
(1 row)Regards
Pavel
--
Ali Akbar
I fixed small issue in regress tests and I enhanced tests for varlena
types and null values.
Thanks.
it is about 15% faster than original implementation.
15% faster than array_agg(scalar)? I haven't verify the performance, but
because the internal array data and null bitmap is copied as-is, that will
be faster.
2014-10-25 1:51 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
Hi Ali
I checked a code. I am thinking so code organization is not good.
accumArrayResult is too long now. makeMdArrayResult will not work, when
arrays was joined (it is not consistent now). I don't like a usage of
state->is_array_accum in array_userfunc.c -- it is signal of wrong
wrapping.
Yes, i was thinking the same. Attached WIP patch to reorganizate the code.
makeMdArrayResult works now, with supplied arguments act as override from
default values calculated from ArrayBuildStateArray.
In array_userfunc.c, because we don't want to override ndims, dims and lbs,
i copied array_agg_finalfn and only change the call to makeMdArrayResult
(we don't uses makeArrayResult because we want to set release to false).
Another alternative is to create new makeArrayResult-like function that has
parameter bool release.
next question: there is function array_append(anyarray, anyelement). Isn't
time to define array_append(anyarray, anyarray) now?
There is array_cat(anyarray, anyarray):
/*-----------------------------------------------------------------------------
* array_cat :
* concatenate two nD arrays to form an nD array, or
* push an (n-1)D array onto the end of an nD array
*----------------------------------------------------------------------------
*/
Regards,
--
Ali Akbar
Attachments:
array_agg_anyarray-8.patchtext/x-patch; charset=US-ASCII; name=array_agg_anyarray-8.patchDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 7e5bcd9..f59738a 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -12046,6 +12046,22 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<indexterm>
+ <primary>array_agg</primary>
+ </indexterm>
+ <function>array_agg(<replaceable class="parameter">anyarray</replaceable>)</function>
+ </entry>
+ <entry>
+ any
+ </entry>
+ <entry>
+ the same array type as input type
+ </entry>
+ <entry>input arrays, aggregated into higher-order multidimesional array. Rejects NULL and empty array as input.</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>average</primary>
</indexterm>
<indexterm>
diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml
index 2f0680f..8c182a4 100644
--- a/doc/src/sgml/syntax.sgml
+++ b/doc/src/sgml/syntax.sgml
@@ -2238,6 +2238,11 @@ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
array
-----------------------------------------------------------------------
{2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31,2412,2413}
+
+SELECT ARRAY(SELECT array(select i) FROM generate_series(1,5) a(i));
+ array
+-----------------------
+ {{1},{2},{3},{4},{5}}
(1 row)
</programlisting>
The subquery must return a single column. The resulting
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 41e973b..0261fcb 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -108,12 +108,16 @@ exprType(const Node *expr)
type = exprType((Node *) tent->expr);
if (sublink->subLinkType == ARRAY_SUBLINK)
{
- type = get_array_type(type);
- if (!OidIsValid(type))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("could not find array type for data type %s",
- format_type_be(exprType((Node *) tent->expr)))));
+ if (!OidIsValid(get_element_type(type)))
+ {
+ /* not array, so check for its array type */
+ type = get_array_type(type);
+ if (!OidIsValid(type))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(exprType((Node *) tent->expr)))));
+ }
}
}
else if (sublink->subLinkType == MULTIEXPR_SUBLINK)
@@ -139,12 +143,16 @@ exprType(const Node *expr)
type = subplan->firstColType;
if (subplan->subLinkType == ARRAY_SUBLINK)
{
- type = get_array_type(type);
- if (!OidIsValid(type))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("could not find array type for data type %s",
- format_type_be(subplan->firstColType))));
+ if (!OidIsValid(get_element_type(type)))
+ {
+ /* not array, so check for its array type */
+ type = get_array_type(type);
+ if (!OidIsValid(type))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(subplan->firstColType))));
+ }
}
}
else if (subplan->subLinkType == MULTIEXPR_SUBLINK)
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 3e7dc85..8fc8b49 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -668,10 +668,16 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
Assert(!te->resjunk);
Assert(testexpr == NULL);
- arraytype = get_array_type(exprType((Node *) te->expr));
- if (!OidIsValid(arraytype))
- elog(ERROR, "could not find array type for datatype %s",
- format_type_be(exprType((Node *) te->expr)));
+
+ arraytype = exprType((Node *) te->expr);
+ if (!OidIsValid(get_element_type(arraytype)))
+ {
+ /* not array, so get the array type */
+ arraytype = get_array_type(exprType((Node *) te->expr));
+ if (!OidIsValid(arraytype))
+ elog(ERROR, "could not find array type for datatype %s",
+ format_type_be(exprType((Node *) te->expr)));
+ }
prm = generate_new_param(root,
arraytype,
exprTypmod((Node *) te->expr),
diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c
index 831466d..8ad623c 100644
--- a/src/backend/utils/adt/array_userfuncs.c
+++ b/src/backend/utils/adt/array_userfuncs.c
@@ -16,7 +16,6 @@
#include "utils/builtins.h"
#include "utils/lsyscache.h"
-
/*-----------------------------------------------------------------------------
* array_push :
* push an element onto either end of a one-dimensional array
@@ -529,16 +528,57 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
state = (ArrayBuildState *) PG_GETARG_POINTER(0);
- dims[0] = state->nelems;
+ /*
+ * Make the result. We cannot release the ArrayBuildState because
+ * sometimes aggregate final functions are re-executed. Rather, it is
+ * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
+ * so.
+ */
+ dims[0] = ((ArrayBuildStateScalar *) state)->nelems;
lbs[0] = 1;
+ result = makeMdArrayResult(state, 1, dims, lbs,
+ CurrentMemoryContext,
+ false);
+
+ PG_RETURN_DATUM(result);
+}
+
+/*
+ * ARRAY_AGG(anyarray) aggregate function
+ */
+Datum
+array_agg_anyarray_transfn(PG_FUNCTION_ARGS)
+{
+ return array_agg_transfn(fcinfo);
+}
+
+Datum
+array_agg_anyarray_finalfn(PG_FUNCTION_ARGS)
+{
+ Datum result;
+ ArrayBuildState *state;
+
+ /*
+ * Test for null before Asserting we are in right context. This is to
+ * avoid possible Assert failure in 8.4beta installations, where it is
+ * possible for users to create NULL constants of type internal.
+ */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL(); /* returns null iff no input values */
+
+ /* cannot be called directly because of internal-type argument */
+ Assert(AggCheckCallContext(fcinfo, NULL));
+
+ state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
/*
* Make the result. We cannot release the ArrayBuildState because
* sometimes aggregate final functions are re-executed. Rather, it is
* nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
* so.
*/
- result = makeMdArrayResult(state, 1, dims, lbs,
+ result = makeMdArrayResult(state, -1, NULL, NULL,
CurrentMemoryContext,
false);
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 6c8b41d..ba51792 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -145,6 +145,19 @@ static int width_bucket_array_variable(Datum operand,
Oid collation,
TypeCacheEntry *typentry);
+/* internal functions of accumArrayResult */
+static ArrayBuildStateScalar *initializeArrayBuildStateScalar(Oid element_type,
+ MemoryContext arr_context);
+static ArrayBuildStateArray *initializeArrayBuildStateArray(Oid subelement_type,
+ MemoryContext arr_context);
+static void appendScalarDatum(ArrayBuildStateScalar *astate_scalar,
+ Datum dvalue, bool disnull);
+static void appendArrayDatum(ArrayBuildStateArray *astate_array,
+ Datum dvalue, bool disnull);
+
+/* internal function of makeMdArrayResult */
+static ArrayType *makeMdArrayResultArray(ArrayBuildStateArray *astate,
+ int ndims, int *dims, int *lbs);
/*
* array_in :
@@ -4588,9 +4601,13 @@ accumArrayResult(ArrayBuildState *astate,
MemoryContext arr_context,
oldcontext;
+ ArrayBuildStateScalar *astate_scalar = NULL; /* for scalar datum accumulation */
+ ArrayBuildStateArray *astate_array = NULL; /* for array datum accumulation */
+
if (astate == NULL)
{
/* First time through --- initialize */
+ Oid subelement_type = get_element_type(element_type);
/* Make a temporary context to hold all the junk */
arr_context = AllocSetContextCreate(rcontext,
@@ -4599,33 +4616,127 @@ accumArrayResult(ArrayBuildState *astate,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
oldcontext = MemoryContextSwitchTo(arr_context);
- astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
- astate->mcontext = arr_context;
- astate->alen = 64; /* arbitrary starting array size */
- astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
- astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
- astate->nelems = 0;
- astate->element_type = element_type;
- get_typlenbyvalalign(element_type,
- &astate->typlen,
- &astate->typbyval,
- &astate->typalign);
+
+ if (!OidIsValid(subelement_type))
+ {
+ astate_scalar = initializeArrayBuildStateScalar(element_type, arr_context);
+ astate = (ArrayBuildState *) astate_scalar;
+
+ appendScalarDatum(astate_scalar, dvalue, disnull);
+ }
+ else
+ {
+ astate_array = initializeArrayBuildStateArray(subelement_type, arr_context);
+ astate = (ArrayBuildState *) astate_array;
+
+ appendArrayDatum(astate_array, dvalue, disnull);
+ }
}
else
{
oldcontext = MemoryContextSwitchTo(astate->mcontext);
- Assert(astate->element_type == element_type);
- /* enlarge dvalues[]/dnulls[] if needed */
- if (astate->nelems >= astate->alen)
+ if (!astate->is_array_accum)
{
- astate->alen *= 2;
- astate->dvalues = (Datum *)
- repalloc(astate->dvalues, astate->alen * sizeof(Datum));
- astate->dnulls = (bool *)
- repalloc(astate->dnulls, astate->alen * sizeof(bool));
+ Assert(astate->element_type == element_type);
+ astate_scalar = (ArrayBuildStateScalar *) astate;
+
+ /* enlarge dvalues[]/dnulls[] if needed */
+ if (astate_scalar->nelems >= astate_scalar->alen)
+ {
+ astate_scalar->alen *= 2;
+ astate_scalar->dvalues = (Datum *)
+ repalloc(astate_scalar->dvalues, astate_scalar->alen * sizeof(Datum));
+ astate_scalar->dnulls = (bool *)
+ repalloc(astate_scalar->dnulls, astate_scalar->alen * sizeof(bool));
+ }
+
+ appendScalarDatum(astate_scalar, dvalue, disnull);
+ }
+ else
+ {
+ Assert(astate->element_type == get_element_type(element_type));
+ astate_array = (ArrayBuildStateArray *) astate;
+ /*
+ * alloc & realloc placed in appendArrayDatum because
+ * we need to know the dvalue length first
+ */
+ appendArrayDatum(astate_array, dvalue, disnull);
}
}
+ MemoryContextSwitchTo(oldcontext);
+
+ return astate;
+}
+
+/*
+ * Initialize ArrayBuildState for scalar accumulation (internal function of
+ * accumArrayResult)
+ */
+ArrayBuildStateScalar *
+initializeArrayBuildStateScalar(Oid element_type,
+ MemoryContext arr_context)
+{
+ ArrayBuildStateScalar *astate_scalar;
+
+ /* scalar accumulate */
+ astate_scalar = (ArrayBuildStateScalar *) palloc(sizeof(ArrayBuildStateScalar));
+ astate_scalar->astate.is_array_accum = false;
+ astate_scalar->astate.mcontext = arr_context;
+
+ astate_scalar->alen = 64; /* arbitrary starting array size */
+ astate_scalar->dvalues = (Datum *) palloc(astate_scalar->alen * sizeof(Datum));
+ astate_scalar->dnulls = (bool *) palloc(astate_scalar->alen * sizeof(bool));
+ astate_scalar->nelems = 0;
+
+ astate_scalar->astate.element_type = element_type;
+ get_typlenbyvalalign(element_type,
+ &astate_scalar->astate.typlen,
+ &astate_scalar->astate.typbyval,
+ &astate_scalar->astate.typalign);
+
+ return astate_scalar;
+}
+
+/*
+ * Initialize ArrayBuildState for array accum (internal of accumArrayResult)
+ */
+ArrayBuildStateArray *
+initializeArrayBuildStateArray(Oid subelement_type,
+ MemoryContext arr_context)
+{
+ ArrayBuildStateArray *astate_array;
+
+ /* array accumulate */
+ astate_array = (ArrayBuildStateArray *) palloc(sizeof(ArrayBuildStateArray));
+ astate_array->astate.is_array_accum = true;
+ astate_array->astate.mcontext = arr_context;
+
+ astate_array->abytes = 0;
+ astate_array->aitems = 0;
+ astate_array->data = NULL;
+ astate_array->nbytes = 0;
+ astate_array->hasnull = false;
+ astate_array->nullbitmap = NULL;
+ astate_array->nitems = 0;
+ astate_array->narray = 0;
+
+ astate_array->astate.element_type = subelement_type;
+ get_typlenbyvalalign(subelement_type,
+ &astate_array->astate.typlen,
+ &astate_array->astate.typbyval,
+ &astate_array->astate.typalign);
+
+ return astate_array;
+}
+
+/*
+ * Append a datum for scalar accumulation (internal of accumArrayResult)
+ */
+void
+appendScalarDatum(ArrayBuildStateScalar *astate_scalar,
+ Datum dvalue, bool disnull)
+{
/*
* Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
* it's varlena. (You might think that detoasting is not needed here
@@ -4634,25 +4745,129 @@ accumArrayResult(ArrayBuildState *astate,
* because that would mean array_agg_finalfn damages its input, which is
* verboten. Also, this way frequently saves one copying step.)
*/
- if (!disnull && !astate->typbyval)
+ if (!disnull && !astate_scalar->astate.typbyval)
{
- if (astate->typlen == -1)
+ if (astate_scalar->astate.typlen == -1)
dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
else
- dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
+ dvalue = datumCopy(dvalue, astate_scalar->astate.typbyval,
+ astate_scalar->astate.typlen);
}
- astate->dvalues[astate->nelems] = dvalue;
- astate->dnulls[astate->nelems] = disnull;
- astate->nelems++;
+ astate_scalar->dvalues[astate_scalar->nelems] = dvalue;
+ astate_scalar->dnulls[astate_scalar->nelems] = disnull;
+ astate_scalar->nelems++;
+}
- MemoryContextSwitchTo(oldcontext);
+/*
+ * Append a datum for array accum (internal of accumArrayResult)
+ */
+void
+appendArrayDatum(ArrayBuildStateArray *astate_array,
+ Datum dvalue, bool disnull)
+{
+ ArrayType *arg;
+ int *dims,
+ *lbs,
+ ndims,
+ nitems,
+ ndatabytes;
+ char *data;
+ int i;
- return astate;
+ if (disnull)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot accumulate null arrays")));
+
+ arg = DatumGetArrayTypeP(dvalue);
+
+ ndims = ARR_NDIM(arg);
+ dims = ARR_DIMS(arg);
+ lbs = ARR_LBOUND(arg);
+ data = ARR_DATA_PTR(arg);
+ nitems = ArrayGetNItems(ndims, dims);
+ ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
+
+ if (astate_array->data == NULL)
+ {
+ /* first allocation */
+ if (ndims == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot accumulate empty arrays")));
+
+ if (ndims + 1 > MAXDIM)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ ndims + 1, MAXDIM)));
+
+ astate_array->ndims = ndims;
+ astate_array->dims = (int *) palloc(ndims * sizeof(int));
+ astate_array->lbs = (int *) palloc(ndims * sizeof(int));
+ memcpy(astate_array->dims, dims, ndims * sizeof(int));
+ memcpy(astate_array->lbs, lbs, ndims * sizeof(int));
+
+ astate_array->abytes = ndatabytes >= 512 ? 4 * ndatabytes : 1024;
+ astate_array->data = (char *) palloc(astate_array->abytes);
+ astate_array->aitems = 4 * nitems;
+ }
+ else
+ {
+ if (astate_array->ndims != ndims)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot accumulate incompatible arrays"),
+ errdetail("Arrays of %d and %d dimensions are not "
+ "compatible for concatenation.",
+ astate_array->ndims, ndims)));
+
+ for (i = 0; i < ndims; i++)
+ if (astate_array->dims[i] != dims[i] || astate_array->lbs[i] != lbs[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot accumulate incompatible arrays"),
+ errdetail("Arrays with differing element dimensions are "
+ "not compatible for concatenation.")));
+
+ if (astate_array->nbytes + ndatabytes >= astate_array->abytes)
+ {
+ astate_array->abytes *= 2;
+ astate_array->data = (char *)
+ repalloc(astate_array->data, astate_array->abytes);
+ }
+ if (astate_array->nitems + nitems >= astate_array->aitems)
+ {
+ astate_array->aitems *= 2;
+ astate_array->nullbitmap = (bits8 *)
+ repalloc(astate_array->nullbitmap, (astate_array->aitems + 7) / 8);
+ }
+ }
+ memcpy(astate_array->data + astate_array->nbytes, data, ndatabytes);
+ astate_array->nbytes += ndatabytes;
+
+ if (ARR_HASNULL(arg) || astate_array->hasnull)
+ {
+ if (!astate_array->hasnull)
+ {
+ astate_array->hasnull = true;
+ astate_array->nullbitmap = (bits8 *) palloc((astate_array->aitems + 7) / 8);
+ array_bitmap_copy(astate_array->nullbitmap, 0,
+ NULL, 0,
+ astate_array->nitems);
+ }
+ array_bitmap_copy(astate_array->nullbitmap, astate_array->nitems,
+ ARR_NULLBITMAP(arg), 0,
+ nitems);
+ astate_array->nitems += nitems;
+ }
+ astate_array->narray += 1;
}
/*
- * makeArrayResult - produce 1-D final result of accumArrayResult
+ * makeArrayResult - produce 1-D final result of scalar accumArrayResult
+ * - produce N+1-D final result of array accumArrayResult
*
* astate is working state (not NULL)
* rcontext is where to construct result
@@ -4661,17 +4876,22 @@ Datum
makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext)
{
- int dims[1];
- int lbs[1];
+ if (!astate->is_array_accum)
+ {
+ int dims[1];
+ int lbs[1];
- dims[0] = astate->nelems;
- lbs[0] = 1;
+ dims[0] = ((ArrayBuildStateScalar *) astate)->nelems;
+ lbs[0] = 1;
- return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true);
+ return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true);
+ }
+ else
+ return makeMdArrayResult(astate, -1, NULL, NULL, rcontext, true);
}
/*
- * makeMdArrayResult - produce multi-D final result of accumArrayResult
+ * makeMdArrayResult - produce multi-D final result of scalar accumArrayResult
*
* beware: no check that specified dimensions match the number of values
* accumulated.
@@ -4679,6 +4899,11 @@ makeArrayResult(ArrayBuildState *astate,
* astate is working state (not NULL)
* rcontext is where to construct result
* release is true if okay to release working state
+ *
+ * for array accumulation:
+ * ndims == -1: default (calculated from astate)
+ * dims == NULL: default dims (calculated from astate)
+ * lbs == NULL: default (calculated from astate)
*/
Datum
makeMdArrayResult(ArrayBuildState *astate,
@@ -4694,8 +4919,13 @@ makeMdArrayResult(ArrayBuildState *astate,
/* Build the final array result in rcontext */
oldcontext = MemoryContextSwitchTo(rcontext);
- result = construct_md_array(astate->dvalues,
- astate->dnulls,
+ if (!astate->is_array_accum)
+ {
+ ArrayBuildStateScalar *astate_scalar;
+ astate_scalar = (ArrayBuildStateScalar *) astate;
+
+ result = construct_md_array(astate_scalar->dvalues,
+ astate_scalar->dnulls,
ndims,
dims,
lbs,
@@ -4703,6 +4933,16 @@ makeMdArrayResult(ArrayBuildState *astate,
astate->typlen,
astate->typbyval,
astate->typalign);
+ }
+ else
+ {
+ ArrayBuildStateArray *astate_array;
+ astate_array = (ArrayBuildStateArray *) astate;
+ result = makeMdArrayResultArray(astate_array,
+ ndims,
+ dims,
+ lbs);
+ }
MemoryContextSwitchTo(oldcontext);
@@ -4713,6 +4953,75 @@ makeMdArrayResult(ArrayBuildState *astate,
return PointerGetDatum(result);
}
+/*
+ * makeMdArrayResultArray - internal of makeArrayResult & makeMdArrayResult
+ *
+ * beware: no check that specified dimensions match the values
+ * accumulated.
+ *
+ * astate is working state (not NULL)
+ * ndims > -1 if wants to override default (calculated from astate)
+ * dims not NULL if wants to override default
+ * lbs not NULL if wants to override default
+ */
+ArrayType *
+makeMdArrayResultArray(ArrayBuildStateArray *astate,
+ int ndims,
+ int *dims,
+ int *lbs)
+{
+ ArrayType *result;
+
+ int dataoffset,
+ nbytes;
+
+ if (ndims == -1)
+ ndims = astate->ndims + 1;
+
+ nbytes = astate->nbytes;
+ /* compute required space */
+ if (astate->hasnull)
+ {
+ dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, astate->nitems);
+ nbytes += dataoffset;
+ }
+ else
+ {
+ dataoffset = 0;
+ nbytes += ARR_OVERHEAD_NONULLS(ndims);
+ }
+
+ result = (ArrayType *) palloc0(nbytes);
+ SET_VARSIZE(result, nbytes);
+ result->ndim = astate->ndims + 1;
+ result->dataoffset = dataoffset;
+ result->elemtype = astate->astate.element_type;
+
+ if (dims != NULL)
+ memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
+ else
+ {
+ ARR_DIMS(result)[0] = astate->narray;
+ memcpy(&(ARR_DIMS(result)[1]), astate->dims, (ndims - 1) * sizeof(int));
+ }
+ if (lbs != NULL)
+ memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
+ else
+ {
+ ARR_LBOUND(result)[0] = 1;
+ memcpy(&(ARR_LBOUND(result)[1]), astate->lbs, (ndims - 1) * sizeof(int));
+ }
+
+ memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
+
+ if (astate->hasnull)
+ array_bitmap_copy(ARR_NULLBITMAP(result), 0,
+ astate->nullbitmap, 0,
+ astate->nitems);
+
+ return result;
+}
+
Datum
array_larger(PG_FUNCTION_ARGS)
{
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 3ba9e5e..2005199 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -275,6 +275,7 @@ DATA(insert ( 2901 n 0 xmlconcat2 - - - - f f 0 142 0 0 0 _null_
/* array */
DATA(insert ( 2335 n 0 array_agg_transfn array_agg_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
+DATA(insert ( 6005 n 0 array_agg_anyarray_transfn array_agg_anyarray_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
/* text */
DATA(insert ( 3538 n 0 string_agg_transfn string_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index b6dc1b8..9273c1f 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -879,11 +879,17 @@ DATA(insert OID = 3167 ( array_remove PGNSP PGUID 12 1 0 0 0 f f f f f f i 2
DESCR("remove any occurrences of an element from an array");
DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ ));
DESCR("replace any occurrences of an element in an array");
-DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2283" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
+DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2776" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function");
-DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2283" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
+DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2776" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
DESCR("aggregate final function");
-DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2283" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2776" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+DESCR("concatenate aggregate input into an array");
+DATA(insert OID = 6003 ( array_agg_anyarray_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_transfn _null_ _null_ _null_ ));
+DESCR("aggregate transition function");
+DATA(insert OID = 6004 ( array_agg_anyarray_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_finalfn _null_ _null_ _null_ ));
+DESCR("aggregate final function");
+DATA(insert OID = 6005 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2277" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into an array");
DATA(insert OID = 3218 ( width_bucket PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "2283 2277" _null_ _null_ _null_ _null_ width_bucket_array _null_ _null_ _null_ ));
DESCR("bucket number of operand given a sorted array of bucket lower bounds");
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index e744314..8dddd02 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -76,14 +76,15 @@ typedef struct
/*
* working state for accumArrayResult() and friends
+ *
+ * is_array_accum: whether accumulating array values.
+ * (if true must be casted to ArrayBuildStateArray, else
+ * cast to ArrayBuildStateScalar)
*/
typedef struct ArrayBuildState
{
+ bool is_array_accum;
MemoryContext mcontext; /* where all the temp stuff is kept */
- Datum *dvalues; /* array of accumulated Datums */
- bool *dnulls; /* array of is-null flags for Datums */
- int alen; /* allocated length of above arrays */
- int nelems; /* number of valid entries in above arrays */
Oid element_type; /* data type of the Datums */
int16 typlen; /* needed info about datatype */
bool typbyval;
@@ -91,6 +92,43 @@ typedef struct ArrayBuildState
} ArrayBuildState;
/*
+ * array build state for array accumulation of scalar datums
+ */
+typedef struct ArrayBuildStateScalar
+{
+ ArrayBuildState astate;
+
+ Datum *dvalues; /* array of accumulated Datums */
+ bool *dnulls; /* array of is-null flags for Datums */
+ int alen; /* allocated length of above arrays */
+ int nelems; /* number of valid entries in above arrays */
+} ArrayBuildStateScalar;
+
+
+/*
+ * array build state for array accumulation of array datums
+ */
+typedef struct
+{
+ ArrayBuildState astate;
+
+ char *data; /* array of accumulated data */
+ bits8 *nullbitmap; /* bitmap of is-null flags for data */
+
+ int abytes; /* allocated length of above arrays */
+ int aitems; /* allocated length of above arrays */
+ int nbytes; /* number of used bytes in above arrays */
+ int nitems; /* number of elements in above arrays */
+ int narray; /* number of array accumulated */
+
+ int ndims; /* element dimensions */
+ int *dims;
+ int *lbs;
+
+ bool hasnull; /* any element has null */
+} ArrayBuildStateArray;
+
+/*
* structure to cache type metadata needed for array manipulation
*/
typedef struct ArrayMetaState
@@ -260,7 +298,6 @@ extern Datum makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext);
extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
int *dims, int *lbs, MemoryContext rcontext, bool release);
-
extern ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim);
extern bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull);
extern void array_free_iterator(ArrayIterator iterator);
@@ -293,6 +330,9 @@ extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
extern Datum array_agg_transfn(PG_FUNCTION_ARGS);
extern Datum array_agg_finalfn(PG_FUNCTION_ARGS);
+extern Datum array_agg_anyarray_transfn(PG_FUNCTION_ARGS);
+extern Datum array_agg_anyarray_finalfn(PG_FUNCTION_ARGS);
+
/*
* prototypes for functions defined in array_typanalyze.c
*/
diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out
index 58df854..607aeea 100644
--- a/src/test/regress/expected/aggregates.out
+++ b/src/test/regress/expected/aggregates.out
@@ -914,6 +914,76 @@ select array_agg(distinct a order by a desc nulls last)
{3,2,1,NULL}
(1 row)
+-- array_agg(anyarray)
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar);
+ array_agg
+---------------
+ {{1,2},{3,4}}
+(1 row)
+
+select array_agg(distinct ar order by ar desc)
+ from (select array[i / 2] from generate_series(1,10) a(i)) b(ar);
+ array_agg
+---------------------------
+ {{5},{4},{3},{2},{1},{0}}
+(1 row)
+
+select array_agg(ar)
+ from (select array_agg(array[i, i+1, i-1])
+ from generate_series(1,2) a(i)) b(ar);
+ array_agg
+---------------------
+ {{{1,2,0},{2,3,1}}}
+(1 row)
+
+-- array_agg(anyarray), varlena types
+select array_agg(array[1.2,1.3,1.4]) from generate_series(1,3);
+ array_agg
+---------------------------------------------
+ {{1.2,1.3,1.4},{1.2,1.3,1.4},{1.2,1.3,1.4}}
+(1 row)
+
+select array_agg(array['Hello','Hohoho','Hi']) from generate_series(1,3);
+ array_agg
+---------------------------------------------------------
+ {{Hello,Hohoho,Hi},{Hello,Hohoho,Hi},{Hello,Hohoho,Hi}}
+(1 row)
+
+-- array_agg(anyarray), arrays with nulls
+select array_agg(array[i, null, i+1, null, i+2]) from generate_series(1,3) g(i);
+ array_agg
+---------------------------------------------------------
+ {{1,NULL,2,NULL,3},{2,NULL,3,NULL,4},{3,NULL,4,NULL,5}}
+(1 row)
+
+select array_agg(array[1.1+ i, null, 1.1+i+1, null, 1.1+i+2]) from generate_series(1,3) g(i);
+ array_agg
+---------------------------------------------------------------------------
+ {{2.1,NULL,3.1,NULL,4.1},{3.1,NULL,4.1,NULL,5.1},{4.1,NULL,5.1,NULL,6.1}}
+(1 row)
+
+select array_agg(array[null, 'Hello','Hohoho', null,'Hi']) from generate_series(1,3);
+ array_agg
+---------------------------------------------------------------------------------------
+ {{NULL,Hello,Hohoho,NULL,Hi},{NULL,Hello,Hohoho,NULL,Hi},{NULL,Hello,Hohoho,NULL,Hi}}
+(1 row)
+
+select array_agg(array[[null, 'Hello', null, 'Hi'],['Hello', null, 'Hi', null]]) from generate_series(1,2);
+ array_agg
+-------------------------------------------------------------------------------------------
+ {{{NULL,Hello,NULL,Hi},{Hello,NULL,Hi,NULL}},{{NULL,Hello,NULL,Hi},{Hello,NULL,Hi,NULL}}}
+(1 row)
+
+-- errors
+select array_agg('{}'::int[]) from generate_series(1,2);
+ERROR: cannot accumulate empty arrays
+select array_agg(null::int[]) from generate_series(1,2);
+ERROR: cannot accumulate null arrays
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3}'::int[])) v(ar);
+ERROR: cannot accumulate incompatible arrays
+DETAIL: Arrays with differing element dimensions are not compatible for concatenation.
-- multi-arg aggs, strict/nonstrict, distinct/order by
select aggfstr(a,b,c)
from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c);
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index 46eff67..e80ebec 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -1521,6 +1521,23 @@ select array_agg(unique1) from tenk1 where unique1 < -15;
(1 row)
+select array(select unique1 from tenk1 where unique1 < 15 order by unique1);
+ array
+--------------------------------------
+ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14}
+(1 row)
+
+select array(select array[i,i/2] from generate_series(1,5) a(i));
+ array
+---------------------------------
+ {{1,0},{2,1},{3,1},{4,2},{5,2}}
+(1 row)
+
+-- cannot accumulate null arrays and empty arrays
+select array(select null::int[]);
+ERROR: cannot accumulate null arrays
+select array(select '{}'::int[]);
+ERROR: cannot accumulate empty arrays
select unnest(array[1,2,3]);
unnest
--------
diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql
index 8096a6f..a70419f 100644
--- a/src/test/regress/sql/aggregates.sql
+++ b/src/test/regress/sql/aggregates.sql
@@ -322,6 +322,32 @@ select array_agg(distinct a order by a desc)
select array_agg(distinct a order by a desc nulls last)
from (values (1),(2),(1),(3),(null),(2)) v(a);
+-- array_agg(anyarray)
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar);
+select array_agg(distinct ar order by ar desc)
+ from (select array[i / 2] from generate_series(1,10) a(i)) b(ar);
+select array_agg(ar)
+ from (select array_agg(array[i, i+1, i-1])
+ from generate_series(1,2) a(i)) b(ar);
+
+-- array_agg(anyarray), varlena types
+select array_agg(array[1.2,1.3,1.4]) from generate_series(1,3);
+select array_agg(array['Hello','Hohoho','Hi']) from generate_series(1,3);
+
+-- array_agg(anyarray), arrays with nulls
+select array_agg(array[i, null, i+1, null, i+2]) from generate_series(1,3) g(i);
+select array_agg(array[1.1+ i, null, 1.1+i+1, null, 1.1+i+2]) from generate_series(1,3) g(i);
+select array_agg(array[null, 'Hello','Hohoho', null,'Hi']) from generate_series(1,3);
+
+select array_agg(array[[null, 'Hello', null, 'Hi'],['Hello', null, 'Hi', null]]) from generate_series(1,2);
+
+-- errors
+select array_agg('{}'::int[]) from generate_series(1,2);
+select array_agg(null::int[]) from generate_series(1,2);
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3}'::int[])) v(ar);
+
-- multi-arg aggs, strict/nonstrict, distinct/order by
select aggfstr(a,b,c)
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index fa8a20a..cb00f5f 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -432,6 +432,12 @@ select array_agg(ten) from (select ten from tenk1 where unique1 < 15 order by un
select array_agg(nullif(ten, 4)) from (select ten from tenk1 where unique1 < 15 order by unique1) ss;
select array_agg(unique1) from tenk1 where unique1 < -15;
+select array(select unique1 from tenk1 where unique1 < 15 order by unique1);
+select array(select array[i,i/2] from generate_series(1,5) a(i));
+-- cannot accumulate null arrays and empty arrays
+select array(select null::int[]);
+select array(select '{}'::int[]);
+
select unnest(array[1,2,3]);
select * from unnest(array[1,2,3]);
select unnest(array[1,2,3,4.5]::float8[]);
2014-10-25 10:29 GMT+07:00 Ali Akbar <the.apaan@gmail.com>:
I fixed small issue in regress tests and I enhanced tests for varlena
types and null values.
Thanks.
it is about 15% faster than original implementation.
15% faster than array_agg(scalar)? I haven't verify the performance, but
because the internal array data and null bitmap is copied as-is, that will
be faster.2014-10-25 1:51 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
Hi Ali
I checked a code. I am thinking so code organization is not good.
accumArrayResult is too long now. makeMdArrayResult will not work, when
arrays was joined (it is not consistent now). I don't like a usage of
state->is_array_accum in array_userfunc.c -- it is signal of wrong
wrapping.Yes, i was thinking the same. Attached WIP patch to reorganizate the code.
makeMdArrayResult works now, with supplied arguments act as override from
default values calculated from ArrayBuildStateArray.In array_userfunc.c, because we don't want to override ndims, dims and
lbs, i copied array_agg_finalfn and only change the call to
makeMdArrayResult (we don't uses makeArrayResult because we want to set
release to false). Another alternative is to create new
makeArrayResult-like function that has parameter bool release.
adding makeArrayResult1 (do you have better name for this?), that accepts
bool release parameter. array_agg_finalfn becomes more clean, and no
duplicate code in array_agg_anyarray_finalfn.
next question: there is function array_append(anyarray, anyelement). Isn't
time to define array_append(anyarray, anyarray) now?
There is array_cat(anyarray, anyarray):
/*-----------------------------------------------------------------------------
* array_cat :
* concatenate two nD arrays to form an nD array, or
* push an (n-1)D array onto the end of an nD array*----------------------------------------------------------------------------
*/
Regards,
--
Ali Akbar
Attachments:
array_agg_anyarray-9.patchtext/x-patch; charset=US-ASCII; name=array_agg_anyarray-9.patchDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 7e5bcd9..f59738a 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -12046,6 +12046,22 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<indexterm>
+ <primary>array_agg</primary>
+ </indexterm>
+ <function>array_agg(<replaceable class="parameter">anyarray</replaceable>)</function>
+ </entry>
+ <entry>
+ any
+ </entry>
+ <entry>
+ the same array type as input type
+ </entry>
+ <entry>input arrays, aggregated into higher-order multidimesional array. Rejects NULL and empty array as input.</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>average</primary>
</indexterm>
<indexterm>
diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml
index 2f0680f..8c182a4 100644
--- a/doc/src/sgml/syntax.sgml
+++ b/doc/src/sgml/syntax.sgml
@@ -2238,6 +2238,11 @@ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
array
-----------------------------------------------------------------------
{2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31,2412,2413}
+
+SELECT ARRAY(SELECT array(select i) FROM generate_series(1,5) a(i));
+ array
+-----------------------
+ {{1},{2},{3},{4},{5}}
(1 row)
</programlisting>
The subquery must return a single column. The resulting
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 41e973b..0261fcb 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -108,12 +108,16 @@ exprType(const Node *expr)
type = exprType((Node *) tent->expr);
if (sublink->subLinkType == ARRAY_SUBLINK)
{
- type = get_array_type(type);
- if (!OidIsValid(type))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("could not find array type for data type %s",
- format_type_be(exprType((Node *) tent->expr)))));
+ if (!OidIsValid(get_element_type(type)))
+ {
+ /* not array, so check for its array type */
+ type = get_array_type(type);
+ if (!OidIsValid(type))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(exprType((Node *) tent->expr)))));
+ }
}
}
else if (sublink->subLinkType == MULTIEXPR_SUBLINK)
@@ -139,12 +143,16 @@ exprType(const Node *expr)
type = subplan->firstColType;
if (subplan->subLinkType == ARRAY_SUBLINK)
{
- type = get_array_type(type);
- if (!OidIsValid(type))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("could not find array type for data type %s",
- format_type_be(subplan->firstColType))));
+ if (!OidIsValid(get_element_type(type)))
+ {
+ /* not array, so check for its array type */
+ type = get_array_type(type);
+ if (!OidIsValid(type))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(subplan->firstColType))));
+ }
}
}
else if (subplan->subLinkType == MULTIEXPR_SUBLINK)
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 3e7dc85..8fc8b49 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -668,10 +668,16 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
Assert(!te->resjunk);
Assert(testexpr == NULL);
- arraytype = get_array_type(exprType((Node *) te->expr));
- if (!OidIsValid(arraytype))
- elog(ERROR, "could not find array type for datatype %s",
- format_type_be(exprType((Node *) te->expr)));
+
+ arraytype = exprType((Node *) te->expr);
+ if (!OidIsValid(get_element_type(arraytype)))
+ {
+ /* not array, so get the array type */
+ arraytype = get_array_type(exprType((Node *) te->expr));
+ if (!OidIsValid(arraytype))
+ elog(ERROR, "could not find array type for datatype %s",
+ format_type_be(exprType((Node *) te->expr)));
+ }
prm = generate_new_param(root,
arraytype,
exprTypmod((Node *) te->expr),
diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c
index 831466d..b55f3f0 100644
--- a/src/backend/utils/adt/array_userfuncs.c
+++ b/src/backend/utils/adt/array_userfuncs.c
@@ -16,7 +16,6 @@
#include "utils/builtins.h"
#include "utils/lsyscache.h"
-
/*-----------------------------------------------------------------------------
* array_push :
* push an element onto either end of a one-dimensional array
@@ -513,8 +512,6 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
{
Datum result;
ArrayBuildState *state;
- int dims[1];
- int lbs[1];
/*
* Test for null before Asserting we are in right context. This is to
@@ -529,18 +526,28 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
state = (ArrayBuildState *) PG_GETARG_POINTER(0);
- dims[0] = state->nelems;
- lbs[0] = 1;
-
/*
* Make the result. We cannot release the ArrayBuildState because
* sometimes aggregate final functions are re-executed. Rather, it is
* nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
* so.
*/
- result = makeMdArrayResult(state, 1, dims, lbs,
- CurrentMemoryContext,
- false);
+ result = makeArrayResult1(state, CurrentMemoryContext, false);
PG_RETURN_DATUM(result);
}
+
+/*
+ * ARRAY_AGG(anyarray) aggregate function
+ */
+Datum
+array_agg_anyarray_transfn(PG_FUNCTION_ARGS)
+{
+ return array_agg_transfn(fcinfo);
+}
+
+Datum
+array_agg_anyarray_finalfn(PG_FUNCTION_ARGS)
+{
+ return array_agg_finalfn(fcinfo);
+}
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 6c8b41d..df2a2d6 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -145,6 +145,19 @@ static int width_bucket_array_variable(Datum operand,
Oid collation,
TypeCacheEntry *typentry);
+/* internal functions of accumArrayResult */
+static ArrayBuildStateScalar *initializeArrayBuildStateScalar(Oid element_type,
+ MemoryContext arr_context);
+static ArrayBuildStateArray *initializeArrayBuildStateArray(Oid subelement_type,
+ MemoryContext arr_context);
+static void appendScalarDatum(ArrayBuildStateScalar *astate_scalar,
+ Datum dvalue, bool disnull);
+static void appendArrayDatum(ArrayBuildStateArray *astate_array,
+ Datum dvalue, bool disnull);
+
+/* internal function of makeMdArrayResult */
+static ArrayType *makeMdArrayResultArray(ArrayBuildStateArray *astate,
+ int ndims, int *dims, int *lbs);
/*
* array_in :
@@ -4588,9 +4601,13 @@ accumArrayResult(ArrayBuildState *astate,
MemoryContext arr_context,
oldcontext;
+ ArrayBuildStateScalar *astate_scalar = NULL; /* for scalar datum accumulation */
+ ArrayBuildStateArray *astate_array = NULL; /* for array datum accumulation */
+
if (astate == NULL)
{
/* First time through --- initialize */
+ Oid subelement_type = get_element_type(element_type);
/* Make a temporary context to hold all the junk */
arr_context = AllocSetContextCreate(rcontext,
@@ -4599,33 +4616,127 @@ accumArrayResult(ArrayBuildState *astate,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
oldcontext = MemoryContextSwitchTo(arr_context);
- astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
- astate->mcontext = arr_context;
- astate->alen = 64; /* arbitrary starting array size */
- astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
- astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
- astate->nelems = 0;
- astate->element_type = element_type;
- get_typlenbyvalalign(element_type,
- &astate->typlen,
- &astate->typbyval,
- &astate->typalign);
+
+ if (!OidIsValid(subelement_type))
+ {
+ astate_scalar = initializeArrayBuildStateScalar(element_type, arr_context);
+ astate = (ArrayBuildState *) astate_scalar;
+
+ appendScalarDatum(astate_scalar, dvalue, disnull);
+ }
+ else
+ {
+ astate_array = initializeArrayBuildStateArray(subelement_type, arr_context);
+ astate = (ArrayBuildState *) astate_array;
+
+ appendArrayDatum(astate_array, dvalue, disnull);
+ }
}
else
{
oldcontext = MemoryContextSwitchTo(astate->mcontext);
- Assert(astate->element_type == element_type);
- /* enlarge dvalues[]/dnulls[] if needed */
- if (astate->nelems >= astate->alen)
+ if (!astate->is_array_accum)
{
- astate->alen *= 2;
- astate->dvalues = (Datum *)
- repalloc(astate->dvalues, astate->alen * sizeof(Datum));
- astate->dnulls = (bool *)
- repalloc(astate->dnulls, astate->alen * sizeof(bool));
+ Assert(astate->element_type == element_type);
+ astate_scalar = (ArrayBuildStateScalar *) astate;
+
+ /* enlarge dvalues[]/dnulls[] if needed */
+ if (astate_scalar->nelems >= astate_scalar->alen)
+ {
+ astate_scalar->alen *= 2;
+ astate_scalar->dvalues = (Datum *)
+ repalloc(astate_scalar->dvalues, astate_scalar->alen * sizeof(Datum));
+ astate_scalar->dnulls = (bool *)
+ repalloc(astate_scalar->dnulls, astate_scalar->alen * sizeof(bool));
+ }
+
+ appendScalarDatum(astate_scalar, dvalue, disnull);
+ }
+ else
+ {
+ Assert(astate->element_type == get_element_type(element_type));
+ astate_array = (ArrayBuildStateArray *) astate;
+ /*
+ * alloc & realloc placed in appendArrayDatum because
+ * we need to know the dvalue length first
+ */
+ appendArrayDatum(astate_array, dvalue, disnull);
}
}
+ MemoryContextSwitchTo(oldcontext);
+
+ return astate;
+}
+
+/*
+ * Initialize ArrayBuildState for scalar accumulation (internal function of
+ * accumArrayResult)
+ */
+ArrayBuildStateScalar *
+initializeArrayBuildStateScalar(Oid element_type,
+ MemoryContext arr_context)
+{
+ ArrayBuildStateScalar *astate_scalar;
+
+ /* scalar accumulate */
+ astate_scalar = (ArrayBuildStateScalar *) palloc(sizeof(ArrayBuildStateScalar));
+ astate_scalar->astate.is_array_accum = false;
+ astate_scalar->astate.mcontext = arr_context;
+
+ astate_scalar->alen = 64; /* arbitrary starting array size */
+ astate_scalar->dvalues = (Datum *) palloc(astate_scalar->alen * sizeof(Datum));
+ astate_scalar->dnulls = (bool *) palloc(astate_scalar->alen * sizeof(bool));
+ astate_scalar->nelems = 0;
+
+ astate_scalar->astate.element_type = element_type;
+ get_typlenbyvalalign(element_type,
+ &astate_scalar->astate.typlen,
+ &astate_scalar->astate.typbyval,
+ &astate_scalar->astate.typalign);
+
+ return astate_scalar;
+}
+
+/*
+ * Initialize ArrayBuildState for array accum (internal of accumArrayResult)
+ */
+ArrayBuildStateArray *
+initializeArrayBuildStateArray(Oid subelement_type,
+ MemoryContext arr_context)
+{
+ ArrayBuildStateArray *astate_array;
+
+ /* array accumulate */
+ astate_array = (ArrayBuildStateArray *) palloc(sizeof(ArrayBuildStateArray));
+ astate_array->astate.is_array_accum = true;
+ astate_array->astate.mcontext = arr_context;
+
+ astate_array->abytes = 0;
+ astate_array->aitems = 0;
+ astate_array->data = NULL;
+ astate_array->nbytes = 0;
+ astate_array->hasnull = false;
+ astate_array->nullbitmap = NULL;
+ astate_array->nitems = 0;
+ astate_array->narray = 0;
+
+ astate_array->astate.element_type = subelement_type;
+ get_typlenbyvalalign(subelement_type,
+ &astate_array->astate.typlen,
+ &astate_array->astate.typbyval,
+ &astate_array->astate.typalign);
+
+ return astate_array;
+}
+
+/*
+ * Append a datum for scalar accumulation (internal of accumArrayResult)
+ */
+void
+appendScalarDatum(ArrayBuildStateScalar *astate_scalar,
+ Datum dvalue, bool disnull)
+{
/*
* Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
* it's varlena. (You might think that detoasting is not needed here
@@ -4634,25 +4745,129 @@ accumArrayResult(ArrayBuildState *astate,
* because that would mean array_agg_finalfn damages its input, which is
* verboten. Also, this way frequently saves one copying step.)
*/
- if (!disnull && !astate->typbyval)
+ if (!disnull && !astate_scalar->astate.typbyval)
{
- if (astate->typlen == -1)
+ if (astate_scalar->astate.typlen == -1)
dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
else
- dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
+ dvalue = datumCopy(dvalue, astate_scalar->astate.typbyval,
+ astate_scalar->astate.typlen);
}
- astate->dvalues[astate->nelems] = dvalue;
- astate->dnulls[astate->nelems] = disnull;
- astate->nelems++;
+ astate_scalar->dvalues[astate_scalar->nelems] = dvalue;
+ astate_scalar->dnulls[astate_scalar->nelems] = disnull;
+ astate_scalar->nelems++;
+}
- MemoryContextSwitchTo(oldcontext);
+/*
+ * Append a datum for array accum (internal of accumArrayResult)
+ */
+void
+appendArrayDatum(ArrayBuildStateArray *astate_array,
+ Datum dvalue, bool disnull)
+{
+ ArrayType *arg;
+ int *dims,
+ *lbs,
+ ndims,
+ nitems,
+ ndatabytes;
+ char *data;
+ int i;
- return astate;
+ if (disnull)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot accumulate null arrays")));
+
+ arg = DatumGetArrayTypeP(dvalue);
+
+ ndims = ARR_NDIM(arg);
+ dims = ARR_DIMS(arg);
+ lbs = ARR_LBOUND(arg);
+ data = ARR_DATA_PTR(arg);
+ nitems = ArrayGetNItems(ndims, dims);
+ ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
+
+ if (astate_array->data == NULL)
+ {
+ /* first allocation */
+ if (ndims == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot accumulate empty arrays")));
+
+ if (ndims + 1 > MAXDIM)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ ndims + 1, MAXDIM)));
+
+ astate_array->ndims = ndims;
+ astate_array->dims = (int *) palloc(ndims * sizeof(int));
+ astate_array->lbs = (int *) palloc(ndims * sizeof(int));
+ memcpy(astate_array->dims, dims, ndims * sizeof(int));
+ memcpy(astate_array->lbs, lbs, ndims * sizeof(int));
+
+ astate_array->abytes = ndatabytes >= 512 ? 4 * ndatabytes : 1024;
+ astate_array->data = (char *) palloc(astate_array->abytes);
+ astate_array->aitems = 4 * nitems;
+ }
+ else
+ {
+ if (astate_array->ndims != ndims)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot accumulate incompatible arrays"),
+ errdetail("Arrays of %d and %d dimensions are not "
+ "compatible for concatenation.",
+ astate_array->ndims, ndims)));
+
+ for (i = 0; i < ndims; i++)
+ if (astate_array->dims[i] != dims[i] || astate_array->lbs[i] != lbs[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot accumulate incompatible arrays"),
+ errdetail("Arrays with differing element dimensions are "
+ "not compatible for concatenation.")));
+
+ if (astate_array->nbytes + ndatabytes >= astate_array->abytes)
+ {
+ astate_array->abytes *= 2;
+ astate_array->data = (char *)
+ repalloc(astate_array->data, astate_array->abytes);
+ }
+ if (astate_array->nitems + nitems >= astate_array->aitems)
+ {
+ astate_array->aitems *= 2;
+ astate_array->nullbitmap = (bits8 *)
+ repalloc(astate_array->nullbitmap, (astate_array->aitems + 7) / 8);
+ }
+ }
+ memcpy(astate_array->data + astate_array->nbytes, data, ndatabytes);
+ astate_array->nbytes += ndatabytes;
+
+ if (astate_array->hasnull || ARR_HASNULL(arg))
+ {
+ if (!astate_array->hasnull)
+ {
+ astate_array->hasnull = true;
+ astate_array->nullbitmap = (bits8 *) palloc((astate_array->aitems + 7) / 8);
+ array_bitmap_copy(astate_array->nullbitmap, 0,
+ NULL, 0,
+ astate_array->nitems);
+ }
+ array_bitmap_copy(astate_array->nullbitmap, astate_array->nitems,
+ ARR_NULLBITMAP(arg), 0,
+ nitems);
+ astate_array->nitems += nitems;
+ }
+ astate_array->narray += 1;
}
/*
- * makeArrayResult - produce 1-D final result of accumArrayResult
+ * makeArrayResult - produce 1-D final result of scalar accumArrayResult
+ * - produce N+1-D final result of array accumArrayResult
*
* astate is working state (not NULL)
* rcontext is where to construct result
@@ -4661,17 +4876,36 @@ Datum
makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext)
{
- int dims[1];
- int lbs[1];
+ return makeArrayResult1(astate, rcontext, true);
+}
- dims[0] = astate->nelems;
- lbs[0] = 1;
+/*
+ * makeArrayResult1 - same as makeArrayResult, with option not to
+ * release working state
+ *
+ * release is true if okay to release working state
+ */
+Datum
+makeArrayResult1(ArrayBuildState *astate,
+ MemoryContext rcontext,
+ bool release)
+{
+ if (!astate->is_array_accum)
+ {
+ int dims[1];
+ int lbs[1];
+
+ dims[0] = ((ArrayBuildStateScalar *) astate)->nelems;
+ lbs[0] = 1;
- return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true);
+ return makeMdArrayResult(astate, 1, dims, lbs, rcontext, release);
+ }
+ else
+ return makeMdArrayResult(astate, -1, NULL, NULL, rcontext, release);
}
/*
- * makeMdArrayResult - produce multi-D final result of accumArrayResult
+ * makeMdArrayResult - produce multi-D final result of scalar accumArrayResult
*
* beware: no check that specified dimensions match the number of values
* accumulated.
@@ -4679,6 +4913,11 @@ makeArrayResult(ArrayBuildState *astate,
* astate is working state (not NULL)
* rcontext is where to construct result
* release is true if okay to release working state
+ *
+ * for array accumulation:
+ * ndims == -1: default (calculated from astate)
+ * dims == NULL: default dims (calculated from astate)
+ * lbs == NULL: default (calculated from astate)
*/
Datum
makeMdArrayResult(ArrayBuildState *astate,
@@ -4694,8 +4933,13 @@ makeMdArrayResult(ArrayBuildState *astate,
/* Build the final array result in rcontext */
oldcontext = MemoryContextSwitchTo(rcontext);
- result = construct_md_array(astate->dvalues,
- astate->dnulls,
+ if (!astate->is_array_accum)
+ {
+ ArrayBuildStateScalar *astate_scalar;
+ astate_scalar = (ArrayBuildStateScalar *) astate;
+
+ result = construct_md_array(astate_scalar->dvalues,
+ astate_scalar->dnulls,
ndims,
dims,
lbs,
@@ -4703,6 +4947,16 @@ makeMdArrayResult(ArrayBuildState *astate,
astate->typlen,
astate->typbyval,
astate->typalign);
+ }
+ else
+ {
+ ArrayBuildStateArray *astate_array;
+ astate_array = (ArrayBuildStateArray *) astate;
+ result = makeMdArrayResultArray(astate_array,
+ ndims,
+ dims,
+ lbs);
+ }
MemoryContextSwitchTo(oldcontext);
@@ -4713,6 +4967,75 @@ makeMdArrayResult(ArrayBuildState *astate,
return PointerGetDatum(result);
}
+/*
+ * makeMdArrayResultArray - internal of makeArrayResult & makeMdArrayResult
+ *
+ * beware: no check that specified dimensions match the values
+ * accumulated.
+ *
+ * astate is working state (not NULL)
+ * ndims > -1 if wants to override default (calculated from astate)
+ * dims not NULL if wants to override default
+ * lbs not NULL if wants to override default
+ */
+ArrayType *
+makeMdArrayResultArray(ArrayBuildStateArray *astate,
+ int ndims,
+ int *dims,
+ int *lbs)
+{
+ ArrayType *result;
+
+ int dataoffset,
+ nbytes;
+
+ if (ndims == -1)
+ ndims = astate->ndims + 1;
+
+ nbytes = astate->nbytes;
+ /* compute required space */
+ if (astate->hasnull)
+ {
+ dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, astate->nitems);
+ nbytes += dataoffset;
+ }
+ else
+ {
+ dataoffset = 0;
+ nbytes += ARR_OVERHEAD_NONULLS(ndims);
+ }
+
+ result = (ArrayType *) palloc0(nbytes);
+ SET_VARSIZE(result, nbytes);
+ result->ndim = astate->ndims + 1;
+ result->dataoffset = dataoffset;
+ result->elemtype = astate->astate.element_type;
+
+ if (dims != NULL)
+ memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
+ else
+ {
+ ARR_DIMS(result)[0] = astate->narray;
+ memcpy(&(ARR_DIMS(result)[1]), astate->dims, (ndims - 1) * sizeof(int));
+ }
+ if (lbs != NULL)
+ memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
+ else
+ {
+ ARR_LBOUND(result)[0] = 1;
+ memcpy(&(ARR_LBOUND(result)[1]), astate->lbs, (ndims - 1) * sizeof(int));
+ }
+
+ memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
+
+ if (astate->hasnull)
+ array_bitmap_copy(ARR_NULLBITMAP(result), 0,
+ astate->nullbitmap, 0,
+ astate->nitems);
+
+ return result;
+}
+
Datum
array_larger(PG_FUNCTION_ARGS)
{
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 3ba9e5e..2005199 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -275,6 +275,7 @@ DATA(insert ( 2901 n 0 xmlconcat2 - - - - f f 0 142 0 0 0 _null_
/* array */
DATA(insert ( 2335 n 0 array_agg_transfn array_agg_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
+DATA(insert ( 6005 n 0 array_agg_anyarray_transfn array_agg_anyarray_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
/* text */
DATA(insert ( 3538 n 0 string_agg_transfn string_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index b6dc1b8..9273c1f 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -879,11 +879,17 @@ DATA(insert OID = 3167 ( array_remove PGNSP PGUID 12 1 0 0 0 f f f f f f i 2
DESCR("remove any occurrences of an element from an array");
DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ ));
DESCR("replace any occurrences of an element in an array");
-DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2283" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
+DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2776" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function");
-DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2283" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
+DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2776" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
DESCR("aggregate final function");
-DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2283" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2776" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+DESCR("concatenate aggregate input into an array");
+DATA(insert OID = 6003 ( array_agg_anyarray_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_transfn _null_ _null_ _null_ ));
+DESCR("aggregate transition function");
+DATA(insert OID = 6004 ( array_agg_anyarray_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_finalfn _null_ _null_ _null_ ));
+DESCR("aggregate final function");
+DATA(insert OID = 6005 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2277" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into an array");
DATA(insert OID = 3218 ( width_bucket PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "2283 2277" _null_ _null_ _null_ _null_ width_bucket_array _null_ _null_ _null_ ));
DESCR("bucket number of operand given a sorted array of bucket lower bounds");
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index e744314..daaae88 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -76,14 +76,15 @@ typedef struct
/*
* working state for accumArrayResult() and friends
+ *
+ * is_array_accum: whether accumulating array values.
+ * (if true must be casted to ArrayBuildStateArray, else
+ * cast to ArrayBuildStateScalar)
*/
typedef struct ArrayBuildState
{
+ bool is_array_accum;
MemoryContext mcontext; /* where all the temp stuff is kept */
- Datum *dvalues; /* array of accumulated Datums */
- bool *dnulls; /* array of is-null flags for Datums */
- int alen; /* allocated length of above arrays */
- int nelems; /* number of valid entries in above arrays */
Oid element_type; /* data type of the Datums */
int16 typlen; /* needed info about datatype */
bool typbyval;
@@ -91,6 +92,43 @@ typedef struct ArrayBuildState
} ArrayBuildState;
/*
+ * array build state for array accumulation of scalar datums
+ */
+typedef struct ArrayBuildStateScalar
+{
+ ArrayBuildState astate;
+
+ Datum *dvalues; /* array of accumulated Datums */
+ bool *dnulls; /* array of is-null flags for Datums */
+ int alen; /* allocated length of above arrays */
+ int nelems; /* number of valid entries in above arrays */
+} ArrayBuildStateScalar;
+
+
+/*
+ * array build state for array accumulation of array datums
+ */
+typedef struct
+{
+ ArrayBuildState astate;
+
+ char *data; /* array of accumulated data */
+ bits8 *nullbitmap; /* bitmap of is-null flags for data */
+
+ int abytes; /* allocated length of above arrays */
+ int aitems; /* allocated length of above arrays */
+ int nbytes; /* number of used bytes in above arrays */
+ int nitems; /* number of elements in above arrays */
+ int narray; /* number of array accumulated */
+
+ int ndims; /* element dimensions */
+ int *dims;
+ int *lbs;
+
+ bool hasnull; /* any element has null */
+} ArrayBuildStateArray;
+
+/*
* structure to cache type metadata needed for array manipulation
*/
typedef struct ArrayMetaState
@@ -258,9 +296,10 @@ extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
MemoryContext rcontext);
extern Datum makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext);
+extern Datum makeArrayResult1(ArrayBuildState *astate,
+ MemoryContext rcontext, bool release);
extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
int *dims, int *lbs, MemoryContext rcontext, bool release);
-
extern ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim);
extern bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull);
extern void array_free_iterator(ArrayIterator iterator);
@@ -293,6 +332,9 @@ extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
extern Datum array_agg_transfn(PG_FUNCTION_ARGS);
extern Datum array_agg_finalfn(PG_FUNCTION_ARGS);
+extern Datum array_agg_anyarray_transfn(PG_FUNCTION_ARGS);
+extern Datum array_agg_anyarray_finalfn(PG_FUNCTION_ARGS);
+
/*
* prototypes for functions defined in array_typanalyze.c
*/
diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out
index 58df854..607aeea 100644
--- a/src/test/regress/expected/aggregates.out
+++ b/src/test/regress/expected/aggregates.out
@@ -914,6 +914,76 @@ select array_agg(distinct a order by a desc nulls last)
{3,2,1,NULL}
(1 row)
+-- array_agg(anyarray)
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar);
+ array_agg
+---------------
+ {{1,2},{3,4}}
+(1 row)
+
+select array_agg(distinct ar order by ar desc)
+ from (select array[i / 2] from generate_series(1,10) a(i)) b(ar);
+ array_agg
+---------------------------
+ {{5},{4},{3},{2},{1},{0}}
+(1 row)
+
+select array_agg(ar)
+ from (select array_agg(array[i, i+1, i-1])
+ from generate_series(1,2) a(i)) b(ar);
+ array_agg
+---------------------
+ {{{1,2,0},{2,3,1}}}
+(1 row)
+
+-- array_agg(anyarray), varlena types
+select array_agg(array[1.2,1.3,1.4]) from generate_series(1,3);
+ array_agg
+---------------------------------------------
+ {{1.2,1.3,1.4},{1.2,1.3,1.4},{1.2,1.3,1.4}}
+(1 row)
+
+select array_agg(array['Hello','Hohoho','Hi']) from generate_series(1,3);
+ array_agg
+---------------------------------------------------------
+ {{Hello,Hohoho,Hi},{Hello,Hohoho,Hi},{Hello,Hohoho,Hi}}
+(1 row)
+
+-- array_agg(anyarray), arrays with nulls
+select array_agg(array[i, null, i+1, null, i+2]) from generate_series(1,3) g(i);
+ array_agg
+---------------------------------------------------------
+ {{1,NULL,2,NULL,3},{2,NULL,3,NULL,4},{3,NULL,4,NULL,5}}
+(1 row)
+
+select array_agg(array[1.1+ i, null, 1.1+i+1, null, 1.1+i+2]) from generate_series(1,3) g(i);
+ array_agg
+---------------------------------------------------------------------------
+ {{2.1,NULL,3.1,NULL,4.1},{3.1,NULL,4.1,NULL,5.1},{4.1,NULL,5.1,NULL,6.1}}
+(1 row)
+
+select array_agg(array[null, 'Hello','Hohoho', null,'Hi']) from generate_series(1,3);
+ array_agg
+---------------------------------------------------------------------------------------
+ {{NULL,Hello,Hohoho,NULL,Hi},{NULL,Hello,Hohoho,NULL,Hi},{NULL,Hello,Hohoho,NULL,Hi}}
+(1 row)
+
+select array_agg(array[[null, 'Hello', null, 'Hi'],['Hello', null, 'Hi', null]]) from generate_series(1,2);
+ array_agg
+-------------------------------------------------------------------------------------------
+ {{{NULL,Hello,NULL,Hi},{Hello,NULL,Hi,NULL}},{{NULL,Hello,NULL,Hi},{Hello,NULL,Hi,NULL}}}
+(1 row)
+
+-- errors
+select array_agg('{}'::int[]) from generate_series(1,2);
+ERROR: cannot accumulate empty arrays
+select array_agg(null::int[]) from generate_series(1,2);
+ERROR: cannot accumulate null arrays
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3}'::int[])) v(ar);
+ERROR: cannot accumulate incompatible arrays
+DETAIL: Arrays with differing element dimensions are not compatible for concatenation.
-- multi-arg aggs, strict/nonstrict, distinct/order by
select aggfstr(a,b,c)
from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c);
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index 46eff67..e80ebec 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -1521,6 +1521,23 @@ select array_agg(unique1) from tenk1 where unique1 < -15;
(1 row)
+select array(select unique1 from tenk1 where unique1 < 15 order by unique1);
+ array
+--------------------------------------
+ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14}
+(1 row)
+
+select array(select array[i,i/2] from generate_series(1,5) a(i));
+ array
+---------------------------------
+ {{1,0},{2,1},{3,1},{4,2},{5,2}}
+(1 row)
+
+-- cannot accumulate null arrays and empty arrays
+select array(select null::int[]);
+ERROR: cannot accumulate null arrays
+select array(select '{}'::int[]);
+ERROR: cannot accumulate empty arrays
select unnest(array[1,2,3]);
unnest
--------
diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql
index 8096a6f..a70419f 100644
--- a/src/test/regress/sql/aggregates.sql
+++ b/src/test/regress/sql/aggregates.sql
@@ -322,6 +322,32 @@ select array_agg(distinct a order by a desc)
select array_agg(distinct a order by a desc nulls last)
from (values (1),(2),(1),(3),(null),(2)) v(a);
+-- array_agg(anyarray)
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar);
+select array_agg(distinct ar order by ar desc)
+ from (select array[i / 2] from generate_series(1,10) a(i)) b(ar);
+select array_agg(ar)
+ from (select array_agg(array[i, i+1, i-1])
+ from generate_series(1,2) a(i)) b(ar);
+
+-- array_agg(anyarray), varlena types
+select array_agg(array[1.2,1.3,1.4]) from generate_series(1,3);
+select array_agg(array['Hello','Hohoho','Hi']) from generate_series(1,3);
+
+-- array_agg(anyarray), arrays with nulls
+select array_agg(array[i, null, i+1, null, i+2]) from generate_series(1,3) g(i);
+select array_agg(array[1.1+ i, null, 1.1+i+1, null, 1.1+i+2]) from generate_series(1,3) g(i);
+select array_agg(array[null, 'Hello','Hohoho', null,'Hi']) from generate_series(1,3);
+
+select array_agg(array[[null, 'Hello', null, 'Hi'],['Hello', null, 'Hi', null]]) from generate_series(1,2);
+
+-- errors
+select array_agg('{}'::int[]) from generate_series(1,2);
+select array_agg(null::int[]) from generate_series(1,2);
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3}'::int[])) v(ar);
+
-- multi-arg aggs, strict/nonstrict, distinct/order by
select aggfstr(a,b,c)
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index fa8a20a..cb00f5f 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -432,6 +432,12 @@ select array_agg(ten) from (select ten from tenk1 where unique1 < 15 order by un
select array_agg(nullif(ten, 4)) from (select ten from tenk1 where unique1 < 15 order by unique1) ss;
select array_agg(unique1) from tenk1 where unique1 < -15;
+select array(select unique1 from tenk1 where unique1 < 15 order by unique1);
+select array(select array[i,i/2] from generate_series(1,5) a(i));
+-- cannot accumulate null arrays and empty arrays
+select array(select null::int[]);
+select array(select '{}'::int[]);
+
select unnest(array[1,2,3]);
select * from unnest(array[1,2,3]);
select unnest(array[1,2,3,4.5]::float8[]);
2014-10-25 8:19 GMT+02:00 Ali Akbar <the.apaan@gmail.com>:
2014-10-25 10:29 GMT+07:00 Ali Akbar <the.apaan@gmail.com>:
I fixed small issue in regress tests and I enhanced tests for varlena
types and null values.
Thanks.
it is about 15% faster than original implementation.
15% faster than array_agg(scalar)? I haven't verify the performance, but
because the internal array data and null bitmap is copied as-is, that will
be faster.2014-10-25 1:51 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
Hi Ali
I checked a code. I am thinking so code organization is not good.
accumArrayResult is too long now. makeMdArrayResult will not work, when
arrays was joined (it is not consistent now). I don't like a usage of
state->is_array_accum in array_userfunc.c -- it is signal of wrong
wrapping.Yes, i was thinking the same. Attached WIP patch to reorganizate the
code. makeMdArrayResult works now, with supplied arguments act as override
from default values calculated from ArrayBuildStateArray.In array_userfunc.c, because we don't want to override ndims, dims and
lbs, i copied array_agg_finalfn and only change the call to
makeMdArrayResult (we don't uses makeArrayResult because we want to set
release to false). Another alternative is to create new
makeArrayResult-like function that has parameter bool release.adding makeArrayResult1 (do you have better name for this?), that accepts
bool release parameter. array_agg_finalfn becomes more clean, and no
duplicate code in array_agg_anyarray_finalfn.
makeArrayResult1 - I have no better name now
I found one next minor detail.
you reuse a array_agg_transfn function. Inside is a message
"array_agg_transfn called in non-aggregate context". It is not correct for
array_agg_anyarray_transfn
probably specification dim and lbs in array_agg_finalfn is useless, when
you expect a natural result. A automatic similar to
array_agg_anyarray_finalfn should work there too.
makeMdArrayResultArray and appendArrayDatum should be marked as "static"
Show quoted text
next question: there is function array_append(anyarray, anyelement).
Isn't time to define array_append(anyarray, anyarray) now?
There is array_cat(anyarray, anyarray):
/*-----------------------------------------------------------------------------
* array_cat :
* concatenate two nD arrays to form an nD array, or
* push an (n-1)D array onto the end of an nD array*----------------------------------------------------------------------------
*/Regards,
--
Ali Akbar
2014-10-25 8:33 GMT+02:00 Pavel Stehule <pavel.stehule@gmail.com>:
2014-10-25 8:19 GMT+02:00 Ali Akbar <the.apaan@gmail.com>:
2014-10-25 10:29 GMT+07:00 Ali Akbar <the.apaan@gmail.com>:
I fixed small issue in regress tests and I enhanced tests for varlena
types and null values.
Thanks.
it is about 15% faster than original implementation.
15% faster than array_agg(scalar)? I haven't verify the performance, but
because the internal array data and null bitmap is copied as-is, that will
be faster.2014-10-25 1:51 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
Hi Ali
I checked a code. I am thinking so code organization is not good.
accumArrayResult is too long now. makeMdArrayResult will not work, when
arrays was joined (it is not consistent now). I don't like a usage of
state->is_array_accum in array_userfunc.c -- it is signal of wrong
wrapping.Yes, i was thinking the same. Attached WIP patch to reorganizate the
code. makeMdArrayResult works now, with supplied arguments act as override
from default values calculated from ArrayBuildStateArray.In array_userfunc.c, because we don't want to override ndims, dims and
lbs, i copied array_agg_finalfn and only change the call to
makeMdArrayResult (we don't uses makeArrayResult because we want to set
release to false). Another alternative is to create new
makeArrayResult-like function that has parameter bool release.adding makeArrayResult1 (do you have better name for this?), that accepts
bool release parameter. array_agg_finalfn becomes more clean, and no
duplicate code in array_agg_anyarray_finalfn.makeArrayResult1 - I have no better name now
I found one next minor detail.
you reuse a array_agg_transfn function. Inside is a message
"array_agg_transfn called in non-aggregate context". It is not correct for
array_agg_anyarray_transfnprobably specification dim and lbs in array_agg_finalfn is useless, when
you expect a natural result. A automatic similar to
array_agg_anyarray_finalfn should work there too.makeMdArrayResultArray and appendArrayDatum should be marked as "static"
I am thinking so change of makeArrayResult is wrong. It is developed for 1D
array. When we expect somewhere ND result now, then we should to use
makeMdArrayResult there. So makeArrayResult should to return 1D array in
all cases. Else a difference between makeArrayResult and makeMdArrayResult
hasn't big change and we have a problem with naming.
Regards
Pavel
Show quoted text
next question: there is function array_append(anyarray, anyelement).
Isn't time to define array_append(anyarray, anyarray) now?
There is array_cat(anyarray, anyarray):
/*-----------------------------------------------------------------------------
* array_cat :
* concatenate two nD arrays to form an nD array, or
* push an (n-1)D array onto the end of an nD array*----------------------------------------------------------------------------
*/Regards,
--
Ali Akbar
makeArrayResult1 - I have no better name now
I found one next minor detail.
you reuse a array_agg_transfn function. Inside is a message
"array_agg_transfn called in non-aggregate context". It is not correct for
array_agg_anyarray_transfn
Fixed.
probably specification dim and lbs in array_agg_finalfn is useless, when
you expect a natural result. A automatic similar to
array_agg_anyarray_finalfn should work there too.makeMdArrayResultArray and appendArrayDatum should be marked as "static"
ok, marked all of that as static.
I am thinking so change of makeArrayResult is wrong. It is developed for
1D array. When we expect somewhere ND result now, then we should to use
makeMdArrayResult there. So makeArrayResult should to return 1D array in
all cases. Else a difference between makeArrayResult and makeMdArrayResult
hasn't big change and we have a problem with naming.
I'm thinking it like this:
- if we want to accumulate array normally, use accumArrayResult and
makeArrayResult. If we accumulate scalar the result will be 1D array. if we
accumulate array, the resulting dimension is incremented by 1.
- if we want, somehow to affect the normal behavior, and change the
dimensions, use makeMdArrayResult.
Searching through the postgres' code, other than internal use in
arrayfuncs.c, makeMdArrayResult is used only
in src/pl/plperl/plperl.c:plperl_array_to_datum, while converting perl
array to postgres array.
So if somehow we will accumulate array other than in array_agg, i think the
most natural way is using accumArrayResult and then makeArrayResult.
CMIIW
Regards,
--
Ali Akbar
Attachments:
array_agg_anyarray-10.patchtext/x-patch; charset=US-ASCII; name=array_agg_anyarray-10.patchDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 7e5bcd9..f59738a 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -12046,6 +12046,22 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<indexterm>
+ <primary>array_agg</primary>
+ </indexterm>
+ <function>array_agg(<replaceable class="parameter">anyarray</replaceable>)</function>
+ </entry>
+ <entry>
+ any
+ </entry>
+ <entry>
+ the same array type as input type
+ </entry>
+ <entry>input arrays, aggregated into higher-order multidimesional array. Rejects NULL and empty array as input.</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>average</primary>
</indexterm>
<indexterm>
diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml
index 2f0680f..8c182a4 100644
--- a/doc/src/sgml/syntax.sgml
+++ b/doc/src/sgml/syntax.sgml
@@ -2238,6 +2238,11 @@ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
array
-----------------------------------------------------------------------
{2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31,2412,2413}
+
+SELECT ARRAY(SELECT array(select i) FROM generate_series(1,5) a(i));
+ array
+-----------------------
+ {{1},{2},{3},{4},{5}}
(1 row)
</programlisting>
The subquery must return a single column. The resulting
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 41e973b..0261fcb 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -108,12 +108,16 @@ exprType(const Node *expr)
type = exprType((Node *) tent->expr);
if (sublink->subLinkType == ARRAY_SUBLINK)
{
- type = get_array_type(type);
- if (!OidIsValid(type))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("could not find array type for data type %s",
- format_type_be(exprType((Node *) tent->expr)))));
+ if (!OidIsValid(get_element_type(type)))
+ {
+ /* not array, so check for its array type */
+ type = get_array_type(type);
+ if (!OidIsValid(type))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(exprType((Node *) tent->expr)))));
+ }
}
}
else if (sublink->subLinkType == MULTIEXPR_SUBLINK)
@@ -139,12 +143,16 @@ exprType(const Node *expr)
type = subplan->firstColType;
if (subplan->subLinkType == ARRAY_SUBLINK)
{
- type = get_array_type(type);
- if (!OidIsValid(type))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("could not find array type for data type %s",
- format_type_be(subplan->firstColType))));
+ if (!OidIsValid(get_element_type(type)))
+ {
+ /* not array, so check for its array type */
+ type = get_array_type(type);
+ if (!OidIsValid(type))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(subplan->firstColType))));
+ }
}
}
else if (subplan->subLinkType == MULTIEXPR_SUBLINK)
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 3e7dc85..8fc8b49 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -668,10 +668,16 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
Assert(!te->resjunk);
Assert(testexpr == NULL);
- arraytype = get_array_type(exprType((Node *) te->expr));
- if (!OidIsValid(arraytype))
- elog(ERROR, "could not find array type for datatype %s",
- format_type_be(exprType((Node *) te->expr)));
+
+ arraytype = exprType((Node *) te->expr);
+ if (!OidIsValid(get_element_type(arraytype)))
+ {
+ /* not array, so get the array type */
+ arraytype = get_array_type(exprType((Node *) te->expr));
+ if (!OidIsValid(arraytype))
+ elog(ERROR, "could not find array type for datatype %s",
+ format_type_be(exprType((Node *) te->expr)));
+ }
prm = generate_new_param(root,
arraytype,
exprTypmod((Node *) te->expr),
diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c
index 831466d..7fbf39d 100644
--- a/src/backend/utils/adt/array_userfuncs.c
+++ b/src/backend/utils/adt/array_userfuncs.c
@@ -16,6 +16,8 @@
#include "utils/builtins.h"
#include "utils/lsyscache.h"
+static Datum array_agg_transfn_internal(PG_FUNCTION_ARGS,
+ const char* fname);
/*-----------------------------------------------------------------------------
* array_push :
@@ -476,6 +478,19 @@ create_singleton_array(FunctionCallInfo fcinfo,
Datum
array_agg_transfn(PG_FUNCTION_ARGS)
{
+ return array_agg_transfn_internal(fcinfo, "array_agg_transfn");
+}
+
+/*
+ * internal function of ARRAY_AGG
+ * called by array_agg_transfn and array_agg_transfn_array
+ *
+ * fname - function name (for error reporting)
+ */
+static Datum
+array_agg_transfn_internal(PG_FUNCTION_ARGS,
+ const char* fname)
+{
Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
MemoryContext aggcontext;
ArrayBuildState *state;
@@ -489,7 +504,7 @@ array_agg_transfn(PG_FUNCTION_ARGS)
if (!AggCheckCallContext(fcinfo, &aggcontext))
{
/* cannot be called directly because of internal-type argument */
- elog(ERROR, "array_agg_transfn called in non-aggregate context");
+ elog(ERROR, "%s called in non-aggregate context", fname);
}
state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
@@ -513,8 +528,6 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
{
Datum result;
ArrayBuildState *state;
- int dims[1];
- int lbs[1];
/*
* Test for null before Asserting we are in right context. This is to
@@ -529,18 +542,28 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
state = (ArrayBuildState *) PG_GETARG_POINTER(0);
- dims[0] = state->nelems;
- lbs[0] = 1;
-
/*
* Make the result. We cannot release the ArrayBuildState because
* sometimes aggregate final functions are re-executed. Rather, it is
* nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
* so.
*/
- result = makeMdArrayResult(state, 1, dims, lbs,
- CurrentMemoryContext,
- false);
+ result = makeArrayResult1(state, CurrentMemoryContext, false);
PG_RETURN_DATUM(result);
}
+
+/*
+ * ARRAY_AGG(anyarray) aggregate function
+ */
+Datum
+array_agg_anyarray_transfn(PG_FUNCTION_ARGS)
+{
+ return array_agg_transfn_internal(fcinfo, "array_agg_anyarray_transfn");
+}
+
+Datum
+array_agg_anyarray_finalfn(PG_FUNCTION_ARGS)
+{
+ return array_agg_finalfn(fcinfo);
+}
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 6c8b41d..24de430 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -145,6 +145,19 @@ static int width_bucket_array_variable(Datum operand,
Oid collation,
TypeCacheEntry *typentry);
+/* internal functions of accumArrayResult */
+static ArrayBuildStateScalar *initializeArrayBuildStateScalar(Oid element_type,
+ MemoryContext arr_context);
+static ArrayBuildStateArray *initializeArrayBuildStateArray(Oid subelement_type,
+ MemoryContext arr_context);
+static void appendScalarDatum(ArrayBuildStateScalar *astate_scalar,
+ Datum dvalue, bool disnull);
+static void appendArrayDatum(ArrayBuildStateArray *astate_array,
+ Datum dvalue, bool disnull);
+
+/* internal function of makeMdArrayResult */
+static ArrayType *makeMdArrayResultArray(ArrayBuildStateArray *astate,
+ int ndims, int *dims, int *lbs);
/*
* array_in :
@@ -4588,9 +4601,13 @@ accumArrayResult(ArrayBuildState *astate,
MemoryContext arr_context,
oldcontext;
+ ArrayBuildStateScalar *astate_scalar = NULL; /* for scalar datum accumulation */
+ ArrayBuildStateArray *astate_array = NULL; /* for array datum accumulation */
+
if (astate == NULL)
{
/* First time through --- initialize */
+ Oid subelement_type = get_element_type(element_type);
/* Make a temporary context to hold all the junk */
arr_context = AllocSetContextCreate(rcontext,
@@ -4599,33 +4616,127 @@ accumArrayResult(ArrayBuildState *astate,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
oldcontext = MemoryContextSwitchTo(arr_context);
- astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
- astate->mcontext = arr_context;
- astate->alen = 64; /* arbitrary starting array size */
- astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
- astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
- astate->nelems = 0;
- astate->element_type = element_type;
- get_typlenbyvalalign(element_type,
- &astate->typlen,
- &astate->typbyval,
- &astate->typalign);
+
+ if (!OidIsValid(subelement_type))
+ {
+ astate_scalar = initializeArrayBuildStateScalar(element_type, arr_context);
+ astate = (ArrayBuildState *) astate_scalar;
+
+ appendScalarDatum(astate_scalar, dvalue, disnull);
+ }
+ else
+ {
+ astate_array = initializeArrayBuildStateArray(subelement_type, arr_context);
+ astate = (ArrayBuildState *) astate_array;
+
+ appendArrayDatum(astate_array, dvalue, disnull);
+ }
}
else
{
oldcontext = MemoryContextSwitchTo(astate->mcontext);
- Assert(astate->element_type == element_type);
- /* enlarge dvalues[]/dnulls[] if needed */
- if (astate->nelems >= astate->alen)
+ if (!astate->is_array_accum)
{
- astate->alen *= 2;
- astate->dvalues = (Datum *)
- repalloc(astate->dvalues, astate->alen * sizeof(Datum));
- astate->dnulls = (bool *)
- repalloc(astate->dnulls, astate->alen * sizeof(bool));
+ Assert(astate->element_type == element_type);
+ astate_scalar = (ArrayBuildStateScalar *) astate;
+
+ /* enlarge dvalues[]/dnulls[] if needed */
+ if (astate_scalar->nelems >= astate_scalar->alen)
+ {
+ astate_scalar->alen *= 2;
+ astate_scalar->dvalues = (Datum *)
+ repalloc(astate_scalar->dvalues, astate_scalar->alen * sizeof(Datum));
+ astate_scalar->dnulls = (bool *)
+ repalloc(astate_scalar->dnulls, astate_scalar->alen * sizeof(bool));
+ }
+
+ appendScalarDatum(astate_scalar, dvalue, disnull);
+ }
+ else
+ {
+ Assert(astate->element_type == get_element_type(element_type));
+ astate_array = (ArrayBuildStateArray *) astate;
+ /*
+ * alloc & realloc placed in appendArrayDatum because
+ * we need to know the dvalue length first
+ */
+ appendArrayDatum(astate_array, dvalue, disnull);
}
}
+ MemoryContextSwitchTo(oldcontext);
+
+ return astate;
+}
+
+/*
+ * Initialize ArrayBuildState for scalar accumulation (internal function of
+ * accumArrayResult)
+ */
+static ArrayBuildStateScalar *
+initializeArrayBuildStateScalar(Oid element_type,
+ MemoryContext arr_context)
+{
+ ArrayBuildStateScalar *astate_scalar;
+
+ /* scalar accumulate */
+ astate_scalar = (ArrayBuildStateScalar *) palloc(sizeof(ArrayBuildStateScalar));
+ astate_scalar->astate.is_array_accum = false;
+ astate_scalar->astate.mcontext = arr_context;
+
+ astate_scalar->alen = 64; /* arbitrary starting array size */
+ astate_scalar->dvalues = (Datum *) palloc(astate_scalar->alen * sizeof(Datum));
+ astate_scalar->dnulls = (bool *) palloc(astate_scalar->alen * sizeof(bool));
+ astate_scalar->nelems = 0;
+
+ astate_scalar->astate.element_type = element_type;
+ get_typlenbyvalalign(element_type,
+ &astate_scalar->astate.typlen,
+ &astate_scalar->astate.typbyval,
+ &astate_scalar->astate.typalign);
+
+ return astate_scalar;
+}
+
+/*
+ * Initialize ArrayBuildState for array accum (internal of accumArrayResult)
+ */
+static ArrayBuildStateArray *
+initializeArrayBuildStateArray(Oid subelement_type,
+ MemoryContext arr_context)
+{
+ ArrayBuildStateArray *astate_array;
+
+ /* array accumulate */
+ astate_array = (ArrayBuildStateArray *) palloc(sizeof(ArrayBuildStateArray));
+ astate_array->astate.is_array_accum = true;
+ astate_array->astate.mcontext = arr_context;
+
+ astate_array->abytes = 0;
+ astate_array->aitems = 0;
+ astate_array->data = NULL;
+ astate_array->nbytes = 0;
+ astate_array->hasnull = false;
+ astate_array->nullbitmap = NULL;
+ astate_array->nitems = 0;
+ astate_array->narray = 0;
+
+ astate_array->astate.element_type = subelement_type;
+ get_typlenbyvalalign(subelement_type,
+ &astate_array->astate.typlen,
+ &astate_array->astate.typbyval,
+ &astate_array->astate.typalign);
+
+ return astate_array;
+}
+
+/*
+ * Append a datum for scalar accumulation (internal of accumArrayResult)
+ */
+static void
+appendScalarDatum(ArrayBuildStateScalar *astate_scalar,
+ Datum dvalue, bool disnull)
+{
/*
* Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
* it's varlena. (You might think that detoasting is not needed here
@@ -4634,25 +4745,129 @@ accumArrayResult(ArrayBuildState *astate,
* because that would mean array_agg_finalfn damages its input, which is
* verboten. Also, this way frequently saves one copying step.)
*/
- if (!disnull && !astate->typbyval)
+ if (!disnull && !astate_scalar->astate.typbyval)
{
- if (astate->typlen == -1)
+ if (astate_scalar->astate.typlen == -1)
dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
else
- dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
+ dvalue = datumCopy(dvalue, astate_scalar->astate.typbyval,
+ astate_scalar->astate.typlen);
}
- astate->dvalues[astate->nelems] = dvalue;
- astate->dnulls[astate->nelems] = disnull;
- astate->nelems++;
+ astate_scalar->dvalues[astate_scalar->nelems] = dvalue;
+ astate_scalar->dnulls[astate_scalar->nelems] = disnull;
+ astate_scalar->nelems++;
+}
- MemoryContextSwitchTo(oldcontext);
+/*
+ * Append a datum for array accum (internal of accumArrayResult)
+ */
+static void
+appendArrayDatum(ArrayBuildStateArray *astate_array,
+ Datum dvalue, bool disnull)
+{
+ ArrayType *arg;
+ int *dims,
+ *lbs,
+ ndims,
+ nitems,
+ ndatabytes;
+ char *data;
+ int i;
- return astate;
+ if (disnull)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot accumulate null arrays")));
+
+ arg = DatumGetArrayTypeP(dvalue);
+
+ ndims = ARR_NDIM(arg);
+ dims = ARR_DIMS(arg);
+ lbs = ARR_LBOUND(arg);
+ data = ARR_DATA_PTR(arg);
+ nitems = ArrayGetNItems(ndims, dims);
+ ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
+
+ if (astate_array->data == NULL)
+ {
+ /* first allocation */
+ if (ndims == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot accumulate empty arrays")));
+
+ if (ndims + 1 > MAXDIM)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ ndims + 1, MAXDIM)));
+
+ astate_array->ndims = ndims;
+ astate_array->dims = (int *) palloc(ndims * sizeof(int));
+ astate_array->lbs = (int *) palloc(ndims * sizeof(int));
+ memcpy(astate_array->dims, dims, ndims * sizeof(int));
+ memcpy(astate_array->lbs, lbs, ndims * sizeof(int));
+
+ astate_array->abytes = ndatabytes >= 512 ? 4 * ndatabytes : 1024;
+ astate_array->data = (char *) palloc(astate_array->abytes);
+ astate_array->aitems = 4 * nitems;
+ }
+ else
+ {
+ if (astate_array->ndims != ndims)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot accumulate incompatible arrays"),
+ errdetail("Arrays of %d and %d dimensions are not "
+ "compatible for concatenation.",
+ astate_array->ndims, ndims)));
+
+ for (i = 0; i < ndims; i++)
+ if (astate_array->dims[i] != dims[i] || astate_array->lbs[i] != lbs[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot accumulate incompatible arrays"),
+ errdetail("Arrays with differing element dimensions are "
+ "not compatible for concatenation.")));
+
+ if (astate_array->nbytes + ndatabytes >= astate_array->abytes)
+ {
+ astate_array->abytes *= 2;
+ astate_array->data = (char *)
+ repalloc(astate_array->data, astate_array->abytes);
+ }
+ if (astate_array->nitems + nitems >= astate_array->aitems)
+ {
+ astate_array->aitems *= 2;
+ astate_array->nullbitmap = (bits8 *)
+ repalloc(astate_array->nullbitmap, (astate_array->aitems + 7) / 8);
+ }
+ }
+ memcpy(astate_array->data + astate_array->nbytes, data, ndatabytes);
+ astate_array->nbytes += ndatabytes;
+
+ if (astate_array->hasnull || ARR_HASNULL(arg))
+ {
+ if (!astate_array->hasnull)
+ {
+ astate_array->hasnull = true;
+ astate_array->nullbitmap = (bits8 *) palloc((astate_array->aitems + 7) / 8);
+ array_bitmap_copy(astate_array->nullbitmap, 0,
+ NULL, 0,
+ astate_array->nitems);
+ }
+ array_bitmap_copy(astate_array->nullbitmap, astate_array->nitems,
+ ARR_NULLBITMAP(arg), 0,
+ nitems);
+ astate_array->nitems += nitems;
+ }
+ astate_array->narray += 1;
}
/*
- * makeArrayResult - produce 1-D final result of accumArrayResult
+ * makeArrayResult - produce 1-D final result of scalar accumArrayResult
+ * - produce N+1-D final result of array accumArrayResult
*
* astate is working state (not NULL)
* rcontext is where to construct result
@@ -4661,17 +4876,36 @@ Datum
makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext)
{
- int dims[1];
- int lbs[1];
+ return makeArrayResult1(astate, rcontext, true);
+}
- dims[0] = astate->nelems;
- lbs[0] = 1;
+/*
+ * makeArrayResult1 - same as makeArrayResult, with option not to
+ * release working state
+ *
+ * release is true if okay to release working state
+ */
+Datum
+makeArrayResult1(ArrayBuildState *astate,
+ MemoryContext rcontext,
+ bool release)
+{
+ if (!astate->is_array_accum)
+ {
+ int dims[1];
+ int lbs[1];
+
+ dims[0] = ((ArrayBuildStateScalar *) astate)->nelems;
+ lbs[0] = 1;
- return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true);
+ return makeMdArrayResult(astate, 1, dims, lbs, rcontext, release);
+ }
+ else
+ return makeMdArrayResult(astate, -1, NULL, NULL, rcontext, release);
}
/*
- * makeMdArrayResult - produce multi-D final result of accumArrayResult
+ * makeMdArrayResult - produce multi-D final result of scalar accumArrayResult
*
* beware: no check that specified dimensions match the number of values
* accumulated.
@@ -4679,6 +4913,11 @@ makeArrayResult(ArrayBuildState *astate,
* astate is working state (not NULL)
* rcontext is where to construct result
* release is true if okay to release working state
+ *
+ * for array accumulation:
+ * ndims == -1: default (calculated from astate)
+ * dims == NULL: default dims (calculated from astate)
+ * lbs == NULL: default (calculated from astate)
*/
Datum
makeMdArrayResult(ArrayBuildState *astate,
@@ -4694,8 +4933,13 @@ makeMdArrayResult(ArrayBuildState *astate,
/* Build the final array result in rcontext */
oldcontext = MemoryContextSwitchTo(rcontext);
- result = construct_md_array(astate->dvalues,
- astate->dnulls,
+ if (!astate->is_array_accum)
+ {
+ ArrayBuildStateScalar *astate_scalar;
+ astate_scalar = (ArrayBuildStateScalar *) astate;
+
+ result = construct_md_array(astate_scalar->dvalues,
+ astate_scalar->dnulls,
ndims,
dims,
lbs,
@@ -4703,6 +4947,16 @@ makeMdArrayResult(ArrayBuildState *astate,
astate->typlen,
astate->typbyval,
astate->typalign);
+ }
+ else
+ {
+ ArrayBuildStateArray *astate_array;
+ astate_array = (ArrayBuildStateArray *) astate;
+ result = makeMdArrayResultArray(astate_array,
+ ndims,
+ dims,
+ lbs);
+ }
MemoryContextSwitchTo(oldcontext);
@@ -4713,6 +4967,75 @@ makeMdArrayResult(ArrayBuildState *astate,
return PointerGetDatum(result);
}
+/*
+ * makeMdArrayResultArray - internal of makeArrayResult & makeMdArrayResult
+ *
+ * beware: no check that specified dimensions match the values
+ * accumulated.
+ *
+ * astate is working state (not NULL)
+ * ndims > -1 if wants to override default (calculated from astate)
+ * dims not NULL if wants to override default
+ * lbs not NULL if wants to override default
+ */
+static ArrayType *
+makeMdArrayResultArray(ArrayBuildStateArray *astate,
+ int ndims,
+ int *dims,
+ int *lbs)
+{
+ ArrayType *result;
+
+ int dataoffset,
+ nbytes;
+
+ if (ndims == -1)
+ ndims = astate->ndims + 1;
+
+ nbytes = astate->nbytes;
+ /* compute required space */
+ if (astate->hasnull)
+ {
+ dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, astate->nitems);
+ nbytes += dataoffset;
+ }
+ else
+ {
+ dataoffset = 0;
+ nbytes += ARR_OVERHEAD_NONULLS(ndims);
+ }
+
+ result = (ArrayType *) palloc0(nbytes);
+ SET_VARSIZE(result, nbytes);
+ result->ndim = astate->ndims + 1;
+ result->dataoffset = dataoffset;
+ result->elemtype = astate->astate.element_type;
+
+ if (dims != NULL)
+ memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
+ else
+ {
+ ARR_DIMS(result)[0] = astate->narray;
+ memcpy(&(ARR_DIMS(result)[1]), astate->dims, (ndims - 1) * sizeof(int));
+ }
+ if (lbs != NULL)
+ memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
+ else
+ {
+ ARR_LBOUND(result)[0] = 1;
+ memcpy(&(ARR_LBOUND(result)[1]), astate->lbs, (ndims - 1) * sizeof(int));
+ }
+
+ memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
+
+ if (astate->hasnull)
+ array_bitmap_copy(ARR_NULLBITMAP(result), 0,
+ astate->nullbitmap, 0,
+ astate->nitems);
+
+ return result;
+}
+
Datum
array_larger(PG_FUNCTION_ARGS)
{
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 3ba9e5e..2005199 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -275,6 +275,7 @@ DATA(insert ( 2901 n 0 xmlconcat2 - - - - f f 0 142 0 0 0 _null_
/* array */
DATA(insert ( 2335 n 0 array_agg_transfn array_agg_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
+DATA(insert ( 6005 n 0 array_agg_anyarray_transfn array_agg_anyarray_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
/* text */
DATA(insert ( 3538 n 0 string_agg_transfn string_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index b6dc1b8..9273c1f 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -879,11 +879,17 @@ DATA(insert OID = 3167 ( array_remove PGNSP PGUID 12 1 0 0 0 f f f f f f i 2
DESCR("remove any occurrences of an element from an array");
DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ ));
DESCR("replace any occurrences of an element in an array");
-DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2283" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
+DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2776" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function");
-DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2283" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
+DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2776" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
DESCR("aggregate final function");
-DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2283" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2776" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+DESCR("concatenate aggregate input into an array");
+DATA(insert OID = 6003 ( array_agg_anyarray_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_transfn _null_ _null_ _null_ ));
+DESCR("aggregate transition function");
+DATA(insert OID = 6004 ( array_agg_anyarray_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_finalfn _null_ _null_ _null_ ));
+DESCR("aggregate final function");
+DATA(insert OID = 6005 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2277" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into an array");
DATA(insert OID = 3218 ( width_bucket PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "2283 2277" _null_ _null_ _null_ _null_ width_bucket_array _null_ _null_ _null_ ));
DESCR("bucket number of operand given a sorted array of bucket lower bounds");
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index e744314..daaae88 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -76,14 +76,15 @@ typedef struct
/*
* working state for accumArrayResult() and friends
+ *
+ * is_array_accum: whether accumulating array values.
+ * (if true must be casted to ArrayBuildStateArray, else
+ * cast to ArrayBuildStateScalar)
*/
typedef struct ArrayBuildState
{
+ bool is_array_accum;
MemoryContext mcontext; /* where all the temp stuff is kept */
- Datum *dvalues; /* array of accumulated Datums */
- bool *dnulls; /* array of is-null flags for Datums */
- int alen; /* allocated length of above arrays */
- int nelems; /* number of valid entries in above arrays */
Oid element_type; /* data type of the Datums */
int16 typlen; /* needed info about datatype */
bool typbyval;
@@ -91,6 +92,43 @@ typedef struct ArrayBuildState
} ArrayBuildState;
/*
+ * array build state for array accumulation of scalar datums
+ */
+typedef struct ArrayBuildStateScalar
+{
+ ArrayBuildState astate;
+
+ Datum *dvalues; /* array of accumulated Datums */
+ bool *dnulls; /* array of is-null flags for Datums */
+ int alen; /* allocated length of above arrays */
+ int nelems; /* number of valid entries in above arrays */
+} ArrayBuildStateScalar;
+
+
+/*
+ * array build state for array accumulation of array datums
+ */
+typedef struct
+{
+ ArrayBuildState astate;
+
+ char *data; /* array of accumulated data */
+ bits8 *nullbitmap; /* bitmap of is-null flags for data */
+
+ int abytes; /* allocated length of above arrays */
+ int aitems; /* allocated length of above arrays */
+ int nbytes; /* number of used bytes in above arrays */
+ int nitems; /* number of elements in above arrays */
+ int narray; /* number of array accumulated */
+
+ int ndims; /* element dimensions */
+ int *dims;
+ int *lbs;
+
+ bool hasnull; /* any element has null */
+} ArrayBuildStateArray;
+
+/*
* structure to cache type metadata needed for array manipulation
*/
typedef struct ArrayMetaState
@@ -258,9 +296,10 @@ extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
MemoryContext rcontext);
extern Datum makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext);
+extern Datum makeArrayResult1(ArrayBuildState *astate,
+ MemoryContext rcontext, bool release);
extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
int *dims, int *lbs, MemoryContext rcontext, bool release);
-
extern ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim);
extern bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull);
extern void array_free_iterator(ArrayIterator iterator);
@@ -293,6 +332,9 @@ extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
extern Datum array_agg_transfn(PG_FUNCTION_ARGS);
extern Datum array_agg_finalfn(PG_FUNCTION_ARGS);
+extern Datum array_agg_anyarray_transfn(PG_FUNCTION_ARGS);
+extern Datum array_agg_anyarray_finalfn(PG_FUNCTION_ARGS);
+
/*
* prototypes for functions defined in array_typanalyze.c
*/
diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out
index 58df854..607aeea 100644
--- a/src/test/regress/expected/aggregates.out
+++ b/src/test/regress/expected/aggregates.out
@@ -914,6 +914,76 @@ select array_agg(distinct a order by a desc nulls last)
{3,2,1,NULL}
(1 row)
+-- array_agg(anyarray)
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar);
+ array_agg
+---------------
+ {{1,2},{3,4}}
+(1 row)
+
+select array_agg(distinct ar order by ar desc)
+ from (select array[i / 2] from generate_series(1,10) a(i)) b(ar);
+ array_agg
+---------------------------
+ {{5},{4},{3},{2},{1},{0}}
+(1 row)
+
+select array_agg(ar)
+ from (select array_agg(array[i, i+1, i-1])
+ from generate_series(1,2) a(i)) b(ar);
+ array_agg
+---------------------
+ {{{1,2,0},{2,3,1}}}
+(1 row)
+
+-- array_agg(anyarray), varlena types
+select array_agg(array[1.2,1.3,1.4]) from generate_series(1,3);
+ array_agg
+---------------------------------------------
+ {{1.2,1.3,1.4},{1.2,1.3,1.4},{1.2,1.3,1.4}}
+(1 row)
+
+select array_agg(array['Hello','Hohoho','Hi']) from generate_series(1,3);
+ array_agg
+---------------------------------------------------------
+ {{Hello,Hohoho,Hi},{Hello,Hohoho,Hi},{Hello,Hohoho,Hi}}
+(1 row)
+
+-- array_agg(anyarray), arrays with nulls
+select array_agg(array[i, null, i+1, null, i+2]) from generate_series(1,3) g(i);
+ array_agg
+---------------------------------------------------------
+ {{1,NULL,2,NULL,3},{2,NULL,3,NULL,4},{3,NULL,4,NULL,5}}
+(1 row)
+
+select array_agg(array[1.1+ i, null, 1.1+i+1, null, 1.1+i+2]) from generate_series(1,3) g(i);
+ array_agg
+---------------------------------------------------------------------------
+ {{2.1,NULL,3.1,NULL,4.1},{3.1,NULL,4.1,NULL,5.1},{4.1,NULL,5.1,NULL,6.1}}
+(1 row)
+
+select array_agg(array[null, 'Hello','Hohoho', null,'Hi']) from generate_series(1,3);
+ array_agg
+---------------------------------------------------------------------------------------
+ {{NULL,Hello,Hohoho,NULL,Hi},{NULL,Hello,Hohoho,NULL,Hi},{NULL,Hello,Hohoho,NULL,Hi}}
+(1 row)
+
+select array_agg(array[[null, 'Hello', null, 'Hi'],['Hello', null, 'Hi', null]]) from generate_series(1,2);
+ array_agg
+-------------------------------------------------------------------------------------------
+ {{{NULL,Hello,NULL,Hi},{Hello,NULL,Hi,NULL}},{{NULL,Hello,NULL,Hi},{Hello,NULL,Hi,NULL}}}
+(1 row)
+
+-- errors
+select array_agg('{}'::int[]) from generate_series(1,2);
+ERROR: cannot accumulate empty arrays
+select array_agg(null::int[]) from generate_series(1,2);
+ERROR: cannot accumulate null arrays
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3}'::int[])) v(ar);
+ERROR: cannot accumulate incompatible arrays
+DETAIL: Arrays with differing element dimensions are not compatible for concatenation.
-- multi-arg aggs, strict/nonstrict, distinct/order by
select aggfstr(a,b,c)
from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c);
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index 46eff67..e80ebec 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -1521,6 +1521,23 @@ select array_agg(unique1) from tenk1 where unique1 < -15;
(1 row)
+select array(select unique1 from tenk1 where unique1 < 15 order by unique1);
+ array
+--------------------------------------
+ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14}
+(1 row)
+
+select array(select array[i,i/2] from generate_series(1,5) a(i));
+ array
+---------------------------------
+ {{1,0},{2,1},{3,1},{4,2},{5,2}}
+(1 row)
+
+-- cannot accumulate null arrays and empty arrays
+select array(select null::int[]);
+ERROR: cannot accumulate null arrays
+select array(select '{}'::int[]);
+ERROR: cannot accumulate empty arrays
select unnest(array[1,2,3]);
unnest
--------
diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql
index 8096a6f..a70419f 100644
--- a/src/test/regress/sql/aggregates.sql
+++ b/src/test/regress/sql/aggregates.sql
@@ -322,6 +322,32 @@ select array_agg(distinct a order by a desc)
select array_agg(distinct a order by a desc nulls last)
from (values (1),(2),(1),(3),(null),(2)) v(a);
+-- array_agg(anyarray)
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar);
+select array_agg(distinct ar order by ar desc)
+ from (select array[i / 2] from generate_series(1,10) a(i)) b(ar);
+select array_agg(ar)
+ from (select array_agg(array[i, i+1, i-1])
+ from generate_series(1,2) a(i)) b(ar);
+
+-- array_agg(anyarray), varlena types
+select array_agg(array[1.2,1.3,1.4]) from generate_series(1,3);
+select array_agg(array['Hello','Hohoho','Hi']) from generate_series(1,3);
+
+-- array_agg(anyarray), arrays with nulls
+select array_agg(array[i, null, i+1, null, i+2]) from generate_series(1,3) g(i);
+select array_agg(array[1.1+ i, null, 1.1+i+1, null, 1.1+i+2]) from generate_series(1,3) g(i);
+select array_agg(array[null, 'Hello','Hohoho', null,'Hi']) from generate_series(1,3);
+
+select array_agg(array[[null, 'Hello', null, 'Hi'],['Hello', null, 'Hi', null]]) from generate_series(1,2);
+
+-- errors
+select array_agg('{}'::int[]) from generate_series(1,2);
+select array_agg(null::int[]) from generate_series(1,2);
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3}'::int[])) v(ar);
+
-- multi-arg aggs, strict/nonstrict, distinct/order by
select aggfstr(a,b,c)
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index fa8a20a..cb00f5f 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -432,6 +432,12 @@ select array_agg(ten) from (select ten from tenk1 where unique1 < 15 order by un
select array_agg(nullif(ten, 4)) from (select ten from tenk1 where unique1 < 15 order by unique1) ss;
select array_agg(unique1) from tenk1 where unique1 < -15;
+select array(select unique1 from tenk1 where unique1 < 15 order by unique1);
+select array(select array[i,i/2] from generate_series(1,5) a(i));
+-- cannot accumulate null arrays and empty arrays
+select array(select null::int[]);
+select array(select '{}'::int[]);
+
select unnest(array[1,2,3]);
select * from unnest(array[1,2,3]);
select unnest(array[1,2,3,4.5]::float8[]);
2014-10-25 10:16 GMT+02:00 Ali Akbar <the.apaan@gmail.com>:
makeArrayResult1 - I have no better name now
I found one next minor detail.
you reuse a array_agg_transfn function. Inside is a message
"array_agg_transfn called in non-aggregate context". It is not correct for
array_agg_anyarray_transfnFixed.
probably specification dim and lbs in array_agg_finalfn is useless, when
you expect a natural result. A automatic similar to
array_agg_anyarray_finalfn should work there too.makeMdArrayResultArray and appendArrayDatum should be marked as "static"
ok, marked all of that as static.
I am thinking so change of makeArrayResult is wrong. It is developed for
1D array. When we expect somewhere ND result now, then we should to use
makeMdArrayResult there. So makeArrayResult should to return 1D array in
all cases. Else a difference between makeArrayResult and makeMdArrayResult
hasn't big change and we have a problem with naming.I'm thinking it like this:
- if we want to accumulate array normally, use accumArrayResult and
makeArrayResult. If we accumulate scalar the result will be 1D array. if we
accumulate array, the resulting dimension is incremented by 1.
- if we want, somehow to affect the normal behavior, and change the
dimensions, use makeMdArrayResult.Searching through the postgres' code, other than internal use in
arrayfuncs.c, makeMdArrayResult is used only
in src/pl/plperl/plperl.c:plperl_array_to_datum, while converting perl
array to postgres array.So if somehow we will accumulate array other than in array_agg, i think
the most natural way is using accumArrayResult and then makeArrayResult.
ok, there is more variants and I can't to decide. But I am not satisfied
with this API. We do some wrong in structure. makeMdArrayResult is now
ugly.
Show quoted text
CMIIW
Regards,
--
Ali Akbar
2014-10-25 15:43 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
2014-10-25 10:16 GMT+02:00 Ali Akbar <the.apaan@gmail.com>:
makeArrayResult1 - I have no better name now
I found one next minor detail.
you reuse a array_agg_transfn function. Inside is a message
"array_agg_transfn called in non-aggregate context". It is not correct for
array_agg_anyarray_transfnFixed.
probably specification dim and lbs in array_agg_finalfn is useless, when
you expect a natural result. A automatic similar to
array_agg_anyarray_finalfn should work there too.makeMdArrayResultArray and appendArrayDatum should be marked as
"static"ok, marked all of that as static.
I am thinking so change of makeArrayResult is wrong. It is developed for
1D array. When we expect somewhere ND result now, then we should to use
makeMdArrayResult there. So makeArrayResult should to return 1D array in
all cases. Else a difference between makeArrayResult and makeMdArrayResult
hasn't big change and we have a problem with naming.I'm thinking it like this:
- if we want to accumulate array normally, use accumArrayResult and
makeArrayResult. If we accumulate scalar the result will be 1D array. if we
accumulate array, the resulting dimension is incremented by 1.
- if we want, somehow to affect the normal behavior, and change the
dimensions, use makeMdArrayResult.Searching through the postgres' code, other than internal use in
arrayfuncs.c, makeMdArrayResult is used only
in src/pl/plperl/plperl.c:plperl_array_to_datum, while converting perl
array to postgres array.So if somehow we will accumulate array other than in array_agg, i think
the most natural way is using accumArrayResult and then makeArrayResult.ok, there is more variants and I can't to decide. But I am not satisfied
with this API. We do some wrong in structure. makeMdArrayResult is now
ugly.
One approach that i can think is we cleanly separate the structures and
API. We don't touch existing ArrayBuildState, accumArrayResult,
makeArrayResult and makeMdArrayResult, and we create new functions:
ArrayBuildStateArray, accumArrayResultArray, makeArrayResultArray and
makeArrayResultArrayCustom.
But if we do that, the array(subselect) implementation will not be as
simple as current patch. We must also change all the ARRAY_SUBLINK-related
accumArrayResult call.
Regarding current patch implementation, the specific typedefs are declared
as:
+typedef struct ArrayBuildStateScalar
+{
+ ArrayBuildState astate;
...
+} ArrayBuildStateArray;
so it necessities rather ugly code like this:
+ result->elemtype = astate->astate.element_type;
Can we use C11 feature of unnamed struct like this? :
+typedef struct ArrayBuildStateScalar
+{
+ ArrayBuildState;
...
+} ArrayBuildStateArray;
so the code can be a little less ugly by doing it like this:
+ result->elemtype = astate->element_type;
I don't know whether all currently supported compilers implements this
feature..
Can you suggest a better structure for this?
If we can't settle on a better structure, i think i'll reimplement
array_agg without the performance improvement, using deconstruct_array and
unchanged accumArrayResult & makeMdArrayResult.
Thanks,
--
Ali Akbar
2014-10-25 12:20 GMT+02:00 Ali Akbar <the.apaan@gmail.com>:
2014-10-25 15:43 GMT+07:00 Pavel Stehule <pavel.stehule@gmail.com>:
2014-10-25 10:16 GMT+02:00 Ali Akbar <the.apaan@gmail.com>:
makeArrayResult1 - I have no better name now
I found one next minor detail.
you reuse a array_agg_transfn function. Inside is a message
"array_agg_transfn called in non-aggregate context". It is not correct for
array_agg_anyarray_transfnFixed.
probably specification dim and lbs in array_agg_finalfn is useless,
when you expect a natural result. A automatic similar to
array_agg_anyarray_finalfn should work there too.makeMdArrayResultArray and appendArrayDatum should be marked as
"static"ok, marked all of that as static.
I am thinking so change of makeArrayResult is wrong. It is developed
for 1D array. When we expect somewhere ND result now, then we should to use
makeMdArrayResult there. So makeArrayResult should to return 1D array in
all cases. Else a difference between makeArrayResult and makeMdArrayResult
hasn't big change and we have a problem with naming.I'm thinking it like this:
- if we want to accumulate array normally, use accumArrayResult and
makeArrayResult. If we accumulate scalar the result will be 1D array. if we
accumulate array, the resulting dimension is incremented by 1.
- if we want, somehow to affect the normal behavior, and change the
dimensions, use makeMdArrayResult.Searching through the postgres' code, other than internal use in
arrayfuncs.c, makeMdArrayResult is used only
in src/pl/plperl/plperl.c:plperl_array_to_datum, while converting perl
array to postgres array.So if somehow we will accumulate array other than in array_agg, i think
the most natural way is using accumArrayResult and then makeArrayResult.ok, there is more variants and I can't to decide. But I am not satisfied
with this API. We do some wrong in structure. makeMdArrayResult is now
ugly.One approach that i can think is we cleanly separate the structures and
API. We don't touch existing ArrayBuildState, accumArrayResult,
makeArrayResult and makeMdArrayResult, and we create new functions:
ArrayBuildStateArray, accumArrayResultArray, makeArrayResultArray and
makeArrayResultArrayCustom.
yes, I am thinking about separatate path, that will be joined in
constructMdArray
In reality, there are two different array builders - with different API and
better to respect it.
But if we do that, the array(subselect) implementation will not be as
simple as current patch. We must also change all the ARRAY_SUBLINK-related
accumArrayResult call.Regarding current patch implementation, the specific typedefs are declared as: +typedef struct ArrayBuildStateScalar +{ + ArrayBuildState astate; ... +} ArrayBuildStateArray;so it necessities rather ugly code like this:
+ result->elemtype = astate->astate.element_type;Can we use C11 feature of unnamed struct like this? :
no, what I know a C11 is prohibited
+typedef struct ArrayBuildStateScalar +{ + ArrayBuildState; ... +} ArrayBuildStateArray;so the code can be a little less ugly by doing it like this:
+ result->elemtype = astate->element_type;I don't know whether all currently supported compilers implements this
feature..Can you suggest a better structure for this?
If we can't settle on a better structure, i think i'll reimplement
array_agg without the performance improvement, using deconstruct_array and
unchanged accumArrayResult & makeMdArrayResult.
you can check it? We can test, how performance lost we get. As second
benefit we can get numbers for introduction new optimized array builder
Regards
Pavel
Show quoted text
Thanks,
--
Ali Akbar
you can check it? We can test, how performance lost we get. As second
benefit we can get numbers for introduction new optimized array builder
array_agg(anyarray) with deconstruct_array, unchanged accumArrayResult and
makeMdArrayResult:
INSERT 0 1
Time: 852,527 ms
INSERT 0 1
Time: 844,275 ms
INSERT 0 1
Time: 858,855 ms
INSERT 0 1
Time: 861,072 ms
INSERT 0 1
Time: 952,006 ms
INSERT 0 1
Time: 953,918 ms
INSERT 0 1
Time: 926,945 ms
INSERT 0 1
Time: 923,692 ms
INSERT 0 1
Time: 940,916 ms
INSERT 0 1
Time: 948,700 ms
INSERT 0 1
Time: 933,333 ms
INSERT 0 1
Time: 948,869 ms
INSERT 0 1
Time: 847,113 ms
INSERT 0 1
Time: 908,572 ms
Total: 12776.83
Avg: 912,63
with last patch (v10):
INSERT 0 1
Time: 643,339 ms
INSERT 0 1
Time: 608,010 ms
INSERT 0 1
Time: 610,465 ms
INSERT 0 1
Time: 613,931 ms
INSERT 0 1
Time: 616,466 ms
INSERT 0 1
Time: 634,754 ms
INSERT 0 1
Time: 683,566 ms
INSERT 0 1
Time: 656,665 ms
INSERT 0 1
Time: 630,096 ms
INSERT 0 1
Time: 607,564 ms
INSERT 0 1
Time: 610,353 ms
INSERT 0 1
Time: 626,816 ms
INSERT 0 1
Time: 610,450 ms
INSERT 0 1
Time: 614,342 ms
Total: 8842,7
Avg: 631,6
It's 30% faster (i tried varlena element - text). I tried several times and
it's consistent in +/- 30%.
quick & dirty non-optimized patch and the test script attached.
Regards,
--
Ali Akbar
Attachments:
array_agg_anyarray_notoptimized_dirty.patchtext/x-patch; charset=US-ASCII; name=array_agg_anyarray_notoptimized_dirty.patchDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 7e5bcd9..f59738a 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -12046,6 +12046,22 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry>
<indexterm>
+ <primary>array_agg</primary>
+ </indexterm>
+ <function>array_agg(<replaceable class="parameter">anyarray</replaceable>)</function>
+ </entry>
+ <entry>
+ any
+ </entry>
+ <entry>
+ the same array type as input type
+ </entry>
+ <entry>input arrays, aggregated into higher-order multidimesional array. Rejects NULL and empty array as input.</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
<primary>average</primary>
</indexterm>
<indexterm>
diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml
index 2f0680f..8c182a4 100644
--- a/doc/src/sgml/syntax.sgml
+++ b/doc/src/sgml/syntax.sgml
@@ -2238,6 +2238,11 @@ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
array
-----------------------------------------------------------------------
{2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31,2412,2413}
+
+SELECT ARRAY(SELECT array(select i) FROM generate_series(1,5) a(i));
+ array
+-----------------------
+ {{1},{2},{3},{4},{5}}
(1 row)
</programlisting>
The subquery must return a single column. The resulting
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 41e973b..0261fcb 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -108,12 +108,16 @@ exprType(const Node *expr)
type = exprType((Node *) tent->expr);
if (sublink->subLinkType == ARRAY_SUBLINK)
{
- type = get_array_type(type);
- if (!OidIsValid(type))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("could not find array type for data type %s",
- format_type_be(exprType((Node *) tent->expr)))));
+ if (!OidIsValid(get_element_type(type)))
+ {
+ /* not array, so check for its array type */
+ type = get_array_type(type);
+ if (!OidIsValid(type))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(exprType((Node *) tent->expr)))));
+ }
}
}
else if (sublink->subLinkType == MULTIEXPR_SUBLINK)
@@ -139,12 +143,16 @@ exprType(const Node *expr)
type = subplan->firstColType;
if (subplan->subLinkType == ARRAY_SUBLINK)
{
- type = get_array_type(type);
- if (!OidIsValid(type))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("could not find array type for data type %s",
- format_type_be(subplan->firstColType))));
+ if (!OidIsValid(get_element_type(type)))
+ {
+ /* not array, so check for its array type */
+ type = get_array_type(type);
+ if (!OidIsValid(type))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(subplan->firstColType))));
+ }
}
}
else if (subplan->subLinkType == MULTIEXPR_SUBLINK)
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 3e7dc85..8fc8b49 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -668,10 +668,16 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
Assert(!te->resjunk);
Assert(testexpr == NULL);
- arraytype = get_array_type(exprType((Node *) te->expr));
- if (!OidIsValid(arraytype))
- elog(ERROR, "could not find array type for datatype %s",
- format_type_be(exprType((Node *) te->expr)));
+
+ arraytype = exprType((Node *) te->expr);
+ if (!OidIsValid(get_element_type(arraytype)))
+ {
+ /* not array, so get the array type */
+ arraytype = get_array_type(exprType((Node *) te->expr));
+ if (!OidIsValid(arraytype))
+ elog(ERROR, "could not find array type for datatype %s",
+ format_type_be(exprType((Node *) te->expr)));
+ }
prm = generate_new_param(root,
arraytype,
exprTypmod((Node *) te->expr),
diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c
index 831466d..35f427d 100644
--- a/src/backend/utils/adt/array_userfuncs.c
+++ b/src/backend/utils/adt/array_userfuncs.c
@@ -16,6 +16,8 @@
#include "utils/builtins.h"
#include "utils/lsyscache.h"
+static Datum array_agg_transfn_internal(PG_FUNCTION_ARGS,
+ const char* fname);
/*-----------------------------------------------------------------------------
* array_push :
@@ -476,6 +478,19 @@ create_singleton_array(FunctionCallInfo fcinfo,
Datum
array_agg_transfn(PG_FUNCTION_ARGS)
{
+ return array_agg_transfn_internal(fcinfo, "array_agg_transfn");
+}
+
+/*
+ * internal function of ARRAY_AGG
+ * called by array_agg_transfn and array_agg_transfn_array
+ *
+ * fname - function name (for error reporting)
+ */
+static Datum
+array_agg_transfn_internal(PG_FUNCTION_ARGS,
+ const char* fname)
+{
Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
MemoryContext aggcontext;
ArrayBuildState *state;
@@ -489,7 +504,7 @@ array_agg_transfn(PG_FUNCTION_ARGS)
if (!AggCheckCallContext(fcinfo, &aggcontext))
{
/* cannot be called directly because of internal-type argument */
- elog(ERROR, "array_agg_transfn called in non-aggregate context");
+ elog(ERROR, "%s called in non-aggregate context", fname);
}
state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
@@ -513,8 +528,8 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
{
Datum result;
ArrayBuildState *state;
- int dims[1];
- int lbs[1];
+ int dims[1];
+ int lbs[1];
/*
* Test for null before Asserting we are in right context. This is to
@@ -529,8 +544,131 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+ /*
+ * Make the result. We cannot release the ArrayBuildState because
+ * sometimes aggregate final functions are re-executed. Rather, it is
+ * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
+ * so.
+ */
dims[0] = state->nelems;
lbs[0] = 1;
+ result = makeMdArrayResult(state,
+ 1, dims, lbs,
+ CurrentMemoryContext, false
+ );
+
+ PG_RETURN_DATUM(result);
+}
+
+typedef struct ArrayBuildStateA {
+ ArrayBuildState * astate;
+ int ndims;
+ int *dims;
+ int *lbs;
+} ArrayBuildStateA;
+
+/*
+ * ARRAY_AGG(anyarray) aggregate function
+ */
+Datum
+array_agg_anyarray_transfn(PG_FUNCTION_ARGS)
+{
+ Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ MemoryContext aggcontext,oldcontext;
+ ArrayBuildStateA *state;
+ ArrayType *elem;
+
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+
+ Datum *elements;
+ bool *nulls;
+ int nelem;
+
+ int i,j;
+
+ if (arg1_typeid == InvalidOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("could not determine input data type")));
+
+ if (!AggCheckCallContext(fcinfo, &aggcontext))
+ {
+ /* cannot be called directly because of internal-type argument */
+ elog(ERROR, "array_agg_anyarray_transfn called in non-aggregate context");
+ }
+
+ state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateA *) PG_GETARG_POINTER(0);
+
+ elem = PG_ARGISNULL(1) ? NULL : PG_GETARG_ARRAYTYPE_P(1);
+
+ oldcontext = MemoryContextSwitchTo(aggcontext);
+ if (state == NULL)
+ {
+ state = (ArrayBuildStateA *) palloc(sizeof(ArrayBuildStateA));
+ state->astate = NULL;
+ state->ndims = ARR_NDIM(elem) + 1;
+ state->dims = (int *) palloc(state->ndims * sizeof(int));
+ state->lbs = (int *) palloc(state->ndims * sizeof(int));
+
+ memcpy(&(state->dims[1]), ARR_DIMS(elem), (state->ndims - 1) * sizeof(int));
+ memcpy(&(state->lbs[1]), ARR_LBOUND(elem), (state->ndims - 1) * sizeof(int));
+
+ state->lbs[0] = 1;
+ state->dims[0] = 1;
+ }
+ else
+ {
+ //TODO: check for correct dimensions
+ state->dims[0] += 1;
+ }
+
+ arg1_typeid = ARR_ELEMTYPE(elem);
+ get_typlenbyvalalign(arg1_typeid,
+ &typlen,
+ &typbyval,
+ &typalign);
+
+ deconstruct_array(elem, arg1_typeid, typlen, typbyval, typalign,
+ &elements, &nulls, &nelem);
+
+ for (i = 0; i < nelem; i++)
+ {
+ state->astate = accumArrayResult(state->astate,
+ elements[i],
+ nulls[i],
+ arg1_typeid,
+ aggcontext);
+ }
+ MemoryContextSwitchTo(oldcontext);
+ /*
+ * The transition type for array_agg() is declared to be "internal", which
+ * is a pass-by-value type the same size as a pointer. So we can safely
+ * pass the ArrayBuildState pointer through nodeAgg.c's machinations.
+ */
+ PG_RETURN_POINTER(state);
+}
+
+Datum
+array_agg_anyarray_finalfn(PG_FUNCTION_ARGS)
+{
+ Datum result;
+ ArrayBuildStateA *state;
+ int i;
+
+ /*
+ * Test for null before Asserting we are in right context. This is to
+ * avoid possible Assert failure in 8.4beta installations, where it is
+ * possible for users to create NULL constants of type internal.
+ */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL(); /* returns null iff no input values */
+
+ /* cannot be called directly because of internal-type argument */
+ Assert(AggCheckCallContext(fcinfo, NULL));
+
+ state = (ArrayBuildStateA *) PG_GETARG_POINTER(0);
/*
* Make the result. We cannot release the ArrayBuildState because
@@ -538,9 +676,11 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
* nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
* so.
*/
- result = makeMdArrayResult(state, 1, dims, lbs,
- CurrentMemoryContext,
- false);
+
+ result = makeMdArrayResult(state->astate,
+ state->ndims, state->dims, state->lbs,
+ CurrentMemoryContext, false
+ );
PG_RETURN_DATUM(result);
}
diff --git a/src/backend/utils/adt/arrayutils.c b/src/backend/utils/adt/arrayutils.c
index 477ccad..af5622a 100644
--- a/src/backend/utils/adt/arrayutils.c
+++ b/src/backend/utils/adt/arrayutils.c
@@ -106,8 +106,8 @@ ArrayGetNItems(int ndim, const int *dims)
if ((Size) ret > MaxArraySize)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("array size exceeds the maximum allowed (%d)",
- (int) MaxArraySize)));
+ errmsg("array size (%d) exceeds the maximum allowed (%d)",
+ (int) ret, (int) MaxArraySize)));
return (int) ret;
}
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 3ba9e5e..2005199 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -275,6 +275,7 @@ DATA(insert ( 2901 n 0 xmlconcat2 - - - - f f 0 142 0 0 0 _null_
/* array */
DATA(insert ( 2335 n 0 array_agg_transfn array_agg_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
+DATA(insert ( 6005 n 0 array_agg_anyarray_transfn array_agg_anyarray_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
/* text */
DATA(insert ( 3538 n 0 string_agg_transfn string_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index b6dc1b8..9273c1f 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -879,11 +879,17 @@ DATA(insert OID = 3167 ( array_remove PGNSP PGUID 12 1 0 0 0 f f f f f f i 2
DESCR("remove any occurrences of an element from an array");
DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ ));
DESCR("replace any occurrences of an element in an array");
-DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2283" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
+DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2776" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function");
-DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2283" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
+DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2776" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
DESCR("aggregate final function");
-DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2283" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2776" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+DESCR("concatenate aggregate input into an array");
+DATA(insert OID = 6003 ( array_agg_anyarray_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_transfn _null_ _null_ _null_ ));
+DESCR("aggregate transition function");
+DATA(insert OID = 6004 ( array_agg_anyarray_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_finalfn _null_ _null_ _null_ ));
+DESCR("aggregate final function");
+DATA(insert OID = 6005 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2277" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into an array");
DATA(insert OID = 3218 ( width_bucket PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "2283 2277" _null_ _null_ _null_ _null_ width_bucket_array _null_ _null_ _null_ ));
DESCR("bucket number of operand given a sorted array of bucket lower bounds");
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index e744314..b4dd433 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -292,6 +292,8 @@ extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
extern Datum array_agg_transfn(PG_FUNCTION_ARGS);
extern Datum array_agg_finalfn(PG_FUNCTION_ARGS);
+extern Datum array_agg_anyarray_transfn(PG_FUNCTION_ARGS);
+extern Datum array_agg_anyarray_finalfn(PG_FUNCTION_ARGS);
/*
* prototypes for functions defined in array_typanalyze.c
diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out
index 58df854..607aeea 100644
--- a/src/test/regress/expected/aggregates.out
+++ b/src/test/regress/expected/aggregates.out
@@ -914,6 +914,76 @@ select array_agg(distinct a order by a desc nulls last)
{3,2,1,NULL}
(1 row)
+-- array_agg(anyarray)
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar);
+ array_agg
+---------------
+ {{1,2},{3,4}}
+(1 row)
+
+select array_agg(distinct ar order by ar desc)
+ from (select array[i / 2] from generate_series(1,10) a(i)) b(ar);
+ array_agg
+---------------------------
+ {{5},{4},{3},{2},{1},{0}}
+(1 row)
+
+select array_agg(ar)
+ from (select array_agg(array[i, i+1, i-1])
+ from generate_series(1,2) a(i)) b(ar);
+ array_agg
+---------------------
+ {{{1,2,0},{2,3,1}}}
+(1 row)
+
+-- array_agg(anyarray), varlena types
+select array_agg(array[1.2,1.3,1.4]) from generate_series(1,3);
+ array_agg
+---------------------------------------------
+ {{1.2,1.3,1.4},{1.2,1.3,1.4},{1.2,1.3,1.4}}
+(1 row)
+
+select array_agg(array['Hello','Hohoho','Hi']) from generate_series(1,3);
+ array_agg
+---------------------------------------------------------
+ {{Hello,Hohoho,Hi},{Hello,Hohoho,Hi},{Hello,Hohoho,Hi}}
+(1 row)
+
+-- array_agg(anyarray), arrays with nulls
+select array_agg(array[i, null, i+1, null, i+2]) from generate_series(1,3) g(i);
+ array_agg
+---------------------------------------------------------
+ {{1,NULL,2,NULL,3},{2,NULL,3,NULL,4},{3,NULL,4,NULL,5}}
+(1 row)
+
+select array_agg(array[1.1+ i, null, 1.1+i+1, null, 1.1+i+2]) from generate_series(1,3) g(i);
+ array_agg
+---------------------------------------------------------------------------
+ {{2.1,NULL,3.1,NULL,4.1},{3.1,NULL,4.1,NULL,5.1},{4.1,NULL,5.1,NULL,6.1}}
+(1 row)
+
+select array_agg(array[null, 'Hello','Hohoho', null,'Hi']) from generate_series(1,3);
+ array_agg
+---------------------------------------------------------------------------------------
+ {{NULL,Hello,Hohoho,NULL,Hi},{NULL,Hello,Hohoho,NULL,Hi},{NULL,Hello,Hohoho,NULL,Hi}}
+(1 row)
+
+select array_agg(array[[null, 'Hello', null, 'Hi'],['Hello', null, 'Hi', null]]) from generate_series(1,2);
+ array_agg
+-------------------------------------------------------------------------------------------
+ {{{NULL,Hello,NULL,Hi},{Hello,NULL,Hi,NULL}},{{NULL,Hello,NULL,Hi},{Hello,NULL,Hi,NULL}}}
+(1 row)
+
+-- errors
+select array_agg('{}'::int[]) from generate_series(1,2);
+ERROR: cannot accumulate empty arrays
+select array_agg(null::int[]) from generate_series(1,2);
+ERROR: cannot accumulate null arrays
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3}'::int[])) v(ar);
+ERROR: cannot accumulate incompatible arrays
+DETAIL: Arrays with differing element dimensions are not compatible for concatenation.
-- multi-arg aggs, strict/nonstrict, distinct/order by
select aggfstr(a,b,c)
from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c);
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index 46eff67..e80ebec 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -1521,6 +1521,23 @@ select array_agg(unique1) from tenk1 where unique1 < -15;
(1 row)
+select array(select unique1 from tenk1 where unique1 < 15 order by unique1);
+ array
+--------------------------------------
+ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14}
+(1 row)
+
+select array(select array[i,i/2] from generate_series(1,5) a(i));
+ array
+---------------------------------
+ {{1,0},{2,1},{3,1},{4,2},{5,2}}
+(1 row)
+
+-- cannot accumulate null arrays and empty arrays
+select array(select null::int[]);
+ERROR: cannot accumulate null arrays
+select array(select '{}'::int[]);
+ERROR: cannot accumulate empty arrays
select unnest(array[1,2,3]);
unnest
--------
diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql
index 8096a6f..a70419f 100644
--- a/src/test/regress/sql/aggregates.sql
+++ b/src/test/regress/sql/aggregates.sql
@@ -322,6 +322,32 @@ select array_agg(distinct a order by a desc)
select array_agg(distinct a order by a desc nulls last)
from (values (1),(2),(1),(3),(null),(2)) v(a);
+-- array_agg(anyarray)
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar);
+select array_agg(distinct ar order by ar desc)
+ from (select array[i / 2] from generate_series(1,10) a(i)) b(ar);
+select array_agg(ar)
+ from (select array_agg(array[i, i+1, i-1])
+ from generate_series(1,2) a(i)) b(ar);
+
+-- array_agg(anyarray), varlena types
+select array_agg(array[1.2,1.3,1.4]) from generate_series(1,3);
+select array_agg(array['Hello','Hohoho','Hi']) from generate_series(1,3);
+
+-- array_agg(anyarray), arrays with nulls
+select array_agg(array[i, null, i+1, null, i+2]) from generate_series(1,3) g(i);
+select array_agg(array[1.1+ i, null, 1.1+i+1, null, 1.1+i+2]) from generate_series(1,3) g(i);
+select array_agg(array[null, 'Hello','Hohoho', null,'Hi']) from generate_series(1,3);
+
+select array_agg(array[[null, 'Hello', null, 'Hi'],['Hello', null, 'Hi', null]]) from generate_series(1,2);
+
+-- errors
+select array_agg('{}'::int[]) from generate_series(1,2);
+select array_agg(null::int[]) from generate_series(1,2);
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3}'::int[])) v(ar);
+
-- multi-arg aggs, strict/nonstrict, distinct/order by
select aggfstr(a,b,c)
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index fa8a20a..cb00f5f 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -432,6 +432,12 @@ select array_agg(ten) from (select ten from tenk1 where unique1 < 15 order by un
select array_agg(nullif(ten, 4)) from (select ten from tenk1 where unique1 < 15 order by unique1) ss;
select array_agg(unique1) from tenk1 where unique1 < -15;
+select array(select unique1 from tenk1 where unique1 < 15 order by unique1);
+select array(select array[i,i/2] from generate_series(1,5) a(i));
+-- cannot accumulate null arrays and empty arrays
+select array(select null::int[]);
+select array(select '{}'::int[]);
+
select unnest(array[1,2,3]);
select * from unnest(array[1,2,3]);
select unnest(array[1,2,3,4.5]::float8[]);
Hi
My idea is using new ArrayBuilder optimized for building multidimensional
arrays with own State type. I think so casting to ArrayBuildState is base
of our problems, so I don't would to do. Code in array_agg_* is simple,
little bit more complex code is in nodeSubplan.c. Some schematic changes
are in attachments.
Regards
Pavel
2014-10-25 15:58 GMT+02:00 Ali Akbar <the.apaan@gmail.com>:
Show quoted text
you can check it? We can test, how performance lost we get. As second
benefit we can get numbers for introduction new optimized array builder
array_agg(anyarray) with deconstruct_array, unchanged accumArrayResult and
makeMdArrayResult:INSERT 0 1
Time: 852,527 ms
INSERT 0 1
Time: 844,275 ms
INSERT 0 1
Time: 858,855 ms
INSERT 0 1
Time: 861,072 ms
INSERT 0 1
Time: 952,006 ms
INSERT 0 1
Time: 953,918 ms
INSERT 0 1
Time: 926,945 ms
INSERT 0 1
Time: 923,692 ms
INSERT 0 1
Time: 940,916 ms
INSERT 0 1
Time: 948,700 ms
INSERT 0 1
Time: 933,333 ms
INSERT 0 1
Time: 948,869 ms
INSERT 0 1
Time: 847,113 ms
INSERT 0 1
Time: 908,572 msTotal: 12776.83
Avg: 912,63
with last patch (v10):
INSERT 0 1
Time: 643,339 ms
INSERT 0 1
Time: 608,010 ms
INSERT 0 1
Time: 610,465 ms
INSERT 0 1
Time: 613,931 ms
INSERT 0 1
Time: 616,466 ms
INSERT 0 1
Time: 634,754 ms
INSERT 0 1
Time: 683,566 ms
INSERT 0 1
Time: 656,665 ms
INSERT 0 1
Time: 630,096 ms
INSERT 0 1
Time: 607,564 ms
INSERT 0 1
Time: 610,353 ms
INSERT 0 1
Time: 626,816 ms
INSERT 0 1
Time: 610,450 ms
INSERT 0 1
Time: 614,342 msTotal: 8842,7
Avg: 631,6
It's 30% faster (i tried varlena element - text). I tried several times
and it's consistent in +/- 30%.quick & dirty non-optimized patch and the test script attached.
Regards,
--
Ali Akbar
Attachments:
design.gifimage/gif; name=design.gifDownload
GIF89a0 � � ��!� � , 0 �
#0#(1 )"-%<&1)5+8"
'+4
8=##''++22###+++ -;#1?66 33/<<$333996;;;(B3R6X9\;`$2B(7H*;M,>P.@T0CW1EY3H]"Bl$Fq%Hu&Jx(N}6Kb8Ne;Sl>Wq?XrE
JNO S![$`&d(k+r-z1@@&DD)MM.SS2YY5]]8aa;ii?CCCIIFKKKSSSXXT[[[@YtC]zEa~mmBqqDzzIccchhclllsss{{{)Q�-W�-X�.Y�0\�1_�1a�3d�Gc�He�Jh�Lj�On�Ro�Po�Qq�Tu�Vx�Xz�Ds�T{�Y}�aZ�\��]��Y��a��`��a��c��e��g��i��j��m��n��q���4�6�8�:�>� ?� @� A�"D�#F�$H�%K�&M�(O�(P�(Q�*U�+V�,X�-[�.]�0_�0`�0a�2e��M��P��S��V��Y��]��c��f��h��s��l��n��q��t��y��~����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� � � H����*\�����#J�H����3j������ C�I��I��R�\�����,O��I����8s�������@?�J�h��H�*]���nP�J�J����X�j������`��K�,8;M
]�6e��p���K�i��x������_�g��<�nD�+^��+Z��"nL������K�s��'S�������A�(�*���� p����XNHC`���N��{v���`@�U�[Z���-5�p�n��z ���G��=�y���N������:�~�J��������8un�Q�� MT��1A�@�}���� �d�@�*�Y\�u��j���7#���P�H��,����.���*����2�H��<������Zk{�@ -��bB���6Y����mc��$�d�OBu�}�L���H*�����@������f�o�9��Z��d��L �����@�[: U�V��-�� ��X����S� -�d�� ��r���-���Pi�i_�m���
��^q@6P�y��Ps���r��4P�,�V�r����M6��U��tZ���2K�n��V���Q����"����J%6�n�����*�������|�8�T��E�g��oiX8���a�r-QaC��J�*7�\l-����\K%pV�l��X����������\P�_�1�{������(��DMR F'M��%��X*N�3�Tw���Jg�5EHo�uFL���]�m��hTv�l'v[m��v�t�m��v��6[y� >}.8Nn��{u��r�S@�����;dN��Q���+���J!y:����/�P��}wn����0��^P��n�����o8�/���o���'���@�����+<��2�o�B �X/��1 |3~��`�=vL"����o������������ � �����w0����7� ��{��1��=b OP�2V n/���A �q���""�2(������s �A/�0 �S���@0/�^2� � �0x���
ExBe� }?�����-a2J���|��<�~�c� ���6���pdcx�������������$�I��)�}MAc���
Z��#��M������W� �a-�$���
������I\�����$���I"