]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Allow explicit casting between bytea and uuid.
authorMasahiko Sawada <msawada@postgresql.org>
Thu, 19 Mar 2026 20:51:50 +0000 (13:51 -0700)
committerMasahiko Sawada <msawada@postgresql.org>
Thu, 19 Mar 2026 20:51:50 +0000 (13:51 -0700)
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

doc/src/sgml/datatype.sgml
src/backend/utils/adt/bytea.c
src/include/catalog/catversion.h
src/include/catalog/pg_cast.dat
src/include/catalog/pg_proc.dat
src/test/regress/expected/uuid.out
src/test/regress/sql/uuid.sql

index 3017c674040fbd96d58f16027f6863ed7986a22a..d8d91678e86d4d5a501e35c78757b6429828f5a1 100644 (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>.
index fd7662d41ee4fedff8a2c5808aa980bd86a9e7cd..39ed04ddae828b8f8d01fe2b436fa08fb04ffc1a 100644 (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);
+}
index 55a8fbbd509d32b1e8b66900da485f55f8c427f1..fd8472cb5a57431702dd296bc977ab63d7d7ded7 100644 (file)
@@ -57,6 +57,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     202603171
+#define CATALOG_VERSION_NO     202603191
 
 #endif
index 9b1cfb1b59063511bdf0fec45e2709128585fce3..a7b6d812c5ac917593e92a6fc9ad0bf95fdcd44b 100644 (file)
 { 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' },
index fc8d82665b881771e1ee627cb833b4cc963dbd00..84e7adde0e5b5b2c21734b3b83d7b3a14a73c576 100644 (file)
   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' },
index 95392003b86ed75dca7f9a2171f8e6fa2909f2de..d157ef7d0b33850ed334b9f0ef6e585c4b33a014 100644 (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;
index 465153a03415cf1bf6384fe1c403c1b45de5ecd8..f512f4dea1d1794e8d347f4019721e5d6f02c4cf 100644 (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;