Add gen_random_uuid function

This adds a built-in function to generate UUIDs.

PostgreSQL hasn't had a built-in function to generate a UUID yet,
relying on external modules such as uuid-ossp and pgcrypto to provide
one.  Now that we have a strong random number generator built-in, we
can easily provide a version 4 (random) UUID generation function.

This patch takes the existing function gen_random_uuid() from pgcrypto
and makes it a built-in function.  The pgcrypto implementation now
internally redirects to the built-in one.

Reviewed-by: Fabien COELHO <coelho@cri.ensmp.fr>
Discussion: https://www.postgresql.org/message-id/6a65610c-46fc-2323-6b78-e8086340a325@2ndquadrant.com
This commit is contained in:
Peter Eisentraut 2019-07-14 14:30:27 +02:00
parent 565f339000
commit 5925e55498
11 changed files with 76 additions and 34 deletions

View File

@ -446,20 +446,8 @@ PG_FUNCTION_INFO_V1(pg_random_uuid);
Datum
pg_random_uuid(PG_FUNCTION_ARGS)
{
uint8 *buf = (uint8 *) palloc(UUID_LEN);
/* Generate random bits. */
if (!pg_strong_random(buf, UUID_LEN))
px_THROW_ERROR(PXE_NO_RANDOM);
/*
* Set magic numbers for a "version 4" (pseudorandom) UUID, see
* http://tools.ietf.org/html/rfc4122#section-4.4
*/
buf[6] = (buf[6] & 0x0f) | 0x40; /* "version" field */
buf[8] = (buf[8] & 0x3f) | 0x80; /* "variant" field */
PG_RETURN_UUID_P((pg_uuid_t *) buf);
/* redirect to built-in function */
return gen_random_uuid(fcinfo);
}
static void *

View File

@ -4195,16 +4195,8 @@ a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11
</para>
<para>
<productname>PostgreSQL</productname> provides storage and comparison
functions for UUIDs, but the core database does not include any
function for generating UUIDs, because no single algorithm is well
suited for every application. The <xref
linkend="uuid-ossp"/> module
provides functions that implement several standard algorithms.
The <xref linkend="pgcrypto"/> module also provides a generation
function for random UUIDs.
Alternatively, UUIDs could be generated by client applications or
other libraries invoked through a server-side function.
See <xref linkend="functions-uuid"/> for how to generate a UUID in
<productname>PostgreSQL</productname>.
</para>
</sect1>

View File

@ -10267,6 +10267,32 @@ CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple
</sect1>
<sect1 id="functions-uuid">
<title>UUID Functions</title>
<indexterm zone="datatype-uuid">
<primary>UUID</primary>
<secondary>generating</secondary>
</indexterm>
<indexterm>
<primary>gen_random_uuid</primary>
</indexterm>
<para>
<productname>PostgreSQL</productname> includes one function to generate a UUID:
<synopsis>
gen_random_uuid() returns uuid
</synopsis>
This function returns a version 4 (random) UUID. This is the most commonly
used type of UUID and is appropriate for most applications.
</para>
<para>
The <xref linkend="uuid-ossp"/> module provides additional functions that
implement other standard algorithms for generating UUIDs.
</para>
</sect1>
<sect1 id="functions-xml">

View File

@ -1132,7 +1132,8 @@ gen_random_bytes(count integer) returns bytea
gen_random_uuid() returns uuid
</synopsis>
<para>
Returns a version 4 (random) UUID.
Returns a version 4 (random) UUID. (Obsolete, this function is now also
included in core <productname>PostgreSQL</productname>.)
</para>
</sect2>

View File

@ -11,6 +11,9 @@
The <filename>uuid-ossp</filename> module provides functions to generate universally
unique identifiers (UUIDs) using one of several standard algorithms. There
are also functions to produce certain special UUID constants.
This module is only necessary for special requirements beyond what is
available in core <productname>PostgreSQL</productname>. See <xref
linkend="functions-uuid"/> for built-in ways to generate UUIDs.
</para>
<sect2>
@ -181,14 +184,6 @@ SELECT uuid_generate_v3(uuid_ns_url(), 'http://www.postgresql.org');
More than one of these libraries might be available on a particular
machine, so <filename>configure</filename> does not automatically choose one.
</para>
<note>
<para>
If you only need randomly-generated (version 4) UUIDs,
consider using the <function>gen_random_uuid()</function> function
from the <xref linkend="pgcrypto"/> module instead.
</para>
</note>
</sect2>
<sect2>

View File

@ -416,3 +416,23 @@ uuid_hash_extended(PG_FUNCTION_ARGS)
return hash_any_extended(key->data, UUID_LEN, PG_GETARG_INT64(1));
}
Datum
gen_random_uuid(PG_FUNCTION_ARGS)
{
pg_uuid_t *uuid = palloc(UUID_LEN);
if (!pg_strong_random(uuid, UUID_LEN))
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("could not generate random values")));
/*
* Set magic numbers for a "version 4" (pseudorandom) UUID, see
* http://tools.ietf.org/html/rfc4122#section-4.4
*/
uuid->data[6] = (uuid->data[6] & 0x0f) | 0x40; /* time_hi_and_version */
uuid->data[8] = (uuid->data[8] & 0x3f) | 0x80; /* clock_seq_hi_and_reserved */
PG_RETURN_UUID_P(uuid);
}

View File

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201907141
#define CATALOG_VERSION_NO 201907142
#endif

View File

@ -8376,6 +8376,9 @@
{ oid => '3412', descr => 'hash',
proname => 'uuid_hash_extended', prorettype => 'int8',
proargtypes => 'uuid int8', prosrc => 'uuid_hash_extended' },
{ oid => '3432', descr => 'generate random UUID',
proname => 'gen_random_uuid', proleakproof => 't', prorettype => 'uuid',
proargtypes => '', prosrc => 'gen_random_uuid' },
# pg_lsn
{ oid => '3229', descr => 'I/O',

View File

@ -742,6 +742,7 @@ sha224(bytea)
sha256(bytea)
sha384(bytea)
sha512(bytea)
gen_random_uuid()
starts_with(text,text)
macaddr8_eq(macaddr8,macaddr8)
macaddr8_lt(macaddr8,macaddr8)

View File

@ -145,5 +145,15 @@ SELECT COUNT(*) FROM guid1 g1 LEFT JOIN guid2 g2 ON g1.guid_field = g2.guid_fiel
1
(1 row)
-- generation test
TRUNCATE guid1;
INSERT INTO guid1 (guid_field) VALUES (gen_random_uuid());
INSERT INTO guid1 (guid_field) VALUES (gen_random_uuid());
SELECT count(DISTINCT guid_field) FROM guid1;
count
-------
2
(1 row)
-- clean up
DROP TABLE guid1, guid2 CASCADE;

View File

@ -75,5 +75,11 @@ INSERT INTO guid2(guid_field) VALUES('3f3e3c3b3a3039383736353433a2313e');
SELECT COUNT(*) FROM guid1 g1 INNER JOIN guid2 g2 ON g1.guid_field = g2.guid_field;
SELECT COUNT(*) FROM guid1 g1 LEFT JOIN guid2 g2 ON g1.guid_field = g2.guid_field WHERE g2.guid_field IS NULL;
-- generation test
TRUNCATE guid1;
INSERT INTO guid1 (guid_field) VALUES (gen_random_uuid());
INSERT INTO guid1 (guid_field) VALUES (gen_random_uuid());
SELECT count(DISTINCT guid_field) FROM guid1;
-- clean up
DROP TABLE guid1, guid2 CASCADE;