]> granicus.if.org Git - postgresql/commitdiff
Rewrite uuid input and output routines to avoid dependency on the
authorNeil Conway <neilc@samurai.com>
Wed, 31 Jan 2007 19:33:54 +0000 (19:33 +0000)
committerNeil Conway <neilc@samurai.com>
Wed, 31 Jan 2007 19:33:54 +0000 (19:33 +0000)
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.

src/backend/utils/adt/uuid.c
src/include/catalog/catversion.h
src/include/catalog/pg_cast.h
src/test/regress/expected/uuid.out
src/test/regress/sql/uuid.sql

index f0b22c83f5a224f8f5554bc64a5752e3715fcf3e..d01fef4b574eec3e52ccffb89220b2b7d3bc52e9 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 #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 */
index 89dfefe30fb511830fc1e6ef53a9cd2ae85eef7a..8157a3f6a597246c7071e2acea802fbe94f8edaf 100644 (file)
@@ -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
index a66b8c5b2e61c9ebc9e321ed3e82bda3bfd6c367..4fbf237dad220dffa22e83533ae8bcf20aa372c9 100644 (file)
@@ -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 */
index 9f714ad2e144f07b17aac35e052b073af7e5236f..331a6de5a2ee33bbc68c18a4bbe4f106b8587068 100644 (file)
@@ -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 
index 93153da9d704e6aa1f95b576b1b90b34f0a7bd44..48fe7e71108f82ab55f022ee052574bd3c1a8b0d 100644 (file)
@@ -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;