/* ** OSSP uuid - Universally Unique Identifier ** Copyright (c) 2004-2007 Ralf S. Engelschall <rse@engelschall.com> ** Copyright (c) 2004-2007 The OSSP Project <http://www.ossp.org/> ** ** This file is part of OSSP uuid, a library for the generation ** of UUIDs which can found at http://www.ossp.org/pkg/lib/uuid/ ** ** Permission to use, copy, modify, and distribute this software for ** any purpose with or without fee is hereby granted, provided that ** the above copyright notice and this permission notice appear in all ** copies. ** ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ** uuid.c: PostgreSQL Binding (C part) */ /* own headers */ #include "uuid.h" /* PostgreSQL (part 1/2) headers */ #include "postgres.h" /* system headers */ #include <string.h> /* PostgreSQL (part 2/2) headers */ #include "fmgr.h" #include "lib/stringinfo.h" #include "access/hash.h" /* PostgreSQL module magic cookie (PostgreSQL >= 8.2 only) */ #ifdef PG_MODULE_MAGIC PG_MODULE_MAGIC; #endif /* internal UUID datum data structure */ typedef struct { unsigned char uuid_bin[UUID_LEN_BIN]; } uuid_datum_t; /* forward declarations */ Datum pg_uuid_in (PG_FUNCTION_ARGS); Datum pg_uuid_out (PG_FUNCTION_ARGS); Datum pg_uuid_recv (PG_FUNCTION_ARGS); Datum pg_uuid_send (PG_FUNCTION_ARGS); Datum pg_uuid_hash (PG_FUNCTION_ARGS); Datum pg_uuid_make (PG_FUNCTION_ARGS); Datum pg_uuid_eq (PG_FUNCTION_ARGS); Datum pg_uuid_ne (PG_FUNCTION_ARGS); Datum pg_uuid_lt (PG_FUNCTION_ARGS); Datum pg_uuid_gt (PG_FUNCTION_ARGS); Datum pg_uuid_le (PG_FUNCTION_ARGS); Datum pg_uuid_ge (PG_FUNCTION_ARGS); Datum pg_uuid_cmp (PG_FUNCTION_ARGS); /* API function: uuid_in */ PG_FUNCTION_INFO_V1(pg_uuid_in); Datum pg_uuid_in(PG_FUNCTION_ARGS) { char *uuid_str; uuid_datum_t *uuid_datum; uuid_rc_t rc; uuid_t *uuid; void *vp; size_t len; /* sanity check input argument */ if ((uuid_str = PG_GETARG_CSTRING(0)) == NULL) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("invalid UUID string"))); if ((len = strlen(uuid_str)) != UUID_LEN_STR) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("invalid UUID string length %d (expected %d)", (int)len, UUID_LEN_STR))); /* import as string representation */ if ((rc = uuid_create(&uuid)) != UUID_RC_OK) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to create UUID object: %s", uuid_error(rc)))); if ((rc = uuid_import(uuid, UUID_FMT_STR, uuid_str, len)) != UUID_RC_OK) { uuid_destroy(uuid); ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to import UUID string representation: %s", uuid_error(rc)))); } /* export as binary representation */ if ((uuid_datum = (uuid_datum_t *)palloc(sizeof(uuid_datum_t))) == NULL) { uuid_destroy(uuid); ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to allocate UUID datum"))); } vp = &(uuid_datum->uuid_bin); len = sizeof(uuid_datum->uuid_bin); if ((rc = uuid_export(uuid, UUID_FMT_BIN, &vp, &len)) != UUID_RC_OK) { uuid_destroy(uuid); ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to export UUID binary representation: %s", uuid_error(rc)))); } uuid_destroy(uuid); /* return UUID datum */ PG_RETURN_POINTER(uuid_datum); } /* API function: uuid_out */ PG_FUNCTION_INFO_V1(pg_uuid_out); Datum pg_uuid_out(PG_FUNCTION_ARGS) { uuid_datum_t *uuid_datum; uuid_rc_t rc; uuid_t *uuid; char *uuid_str; void *vp; size_t len; /* sanity check input argument */ if ((uuid_datum = (uuid_datum_t *)PG_GETARG_POINTER(0)) == NULL) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("invalid UUID datum"))); /* import as binary representation */ if ((rc = uuid_create(&uuid)) != UUID_RC_OK) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to create UUID object: %s", uuid_error(rc)))); if ((rc = uuid_import(uuid, UUID_FMT_BIN, uuid_datum->uuid_bin, sizeof(uuid_datum->uuid_bin))) != UUID_RC_OK) { uuid_destroy(uuid); ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to import UUID binary representation: %s", uuid_error(rc)))); } /* export as string representation */ len = UUID_LEN_STR+1; if ((vp = uuid_str = (char *)palloc(len)) == NULL) { uuid_destroy(uuid); ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to allocate UUID string"))); } if ((rc = uuid_export(uuid, UUID_FMT_STR, &vp, &len)) != UUID_RC_OK) { uuid_destroy(uuid); ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to export UUID string representation: %s", uuid_error(rc)))); } uuid_destroy(uuid); /* return UUID string */ PG_RETURN_CSTRING(uuid_str); } /* API function: uuid_recv */ PG_FUNCTION_INFO_V1(pg_uuid_recv); Datum pg_uuid_recv(PG_FUNCTION_ARGS) { StringInfo uuid_internal; uuid_datum_t *uuid_datum; /* sanity check input argument */ if ((uuid_internal = (StringInfo)PG_GETARG_POINTER(0)) == NULL) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("invalid UUID StringInfo object"))); if (uuid_internal->len != UUID_LEN_BIN) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("invalid UUID binary length %d (expected %d)", uuid_internal->len, UUID_LEN_BIN))); /* import as binary representation */ if ((uuid_datum = (uuid_datum_t *)palloc(sizeof(uuid_datum_t))) == NULL) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to allocate UUID datum"))); memcpy(uuid_datum->uuid_bin, uuid_internal->data, uuid_internal->len); /* return UUID datum */ PG_RETURN_POINTER(uuid_datum); } /* API function: uuid_send */ PG_FUNCTION_INFO_V1(pg_uuid_send); Datum pg_uuid_send(PG_FUNCTION_ARGS) { uuid_datum_t *uuid_datum; bytea *uuid_bytea; /* sanity check input argument */ if ((uuid_datum = (uuid_datum_t *)PG_GETARG_POINTER(0)) == NULL) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("invalid UUID datum"))); /* export as binary representation */ if ((uuid_bytea = (bytea *)palloc(VARHDRSZ + UUID_LEN_BIN)) == NULL) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to allocate UUID bytea"))); #if defined(SET_VARSIZE) /* PostgreSQL >= 8.3 */ SET_VARSIZE(uuid_bytea, VARHDRSZ + UUID_LEN_BIN); #else uuid_bytea->vl_len = VARHDRSZ + UUID_LEN_BIN; #endif memcpy(uuid_bytea->vl_dat, uuid_datum->uuid_bin, UUID_LEN_BIN); /* return UUID bytea */ PG_RETURN_BYTEA_P(uuid_bytea); } /* API function: uuid_make */ PG_FUNCTION_INFO_V1(pg_uuid_make); Datum pg_uuid_make(PG_FUNCTION_ARGS) { uuid_t *uuid; uuid_t *uuid_ns; uuid_rc_t rc; int version; unsigned int mode = 0; uuid_datum_t *uuid_datum; char *str_ns; char *str_name; void *vp; size_t len; /* sanity check input argument */ version = (int)PG_GETARG_INT32(0); switch (version) { case 1: mode = UUID_MAKE_V1; break; case 3: mode = UUID_MAKE_V3; break; case 4: mode = UUID_MAKE_V4; break; case 5: mode = UUID_MAKE_V5; break; default: ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("invalid UUID version %d (expected 1, 3, 4 or 5)", version))); } if ( ((mode & (UUID_MAKE_V1|UUID_MAKE_V4)) && PG_NARGS() != 1) || ((mode & (UUID_MAKE_V3|UUID_MAKE_V5)) && PG_NARGS() != 3)) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("invalid number (%d) of arguments", PG_NARGS()))); /* make a new UUID */ if ((rc = uuid_create(&uuid)) != UUID_RC_OK) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to create UUID object: %s", uuid_error(rc)))); if (version == 3 || version == 5) { if ((str_ns = PG_GETARG_CSTRING(1)) == NULL) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("invalid namespace UUID string"))); if ((str_name = PG_GETARG_CSTRING(2)) == NULL) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("invalid name string"))); if ((rc = uuid_create(&uuid_ns)) != UUID_RC_OK) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to create UUID namespace object: %s", uuid_error(rc)))); if ((rc = uuid_load(uuid_ns, str_ns)) != UUID_RC_OK) { if ((rc = uuid_import(uuid_ns, UUID_FMT_STR, str_ns, strlen(str_ns))) != UUID_RC_OK) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to import UUID namespace: %s", uuid_error(rc)))); } if ((rc = uuid_make(uuid, mode, uuid_ns, str_name)) != UUID_RC_OK) { uuid_destroy(uuid); ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to make v%d UUID: %s", version, uuid_error(rc)))); } uuid_destroy(uuid_ns); } else { if ((rc = uuid_make(uuid, mode)) != UUID_RC_OK) { uuid_destroy(uuid); ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to make v%d UUID: %s", version, uuid_error(rc)))); } } /* export as binary representation */ if ((uuid_datum = (uuid_datum_t *)palloc(sizeof(uuid_datum_t))) == NULL) { uuid_destroy(uuid); ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to allocate UUID datum"))); } vp = &(uuid_datum->uuid_bin); len = sizeof(uuid_datum->uuid_bin); if ((rc = uuid_export(uuid, UUID_FMT_BIN, &vp, &len)) != UUID_RC_OK) { uuid_destroy(uuid); ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to export UUID binary representation: %s", uuid_error(rc)))); } uuid_destroy(uuid); PG_RETURN_POINTER(uuid_datum); } /* API function: uuid_hash */ PG_FUNCTION_INFO_V1(pg_uuid_hash); Datum pg_uuid_hash(PG_FUNCTION_ARGS) { uuid_datum_t *uuid_datum; /* sanity check input argument */ if ((uuid_datum = (uuid_datum_t *)PG_GETARG_POINTER(0)) == NULL) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("invalid UUID datum argument"))); /* return hash value of the UUID */ PG_RETURN_INT32(hash_any(uuid_datum->uuid_bin, sizeof(uuid_datum->uuid_bin))); } /* INTERNAL function: _uuid_cmp */ static int _uuid_cmp(PG_FUNCTION_ARGS) { uuid_datum_t *uuid_datum1; uuid_datum_t *uuid_datum2; uuid_t *uuid1; uuid_t *uuid2; uuid_rc_t rc; int result; /* sanity check input argument */ if ((uuid_datum1 = (uuid_datum_t *)PG_GETARG_POINTER(0)) == NULL) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("invalid first UUID datum argument"))); if ((uuid_datum2 = (uuid_datum_t *)PG_GETARG_POINTER(1)) == NULL) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("invalid second UUID datum argument"))); /* load both UUIDs */ if ((rc = uuid_create(&uuid1)) != UUID_RC_OK) ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to create UUID object: %s", uuid_error(rc)))); if ((rc = uuid_create(&uuid2)) != UUID_RC_OK) { uuid_destroy(uuid1); ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to create UUID object: %s", uuid_error(rc)))); } if ((rc = uuid_import(uuid1, UUID_FMT_BIN, uuid_datum1->uuid_bin, sizeof(uuid_datum1->uuid_bin))) != UUID_RC_OK) { uuid_destroy(uuid1); uuid_destroy(uuid2); ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to import UUID: %s", uuid_error(rc)))); } if ((rc = uuid_import(uuid2, UUID_FMT_BIN, uuid_datum2->uuid_bin, sizeof(uuid_datum2->uuid_bin))) != UUID_RC_OK) { uuid_destroy(uuid1); uuid_destroy(uuid2); ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to import UUID: %s", uuid_error(rc)))); } /* compare UUIDs */ if ((rc = uuid_compare(uuid1, uuid2, &result)) != UUID_RC_OK) { uuid_destroy(uuid1); uuid_destroy(uuid2); ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("failed to compare UUID objects: %s", uuid_error(rc)))); } /* cleanup */ uuid_destroy(uuid1); uuid_destroy(uuid2); /* return result */ return result; } /* API function: uuid_eq */ PG_FUNCTION_INFO_V1(pg_uuid_eq); Datum pg_uuid_eq(PG_FUNCTION_ARGS) { int rc; rc = _uuid_cmp(fcinfo); PG_RETURN_BOOL(rc == 0); } /* API function: uuid_ne */ PG_FUNCTION_INFO_V1(pg_uuid_ne); Datum pg_uuid_ne(PG_FUNCTION_ARGS) { int rc; rc = _uuid_cmp(fcinfo); PG_RETURN_BOOL(rc != 0); } /* API function: uuid_lt */ PG_FUNCTION_INFO_V1(pg_uuid_lt); Datum pg_uuid_lt(PG_FUNCTION_ARGS) { int rc; rc = _uuid_cmp(fcinfo); PG_RETURN_BOOL(rc == -1); } /* API function: uuid_gt */ PG_FUNCTION_INFO_V1(pg_uuid_gt); Datum pg_uuid_gt(PG_FUNCTION_ARGS) { int rc; rc = _uuid_cmp(fcinfo); PG_RETURN_BOOL(rc == 1); } /* API function: uuid_le */ PG_FUNCTION_INFO_V1(pg_uuid_le); Datum pg_uuid_le(PG_FUNCTION_ARGS) { int rc; rc = _uuid_cmp(fcinfo); PG_RETURN_BOOL(rc < 1); } /* API function: uuid_ge */ PG_FUNCTION_INFO_V1(pg_uuid_ge); Datum pg_uuid_ge(PG_FUNCTION_ARGS) { int rc; rc = _uuid_cmp(fcinfo); PG_RETURN_BOOL(rc > -1); } /* API function: uuid_cmp */ PG_FUNCTION_INFO_V1(pg_uuid_cmp); Datum pg_uuid_cmp(PG_FUNCTION_ARGS) { int rc; rc = _uuid_cmp(fcinfo); PG_RETURN_INT32(rc); }