]> pd.if.org Git - uuid/blob - postgres/uuid.c
fix sprintf format
[uuid] / postgres / uuid.c
1 /*
2  * uuid type support functions for Postgres 8.4
3  *
4  * written by Nathan Wagner and placed in the public domain
5  */
6 #include "postgres.h"
7 #include "fmgr.h"
8
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"
16
17 #include "pduuid.h"
18
19 #define UUID_LEN_BIN 16
20
21 PG_MODULE_MAGIC;
22
23 void _PG_init(void);
24 void _PG_fini(void);
25
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);
46
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);
51
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;
57
58 void _PG_init(void) {
59         gid = &i_gid;
60         memset(gid, 0, 16);
61         ns_uuid = &i_ns_uuid;
62         memset(ns_uuid, 0, 16);
63
64         pd_uuid_init_state(&us);
65
66         return;
67 }
68
69 #if 0
70 void _PG_fini(void) {
71         if (gid) {
72                 uuid_destroy(gid);
73         }
74         if (ns_uuid) {
75                 uuid_destroy(ns_uuid);
76         }
77         return;
78 }
79 #endif
80
81 PG_FUNCTION_INFO_V1(uuid_nil);
82 Datum uuid_nil(PG_FUNCTION_ARGS) {
83         pg_uuid_t *uuid;
84
85         uuid = palloc(UUID_LEN_BIN);
86         memset(uuid, 0, 16);
87         PG_RETURN_UUID_P(uuid);
88 }
89
90 PG_FUNCTION_INFO_V1(uuid_dns);
91 Datum uuid_dns(PG_FUNCTION_ARGS) {
92         pg_uuid_t *uuid;
93         unsigned char bytes[] = { 0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11,
94                 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 };
95
96         uuid = palloc(UUID_LEN_BIN);
97         memcpy(uuid, bytes, 16);
98         PG_RETURN_UUID_P(uuid);
99 }
100
101 PG_FUNCTION_INFO_V1(uuid_url);
102 Datum uuid_url(PG_FUNCTION_ARGS) {
103         pg_uuid_t *uuid;
104         unsigned char bytes[] = { 0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11,
105                 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 };
106
107         uuid = palloc(UUID_LEN_BIN);
108         memcpy(uuid, bytes, 16);
109         PG_RETURN_UUID_P(uuid);
110 }
111
112 PG_FUNCTION_INFO_V1(uuid_oid);
113 Datum uuid_oid(PG_FUNCTION_ARGS) {
114         pg_uuid_t *uuid;
115         unsigned char bytes[] = { 0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11,
116                 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 };
117
118         uuid = palloc(UUID_LEN_BIN);
119         memcpy(uuid, bytes, 16);
120         PG_RETURN_UUID_P(uuid);
121 }
122
123 PG_FUNCTION_INFO_V1(uuid_x500);
124 Datum uuid_x500(PG_FUNCTION_ARGS) {
125         pg_uuid_t *uuid;
126         unsigned char bytes[] = { 0x6b, 0xa7, 0xb8, 0x13, 0x9d, 0xad, 0x11,
127                 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 };
128
129         uuid = palloc(UUID_LEN_BIN);
130         memcpy(uuid, bytes, 16);
131         PG_RETURN_UUID_P(uuid);
132 }
133
134 static pg_uuid_t *export(pd_uuid_t *u) {
135         pg_uuid_t *pgid;
136
137         pgid = palloc(UUID_LEN_BIN);
138         memcpy(pgid, u, 16);
139
140         return pgid;
141 }
142
143 static void import(pd_uuid_t *u, pg_uuid_t *pgid) {
144         memcpy(u, pgid, 16);
145 }
146
147 PG_FUNCTION_INFO_V1(uuid_recent);
148
149 Datum uuid_recent(PG_FUNCTION_ARGS) {
150         pg_uuid_t *pgid;
151
152         pgid = export(gid);
153         PG_RETURN_UUID_P(pgid);
154 }
155
156 PG_FUNCTION_INFO_V1(uuid_gen_v1);
157
158 Datum uuid_gen_v1(PG_FUNCTION_ARGS) {
159         pg_uuid_t *pgid;
160         
161         if (!pd_uuid_make_v1(&us, gid)) {
162                 elog(ERROR, "can't make uuid");
163         }
164
165         pgid = export(gid);
166         PG_RETURN_UUID_P(pgid);
167 }
168
169 PG_FUNCTION_INFO_V1(uuid_gen_v1_mc);
170
171 Datum uuid_gen_v1_mc(PG_FUNCTION_ARGS) {
172         pg_uuid_t *pgid;
173         
174         if (!pd_uuid_make_v1mc(&us, gid)) {
175                 elog(ERROR, "can't make uuid");
176         }
177
178         pgid = export(gid);
179         PG_RETURN_UUID_P(pgid);
180 }
181
182 PG_FUNCTION_INFO_V1(uuid_gen_v3);
183
184 Datum uuid_gen_v3(PG_FUNCTION_ARGS) {
185         pg_uuid_t *pgid;
186         pg_uuid_t *ns;
187         text    *t;
188         char *string;
189         
190         ns = PG_GETARG_UUID_P(0);
191         t = PG_GETARG_TEXT_P(1);
192
193         import(ns_uuid, ns);
194
195         string = text_to_cstring(t);
196
197         if (!pd_uuid_make_v3(&us, gid, ns_uuid, string, strlen(string))) {
198                 elog(ERROR, "can't make uuid");
199         }
200         pfree(string);
201
202         pgid = export(gid);
203         PG_RETURN_UUID_P(pgid);
204 }
205
206 PG_FUNCTION_INFO_V1(uuid_gen_v4);
207
208 Datum uuid_gen_v4(PG_FUNCTION_ARGS) {
209         pg_uuid_t *pgid;
210
211         if (!pd_uuid_make_v4(&us, gid)) {
212                 elog(ERROR, "can't make uuid");
213         }
214
215         pgid = export(gid);
216         PG_RETURN_UUID_P(pgid);
217 }
218
219 PG_FUNCTION_INFO_V1(uuid_gen_v5);
220
221 Datum uuid_gen_v5(PG_FUNCTION_ARGS) {
222         pg_uuid_t *pgid;
223         pg_uuid_t *ns;
224         text    *t;
225         char *string;
226         
227         if (!gid || !ns_uuid) {
228                 elog(ERROR, "UUID library failed to initialize");
229         }
230
231         ns = PG_GETARG_UUID_P(0);
232         t = PG_GETARG_TEXT_P(1);
233
234         import(ns_uuid, ns);
235
236         string = text_to_cstring(t);
237
238         if (!pd_uuid_make_v5(&us, gid,ns_uuid, string, strlen(string))) {
239                 elog(ERROR, "can't make uuid");
240         }
241         pfree(string);
242
243         pgid = export(gid);
244         PG_RETURN_UUID_P(pgid);
245 }
246
247 PG_FUNCTION_INFO_V1(uuid_cast_bit);
248 Datum uuid_cast_bit(PG_FUNCTION_ARGS) {
249         VarBit  *bit;
250
251         pg_uuid_t *uuid = PG_GETARG_UUID_P(0);
252
253         bit = palloc(VARBITTOTALLEN(128));
254         bit->bit_len = 128;
255         bit->vl_len_ = VARBITTOTALLEN(128);
256         memcpy(VARBITS(bit), uuid, 16);
257         PG_RETURN_VARBIT_P(bit);
258 }
259
260 PG_FUNCTION_INFO_V1(uuid_cast_from_bit);
261 Datum uuid_cast_from_bit(PG_FUNCTION_ARGS) {
262         pg_uuid_t *uuid;
263
264         VarBit  *bit = PG_GETARG_VARBIT_P(0);
265
266         if (bit->bit_len != 128) {
267                 /* Throw error? */
268                 elog(ERROR, "can't cast from bit(%d) to uuid, must cast from bit(128)", bit->bit_len);
269         }
270
271         uuid = palloc(UUID_LEN_BIN);
272         memcpy(uuid, VARBITS(bit), 16);
273         PG_RETURN_UUID_P(uuid);
274 }
275
276 PG_FUNCTION_INFO_V1(uuid_cast_bytea);
277 Datum uuid_cast_bytea(PG_FUNCTION_ARGS) {
278         bytea   *bit;
279
280         pg_uuid_t *uuid = PG_GETARG_UUID_P(0);
281
282         bit = palloc(VARHDRSZ + 16);
283         SET_VARSIZE(bit, VARHDRSZ + 16);
284         memcpy(VARDATA(bit), uuid, 16);
285         PG_RETURN_BYTEA_P(bit);
286 }
287
288 PG_FUNCTION_INFO_V1(uuid_cast_from_bytea);
289 Datum uuid_cast_from_bytea(PG_FUNCTION_ARGS) {
290         pg_uuid_t *uuid; 
291
292         bytea   *bit = PG_GETARG_BYTEA_P(0);
293
294         if (VARSIZE_ANY_EXHDR(bit) != 16) {
295                 elog(ERROR, "invalid length of bytea for cast to uuid, need 16, attempted %ld", VARSIZE_ANY_EXHDR(bit));
296         }
297
298         uuid = palloc(UUID_LEN_BIN);
299         memcpy(uuid, VARDATA(bit), 16);
300         PG_RETURN_UUID_P(uuid);
301 }
302
303 /* TODO construct the uuid directly from the numeric */
304 /* TODO have a static c256 numeric */
305
306 PG_FUNCTION_INFO_V1(uuid_cast_from_numeric);
307 Datum uuid_cast_from_numeric(PG_FUNCTION_ARGS) {
308         int i;
309         unsigned char *data;
310         int32   byte;
311
312         Numeric c256;
313         Numeric input;
314         Numeric result;
315         pg_uuid_t       *uuid;
316
317         input = PG_GETARG_NUMERIC(0);
318         uuid = palloc(UUID_LEN_BIN);
319         data = (unsigned char *)uuid;
320
321         c256 = DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum(256)));
322
323         for (i=15;i>=0;i--) {
324                 result = DatumGetNumeric(DirectFunctionCall2(numeric_mod, NumericGetDatum(input), NumericGetDatum(c256)));
325
326                 input = DatumGetNumeric(DirectFunctionCall2(numeric_div_trunc, NumericGetDatum(input), NumericGetDatum(c256)));
327
328                 byte = DatumGetInt32(DirectFunctionCall1(numeric_int4, NumericGetDatum(result)));
329                 data[i] = byte & 0xff;
330         }
331
332         /* TODO will this leak if I don't do this?  Segfault if i do? */
333         pfree(c256);
334
335         PG_RETURN_UUID_P(uuid);
336 }
337
338 /*
339  * TODO construct the numeric directly
340  */
341 PG_FUNCTION_INFO_V1(uuid_cast_numeric);
342 Datum uuid_cast_numeric(PG_FUNCTION_ARGS) {
343         int i;
344         unsigned char *data;
345         int32   byte;
346
347         Numeric result;
348         Numeric c256;
349
350         pg_uuid_t *uuid = PG_GETARG_UUID_P(0);
351
352         data = (unsigned char *)uuid;
353
354         result = DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum(0)));
355         
356         c256 = DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum(256)));
357
358         for (i=0;i<16;i++) {
359                 byte = (int32) data[i];
360
361                 result = DatumGetNumeric(DirectFunctionCall2(numeric_mul, NumericGetDatum(result), NumericGetDatum(c256)));
362
363                 result = DatumGetNumeric(
364                                 DirectFunctionCall2(numeric_add,
365                                         NumericGetDatum(result),
366                                         DirectFunctionCall1(int4_numeric, Int32GetDatum(byte))));
367
368         }
369
370         pfree(c256);
371
372         PG_RETURN_NUMERIC(result);
373 }
374
375 PG_FUNCTION_INFO_V1(uuid_extract_version);
376 Datum uuid_extract_version(PG_FUNCTION_ARGS) {
377         int32 version;
378         unsigned char *data;
379
380         pg_uuid_t *uuid = PG_GETARG_UUID_P(0);
381         data = (unsigned char *)uuid;
382
383         version = data[6] >> 4;
384         PG_RETURN_INT32(version);
385 }
386
387 PG_FUNCTION_INFO_V1(uuid_extract_macaddr);
388 Datum uuid_extract_macaddr(PG_FUNCTION_ARGS) {
389         macaddr *mac;
390         unsigned char *data;
391
392         pg_uuid_t *uuid = PG_GETARG_UUID_P(0);
393         data = (unsigned char *)uuid;
394
395         mac = palloc(sizeof *mac);
396         mac->a = data[10];
397         mac->b = data[11];
398         mac->c = data[12];
399         mac->d = data[13];
400         mac->e = data[14];
401         mac->f = data[15];
402
403         PG_RETURN_MACADDR_P(mac);
404 }
405
406 /* number of 100 nanosecond intervals from 1585-10-15 to 1970-01-01 */
407 #define GREGORIAN 122192928000000000ULL
408
409 PG_FUNCTION_INFO_V1(uuid_extract_timestamp);
410 Datum uuid_extract_timestamp(PG_FUNCTION_ARGS) {
411         Timestamp ts;
412         unsigned char *data;
413
414         pg_uuid_t *uuid = PG_GETARG_UUID_P(0);
415         data = (unsigned char *)uuid;
416
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);
425
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 */
429
430         ts -= ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY * 1000000LL);
431
432 #ifdef HAVE_INT64_TIMESTAMP
433         PG_RETURN_TIMESTAMP(ts);
434 #else
435         PG_RETURN_TIMESTAMP(ts/1000000UL);
436 #endif
437 }