mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 10:30:33 +03:00 
			
		
		
		
	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.
		
			
				
	
	
		
			266 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			266 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*-------------------------------------------------------------------------
 | |
|  *
 | |
|  * UUID generation functions using the OSSP UUID library
 | |
|  *
 | |
|  * Copyright (c) 2007-2013, PostgreSQL Global Development Group
 | |
|  *
 | |
|  * contrib/uuid-ossp/uuid-ossp.c
 | |
|  *
 | |
|  *-------------------------------------------------------------------------
 | |
|  */
 | |
| 
 | |
| #include "postgres.h"
 | |
| #include "fmgr.h"
 | |
| #include "utils/builtins.h"
 | |
| #include "utils/uuid.h"
 | |
| 
 | |
| /*
 | |
|  * There's some confusion over the location of the uuid.h header file.
 | |
|  * On Debian, it's installed as ossp/uuid.h, while on Fedora, or if you
 | |
|  * install ossp-uuid from a tarball, it's installed as uuid.h. Don't know
 | |
|  * what other systems do.
 | |
|  */
 | |
| #ifdef HAVE_OSSP_UUID_H
 | |
| #include <ossp/uuid.h>
 | |
| #else
 | |
| #ifdef HAVE_UUID_H
 | |
| #include <uuid.h>
 | |
| #else
 | |
| #error OSSP uuid.h not found
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| /* better both be 16 */
 | |
| #if (UUID_LEN != UUID_LEN_BIN)
 | |
| #error UUID length mismatch
 | |
| #endif
 | |
| 
 | |
| 
 | |
| PG_MODULE_MAGIC;
 | |
| 
 | |
| 
 | |
| Datum		uuid_nil(PG_FUNCTION_ARGS);
 | |
| Datum		uuid_ns_dns(PG_FUNCTION_ARGS);
 | |
| Datum		uuid_ns_url(PG_FUNCTION_ARGS);
 | |
| Datum		uuid_ns_oid(PG_FUNCTION_ARGS);
 | |
| Datum		uuid_ns_x500(PG_FUNCTION_ARGS);
 | |
| 
 | |
| Datum		uuid_generate_v1(PG_FUNCTION_ARGS);
 | |
| Datum		uuid_generate_v1mc(PG_FUNCTION_ARGS);
 | |
| Datum		uuid_generate_v3(PG_FUNCTION_ARGS);
 | |
| Datum		uuid_generate_v4(PG_FUNCTION_ARGS);
 | |
| Datum		uuid_generate_v5(PG_FUNCTION_ARGS);
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(uuid_nil);
 | |
| PG_FUNCTION_INFO_V1(uuid_ns_dns);
 | |
| PG_FUNCTION_INFO_V1(uuid_ns_url);
 | |
| PG_FUNCTION_INFO_V1(uuid_ns_oid);
 | |
| PG_FUNCTION_INFO_V1(uuid_ns_x500);
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(uuid_generate_v1);
 | |
| PG_FUNCTION_INFO_V1(uuid_generate_v1mc);
 | |
| PG_FUNCTION_INFO_V1(uuid_generate_v3);
 | |
| PG_FUNCTION_INFO_V1(uuid_generate_v4);
 | |
| PG_FUNCTION_INFO_V1(uuid_generate_v5);
 | |
| 
 | |
| static void
 | |
| pguuid_complain(uuid_rc_t rc)
 | |
| {
 | |
| 	char	   *err = uuid_error(rc);
 | |
| 
 | |
| 	if (err != NULL)
 | |
| 		ereport(ERROR,
 | |
| 				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
 | |
| 				 errmsg("OSSP uuid library failure: %s", err)));
 | |
| 	else
 | |
