1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-28 23:42:10 +03:00

Support BSD and e2fsprogs UUID libraries alongside OSSP UUID library.

Allow the contrib/uuid-ossp extension to be built atop any one of these
three popular UUID libraries.  (The extension's name is now arguably a
misnomer, but we'll keep it the same so as not to cause unnecessary
compatibility issues for users.)

We would not normally consider a change like this post-beta1, but the issue
has been forced by our upgrade to autoconf 2.69, whose more rigorous header
checks are causing OSSP's header files to be rejected on some platforms.
It's been foreseen for some time that we'd have to move away from depending
on OSSP UUID due to lack of upstream maintenance, so this is a down payment
on that problem.

While at it, add some simple regression tests, in hopes of catching any
major incompatibilities between the three implementations.

Matteo Beccati, with some further hacking by me
This commit is contained in:
Tom Lane
2014-05-27 19:42:08 -04:00
parent 616afee14d
commit b8cc8f9473
12 changed files with 927 additions and 181 deletions

6
contrib/uuid-ossp/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
/md5.c
/sha1.c
# Generated subdirectories
/log/
/results/
/tmp_check/

View File

@ -1,12 +1,21 @@
# contrib/uuid-ossp/Makefile
MODULE_big = uuid-ossp
OBJS = uuid-ossp.o
OBJS = uuid-ossp.o $(UUID_EXTRA_OBJS)
EXTENSION = uuid-ossp
DATA = uuid-ossp--1.0.sql uuid-ossp--unpackaged--1.0.sql
SHLIB_LINK += $(OSSP_UUID_LIBS)
REGRESS = uuid_ossp
SHLIB_LINK += $(UUID_LIBS)
# We copy some needed files verbatim from pgcrypto
pgcrypto_src = $(top_srcdir)/contrib/pgcrypto
PG_CPPFLAGS = -I$(pgcrypto_src)
EXTRA_CLEAN = md5.c sha1.c
ifdef USE_PGXS
PG_CONFIG = pg_config
@ -18,3 +27,6 @@ top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif
md5.c sha1.c: % : $(pgcrypto_src)/%
rm -f $@ && $(LN_S) $< .

View File

@ -0,0 +1,91 @@
CREATE EXTENSION "uuid-ossp";
SELECT uuid_nil();
uuid_nil
--------------------------------------
00000000-0000-0000-0000-000000000000
(1 row)
SELECT uuid_ns_dns();
uuid_ns_dns
--------------------------------------
6ba7b810-9dad-11d1-80b4-00c04fd430c8
(1 row)
SELECT uuid_ns_url();
uuid_ns_url
--------------------------------------
6ba7b811-9dad-11d1-80b4-00c04fd430c8
(1 row)
SELECT uuid_ns_oid();
uuid_ns_oid
--------------------------------------
6ba7b812-9dad-11d1-80b4-00c04fd430c8
(1 row)
SELECT uuid_ns_x500();
uuid_ns_x500
--------------------------------------
6ba7b814-9dad-11d1-80b4-00c04fd430c8
(1 row)
SELECT uuid_generate_v1() < uuid_generate_v1();
?column?
----------
t
(1 row)
SELECT uuid_generate_v1() < uuid_generate_v1mc();
?column?
----------
t
(1 row)
SELECT substr(uuid_generate_v1()::text, 25) = substr(uuid_generate_v1()::text, 25);
?column?
----------
t
(1 row)
SELECT substr(uuid_generate_v1()::text, 25) <> substr(uuid_generate_v1mc()::text, 25);
?column?
----------
t
(1 row)
SELECT substr(uuid_generate_v1mc()::text, 25) <> substr(uuid_generate_v1mc()::text, 25);
?column?
----------
t
(1 row)
SELECT ('x' || substr(uuid_generate_v1mc()::text, 25, 2))::bit(8) & '00000011';
?column?
----------
00000011
(1 row)
SELECT uuid_generate_v3(uuid_ns_dns(), 'www.widgets.com');
uuid_generate_v3
--------------------------------------
3d813cbb-47fb-32ba-91df-831e1593ac29
(1 row)
SELECT uuid_generate_v5(uuid_ns_dns(), 'www.widgets.com');
uuid_generate_v5
--------------------------------------
21f7f8de-8051-5b89-8680-0195ef798b6a
(1 row)
SELECT uuid_generate_v4()::text ~ '^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$';
?column?
----------
t
(1 row)
SELECT uuid_generate_v4() <> uuid_generate_v4();
?column?
----------
t
(1 row)

View File

