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, const int *d)
95 : {
96 0 : (void) d;
97 0 : return UUIDgenerateUuid(retval);
98 : }
99 :
100 : static inline bit
101 102 : isaUUID(const char *s)
102 : {
103 102 : uuid u, *pu = &u;
104 102 : size_t l = UUID_SIZE;
105 102 : ssize_t res = BATatoms[TYPE_uuid].atomFromStr(s, &l, (void **) &pu, false);
106 :
107 102 : if (res > 1)
108 : return true;
109 86 : 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 0 : QryCtx *qry_ctx = MT_thread_get_qry_ctx();
125 :
126 0 : (void) cntxt;
127 0 : if (isaBatType(getArgType(mb, pci, 1))) {
128 0 : bat *bid = getArgReference_bat(stk, pci, 1);
129 0 : if (!(b = BBPquickdesc(*bid))) {
130 0 : throw(MAL, "uuid.generateuuidint_bulk",
131 : SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
132 : }
133 0 : n = BATcount(b);
134 : } else {
135 0 : n = (BUN) *getArgReference_lng(stk, pci, 1);
136 : }
137 :
138 0 : if ((bn = COLnew(b ? b->hseqbase : 0, TYPE_uuid, n, TRANSIENT)) == NULL) {
139 0 : throw(MAL, "uuid.generateuuidint_bulk",
140 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
141 : }
142 0 : bnt = Tloc(bn, 0);
143 0 : TIMEOUT_LOOP_IDX_DECL(i, n, qry_ctx)
144 0 : UUIDgenerateUuid_internal(&(bnt[i]));
145 0 : TIMEOUT_CHECK(qry_ctx, GOTO_LABEL_TIMEOUT_HANDLER(bailout, qry_ctx));
146 0 : BATsetcount(bn, n);
147 0 : bn->tnonil = true;
148 0 : bn->tnil = false;
149 0 : bn->tsorted = n <= 1;
150 0 : bn->trevsorted = n <= 1;
151 0 : bn->tkey = n <= 1;
152 0 : *ret = bn->batCacheid;
153 0 : BBPkeepref(bn);
154 0 : return msg;
155 0 : bailout:
156 0 : BBPreclaim(bn);
157 0 : throw(MAL, "uuid.generateuuidint_bulk", "%s", TIMEOUT_MESSAGE(qry_ctx));
158 : }
159 :
160 : static str
161 31 : UUIDisaUUID(bit *retval, const char *const *s)
162 : {
163 31 : *retval = isaUUID(*s);
164 31 : if (*retval == false)
165 26 : GDKclrerr();
166 31 : return MAL_SUCCEED;
167 : }
168 :
169 : static str
170 21 : UUIDisaUUID_bulk(bat *ret, const bat *bid)
171 : {
172 21 : BAT *b = NULL, *bn = NULL;
173 21 : BUN q;
174 21 : bit *restrict dst;
175 21 : str msg = MAL_SUCCEED;
176 21 : BATiter bi;
177 :
178 21 : if ((b = BATdescriptor(*bid)) == NULL) {
179 0 : msg = createException(MAL, "uuid.isaUUID_bulk",
180 : SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
181 0 : goto bailout;
182 : }
183 21 : q = BATcount(b);
184 21 : if ((bn = COLnew(b->hseqbase, TYPE_bit, q, TRANSIENT)) == NULL) {
185 0 : msg = createException(MAL, "uuid.isaUUID_bulk",
186 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
187 0 : goto bailout;
188 : }
189 21 : dst = Tloc(bn, 0);
190 21 : bi = bat_iterator(b);
191 92 : for (BUN p = 0; p < q; p++)
192 71 : dst[p] = isaUUID(BUNtvar(bi, p));
193 21 : GDKclrerr(); /* Not interested in atomFromStr errors */
194 21 : BATsetcount(bn, q);
195 21 : bn->tnonil = bi.nonil;
196 21 : bn->tnil = bi.nil;
197 21 : bn->tsorted = bn->trevsorted = q < 2;
198 21 : bn->tkey = false;
199 21 : bat_iterator_end(&bi);
200 21 : bailout:
201 21 : BBPreclaim(b);
202 21 : if (bn) { /* implies msg==MAL_SUCCEED */
203 21 : *ret = bn->batCacheid;
204 21 : BBPkeepref(bn);
205 : }
206 21 : return msg;
207 : }
208 :
209 : static str
210 0 : UUIDuuid2uuid(uuid *retval, const uuid *i)
211 : {
212 0 : *retval = *i;
213 0 : return MAL_SUCCEED;
214 : }
215 :
216 : static str
217 0 : UUIDuuid2uuid_bulk(bat *res, const bat *bid, const bat *sid)
218 : {
219 0 : BAT *b = NULL, *s = NULL, *dst = NULL;
220 0 : uuid *restrict bv, *restrict dv;
221 0 : str msg = NULL;
222 0 : struct canditer ci;
223 0 : oid off;
224 0 : bool nils = false, btsorted = false, btrevsorted = false, btkey = false;
225 0 : BATiter bi;
226 :
227 0 : if (sid && !is_bat_nil(*sid)) {
228 0 : if ((s = BATdescriptor(*sid)) == NULL) {
229 0 : msg = createException(SQL, "batcalc.uuid2uuidbulk",
230 : SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
231 0 : goto bailout;
232 : }
233 : } else {
234 0 : BBPretain(*res = *bid); /* nothing to convert, return */
235 0 : return MAL_SUCCEED;
236 : }
237 0 : if ((b = BATdescriptor(*bid)) == NULL) {
238 0 : msg = createException(SQL, "batcalc.uuid2uuidbulk",
239 : SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
240 0 : goto bailout;
241 : }
242 0 : off = b->hseqbase;
243 0 : canditer_init(&ci, b, s);
244 0 : if (!(dst = COLnew(ci.hseq, TYPE_uuid, ci.ncand, TRANSIENT))) {
245 0 : msg = createException(SQL, "batcalc.uuid2uuidbulk",
246 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
247 0 : goto bailout;
248 : }
249 :
250 0 : bi = bat_iterator(b);
251 0 : bv = bi.base;
252 0 : dv = Tloc(dst, 0);
253 0 : if (ci.tpe == cand_dense) {
254 0 : for (BUN i = 0; i < ci.ncand; i++) {
255 0 : oid p = (canditer_next_dense(&ci) - off);
256 0 : uuid v = bv[p];
257 :
258 0 : dv[i] = v;
259 0 : nils |= is_uuid_nil(v);
260 : }
261 : } else {
262 0 : for (BUN i = 0; i < ci.ncand; i++) {
263 0 : oid p = (canditer_next(&ci) - off);
264 0 : uuid v = bv[p];
265 :
266 0 : dv[i] = v;
267 0 : nils |= is_uuid_nil(v);
268 : }
269 : }
270 0 : btkey = bi.key;
271 0 : btsorted = bi.sorted;
272 0 : btrevsorted = bi.revsorted;
273 0 : bat_iterator_end(&bi);
274 :
275 0 : bailout:
276 0 : BBPreclaim(b);
277 0 : BBPreclaim(s);
278 0 : if (dst) { /* implies msg==MAL_SUCCEED */
279 0 : BATsetcount(dst, ci.ncand);
280 0 : dst->tnil = nils;
281 0 : dst->tnonil = !nils;
282 0 : dst->tkey = btkey;
283 0 : dst->tsorted = btsorted;
284 0 : dst->trevsorted = btrevsorted;
285 0 : *res = dst->batCacheid;
286 0 : BBPkeepref(dst);
287 : }
288 : return msg;
289 : }
290 :
291 : static str
292 58 : UUIDstr2uuid(uuid *retval, const char *const *s)
293 : {
294 58 : size_t l = UUID_SIZE;
295 :
296 58 : if (BATatoms[TYPE_uuid].atomFromStr(*s, &l, (void **) &retval, false) > 0) {
297 : return MAL_SUCCEED;
298 : }
299 6 : throw(MAL, "uuid.uuid", "Not a UUID");
300 : }
301 :
302 : static str
303 3 : UUIDstr2uuid_bulk(bat *res, const bat *bid, const bat *sid)
304 : {
305 3 : BAT *b = NULL, *s = NULL, *dst = NULL;
306 3 : BATiter bi;
307 3 : str msg = NULL;
308 3 : uuid *restrict vals;
309 3 : struct canditer ci;
310 3 : oid off;
311 3 : bool nils = false, btkey = false;
312 3 : size_t l = UUID_SIZE;
313 3 : ssize_t (*conv)(const char *, size_t *, void **, bool) = BATatoms[TYPE_uuid].atomFromStr;
314 :
315 3 : if ((b = BATdescriptor(*bid)) == NULL) {
316 0 : msg = createException(SQL, "batcalc.str2uuidbulk",
317 : SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
318 0 : goto bailout;
319 : }
320 3 : if (sid && !is_bat_nil(*sid) && (s = BATdescriptor(*sid)) == NULL) {
321 0 : msg = createException(SQL, "batcalc.str2uuidbulk",
322 : SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
323 0 : goto bailout;
324 : }
325 3 : off = b->hseqbase;
326 3 : canditer_init(&ci, b, s);
327 3 : if (!(dst = COLnew(ci.hseq, TYPE_uuid, ci.ncand, TRANSIENT))) {
328 0 : msg = createException(SQL, "batcalc.str2uuidbulk",
329 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
330 0 : goto bailout;
331 : }
332 :
333 3 : bi = bat_iterator(b);
334 3 : vals = Tloc(dst, 0);
335 3 : if (ci.tpe == cand_dense) {
336 6 : for (BUN i = 0; i < ci.ncand; i++) {
337 3 : oid p = (canditer_next_dense(&ci) - off);
338 3 : const char *v = BUNtvar(bi, p);
339 3 : uuid *up = &vals[i], **pp = &up;
340 :
341 3 : if (conv(v, &l, (void **) pp, false) <= 0) {
342 0 : msg = createException(SQL, "batcalc.str2uuidbulk",
343 : SQLSTATE(42000) "Not a UUID");
344 0 : goto bailout1;
345 : }
346 6 : nils |= strNil(v);
347 : }
348 : } else {
349 0 : for (BUN i = 0; i < ci.ncand; i++) {
350 0 : oid p = (canditer_next(&ci) - off);
351 0 : const char *v = BUNtvar(bi, p);
352 0 : uuid *up = &vals[i], **pp = &up;
353 :
354 0 : if (conv(v, &l, (void **) pp, false) <= 0) {
355 0 : msg = createException(SQL, "batcalc.str2uuidbulk",
356 : SQLSTATE(42000) "Not a UUID");
357 0 : goto bailout1;
358 : }
359 0 : nils |= strNil(v);
360 : }
361 : }
362 3 : btkey = bi.key;
363 3 : bailout1:
364 3 : bat_iterator_end(&bi);
365 :
366 3 : bailout:
367 3 : BBPreclaim(b);
368 3 : BBPreclaim(s);
369 3 : if (dst && !msg) {
370 3 : BATsetcount(dst, ci.ncand);
371 3 : dst->tnil = nils;
372 3 : dst->tnonil = !nils;
373 3 : dst->tkey = btkey;
374 3 : dst->tsorted = BATcount(dst) <= 1;
375 3 : dst->trevsorted = BATcount(dst) <= 1;
376 3 : *res = dst->batCacheid;
377 3 : BBPkeepref(dst);
378 0 : } else if (dst)
379 0 : BBPreclaim(dst);
380 3 : return msg;
381 : }
382 :
383 : static str
384 101 : UUIDuuid2str(str *retval, const uuid *u)
385 : {
386 101 : size_t l = 0;
387 101 : *retval = NULL;
388 101 : if (BATatoms[TYPE_uuid].atomToStr(retval, &l, u, false) < 0)
389 0 : throw(MAL, "uuid.str", GDK_EXCEPTION);
390 : return MAL_SUCCEED;
391 : }
392 :
393 : static str
394 0 : UUIDuuid2str_bulk(bat *res, const bat *bid, const bat *sid)
395 : {
396 0 : BAT *b = NULL, *s = NULL, *dst = NULL;
397 0 : str msg = NULL;
398 0 : uuid *restrict vals;
399 0 : struct canditer ci;
400 0 : oid off;
401 0 : bool nils = false, btkey = false;
402 0 : char buf[UUID_STRLEN + 2], *pbuf = buf;
403 0 : size_t l = sizeof(buf);
404 0 : ssize_t (*conv)(char **, size_t *, const void *, bool) = BATatoms[TYPE_uuid].atomToStr;
405 0 : BATiter bi;
406 :
407 0 : if ((b = BATdescriptor(*bid)) == NULL) {
408 0 : msg = createException(SQL, "batcalc.uuid2strbulk",
409 : SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
410 0 : goto bailout;
411 : }
412 0 : if (sid && !is_bat_nil(*sid) && (s = BATdescriptor(*sid)) == NULL) {
413 0 : msg = createException(SQL, "batcalc.uuid2strbulk",
414 : SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
415 0 : goto bailout;
416 : }
417 0 : off = b->hseqbase;
418 0 : canditer_init(&ci, b, s);
419 0 : if (!(dst = COLnew(ci.hseq, TYPE_str, ci.ncand, TRANSIENT))) {
420 0 : msg = createException(SQL, "batcalc.uuid2strbulk",
421 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
422 0 : goto bailout;
423 : }
424 :
425 0 : bi = bat_iterator(b);
426 0 : vals = bi.base;
427 0 : if (ci.tpe == cand_dense) {
428 0 : for (BUN i = 0; i < ci.ncand; i++) {
429 0 : oid p = (canditer_next_dense(&ci) - off);
430 0 : uuid v = vals[p];
431 :
432 0 : if (conv(&pbuf, &l, &v, false) < 0) { /* it should never be reallocated */
433 0 : msg = createException(MAL, "batcalc.uuid2strbulk",
434 : GDK_EXCEPTION);
435 0 : goto bailout1;
436 : }
437 0 : if (tfastins_nocheckVAR(dst, i, buf) != GDK_SUCCEED) {
438 0 : msg = createException(SQL, "batcalc.uuid2strbulk",
439 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
440 0 : goto bailout1;
441 : }
442 0 : nils |= strNil(buf);
443 : }
444 : } else {
445 0 : for (BUN i = 0; i < ci.ncand; i++) {
446 0 : oid p = (canditer_next(&ci) - off);
447 0 : uuid v = vals[p];
448 :
449 0 : if (conv(&pbuf, &l, &v, false) < 0) { /* it should never be reallocated */
450 0 : msg = createException(MAL, "batcalc.uuid2strbulk",
451 : GDK_EXCEPTION);
452 0 : goto bailout1;
453 : }
454 0 : if (tfastins_nocheckVAR(dst, i, buf) != GDK_SUCCEED) {
455 0 : msg = createException(SQL, "batcalc.uuid2strbulk",
456 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
457 0 : goto bailout1;
458 : }
459 0 : nils |= strNil(buf);
460 : }
461 : }
462 0 : btkey = bi.key;
463 0 : bailout1:
464 0 : bat_iterator_end(&bi);
465 :
466 0 : bailout:
467 0 : BBPreclaim(b);
468 0 : BBPreclaim(s);
469 0 : if (dst && !msg) {
470 0 : BATsetcount(dst, ci.ncand);
471 0 : dst->tnil = nils;
472 0 : dst->tnonil = !nils;
473 0 : dst->tkey = btkey;
474 0 : dst->tsorted = BATcount(dst) <= 1;
475 0 : dst->trevsorted = BATcount(dst) <= 1;
476 0 : *res = dst->batCacheid;
477 0 : BBPkeepref(dst);
478 0 : } else if (dst)
479 0 : BBPreclaim(dst);
480 0 : return msg;
481 : }
482 :
483 : #include "mel.h"
484 : mel_func uuid_init_funcs[] = {
485 : command("uuid", "new", UUIDgenerateUuid, true, "Generate a new uuid", args(1,1, arg("",uuid))),
486 : 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))),
487 : 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))),
488 : 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 */
489 : command("uuid", "uuid", UUIDstr2uuid, false, "Coerce a string to a uuid, validating its format", args(1,2, arg("",uuid),arg("s",str))),
490 : command("uuid", "str", UUIDuuid2str, false, "Coerce a uuid to its string type", args(1,2, arg("",str),arg("u",uuid))),
491 : command("uuid", "isaUUID", UUIDisaUUID, false, "Test a string for a UUID format", args(1,2, arg("",bit),arg("u",str))),
492 : command("batuuid", "isaUUID", UUIDisaUUID_bulk, false, "Test a string for a UUID format", args(1,2, batarg("",bit),batarg("u",str))),
493 : command("calc", "uuid", UUIDstr2uuid, false, "Coerce a string to a uuid, validating its format", args(1,2, arg("",uuid),arg("s",str))),
494 : 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))),
495 : command("calc", "uuid", UUIDuuid2uuid, false, "", args(1,2, arg("",uuid),arg("u",uuid))),
496 : command("batcalc", "uuid", UUIDuuid2uuid_bulk, false, "", args(1,3, batarg("",uuid),batarg("u",uuid),batarg("c",oid))),
497 : command("calc", "str", UUIDuuid2str, false, "Coerce a uuid to a string type", args(1,2, arg("",str),arg("s",uuid))),
498 : command("batcalc", "str", UUIDuuid2str_bulk, false, "Coerce a uuid to a string type", args(1,3, batarg("",str),batarg("s",uuid),batarg("c",oid))),
499 : { .imp=NULL }
500 : };
501 : #include "mal_import.h"
502 : #ifdef _MSC_VER
503 : #undef read
504 : #pragma section(".CRT$XCU",read)
505 : #endif
506 308 : LIB_STARTUP_FUNC(init_uuid_mal)
507 308 : { mal_module("uuid", NULL, uuid_init_funcs); }
|