/* * uuid type support functions for Postgres 8.4 * * written by Nathan Wagner and placed in the public domain */ #include "postgres.h" #include "fmgr.h" #include "utils/builtins.h" #include "utils/uuid.h" #include "utils/numeric.h" #include "utils/inet.h" #include "utils/timestamp.h" #include "utils/datetime.h" #include "utils/varbit.h" #include "pduuid.h" #define UUID_LEN_BIN 16 PG_MODULE_MAGIC; void _PG_init(void); void _PG_fini(void); Datum uuid_nil(PG_FUNCTION_ARGS); Datum uuid_dns(PG_FUNCTION_ARGS); Datum uuid_url(PG_FUNCTION_ARGS); Datum uuid_oid(PG_FUNCTION_ARGS); Datum uuid_x500(PG_FUNCTION_ARGS); Datum uuid_recent(PG_FUNCTION_ARGS); Datum uuid_gen_v1(PG_FUNCTION_ARGS); Datum uuid_gen_v1_mc(PG_FUNCTION_ARGS); Datum uuid_gen_v3(PG_FUNCTION_ARGS); Datum uuid_gen_v4(PG_FUNCTION_ARGS); Datum uuid_gen_v5(PG_FUNCTION_ARGS); Datum uuid_cast_bit(PG_FUNCTION_ARGS); Datum uuid_cast_from_bit(PG_FUNCTION_ARGS); Datum uuid_cast_numeric(PG_FUNCTION_ARGS); Datum uuid_cast_from_numeric(PG_FUNCTION_ARGS); Datum uuid_cast_bytea(PG_FUNCTION_ARGS); Datum uuid_cast_from_bytea(PG_FUNCTION_ARGS); Datum uuid_extract_version(PG_FUNCTION_ARGS); Datum uuid_extract_macaddr(PG_FUNCTION_ARGS); Datum uuid_extract_timestamp(PG_FUNCTION_ARGS); Datum uuid_ns_dns(PG_FUNCTION_ARGS); Datum uuid_ns_url(PG_FUNCTION_ARGS); Datum uuid_ns_oid(PG_FUNCTION_ARGS); Datum uuid_ns_x500(PG_FUNCTION_ARGS); static pd_uuid_t i_gid; static pd_uuid_t i_ns_uuid; static pd_uuid_t *gid = NULL; static pd_uuid_t *ns_uuid = NULL; static struct pd_uuid_state us; void _PG_init(void) { gid = &i_gid; memset(gid, 0, 16); ns_uuid = &i_ns_uuid; memset(ns_uuid, 0, 16); pd_uuid_init_state(&us); return; } #if 0 void _PG_fini(void) { if (gid) { uuid_destroy(gid); } if (ns_uuid) { uuid_destroy(ns_uuid); } return; } #endif PG_FUNCTION_INFO_V1(uuid_nil); Datum uuid_nil(PG_FUNCTION_ARGS) { pg_uuid_t *uuid; uuid = palloc(UUID_LEN_BIN); memset(uuid, 0, 16); PG_RETURN_UUID_P(uuid); } PG_FUNCTION_INFO_V1(uuid_dns); Datum uuid_dns(PG_FUNCTION_ARGS) { pg_uuid_t *uuid; unsigned char bytes[] = { 0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }; uuid = palloc(UUID_LEN_BIN); memcpy(uuid, bytes, 16); PG_RETURN_UUID_P(uuid); } PG_FUNCTION_INFO_V1(uuid_url); Datum uuid_url(PG_FUNCTION_ARGS) { pg_uuid_t *uuid; unsigned char bytes[] = { 0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }; uuid = palloc(UUID_LEN_BIN); memcpy(uuid, bytes, 16); PG_RETURN_UUID_P(uuid); } PG_FUNCTION_INFO_V1(uuid_oid); Datum uuid_oid(PG_FUNCTION_ARGS) { pg_uuid_t *uuid; unsigned char bytes[] = { 0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }; uuid = palloc(UUID_LEN_BIN); memcpy(uuid, bytes, 16); PG_RETURN_UUID_P(uuid); } PG_FUNCTION_INFO_V1(uuid_x500); Datum uuid_x500(PG_FUNCTION_ARGS) { pg_uuid_t *uuid; unsigned char bytes[] = { 0x6b, 0xa7, 0xb8, 0x13, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 }; uuid = palloc(UUID_LEN_BIN); memcpy(uuid, bytes, 16); PG_RETURN_UUID_P(uuid); } static pg_uuid_t *export(pd_uuid_t *u) { pg_uuid_t *pgid; pgid = palloc(UUID_LEN_BIN); memcpy(pgid, u, 16); return pgid; } static void import(pd_uuid_t *u, pg_uuid_t *pgid) { memcpy(u, pgid, 16); } PG_FUNCTION_INFO_V1(uuid_recent); Datum uuid_recent(PG_FUNCTION_ARGS) { pg_uuid_t *pgid; pgid = export(gid); PG_RETURN_UUID_P(pgid); } PG_FUNCTION_INFO_V1(uuid_gen_v1); Datum uuid_gen_v1(PG_FUNCTION_ARGS) { pg_uuid_t *pgid; if (!pd_uuid_make_v1(&us, gid)) { elog(ERROR, "can't make uuid"); } pgid = export(gid); PG_RETURN_UUID_P(pgid); } PG_FUNCTION_INFO_V1(uuid_gen_v1_mc); Datum uuid_gen_v1_mc(PG_FUNCTION_ARGS) { pg_uuid_t *pgid; if (!pd_uuid_make_v1mc(&us, gid)) { elog(ERROR, "can't make uuid"); } pgid = export(gid); PG_RETURN_UUID_P(pgid); } PG_FUNCTION_INFO_V1(uuid_gen_v3); Datum uuid_gen_v3(PG_FUNCTION_ARGS) { pg_uuid_t *pgid; pg_uuid_t *ns; text *t; char *string; ns = PG_GETARG_UUID_P(0); t = PG_GETARG_TEXT_P(1); import(ns_uuid, ns); string = text_to_cstring(t); if (!pd_uuid_make_v3(&us, gid, ns_uuid, string, strlen(string))) { elog(ERROR, "can't make uuid"); } pfree(string); pgid = export(gid); PG_RETURN_UUID_P(pgid); } PG_FUNCTION_INFO_V1(uuid_gen_v4); Datum uuid_gen_v4(PG_FUNCTION_ARGS) { pg_uuid_t *pgid; if (!pd_uuid_make_v4(&us, gid)) { elog(ERROR, "can't make uuid"); } pgid = export(gid); PG_RETURN_UUID_P(pgid); } PG_FUNCTION_INFO_V1(uuid_gen_v5); Datum uuid_gen_v5(PG_FUNCTION_ARGS) { pg_uuid_t *pgid; pg_uuid_t *ns; text *t; char *string; if (!gid || !ns_uuid) { elog(ERROR, "UUID library failed to initialize"); } ns = PG_GETARG_UUID_P(0); t = PG_GETARG_TEXT_P(1); import(ns_uuid, ns); string = text_to_cstring(t); if (!pd_uuid_make_v5(&us, gid,ns_uuid, string, strlen(string))) { elog(ERROR, "can't make uuid"); } pfree(string); pgid = export(gid); PG_RETURN_UUID_P(pgid); } PG_FUNCTION_INFO_V1(uuid_cast_bit); Datum uuid_cast_bit(PG_FUNCTION_ARGS) { VarBit *bit; pg_uuid_t *uuid = PG_GETARG_UUID_P(0); bit = palloc(VARBITTOTALLEN(128)); bit->bit_len = 128; bit->vl_len_ = VARBITTOTALLEN(128); memcpy(VARBITS(bit), uuid, 16); PG_RETURN_VARBIT_P(bit); } PG_FUNCTION_INFO_V1(uuid_cast_from_bit); Datum uuid_cast_from_bit(PG_FUNCTION_ARGS) { pg_uuid_t *uuid; VarBit *bit = PG_GETARG_VARBIT_P(0); if (bit->bit_len != 128) { /* Throw error? */ elog(ERROR, "can't cast from bit(%d) to uuid, must cast from bit(128)", bit->bit_len); } uuid = palloc(UUID_LEN_BIN); memcpy(uuid, VARBITS(bit), 16); PG_RETURN_UUID_P(uuid); } PG_FUNCTION_INFO_V1(uuid_cast_bytea); Datum uuid_cast_bytea(PG_FUNCTION_ARGS) { bytea *bit; pg_uuid_t *uuid = PG_GETARG_UUID_P(0); bit = palloc(VARHDRSZ + 16); SET_VARSIZE(bit, VARHDRSZ + 16); memcpy(VARDATA(bit), uuid, 16); PG_RETURN_BYTEA_P(bit); } PG_FUNCTION_INFO_V1(uuid_cast_from_bytea); Datum uuid_cast_from_bytea(PG_FUNCTION_ARGS) { pg_uuid_t *uuid; bytea *bit = PG_GETARG_BYTEA_P(0); if (VARSIZE_ANY_EXHDR(bit) != 16) { elog(ERROR, "invalid length of bytea for cast to uuid, need 16, attempted %ld", VARSIZE_ANY_EXHDR(bit)); } uuid = palloc(UUID_LEN_BIN); memcpy(uuid, VARDATA(bit), 16); PG_RETURN_UUID_P(uuid); } /* TODO construct the uuid directly from the numeric */ /* TODO have a static c256 numeric */ PG_FUNCTION_INFO_V1(uuid_cast_from_numeric); Datum uuid_cast_from_numeric(PG_FUNCTION_ARGS) { int i; unsigned char *data; int32 byte; Numeric c256; Numeric input; Numeric result; pg_uuid_t *uuid; input = PG_GETARG_NUMERIC(0); uuid = palloc(UUID_LEN_BIN); data = (unsigned char *)uuid; c256 = DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum(256))); for (i=15;i>=0;i--) { result = DatumGetNumeric(DirectFunctionCall2(numeric_mod, NumericGetDatum(input), NumericGetDatum(c256))); input = DatumGetNumeric(DirectFunctionCall2(numeric_div_trunc, NumericGetDatum(input), NumericGetDatum(c256))); byte = DatumGetInt32(DirectFunctionCall1(numeric_int4, NumericGetDatum(result))); data[i] = byte & 0xff; } /* TODO will this leak if I don't do this? Segfault if i do? */ pfree(c256); PG_RETURN_UUID_P(uuid); } /* * TODO construct the numeric directly */ PG_FUNCTION_INFO_V1(uuid_cast_numeric); Datum uuid_cast_numeric(PG_FUNCTION_ARGS) { int i; unsigned char *data; int32 byte; Numeric result; Numeric c256; pg_uuid_t *uuid = PG_GETARG_UUID_P(0); data = (unsigned char *)uuid; result = DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum(0))); c256 = DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum(256))); for (i=0;i<16;i++) { byte = (int32) data[i]; result = DatumGetNumeric(DirectFunctionCall2(numeric_mul, NumericGetDatum(result), NumericGetDatum(c256))); result = DatumGetNumeric( DirectFunctionCall2(numeric_add, NumericGetDatum(result), DirectFunctionCall1(int4_numeric, Int32GetDatum(byte)))); } pfree(c256); PG_RETURN_NUMERIC(result); } PG_FUNCTION_INFO_V1(uuid_extract_version); Datum uuid_extract_version(PG_FUNCTION_ARGS) { int32 version; unsigned char *data; pg_uuid_t *uuid = PG_GETARG_UUID_P(0); data = (unsigned char *)uuid; version = data[6] >> 4; PG_RETURN_INT32(version); } PG_FUNCTION_INFO_V1(uuid_extract_macaddr); Datum uuid_extract_macaddr(PG_FUNCTION_ARGS) { macaddr *mac; unsigned char *data; pg_uuid_t *uuid = PG_GETARG_UUID_P(0); data = (unsigned char *)uuid; mac = palloc(sizeof *mac); mac->a = data[10]; mac->b = data[11]; mac->c = data[12]; mac->d = data[13]; mac->e = data[14]; mac->f = data[15]; PG_RETURN_MACADDR_P(mac); } /* number of 100 nanosecond intervals from 1585-10-15 to 1970-01-01 */ #define GREGORIAN 122192928000000000ULL PG_FUNCTION_INFO_V1(uuid_extract_timestamp); Datum uuid_extract_timestamp(PG_FUNCTION_ARGS) { Timestamp ts; unsigned char *data; pg_uuid_t *uuid = PG_GETARG_UUID_P(0); data = (unsigned char *)uuid; ts = (data[6] & 0xf); ts *= 256; ts += (data[7] & 0xff); ts *= 256; ts += (data[4] & 0xff); ts *= 256; ts += (data[5] & 0xff); ts *= 256; ts += (data[0] & 0xff); ts *= 256; ts += (data[1] & 0xff); ts *= 256; ts += (data[2] & 0xff); ts *= 256; ts += (data[3] & 0xff); /* TODO could probably combine some of this */ ts -= GREGORIAN; /* adjust by offset from gregorian to unix epoch */ ts /= 10; /* convert from 100 ns to us */ ts -= ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY * 1000000LL); #ifdef HAVE_INT64_TIMESTAMP PG_RETURN_TIMESTAMP(ts); #else PG_RETURN_TIMESTAMP(ts/1000000UL); #endif }