@ -0,0 +1,22 @@
CREATE EXTENSION "uuid-ossp";
SELECT uuid_nil();
SELECT uuid_ns_dns();
SELECT uuid_ns_url();
SELECT uuid_ns_oid();
SELECT uuid_ns_x500();
SELECT uuid_generate_v1() < uuid_generate_v1();
SELECT uuid_generate_v1() < uuid_generate_v1mc();
SELECT substr(uuid_generate_v1()::text, 25) = substr(uuid_generate_v1()::text, 25);
SELECT substr(uuid_generate_v1()::text, 25) <> substr(uuid_generate_v1mc()::text, 25);
SELECT substr(uuid_generate_v1mc()::text, 25) <> substr(uuid_generate_v1mc()::text, 25);
SELECT ('x' || substr(uuid_generate_v1mc()::text, 25, 2))::bit(8) & '00000011';
SELECT uuid_generate_v3(uuid_ns_dns(), 'www.widgets.com');
SELECT uuid_generate_v5(uuid_ns_dns(), 'www.widgets.com');
SELECT uuid_generate_v4()::text ~ '^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$';
SELECT uuid_generate_v4() <> uuid_generate_v4();

View File

@ -1,40 +1,113 @@
/*-------------------------------------------------------------------------
*
* UUID generation functions using the OSSP UUID library
* UUID generation functions using the BSD, E2FS or OSSP UUID library
*
* Copyright (c) 2007-2014, PostgreSQL Global Development Group
*
* Portions Copyright (c) 2009 Andrew Gierth
*
* 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.
* It's possible that there's more than one uuid.h header file present.
* We expect configure to set the HAVE_ symbol for only the one we want.
*
* BSD includes a uuid_hash() function that conflicts with the one in
* builtins.h; we #define it out of the way.
*/
#ifdef HAVE_OSSP_UUID_H
#include <ossp/uuid.h>
#else
#define uuid_hash bsd_uuid_hash
#ifdef HAVE_UUID_H
#include <uuid.h>
#else
#error OSSP uuid.h not found
#endif
#ifdef HAVE_OSSP_UUID_H
#include <ossp/uuid.h>
#endif
#ifdef HAVE_UUID_UUID_H
#include <uuid/uuid.h>
#endif
/* better both be 16 */
#if (UUID_LEN != UUID_LEN_BIN)
#undef uuid_hash
/*
* Some BSD variants offer md5 and sha1 implementations but Linux does not,
* so we use a copy of the ones from pgcrypto. Not needed with OSSP, though.
*/
#ifndef HAVE_UUID_OSSP
#include "md5.h"
#include "sha1.h"
#endif
/* Check our UUID length against OSSP's; better both be 16 */
#if defined(HAVE_UUID_OSSP) && (UUID_LEN != UUID_LEN_BIN)
#error UUID length mismatch
#endif
/* Define some constants like OSSP's, to make the code more readable */
#ifndef HAVE_UUID_OSSP
#define UUID_MAKE_MC 0
#define UUID_MAKE_V1 1
#define UUID_MAKE_V2 2
#define UUID_MAKE_V3 3
#define UUID_MAKE_V4 4
#define UUID_MAKE_V5 5
#endif
/*
* A DCE 1.1 compatible source representation of UUIDs, derived from
* the BSD implementation. BSD already has this; OSSP doesn't need it.
*/
#ifdef HAVE_UUID_E2FS
typedef struct
{
uint32_t time_low;
uint16_t time_mid;
uint16_t time_hi_and_version;
uint8_t clock_seq_hi_and_reserved;
uint8_t clock_seq_low;
uint8_t node[6];
} dce_uuid_t;
#else
#define dce_uuid_t uuid_t
#endif
/* If not OSSP, we need some endianness-manipulation macros */
#ifndef HAVE_UUID_OSSP
#define UUID_TO_NETWORK(uu) \
do { \
uu.time_low = htonl(uu.time_low); \
uu.time_mid = htons(uu.time_mid); \
uu.time_hi_and_version = htons(uu.time_hi_and_version); \
} while (0)
#define UUID_TO_LOCAL(uu) \
do { \
uu.time_low = ntohl(uu.time_low); \
uu.time_mid = ntohs(uu.time_mid); \
uu.time_hi_and_version = ntohs(uu.time_hi_and_version); \
} while (0)
#define UUID_V3_OR_V5(uu, v) \
do { \
uu.time_hi_and_version &= 0x0FFF; \
uu.time_hi_and_version |= (v << 12); \
uu.clock_seq_hi_and_reserved &= 0x3F; \
uu.clock_seq_hi_and_reserved |= 0x80; \
} while(0)
#endif /* !HAVE_UUID_OSSP */
PG_MODULE_MAGIC;
@ -51,6 +124,8 @@ PG_FUNCTION_INFO_V1(uuid_generate_v3);
PG_FUNCTION_INFO_V1(uuid_generate_v4);
PG_FUNCTION_INFO_V1(uuid_generate_v5);
#ifdef HAVE_UUID_OSSP
static void
pguuid_complain(uuid_rc_t rc)
{
@ -114,44 +189,9 @@ special_uuid_value(const char *name)
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");
}
/* len is unused with OSSP, but we want to have the same number of args */
static Datum
uuid_generate_internal(int mode, const uuid_t *ns, const char *name)
uuid_generate_internal(int mode, const uuid_t *ns, const char *name, int len)
{
uuid_t *uuid;
char *str;
@ -172,20 +212,6 @@ uuid_generate_internal(int mode, const uuid_t *ns, const char *name)
}
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)
{
@ -201,7 +227,8 @@ uuid_generate_v35_internal(int mode, pg_uuid_t *ns, text *name)
result = uuid_generate_internal(mode,
ns_uuid,
text_to_cstring(name));
text_to_cstring(name),
0);
rc = uuid_destroy(ns_uuid);
if (rc != UUID_RC_OK)
@ -210,6 +237,248 @@ uuid_generate_v35_internal(int mode, pg_uuid_t *ns, text *name)
return result;
}
#else /* !HAVE_UUID_OSSP */
static Datum
uuid_generate_internal(int v, unsigned char *ns, char *ptr, int len)
{
char strbuf[40];
switch (v)
{
case 0: /* constant-value uuids */
strlcpy(strbuf, ptr, 37);
break;
case 1: /* time/node-based uuids */
{
#ifdef HAVE_UUID_E2FS
uuid_t uu;
uuid_generate_time(uu);
uuid_unparse(uu, strbuf);
/*
* PTR, if set, replaces the trailing characters of the uuid;
* this is to support v1mc, where a random multicast MAC is
* used instead of the physical one
*/
if (ptr && len <= 36)
strcpy(strbuf + (36 - len), ptr);
#else /* BSD */
uuid_t uu;
uint32_t status = uuid_s_ok;
char *str = NULL;
uuid_create(&uu, &status);
if (status == uuid_s_ok)
{
uuid_to_string(&uu, &str, &status);
if (status == uuid_s_ok)
{
strlcpy(strbuf, str, 37);
/*
* PTR, if set, replaces the trailing characters of
* the uuid; this is to support v1mc, where a random
* multicast MAC is used instead of the physical one
*/
if (ptr && len <= 36)
strcpy(strbuf + (36 - len), ptr);
}
if (str)
free(str);
}
if (status != uuid_s_ok)
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
errmsg("uuid library failure: %d",
(int) status)));
#endif
break;
}
case 3: /* namespace-based MD5 uuids */
case 5: /* namespace-based SHA1 uuids */
{
dce_uuid_t uu;
#ifdef HAVE_UUID_BSD
uint32_t status = uuid_s_ok;
char *str = NULL;
#endif
if (v == 3)
{
MD5_CTX ctx;
MD5Init(&ctx);
MD5Update(&ctx, ns, sizeof(uu));
MD5Update(&ctx, (unsigned char *) ptr, len);
MD5Final((unsigned char *) &uu, &ctx);
}
else
{
SHA1_CTX ctx;
SHA1Init(&ctx);
SHA1Update(&ctx, ns, sizeof(uu));
SHA1Update(&ctx, (unsigned char *) ptr, len);
SHA1Final((unsigned char *) &uu, &ctx);
}
/* the calculated hash is using local order */
UUID_TO_NETWORK(uu);
UUID_V3_OR_V5(uu, v);
#ifdef HAVE_UUID_E2FS
/* uuid_unparse expects local order */
UUID_TO_LOCAL(uu);
uuid_unparse((unsigned char *) &uu, strbuf);
#else /* BSD */
uuid_to_string(&uu, &str, &status);
if (status == uuid_s_ok)
strlcpy(strbuf, str, 37);
if (str)
free(str);
if (status != uuid_s_ok)
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
errmsg("uuid library failure: %d",
(int) status)));
#endif
break;
}
case 4: /* random uuid */
default:
{
#ifdef HAVE_UUID_E2FS
uuid_t uu;
uuid_generate_random(uu);
uuid_unparse(uu, strbuf);
#else /* BSD */
snprintf(strbuf, sizeof(strbuf),
"%08lx-%04x-%04x-%04x-%04x%08lx",
(unsigned long) arc4random(),
(unsigned) (arc4random() & 0xffff),
(unsigned) ((arc4random() & 0xfff) | 0x4000),
(unsigned) ((arc4random() & 0x3fff) | 0x8000),
(unsigned) (arc4random() & 0xffff),
(unsigned long) arc4random());
#endif
break;
}
}
return DirectFunctionCall1(uuid_in, CStringGetDatum(strbuf));
}
#endif /* HAVE_UUID_OSSP */
Datum
uuid_nil(PG_FUNCTION_ARGS)
{
#ifdef HAVE_UUID_OSSP
return special_uuid_value("nil");
#else
return uuid_generate_internal(0, NULL,
"00000000-0000-0000-0000-000000000000", 36);
#endif
}
Datum
uuid_ns_dns(PG_FUNCTION_ARGS)
{
#ifdef HAVE_UUID_OSSP
return special_uuid_value("ns:DNS");
#else
return uuid_generate_internal(0, NULL,
"6ba7b810-9dad-11d1-80b4-00c04fd430c8", 36);
#endif
}
Datum
uuid_ns_url(PG_FUNCTION_ARGS)
{
#ifdef HAVE_UUID_OSSP
return special_uuid_value("ns:URL");
#else
return uuid_generate_internal(0, NULL,
"6ba7b811-9dad-11d1-80b4-00c04fd430c8", 36);
#endif
}
Datum
uuid_ns_oid(PG_FUNCTION_ARGS)
{
#ifdef HAVE_UUID_OSSP
return special_uuid_value("ns:OID");
#else
return uuid_generate_internal(0, NULL,
"6ba7b812-9dad-11d1-80b4-00c04fd430c8", 36);
#endif
}
Datum
uuid_ns_x500(PG_FUNCTION_ARGS)
{
#ifdef HAVE_UUID_OSSP
return special_uuid_value("ns:X500");
#else
return uuid_generate_internal(0, NULL,
"6ba7b814-9dad-11d1-80b4-00c04fd430c8", 36);
#endif
}
Datum
uuid_generate_v1(PG_FUNCTION_ARGS)
{
return uuid_generate_internal(UUID_MAKE_V1, NULL, NULL, 0);
}
Datum
uuid_generate_v1mc(PG_FUNCTION_ARGS)
{
#ifdef HAVE_UUID_OSSP
char *buf = NULL;
#elif defined(HAVE_UUID_E2FS)
char strbuf[40];
char *buf;
uuid_t uu;
uuid_generate_random(uu);
/* set IEEE802 multicast and local-admin bits */
((dce_uuid_t *) &uu)->node[0] |= 0x03;
uuid_unparse(uu, strbuf);
buf = strbuf + 24;
#else /* BSD */
char buf[16];
/* set IEEE802 multicast and local-admin bits */
snprintf(buf, sizeof(buf), "-%04x%08lx",
(unsigned) ((arc4random() & 0xffff) | 0x0300),
(unsigned long) arc4random());
#endif
return uuid_generate_internal(UUID_MAKE_V1 | UUID_MAKE_MC, NULL,
buf, 13);
}
Datum
uuid_generate_v3(PG_FUNCTION_ARGS)
@ -217,14 +486,19 @@ uuid_generate_v3(PG_FUNCTION_ARGS)
pg_uuid_t *ns = PG_GETARG_UUID_P(0);
text *name = PG_GETARG_TEXT_P(1);
#ifdef HAVE_UUID_OSSP
return uuid_generate_v35_internal(UUID_MAKE_V3, ns, name);
#else
return uuid_generate_internal(UUID_MAKE_V3, (unsigned char *) ns,
VARDATA(name), VARSIZE(name) - VARHDRSZ);
#endif
}
Datum
uuid_generate_v4(PG_FUNCTION_ARGS)
{
return uuid_generate_internal(UUID_MAKE_V4, NULL, NULL);
return uuid_generate_internal(UUID_MAKE_V4, NULL, NULL, 0);
}
@ -234,5 +508,10 @@ uuid_generate_v5(PG_FUNCTION_ARGS)
pg_uuid_t *ns = PG_GETARG_UUID_P(0);
text *name = PG_GETARG_TEXT_P(1);
#ifdef HAVE_UUID_OSSP
return uuid_generate_v35_internal(UUID_MAKE_V5, ns, name);
#else
return uuid_generate_internal(UUID_MAKE_V5, (unsigned char *) ns,
VARDATA(name), VARSIZE(name) - VARHDRSZ);
#endif
}