diff --git a/src/backend/utils/adt/uuid.c b/src/backend/utils/adt/uuid.c index f0b22c83f5a224f8f5554bc64a5752e3715fcf3e..d01fef4b574eec3e52ccffb89220b2b7d3bc52e9 100644 --- a/src/backend/utils/adt/uuid.c +++ b/src/backend/utils/adt/uuid.c @@ -6,7 +6,7 @@ * Copyright (c) 2007, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/uuid.c,v 1.2 2007/01/28 20:25:38 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/uuid.c,v 1.3 2007/01/31 19:33:54 neilc Exp $ * *------------------------------------------------------------------------- */ @@ -18,35 +18,16 @@ #include "utils/builtins.h" #include "utils/uuid.h" -/* Accepted GUID formats */ - -/* UUID_FMT1 is the default output format */ -#define UUID_FMT1 "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" -#define UUID_FMT2 "{%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx}" -#define UUID_FMT3 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" - -/* UUIDs are accepted in any of the following textual input formats. */ -#define UUID_CHK_FMT1 "00000000-0000-0000-0000-000000000000" -#define UUID_CHK_FMT2 "{00000000-0000-0000-0000-000000000000}" -#define UUID_CHK_FMT3 "00000000000000000000000000000000" - -#define PRINT_SIZE 40 - /* uuid size in bytes */ #define UUID_LEN 16 /* pg_uuid_t is declared to be struct pg_uuid_t in uuid.h */ struct pg_uuid_t { - char data[UUID_LEN]; + unsigned char data[UUID_LEN]; }; static void string_to_uuid(const char *source, pg_uuid_t *uuid); -static void uuid_to_string(const char *fmt, const pg_uuid_t *uuid, - char *uuid_str); -static bool parse_uuid_string(const char *fmt, const char *chk_fmt, - const char *source, char *data); -static bool is_valid_format(const char *source, const char *fmt); static int uuid_internal_cmp(const pg_uuid_t *arg1, const pg_uuid_t *arg2); Datum @@ -63,96 +44,105 @@ uuid_in(PG_FUNCTION_ARGS) Datum uuid_out(PG_FUNCTION_ARGS) { - pg_uuid_t *uuid = PG_GETARG_UUID_P(0); - char *uuid_str; + pg_uuid_t *uuid = PG_GETARG_UUID_P(0); + static const char hex_chars[] = "0123456789abcdef"; + StringInfoData buf; + int i; - uuid_str = (char *) palloc(PRINT_SIZE); - uuid_to_string(UUID_FMT1, uuid, uuid_str); - PG_RETURN_CSTRING(uuid_str); + initStringInfo(&buf); + for (i = 0; i < UUID_LEN; i++) + { + int hi; + int lo; + + /* + * We print uuid values as a string of 8, 4, 4, 4, and then 12 + * hexadecimal characters, with each group is separated by a + * hyphen ("-"). Therefore, add the hyphens at the appropriate + * places here. + */ + if (i == 4 || i == 6 || i == 8 || i == 10) + appendStringInfoChar(&buf, '-'); + + hi = uuid->data[i] >> 4; + lo = uuid->data[i] & 0x0F; + + appendStringInfoChar(&buf, hex_chars[hi]); + appendStringInfoChar(&buf, hex_chars[lo]); + } + + PG_RETURN_CSTRING(buf.data); } -/* string to uuid convertor by various format types */ +/* + * We allow UUIDs in three input formats: 8x-4x-4x-4x-12x, + * {8x-4x-4x-4x-12x}, and 32x, where "nx" means n hexadecimal digits + * (only the first format is used for output). We convert the first + * two formats into the latter format before further processing. + */ static void string_to_uuid(const char *source, pg_uuid_t *uuid) { - if (!parse_uuid_string(UUID_FMT1, UUID_CHK_FMT1, source, uuid->data) && - !parse_uuid_string(UUID_FMT2, UUID_CHK_FMT2, source, uuid->data) && - !parse_uuid_string(UUID_FMT3, UUID_CHK_FMT3, source, uuid->data)) + char hex_buf[32]; /* not NUL terminated */ + int i; + int src_len; + + src_len = strlen(source); + if (src_len != 32 && src_len != 36 && src_len != 38) + goto syntax_error; + + if (src_len == 32) + memcpy(hex_buf, source, src_len); + else { - ereport(ERROR, - (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), - errmsg("invalid input syntax for uuid: \"%s\"", - source))); - } -} + const char *str = source; -/* check the validity of a uuid string by a given format */ -static bool -is_valid_format(const char *source, const char *fmt) -{ - int i; - int fmtlen = strlen(fmt); + if (src_len == 38) + { + if (str[0] != '{' || str[37] != '}') + goto syntax_error; - /* check length first */ - if (fmtlen != strlen(source)) - return false; + str++; /* skip the first character */ + } - for (i = 0; i < fmtlen; i++) - { - int fc; - int sc; - bool valid_chr; - - fc = fmt[i]; - sc = source[i]; - - /* false if format chr is { or - and source is not */ - if (fc != '0' && fc != sc) - return false; - - /* check for valid char in source */ - valid_chr = (sc >= '0' && sc <= '9') || - (sc >= 'a' && sc <= 'f' ) || - (sc >= 'A' && sc <= 'F' ); - - if (fc == '0' && !valid_chr) - return false; + if (str[8] != '-' || str[13] != '-' || + str[18] != '-' || str[23] != '-') + goto syntax_error; + + memcpy(hex_buf, str, 8); + memcpy(hex_buf + 8, str + 9, 4); + memcpy(hex_buf + 12, str + 14, 4); + memcpy(hex_buf + 16, str + 19, 4); + memcpy(hex_buf + 20, str + 24, 12); } - return true; -} + for (i = 0; i < UUID_LEN; i++) + { + char str_buf[3]; -/* parse the uuid string to a format and return true if okay */ -static bool -parse_uuid_string(const char *fmt, const char *chk_fmt, - const char *source, char *data) -{ - int result = sscanf(source, fmt, - &data[0], &data[1], &data[2], &data[3], &data[4], - &data[5], &data[6], &data[7], &data[8], &data[9], - &data[10], &data[11], &data[12], &data[13], - &data[14], &data[15]); + memcpy(str_buf, &hex_buf[i * 2], 2); + if (!isxdigit((unsigned char) str_buf[0]) || + !isxdigit((unsigned char) str_buf[1])) + goto syntax_error; - return (result == 16) && is_valid_format(source, chk_fmt); -} + str_buf[2] = '\0'; + uuid->data[i] = (unsigned char) strtoul(str_buf, NULL, 16); + } -/* create a string representation of the uuid */ -static void -uuid_to_string(const char *fmt, const pg_uuid_t *uuid, char *uuid_str) -{ - const char *data = uuid->data; - snprintf(uuid_str, PRINT_SIZE, fmt, - data[0], data[1], data[2], data[3], data[4], - data[5], data[6], data[7], data[8], data[9], - data[10], data[11], data[12], data[13], - data[14], data[15]); + return; + +syntax_error: + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for uuid: \"%s\"", + source))); } Datum uuid_recv(PG_FUNCTION_ARGS) { StringInfo buffer = (StringInfo) PG_GETARG_POINTER(0); - pg_uuid_t *uuid; + pg_uuid_t *uuid; uuid = (pg_uuid_t *) palloc(UUID_LEN); memcpy(uuid->data, pq_getmsgbytes(buffer, UUID_LEN), UUID_LEN); @@ -166,7 +156,7 @@ uuid_send(PG_FUNCTION_ARGS) StringInfoData buffer; pq_begintypsend(&buffer); - pq_sendbytes(&buffer, uuid->data, UUID_LEN); + pq_sendbytes(&buffer, (char *) uuid->data, UUID_LEN); PG_RETURN_BYTEA_P(pq_endtypsend(&buffer)); } @@ -246,7 +236,7 @@ Datum uuid_hash(PG_FUNCTION_ARGS) { pg_uuid_t *key = PG_GETARG_UUID_P(0); - return hash_any((unsigned char *) key, sizeof(pg_uuid_t)); + return hash_any(key->data, UUID_LEN); } /* cast text to uuid */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 89dfefe30fb511830fc1e6ef53a9cd2ae85eef7a..8157a3f6a597246c7071e2acea802fbe94f8edaf 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.377 2007/01/28 16:16:52 neilc Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.378 2007/01/31 19:33:54 neilc Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200701281 +#define CATALOG_VERSION_NO 200701311 #endif diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h index a66b8c5b2e61c9ebc9e321ed3e82bda3bfd6c367..4fbf237dad220dffa22e83533ae8bcf20aa372c9 100644 --- a/src/include/catalog/pg_cast.h +++ b/src/include/catalog/pg_cast.h @@ -10,7 +10,7 @@ * * Copyright (c) 2002-2007, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/include/catalog/pg_cast.h,v 1.29 2007/01/28 16:16:52 neilc Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_cast.h,v 1.30 2007/01/31 19:33:54 neilc Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -400,5 +400,7 @@ DATA(insert ( 1700 1700 1703 i )); /* casts to and from uuid */ DATA(insert ( 25 2950 2964 a )); DATA(insert ( 2950 25 2965 a )); +DATA(insert ( 1043 2950 2964 a )); +DATA(insert ( 2950 1043 2965 a )); #endif /* PG_CAST_H */ diff --git a/src/test/regress/expected/uuid.out b/src/test/regress/expected/uuid.out index 9f714ad2e144f07b17aac35e052b073af7e5236f..331a6de5a2ee33bbc68c18a4bbe4f106b8587068 100644 --- a/src/test/regress/expected/uuid.out +++ b/src/test/regress/expected/uuid.out @@ -30,14 +30,14 @@ ERROR: invalid input syntax for uuid: "11+11111-1111-1111-1111-111111111111" --inserting three input formats INSERT INTO guid1(guid_field) VALUES('11111111-1111-1111-1111-111111111111'); INSERT INTO guid1(guid_field) VALUES('{22222222-2222-2222-2222-222222222222}'); -INSERT INTO guid1(guid_field) VALUES('33333333333333333333333333333333'); +INSERT INTO guid1(guid_field) VALUES('3f3e3c3b3a3039383736353433a2313e'); -- retrieving the inserted data SELECT guid_field FROM guid1; guid_field -------------------------------------- 11111111-1111-1111-1111-111111111111 22222222-2222-2222-2222-222222222222 - 33333333-3333-3333-3333-333333333333 + 3f3e3c3b-3a30-3938-3736-353433a2313e (3 rows) -- ordering test @@ -46,19 +46,19 @@ SELECT guid_field FROM guid1 ORDER BY guid_field ASC; -------------------------------------- 11111111-1111-1111-1111-111111111111 22222222-2222-2222-2222-222222222222 - 33333333-3333-3333-3333-333333333333 + 3f3e3c3b-3a30-3938-3736-353433a2313e (3 rows) SELECT guid_field FROM guid1 ORDER BY guid_field DESC; guid_field -------------------------------------- - 33333333-3333-3333-3333-333333333333 + 3f3e3c3b-3a30-3938-3736-353433a2313e 22222222-2222-2222-2222-222222222222 11111111-1111-1111-1111-111111111111 (3 rows) -- = operator test -SELECT COUNT(*) FROM guid1 WHERE guid_field = '33333333-3333-3333-3333-333333333333'; +SELECT COUNT(*) FROM guid1 WHERE guid_field = '3f3e3c3b-3a30-3938-3736-353433a2313e'; count ------- 1 @@ -118,7 +118,7 @@ SELECT count(*) FROM pg_class WHERE relkind='i' AND relname LIKE 'guid%'; INSERT INTO guid1(guid_field) VALUES('44444444-4444-4444-4444-444444444444'); INSERT INTO guid2(guid_field) VALUES('11111111-1111-1111-1111-111111111111'); INSERT INTO guid2(guid_field) VALUES('{22222222-2222-2222-2222-222222222222}'); -INSERT INTO guid2(guid_field) VALUES('33333333333333333333333333333333'); +INSERT INTO guid2(guid_field) VALUES('3f3e3c3b3a3039383736353433a2313e'); -- join test SELECT COUNT(*) FROM guid1 g1 INNER JOIN guid2 g2 ON g1.guid_field = g2.guid_field; count diff --git a/src/test/regress/sql/uuid.sql b/src/test/regress/sql/uuid.sql index 93153da9d704e6aa1f95b576b1b90b34f0a7bd44..48fe7e71108f82ab55f022ee052574bd3c1a8b0d 100644 --- a/src/test/regress/sql/uuid.sql +++ b/src/test/regress/sql/uuid.sql @@ -26,7 +26,7 @@ INSERT INTO guid1(guid_field) VALUES('11+11111-1111-1111-1111-111111111111'); --inserting three input formats INSERT INTO guid1(guid_field) VALUES('11111111-1111-1111-1111-111111111111'); INSERT INTO guid1(guid_field) VALUES('{22222222-2222-2222-2222-222222222222}'); -INSERT INTO guid1(guid_field) VALUES('33333333333333333333333333333333'); +INSERT INTO guid1(guid_field) VALUES('3f3e3c3b3a3039383736353433a2313e'); -- retrieving the inserted data SELECT guid_field FROM guid1; @@ -36,7 +36,7 @@ SELECT guid_field FROM guid1 ORDER BY guid_field ASC; SELECT guid_field FROM guid1 ORDER BY guid_field DESC; -- = operator test -SELECT COUNT(*) FROM guid1 WHERE guid_field = '33333333-3333-3333-3333-333333333333'; +SELECT COUNT(*) FROM guid1 WHERE guid_field = '3f3e3c3b-3a30-3938-3736-353433a2313e'; -- <> operator test SELECT COUNT(*) FROM guid1 WHERE guid_field <> '11111111111111111111111111111111'; @@ -69,7 +69,7 @@ SELECT count(*) FROM pg_class WHERE relkind='i' AND relname LIKE 'guid%'; INSERT INTO guid1(guid_field) VALUES('44444444-4444-4444-4444-444444444444'); INSERT INTO guid2(guid_field) VALUES('11111111-1111-1111-1111-111111111111'); INSERT INTO guid2(guid_field) VALUES('{22222222-2222-2222-2222-222222222222}'); -INSERT INTO guid2(guid_field) VALUES('33333333333333333333333333333333'); +INSERT INTO guid2(guid_field) VALUES('3f3e3c3b3a3039383736353433a2313e'); -- join test SELECT COUNT(*) FROM guid1 g1 INNER JOIN guid2 g2 ON g1.guid_field = g2.guid_field;