Allow explicit casting between bytea and uuid.

This enables the use of functions such as encode() and decode() with
UUID values, allowing them to be converted to and from alternative
formats like base64 or hex.

The cast maps the 16-byte internal representation of a UUID directly
to a bytea datum. This is more efficient than going through a text
forepresentation.

Bump catalog version.

Author:	Dagfinn Ilmari Mannsåker <ilmari@ilmari.org>
Co-authored-by: Aleksander Alekseev <aleksander@tigerdata.com>
Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
Reviewed-by: Andrey Borodin <x4mmm@yandex-team.ru>
Reviewed-by: Jelte Fennema-Nio <postgres@jeltef.nl>
Discussion: https://postgr.es/m/CAJ7c6TOramr1UTLcyB128LWMqita1Y7%3Darq3KHaU%3Dqikf5yKOQ%40mail.gmail.com
This commit is contained in:
Masahiko Sawada 2026-03-19 13:51:50 -07:00
parent 1811f1af98
commit ba21f5bf8a
7 changed files with 79 additions and 1 deletions

View file

@ -4439,6 +4439,17 @@ a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11
Output is always in the standard form.
</para>
<para>
It is possible to cast <type>uuid</type> values to and from type
<type>bytea</type>. This is useful for using functions such as
<function>encode()</function> and <function>decode()</function>
with UUID values. For example:
<programlisting>
encode('1ea3d64c-bc40-4cc3-84bb-6b11ee31e5c2'::uuid::bytea, 'base64')
decode('HqPWTLxATMOEu2sR7jHlwg==', 'base64')::uuid
</programlisting>
</para>
<para>
See <xref linkend="functions-uuid"/> for how to generate a UUID in
<productname>PostgreSQL</productname>.

View file

@ -28,6 +28,7 @@
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/sortsupport.h"
#include "utils/uuid.h"
#include "varatt.h"
/* GUC variable */
@ -1340,3 +1341,29 @@ int8_bytea(PG_FUNCTION_ARGS)
{
return int8send(fcinfo);
}
/* Cast bytea -> uuid */
Datum
bytea_uuid(PG_FUNCTION_ARGS)
{
bytea *v = PG_GETARG_BYTEA_PP(0);
int len = VARSIZE_ANY_EXHDR(v);
pg_uuid_t *uuid;
if (len != UUID_LEN)
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid input length for type %s", "uuid"),
errdetail("Expected %d bytes, got %d.", UUID_LEN, len)));
uuid = palloc_object(pg_uuid_t);
memcpy(uuid->data, VARDATA_ANY(v), UUID_LEN);
PG_RETURN_UUID_P(uuid);
}
/* Cast uuid -> bytea; can just use uuid_send() */
Datum
uuid_bytea(PG_FUNCTION_ARGS)
{
return uuid_send(fcinfo);
}

View file

@ -57,6 +57,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 202603171
#define CATALOG_VERSION_NO 202603191
#endif

View file

@ -362,6 +362,12 @@
{ castsource => 'bytea', casttarget => 'int8', castfunc => 'int8(bytea)',
castcontext => 'e', castmethod => 'f' },
# Allow explicit coercions between bytea and uuid type
{ castsource => 'bytea', casttarget => 'uuid', castfunc => 'uuid(bytea)',
castcontext => 'e', castmethod => 'f' },
{ castsource => 'uuid', casttarget => 'bytea', castfunc => 'bytea(uuid)',
castcontext => 'e', castmethod => 'f' },
# Allow explicit coercions between int4 and "char"
{ castsource => 'char', casttarget => 'int4', castfunc => 'int4(char)',
castcontext => 'e', castmethod => 'f' },

View file

@ -1208,6 +1208,13 @@
proname => 'int8', prorettype => 'int8', proargtypes => 'bytea',
prosrc => 'bytea_int8' },
{ oid => '9880', descr => 'convert uuid to bytea',
proname => 'bytea', prorettype => 'bytea', proargtypes => 'uuid',
prosrc => 'uuid_bytea' },
{ oid => '9881', descr => 'convert bytea to uuid',
proname => 'uuid', prorettype => 'uuid', proargtypes => 'bytea',
prosrc => 'bytea_uuid' },
{ oid => '449', descr => 'hash',
proname => 'hashint2', prorettype => 'int4', proargtypes => 'int2',
prosrc => 'hashint2' },

View file

@ -305,5 +305,27 @@ SELECT uuid_extract_timestamp('11111111-1111-1111-1111-111111111111'); -- null
(1 row)
-- casts
SELECT '5b35380a-7143-4912-9b55-f322699c6770'::uuid::bytea;
bytea
------------------------------------
\x5b35380a714349129b55f322699c6770
(1 row)
SELECT '\x019a2f859ced7225b99d9c55044a2563'::bytea::uuid;
uuid
--------------------------------------
019a2f85-9ced-7225-b99d-9c55044a2563
(1 row)
SELECT '\x1234567890abcdef'::bytea::uuid; -- error
ERROR: invalid input length for type uuid
DETAIL: Expected 16 bytes, got 8.
SELECT v = v::bytea::uuid as matched FROM gen_random_uuid() v;
matched
---------
t
(1 row)
-- clean up
DROP TABLE guid1, guid2, guid3 CASCADE;

View file

@ -146,6 +146,11 @@ SELECT uuid_extract_timestamp('017F22E2-79B0-7CC3-98C4-DC0C0C07398F') = 'Tuesday
SELECT uuid_extract_timestamp(gen_random_uuid()); -- null
SELECT uuid_extract_timestamp('11111111-1111-1111-1111-111111111111'); -- null
-- casts
SELECT '5b35380a-7143-4912-9b55-f322699c6770'::uuid::bytea;
SELECT '\x019a2f859ced7225b99d9c55044a2563'::bytea::uuid;
SELECT '\x1234567890abcdef'::bytea::uuid; -- error
SELECT v = v::bytea::uuid as matched FROM gen_random_uuid() v;
-- clean up
DROP TABLE guid1, guid2, guid3 CASCADE;