diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 7b4fbb50471..b26db3b04b0 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -22125,7 +22125,7 @@ SELECT NULLIF(value, '(none)') ... Computes the maximum of the non-null input values. Available for any numeric, string, date/time, or enum type, - as well as inet, interval, + as well as bytea, inet, interval, money, oid, pg_lsn, tid, xid8, and also arrays and composite types containing sortable data types. @@ -22144,7 +22144,7 @@ SELECT NULLIF(value, '(none)') ... Computes the minimum of the non-null input values. Available for any numeric, string, date/time, or enum type, - as well as inet, interval, + as well as bytea, inet, interval, money, oid, pg_lsn, tid, xid8, and also arrays and composite types containing sortable data types. diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index d46ed3ccf9f..533bebc1c7b 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -3931,6 +3931,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_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_sortsupport(PG_FUNCTION_ARGS) { diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 504bbe53276..4d4a2dd7afa 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -57,6 +57,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202410021 +#define CATALOG_VERSION_NO 202410081 #endif diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat index b6b6352d917..6ba6a997c29 100644 --- a/src/include/catalog/pg_aggregate.dat +++ b/src/include/catalog/pg_aggregate.dat @@ -161,6 +161,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', @@ -232,6 +235,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 77f54a79e6a..3ae31a614ce 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -1314,6 +1314,13 @@ proname => 'text_smaller', proleakproof => 't', prorettype => 'text', proargtypes => 'text text', prosrc => 'text_smaller' }, +{ oid => '8920', descr => 'larger of two', + proname => 'bytea_larger', proleakproof => 't', prorettype => 'bytea', + proargtypes => 'bytea bytea', prosrc => 'bytea_larger' }, +{ oid => '8921', 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' }, @@ -6879,6 +6886,9 @@ { oid => '5099', descr => 'maximum value of all xid8 input values', proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'xid8', proargtypes => 'xid8', prosrc => 'aggregate_dummy' }, +{ oid => '8922', descr => 'maximum value of all bytea input values', + proname => 'max', prokind => 'a', proisstrict => 'f', prorettype => 'bytea', + proargtypes => 'bytea', prosrc => 'aggregate_dummy' }, { oid => '2131', descr => 'minimum value of all bigint input values', proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'int8', @@ -6952,6 +6962,9 @@ { oid => '5100', descr => 'minimum value of all xid8 input values', proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'xid8', proargtypes => 'xid8', prosrc => 'aggregate_dummy' }, +{ oid => '8923', descr => 'minimum value of all bytea input values', + proname => 'min', prokind => 'a', proisstrict => 'f', prorettype => 'bytea', + proargtypes => 'bytea', prosrc => 'aggregate_dummy' }, # count has two forms: count(any) and count(*) { oid => '2147', diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index 8ac13b562c4..e14e7356567 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -1950,7 +1950,7 @@ select string_agg(distinct f1::text, ',' order by f1::text) from varchar_tbl; - a,ab,abcd (1 row) --- 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; string_agg @@ -1984,6 +1984,32 @@ 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) + +insert into bytea_test_table values(decode('ffff','hex')); +insert into bytea_test_table values(decode('aaaa','hex')); +select min(v) from bytea_test_table; + min +------ + \xaa +(1 row) + +select max(v) from bytea_test_table; + max +-------- + \xffff +(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 0d734169f11..34a32bd11d2 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -876,6 +876,8 @@ uuid_extract_timestamp(uuid) uuid_extract_version(uuid) crc32(bytea) crc32c(bytea) +bytea_larger(bytea,bytea) +bytea_smaller(bytea,bytea) -- restore normal output mode \a\t -- List of functions used by libpq's fe-lobj.c diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql index ca6d1bcfb7f..ddf38bafb42 100644 --- a/src/test/regress/sql/aggregates.sql +++ b/src/test/regress/sql/aggregates.sql @@ -747,7 +747,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; @@ -762,6 +762,15 @@ 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; + +insert into bytea_test_table values(decode('ffff','hex')); +insert into bytea_test_table values(decode('aaaa','hex')); + +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