1 /*
2 ** OSSP uuid - Universally Unique Identifier
3 ** Copyright (c) 2004-2007 Ralf S. Engelschall <rse@engelschall.com>
4 ** Copyright (c) 2004-2007 The OSSP Project <http://www.ossp.org/>
5 **
6 ** This file is part of OSSP uuid, a library for the generation
7 ** of UUIDs which can found at http://www.ossp.org/pkg/lib/uuid/
8 **
9 ** Permission to use, copy, modify, and distribute this software for
10 ** any purpose with or without fee is hereby granted, provided that
11 ** the above copyright notice and this permission notice appear in all
12 ** copies.
13 **
14 ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
15 ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16 ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 ** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
18 ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
21 ** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
24 ** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 ** SUCH DAMAGE.
26 **
27 ** uuid.c: PostgreSQL Binding (C part)
28 */
29
30 /* own headers */
31 #include "uuid.h"
32
33 /* PostgreSQL (part 1/2) headers */
34 #include "postgres.h"
35
36 /* system headers */
37 #include <string.h>
38
39 /* PostgreSQL (part 2/2) headers */
40 #include "fmgr.h"
41 #include "lib/stringinfo.h"
42 #include "access/hash.h"
43
44 /* PostgreSQL module magic cookie
45 (PostgreSQL >= 8.2 only) */
46 #ifdef PG_MODULE_MAGIC
47 PG_MODULE_MAGIC;
48 #endif
49
50 /* internal UUID datum data structure */
51 typedef struct {
52 unsigned char uuid_bin[UUID_LEN_BIN];
53 } uuid_datum_t;
54
55 /* forward declarations */
56 Datum pg_uuid_in (PG_FUNCTION_ARGS);
57 Datum pg_uuid_out (PG_FUNCTION_ARGS);
58 Datum pg_uuid_recv (PG_FUNCTION_ARGS);
59 Datum pg_uuid_send (PG_FUNCTION_ARGS);
60 Datum pg_uuid_hash (PG_FUNCTION_ARGS);
61 Datum pg_uuid_make (PG_FUNCTION_ARGS);
62 Datum pg_uuid_eq (PG_FUNCTION_ARGS);
63 Datum pg_uuid_ne (PG_FUNCTION_ARGS);
64 Datum pg_uuid_lt (PG_FUNCTION_ARGS);
65 Datum pg_uuid_gt (PG_FUNCTION_ARGS);
66 Datum pg_uuid_le (PG_FUNCTION_ARGS);
67 Datum pg_uuid_ge (PG_FUNCTION_ARGS);
68 Datum pg_uuid_cmp (PG_FUNCTION_ARGS);
69
70 /* API function: uuid_in */
71 PG_FUNCTION_INFO_V1(pg_uuid_in);
pg_uuid_in(PG_FUNCTION_ARGS)72 Datum pg_uuid_in(PG_FUNCTION_ARGS)
73 {
74 char *uuid_str;
75 uuid_datum_t *uuid_datum;
76 uuid_rc_t rc;
77 uuid_t *uuid;
78 void *vp;
79 size_t len;
80
81 /* sanity check input argument */
82 if ((uuid_str = PG_GETARG_CSTRING(0)) == NULL)
83 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
84 errmsg("invalid UUID string")));
85 if ((len = strlen(uuid_str)) != UUID_LEN_STR)
86 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
87 errmsg("invalid UUID string length %d (expected %d)", (int)len, UUID_LEN_STR)));
88
89 /* import as string representation */
90 if ((rc = uuid_create(&uuid)) != UUID_RC_OK)
91 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
92 errmsg("failed to create UUID object: %s", uuid_error(rc))));
93 if ((rc = uuid_import(uuid, UUID_FMT_STR, uuid_str, len)) != UUID_RC_OK) {
94 uuid_destroy(uuid);
95 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
96 errmsg("failed to import UUID string representation: %s", uuid_error(rc))));
97 }
98
99 /* export as binary representation */
100 if ((uuid_datum = (uuid_datum_t *)palloc(sizeof(uuid_datum_t))) == NULL) {
101 uuid_destroy(uuid);
102 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
103 errmsg("failed to allocate UUID datum")));
104 }
105 vp = &(uuid_datum->uuid_bin);
106 len = sizeof(uuid_datum->uuid_bin);
107 if ((rc = uuid_export(uuid, UUID_FMT_BIN, &vp, &len)) != UUID_RC_OK) {
108 uuid_destroy(uuid);
109 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
110 errmsg("failed to export UUID binary representation: %s", uuid_error(rc))));
111 }
112 uuid_destroy(uuid);
113
114 /* return UUID datum */
115 PG_RETURN_POINTER(uuid_datum);
116 }
117
118 /* API function: uuid_out */
119 PG_FUNCTION_INFO_V1(pg_uuid_out);
pg_uuid_out(PG_FUNCTION_ARGS)120 Datum pg_uuid_out(PG_FUNCTION_ARGS)
121 {
122 uuid_datum_t *uuid_datum;
123 uuid_rc_t rc;
124 uuid_t *uuid;
125 char *uuid_str;
126 void *vp;
127 size_t len;
128
129 /* sanity check input argument */
130 if ((uuid_datum = (uuid_datum_t *)PG_GETARG_POINTER(0)) == NULL)
131 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
132 errmsg("invalid UUID datum")));
133
134 /* import as binary representation */
135 if ((rc = uuid_create(&uuid)) != UUID_RC_OK)
136 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
137 errmsg("failed to create UUID object: %s", uuid_error(rc))));
138 if ((rc = uuid_import(uuid, UUID_FMT_BIN, uuid_datum->uuid_bin, sizeof(uuid_datum->uuid_bin))) != UUID_RC_OK) {
139 uuid_destroy(uuid);
140 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
141 errmsg("failed to import UUID binary representation: %s", uuid_error(rc))));
142 }
143
144 /* export as string representation */
145 len = UUID_LEN_STR+1;
146 if ((vp = uuid_str = (char *)palloc(len)) == NULL) {
147 uuid_destroy(uuid);
148 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
149 errmsg("failed to allocate UUID string")));
150 }
151 if ((rc = uuid_export(uuid, UUID_FMT_STR, &vp, &len)) != UUID_RC_OK) {
152 uuid_destroy(uuid);
153 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
154 errmsg("failed to export UUID string representation: %s", uuid_error(rc))));
155 }
156 uuid_destroy(uuid);
157
158 /* return UUID string */
159 PG_RETURN_CSTRING(uuid_str);
160 }
161
162 /* API function: uuid_recv */
163 PG_FUNCTION_INFO_V1(pg_uuid_recv);
pg_uuid_recv(PG_FUNCTION_ARGS)164 Datum pg_uuid_recv(PG_FUNCTION_ARGS)
165 {
166 StringInfo uuid_internal;
167 uuid_datum_t *uuid_datum;
168
169 /* sanity check input argument */
170 if ((uuid_internal = (StringInfo)PG_GETARG_POINTER(0)) == NULL)
171 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
172 errmsg("invalid UUID StringInfo object")));
173 if (uuid_internal->len != UUID_LEN_BIN)
174 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
175 errmsg("invalid UUID binary length %d (expected %d)", uuid_internal->len, UUID_LEN_BIN)));
176
177 /* import as binary representation */
178 if ((uuid_datum = (uuid_datum_t *)palloc(sizeof(uuid_datum_t))) == NULL)
179 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
180 errmsg("failed to allocate UUID datum")));
181 memcpy(uuid_datum->uuid_bin, uuid_internal->data, uuid_internal->len);
182
183 /* return UUID datum */
184 PG_RETURN_POINTER(uuid_datum);
185 }
186
187 /* API function: uuid_send */
188 PG_FUNCTION_INFO_V1(pg_uuid_send);
pg_uuid_send(PG_FUNCTION_ARGS)189 Datum pg_uuid_send(PG_FUNCTION_ARGS)
190 {
191 uuid_datum_t *uuid_datum;
192 bytea *uuid_bytea;
193
194 /* sanity check input argument */
195 if ((uuid_datum = (uuid_datum_t *)PG_GETARG_POINTER(0)) == NULL)
196 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
197 errmsg("invalid UUID datum")));
198
199 /* export as binary representation */
200 if ((uuid_bytea = (bytea *)palloc(VARHDRSZ + UUID_LEN_BIN)) == NULL)
201 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
202 errmsg("failed to allocate UUID bytea")));
203 #if defined(SET_VARSIZE) /* PostgreSQL >= 8.3 */
204 SET_VARSIZE(uuid_bytea, VARHDRSZ + UUID_LEN_BIN);
205 #else
206 uuid_bytea->vl_len = VARHDRSZ + UUID_LEN_BIN;
207 #endif
208 memcpy(uuid_bytea->vl_dat, uuid_datum->uuid_bin, UUID_LEN_BIN);
209
210 /* return UUID bytea */
211 PG_RETURN_BYTEA_P(uuid_bytea);
212 }
213
214 /* API function: uuid_make */
215 PG_FUNCTION_INFO_V1(pg_uuid_make);
pg_uuid_make(PG_FUNCTION_ARGS)216 Datum pg_uuid_make(PG_FUNCTION_ARGS)
217 {
218 uuid_t *uuid;
219 uuid_t *uuid_ns;
220 uuid_rc_t rc;
221 int version;
222 unsigned int mode = 0;
223 uuid_datum_t *uuid_datum;
224 char *str_ns;
225 char *str_name;
226 void *vp;
227 size_t len;
228
229 /* sanity check input argument */
230 version = (int)PG_GETARG_INT32(0);
231 switch (version) {
232 case 1: mode = UUID_MAKE_V1; break;
233 case 3: mode = UUID_MAKE_V3; break;
234 case 4: mode = UUID_MAKE_V4; break;
235 case 5: mode = UUID_MAKE_V5; break;
236 default:
237 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
238 errmsg("invalid UUID version %d (expected 1, 3, 4 or 5)", version)));
239 }
240 if ( ((mode & (UUID_MAKE_V1|UUID_MAKE_V4)) && PG_NARGS() != 1)
241 || ((mode & (UUID_MAKE_V3|UUID_MAKE_V5)) && PG_NARGS() != 3))
242 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
243 errmsg("invalid number (%d) of arguments", PG_NARGS())));
244
245 /* make a new UUID */
246 if ((rc = uuid_create(&uuid)) != UUID_RC_OK)
247 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
248 errmsg("failed to create UUID object: %s", uuid_error(rc))));
249 if (version == 3 || version == 5) {
250 if ((str_ns = PG_GETARG_CSTRING(1)) == NULL)
251 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
252 errmsg("invalid namespace UUID string")));
253 if ((str_name = PG_GETARG_CSTRING(2)) == NULL)
254 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
255 errmsg("invalid name string")));
256 if ((rc = uuid_create(&uuid_ns)) != UUID_RC_OK)
257 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
258 errmsg("failed to create UUID namespace object: %s", uuid_error(rc))));
259 if ((rc = uuid_load(uuid_ns, str_ns)) != UUID_RC_OK) {
260 if ((rc = uuid_import(uuid_ns, UUID_FMT_STR, str_ns, strlen(str_ns))) != UUID_RC_OK)
261 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
262 errmsg("failed to import UUID namespace: %s", uuid_error(rc))));
263 }
264 if ((rc = uuid_make(uuid, mode, uuid_ns, str_name)) != UUID_RC_OK) {
265 uuid_destroy(uuid);
266 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
267 errmsg("failed to make v%d UUID: %s", version, uuid_error(rc))));
268 }
269 uuid_destroy(uuid_ns);
270 }
271 else {
272 if ((rc = uuid_make(uuid, mode)) != UUID_RC_OK) {
273 uuid_destroy(uuid);
274 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
275 errmsg("failed to make v%d UUID: %s", version, uuid_error(rc))));
276 }
277 }
278
279 /* export as binary representation */
280 if ((uuid_datum = (uuid_datum_t *)palloc(sizeof(uuid_datum_t))) == NULL) {
281 uuid_destroy(uuid);
282 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
283 errmsg("failed to allocate UUID datum")));
284 }
285 vp = &(uuid_datum->uuid_bin);
286 len = sizeof(uuid_datum->uuid_bin);
287 if ((rc = uuid_export(uuid, UUID_FMT_BIN, &vp, &len)) != UUID_RC_OK) {
288 uuid_destroy(uuid);
289 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
290 errmsg("failed to export UUID binary representation: %s", uuid_error(rc))));
291 }
292 uuid_destroy(uuid);
293 PG_RETURN_POINTER(uuid_datum);
294 }
295
296 /* API function: uuid_hash */
297 PG_FUNCTION_INFO_V1(pg_uuid_hash);
pg_uuid_hash(PG_FUNCTION_ARGS)298 Datum pg_uuid_hash(PG_FUNCTION_ARGS)
299 {
300 uuid_datum_t *uuid_datum;
301
302 /* sanity check input argument */
303 if ((uuid_datum = (uuid_datum_t *)PG_GETARG_POINTER(0)) == NULL)
304 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
305 errmsg("invalid UUID datum argument")));
306
307 /* return hash value of the UUID */
308 PG_RETURN_INT32(hash_any(uuid_datum->uuid_bin, sizeof(uuid_datum->uuid_bin)));
309 }
310
311 /* INTERNAL function: _uuid_cmp */
_uuid_cmp(PG_FUNCTION_ARGS)312 static int _uuid_cmp(PG_FUNCTION_ARGS)
313 {
314 uuid_datum_t *uuid_datum1;
315 uuid_datum_t *uuid_datum2;
316 uuid_t *uuid1;
317 uuid_t *uuid2;
318 uuid_rc_t rc;
319 int result;
320
321 /* sanity check input argument */
322 if ((uuid_datum1 = (uuid_datum_t *)PG_GETARG_POINTER(0)) == NULL)
323 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
324 errmsg("invalid first UUID datum argument")));
325 if ((uuid_datum2 = (uuid_datum_t *)PG_GETARG_POINTER(1)) == NULL)
326 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
327 errmsg("invalid second UUID datum argument")));
328
329 /* load both UUIDs */
330 if ((rc = uuid_create(&uuid1)) != UUID_RC_OK)
331 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
332 errmsg("failed to create UUID object: %s", uuid_error(rc))));
333 if ((rc = uuid_create(&uuid2)) != UUID_RC_OK) {
334 uuid_destroy(uuid1);
335 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
336 errmsg("failed to create UUID object: %s", uuid_error(rc))));
337 }
338 if ((rc = uuid_import(uuid1, UUID_FMT_BIN, uuid_datum1->uuid_bin, sizeof(uuid_datum1->uuid_bin))) != UUID_RC_OK) {
339 uuid_destroy(uuid1);
340 uuid_destroy(uuid2);
341 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
342 errmsg("failed to import UUID: %s", uuid_error(rc))));
343 }
344 if ((rc = uuid_import(uuid2, UUID_FMT_BIN, uuid_datum2->uuid_bin, sizeof(uuid_datum2->uuid_bin))) != UUID_RC_OK) {
345 uuid_destroy(uuid1);
346 uuid_destroy(uuid2);
347 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
348 errmsg("failed to import UUID: %s", uuid_error(rc))));
349 }
350
351 /* compare UUIDs */
352 if ((rc = uuid_compare(uuid1, uuid2, &result)) != UUID_RC_OK) {
353 uuid_destroy(uuid1);
354 uuid_destroy(uuid2);
355 ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
356 errmsg("failed to compare UUID objects: %s", uuid_error(rc))));
357 }
358
359 /* cleanup */
360 uuid_destroy(uuid1);
361 uuid_destroy(uuid2);
362
363 /* return result */
364 return result;
365 }
366
367 /* API function: uuid_eq */
368 PG_FUNCTION_INFO_V1(pg_uuid_eq);
pg_uuid_eq(PG_FUNCTION_ARGS)369 Datum pg_uuid_eq(PG_FUNCTION_ARGS)
370 {
371 int rc;
372
373 rc = _uuid_cmp(fcinfo);
374 PG_RETURN_BOOL(rc == 0);
375 }
376
377 /* API function: uuid_ne */
378 PG_FUNCTION_INFO_V1(pg_uuid_ne);
pg_uuid_ne(PG_FUNCTION_ARGS)379 Datum pg_uuid_ne(PG_FUNCTION_ARGS)
380 {
381 int rc;
382
383 rc = _uuid_cmp(fcinfo);
384 PG_RETURN_BOOL(rc != 0);
385 }
386
387 /* API function: uuid_lt */
388 PG_FUNCTION_INFO_V1(pg_uuid_lt);
pg_uuid_lt(PG_FUNCTION_ARGS)389 Datum pg_uuid_lt(PG_FUNCTION_ARGS)
390 {
391 int rc;
392
393 rc = _uuid_cmp(fcinfo);
394 PG_RETURN_BOOL(rc == -1);
395 }
396
397 /* API function: uuid_gt */
398 PG_FUNCTION_INFO_V1(pg_uuid_gt);
pg_uuid_gt(PG_FUNCTION_ARGS)399 Datum pg_uuid_gt(PG_FUNCTION_ARGS)
400 {
401 int rc;
402
403 rc = _uuid_cmp(fcinfo);
404 PG_RETURN_BOOL(rc == 1);
405 }
406
407 /* API function: uuid_le */
408 PG_FUNCTION_INFO_V1(pg_uuid_le);
pg_uuid_le(PG_FUNCTION_ARGS)409 Datum pg_uuid_le(PG_FUNCTION_ARGS)
410 {
411 int rc;
412
413 rc = _uuid_cmp(fcinfo);
414 PG_RETURN_BOOL(rc < 1);
415 }
416
417 /* API function: uuid_ge */
418 PG_FUNCTION_INFO_V1(pg_uuid_ge);
pg_uuid_ge(PG_FUNCTION_ARGS)419 Datum pg_uuid_ge(PG_FUNCTION_ARGS)
420 {
421 int rc;
422
423 rc = _uuid_cmp(fcinfo);
424 PG_RETURN_BOOL(rc > -1);
425 }
426
427 /* API function: uuid_cmp */
428 PG_FUNCTION_INFO_V1(pg_uuid_cmp);
pg_uuid_cmp(PG_FUNCTION_ARGS)429 Datum pg_uuid_cmp(PG_FUNCTION_ARGS)
430 {
431 int rc;
432
433 rc = _uuid_cmp(fcinfo);
434 PG_RETURN_INT32(rc);
435 }
436
437