2 * uuid type support functions for Postgres 8.4
4 * written by Nathan Wagner and placed in the public domain
9 #include "utils/builtins.h"
10 #include "utils/uuid.h"
11 #include "utils/numeric.h"
12 #include "utils/inet.h"
13 #include "utils/timestamp.h"
14 #include "utils/datetime.h"
15 #include "utils/varbit.h"
19 #define UUID_LEN_BIN 16
26 Datum uuid_nil(PG_FUNCTION_ARGS);
27 Datum uuid_dns(PG_FUNCTION_ARGS);
28 Datum uuid_url(PG_FUNCTION_ARGS);
29 Datum uuid_oid(PG_FUNCTION_ARGS);
30 Datum uuid_x500(PG_FUNCTION_ARGS);
31 Datum uuid_recent(PG_FUNCTION_ARGS);
32 Datum uuid_gen_v1(PG_FUNCTION_ARGS);
33 Datum uuid_gen_v1_mc(PG_FUNCTION_ARGS);
34 Datum uuid_gen_v3(PG_FUNCTION_ARGS);
35 Datum uuid_gen_v4(PG_FUNCTION_ARGS);
36 Datum uuid_gen_v5(PG_FUNCTION_ARGS);
37 Datum uuid_cast_bit(PG_FUNCTION_ARGS);
38 Datum uuid_cast_from_bit(PG_FUNCTION_ARGS);
39 Datum uuid_cast_numeric(PG_FUNCTION_ARGS);
40 Datum uuid_cast_from_numeric(PG_FUNCTION_ARGS);
41 Datum uuid_cast_bytea(PG_FUNCTION_ARGS);
42 Datum uuid_cast_from_bytea(PG_FUNCTION_ARGS);
43 Datum uuid_extract_version(PG_FUNCTION_ARGS);
44 Datum uuid_extract_macaddr(PG_FUNCTION_ARGS);
45 Datum uuid_extract_timestamp(PG_FUNCTION_ARGS);
47 Datum uuid_ns_dns(PG_FUNCTION_ARGS);
48 Datum uuid_ns_url(PG_FUNCTION_ARGS);
49 Datum uuid_ns_oid(PG_FUNCTION_ARGS);
50 Datum uuid_ns_x500(PG_FUNCTION_ARGS);
52 static pd_uuid_t i_gid;
53 static pd_uuid_t i_ns_uuid;
54 static pd_uuid_t *gid = NULL;
55 static pd_uuid_t *ns_uuid = NULL;
56 static struct pd_uuid_state us;
62 memset(ns_uuid, 0, 16);
64 pd_uuid_init_state(&us);
75 uuid_destroy(ns_uuid);
81 PG_FUNCTION_INFO_V1(uuid_nil);
82 Datum uuid_nil(PG_FUNCTION_ARGS) {
85 uuid = palloc(UUID_LEN_BIN);
87 PG_RETURN_UUID_P(uuid);
90 PG_FUNCTION_INFO_V1(uuid_dns);
91 Datum uuid_dns(PG_FUNCTION_ARGS) {
93 unsigned char bytes[] = { 0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11,
94 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 };
96 uuid = palloc(UUID_LEN_BIN);
97 memcpy(uuid, bytes, 16);
98 PG_RETURN_UUID_P(uuid);
101 PG_FUNCTION_INFO_V1(uuid_url);
102 Datum uuid_url(PG_FUNCTION_ARGS) {
104 unsigned char bytes[] = { 0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11,
105 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 };
107 uuid = palloc(UUID_LEN_BIN);
108 memcpy(uuid, bytes, 16);
109 PG_RETURN_UUID_P(uuid);
112 PG_FUNCTION_INFO_V1(uuid_oid);
113 Datum uuid_oid(PG_FUNCTION_ARGS) {
115 unsigned char bytes[] = { 0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11,
116 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 };
118 uuid = palloc(UUID_LEN_BIN);
119 memcpy(uuid, bytes, 16);
120 PG_RETURN_UUID_P(uuid);
123 PG_FUNCTION_INFO_V1(uuid_x500);
124 Datum uuid_x500(PG_FUNCTION_ARGS) {
126 unsigned char bytes[] = { 0x6b, 0xa7, 0xb8, 0x13, 0x9d, 0xad, 0x11,
127 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 };
129 uuid = palloc(UUID_LEN_BIN);
130 memcpy(uuid, bytes, 16);
131 PG_RETURN_UUID_P(uuid);
134 static pg_uuid_t *export(pd_uuid_t *u) {
137 pgid = palloc(UUID_LEN_BIN);
143 static void import(pd_uuid_t *u, pg_uuid_t *pgid) {
147 PG_FUNCTION_INFO_V1(uuid_recent);
149 Datum uuid_recent(PG_FUNCTION_ARGS) {
153 PG_RETURN_UUID_P(pgid);
156 PG_FUNCTION_INFO_V1(uuid_gen_v1);
158 Datum uuid_gen_v1(PG_FUNCTION_ARGS) {
161 if (!pd_uuid_make_v1(&us, gid)) {
162 elog(ERROR, "can't make uuid");
166 PG_RETURN_UUID_P(pgid);
169 PG_FUNCTION_INFO_V1(uuid_gen_v1_mc);
171 Datum uuid_gen_v1_mc(PG_FUNCTION_ARGS) {
174 if (!pd_uuid_make_v1mc(&us, gid)) {
175 elog(ERROR, "can't make uuid");
179 PG_RETURN_UUID_P(pgid);
182 PG_FUNCTION_INFO_V1(uuid_gen_v3);
184 Datum uuid_gen_v3(PG_FUNCTION_ARGS) {
190 ns = PG_GETARG_UUID_P(0);
191 t = PG_GETARG_TEXT_P(1);
195 string = text_to_cstring(t);
197 if (!pd_uuid_make_v3(&us, gid, ns_uuid, string, strlen(string))) {
198 elog(ERROR, "can't make uuid");
203 PG_RETURN_UUID_P(pgid);
206 PG_FUNCTION_INFO_V1(uuid_gen_v4);
208 Datum uuid_gen_v4(PG_FUNCTION_ARGS) {
211 if (!pd_uuid_make_v4(&us, gid)) {
212 elog(ERROR, "can't make uuid");
216 PG_RETURN_UUID_P(pgid);
219 PG_FUNCTION_INFO_V1(uuid_gen_v5);
221 Datum uuid_gen_v5(PG_FUNCTION_ARGS) {
227 if (!gid || !ns_uuid) {
228 elog(ERROR, "UUID library failed to initialize");
231 ns = PG_GETARG_UUID_P(0);
232 t = PG_GETARG_TEXT_P(1);
236 string = text_to_cstring(t);
238 if (!pd_uuid_make_v5(&us, gid,ns_uuid, string, strlen(string))) {
239 elog(ERROR, "can't make uuid");
244 PG_RETURN_UUID_P(pgid);
247 PG_FUNCTION_INFO_V1(uuid_cast_bit);
248 Datum uuid_cast_bit(PG_FUNCTION_ARGS) {
251 pg_uuid_t *uuid = PG_GETARG_UUID_P(0);
253 bit = palloc(VARBITTOTALLEN(128));
255 bit->vl_len_ = VARBITTOTALLEN(128);
256 memcpy(VARBITS(bit), uuid, 16);
257 PG_RETURN_VARBIT_P(bit);
260 PG_FUNCTION_INFO_V1(uuid_cast_from_bit);
261 Datum uuid_cast_from_bit(PG_FUNCTION_ARGS) {
264 VarBit *bit = PG_GETARG_VARBIT_P(0);
266 if (bit->bit_len != 128) {
268 elog(ERROR, "can't cast from bit(%d) to uuid, must cast from bit(128)", bit->bit_len);
271 uuid = palloc(UUID_LEN_BIN);
272 memcpy(uuid, VARBITS(bit), 16);
273 PG_RETURN_UUID_P(uuid);
276 PG_FUNCTION_INFO_V1(uuid_cast_bytea);
277 Datum uuid_cast_bytea(PG_FUNCTION_ARGS) {
280 pg_uuid_t *uuid = PG_GETARG_UUID_P(0);
282 bit = palloc(VARHDRSZ + 16);
283 SET_VARSIZE(bit, VARHDRSZ + 16);
284 memcpy(VARDATA(bit), uuid, 16);
285 PG_RETURN_BYTEA_P(bit);
288 PG_FUNCTION_INFO_V1(uuid_cast_from_bytea);
289 Datum uuid_cast_from_bytea(PG_FUNCTION_ARGS) {
292 bytea *bit = PG_GETARG_BYTEA_P(0);
294 if (VARSIZE_ANY_EXHDR(bit) != 16) {
295 elog(ERROR, "invalid length of bytea for cast to uuid, need 16, attempted %d", VARSIZE_ANY_EXHDR(bit));
298 uuid = palloc(UUID_LEN_BIN);
299 memcpy(uuid, VARDATA(bit), 16);
300 PG_RETURN_UUID_P(uuid);
303 /* TODO construct the uuid directly from the numeric */
304 /* TODO have a static c256 numeric */
306 PG_FUNCTION_INFO_V1(uuid_cast_from_numeric);
307 Datum uuid_cast_from_numeric(PG_FUNCTION_ARGS) {
317 input = PG_GETARG_NUMERIC(0);
318 uuid = palloc(UUID_LEN_BIN);
319 data = (unsigned char *)uuid;
321 c256 = DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum(256)));
323 for (i=15;i>=0;i--) {
324 result = DatumGetNumeric(DirectFunctionCall2(numeric_mod, NumericGetDatum(input), NumericGetDatum(c256)));
326 input = DatumGetNumeric(DirectFunctionCall2(numeric_div_trunc, NumericGetDatum(input), NumericGetDatum(c256)));
328 byte = DatumGetInt32(DirectFunctionCall1(numeric_int4, NumericGetDatum(result)));
329 data[i] = byte & 0xff;
332 /* TODO will this leak if I don't do this? Segfault if i do? */
335 PG_RETURN_UUID_P(uuid);
339 * TODO construct the numeric directly
341 PG_FUNCTION_INFO_V1(uuid_cast_numeric);
342 Datum uuid_cast_numeric(PG_FUNCTION_ARGS) {
350 pg_uuid_t *uuid = PG_GETARG_UUID_P(0);
352 data = (unsigned char *)uuid;
354 result = DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum(0)));
356 c256 = DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum(256)));
359 byte = (int32) data[i];
361 result = DatumGetNumeric(DirectFunctionCall2(numeric_mul, NumericGetDatum(result), NumericGetDatum(c256)));
363 result = DatumGetNumeric(
364 DirectFunctionCall2(numeric_add,
365 NumericGetDatum(result),
366 DirectFunctionCall1(int4_numeric, Int32GetDatum(byte))));
372 PG_RETURN_NUMERIC(result);
375 PG_FUNCTION_INFO_V1(uuid_extract_version);
376 Datum uuid_extract_version(PG_FUNCTION_ARGS) {
380 pg_uuid_t *uuid = PG_GETARG_UUID_P(0);
381 data = (unsigned char *)uuid;
383 version = data[6] >> 4;
384 PG_RETURN_INT32(version);
387 PG_FUNCTION_INFO_V1(uuid_extract_macaddr);
388 Datum uuid_extract_macaddr(PG_FUNCTION_ARGS) {
392 pg_uuid_t *uuid = PG_GETARG_UUID_P(0);
393 data = (unsigned char *)uuid;
395 mac = palloc(sizeof *mac);
403 PG_RETURN_MACADDR_P(mac);
406 /* number of 100 nanosecond intervals from 1585-10-15 to 1970-01-01 */
407 #define GREGORIAN 122192928000000000ULL
409 PG_FUNCTION_INFO_V1(uuid_extract_timestamp);
410 Datum uuid_extract_timestamp(PG_FUNCTION_ARGS) {
414 pg_uuid_t *uuid = PG_GETARG_UUID_P(0);
415 data = (unsigned char *)uuid;
417 ts = (data[6] & 0xf);
418 ts *= 256; ts += (data[7] & 0xff);
419 ts *= 256; ts += (data[4] & 0xff);
420 ts *= 256; ts += (data[5] & 0xff);
421 ts *= 256; ts += (data[0] & 0xff);
422 ts *= 256; ts += (data[1] & 0xff);
423 ts *= 256; ts += (data[2] & 0xff);
424 ts *= 256; ts += (data[3] & 0xff);
426 /* TODO could probably combine some of this */
427 ts -= GREGORIAN; /* adjust by offset from gregorian to unix epoch */
428 ts /= 10; /* convert from 100 ns to us */
430 ts -= ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY * 1000000LL);
432 #ifdef HAVE_INT64_TIMESTAMP
433 PG_RETURN_TIMESTAMP(ts);
435 PG_RETURN_TIMESTAMP(ts/1000000UL);