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