PostgreSQL Source Code git master
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
112
118
119#ifdef HAVE_UUID_OSSP
120
121static void
122pguuid_complain(uuid_rc_t rc)
123{
124 char *err = uuid_error(rc);
125
126 if (err != NULL)
128 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
129 errmsg("OSSP uuid library failure: %s", err)));
130 else
132 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
133 errmsg("OSSP uuid library failure: error code %d", rc)));
134}
135
136/*
137 * We create a uuid_t object just once per session and re-use it for all
138 * operations in this module. OSSP UUID caches the system MAC address and
139 * other state in this object. Reusing the object has a number of benefits:
140 * saving the cycles needed to fetch the system MAC address over and over,
141 * reducing the amount of entropy we draw from /dev/urandom, and providing a
142 * positive guarantee that successive generated V1-style UUIDs don't collide.
143 * (On a machine fast enough to generate multiple UUIDs per microsecond,
144 * or whatever the system's wall-clock resolution is, we'd otherwise risk
145 * collisions whenever random initialization of the uuid_t's clock sequence
146 * value chanced to produce duplicates.)
147 *
148 * However: when we're doing V3 or V5 UUID creation, uuid_make needs two
149 * uuid_t objects, one holding the namespace UUID and one for the result.
150 * It's unspecified whether it's safe to use the same uuid_t for both cases,
151 * so let's cache a second uuid_t for use as the namespace holder object.
152 */
153static uuid_t *
154get_cached_uuid_t(int which)
155{
156 static uuid_t *cached_uuid[2] = {NULL, NULL};
157
158 if (cached_uuid[which] == NULL)
159 {
160 uuid_rc_t rc;
161
162 rc = uuid_create(&cached_uuid[which]);
163 if (rc != UUID_RC_OK)
164 {
165 cached_uuid[which] = NULL;
166 pguuid_complain(rc);
167 }
168 }
169 return cached_uuid[which];
170}
171
172static char *
173uuid_to_string(const uuid_t *uuid)
174{
175 char *buf = palloc(UUID_LEN_STR + 1);
176 void *ptr = buf;
177 size_t len = UUID_LEN_STR + 1;
178 uuid_rc_t rc;
179
180 rc = uuid_export(uuid, UUID_FMT_STR, &ptr, &len);
181 if (rc != UUID_RC_OK)
182 pguuid_complain(rc);
183
184 return buf;
185}
186
187
188static void
189string_to_uuid(const char *str, uuid_t *uuid)
190{
191 uuid_rc_t rc;
192
193 rc = uuid_import(uuid, UUID_FMT_STR, str, UUID_LEN_STR + 1);
194 if (rc != UUID_RC_OK)
195 pguuid_complain(rc);
196}
197
198
199static Datum
200special_uuid_value(const char *name)
201{
202 uuid_t *uuid = get_cached_uuid_t(0);
203 char *str;
204 uuid_rc_t rc;
205
206 rc = uuid_load(uuid, name);
207 if (rc != UUID_RC_OK)
208 pguuid_complain(rc);
209 str = uuid_to_string(uuid);
210
212}
213
214/* len is unused with OSSP, but we want to have the same number of args */
215static Datum
216uuid_generate_internal(int mode, const uuid_t *ns, const char *name, int len)
217{
218 uuid_t *uuid = get_cached_uuid_t(0);
219 char *str;
220 uuid_rc_t rc;
221
222 rc = uuid_make(uuid, mode, ns, name);
223 if (rc != UUID_RC_OK)
224 pguuid_complain(rc);
225 str = uuid_to_string(uuid);
226
228}
229
230
231static Datum
232uuid_generate_v35_internal(int mode, pg_uuid_t *ns, text *name)
233{
234 uuid_t *ns_uuid = get_cached_uuid_t(1);
235
237 UUIDPGetDatum(ns))),
238 ns_uuid);
239
241 ns_uuid,
243 0);
244}
245
246#else /* !HAVE_UUID_OSSP */
247
248static Datum
249uuid_generate_internal(int v, unsigned char *ns, const char *ptr, int len)
250{
251 char strbuf[40];
252
253 switch (v)
254 {
255 case 0: /* constant-value uuids */
256 strlcpy(strbuf, ptr, 37);
257 break;
258
259 case 1: /* time/node-based uuids */
260 {
261#ifdef HAVE_UUID_E2FS
262 uuid_t uu;
263
264 uuid_generate_time(uu);
265 uuid_unparse(uu, strbuf);
266
267 /*
268 * PTR, if set, replaces the trailing characters of the uuid;
269 * this is to support v1mc, where a random multicast MAC is
270 * used instead of the physical one
271 */
272 if (ptr && len <= 36)
273 strcpy(strbuf + (36 - len), ptr);
274#else /* BSD */
275 uuid_t uu;
276 uint32_t status = uuid_s_ok;
277 char *str = NULL;
278
279 uuid_create(&uu, &status);
280
281 if (status == uuid_s_ok)
282 {
283 uuid_to_string(&uu, &str, &status);
284 if (status == uuid_s_ok)
285 {
286 strlcpy(strbuf, str, 37);
287
288 /*
289 * In recent NetBSD, uuid_create() has started
290 * producing v4 instead of v1 UUIDs. Check the
291 * version field and complain if it's not v1.
292 */
293 if (strbuf[14] != '1')
295 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
296 /* translator: %c will be a hex digit */
297 errmsg("uuid_create() produced a version %c UUID instead of the expected version 1",
298 strbuf[14])));
299
300 /*
301 * PTR, if set, replaces the trailing characters of
302 * the uuid; this is to support v1mc, where a random
303 * multicast MAC is used instead of the physical one
304 */
305 if (ptr && len <= 36)
306 strcpy(strbuf + (36 - len), ptr);
307 }
308 free(str);
309 }
310
311 if (status != uuid_s_ok)
313 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
314 errmsg("uuid library failure: %d",
315 (int) status)));
316#endif
317 break;
318 }
319
320 case 3: /* namespace-based MD5 uuids */
321 case 5: /* namespace-based SHA1 uuids */
322 {
323 dce_uuid_t uu;
324#ifdef HAVE_UUID_BSD
325 uint32_t status = uuid_s_ok;
326 char *str = NULL;
327#endif
328
329 if (v == 3)
330 {
332
333 if (pg_cryptohash_init(ctx) < 0)
334 elog(ERROR, "could not initialize %s context: %s", "MD5",
336 if (pg_cryptohash_update(ctx, ns, sizeof(uu)) < 0 ||
337 pg_cryptohash_update(ctx, (unsigned char *) ptr, len) < 0)
338 elog(ERROR, "could not update %s context: %s", "MD5",
340 /* we assume sizeof MD5 result is 16, same as UUID size */
341 if (pg_cryptohash_final(ctx, (unsigned char *) &uu,
342 sizeof(uu)) < 0)
343 elog(ERROR, "could not finalize %s context: %s", "MD5",
346 }
347 else
348 {
350 unsigned char sha1result[SHA1_DIGEST_LENGTH];
351
352 if (pg_cryptohash_init(ctx) < 0)
353 elog(ERROR, "could not initialize %s context: %s", "SHA1",
355 if (pg_cryptohash_update(ctx, ns, sizeof(uu)) < 0 ||
356 pg_cryptohash_update(ctx, (unsigned char *) ptr, len) < 0)
357 elog(ERROR, "could not update %s context: %s", "SHA1",
359 if (pg_cryptohash_final(ctx, sha1result, sizeof(sha1result)) < 0)
360 elog(ERROR, "could not finalize %s context: %s", "SHA1",
363
364 memcpy(&uu, sha1result, sizeof(uu));
365 }
366
367 /* the calculated hash is using local order */
368 UUID_TO_NETWORK(uu);
369 UUID_V3_OR_V5(uu, v);
370
371#ifdef HAVE_UUID_E2FS
372 /* uuid_unparse expects local order */
373 UUID_TO_LOCAL(uu);
374 uuid_unparse((unsigned char *) &uu, strbuf);
375#else /* BSD */
376 uuid_to_string(&uu, &str, &status);
377
378 if (status == uuid_s_ok)
379 strlcpy(strbuf, str, 37);
380
381 free(str);
382
383 if (status != uuid_s_ok)
385 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
386 errmsg("uuid library failure: %d",
387 (int) status)));
388#endif
389 break;
390 }
391
392 case 4: /* random uuid */
393 default:
394 {
395#ifdef HAVE_UUID_E2FS
396 uuid_t uu;
397
398 uuid_generate_random(uu);
399 uuid_unparse(uu, strbuf);
400#else /* BSD */
401 snprintf(strbuf, sizeof(strbuf),
402 "%08lx-%04x-%04x-%04x-%04x%08lx",
403 (unsigned long) arc4random(),
404 (unsigned) (arc4random() & 0xffff),
405 (unsigned) ((arc4random() & 0xfff) | 0x4000),
406 (unsigned) ((arc4random() & 0x3fff) | 0x8000),
407 (unsigned) (arc4random() & 0xffff),
408 (unsigned long) arc4random());
409#endif
410 break;
411 }
412 }
413
415}
416
417#endif /* HAVE_UUID_OSSP */
418
419
420Datum
422{
423#ifdef HAVE_UUID_OSSP
424 return special_uuid_value("nil");
425#else
426 return uuid_generate_internal(0, NULL,
427 "00000000-0000-0000-0000-000000000000", 36);
428#endif
429}
430
431
432Datum
434{
435#ifdef HAVE_UUID_OSSP
436 return special_uuid_value("ns:DNS");
437#else
438 return uuid_generate_internal(0, NULL,
439 "6ba7b810-9dad-11d1-80b4-00c04fd430c8", 36);
440#endif
441}
442
443
444Datum
446{
447#ifdef HAVE_UUID_OSSP
448 return special_uuid_value("ns:URL");
449#else
450 return uuid_generate_internal(0, NULL,
451 "6ba7b811-9dad-11d1-80b4-00c04fd430c8", 36);
452#endif
453}
454
455
456Datum
458{
459#ifdef HAVE_UUID_OSSP
460 return special_uuid_value("ns:OID");
461#else
462 return uuid_generate_internal(0, NULL,
463 "6ba7b812-9dad-11d1-80b4-00c04fd430c8", 36);
464#endif
465}
466
467
468Datum
470{
471#ifdef HAVE_UUID_OSSP
472 return special_uuid_value("ns:X500");
473#else
474 return uuid_generate_internal(0, NULL,
475 "6ba7b814-9dad-11d1-80b4-00c04fd430c8", 36);
476#endif
477}
478
479
480Datum
482{
483 return uuid_generate_internal(UUID_MAKE_V1, NULL, NULL, 0);
484}
485
486
487Datum
489{
490#ifdef HAVE_UUID_OSSP
491 char *buf = NULL;
492#elif defined(HAVE_UUID_E2FS)
493 char strbuf[40];
494 char *buf;
495 uuid_t uu;
496
497 uuid_generate_random(uu);
498
499 /* set IEEE802 multicast and local-admin bits */
500 ((dce_uuid_t *) &uu)->node[0] |= 0x03;
501
502 uuid_unparse(uu, strbuf);
503 buf = strbuf + 24;
504#else /* BSD */
505 char buf[16];
506
507 /* set IEEE802 multicast and local-admin bits */
508 snprintf(buf, sizeof(buf), "-%04x%08lx",
509 (unsigned) ((arc4random() & 0xffff) | 0x0300),
510 (unsigned long) arc4random());
511#endif
512
514 buf, 13);
515}
516
517
518Datum
520{
523
524#ifdef HAVE_UUID_OSSP
525 return uuid_generate_v35_internal(UUID_MAKE_V3, ns, name);
526#else
527 return uuid_generate_internal(UUID_MAKE_V3, (unsigned char *) ns,
529#endif
530}
531
532
533Datum
535{
536 return uuid_generate_internal(UUID_MAKE_V4, NULL, NULL, 0);
537}
538
539
540Datum
542{
545
546#ifdef HAVE_UUID_OSSP
547 return uuid_generate_v35_internal(UUID_MAKE_V5, ns, name);
548#else
549 return uuid_generate_internal(UUID_MAKE_V5, (unsigned char *) ns,
551#endif
552}
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:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#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:641
#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:1317
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:644
#define dce_uuid_t
Definition: uuid-ossp.c:75
Datum uuid_ns_oid(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:457
#define UUID_MAKE_V4
Definition: uuid-ossp.c:56
#define UUID_MAKE_V3
Definition: uuid-ossp.c:55
Datum uuid_ns_dns(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:433
Datum uuid_nil(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:421
PG_MODULE_MAGIC
Definition: uuid-ossp.c:105
Datum uuid_ns_url(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:445
Datum uuid_generate_v1mc(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:488
Datum uuid_generate_v4(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:534
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:541
#define UUID_V3_OR_V5(uu, v)
Definition: uuid-ossp.c:95
Datum uuid_ns_x500(PG_FUNCTION_ARGS)
Definition: uuid-ossp.c:469
#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:481
static Datum uuid_generate_internal(int v, unsigned char *ns, const char *ptr, int len)
Definition: uuid-ossp.c:249
#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:519
static void string_to_uuid(const char *source, pg_uuid_t *uuid, Node *escontext)
Definition: uuid.c:127
Datum uuid_out(PG_FUNCTION_ARGS)
Definition: uuid.c:85
Datum uuid_in(PG_FUNCTION_ARGS)
Definition: uuid.c:74
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:217
const char * name