| 		ereport(ERROR,
 | |
| 				(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
 | |
| 				 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)
 | |
| {
 | |
| 	char	   *buf = palloc(UUID_LEN_STR + 1);
 | |
| 	void	   *ptr = buf;
 | |
| 	size_t		len = UUID_LEN_STR + 1;
 | |
| 	uuid_rc_t	rc;
 | |
| 
 | |
| 	rc = uuid_export(uuid, UUID_FMT_STR, &ptr, &len);
 | |
| 	if (rc != UUID_RC_OK)
 | |
| 		pguuid_complain(rc);
 | |
| 
 | |
| 	return buf;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| string_to_uuid(const char *str, uuid_t *uuid)
 | |
| {
 | |
| 	uuid_rc_t	rc;
 | |
| 
 | |
| 	rc = uuid_import(uuid, UUID_FMT_STR, str, UUID_LEN_STR + 1);
 | |
| 	if (rc != UUID_RC_OK)
 | |
| 		pguuid_complain(rc);
 | |
| }
 | |
| 
 | |
| 
 | |
| static Datum
 | |
| special_uuid_value(const char *name)
 | |
| {
 | |
| 	uuid_t	   *uuid = get_cached_uuid_t(0);
 | |
| 	char	   *str;
 | |
| 	uuid_rc_t	rc;
 | |
| 
 | |
| 	rc = uuid_load(uuid, name);
 | |
| 	if (rc != UUID_RC_OK)
 | |
| 		pguuid_complain(rc);
 | |
| 	str = uuid_to_string(uuid);
 | |
| 
 | |
| 	return DirectFunctionCall1(uuid_in, CStringGetDatum(str));
 | |
| }
 | |
| 
 | |
| 
 | |
| Datum
 | |
| uuid_nil(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	return special_uuid_value("nil");
 | |
| }
 | |
| 
 | |
| 
 | |
| Datum
 | |
| uuid_ns_dns(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	return special_uuid_value("ns:DNS");
 | |
| }
 | |
| 
 | |
| 
 | |
| Datum
 | |
| uuid_ns_url(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	return special_uuid_value("ns:URL");
 | |
| }
 | |
| 
 | |
| 
 | |
| Datum
 | |
| uuid_ns_oid(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	return special_uuid_value("ns:OID");
 | |
| }
 | |
| 
 | |
| 
 | |
| Datum
 | |
| uuid_ns_x500(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	return special_uuid_value("ns:X500");
 | |
| }
 | |
| 
 | |
| 
 | |
| static Datum
 | |
| uuid_generate_internal(int mode, const uuid_t *ns, const char *name)
 | |
| {
 | |
| 	uuid_t	   *uuid = get_cached_uuid_t(0);
 | |
| 	char	   *str;
 | |
| 	uuid_rc_t	rc;
 | |
| 
 | |
| 	rc = uuid_make(uuid, mode, ns, name);
 | |
| 	if (rc != UUID_RC_OK)
 | |
| 		pguuid_complain(rc);
 | |
| 	str = uuid_to_string(uuid);
 | |
| 
 | |
| 	return DirectFunctionCall1(uuid_in, CStringGetDatum(str));
 | |
| }
 | |
| 
 | |
| 
 | |
| Datum
 | |
| uuid_generate_v1(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	return uuid_generate_internal(UUID_MAKE_V1, NULL, NULL);
 | |
| }
 | |
| 
 | |
| 
 | |
| Datum
 | |
| uuid_generate_v1mc(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	return uuid_generate_internal(UUID_MAKE_V1 | UUID_MAKE_MC, NULL, NULL);
 | |
| }
 | |
| 
 | |
| 
 | |
| static Datum
 | |
| uuid_generate_v35_internal(int mode, pg_uuid_t *ns, text *name)
 | |
| {
 | |
| 	uuid_t	   *ns_uuid = get_cached_uuid_t(1);
 | |
| 
 | |
| 	string_to_uuid(DatumGetCString(DirectFunctionCall1(uuid_out,
 | |
| 													   UUIDPGetDatum(ns))),
 | |
| 				   ns_uuid);
 | |
| 
 | |
| 	return uuid_generate_internal(mode,
 | |
| 								  ns_uuid,
 | |
| 								  text_to_cstring(name));
 | |
| }
 | |
| 
 | |
| 
 | |
| Datum
 | |
| uuid_generate_v3(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	pg_uuid_t  *ns = PG_GETARG_UUID_P(0);
 | |
| 	text	   *name = PG_GETARG_TEXT_P(1);
 | |
| 
 | |
| 	return uuid_generate_v35_internal(UUID_MAKE_V3, ns, name);
 | |
| }
 | |
| 
 | |
| 
 | |
| Datum
 | |
| uuid_generate_v4(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	return uuid_generate_internal(UUID_MAKE_V4, NULL, NULL);
 | |
| }
 | |
| 
 | |
| 
 | |
| Datum
 | |
| uuid_generate_v5(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	pg_uuid_t  *ns = PG_GETARG_UUID_P(0);
 | |
| 	text	   *name = PG_GETARG_TEXT_P(1);
 | |
| 
 | |
| 	return uuid_generate_v35_internal(UUID_MAKE_V5, ns, name);
 | |
| }
 |