Allow casting between bytea and integer types.

This allows smallint, integer, and bigint values to be cast to and
from bytea. The bytea value is the two's complement representation of
the integer, with the most significant byte first. For example:

  1234::bytea -> \x000004d2
  (-1234)::bytea -> \xfffffb2e

Author: Aleksander Alekseev <aleksander@timescale.com>
Reviewed-by: Joel Jacobson <joel@compiler.org>
Reviewed-by: Yugo Nagata <nagata@sraoss.co.jp>
Reviewed-by: Peter Eisentraut <peter@eisentraut.org>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Reviewed-by: Dean Rasheed <dean.a.rasheed@gmail.com>
Discussion: https://postgr.es/m/CAJ7c6TPtOp6%2BkFX5QX3fH1SVr7v65uHr-7yEJ%3DGMGQi5uhGtcA%40mail.gmail.com
This commit is contained in:
Dean Rasheed 2025-03-07 09:31:18 +00:00
parent d611f8b158
commit 6da469bada
8 changed files with 281 additions and 1 deletions

View File

@ -5035,6 +5035,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three');
</variablelist>
</para>
<para>
In addition, it is possible to cast integral values to and from type
<type>bytea</type>. Casting an integer to <type>bytea</type> produces
2, 4, or 8 bytes, depending on the width of the integer type. The result
is the two's complement representation of the integer, with the most
significant byte first. Some examples:
<programlisting>
1234::smallint::bytea <lineannotation>\x04d2</lineannotation>
cast(1234 as bytea) <lineannotation>\x000004d2</lineannotation>
cast(-1234 as bytea) <lineannotation>\xfffffb2e</lineannotation>
'\x8000'::bytea::smallint <lineannotation>-32768</lineannotation>
'\x8000'::bytea::integer <lineannotation>32768</lineannotation>
</programlisting>
Casting a <type>bytea</type> to an integer will raise an error if the
length of the <type>bytea</type> exceeds the width of the integer type.
</para>
<para>
See also the aggregate function <function>string_agg</function> in
<xref linkend="functions-aggregate"/> and the large object functions

View File

