From 3af3f4f6fdab95035172f9a89293a39cfddefbeb Mon Sep 17 00:00:00 2001 From: Sehrope Sarkuni Date: Sun, 20 Sep 2026 12:49:56 +0000 Subject: [PATCH 2/2] Add set_byte(bytea, int, int, int) to set a range of bytes Setting several consecutive bytes to the same value currently needs nested set_byte() calls, each of which copies the whole value. The new four-argument form sets count bytes starting at the given offset with one copy and one memset. count = 0 is allowed and returns the input unchanged, including with an offset equal to the length. Ranges extending past the end and negative counts are errors. The bounds check compares count against the remaining length so offset + count cannot overflow. Both forms fetch their arguments and call a shared helper. The new pg_proc entry gets its own prosrc because opr_sanity rejects entries that share a prosrc but differ in pronargs. --- doc/src/sgml/func/func-binarystring.sgml | 11 +++- src/backend/utils/adt/bytea.c | 54 +++++++++++++++---- src/include/catalog/pg_proc.dat | 3 ++ src/test/regress/expected/strings.out | 68 ++++++++++++++++++++++++ src/test/regress/sql/strings.sql | 18 +++++++ 5 files changed, 141 insertions(+), 13 deletions(-) diff --git a/doc/src/sgml/func/func-binarystring.sgml b/doc/src/sgml/func/func-binarystring.sgml index ebbf4dce7c3..f1033efa53c 100644 --- a/doc/src/sgml/func/func-binarystring.sgml +++ b/doc/src/sgml/func/func-binarystring.sgml @@ -481,17 +481,24 @@ set_byte ( bytes bytea, n integer, - newvalue integer ) + newvalue integer + , count integer ) bytea Sets n'th byte in binary string to newvalue, which must be - between 0 and 255. + between 0 and 255. If count is specified, + sets count consecutive bytes starting at + byte n; the range must lie within the string. set_byte('\x1234567890'::bytea, 4, 64) \x1234567840 + + + set_byte('\x1234567890'::bytea, 1, 255, 3) + \x12ffffff90 diff --git a/src/backend/utils/adt/bytea.c b/src/backend/utils/adt/bytea.c index 2eb3cbc7229..85f551f7e3d 100644 --- a/src/backend/utils/adt/bytea.c +++ b/src/backend/utils/adt/bytea.c @@ -38,6 +38,7 @@ static bytea *bytea_catenate(bytea *t1, bytea *t2); static bytea *bytea_substring(Datum str, int S, int L, bool length_not_specified); static bytea *bytea_overlay(bytea *t1, bytea *t2, int sp, int sl); +static void bytea_set_bytes(bytea *res, int32 n, int32 newByte, int32 count); typedef struct { @@ -698,7 +699,8 @@ byteaGetBit(PG_FUNCTION_ARGS) * byteaSetByte * * Given an instance of type 'bytea' creates a new one with - * the Nth byte set to the given value. + * the Nth byte set to the given value. byteaSetByteRange + * instead sets 'count' consecutive bytes starting at the Nth. * *------------------------------------------------------------- */ @@ -708,27 +710,57 @@ byteaSetByte(PG_FUNCTION_ARGS) bytea *res = PG_GETARG_BYTEA_P_COPY(0); int32 n = PG_GETARG_INT32(1); int32 newByte = PG_GETARG_INT32(2); - int len; - len = VARSIZE(res) - VARHDRSZ; + bytea_set_bytes(res, n, newByte, 1); - if (n < 0 || n >= len) + PG_RETURN_BYTEA_P(res); +} + +Datum +byteaSetByteRange(PG_FUNCTION_ARGS) +{ + bytea *res = PG_GETARG_BYTEA_P_COPY(0); + int32 n = PG_GETARG_INT32(1); + int32 newByte = PG_GETARG_INT32(2); + int32 count = PG_GETARG_INT32(3); + + bytea_set_bytes(res, n, newByte, count); + + PG_RETURN_BYTEA_P(res); +} + +/* Validate and set 'count' bytes of 'res' starting at 'n', in place. */ +static void +bytea_set_bytes(bytea *res, int32 n, int32 newByte, int32 count) +{ + int len = VARSIZE(res) - VARHDRSZ; + + if (count < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("count must not be negative"))); + + /* An empty range may start just past the last byte. */ + if (n < 0 || n > len || (count > 0 && n == len)) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("index %d out of valid range, 0..%d", - n, len - 1))); + n, count == 0 ? len : len - 1))); + + /* n <= len here, so this cannot overflow */ + if (count > len - n) + ereport(ERROR, + (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), + errmsg("bytes %d..%" PRId64 " out of valid range, 0..%d", + n, (int64) n + count - 1, len - 1))); if (newByte < 0 || newByte > 255) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("new byte must be 0..255"))); - /* - * Now set the byte. - */ - ((unsigned char *) VARDATA(res))[n] = newByte; - - PG_RETURN_BYTEA_P(res); + if (count > 0) + memset(VARDATA(res) + n, newByte, count); } /*------------------------------------------------------------- diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index f46427258e3..45b31f05ddf 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -1531,6 +1531,9 @@ { oid => '722', descr => 'set byte', proname => 'set_byte', prorettype => 'bytea', proargtypes => 'bytea int4 int4', prosrc => 'byteaSetByte' }, +{ oid => '9578', descr => 'set range of bytes', + proname => 'set_byte', prorettype => 'bytea', + proargtypes => 'bytea int4 int4 int4', prosrc => 'byteaSetByteRange' }, { 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 ab2b3b99768..173dc6cc9fb 100644 --- a/src/test/regress/expected/strings.out +++ b/src/test/regress/expected/strings.out @@ -3273,6 +3273,74 @@ SELECT set_byte('\x1234567890abcdef00'::bytea, 0, 256); -- error ERROR: new byte must be 0..255 SELECT set_byte('\x1234567890abcdef00'::bytea, 0, -1); -- error ERROR: new byte must be 0..255 +SELECT set_byte('\x01020304'::bytea, 1, 255, 1); + set_byte +------------ + \x01ff0304 +(1 row) + +SELECT set_byte('\x01020304'::bytea, 1, 255, 2); + set_byte +------------ + \x01ffff04 +(1 row) + +SELECT set_byte('\x01020304'::bytea, 0, 255, 2); + set_byte +------------ + \xffff0304 +(1 row) + +SELECT set_byte('\x01020304'::bytea, 2, 0, 2); + set_byte +------------ + \x01020000 +(1 row) + +SELECT set_byte('\x01020304'::bytea, 0, 0, 4); + set_byte +------------ + \x00000000 +(1 row) + +SELECT set_byte('\x01020304'::bytea, 1, 255, 0); + set_byte +------------ + \x01020304 +(1 row) + +SELECT set_byte('\x01020304'::bytea, 4, 0, 0); + set_byte +------------ + \x01020304 +(1 row) + +SELECT set_byte('\x'::bytea, 0, 0, 0); + set_byte +---------- + \x +(1 row) + +SELECT set_byte('\x01020304'::bytea, 0, 0, -1); -- error +ERROR: count must not be negative +SELECT set_byte('\x01020304'::bytea, -1, 0, 1); -- error +ERROR: index -1 out of valid range, 0..3 +SELECT set_byte('\x01020304'::bytea, 5, 0, 0); -- error +ERROR: index 5 out of valid range, 0..4 +SELECT set_byte('\x01020304'::bytea, 4, 0, 1); -- error +ERROR: index 4 out of valid range, 0..3 +SELECT set_byte('\x01020304'::bytea, 3, 0, 2); -- error +ERROR: bytes 3..4 out of valid range, 0..3 +SELECT set_byte('\x01020304'::bytea, 0, 0, 2147483647); -- error +ERROR: bytes 0..2147483646 out of valid range, 0..3 +SELECT set_byte('\x01020304'::bytea, 3, 0, 2147483647); -- error, end overflows int32 +ERROR: bytes 3..2147483649 out of valid range, 0..3 +SELECT set_byte('\x01020304'::bytea, 0, 256, 0); -- error, newvalue checked when count is 0 +ERROR: new byte must be 0..255 +SELECT set_byte('\x01020304'::bytea, 0, -1, 1); -- error +ERROR: new byte must be 0..255 +SELECT set_byte('\x01020304'::bytea, 0, 256, 1); -- error +ERROR: new byte must be 0..255 -- -- conversions between bytea and integer types -- diff --git a/src/test/regress/sql/strings.sql b/src/test/regress/sql/strings.sql index d1c9a64264a..60b83ed1cc6 100644 --- a/src/test/regress/sql/strings.sql +++ b/src/test/regress/sql/strings.sql @@ -1118,6 +1118,24 @@ SELECT set_byte('\x1234567890abcdef00'::bytea, 7, 11); SELECT set_byte('\x1234567890abcdef00'::bytea, 99, 11); -- error SELECT set_byte('\x1234567890abcdef00'::bytea, 0, 256); -- error SELECT set_byte('\x1234567890abcdef00'::bytea, 0, -1); -- error +SELECT set_byte('\x01020304'::bytea, 1, 255, 1); +SELECT set_byte('\x01020304'::bytea, 1, 255, 2); +SELECT set_byte('\x01020304'::bytea, 0, 255, 2); +SELECT set_byte('\x01020304'::bytea, 2, 0, 2); +SELECT set_byte('\x01020304'::bytea, 0, 0, 4); +SELECT set_byte('\x01020304'::bytea, 1, 255, 0); +SELECT set_byte('\x01020304'::bytea, 4, 0, 0); +SELECT set_byte('\x'::bytea, 0, 0, 0); +SELECT set_byte('\x01020304'::bytea, 0, 0, -1); -- error +SELECT set_byte('\x01020304'::bytea, -1, 0, 1); -- error +SELECT set_byte('\x01020304'::bytea, 5, 0, 0); -- error +SELECT set_byte('\x01020304'::bytea, 4, 0, 1); -- error +SELECT set_byte('\x01020304'::bytea, 3, 0, 2); -- error +SELECT set_byte('\x01020304'::bytea, 0, 0, 2147483647); -- error +SELECT set_byte('\x01020304'::bytea, 3, 0, 2147483647); -- error, end overflows int32 +SELECT set_byte('\x01020304'::bytea, 0, 256, 0); -- error, newvalue checked when count is 0 +SELECT set_byte('\x01020304'::bytea, 0, -1, 1); -- error +SELECT set_byte('\x01020304'::bytea, 0, 256, 1); -- error -- -- conversions between bytea and integer types -- 2.43.0