Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : /*
14 : * K.S. Mullender & A. de Rijke
15 : * The UUID module
16 : * The UUID module contains a wrapper for all function in
17 : * libuuid.
18 : */
19 :
20 : #include "monetdb_config.h"
21 : #if defined(HAVE_GETENTROPY) && defined(HAVE_SYS_RANDOM_H)
22 : #include <sys/random.h>
23 : #endif
24 : #include "mal.h"
25 : #include "mal_exception.h"
26 : #include "mal_interpreter.h"
27 :
28 : #if !defined(HAVE_GETENTROPY) && defined(HAVE_RAND_S)
29 : static inline bool
30 : generate_uuid(uuid *U)
31 : {
32 : union {
33 : unsigned int randbuf[4];
34 : unsigned char uuid[16];
35 : } u;
36 : for (int i = 0; i < 4; i++)
37 : if (rand_s(&u.randbuf[i]) != 0)
38 : return false;
39 : /* make sure this is a variant 1 UUID (RFC 4122/DCE 1.1) */
40 : u.uuid[8] = (u.uuid[8] & 0x3F) |0x80;
41 : /* make sure this is version 4 (random UUID) */
42 : u.uuid[6] = (u.uuid[6] & 0x0F) |0x40;
43 : memcpy(U->u, u.uuid, 16);
44 : return true;
45 : }
46 : #endif
47 :
48 : /**
49 : * Returns the string representation of the given uuid value.
50 : * Warning: GDK function
51 : * Returns the length of the string
52 : */
53 : static inline void
54 102 : UUIDgenerateUuid_internal(uuid *u)
55 : {
56 : #if defined(HAVE_GETENTROPY)
57 102 : if (getentropy(u->u, 16) == 0) {
58 : /* make sure this is a variant 1 UUID (RFC 4122/DCE 1.1) */
59 102 : u->u[8] = (u->u[8] & 0x3F) | 0x80;
60 : /* make sure this is version 4 (random UUID) */
61 102 : u->u[6] = (u->u[6] & 0x0F) | 0x40;
62 : } else
63 : #elif defined(HAVE_RAND_S)
64 : if (!generate_uuid(u))
65 : #endif
66 : {
67 : /* generate something like this:
68 : * cefa7a9c-1dd2-41b2-8350-880020adbeef
69 : * ("%08x-%04x-%04x-%04x-%012x") */
70 0 : for (int i = 0; i < 16; i += 2) {
71 : #ifdef __COVERITY__
72 : int r = 0;
73 : #else
74 0 : int r = rand();
75 : #endif
76 0 : u->u[i] = (unsigned char) (r >> 8);
77 0 : u->u[i + 1] = (unsigned char) r;
78 : }
79 : /* make sure this is a variant 1 UUID (RFC 4122/DCE 1.1) */
80 0 : u->u[8] = (u->u[8] & 0x3F) | 0x80;
81 : /* make sure this is version 4 (random UUID) */
82 0 : u->u[6] = (u->u[6] & 0x0F) | 0x40;
83 : }
84 102 : }
85 :
86 : static str
87 102 : UUIDgenerateUuid(uuid *retval)
88 : {
89 102 : UUIDgenerateUuid_internal(retval);
90 102 : return MAL_SUCCEED;
91 : }
92 :
93 : static str
94 0 : UUIDgenerateUuidInt(uuid *retval, int *d)
95 : {
96 0 : (void) d;
97 0 : return UUIDgenerateUuid(retval);
98 : }
99 :
100 : static inline bit
101 99 : isaUUID(const char *s)
102 : {
103 99 : uuid u, *pu = &u;
104 99 : size_t l = UUID_SIZE;
105 99 : ssize_t res = BATatoms[TYPE_uuid].atomFromStr(s, &l, (void **) &pu, false);
106 :
107 99 : if (res > 1)
108 : return true;
109 83 : else if (res == 1)
110 31 : return bit_nil;
111 : else
112 : return false;
113 : }
114 :
115 : static str
116 0 : UUIDgenerateUuidInt_bulk(Client cntxt, MalBlkPtr mb, MalStkPtr stk,
117 : InstrPtr pci)
118 : {
119 0 : BAT *b = NULL, *bn = NULL;
120 0 : BUN n = 0;
121 0 : str msg = MAL_SUCCEED;
122 0 : uuid *restrict bnt = NULL;
123 0 : bat *ret = getArgReference_bat(stk, pci, 0);
124 :
125 0 : (void) cntxt;
126 0 : if (isaBatType(getArgType(mb, pci, 1))) {
127 0 : bat *bid = getArgReference_bat(stk, pci, 1);
128 0 : if (!(b = BBPquickdesc(*bid))) {
129 0 : throw(MAL, "uuid.generateuuidint_bulk",
130 : SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
131 : }
132 0 : n = BATcount(b);
133 : } else {
134 0 : n = (BUN) *getArgReference_lng(stk, pci, 1);
135 : }
136 :
137 0 : if ((bn = COLnew(b ? b->hseqbase : 0, TYPE_uuid, n, TRANSIENT)) == NULL) {
138 0 : throw(MAL, "uuid.generateuuidint_bulk",
139 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
140 : }
141 0 : bnt = Tloc(bn, 0);
142 0 : for (BUN i = 0; i < n; i++)
143 0 : UUIDgenerateUuid_internal(&(bnt[i]));
144 0 : BATsetcount(bn, n);
145 0 : bn->tnonil = true;
146 0 : bn->tnil = false;
147 0 : bn->tsorted = n <= 1;
148 0 : bn->trevsorted = n <= 1;
149 0 : bn->tkey = n <= 1;
150 0 : *ret = bn->batCacheid;
151 0 : BBPkeepref(bn);
152 0 : return msg;
153 : }
154 :
155 : static str
156 31 : UUIDisaUUID(bit *retval, str *s)
157 : {
158 31 : *retval = isaUUID(*s);
159 31 : if (*retval == false)
160 26 : GDKclrerr();
161 31 : return MAL_SUCCEED;
162 : }
163 :
164 : static str
165 18 : UUIDisaUUID_bulk(bat *ret, const bat *bid)
166 : {
167 18 : BAT *b = NULL, *bn = NULL;
168 18 : BUN q;
169 18 : bit *restrict dst;
170 18 : str msg = MAL_SUCCEED;
171 18 : BATiter bi;
172 :
173 18 : if ((b = BATdescriptor(*bid)) == NULL) {
174 0 : msg = createException(MAL, "uuid.isaUUID_bulk",
175 : SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
176 0 : goto bailout;
177 : }
178 18 : q = BATcount(b);
179 18 : if ((bn = COLnew(b->hseqbase, TYPE_bit, q, TRANSIENT)) == NULL) {
180 0 : msg = createException(MAL, "uuid.isaUUID_bulk",
181 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
182 0 : goto bailout;
183 : }
184 18 : dst = Tloc(bn, 0);
185 18 : bi = bat_iterator(b);
186 86 : for (BUN p = 0; p < q; p++)
187 68 : dst[p] = isaUUID(BUNtvar(bi, p));
188 18 : GDKclrerr(); /* Not interested in atomFromStr errors */
189 18 : BATsetcount(bn, q);
190 18 : bn->tnonil = bi.nonil;
191 18 : bn->tnil = bi.nil;
192 18 : bn->tsorted = bn->trevsorted = q < 2;
193 18 : bn->tkey = false;
194 18 : bat_iterator_end(&bi);
195 18 : bailout:
196 18 : BBPreclaim(b);
197 18 : if (bn) { /* implies msg==MAL_SUCCEED */
198 18 : *ret = bn->batCacheid;
199 18 : BBPkeepref(bn);
200 : }
201 18 : return msg;
202 : }
203 :
204 : static str
205 0 : UUIDuuid2uuid(uuid *retval, uuid *i)
206 : {
207 0 : *retval = *i;
208 0 : return MAL_SUCCEED;
209 : }
210 :
211 : static str
212 0 : UUIDuuid2uuid_bulk(bat *res, const bat *bid, const bat *sid)
213 : {
214 0 : BAT *b = NULL, *s = NULL, *dst = NULL;
215 0 : uuid *restrict bv, *restrict dv;
216 0 : str msg = NULL;
217 0 : struct canditer ci;
218 0 : oid off;
219 0 : bool nils = false, btsorted = false, btrevsorted = false, btkey = false;
220 0 : BATiter bi;
221 :
222 0 : if (sid && !is_bat_nil(*sid)) {
223 0 : if ((s = BATdescriptor(*sid)) == NULL) {
224 0 : msg = createException(SQL, "batcalc.uuid2uuidbulk",
225 : SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
226 0 : goto bailout;
227 : }
228 : } else {
229 0 : BBPretain(*res = *bid); /* nothing to convert, return */
230 0 : return MAL_SUCCEED;
231 : }
232 0 : if ((b = BATdescriptor(*bid)) == NULL) {
233 0 : msg = createException(SQL, "batcalc.uuid2uuidbulk",
234 : SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
235 0 : goto bailout;
236 : }
237 0 : off = b->hseqbase;
238 0 : canditer_init(&ci, b, s);
239 0 : if (!(dst = COLnew(ci.hseq, TYPE_uuid, ci.ncand, TRANSIENT))) {
240 0 : msg = createException(SQL, "batcalc.uuid2uuidbulk",
241 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
242 0 : goto bailout;
243 : }
244 :
245 0 : bi = bat_iterator(b);
246 0 : bv = bi.base;
247 0 : dv = Tloc(dst, 0);
248 0 : if (ci.tpe == cand_dense) {
249 0 : for (BUN i = 0; i < ci.ncand; i++) {
250 0 : oid p = (canditer_next_dense(&ci) - off);
251 0 : uuid v = bv[p];
252 :
253 0 : dv[i] = v;
254 0 : nils |= is_uuid_nil(v);
255 : }
256 : } else {
257 0 : for (BUN i = 0; i < ci.ncand; i++) {
258 0 : oid p = (canditer_next(&ci) - off);
259 0 : uuid v = bv[p];
260 :
261 0 : dv[i] = v;
262 0 : nils |= is_uuid_nil(v);
263 : }
264 : }
265 0 : btkey = bi.key;
266 0 : btsorted = bi.sorted;
267 0 : btrevsorted = bi.revsorted;
268 0 : bat_iterator_end(&bi);
269 :
270 0 : bailout:
271 0 : BBPreclaim(b);
272 0 : BBPreclaim(s);
273 0 : if (dst) { /* implies msg==MAL_SUCCEED */
274 0 : BATsetcount(dst, ci.ncand);
275 0 : dst->tnil = nils;
276 0 : dst->tnonil = !nils;
277 0 : dst->tkey = btkey;
278 0 : dst->tsorted = btsorted;
279 0 : dst->trevsorted = btrevsorted;
280 0 : *res = dst->batCacheid;
281 0 : BBPkeepref(dst);
282 : }
283 : return msg;
284 : }
285 :
286 : static str
287 59 : UUIDstr2uuid(uuid *retval, str *s)
288 : {
289 59 : size_t l = UUID_SIZE;
290 :
291 59 : if (BATatoms[TYPE_uuid].atomFromStr(*s, &l, (void **) &retval, false) > 0) {
292 : return MAL_SUCCEED;
293 : }
294 6 : throw(MAL, "uuid.uuid", "Not a UUID");
295 : }
296 :
297 : static str
298 2 : UUIDstr2uuid_bulk(bat *res, const bat *bid, const bat *sid)
299 : {
300 2 : BAT *b = NULL, *s = NULL, *dst = NULL;
301 2 : BATiter bi;
302 2 : str msg = NULL;
303 2 : uuid *restrict vals;
304 2 : struct canditer ci;
305 2 : oid off;
306 2 : bool nils = false, btkey = false;
307 2 : size_t l = UUID_SIZE;
308 2 : ssize_t (*conv)(const char *, size_t *, void **, bool) = BATatoms[TYPE_uuid].atomFromStr;
309 :
310 2 : if ((b = BATdescriptor(*bid)) == NULL) {
311 0 : msg = createException(SQL, "batcalc.str2uuidbulk",
312 : SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
313 0 : goto bailout;
314 : }
315 2 : if (sid && !is_bat_nil(*sid) && (s = BATdescriptor(*sid)) == NULL) {
316 0 : msg = createException(SQL, "batcalc.str2uuidbulk",
317 : SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
318 0 : goto bailout;
319 : }
320 2 : off = b->hseqbase;
321 2 : canditer_init(&ci, b, s);
322 2 : if (!(dst = COLnew(ci.hseq, TYPE_uuid, ci.ncand, TRANSIENT))) {
323 0 : msg = createException(SQL, "batcalc.str2uuidbulk",
324 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
325 0 : goto bailout;
326 : }
327 :
328 2 : bi = bat_iterator(b);
329 2 : vals = Tloc(dst, 0);
330 2 : if (ci.tpe == cand_dense) {
331 4 : for (BUN i = 0; i < ci.ncand; i++) {
332 2 : oid p = (canditer_next_dense(&ci) - off);
333 2 : const char *v = BUNtvar(bi, p);
334 2 : uuid *up = &vals[i], **pp = &up;
335 :
336 2 : if (conv(v, &l, (void **) pp, false) <= 0) {
337 0 : msg = createException(SQL, "batcalc.str2uuidbulk",
338 : SQLSTATE(42000) "Not a UUID");
339 0 : goto bailout1;
340 : }
341 4 : nils |= strNil(v);
342 : }
343 : } else {
344 0 : for (BUN i = 0; i < ci.ncand; i++) {
345 0 : oid p = (canditer_next(&ci) - off);
346 0 : const char *v = BUNtvar(bi, p);
347 0 : uuid *up = &vals[i], **pp = &up;
348 :
349 0 : if (conv(v, &l, (void **) pp, false) <= 0) {
350 0 : msg = createException(SQL, "batcalc.str2uuidbulk",
351 : SQLSTATE(42000) "Not a UUID");
352 0 : goto bailout1;
353 : }
354 0 : nils |= strNil(v);
355 : }
356 : }
357 2 : btkey = bi.key;
358 2 : bailout1:
359 2 : bat_iterator_end(&bi);
360 :
361 2 : bailout:
362 2 : BBPreclaim(b);
363 2 : BBPreclaim(s);
364 2 : if (dst && !msg) {
365 2 : BATsetcount(dst, ci.ncand);
366 2 : dst->tnil = nils;
367 2 : dst->tnonil = !nils;
368 2 : dst->tkey = btkey;
369 2 : dst->tsorted = BATcount(dst) <= 1;
370 2 : dst->trevsorted = BATcount(dst) <= 1;
371 2 : *res = dst->batCacheid;
372 2 : BBPkeepref(dst);
373 0 : } else if (dst)
374 0 : BBPreclaim(dst);
375 2 : return msg;
376 : }
377 :
378 : static str
379 101 : UUIDuuid2str(str *retval, const uuid *u)
380 : {
381 101 : size_t l = 0;
382 101 : *retval = NULL;
383 101 : if (BATatoms[TYPE_uuid].atomToStr(retval, &l, u, false) < 0)
384 0 : throw(MAL, "uuid.str", GDK_EXCEPTION);
385 : return MAL_SUCCEED;
386 : }
387 :
388 : static str
389 0 : UUIDuuid2str_bulk(bat *res, const bat *bid, const bat *sid)
390 : {
391 0 : BAT *b = NULL, *s = NULL, *dst = NULL;
392 0 : str msg = NULL;
393 0 : uuid *restrict vals;
394 0 : struct canditer ci;
395 0 : oid off;
396 0 : bool nils = false, btkey = false;
397 0 : char buf[UUID_STRLEN + 2], *pbuf = buf;
398 0 : size_t l = sizeof(buf);
399 0 : ssize_t (*conv)(char **, size_t *, const void *, bool) = BATatoms[TYPE_uuid].atomToStr;
400 0 : BATiter bi;
401 :
402 0 : if ((b = BATdescriptor(*bid)) == NULL) {
403 0 : msg = createException(SQL, "batcalc.uuid2strbulk",
404 : SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
405 0 : goto bailout;
406 : }
407 0 : if (sid && !is_bat_nil(*sid) && (s = BATdescriptor(*sid)) == NULL) {
408 0 : msg = createException(SQL, "batcalc.uuid2strbulk",
409 : SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
410 0 : goto bailout;
411 : }
412 0 : off = b->hseqbase;
413 0 : canditer_init(&ci, b, s);
414 0 : if (!(dst = COLnew(ci.hseq, TYPE_str, ci.ncand, TRANSIENT))) {
415 0 : msg = createException(SQL, "batcalc.uuid2strbulk",
416 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
417 0 : goto bailout;
418 : }
419 :
420 0 : bi = bat_iterator(b);
421 0 : vals = bi.base;
422 0 : if (ci.tpe == cand_dense) {
423 0 : for (BUN i = 0; i < ci.ncand; i++) {
424 0 : oid p = (canditer_next_dense(&ci) - off);
425 0 : uuid v = vals[p];
426 :
427 0 : if (conv(&pbuf, &l, &v, false) < 0) { /* it should never be reallocated */
428 0 : msg = createException(MAL, "batcalc.uuid2strbulk",
429 : GDK_EXCEPTION);
430 0 : goto bailout1;
431 : }
432 0 : if (tfastins_nocheckVAR(dst, i, buf) != GDK_SUCCEED) {
433 0 : msg = createException(SQL, "batcalc.uuid2strbulk",
434 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
435 0 : goto bailout1;
436 : }
437 0 : nils |= strNil(buf);
438 : }
439 : } else {
440 0 : for (BUN i = 0; i < ci.ncand; i++) {
441 0 : oid p = (canditer_next(&ci) - off);
442 0 : uuid v = vals[p];
443 :
444 0 : if (conv(&pbuf, &l, &v, false) < 0) { /* it should never be reallocated */
445 0 : msg = createException(MAL, "batcalc.uuid2strbulk",
446 : GDK_EXCEPTION);
447 0 : goto bailout1;
448 : }
449 0 : if (tfastins_nocheckVAR(dst, i, buf) != GDK_SUCCEED) {
450 0 : msg = createException(SQL, "batcalc.uuid2strbulk",
451 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
452 0 : goto bailout1;
453 : }
454 0 : nils |= strNil(buf);
455 : }
456 : }
457 0 : btkey = bi.key;
458 0 : bailout1:
459 0 : bat_iterator_end(&bi);
460 :
461 0 : bailout:
462 0 : BBPreclaim(b);
463 0 : BBPreclaim(s);
464 0 : if (dst && !msg) {
465 0 : BATsetcount(dst, ci.ncand);
466 0 : dst->tnil = nils;
467 0 : dst->tnonil = !nils;
468 0 : dst->tkey = btkey;
469 0 : dst->tsorted = BATcount(dst) <= 1;
470 0 : dst->trevsorted = BATcount(dst) <= 1;
471 0 : *res = dst->batCacheid;
472 0 : BBPkeepref(dst);
473 0 : } else if (dst)
474 0 : BBPreclaim(dst);
475 0 : return msg;
476 : }
477 :
478 : #include "mel.h"
479 : mel_func uuid_init_funcs[] = {
480 : command("uuid", "new", UUIDgenerateUuid, true, "Generate a new uuid", args(1,1, arg("",uuid))),
481 : command("uuid", "new", UUIDgenerateUuidInt, false, "Generate a new uuid (dummy version for side effect free multiplex loop)", args(1,2, arg("",uuid),arg("d",int))),
482 : pattern("batuuid", "new", UUIDgenerateUuidInt_bulk, false, "Generate a new uuid (dummy version for side effect free multiplex loop)", args(1,2, batarg("",uuid),batarg("d",int))),
483 : pattern("batuuid", "new", UUIDgenerateUuidInt_bulk, false, "Generate a new uuid (dummy version for side effect free multiplex loop)", args(1,2, batarg("",uuid),arg("card",lng))), /* version with cardinality input */
484 : command("uuid", "uuid", UUIDstr2uuid, false, "Coerce a string to a uuid, validating its format", args(1,2, arg("",uuid),arg("s",str))),
485 : command("uuid", "str", UUIDuuid2str, false, "Coerce a uuid to its string type", args(1,2, arg("",str),arg("u",uuid))),
486 : command("uuid", "isaUUID", UUIDisaUUID, false, "Test a string for a UUID format", args(1,2, arg("",bit),arg("u",str))),
487 : command("batuuid", "isaUUID", UUIDisaUUID_bulk, false, "Test a string for a UUID format", args(1,2, batarg("",bit),batarg("u",str))),
488 : command("calc", "uuid", UUIDstr2uuid, false, "Coerce a string to a uuid, validating its format", args(1,2, arg("",uuid),arg("s",str))),
489 : command("batcalc", "uuid", UUIDstr2uuid_bulk, false, "Coerce a string to a uuid, validating its format", args(1,3, batarg("",uuid),batarg("s",str),batarg("c",oid))),
490 : command("calc", "uuid", UUIDuuid2uuid, false, "", args(1,2, arg("",uuid),arg("u",uuid))),
491 : command("batcalc", "uuid", UUIDuuid2uuid_bulk, false, "", args(1,3, batarg("",uuid),batarg("u",uuid),batarg("c",oid))),
492 : command("calc", "str", UUIDuuid2str, false, "Coerce a uuid to a string type", args(1,2, arg("",str),arg("s",uuid))),
493 : command("batcalc", "str", UUIDuuid2str_bulk, false, "Coerce a uuid to a string type", args(1,3, batarg("",str),batarg("s",uuid),batarg("c",oid))),
494 : { .imp=NULL }
495 : };
496 : #include "mal_import.h"
497 : #ifdef _MSC_VER
498 : #undef read
499 : #pragma section(".CRT$XCU",read)
500 : #endif
501 329 : LIB_STARTUP_FUNC(init_uuid_mal)
502 329 : { mal_module("uuid", NULL, uuid_init_funcs); }
|