@ -4057,6 +4057,102 @@ bytea_sortsupport(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
/* Cast bytea -> int2 */
Datum
bytea_int2(PG_FUNCTION_ARGS)
{
bytea *v = PG_GETARG_BYTEA_PP(0);
int len = VARSIZE_ANY_EXHDR(v);
uint16 result;
/* Check that the byte array is not too long */
if (len > sizeof(result))
ereport(ERROR,
errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range"));
/* Convert it to an integer; most significant bytes come first */
result = 0;
for (int i = 0; i < len; i++)
{
result <<= BITS_PER_BYTE;
result |= ((unsigned char *) VARDATA_ANY(v))[i];
}
PG_RETURN_INT16(result);
}
/* Cast bytea -> int4 */
Datum
bytea_int4(PG_FUNCTION_ARGS)
{
bytea *v = PG_GETARG_BYTEA_PP(0);
int len = VARSIZE_ANY_EXHDR(v);
uint32 result;
/* Check that the byte array is not too long */
if (len > sizeof(result))
ereport(ERROR,
errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range"));
/* Convert it to an integer; most significant bytes come first */
result = 0;
for (int i = 0; i < len; i++)
{
result <<= BITS_PER_BYTE;
result |= ((unsigned char *) VARDATA_ANY(v))[i];
}
PG_RETURN_INT32(result);
}
/* Cast bytea -> int8 */
Datum
bytea_int8(PG_FUNCTION_ARGS)
{
bytea *v = PG_GETARG_BYTEA_PP(0);
int len = VARSIZE_ANY_EXHDR(v);
uint64 result;
/* Check that the byte array is not too long */
if (len > sizeof(result))
ereport(ERROR,
errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("bigint out of range"));
/* Convert it to an integer; most significant bytes come first */
result = 0;
for (int i = 0; i < len; i++)
{
result <<= BITS_PER_BYTE;
result |= ((unsigned char *) VARDATA_ANY(v))[i];
}
PG_RETURN_INT64(result);
}
/* Cast int2 -> bytea; can just use int2send() */
Datum
int2_bytea(PG_FUNCTION_ARGS)
{
return int2send(fcinfo);
}
/* Cast int4 -> bytea; can just use int4send() */
Datum
int4_bytea(PG_FUNCTION_ARGS)
{
return int4send(fcinfo);
}
/* Cast int8 -> bytea; can just use int8send() */
Datum
int8_bytea(PG_FUNCTION_ARGS)
{
return int8send(fcinfo);
}
/*
* appendStringInfoText
*

View File

@ -57,6 +57,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 202503031
#define CATALOG_VERSION_NO 202503071
#endif

View File

@ -320,6 +320,20 @@
{ castsource => 'varchar', casttarget => 'name', castfunc => 'name(varchar)',
castcontext => 'i', castmethod => 'f' },
# Allow explicit coercions between bytea and integer types
{ castsource => 'int2', casttarget => 'bytea', castfunc => 'bytea(int2)',
castcontext => 'e', castmethod => 'f' },
{ castsource => 'int4', casttarget => 'bytea', castfunc => 'bytea(int4)',
castcontext => 'e', castmethod => 'f' },
{ castsource => 'int8', casttarget => 'bytea', castfunc => 'bytea(int8)',
castcontext => 'e', castmethod => 'f' },
{ castsource => 'bytea', casttarget => 'int2', castfunc => 'int2(bytea)',
castcontext => 'e', castmethod => 'f' },
{ castsource => 'bytea', casttarget => 'int4', castfunc => 'int4(bytea)',
castcontext => 'e', castmethod => 'f' },
{ castsource => 'bytea', casttarget => 'int8', castfunc => 'int8(bytea)',
castcontext => 'e', castmethod => 'f' },
# Allow explicit coercions between int4 and "char"
{ castsource => 'char', casttarget => 'int4', castfunc => 'int4(char)',
castcontext => 'e', castmethod => 'f' },

View File

@ -1165,6 +1165,25 @@
proname => 'name', proleakproof => 't', prorettype => 'name',
proargtypes => 'bpchar', prosrc => 'bpchar_name' },
{ oid => '8577', descr => 'convert int2 to bytea',
proname => 'bytea', proleakproof => 't', prorettype => 'bytea',
proargtypes => 'int2', prosrc => 'int2_bytea' },
{ oid => '8578', descr => 'convert int4 to bytea',
proname => 'bytea', proleakproof => 't', prorettype => 'bytea',
proargtypes => 'int4', prosrc => 'int4_bytea' },
{ oid => '8579', descr => 'convert int8 to bytea',
proname => 'bytea', proleakproof => 't', prorettype => 'bytea',
proargtypes => 'int8', prosrc => 'int8_bytea' },
{ oid => '8580', descr => 'convert bytea to int2',
proname => 'int2', prorettype => 'int2',
proargtypes => 'bytea', prosrc => 'bytea_int2' },
{ oid => '8581', descr => 'convert bytea to int4',
proname => 'int4', prorettype => 'int4',
proargtypes => 'bytea', prosrc => 'bytea_int4' },
{ oid => '8582', descr => 'convert bytea to int8',
proname => 'int8', prorettype => 'int8',
proargtypes => 'bytea', prosrc => 'bytea_int8' },
{ oid => '449', descr => 'hash',
proname => 'hashint2', prorettype => 'int4', proargtypes => 'int2',
prosrc => 'hashint2' },

View File

@ -875,6 +875,9 @@ uuid_extract_timestamp(uuid)
uuid_extract_version(uuid)
crc32(bytea)
crc32c(bytea)
bytea(smallint)
bytea(integer)
bytea(bigint)
bytea_larger(bytea,bytea)
bytea_smaller(bytea,bytea)
-- Check that functions without argument are not marked as leakproof.

View File

@ -2358,6 +2358,108 @@ SELECT set_byte('\x1234567890abcdef00'::bytea, 7, 11);
SELECT set_byte('\x1234567890abcdef00'::bytea, 99, 11); -- error
ERROR: index 99 out of valid range, 0..8
--
-- conversions between bytea and integer types
--
SELECT 0x1234::int2::bytea AS "\x1234", (-0x1234)::int2::bytea AS "\xedcc";
\x1234 | \xedcc
--------+--------
\x1234 | \xedcc
(1 row)
SELECT 0x12345678::int4::bytea AS "\x12345678", (-0x12345678)::int4::bytea AS "\xedcba988";
\x12345678 | \xedcba988
------------+------------
\x12345678 | \xedcba988
(1 row)
SELECT 0x1122334455667788::int8::bytea AS "\x1122334455667788",
(-0x1122334455667788)::int8::bytea AS "\xeeddccbbaa998878";
\x1122334455667788 | \xeeddccbbaa998878
--------------------+--------------------
\x1122334455667788 | \xeeddccbbaa998878
(1 row)
SELECT ''::bytea::int2 AS "0";
0
---
0
(1 row)
SELECT '\x12'::bytea::int2 AS "18";
18
----
18
(1 row)
SELECT '\x1234'::bytea::int2 AS "4460";
4460
------
4660
(1 row)
SELECT '\x123456'::bytea::int2; -- error
ERROR: smallint out of range
SELECT ''::bytea::int4 AS "0";
0
---
0
(1 row)
SELECT '\x12'::bytea::int4 AS "18";
18
----
18
(1 row)
SELECT '\x12345678'::bytea::int4 AS "305419896";
305419896
-----------
305419896
(1 row)
SELECT '\x123456789A'::bytea::int4; -- error
ERROR: integer out of range
SELECT ''::bytea::int8 AS "0";
0
---
0
(1 row)
SELECT '\x12'::bytea::int8 AS "18";
18
----
18
(1 row)
SELECT '\x1122334455667788'::bytea::int8 AS "1234605616436508552";
1234605616436508552
---------------------
1234605616436508552
(1 row)
SELECT '\x112233445566778899'::bytea::int8; -- error
ERROR: bigint out of range
-- min/max integer values
SELECT '\x8000'::bytea::int2 AS "-32768", '\x7FFF'::bytea::int2 AS "32767";
-32768 | 32767
--------+-------
-32768 | 32767
(1 row)
SELECT '\x80000000'::bytea::int4 AS "-2147483648", '\x7FFFFFFF'::bytea::int4 AS "2147483647";
-2147483648 | 2147483647
-------------+------------
-2147483648 | 2147483647
(1 row)
SELECT '\x8000000000000000'::bytea::int8 AS "-9223372036854775808",
'\x7FFFFFFFFFFFFFFF'::bytea::int8 AS "9223372036854775807";
-9223372036854775808 | 9223372036854775807
----------------------+---------------------
-9223372036854775808 | 9223372036854775807
(1 row)
--
-- test behavior of escape_string_warning and standard_conforming_strings options
--

View File

@ -751,6 +751,35 @@ SELECT get_byte('\x1234567890abcdef00'::bytea, 99); -- error
SELECT set_byte('\x1234567890abcdef00'::bytea, 7, 11);
SELECT set_byte('\x1234567890abcdef00'::bytea, 99, 11); -- error
--
-- conversions between bytea and integer types
--
SELECT 0x1234::int2::bytea AS "\x1234", (-0x1234)::int2::bytea AS "\xedcc";
SELECT 0x12345678::int4::bytea AS "\x12345678", (-0x12345678)::int4::bytea AS "\xedcba988";
SELECT 0x1122334455667788::int8::bytea AS "\x1122334455667788",
(-0x1122334455667788)::int8::bytea AS "\xeeddccbbaa998878";
SELECT ''::bytea::int2 AS "0";
SELECT '\x12'::bytea::int2 AS "18";
SELECT '\x1234'::bytea::int2 AS "4460";
SELECT '\x123456'::bytea::int2; -- error
SELECT ''::bytea::int4 AS "0";
SELECT '\x12'::bytea::int4 AS "18";
SELECT '\x12345678'::bytea::int4 AS "305419896";
SELECT '\x123456789A'::bytea::int4; -- error
SELECT ''::bytea::int8 AS "0";
SELECT '\x12'::bytea::int8 AS "18";
SELECT '\x1122334455667788'::bytea::int8 AS "1234605616436508552";
SELECT '\x112233445566778899'::bytea::int8; -- error
-- min/max integer values
SELECT '\x8000'::bytea::int2 AS "-32768", '\x7FFF'::bytea::int2 AS "32767";
SELECT '\x80000000'::bytea::int4 AS "-2147483648", '\x7FFFFFFF'::bytea::int4 AS "2147483647";
SELECT '\x8000000000000000'::bytea::int8 AS "-9223372036854775808",
'\x7FFFFFFFFFFFFFFF'::bytea::int8 AS "9223372036854775807";
--
-- test behavior of escape_string_warning and standard_conforming_strings options
--