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