]> granicus.if.org Git - postgresql/commitdiff
When using the OSSP UUID library, cache its uuid_t state object.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 29 May 2014 17:51:02 +0000 (13:51 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 29 May 2014 17:51:02 +0000 (13:51 -0400)
The original coding in contrib/uuid-ossp created and destroyed a uuid_t
object (or, in some cases, even two of them) each time it was called.
This is not the intended usage: you're supposed to keep the uuid_t object
around so that the library can cache its state across uses.  (Other UUID
libraries seem to keep equivalent state behind-the-scenes in static
variables, but OSSP chose differently.)  Aside from being quite inefficient,
creating a new uuid_t loses knowledge of the previously generated UUID,
which in theory could result in duplicate V1-style UUIDs being created
on sufficiently fast machines.

On at least some platforms, creating a new uuid_t also draws some entropy
from /dev/urandom, leaving less for the rest of the system.  This seems
sufficiently unpleasant to justify back-patching this change.

contrib/uuid-ossp/uuid-ossp.c

index 88da168388916b19db5d980fd7c0d6b53b0cec6d..9e9905bfdefb7a060bfd24fea3583e11a2379e9c 100644 (file)
@@ -141,6 +141,42 @@ pguuid_complain(uuid_rc_t rc)
                                 errmsg("OSSP uuid library failure: error code %d", rc)));
 }
 
+/*
+ * We create a uuid_t object just once per session and re-use it for all
+ * operations in this module.  OSSP UUID caches the system MAC address and
+ * other state in this object.  Reusing the object has a number of benefits:
+ * saving the cycles needed to fetch the system MAC address over and over,
+ * reducing the amount of entropy we draw from /dev/urandom, and providing a
+ * positive guarantee that successive generated V1-style UUIDs don't collide.
+ * (On a machine fast enough to generate multiple UUIDs per microsecond,
+ * or whatever the system's wall-clock resolution is, we'd otherwise risk
+ * collisions whenever random initialization of the uuid_t's clock sequence
+ * value chanced to produce duplicates.)
+ *
+ * However: when we're doing V3 or V5 UUID creation, uuid_make needs two
+ * uuid_t objects, one holding the namespace UUID and one for the result.
+ * It's unspecified whether it's safe to use the same uuid_t for both cases,
+ * so let's cache a second uuid_t for use as the namespace holder object.
+ */
+static uuid_t *
+get_cached_uuid_t(int which)
+{
+       static uuid_t *cached_uuid[2] = {NULL, NULL};
+
+       if (cached_uuid[which] == NULL)
+       {
+               uuid_rc_t       rc;
+
+               rc = uuid_create(&cached_uuid[which]);
+               if (rc != UUID_RC_OK)
+               {
+                       cached_uuid[which] = NULL;
+                       pguuid_complain(rc);
+               }
+       }
+       return cached_uuid[which];
+}
+
 static char *
 uuid_to_string(const uuid_t *uuid)
 {
@@ -171,20 +207,14 @@ string_to_uuid(const char *str, uuid_t *uuid)
 static Datum
 special_uuid_value(const char *name)
 {
-       uuid_t     *uuid;
+       uuid_t     *uuid = get_cached_uuid_t(0);
        char       *str;
        uuid_rc_t       rc;
 
-       rc = uuid_create(&uuid);
-       if (rc != UUID_RC_OK)
-               pguuid_complain(rc);
        rc = uuid_load(uuid, name);
        if (rc != UUID_RC_OK)
                pguuid_complain(rc);
        str = uuid_to_string(uuid);
-       rc = uuid_destroy(uuid);
-       if (rc != UUID_RC_OK)
-               pguuid_complain(rc);
 
        return DirectFunctionCall1(uuid_in, CStringGetDatum(str));
 }
@@ -193,20 +223,14 @@ special_uuid_value(const char *name)
 static Datum
 uuid_generate_internal(int mode, const uuid_t *ns, const char *name, int len)
 {
-       uuid_t     *uuid;
+       uuid_t     *uuid = get_cached_uuid_t(0);
        char       *str;
        uuid_rc_t       rc;
 
-       rc = uuid_create(&uuid);
-       if (rc != UUID_RC_OK)
-               pguuid_complain(rc);
        rc = uuid_make(uuid, mode, ns, name);
        if (rc != UUID_RC_OK)
                pguuid_complain(rc);
        str = uuid_to_string(uuid);
-       rc = uuid_destroy(uuid);
-       if (rc != UUID_RC_OK)
-               pguuid_complain(rc);
 
        return DirectFunctionCall1(uuid_in, CStringGetDatum(str));
 }
@@ -215,26 +239,16 @@ uuid_generate_internal(int mode, const uuid_t *ns, const char *name, int len)
 static Datum
 uuid_generate_v35_internal(int mode, pg_uuid_t *ns, text *name)
 {
-       uuid_t     *ns_uuid;
-       Datum           result;
-       uuid_rc_t       rc;
+       uuid_t     *ns_uuid = get_cached_uuid_t(1);
 
-       rc = uuid_create(&ns_uuid);
-       if (rc != UUID_RC_OK)
-               pguuid_complain(rc);
-       string_to_uuid(DatumGetCString(DirectFunctionCall1(uuid_out, UUIDPGetDatum(ns))),
+       string_to_uuid(DatumGetCString(DirectFunctionCall1(uuid_out,
+                                                                                                          UUIDPGetDatum(ns))),
                                   ns_uuid);
 
-       result = uuid_generate_internal(mode,
-                                                                       ns_uuid,
-                                                                       text_to_cstring(name),
-                                                                       0);
-
-       rc = uuid_destroy(ns_uuid);
-       if (rc != UUID_RC_OK)
-               pguuid_complain(rc);
-
-       return result;
+       return uuid_generate_internal(mode,
+                                                                 ns_uuid,
+                                                                 text_to_cstring(name),
+                                                                 0);
 }
 
 #else                                                  /* !HAVE_UUID_OSSP */