Hello. BYTEA type has the ability to use comparison operations. But it is absent of min/max aggregate functions. They are nice to have to provide consistency with the TEXT type.
-- With best regards, Marat Bukharov
From 109b91999b7a8d8c1808658134494b91616c2825 Mon Sep 17 00:00:00 2001 From: Marat Bukharov <mara...@yandex-team.ru> Date: Wed, 3 Jul 2024 15:53:52 +0300 Subject: [PATCH v1] add bytea agg funcs --- doc/src/sgml/func.sgml | 4 +-- src/backend/utils/adt/varlena.c | 38 ++++++++++++++++++++++++ src/include/catalog/pg_aggregate.dat | 6 ++++ src/include/catalog/pg_proc.dat | 7 +++++ src/test/regress/expected/aggregates.out | 12 ++++++++ src/test/regress/expected/opr_sanity.out | 3 +- src/test/regress/sql/aggregates.sql | 5 +++- 7 files changed, 71 insertions(+), 4 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index f1f22a1960..891f7a4259 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -21976,7 +21976,7 @@ SELECT NULLIF(value, '(none)') ... <para> Computes the maximum of the non-null input values. Available for any numeric, string, date/time, or enum type, - as well as <type>inet</type>, <type>interval</type>, + as well as <type>bytea</type>, <type>inet</type>, <type>interval</type>, <type>money</type>, <type>oid</type>, <type>pg_lsn</type>, <type>tid</type>, <type>xid8</type>, and arrays of any of these types. @@ -21995,7 +21995,7 @@ SELECT NULLIF(value, '(none)') ... <para> Computes the minimum of the non-null input values. Available for any numeric, string, date/time, or enum type, - as well as <type>inet</type>, <type>interval</type>, + as well as <type>bytea</type>, <type>inet</type>, <type>interval</type>, <type>money</type>, <type>oid</type>, <type>pg_lsn</type>, <type>tid</type>, <type>xid8</type>, and arrays of any of these types. diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index d2e2e9bbba..408fff0aff 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -3956,6 +3956,44 @@ byteacmp(PG_FUNCTION_ARGS) PG_RETURN_INT32(cmp); } +Datum +bytea_larger(PG_FUNCTION_ARGS) +{ + bytea *arg1 = PG_GETARG_BYTEA_PP(0); + bytea *arg2 = PG_GETARG_BYTEA_PP(1); + bytea *result; + int len1, + len2; + int cmp; + + len1 = VARSIZE_ANY_EXHDR(arg1); + len2 = VARSIZE_ANY_EXHDR(arg2); + + cmp = memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), Min(len1, len2)); + result = ((cmp > 0) || ((cmp == 0) && (len1 >= len2)) ? arg1 : arg2); + + PG_RETURN_BYTEA_P(result); +} + +Datum +bytea_smaller(PG_FUNCTION_ARGS) +{ + bytea *arg1 = PG_GETARG_TEXT_PP(0); + bytea *arg2 = PG_GETARG_TEXT_PP(1); + bytea *result; + int len1, + len2; + int cmp; + + len1 = VARSIZE_ANY_EXHDR(arg1); + len2 = VARSIZE_ANY_EXHDR(arg2); + + cmp = memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), Min(len1, len2)); + result = ((cmp < 0) || ((cmp == 0) && (len1 <= len2)) ? arg1 : arg2); + + PG_RETURN_BYTEA_P(result); +} + Datum bytea_sortsupport(PG_FUNCTION_ARGS) { diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat index 5f13532abc..f0ee024b7a 100644 --- a/src/include/catalog/pg_aggregate.dat +++ b/src/include/catalog/pg_aggregate.dat @@ -158,6 +158,9 @@ { aggfnoid => 'max(xid8)', aggtransfn => 'xid8_larger', aggcombinefn => 'xid8_larger', aggsortop => '>(xid8,xid8)', aggtranstype => 'xid8' }, +{ aggfnoid => 'max(bytea)', aggtransfn => 'bytea_larger', + aggcombinefn => 'bytea_larger', aggsortop => '>(bytea,bytea)', + aggtranstype => 'bytea' } # min { aggfnoid => 'min(int8)', aggtransfn => 'int8smaller', @@ -226,6 +229,9 @@ { aggfnoid => 'min(xid8)', aggtransfn => 'xid8_smaller', aggcombinefn => 'xid8_smaller', aggsortop => '<(xid8,xid8)', aggtranstype => 'xid8' }, +{ aggfnoid => 'min(bytea)', aggtransfn => 'bytea_smaller', + aggcombinefn => 'bytea_smaller', aggsortop => '<(bytea,bytea)', + aggtranstype => 'bytea' } # count { aggfnoid => 'count(any)', aggtransfn => 'int8inc_any', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index d4ac578ae6..fd2e4e7fd6 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -1278,6 +1278,13 @@ proname => 'text_smaller', proleakproof => 't', prorettype => 'text', proargtypes => 'text text', prosrc => 'text_smaller' }, +{ oid => '6347', descr => 'larger of two', + proname => 'bytea_larger', proleakproof => 't', prorettype => 'bytea', + proargtypes => 'bytea bytea', prosrc => 'bytea_larger' }, +{ oid => '6348', descr => 'smaller of two', + proname => 'bytea_smaller', proleakproof => 't', prorettype => 'bytea', + proargtypes => 'bytea bytea', prosrc => 'bytea_smaller' }, + { oid => '460', descr => 'I/O', proname => 'int8in', prorettype => 'int8', proargtypes => 'cstring', prosrc => 'int8in' }, diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index 1c1ca7573a..0540ccaa9f 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1959,6 +1959,18 @@ select string_agg(v, decode('ee', 'hex')) from bytea_test_table; \xffeeaa (1 row) +select min(v) from bytea_test_table; + min +------ + \xaa +(1 row) + +select max(v) from bytea_test_table; + max +------ + \xff +(1 row) + drop table bytea_test_table; -- Test parallel string_agg and array_agg create table pagg_test (x int, y int) with (autovacuum_enabled = off); diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 9d047b21b8..693f339eba 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -634,6 +634,8 @@ name(text) name(character) text_larger(text,text) text_smaller(text,text) +bytea_larger(bytea,bytea) +bytea_smaller(bytea,bytea) int8eq(bigint,bigint) int8ne(bigint,bigint) int8lt(bigint,bigint) @@ -2300,4 +2302,3 @@ WHERE icoll != 0 AND indexrelid | indrelid | iclass | icoll ------------+----------+--------+------- (0 rows) - diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql index 1a18ca3d8f..1f438dbf79 100644 --- a/src/test/regress/sql/aggregates.sql +++ b/src/test/regress/sql/aggregates.sql @@ -741,7 +741,7 @@ select string_agg(distinct f1::text, ',' order by f1) from varchar_tbl; -- not select string_agg(distinct f1, ',' order by f1::text) from varchar_tbl; -- not ok select string_agg(distinct f1::text, ',' order by f1::text) from varchar_tbl; -- ok --- string_agg bytea tests +-- string_agg, min, max bytea tests create table bytea_test_table(v bytea); select string_agg(v, '') from bytea_test_table; @@ -756,6 +756,9 @@ select string_agg(v, '') from bytea_test_table; select string_agg(v, NULL) from bytea_test_table; select string_agg(v, decode('ee', 'hex')) from bytea_test_table; +select min(v) from bytea_test_table; +select max(v) from bytea_test_table; + drop table bytea_test_table; -- Test parallel string_agg and array_agg -- 2.44.2