From 0b12f247752930f4873555045a7885ea16bc76a2 Mon Sep 17 00:00:00 2001 From: Aleksander Alekseev Date: Wed, 14 Aug 2024 12:11:47 +0300 Subject: [PATCH v1] Add get_bytes() and set_bytes() functions. The new functions provide a convenient way of converting between integer types and bytea. Previously there were only get_byte() and set_byte() which operate with a single byte. Aleksander Alekseev, reviewed by TODO FIXME Discussion: TODO FIXME BUMP CATVERSION --- doc/src/sgml/func.sgml | 46 ++++++++++++++- src/backend/utils/adt/varlena.c | 80 +++++++++++++++++++++++++++ src/include/catalog/pg_proc.dat | 6 ++ src/test/regress/expected/strings.out | 36 ++++++++++++ src/test/regress/sql/strings.sql | 10 ++++ 5 files changed, 177 insertions(+), 1 deletion(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index cdde647513..4034cc6040 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -4562,6 +4562,27 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); + + + + get_bytes + + get_bytes ( bytes bytea, + offset integer, + size integer ) + bigint + + + Extracts size bytes at a given + offset + from binary string. + + + get_bytes('\x0123456789ABCDEF'::bytea, 5, 2) + 43981 + + + @@ -4662,6 +4683,28 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); + + + + set_bytes + + set_bytes ( bytes bytea, + offset integer, + size integer, + newvalue bigint ) + bytea + + + Sets size bytes at a given + offset + in binary string to newvalue. + + + set_bytes('\x0123456789abcdef'::bytea, 5, 2, 0x1122) + \x01234567891122ef + + + @@ -4761,7 +4804,8 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); - Functions get_byte and set_byte + Functions get_byte, set_byte, + get_bytes and set_bytes number the first byte of a binary string as byte 0. Functions get_bit and set_bit number bits from the right within each byte; for example bit 0 is the least diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index 4f9a676c93..971aab4a45 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -3212,6 +3212,46 @@ byteaGetByte(PG_FUNCTION_ARGS) PG_RETURN_INT32(byte); } +/*------------------------------------------------------------- + * byteaGetBytes + * + * this routine treats "bytea" as an array of bytes. + * It returns the N bytes at a given offset as a bigint value. + *------------------------------------------------------------- + */ +Datum +byteaGetBytes(PG_FUNCTION_ARGS) +{ + bytea *v = PG_GETARG_BYTEA_PP(0); + int32 offset = PG_GETARG_INT32(1); + int32 size = PG_GETARG_INT32(2); + int64 result = 0; + int len; + + if (size < 1 || size > 8) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("size %d out of valid range, 1..8", + size))); + + len = VARSIZE_ANY_EXHDR(v); + + if (offset < 0 || offset > (len-size)) + ereport(ERROR, + (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), + errmsg("index %d out of valid range, 0..%d", + offset, len - size))); + + while(size--) + { + result = result << 8; + result |= ((unsigned char *) VARDATA_ANY(v))[offset]; + offset++; + } + + PG_RETURN_INT64(result); +} + /*------------------------------------------------------------- * byteaGetBit * @@ -3282,6 +3322,46 @@ byteaSetByte(PG_FUNCTION_ARGS) PG_RETURN_BYTEA_P(res); } +/*------------------------------------------------------------- + * byteaSetBytes + * + * Given an instance of type 'bytea' creates a new one with + * the N bytes at a given offset set to the provided bigint value. + * + *------------------------------------------------------------- + */ +Datum +byteaSetBytes(PG_FUNCTION_ARGS) +{ + bytea *res = PG_GETARG_BYTEA_P_COPY(0); + int32 offset = PG_GETARG_INT32(1); + int32 size = PG_GETARG_INT32(2); + int64 newValue = PG_GETARG_INT64(3); + int len; + + if (size < 1 || size > 8) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("size %d out of valid range, 1..8", + size))); + + len = VARSIZE_ANY_EXHDR(res); + + if (offset < 0 || offset > (len-size)) + ereport(ERROR, + (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), + errmsg("index %d out of valid range, 0..%d", + offset, len - size))); + while(size) + { + ((unsigned char*) VARDATA_ANY(res))[offset+size-1] = newValue & 0xFF; + newValue = newValue >> 8; + size--; + } + + PG_RETURN_BYTEA_P(res); +} + /*------------------------------------------------------------- * byteaSetBit * diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 4abc6d9526..5daa1132c7 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -1441,6 +1441,12 @@ { oid => '722', descr => 'set byte', proname => 'set_byte', prorettype => 'bytea', proargtypes => 'bytea int4 int4', prosrc => 'byteaSetByte' }, +{ oid => '8573', descr => 'get bytes', + proname => 'get_bytes', prorettype => 'int8', proargtypes => 'bytea int4 int4', + prosrc => 'byteaGetBytes' }, +{ oid => '8574', descr => 'set bytes', + proname => 'set_bytes', prorettype => 'bytea', + proargtypes => 'bytea int4 int4 int8', prosrc => 'byteaSetBytes' }, { oid => '723', descr => 'get bit', proname => 'get_bit', prorettype => 'int4', proargtypes => 'bytea int8', prosrc => 'byteaGetBit' }, diff --git a/src/test/regress/expected/strings.out b/src/test/regress/expected/strings.out index b65bb2d536..9fd52a1e11 100644 --- a/src/test/regress/expected/strings.out +++ b/src/test/regress/expected/strings.out @@ -2358,6 +2358,42 @@ SELECT set_byte('\x1234567890abcdef00'::bytea, 7, 11); SELECT set_byte('\x1234567890abcdef00'::bytea, 99, 11); -- error ERROR: index 99 out of valid range, 0..8 +SELECT get_bytes('\x1122334455667788'::bytea, 0, 8) = 0x1122334455667788; + ?column? +---------- + t +(1 row) + +SELECT get_bytes('\x1122334455667788'::bytea, 1, 2) = 0x2233; + ?column? +---------- + t +(1 row) + +SELECT get_bytes('\x1122334455667788'::bytea, 0, 0); -- error +ERROR: size 0 out of valid range, 1..8 +SELECT get_bytes('\x1122334455667788'::bytea, 0, 9); -- error +ERROR: size 9 out of valid range, 1..8 +SELECT get_bytes('\x1122334455667788'::bytea, 1, 8); -- error +ERROR: index 1 out of valid range, 0..0 +SELECT set_bytes('\x0123456789abcdef'::bytea, 0, 8, 0x1122334455667788); + set_bytes +-------------------- + \x1122334455667788 +(1 row) + +SELECT set_bytes('\x1122334455667788'::bytea, 1, 2, 0xAABB); + set_bytes +-------------------- + \x11aabb4455667788 +(1 row) + +SELECT set_bytes('\x0123456789abcdef'::bytea, 0, 0, 123); -- error +ERROR: size 0 out of valid range, 1..8 +SELECT set_bytes('\x0123456789abcdef'::bytea, 0, 9, 123); -- error +ERROR: size 9 out of valid range, 1..8 +SELECT set_bytes('\x0123456789abcdef'::bytea, 1, 8, 123); -- error +ERROR: index 1 out of valid range, 0..0 -- -- test behavior of escape_string_warning and standard_conforming_strings options -- diff --git a/src/test/regress/sql/strings.sql b/src/test/regress/sql/strings.sql index 8e0f3a0e75..d0aa44ee2c 100644 --- a/src/test/regress/sql/strings.sql +++ b/src/test/regress/sql/strings.sql @@ -750,6 +750,16 @@ SELECT get_byte('\x1234567890abcdef00'::bytea, 3); SELECT get_byte('\x1234567890abcdef00'::bytea, 99); -- error SELECT set_byte('\x1234567890abcdef00'::bytea, 7, 11); SELECT set_byte('\x1234567890abcdef00'::bytea, 99, 11); -- error +SELECT get_bytes('\x1122334455667788'::bytea, 0, 8) = 0x1122334455667788; +SELECT get_bytes('\x1122334455667788'::bytea, 1, 2) = 0x2233; +SELECT get_bytes('\x1122334455667788'::bytea, 0, 0); -- error +SELECT get_bytes('\x1122334455667788'::bytea, 0, 9); -- error +SELECT get_bytes('\x1122334455667788'::bytea, 1, 8); -- error +SELECT set_bytes('\x0123456789abcdef'::bytea, 0, 8, 0x1122334455667788); +SELECT set_bytes('\x1122334455667788'::bytea, 1, 2, 0xAABB); +SELECT set_bytes('\x0123456789abcdef'::bytea, 0, 0, 123); -- error +SELECT set_bytes('\x0123456789abcdef'::bytea, 0, 9, 123); -- error +SELECT set_bytes('\x0123456789abcdef'::bytea, 1, 8, 123); -- error -- -- test behavior of escape_string_warning and standard_conforming_strings options -- 2.46.0