]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/uuid.c
Allow uuid_in() to parse a wider variety of variant input formats for the UUID
[postgresql] / src / backend / utils / adt / uuid.c
1 /*-------------------------------------------------------------------------
2  *
3  * uuid.c
4  *        Functions for the built-in type "uuid".
5  *
6  * Copyright (c) 2007-2008, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  *        $PostgreSQL: pgsql/src/backend/utils/adt/uuid.c,v 1.8 2008/11/03 22:14:40 petere Exp $
10  *
11  *-------------------------------------------------------------------------
12  */
13
14 #include "postgres.h"
15
16 #include "access/hash.h"
17 #include "libpq/pqformat.h"
18 #include "utils/builtins.h"
19 #include "utils/uuid.h"
20
21 /* uuid size in bytes */
22 #define UUID_LEN 16
23
24 /* pg_uuid_t is declared to be struct pg_uuid_t in uuid.h */
25 struct pg_uuid_t
26 {
27         unsigned char data[UUID_LEN];
28 };
29
30 static void string_to_uuid(const char *source, pg_uuid_t *uuid);
31 static int      uuid_internal_cmp(const pg_uuid_t *arg1, const pg_uuid_t *arg2);
32
33 Datum
34 uuid_in(PG_FUNCTION_ARGS)
35 {
36         char       *uuid_str = PG_GETARG_CSTRING(0);
37         pg_uuid_t  *uuid;
38
39         uuid = (pg_uuid_t *) palloc(sizeof(*uuid));
40         string_to_uuid(uuid_str, uuid);
41         PG_RETURN_UUID_P(uuid);
42 }
43
44 Datum
45 uuid_out(PG_FUNCTION_ARGS)
46 {
47         pg_uuid_t  *uuid = PG_GETARG_UUID_P(0);
48         static const char hex_chars[] = "0123456789abcdef";
49         StringInfoData buf;
50         int                     i;
51
52         initStringInfo(&buf);
53         for (i = 0; i < UUID_LEN; i++)
54         {
55                 int                     hi;
56                 int                     lo;
57
58                 /*
59                  * We print uuid values as a string of 8, 4, 4, 4, and then 12
60                  * hexadecimal characters, with each group is separated by a hyphen
61                  * ("-"). Therefore, add the hyphens at the appropriate places here.
62                  */
63                 if (i == 4 || i == 6 || i == 8 || i == 10)
64                         appendStringInfoChar(&buf, '-');
65
66                 hi = uuid->data[i] >> 4;
67                 lo = uuid->data[i] & 0x0F;
68
69                 appendStringInfoChar(&buf, hex_chars[hi]);
70                 appendStringInfoChar(&buf, hex_chars[lo]);
71         }
72
73         PG_RETURN_CSTRING(buf.data);
74 }
75
76 /*
77  * We allow UUIDs as a series of 32 hexadecimal digits with an optional dash
78  * after each group of 4 hexadecimal digits, and optionally surrounded by {}.
79  * (The canonical format 8x-4x-4x-4x-12x, where "nx" means n hexadecimal
80  * digits, is the only one used for output.)
81  */
82 static void
83 string_to_uuid(const char *source, pg_uuid_t *uuid)
84 {
85         const char *src = source;
86         int     i, braces = 0;
87
88         if (src[0] == '{')
89         {
90                 ++src;
91                 braces = 1;
92         }
93
94         for (i = 0; i < UUID_LEN; i++)
95         {
96                 char            str_buf[3];
97
98                 if (src[0] == '\0' || src[1] == '\0')
99                         goto syntax_error;
100                 memcpy(str_buf, src, 2);
101                 if (!isxdigit((unsigned char) str_buf[0]) ||
102                         !isxdigit((unsigned char) str_buf[1]))
103                         goto syntax_error;
104
105                 str_buf[2] = '\0';
106                 uuid->data[i] = (unsigned char) strtoul(str_buf, NULL, 16);
107                 src += 2;
108                 if (src[0] == '-' && (i % 2) == 1 && i < UUID_LEN - 1)
109                         src++;
110         }
111
112         if (braces)
113         {
114                 if (*src!= '}')
115                         goto syntax_error;
116                 ++src;
117         }
118
119         if (*src != '\0')
120                 goto syntax_error;
121
122         return;
123
124 syntax_error:
125         ereport(ERROR,
126                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
127                          errmsg("invalid input syntax for uuid: \"%s\"",
128                                         source)));
129 }
130
131 Datum
132 uuid_recv(PG_FUNCTION_ARGS)
133 {
134         StringInfo      buffer = (StringInfo) PG_GETARG_POINTER(0);
135         pg_uuid_t  *uuid;
136
137         uuid = (pg_uuid_t *) palloc(UUID_LEN);
138         memcpy(uuid->data, pq_getmsgbytes(buffer, UUID_LEN), UUID_LEN);
139         PG_RETURN_POINTER(uuid);
140 }
141
142 Datum
143 uuid_send(PG_FUNCTION_ARGS)
144 {
145         pg_uuid_t  *uuid = PG_GETARG_UUID_P(0);
146         StringInfoData buffer;
147
148         pq_begintypsend(&buffer);
149         pq_sendbytes(&buffer, (char *) uuid->data, UUID_LEN);
150         PG_RETURN_BYTEA_P(pq_endtypsend(&buffer));
151 }
152
153 /* internal uuid compare function */
154 static int
155 uuid_internal_cmp(const pg_uuid_t *arg1, const pg_uuid_t *arg2)
156 {
157         return memcmp(arg1->data, arg2->data, UUID_LEN);
158 }
159
160 Datum
161 uuid_lt(PG_FUNCTION_ARGS)
162 {
163         pg_uuid_t  *arg1 = PG_GETARG_UUID_P(0);
164         pg_uuid_t  *arg2 = PG_GETARG_UUID_P(1);
165
166         PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) < 0);
167 }
168
169 Datum
170 uuid_le(PG_FUNCTION_ARGS)
171 {
172         pg_uuid_t  *arg1 = PG_GETARG_UUID_P(0);
173         pg_uuid_t  *arg2 = PG_GETARG_UUID_P(1);
174
175         PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) <= 0);
176 }
177
178 Datum
179 uuid_eq(PG_FUNCTION_ARGS)
180 {
181         pg_uuid_t  *arg1 = PG_GETARG_UUID_P(0);
182         pg_uuid_t  *arg2 = PG_GETARG_UUID_P(1);
183
184         PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) == 0);
185 }
186
187 Datum
188 uuid_ge(PG_FUNCTION_ARGS)
189 {
190         pg_uuid_t  *arg1 = PG_GETARG_UUID_P(0);
191         pg_uuid_t  *arg2 = PG_GETARG_UUID_P(1);
192
193         PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) >= 0);
194 }
195
196 Datum
197 uuid_gt(PG_FUNCTION_ARGS)
198 {
199         pg_uuid_t  *arg1 = PG_GETARG_UUID_P(0);
200         pg_uuid_t  *arg2 = PG_GETARG_UUID_P(1);
201
202         PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) > 0);
203 }
204
205 Datum
206 uuid_ne(PG_FUNCTION_ARGS)
207 {
208         pg_uuid_t  *arg1 = PG_GETARG_UUID_P(0);
209         pg_uuid_t  *arg2 = PG_GETARG_UUID_P(1);
210
211         PG_RETURN_BOOL(uuid_internal_cmp(arg1, arg2) != 0);
212 }
213
214 /* handler for btree index operator */
215 Datum
216 uuid_cmp(PG_FUNCTION_ARGS)
217 {
218         pg_uuid_t  *arg1 = PG_GETARG_UUID_P(0);
219         pg_uuid_t  *arg2 = PG_GETARG_UUID_P(1);
220
221         PG_RETURN_INT32(uuid_internal_cmp(arg1, arg2));
222 }
223
224 /* hash index support */
225 Datum
226 uuid_hash(PG_FUNCTION_ARGS)
227 {
228         pg_uuid_t  *key = PG_GETARG_UUID_P(0);
229
230         return hash_any(key->data, UUID_LEN);
231 }