PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
uuid-ossp.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * UUID generation functions using the BSD, E2FS or OSSP UUID library
4 *
5 * Copyright (c) 2007-2025, PostgreSQL Global Development Group
6 *
7 * Portions Copyright (c) 2009 Andrew Gierth
8 *
9 * contrib/uuid-ossp/uuid-ossp.c
10 *
11 *-------------------------------------------------------------------------
12 */
13
14#include "postgres.h"
15
16#include "common/cryptohash.h"
17#include "common/sha1.h"
18#include "fmgr.h"
19#include "port/pg_bswap.h"
20#include "utils/builtins.h"
21#include "utils/uuid.h"
22#include "varatt.h"
23
24/*
25 * It's possible that there's more than one uuid.h header file present.
26 * We expect configure to set the HAVE_ symbol for only the one we want.
27 *
28 * BSD includes a uuid_hash() function that conflicts with the one in
29 * builtins.h; we #define it out of the way.
30 */
31#define uuid_hash bsd_uuid_hash
32
33#if defined(HAVE_UUID_H)
34#include <uuid.h>
35#elif defined(HAVE_OSSP_UUID_H)
36#include <ossp/uuid.h>
37#elif defined(HAVE_UUID_UUID_H)
38#include <uuid/uuid.h>
39#else
40#error "please use configure's --with-uuid switch to select a UUID library"
41#endif
42
43#undef uuid_hash
44
45/* Check our UUID length against OSSP's; better both be 16 */
46#if defined(HAVE_UUID_OSSP) && (UUID_LEN != UUID_LEN_BIN)
47#error UUID length mismatch
48#endif
49
50/* Define some constants like OSSP's, to make the code more readable */
51#ifndef HAVE_UUID_OSSP
52#define UUID_MAKE_MC 0
53#define UUID_MAKE_V1 1
54#define UUID_MAKE_V2 2
55#define UUID_MAKE_V3 3
56#define UUID_MAKE_V4 4
57#define UUID_MAKE_V5 5
58#endif
59
60/*
61 * A DCE 1.1 compatible source representation of UUIDs, derived from
62 * the BSD implementation. BSD already has this; OSSP doesn't need it.
63 */
64#ifdef HAVE_UUID_E2FS
65typedef struct
66{
67 uint32_t time_low;
68 uint16_t time_mid;
69 uint16_t time_hi_and_version;
70 uint8_t clock_seq_hi_and_reserved;
71 uint8_t clock_seq_low;
72 uint8_t node[6];
74#else
75#define dce_uuid_t uuid_t
76#endif
77
78/* If not OSSP, we need some endianness-manipulation macros */
79#ifndef HAVE_UUID_OSSP
80
81#define UUID_TO_NETWORK(uu) \
82do { \
83 uu.time_low = pg_hton32(uu.time_low); \
84 uu.time_mid = pg_hton16(uu.time_mid); \
85 uu.time_hi_and_version = pg_hton16(uu.time_hi_and_version); \
86} while (0)
87
88#define UUID_TO_LOCAL(uu) \
89do { \
90 uu.time_low = pg_ntoh32(uu.time_low); \
91 uu.time_mid = pg_ntoh16(uu.time_mid); \
92 uu.time_hi_and_version = pg_ntoh16(uu.time_hi_and_version); \
93} while (0)
94
95#define UUID_V3_OR_V5(uu, v) \
96do { \
97 uu.time_hi_and_version &= 0x0FFF; \
98 uu.time_hi_and_version |= (v << 12); \
99 uu.clock_seq_hi_and_reserved &= 0x3F; \
100 uu.clock_seq_hi_and_reserved |= 0x80; \
101} while(0)
102
103#endif /* !HAVE_UUID_OSSP */
104
106 .name = "uuid-ossp",
107 .version = PG_VERSION
108);
109
115
121
122#ifdef HAVE_UUID_OSSP
123
124static void
125pguuid_complain(uuid_rc_t rc)
126{
127 char *err = uuid_error(rc);
128
129 if (err != NULL)
131 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
132 errmsg("OSSP uuid library failure: %s", err)));
133 else
135 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
136 errmsg("OSSP uuid library failure: error code %d", rc)));
137}
138
139/*
140 * We create a uuid_t object just once per session and re-use it for all
141 * operations in this module. OSSP UUID caches the system MAC address and
142 * other state in this object. Reusing the object has a number of benefits:
143 * saving the cycles needed to fetch the system MAC address over and over,
144 * reducing the amount of entropy we draw from /dev/urandom, and providing a
145 * positive guarantee that successive generated V1-style UUIDs don't collide.
146 * (On a machine fast enough to generate multiple UUIDs per microsecond,
147 * or whatever the system's wall-clock resolution is, we'd otherwise risk
148 * collisions whenever random initialization of the uuid_t's clock sequence
149 * value chanced to produce duplicates.)
150 *
151 * However: when we're doing V3 or V5 UUID creation, uuid_make needs two
152 * uuid_t objects, one holding the namespace UUID and one for the result.
153 * It's unspecified whether it's safe to use the same uuid_t for both cases,
154 * so let's cache a second uuid_t for use as the namespace holder object.
155 */
156static uuid_t *
157get_cached_uuid_t(int which)
158{
159 static uuid_t *cached_uuid[2] = {NULL, NULL};
160
161 if (cached_uuid[which] == NULL)
162 {
163 uuid_rc_t rc;
164
165 rc = uuid_create(&cached_uuid[which]);
166 if (rc != UUID_RC_OK)
167 {
168 cached_uuid[which] = NULL;
169 pguuid_complain(rc);
170 }
171 }
172 return cached_uuid[which];
173}
174
175static char *
176uuid_to_string(const uuid_t *uuid)
177{
178 char *buf = palloc(UUID_LEN_STR + 1);
179 void *ptr = buf;
180 size_t len = UUID_LEN_STR + 1;
181 uuid_rc_t rc;
182
183 rc = uuid_export(uuid, UUID_FMT_STR, &ptr, &len);
184 if (rc != UUID_RC_OK)
185 pguuid_complain(rc);
186
187 return buf;
188}
189
190
191static void
192string_to_uuid(const char *str, uuid_t *uuid)
193{
194 uuid_rc_t rc;
195
196 rc = uuid_import(uuid, UUID_FMT_STR, str, UUID_LEN_STR + 1);
197 if (rc != UUID_RC_OK)
198 pguuid_complain(rc);
199}
200
201
202static Datum
203special_uuid_value(const char *name)
204{
205 uuid_t *uuid = get_cached_uuid_t(0);
206 char *str;
207 uuid_rc_t rc;
208
209 rc = uuid_load(uuid, name);
210 if (rc != UUID_RC_OK)
211 pguuid_complain(rc);
212 str = uuid_to_string(uuid);
213
215}
216
217/* len is unused with OSSP, but we want to have the same number of args */
218static Datum
219uuid_generate_internal(int mode, const uuid_t *ns, const char *name, int len)
220{
221 uuid_t *uuid = get_cached_uuid_t(0);
222 char *str;
223 uuid_rc_t rc;
224
225 rc = uuid_make(uuid, mode, ns, name);
226 if (rc != UUID_RC_OK)
227 pguuid_complain(rc);
228 str = uuid_to_string(uuid);
229
231}
232
233
234static Datum
235uuid_generate_v35_internal(int mode, pg_uuid_t *ns, text *name)
236{
237 uuid_t *ns_uuid = get_cached_uuid_t(1);
238
240 UUIDPGetDatum(ns))),
241 ns_uuid);
242
244 ns_uuid,
246 0);
247}
248
249#else /* !HAVE_UUID_OSSP */
250
251static Datum
252uuid_generate_internal(int v, unsigned char *ns, const char *ptr, int len)
253{
254 char strbuf[40];
255
256 switch (v)
257 {
258 case 0: /* constant-value uuids */
259 strlcpy(strbuf, ptr, 37);
260 break;
261
262 case 1: /* time/node-based uuids */
263 {
264#ifdef HAVE_UUID_E2FS
265 uuid_t uu;
266
267 uuid_generate_time(uu);
268 uuid_unparse(uu, strbuf);
269
270 /*
271 * PTR, if set, replaces the trailing characters of the uuid;
272 * this is to support v1mc, where a random multicast MAC is
273 * used instead of the physical one
274 */
275 if (ptr && len <= 36)
276 strcpy(strbuf + (36 - len), ptr);
277#else /* BSD */
278 uuid_t uu;
279 uint32_t status = uuid_s_ok;
280 char *str = NULL;
281
282 uuid_create(&uu, &status);
283
284 if (status == uuid_s_ok)
285 {
286 uuid_to_string(&uu, &str, &status);
287 if (status == uuid_s_ok)
288 {
289 strlcpy(strbuf, str, 37);
290
291 /*
292 * In recent NetBSD, uuid_create() has started
293 * producing v4 instead of v1 UUIDs. Check the
294 * version field and complain if it's not v1.
295 */
296 if (strbuf[14] != '1')
298 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
299 /* translator: %c will be a hex digit */
300 errmsg("uuid_create() produced a version %c UUID instead of the expected version 1",
301 strbuf[14])));
302
303 /*
304 * PTR, if set, replaces the trailing characters of
305 * the uuid; this is to support v1mc, where a random
306 * multicast MAC is used instead of the physical one
307 */
308 if (ptr && len <= 36)
309 strcpy(strbuf + (36 - len), ptr);
310 }
311 free(str);
312 }
313
314 if (status != uuid_s_ok)
316 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
317 errmsg("uuid library failure: %d",
318 (int) status)));
319#endif
320 break;
321 }
322
323 case 3: /* namespace-based MD5 uuids */
324 case 5: /* namespace-based SHA1 uuids */
325 {
326 dce_uuid_t uu;
327#ifdef HAVE_UUID_BSD
328 uint32_t status = uuid_s_ok;
329 char *str = NULL;
330#endif
331
332 if (v == 3)
333 {
335
336 if (pg_cryptohash_init(ctx) < 0)
337 elog(ERROR, "could not initialize %s context: %s", "MD5",
339 if (pg_cryptohash_update(ctx, ns, sizeof(uu)) < 0 ||
340 pg_cryptohash_update(ctx, (unsigned char *) ptr, len) < 0)
341 elog(ERROR, "could not update %s context: %s", "MD5",
343 /* we assume sizeof MD5 result is 16, same as UUID size */
344 if (pg_cryptohash_final(ctx, (unsigned char *) &uu,
345 sizeof(uu)) < 0)
346 elog(ERROR, "could not finalize %s context: %s", "MD5",
349 }
350 else
351 {
353 unsigned char sha1result[SHA1_DIGEST_LENGTH];
354
355 if (pg_cryptohash_init(ctx) < 0)
356 elog(ERROR, "could not initialize %s context: %s", "SHA1",
358 if (pg_cryptohash_update(ctx, ns, sizeof(uu)) < 0 ||
359 pg_cryptohash_update(ctx, (unsigned char *) ptr, len) < 0)
360 elog(ERROR, "could not update %s context: %s", "SHA1",
362 if (pg_cryptohash_final(ctx, sha1result, sizeof(sha1result)) < 0)
363 elog(ERROR, "could not finalize %s context: %s", "SHA1",
366
367 memcpy(&uu, sha1result, sizeof(uu));
368 }
369
370 /* the calculated hash is using local order */
371 UUID_TO_NETWORK(uu);
372 UUID_V3_OR_V5(uu, v);
373
374#ifdef HAVE_UUID_E2FS
375 /* uuid_unparse expects local order */
376 UUID_TO_LOCAL(uu);
377 uuid_unparse((unsigned char *) &uu, strbuf);
378#else /* BSD */
379 uuid_to_string(&uu, &str, &status);
380
381 if (status == uuid_s_ok)
382 strlcpy(strbuf, str, 37);
383
384 free(str);
385
386 if (status != uuid_s_ok)
388 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
389 errmsg("uuid library failure: %d",
390 (int) status)));
391#endif
392 break;
393 }
394
395 case 4: /* random uuid */
396 default:
397 {
398#ifdef HAVE_UUID_E2FS
399 uuid_t uu;
400
401 uuid_generate_random(uu);
402 uuid_unparse(uu, strbuf);
403#else /* BSD */
404 snprintf(strbuf, sizeof(strbuf),
405 "%08lx-%04x-%04x-%04x-%04x%08lx",
406 (unsigned long) arc4random(),
407 (unsigned) (arc4random() & 0xffff),
408 (unsigned) ((arc4random() & 0xfff) | 0x4000),
409 (unsigned) ((arc4random() & 0x3fff) | 0x8000),
410 (unsigned) (arc4random() & 0xffff),
411 (unsigned long) arc4random());
412#endif
413 break;
414 }
415 }
416
418}
419
420#endif /* HAVE_UUID_OSSP */
421
422
423Datum
425{
426#ifdef HAVE_UUID_OSSP
427 return special_uuid_value("nil");
428#else
429 return uuid_generate_internal(0, NULL,
430 "00000000-0000-0000-0000-000000000000", 36);
431#endif
432}
433
434
435Datum
437{
438#ifdef HAVE_UUID_OSSP
439 return special_uuid_value("ns:DNS");
440#else
441 return uuid_generate_internal(0, NULL,
442 "6ba7b810-9dad-11d1-80b4-00c04fd430c8", 36);
443#endif
444}
445
446
447Datum
449{
450#ifdef HAVE_UUID_OSSP
451 return special_uuid_value("ns:URL");
452#else
453 return uuid_generate_internal(0, NULL,
454 "6ba7b811-9dad-11d1-80b4-00c04fd430c8", 36);
455#endif
456}
457
458
459Datum
461{
462#ifdef HAVE_UUID_OSSP
463 return special_uuid_value("ns:OID");
464#else
465 return uuid_generate_internal(0, NULL,
466 "6ba7b812-9dad-11d1-80b4-00c04fd430c8", 36);
467#endif
468}
469
470
471Datum
473{
474#ifdef HAVE_UUID_OSSP
475 return special_uuid_value("ns:X500");
476#else
477 return uuid_generate_internal(0, NULL,
478 "6ba7b814-9dad-11d1-80b4-00c04fd430c8", 36);
479#endif
480}
481
482
483Datum
485{
486 return uuid_generate_internal(UUID_MAKE_V1, NULL, NULL, 0);
487}
488
489
490Datum
492{
493#ifdef HAVE_UUID_OSSP
494 char *buf = NULL;
495#elif defined(HAVE_UUID_E2FS)
496 char strbuf[40];
497 char *buf;
498 uuid_t uu;
499
500 uuid_generate_random(uu);
501
502 /* set IEEE802 multicast and local-admin bits */
503 ((dce_uuid_t *) &uu)->node[0] |= 0x03;
504
505 uuid_unparse(uu, strbuf);
506 buf = strbuf + 24;
507#else /* BSD */
508 char buf[16];
509
510 /* set IEEE802 multicast and local-admin bits */
511 snprintf(buf, sizeof(buf), "-%04x%08lx",
512 (unsigned) ((arc4random() & 0xffff) | 0x0300),
513 (unsigned long) arc4random());
514#endif
515
517 buf, 13);
518}
519
520
521Datum
523{
526
527#ifdef HAVE_UUID_OSSP
528 return uuid_generate_v35_internal(UUID_MAKE_V3, ns, name);
529#else
530 return uuid_generate_internal(UUID_MAKE_V3, (unsigned char *) ns,
532#endif
533}
534
535
536Datum
538{
539 return uuid_generate_internal(UUID_MAKE_V4, NULL, NULL, 0);
540}
541
542
543Datum
545{
548
549#ifdef HAVE_UUID_OSSP
550 return uuid_generate_v35_internal(UUID_MAKE_V5, ns, name);
551#else
552 return uuid_generate_internal(UUID_MAKE_V5, (unsigned char *) ns,
554#endif
555}
const char * pg_cryptohash_error(pg_cryptohash_ctx *ctx)
Definition: cryptohash.c:254
int pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
Definition: cryptohash.c:136
pg_cryptohash_ctx * pg_cryptohash_create(pg_cryptohash_type type)
Definition: cryptohash.c:74
int pg_cryptohash_init(pg_cryptohash_ctx *ctx)
Definition: cryptohash.c:100
void pg_cryptohash_free(pg_cryptohash_ctx *ctx)
Definition: cryptohash.c:238
int pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest, size_t len)
Definition: cryptohash.c:172
@ PG_SHA1
Definition: cryptohash.h:22
@ PG_MD5
Definition: cryptohash.h:21
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
#define ereport(elevel,...)
Definition: elog.h:149
void err(int eval, const char *fmt,...)
Definition: err.c:43
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:309
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:682
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
const char * str
#define free(a)
Definition: header.h:65
void * palloc(Size size)
Definition: mcxt.c:1943
static PgChecksumMode mode
Definition: pg_checksums.c:55
const void size_t len
static char * buf
Definition: pg_test_fsync.c:72
#define snprintf
Definition: port.h:239
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
uintptr_t Datum
Definition: postgres.h:69
static char * DatumGetCString(Datum X)
Definition: postgres.h:340
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:355
#define SHA1_DIGEST_LENGTH
Definition: sha1.h:17
Definition: uuid.h:21
Definition: c.h:658
#define dce_uuid_t
Definition: uuid-ossp.c:75
Datum uuid_ns_oid(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:460
#define UUID_MAKE_V4
Definition: uuid-ossp.c:56
#define UUID_MAKE_V3
Definition: uuid-ossp.c:55
PG_MODULE_MAGIC_EXT(.name="uuid-ossp",.version=PG_VERSION)
Datum uuid_ns_dns(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:436
Datum uuid_nil(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:424
Datum uuid_ns_url(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:448
Datum uuid_generate_v1mc(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:491
Datum uuid_generate_v4(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:537
PG_FUNCTION_INFO_V1(uuid_nil)
#define UUID_MAKE_V1
Definition: uuid-ossp.c:53
Datum uuid_generate_v5(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:544
#define UUID_V3_OR_V5(uu, v)
Definition: uuid-ossp.c:95
Datum uuid_ns_x500(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:472
#define UUID_MAKE_MC
Definition: uuid-ossp.c:52
#define UUID_MAKE_V5
Definition: uuid-ossp.c:57
Datum uuid_generate_v1(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:484
static Datum uuid_generate_internal(int v, unsigned char *ns, const char *ptr, int len)
Definition: uuid-ossp.c:252
#define UUID_TO_LOCAL(uu)
Definition: uuid-ossp.c:88
#define UUID_TO_NETWORK(uu)
Definition: uuid-ossp.c:81
Datum uuid_generate_v3(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:522
static void string_to_uuid(const char *source, pg_uuid_t *uuid, Node *escontext)
Definition: uuid.c:131
Datum uuid_out(PG_FUNCTION_ARGS)
Definition: uuid.c:89
Datum uuid_in(PG_FUNCTION_ARGS)
Definition: uuid.c:78
static Datum UUIDPGetDatum(const pg_uuid_t *X)
Definition: uuid.h:27
#define PG_GETARG_UUID_P(X)
Definition: uuid.h:40
#define VARDATA_ANY(PTR)
Definition: varatt.h:324
#define VARSIZE_ANY_EXHDR(PTR)
Definition: varatt.h:317
char * text_to_cstring(const text *t)
Definition: varlena.c:225
const char * name