From: Neil Conway Date: Wed, 31 Jan 2007 19:33:54 +0000 (+0000) Subject: Rewrite uuid input and output routines to avoid dependency on the X-Git-Tag: REL8_3_BETA1~1371 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=05ce7d6a418633c665e8c2ace4f51deaf05846d7;p=postgresql Rewrite uuid input and output routines to avoid dependency on the nonportable "hh" sprintf(3) length modifier. Instead, do the parsing and output by hand. The code to do this isn't ideal, but this is an interim measure anyway: the uuid type should probably use the in-memory struct layout specified by RFC 4122. For now, this patch should hopefully rectify the buildfarm failures for the uuid test. Along the way, re-add pg_cast entries for uuid <-> varchar, which I mistakenly removed earlier, and bump the catversion. --- diff --git a/src/backend/utils/adt/uuid.c b/src/backend/utils/adt/uuid.c index f0b22c83f5..d01fef4b57 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 89dfefe30f..8157a3f6a5 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 a66b8c5b2e..4fbf237dad 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 9f714ad2e1..331a6de5a2 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 93153da9d7..48fe7e7110 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;