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 : #include "monetdb_config.h"
14 : #include "mal.h"
15 : #include "mal_stack.h"
16 : #include "mal_linker.h"
17 : #include "gdk.h"
18 : #include "sql_catalog.h"
19 : #include "sql_scenario.h"
20 : #include "sql_cast.h"
21 : #include "sql_execute.h"
22 : #include "sql_storage.h"
23 : #include "cheader.h"
24 : #include "cheader.text.h"
25 :
26 : #include "gdk_time.h"
27 : #include "mutils.h"
28 :
29 : #include <setjmp.h>
30 : #include <signal.h>
31 : #include <sys/mman.h>
32 : #include <unistd.h>
33 : #include <string.h>
34 :
35 : #if defined(__GNUC__) && !defined(__clang__)
36 : #pragma GCC diagnostic ignored "-Wclobbered"
37 : #endif
38 :
39 : const char *mprotect_enableflag = "enable_mprotect";
40 : static bool option_enable_mprotect = false;
41 : const char *longjmp_enableflag = "enable_longjmp";
42 : static bool option_enable_longjmp = false;
43 :
44 : typedef struct _allocated_region {
45 : struct _allocated_region *next;
46 : } allocated_region;
47 :
48 : struct _mprotected_region;
49 : typedef struct _mprotected_region {
50 : void *addr;
51 : size_t len;
52 :
53 : struct _mprotected_region *next;
54 : } mprotected_region;
55 :
56 : static char *mprotect_region(void *addr, size_t len,
57 : mprotected_region **regions);
58 : struct capi_tls_s {
59 : allocated_region *ar;
60 : jmp_buf jb;
61 : };
62 : static MT_TLS_t capi_tls_key;
63 :
64 : typedef char *(*jitted_function)(void **inputs, void **outputs,
65 : malloc_function_ptr malloc, free_function_ptr free);
66 :
67 : struct _cached_functions;
68 : typedef struct _cached_functions {
69 : jitted_function function;
70 : BUN expression_hash;
71 : char *parameters;
72 : void *dll_handle;
73 : struct _cached_functions *next;
74 : } cached_functions;
75 :
76 : #define FUNCTION_CACHE_SIZE 128
77 :
78 : static cached_functions *function_cache[FUNCTION_CACHE_SIZE];
79 : static MT_Lock cache_lock = MT_LOCK_INITIALIZER(cache_lock);
80 : static int cudf_initialized = 0;
81 :
82 : static str CUDFeval(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci,
83 : bool grouped);
84 :
85 36 : static str CUDFevalStd(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
86 : {
87 36 : return CUDFeval(cntxt, mb, stk, pci, false);
88 : }
89 :
90 11 : static str CUDFevalAggr(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
91 : {
92 11 : return CUDFeval(cntxt, mb, stk, pci, true);
93 : }
94 :
95 5 : static str CUDFprelude(void)
96 : {
97 5 : if (!cudf_initialized) {
98 5 : cudf_initialized = true;
99 5 : option_enable_mprotect = GDKgetenv_istrue(mprotect_enableflag) || GDKgetenv_isyes(mprotect_enableflag);
100 5 : option_enable_longjmp = GDKgetenv_istrue(longjmp_enableflag) || GDKgetenv_isyes(longjmp_enableflag);
101 5 : MT_alloc_tls(&capi_tls_key);
102 : }
103 5 : return MAL_SUCCEED;
104 : }
105 :
106 702 : static bool WriteDataToFile(FILE *f, const void *data, size_t data_size)
107 : {
108 702 : fwrite(data, data_size, 1, f);
109 702 : return (!ferror(f));
110 : }
111 :
112 694 : static bool WriteTextToFile(FILE *f, const char *data)
113 : {
114 694 : return WriteDataToFile(f, data, strlen(data));
115 : }
116 :
117 0 : static _Noreturn void handler(int sig, siginfo_t *si, void *unused)
118 : {
119 0 : (void)sig;
120 0 : (void)si;
121 0 : (void)unused;
122 :
123 0 : struct capi_tls_s *tls = MT_tls_get(capi_tls_key);
124 0 : longjmp(tls->jb, 1);
125 : }
126 :
127 46 : static bool can_mprotect_region(void* addr) {
128 46 : if (!option_enable_mprotect) return false;
129 0 : int pagesize = MT_pagesize();
130 0 : void* page_begin = (void *)((size_t)addr - (size_t)addr % pagesize);
131 0 : return page_begin == addr;
132 : }
133 :
134 0 : static char *mprotect_region(void *addr, size_t len,
135 : mprotected_region **regions)
136 : {
137 0 : mprotected_region *region;
138 0 : if (len == 0)
139 : return NULL;
140 :
141 0 : assert(can_mprotect_region(addr));
142 :
143 0 : region = GDKmalloc(sizeof(mprotected_region));
144 0 : if (!region) {
145 : return MAL_MALLOC_FAIL;
146 : }
147 0 : region->addr = addr;
148 0 : region->len = len;
149 0 : region->next = *regions;
150 0 : *regions = region;
151 0 : return NULL;
152 : }
153 :
154 0 : static void clear_mprotect(void *addr, size_t len)
155 : {
156 0 : if (addr)
157 0 : mprotect(addr, len, PROT_READ | PROT_WRITE);
158 : }
159 :
160 : #define ATTEMPT_TO_WRITE_TO_FILE(f, data) \
161 : if (!WriteTextToFile(f, data)) { \
162 : errno = 0; \
163 : msg = createException(MAL, "cudf.eval", "Write error."); \
164 : goto wrapup; \
165 : }
166 :
167 : #define ATTEMPT_TO_WRITE_DATA_TO_FILE(f, data, size) \
168 : if (!WriteDataToFile(f, data, size)) { \
169 : errno = 0; \
170 : msg = createException(MAL, "cudf.eval", "Write error."); \
171 : goto wrapup; \
172 : }
173 :
174 310 : static void *jump_GDK_malloc(size_t size)
175 : {
176 310 : if (size == 0)
177 : return NULL;
178 310 : void *ptr = GDKmalloc(size);
179 310 : if (!ptr && option_enable_longjmp) {
180 0 : struct capi_tls_s *tls = MT_tls_get(capi_tls_key);
181 0 : longjmp(tls->jb, 2);
182 : }
183 : return ptr;
184 : }
185 :
186 294 : static inline void *add_allocated_region(void *ptr)
187 : {
188 294 : allocated_region *region = (allocated_region *)ptr;
189 294 : struct capi_tls_s *tls = MT_tls_get(capi_tls_key);
190 294 : region->next = tls->ar;
191 294 : tls->ar = region;
192 294 : return (char *)ptr + sizeof(allocated_region);
193 : }
194 :
195 295 : static void *wrapped_GDK_malloc(size_t size)
196 : {
197 295 : if (size == 0)
198 : return NULL;
199 294 : void *ptr = jump_GDK_malloc(size + sizeof(allocated_region));
200 294 : return add_allocated_region(ptr);
201 : }
202 :
203 0 : static void wrapped_GDK_free(void* ptr) {
204 0 : (void) ptr;
205 0 : return;
206 : }
207 :
208 : #define GENERATE_NUMERIC_IS_NULL(type, tpename) \
209 : static int tpename##_is_null(type value) { return is_##tpename##_nil(value); }
210 :
211 : #define GENERATE_NUMERIC_INITIALIZE(type, tpename) \
212 : static void tpename##_initialize(struct cudf_data_struct_##tpename *self, \
213 : size_t count) \
214 : { \
215 : BAT* b; \
216 : if (self->bat) { \
217 : BBPunfix(((BAT*)self->bat)->batCacheid); \
218 : self->bat = NULL; \
219 : } \
220 : b = COLnew(0, TYPE_##tpename, count, TRANSIENT); \
221 : if (!b) { \
222 : if (option_enable_longjmp) { \
223 : struct capi_tls_s *tls = MT_tls_get(capi_tls_key); \
224 : longjmp(tls->jb, 2); \
225 : } \
226 : else return; \
227 : } \
228 : self->bat = (void*) b; \
229 : self->count = count; \
230 : self->data = (type*) b->theap->base; \
231 : BATsetcount(b, count); \
232 : }
233 :
234 : #define GENERATE_NUMERIC_ALL(type, tpename) \
235 : GENERATE_NUMERIC_INITIALIZE(type, tpename) \
236 : GENERATE_NUMERIC_IS_NULL(type, tpename)
237 :
238 :
239 : #define GENERATE_BASE_HEADERS(type, tpename) \
240 : static int tpename##_is_null(type value); \
241 : static void tpename##_initialize(struct cudf_data_struct_##tpename *self, \
242 : size_t count) \
243 : { \
244 : self->count = count; \
245 : self->data = jump_GDK_malloc(count * sizeof(self->null_value)); \
246 : }
247 :
248 0 : GENERATE_NUMERIC_ALL(bit, bit);
249 0 : GENERATE_NUMERIC_ALL(bte, bte);
250 0 : GENERATE_NUMERIC_ALL(sht, sht);
251 19 : GENERATE_NUMERIC_ALL(int, int);
252 9 : GENERATE_NUMERIC_ALL(lng, lng);
253 5 : GENERATE_NUMERIC_ALL(flt, flt);
254 10 : GENERATE_NUMERIC_ALL(dbl, dbl);
255 0 : GENERATE_NUMERIC_ALL(oid, oid);
256 :
257 8 : GENERATE_BASE_HEADERS(char *, str);
258 2 : GENERATE_BASE_HEADERS(cudf_data_date, date);
259 2 : GENERATE_BASE_HEADERS(cudf_data_time, time);
260 2 : GENERATE_BASE_HEADERS(cudf_data_timestamp, timestamp);
261 : static int blob_is_null(cudf_data_blob value);
262 : static void blob_initialize(struct cudf_data_struct_blob *self,
263 : size_t count);
264 :
265 : #define GENERATE_BAT_INPUT_BASE(tpe) \
266 : struct cudf_data_struct_##tpe *bat_data = \
267 : GDKzalloc(sizeof(struct cudf_data_struct_##tpe)); \
268 : if (!bat_data) { \
269 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL); \
270 : goto wrapup; \
271 : } \
272 : inputs[index] = bat_data; \
273 : bat_data->is_null = tpe##_is_null; \
274 : bat_data->scale = \
275 : argnode ? pow(10, ((sql_arg *)argnode->data)->type.scale) : 1; \
276 : bat_data->bat = NULL; \
277 : bat_data->initialize = (void (*)(void *, size_t))tpe##_initialize;
278 :
279 : #define GENERATE_BAT_INPUT(b, tpe) \
280 : { \
281 : char *mprotect_retval; \
282 : GENERATE_BAT_INPUT_BASE(tpe); \
283 : bat_data->count = BATcount(b); \
284 : bat_data->null_value = tpe##_nil; \
285 : if (BATtdense(b)) { \
286 : size_t it = 0; \
287 : tpe val = b->tseqbase; \
288 : /* bat is dense, materialize it */ \
289 : bat_data->data = GDKmalloc( \
290 : bat_data->count * sizeof(bat_data->null_value)); \
291 : if (!bat_data->data) { \
292 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL); \
293 : goto wrapup; \
294 : } \
295 : bat_data->alloced = true; \
296 : for (it = 0; it < bat_data->count; it++) { \
297 : bat_data->data[it] = val++; \
298 : } \
299 : } else if (can_mprotect_region(Tloc(b, 0))) { \
300 : bat_data->data = (tpe *)Tloc(b, 0); \
301 : mprotect_retval = mprotect_region( \
302 : bat_data->data, \
303 : bat_data->count * sizeof(bat_data->null_value), ®ions); \
304 : if (mprotect_retval) { \
305 : msg = createException(MAL, "cudf.eval", \
306 : "Failed to mprotect region: %s", \
307 : mprotect_retval); \
308 : goto wrapup; \
309 : } \
310 : } else { \
311 : /* cannot mprotect bat region, copy data */ \
312 : bat_data->data = GDKmalloc( \
313 : bat_data->count * sizeof(bat_data->null_value)); \
314 : if (bat_data->count > 0 && !bat_data->data) { \
315 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL); \
316 : goto wrapup; \
317 : } \
318 : bat_data->alloced = true; \
319 : memcpy(bat_data->data, Tloc(b, 0), \
320 : bat_data->count * sizeof(bat_data->null_value)); \
321 : } \
322 : }
323 :
324 : #define GENERATE_BAT_OUTPUT_BASE(tpe) \
325 : struct cudf_data_struct_##tpe *bat_data = \
326 : GDKzalloc(sizeof(struct cudf_data_struct_##tpe)); \
327 : if (!bat_data) { \
328 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL); \
329 : goto wrapup; \
330 : } \
331 : outputs[index] = bat_data; \
332 : bat_data->count = 0; \
333 : bat_data->data = NULL; \
334 : bat_data->is_null = tpe##_is_null; \
335 : bat_data->scale = \
336 : argnode ? pow(10, ((sql_arg *)argnode->data)->type.scale) : 1; \
337 : bat_data->initialize = (void (*)(void *, size_t))tpe##_initialize;
338 :
339 : #define GENERATE_BAT_OUTPUT(tpe) \
340 : { \
341 : GENERATE_BAT_OUTPUT_BASE(tpe); \
342 : bat_data->null_value = tpe##_nil; \
343 : }
344 :
345 : const char *debug_flag = "capi_use_debug";
346 : const char *cc_flag = "capi_cc";
347 : const char *cpp_flag = "capi_cpp";
348 :
349 : const char *cflags_pragma = "#pragma CFLAGS ";
350 : const char *ldflags_pragma = "#pragma LDFLAGS ";
351 :
352 : #define JIT_COMPILER_NAME "cc"
353 : #define JIT_CPP_COMPILER_NAME "c++"
354 :
355 : static bool isAlloced(int type, void *struct_ptr);
356 : static bool isValloced(int type, void *struct_ptr);
357 : static size_t GetTypeCount(int type, void *struct_ptr);
358 : static void *GetTypeData(int type, void *struct_ptr);
359 : static void *GetTypeBat(int type, void *struct_ptr);
360 : static const char *GetTypeName(int type);
361 :
362 : static void data_from_date(date d, cudf_data_date *ptr);
363 : static date date_from_data(cudf_data_date *ptr);
364 : static void data_from_time(daytime d, cudf_data_time *ptr);
365 : static daytime time_from_data(cudf_data_time *ptr);
366 : static void data_from_timestamp(timestamp d, cudf_data_timestamp *ptr);
367 : static timestamp timestamp_from_data(cudf_data_timestamp *ptr);
368 :
369 : static const char valid_path_characters[] = "abcdefghijklmnopqrstuvwxyz";
370 :
371 : static str
372 3 : empty_return(MalBlkPtr mb, MalStkPtr stk, InstrPtr pci, size_t retcols, oid seqbase)
373 : {
374 3 : str msg = MAL_SUCCEED;
375 3 : void **res = GDKzalloc(retcols * sizeof(void*));
376 :
377 3 : if (!res) {
378 0 : msg = createException(MAL, "capi.eval", SQLSTATE(HY013) MAL_MALLOC_FAIL);
379 0 : goto bailout;
380 : }
381 :
382 6 : for (size_t i = 0; i < retcols; i++) {
383 3 : if (isaBatType(getArgType(mb, pci, i))) {
384 2 : BAT *b = COLnew(seqbase, getBatType(getArgType(mb, pci, i)), 0, TRANSIENT);
385 2 : if (!b) {
386 0 : msg = createException(MAL, "capi.eval", GDK_EXCEPTION);
387 0 : goto bailout;
388 : }
389 2 : ((BAT**)res)[i] = b;
390 : } else { // single value return, only for non-grouped aggregations
391 : // return NULL to conform to SQL aggregates
392 1 : int tpe = getArgType(mb, pci, i);
393 1 : if (!VALinit(&stk->stk[pci->argv[i]], tpe, ATOMnilptr(tpe))) {
394 0 : msg = createException(MAL, "capi.eval", SQLSTATE(HY013) MAL_MALLOC_FAIL);
395 0 : goto bailout;
396 : }
397 1 : ((ValPtr*)res)[i] = &stk->stk[pci->argv[i]];
398 : }
399 : }
400 :
401 3 : bailout:
402 3 : if (res) {
403 6 : for (size_t i = 0; i < retcols; i++) {
404 3 : if (isaBatType(getArgType(mb, pci, i))) {
405 2 : BAT *b = ((BAT**)res)[i];
406 :
407 2 : if (b && msg) {
408 0 : BBPreclaim(b);
409 2 : } else if (b) {
410 2 : *getArgReference_bat(stk, pci, i) = b->batCacheid;
411 2 : BBPkeepref(b);
412 : }
413 1 : } else if (msg) {
414 0 : ValPtr pt = ((ValPtr*)res)[i];
415 :
416 0 : if (pt)
417 0 : VALclear(pt);
418 : }
419 : }
420 3 : GDKfree(res);
421 : }
422 3 : return msg;
423 : }
424 :
425 47 : static str CUDFeval(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci,
426 : bool grouped)
427 : {
428 47 : sql_func *sqlfun = NULL;
429 47 : bit use_cpp = *getArgReference_bit(stk, pci, pci->retc + 1);
430 47 : str exprStr = *getArgReference_str(stk, pci, pci->retc + 2);
431 :
432 47 : const int ARG_OFFSET = 3;
433 :
434 47 : size_t i = 0, j = 0;
435 47 : char argbuf[64];
436 47 : char buf[8192];
437 47 : char *fname = NULL;
438 47 : char *oname = NULL;
439 47 : char *libname = NULL;
440 47 : char error_buf[BUFSIZ];
441 47 : char total_error_buf[8192];
442 47 : size_t error_buffer_position = 0;
443 47 : str *args = NULL;
444 47 : str *output_names = NULL;
445 47 : char *msg = MAL_SUCCEED;
446 47 : node *argnode;
447 47 : int seengrp = 0;
448 47 : FILE *f = NULL;
449 47 : void *handle = NULL;
450 47 : jitted_function func = NULL;
451 47 : int ret, limit_argc = 0;
452 :
453 47 : FILE *compiler = NULL;
454 47 : int compiler_return_code;
455 :
456 47 : void **inputs = NULL;
457 47 : size_t input_count = 0;
458 47 : void **outputs = NULL;
459 47 : size_t output_count = 0;
460 47 : BAT **input_bats = NULL;
461 47 : mprotected_region *regions = NULL, *region_iter = NULL;
462 :
463 47 : lng initial_output_count = -1;
464 :
465 47 : struct sigaction sa = (struct sigaction) {.sa_flags = 0}, oldsa, oldsb;
466 47 : sigset_t signal_set;
467 :
468 : #ifdef NDEBUG
469 : bool debug_build =
470 : GDKgetenv_istrue(debug_flag) || GDKgetenv_isyes(debug_flag);
471 : #else
472 47 : bool debug_build = true;
473 : #endif
474 47 : char* extra_cflags = NULL;
475 47 : char* extra_ldflags = NULL;
476 :
477 :
478 47 : const char *compilation_flags = debug_build ? "-g -O0" : "-O2";
479 94 : const char *c_compiler =
480 1 : use_cpp ? (GDKgetenv(cpp_flag) ? GDKgetenv(cpp_flag)
481 1 : : JIT_CPP_COMPILER_NAME)
482 47 : : (GDKgetenv(cc_flag) ? GDKgetenv(cc_flag) : JIT_COMPILER_NAME);
483 :
484 47 : const char *struct_prefix = "struct cudf_data_struct_";
485 47 : const char *funcname;
486 :
487 47 : BUN expression_hash = 0, funcname_hash = 0;
488 47 : cached_functions *cached_function;
489 47 : char *function_parameters = NULL;
490 47 : size_t input_size = 0;
491 47 : bit non_grouped_aggregate = 0;
492 :
493 47 : size_t index = 0;
494 47 : int bat_type = 0;
495 47 : const char* tpe = NULL;
496 :
497 47 : size_t extra_inputs = 0;
498 :
499 47 : struct capi_tls_s tls;
500 :
501 47 : tls.ar = NULL;
502 47 : MT_tls_set(capi_tls_key, &tls);
503 :
504 47 : (void)cntxt;
505 :
506 47 : if (!GDKgetenv_istrue("embedded_c") && !GDKgetenv_isyes("embedded_c"))
507 0 : throw(MAL, "cudf.eval", "Embedded C has not been enabled. "
508 : "Start server with --set embedded_c=true");
509 :
510 : // we need to be able to catch segfaults and bus errors
511 : // so we can work with mprotect to prevent UDFs from changing
512 : // the input data
513 :
514 : // we remove them from the pthread_sigmask
515 47 : if (option_enable_mprotect) {
516 0 : (void)sigemptyset(&signal_set);
517 0 : (void)sigaddset(&signal_set, SIGSEGV);
518 0 : (void)sigaddset(&signal_set, SIGBUS);
519 0 : (void)pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
520 : }
521 :
522 47 : sqlfun = (sql_func *)*getArgReference_ptr(stk, pci, pci->retc);
523 47 : funcname = sqlfun ? sqlfun->base.name : "yet_another_c_function";
524 :
525 47 : args = (str *)GDKzalloc(sizeof(str) * pci->argc);
526 47 : output_names = (str *)GDKzalloc(sizeof(str) * pci->argc);
527 47 : if (!args || !output_names) {
528 0 : throw(MAL, "cudf.eval", MAL_MALLOC_FAIL);
529 : }
530 :
531 : // retrieve the argument names from the sqlfun structure
532 : // first argument after the return contains the pointer to the sql_func
533 : // structure
534 47 : if (sqlfun != NULL) {
535 : // retrieve the argument names (inputs)
536 47 : if (sqlfun->ops->cnt > 0) {
537 46 : int carg = pci->retc + ARG_OFFSET;
538 46 : argnode = sqlfun->ops->h;
539 103 : while (argnode) {
540 57 : char *argname = ((sql_arg *)argnode->data)->name;
541 57 : args[carg] = GDKstrdup(argname);
542 57 : if (!args[carg]) {
543 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
544 0 : goto wrapup;
545 : }
546 57 : carg++;
547 57 : argnode = argnode->next;
548 : }
549 : }
550 : // retrieve the output names
551 47 : argnode = sqlfun->res->h;
552 96 : for (i = 0; i < (size_t)sqlfun->res->cnt; i++) {
553 49 : output_names[i] = GDKstrdup(((sql_arg *)argnode->data)->name);
554 49 : argnode = argnode->next;
555 : }
556 : }
557 :
558 : // name unnamed outputs
559 96 : for (i = 0; i < (size_t)pci->retc; i++) {
560 49 : if (!output_names[i]) {
561 0 : if (pci->retc > 1) {
562 0 : snprintf(argbuf, sizeof(argbuf), "output%zu", i);
563 : } else {
564 : // just call it "output" if there is only one
565 0 : snprintf(argbuf, sizeof(argbuf), "output");
566 : }
567 0 : output_names[i] = GDKstrdup(argbuf);
568 : }
569 : }
570 : // the first unknown argument is the group, we don't really care for the
571 : // rest.
572 109 : for (i = pci->retc + ARG_OFFSET; i < (size_t)pci->argc && !seengrp; i++) {
573 62 : if (args[i] == NULL) {
574 5 : if (!seengrp && grouped) {
575 5 : args[i] = GDKstrdup("aggr_group");
576 5 : if (!args[i]) {
577 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
578 0 : goto wrapup;
579 : }
580 5 : seengrp = i; /* Don't be interested in the extents BAT */
581 : } else {
582 0 : snprintf(argbuf, sizeof(argbuf), "arg%zu", i - pci->retc - 1);
583 0 : args[i] = GDKstrdup(argbuf);
584 0 : if (!args[i]) {
585 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
586 0 : goto wrapup;
587 : }
588 : }
589 : }
590 : }
591 : // the first index where input arguments are not relevant for the C UDF
592 47 : limit_argc = i;
593 : // non-grouped aggregates don't have the group list
594 : // to allow users to write code for both grouped and non-grouped aggregates
595 : // we create an "aggr_group" BAT for non-grouped aggregates
596 47 : non_grouped_aggregate = grouped && !seengrp;
597 :
598 47 : input_count = limit_argc - (pci->retc + ARG_OFFSET);
599 47 : output_count = pci->retc;
600 :
601 : // begin the compilation phase
602 : // first look up if we have already compiled this function
603 47 : expression_hash = 0;
604 47 : expression_hash = strHash(exprStr);
605 47 : funcname_hash = strHash(funcname);
606 47 : funcname_hash = funcname_hash % FUNCTION_CACHE_SIZE;
607 47 : j = 0;
608 299 : for (i = 0; i < (size_t)limit_argc; i++) {
609 252 : if (args[i]) {
610 62 : j += strlen(args[i]);
611 : }
612 252 : if (output_names[i]) {
613 49 : j += strlen(output_names[i]);
614 : }
615 : }
616 :
617 94 : function_parameters =
618 47 : GDKzalloc((j + input_count + output_count + 1) * sizeof(char));
619 47 : if (!function_parameters) {
620 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
621 0 : goto wrapup;
622 : }
623 109 : for (i = 0; i < input_count; i++) {
624 62 : if (!isaBatType(getArgType(mb, pci, i))) {
625 30 : function_parameters[i] = getArgType(mb, pci, i);
626 : } else {
627 32 : function_parameters[i] = getBatType(getArgType(mb, pci, i));
628 : }
629 : }
630 96 : for (i = 0; i < output_count; i++) {
631 49 : if (!isaBatType(getArgType(mb, pci, i))) {
632 16 : function_parameters[input_count + i] = getArgType(mb, pci, i);
633 : } else {
634 33 : function_parameters[input_count + i] =
635 : getBatType(getArgType(mb, pci, i));
636 : }
637 : }
638 47 : j = input_count + output_count;
639 299 : for (i = 0; i < (size_t)limit_argc; i++) {
640 252 : if (args[i]) {
641 62 : size_t len = strlen(args[i]);
642 62 : memcpy(function_parameters + j, args[i], len);
643 62 : j += len;
644 : }
645 252 : if (output_names[i]) {
646 49 : size_t len = strlen(output_names[i]);
647 49 : memcpy(function_parameters + j, output_names[i], len);
648 49 : j += len;
649 : }
650 : }
651 :
652 47 : MT_lock_set(&cache_lock);
653 47 : cached_function = function_cache[funcname_hash];
654 71 : while (cached_function) {
655 36 : if (cached_function->expression_hash == expression_hash &&
656 18 : strcmp(cached_function->parameters, function_parameters) == 0) {
657 : // this function matches our compiled function
658 : // in both source code and parameters
659 : // use the already compiled function instead of recompiling
660 12 : func = cached_function->function;
661 12 : break;
662 : }
663 24 : cached_function = cached_function->next;
664 : }
665 47 : MT_lock_unset(&cache_lock);
666 :
667 47 : if (!func) {
668 : // function was not found in the cache
669 : // we have to compile it
670 :
671 : // first generate the names of the files
672 : // we place the temporary files in the DELDIR directory
673 : // because this will be removed again upon server startup
674 35 : const int RANDOM_NAME_SIZE = 32;
675 35 : const char *prefix = TEMPDIR_NAME DIR_SEP_STR;
676 35 : size_t prefix_size = strlen(prefix);
677 35 : char *deldirpath;
678 :
679 35 : memcpy(buf, prefix, sizeof(char) * strlen(prefix));
680 : // generate a random 32-character name for the temporary files
681 1155 : for (i = prefix_size; i < prefix_size + RANDOM_NAME_SIZE; i++) {
682 1120 : buf[i] = valid_path_characters[rand() %
683 : (sizeof(valid_path_characters) - 1)];
684 : }
685 35 : buf[i] = '\0';
686 35 : fname = GDKfilepath(0, BATDIR, buf, "c");
687 35 : if (fname == NULL) {
688 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
689 0 : goto wrapup;
690 : }
691 35 : oname = GDKstrdup(fname);
692 35 : if (oname == NULL) {
693 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
694 0 : goto wrapup;
695 : }
696 35 : oname[strlen(oname) - 1] = 'o';
697 :
698 35 : memmove(buf + strlen(SO_PREFIX) + prefix_size, buf + prefix_size,
699 : i + 1 - prefix_size);
700 35 : memcpy(buf + prefix_size, SO_PREFIX, sizeof(char) * strlen(SO_PREFIX));
701 35 : libname =
702 35 : GDKfilepath(0, BATDIR, buf, SO_EXT[0] == '.' ? &SO_EXT[1] : SO_EXT);
703 35 : if (libname == NULL) {
704 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
705 0 : goto wrapup;
706 : }
707 :
708 : // if DELDIR directory does not exist, create it
709 35 : deldirpath = GDKfilepath(0, NULL, TEMPDIR, NULL);
710 35 : if (deldirpath == NULL) {
711 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
712 0 : goto wrapup;
713 : }
714 35 : if (MT_mkdir(deldirpath) < 0 && errno != EEXIST) {
715 0 : msg = createException(MAL, "cudf.eval",
716 : "cannot create directory %s\n", deldirpath);
717 0 : goto wrapup;
718 : }
719 35 : GDKfree(deldirpath);
720 :
721 : // now generate the source file
722 35 : f = MT_fopen(fname, "w+");
723 35 : if (!f) {
724 0 : msg = createException(MAL, "cudf.eval",
725 : "Failed to open file for JIT compilation: %s",
726 0 : GDKstrerror(errno, (char[128]){0}, 128));
727 0 : errno = 0;
728 0 : goto wrapup;
729 : }
730 :
731 : // include some standard C headers first
732 35 : ATTEMPT_TO_WRITE_TO_FILE(f, "#include <stdio.h>\n");
733 35 : ATTEMPT_TO_WRITE_TO_FILE(f, "#include <stdlib.h>\n");
734 35 : ATTEMPT_TO_WRITE_TO_FILE(f, "#include <string.h>\n");
735 : // we include "cheader.h", but not directly to avoid having to deal with
736 : // headers, etc...
737 : // Instead it is embedded in a string (loaded from "cheader.text.h")
738 : // this file contains the structures used for input/output arguments
739 35 : ATTEMPT_TO_WRITE_TO_FILE(f, cheader_header_text);
740 : // some monetdb-style typedefs to make it easier
741 35 : ATTEMPT_TO_WRITE_TO_FILE(f, "typedef int8_t bte;\n");
742 35 : ATTEMPT_TO_WRITE_TO_FILE(f, "typedef int16_t sht;\n");
743 35 : ATTEMPT_TO_WRITE_TO_FILE(f, "typedef int64_t lng;\n");
744 35 : ATTEMPT_TO_WRITE_TO_FILE(f, "typedef float flt;\n");
745 35 : ATTEMPT_TO_WRITE_TO_FILE(f, "typedef double dbl;\n");
746 35 : ATTEMPT_TO_WRITE_TO_FILE(f, "typedef char* str;\n");
747 35 : ATTEMPT_TO_WRITE_TO_FILE(f, "typedef size_t oid;\n");
748 : // now we search exprStr for any preprocessor directives (#)
749 : // we move these to the top of the file
750 : // this allows the user to normally #include files
751 : {
752 : int preprocessor_start = 0;
753 : bool is_preprocessor_directive = false;
754 : bool new_line = false;
755 9521 : for (i = 0; i < strlen(exprStr); i++) {
756 9486 : if (exprStr[i] == '\n') {
757 349 : if (is_preprocessor_directive) {
758 : // the previous line was a preprocessor directive
759 : // first check if it is one of our special preprocessor directives
760 8 : if (i - preprocessor_start >= strlen(cflags_pragma) &&
761 8 : memcmp(exprStr + preprocessor_start, cflags_pragma, strlen(cflags_pragma)) == 0) {
762 0 : size_t cflags_characters = (i - preprocessor_start) - strlen(cflags_pragma);
763 0 : if (cflags_characters > 0 && !extra_cflags) {
764 0 : extra_cflags = GDKzalloc(cflags_characters + 1);
765 0 : if (extra_cflags) {
766 0 : memcpy(extra_cflags, exprStr + preprocessor_start + strlen(cflags_pragma), cflags_characters);
767 : }
768 : }
769 8 : } else if (i - preprocessor_start >= strlen(ldflags_pragma) &&
770 8 : memcmp(exprStr + preprocessor_start, ldflags_pragma, strlen(ldflags_pragma)) == 0) {
771 0 : size_t ldflags_characters = (i - preprocessor_start) - strlen(ldflags_pragma);
772 0 : if (ldflags_characters > 0 && !extra_ldflags) {
773 0 : extra_ldflags = GDKzalloc(ldflags_characters + 1);
774 0 : if (extra_ldflags) {
775 0 : memcpy(extra_ldflags, exprStr + preprocessor_start + strlen(ldflags_pragma), ldflags_characters);
776 : }
777 : }
778 : } else {
779 : // regular preprocessor directive: write it to the file
780 8 : ATTEMPT_TO_WRITE_DATA_TO_FILE(f, exprStr +
781 : preprocessor_start,
782 8 : i - preprocessor_start);
783 8 : ATTEMPT_TO_WRITE_TO_FILE(f, "\n");
784 : }
785 : // now overwrite the preprocessor directive in the
786 : // expression string with spaces
787 157 : for (j = preprocessor_start; j < i; j++) {
788 149 : exprStr[j] = ' ';
789 : }
790 : }
791 : is_preprocessor_directive = false;
792 : new_line = true;
793 9137 : } else if (exprStr[i] == ' ' || exprStr[i] == '\t') {
794 : // skip any spaces
795 2489 : continue;
796 6648 : } else if (new_line) {
797 349 : if (exprStr[i] == '#') {
798 8 : preprocessor_start = i;
799 8 : is_preprocessor_directive = true;
800 : }
801 : new_line = false;
802 : }
803 : }
804 : }
805 :
806 : // create the actual function
807 35 : if (use_cpp) {
808 : // avoid name wrangling if we are compiling C++ code
809 1 : ATTEMPT_TO_WRITE_TO_FILE(f, "\nextern \"C\"");
810 : }
811 35 : ATTEMPT_TO_WRITE_TO_FILE(f, "\nchar* ");
812 35 : ATTEMPT_TO_WRITE_TO_FILE(f, funcname);
813 35 : ATTEMPT_TO_WRITE_TO_FILE(f, "(void** __inputs, void** __outputs, "
814 35 : "malloc_function_ptr malloc, free_function_ptr free) {\n");
815 :
816 : // now we convert the input arguments from void** to the proper
817 : // input/output
818 : // of the function
819 : // first convert the input
820 84 : for (i = pci->retc + ARG_OFFSET; i < (size_t)limit_argc; i++) {
821 98 : bat_type = !isaBatType(getArgType(mb, pci, i))
822 : ? getArgType(mb, pci, i)
823 49 : : getBatType(getArgType(mb, pci, i));
824 49 : tpe = GetTypeName(bat_type);
825 49 : assert(tpe);
826 49 : if (tpe) {
827 49 : snprintf(buf, sizeof(buf),
828 : "\t%s%s %s = *((%s%s*)__inputs[%zu]);\n", struct_prefix,
829 49 : tpe, args[i], struct_prefix, tpe,
830 49 : i - (pci->retc + ARG_OFFSET));
831 49 : ATTEMPT_TO_WRITE_TO_FILE(f, buf);
832 : }
833 : }
834 35 : if (non_grouped_aggregate) {
835 : // manually add "aggr_group" for non-grouped aggregates
836 4 : bat_type = TYPE_oid;
837 4 : tpe = GetTypeName(bat_type);
838 4 : assert(tpe);
839 4 : if (tpe) {
840 4 : snprintf(buf, sizeof(buf),
841 : "\t%s%s %s = *((%s%s*)__inputs[%zu]);\n", struct_prefix,
842 : tpe, "aggr_group", struct_prefix, tpe, input_count);
843 4 : ATTEMPT_TO_WRITE_TO_FILE(f, buf);
844 : }
845 : }
846 : // output types
847 72 : for (i = 0; i < (size_t)pci->retc; i++) {
848 37 : bat_type = getBatType(getArgType(mb, pci, i));
849 37 : tpe = GetTypeName(bat_type);
850 37 : assert(tpe);
851 37 : if (tpe) {
852 37 : snprintf(buf, sizeof(buf),
853 : "\t%s%s* %s = ((%s%s*)__outputs[%zu]);\n", struct_prefix,
854 37 : tpe, output_names[i], struct_prefix, tpe, i);
855 37 : ATTEMPT_TO_WRITE_TO_FILE(f, buf);
856 : }
857 : }
858 :
859 35 : ATTEMPT_TO_WRITE_TO_FILE(f, "\n");
860 : // write the actual user defined code into the file
861 35 : ATTEMPT_TO_WRITE_TO_FILE(f, exprStr);
862 :
863 35 : ATTEMPT_TO_WRITE_TO_FILE(f, "\nreturn 0;\n}\n");
864 :
865 35 : fclose(f);
866 35 : f = NULL;
867 :
868 : // now it's time to try to compile the code
869 : // we use popen to capture any error output
870 35 : snprintf(buf, sizeof(buf), "%s %s -c -fPIC %s %s -o %s 2>&1 >/dev/null",
871 : c_compiler, extra_cflags ? extra_cflags : "", compilation_flags, fname, oname);
872 35 : GDKfree(fname);
873 35 : fname = NULL;
874 35 : compiler = popen(buf, "r");
875 35 : if (!compiler) {
876 0 : msg = createException(MAL, "cudf.eval", "Failed popen");
877 0 : goto wrapup;
878 : }
879 : // read the error stream into the error buffer until the compiler is
880 : // done
881 35 : while (fgets(error_buf, sizeof(error_buf), compiler)) {
882 0 : size_t error_size = strlen(error_buf);
883 0 : snprintf(total_error_buf + error_buffer_position,
884 : sizeof(total_error_buf) - error_buffer_position, "%s",
885 : error_buf);
886 0 : error_buffer_position += error_size;
887 0 : if (error_buffer_position >= sizeof(total_error_buf)) break;
888 : }
889 :
890 35 : compiler_return_code = pclose(compiler);
891 35 : compiler = NULL;
892 :
893 35 : if (compiler_return_code != 0) {
894 : // failure in compiling the code
895 : // report the failure to the user
896 0 : msg = createException(MAL, "cudf.eval",
897 : "Failed to compile C UDF:\n%s",
898 : total_error_buf);
899 0 : goto wrapup;
900 : }
901 :
902 35 : error_buffer_position = 0;
903 35 : error_buf[0] = '\0';
904 :
905 35 : snprintf(buf, sizeof(buf), "%s %s %s -shared -o %s 2>&1 >/dev/null", c_compiler,
906 : extra_ldflags ? extra_ldflags : "", oname, libname);
907 35 : GDKfree(oname);
908 35 : oname = NULL;
909 35 : compiler = popen(buf, "r");
910 35 : if (!compiler) {
911 0 : msg = createException(MAL, "cudf.eval", "Failed popen");
912 0 : goto wrapup;
913 : }
914 35 : while (fgets(error_buf, sizeof(error_buf), compiler)) {
915 0 : size_t error_size = strlen(error_buf);
916 0 : snprintf(total_error_buf + error_buffer_position,
917 : sizeof(total_error_buf) - error_buffer_position, "%s",
918 : error_buf);
919 0 : error_buffer_position += error_size;
920 0 : if (error_buffer_position >= sizeof(total_error_buf)) break;
921 : }
922 :
923 35 : compiler_return_code = pclose(compiler);
924 35 : compiler = NULL;
925 :
926 35 : if (compiler_return_code != 0) {
927 : // failure in compiler
928 0 : msg = createException(MAL, "cudf.eval", "Failed to link C UDF.\n%s",
929 : total_error_buf);
930 0 : goto wrapup;
931 : }
932 :
933 35 : handle = dlopen(libname, RTLD_LAZY);
934 35 : GDKfree(libname);
935 35 : libname = NULL;
936 35 : if (!handle) {
937 0 : msg = createException(MAL, "cudf.eval",
938 : "Failed to open shared library: %s.",
939 : dlerror());
940 0 : goto wrapup;
941 : }
942 35 : func = (jitted_function)dlsym(handle, funcname);
943 35 : if (!func) {
944 0 : msg = createException(MAL, "cudf.eval",
945 : "Failed to load function from library: %s.",
946 : dlerror());
947 0 : goto wrapup;
948 : }
949 : // now that we have compiled this function
950 : // store it in our function cache
951 : {
952 35 : cached_functions *new_entry = GDKmalloc(sizeof(cached_functions));
953 35 : if (!new_entry) {
954 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
955 0 : goto wrapup;
956 : }
957 35 : new_entry->function = func;
958 35 : new_entry->expression_hash = expression_hash;
959 35 : new_entry->parameters = function_parameters;
960 35 : new_entry->dll_handle = handle;
961 35 : function_parameters = NULL;
962 35 : handle = NULL;
963 35 : MT_lock_set(&cache_lock);
964 35 : new_entry->next = function_cache[funcname_hash];
965 35 : function_cache[funcname_hash] = new_entry;
966 35 : MT_lock_unset(&cache_lock);
967 : }
968 : }
969 47 : if (input_count > 0) {
970 : // add "aggr_group" for non-grouped aggregates
971 46 : extra_inputs = non_grouped_aggregate ? 1 : 0;
972 46 : input_bats = GDKzalloc(sizeof(BAT *) * (input_count + extra_inputs));
973 46 : inputs = GDKzalloc(sizeof(void *) * (input_count + extra_inputs));
974 46 : if (!inputs || !input_bats) {
975 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
976 0 : goto wrapup;
977 : }
978 : }
979 47 : if (output_count > 0) {
980 47 : outputs = GDKzalloc(sizeof(void *) * output_count);
981 47 : if (!outputs) {
982 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
983 0 : goto wrapup;
984 : }
985 : }
986 : // create the inputs
987 47 : argnode = sqlfun ? sqlfun->ops->h : NULL;
988 100 : for (i = pci->retc + ARG_OFFSET; i < (size_t)limit_argc; i++) {
989 56 : index = i - (pci->retc + ARG_OFFSET);
990 56 : bat_type = getArgType(mb, pci, i);
991 56 : if (!isaBatType(bat_type)) {
992 13 : void* input = NULL;
993 13 : if (bat_type == TYPE_str) {
994 2 : input = *getArgReference_str(stk, pci, i);
995 11 : } else if (bat_type == TYPE_blob) {
996 1 : input = *(blob**)getArgReference(stk, pci, i);
997 : } else {
998 10 : input = getArgReference(stk, pci, i);
999 : }
1000 : // scalar input
1001 : // create a temporary BAT
1002 13 : input_bats[index] = COLnew(0, bat_type, 1, TRANSIENT);
1003 13 : if (!input_bats[index]) {
1004 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
1005 0 : goto wrapup;
1006 : }
1007 13 : if (BUNappend(input_bats[index], input,
1008 : false) != GDK_SUCCEED) {
1009 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
1010 0 : goto wrapup;
1011 : }
1012 : } else {
1013 : // deal with BAT input
1014 43 : bat_type = getBatType(getArgType(mb, pci, i));
1015 43 : if (!(input_bats[index] =
1016 43 : BATdescriptor(*getArgReference_bat(stk, pci, i)))) {
1017 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
1018 0 : goto wrapup;
1019 : }
1020 43 : if (BATcount(input_bats[index]) == 0) {
1021 : /* empty input, generate trivial return */
1022 : /* I expect all inputs to have the same size,
1023 : so this should be safe */
1024 3 : msg = empty_return(mb, stk, pci, output_count,
1025 : input_bats[index]->hseqbase);
1026 3 : goto wrapup;
1027 : }
1028 : }
1029 :
1030 53 : if (bat_type == TYPE_bit) {
1031 0 : GENERATE_BAT_INPUT(input_bats[index], bit);
1032 : } else if (bat_type == TYPE_bte) {
1033 0 : GENERATE_BAT_INPUT(input_bats[index], bte);
1034 : } else if (bat_type == TYPE_sht) {
1035 0 : GENERATE_BAT_INPUT(input_bats[index], sht);
1036 : } else if (bat_type == TYPE_int) {
1037 27 : GENERATE_BAT_INPUT(input_bats[index], int);
1038 : } else if (bat_type == TYPE_oid) {
1039 4 : GENERATE_BAT_INPUT(input_bats[index], oid);
1040 : // Hack for groups BAT, the count should reflect on the number of groups and not the number
1041 : // of rows, so use extents BAT
1042 4 : if (i == (size_t)seengrp) {
1043 4 : struct cudf_data_struct_oid *t = inputs[index];
1044 4 : BAT *ex = BBPquickdesc(*getArgReference_bat(stk, pci, i + 1));
1045 4 : if (!ex) {
1046 0 : msg = createException(MAL, "cudf.eval", RUNTIME_OBJECT_MISSING);
1047 0 : goto wrapup;
1048 : }
1049 4 : t->count = BATcount(ex);
1050 : }
1051 : } else if (bat_type == TYPE_lng) {
1052 2 : GENERATE_BAT_INPUT(input_bats[index], lng);
1053 : } else if (bat_type == TYPE_flt) {
1054 1 : GENERATE_BAT_INPUT(input_bats[index], flt);
1055 : } else if (bat_type == TYPE_dbl) {
1056 4 : GENERATE_BAT_INPUT(input_bats[index], dbl);
1057 : } else if (bat_type == TYPE_str) {
1058 6 : BATiter li;
1059 6 : BUN p = 0, q = 0;
1060 6 : bool can_mprotect_varheap = false;
1061 6 : str mprotect_retval;
1062 6 : GENERATE_BAT_INPUT_BASE(str);
1063 6 : bat_data->count = BATcount(input_bats[index]);
1064 6 : bat_data->data = bat_data->count == 0 ? NULL : GDKmalloc(sizeof(char *) * bat_data->count);
1065 6 : bat_data->null_value = NULL;
1066 6 : if (bat_data->count > 0 && !bat_data->data) {
1067 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
1068 0 : goto wrapup;
1069 : }
1070 6 : bat_data->alloced = true;
1071 6 : j = 0;
1072 :
1073 : // check if we can mprotect the varheap
1074 : // if we can't mprotect, copy the strings instead
1075 6 : assert(input_bats[index]->tvheap);
1076 6 : can_mprotect_varheap = can_mprotect_region(input_bats[index]->tvheap->base);
1077 6 : bat_data->valloced = !can_mprotect_varheap;
1078 :
1079 6 : li = bat_iterator(input_bats[index]);
1080 302 : BATloop(input_bats[index], p, q)
1081 : {
1082 296 : char *t = (char *)BUNtvar(li, p);
1083 296 : if (strNil(t)) {
1084 2 : bat_data->data[j] = NULL;
1085 : } else {
1086 294 : if (can_mprotect_varheap) {
1087 0 : bat_data->data[j] = t;
1088 : } else {
1089 294 : bat_data->data[j] = GDKmalloc(strlen(t) + 1);
1090 294 : if (!bat_data->data[j]) {
1091 0 : bat_iterator_end(&li);
1092 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
1093 0 : goto wrapup;
1094 : }
1095 294 : strcpy(bat_data->data[j], t);
1096 : }
1097 : }
1098 296 : j++;
1099 : }
1100 6 : bat_iterator_end(&li);
1101 6 : if (can_mprotect_varheap) {
1102 : // mprotect the varheap of the BAT to prevent modification of input strings
1103 0 : mprotect_retval =
1104 0 : mprotect_region(input_bats[index]->tvheap->base,
1105 0 : input_bats[index]->tvheap->size, ®ions);
1106 0 : if (mprotect_retval) {
1107 0 : msg = createException(MAL, "cudf.eval",
1108 : "Failed to mprotect region: %s",
1109 : mprotect_retval);
1110 0 : goto wrapup;
1111 : }
1112 : }
1113 : } else if (bat_type == TYPE_date) {
1114 2 : date *baseptr;
1115 2 : GENERATE_BAT_INPUT_BASE(date);
1116 2 : bat_data->count = BATcount(input_bats[index]);
1117 2 : bat_data->data = bat_data->count == 0 ? NULL :
1118 2 : GDKmalloc(sizeof(bat_data->null_value) * bat_data->count);
1119 2 : if (bat_data->count > 0 && !bat_data->data) {
1120 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
1121 0 : goto wrapup;
1122 : }
1123 2 : bat_data->alloced = true;
1124 :
1125 2 : baseptr = (date *)Tloc(input_bats[index], 0);
1126 7 : for (j = 0; j < bat_data->count; j++) {
1127 5 : data_from_date(baseptr[j], bat_data->data + j);
1128 : }
1129 2 : data_from_date(date_nil, &bat_data->null_value);
1130 : } else if (bat_type == TYPE_daytime) {
1131 2 : daytime *baseptr;
1132 2 : GENERATE_BAT_INPUT_BASE(time);
1133 2 : bat_data->count = BATcount(input_bats[index]);
1134 2 : bat_data->data = bat_data->count == 0 ? NULL :
1135 2 : GDKmalloc(sizeof(bat_data->null_value) * bat_data->count);
1136 2 : if (bat_data->count > 0 && !bat_data->data) {
1137 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
1138 0 : goto wrapup;
1139 : }
1140 2 : bat_data->alloced = true;
1141 :
1142 2 : baseptr = (daytime *)Tloc(input_bats[index], 0);
1143 6 : for (j = 0; j < bat_data->count; j++) {
1144 4 : data_from_time(baseptr[j], bat_data->data + j);
1145 : }
1146 2 : data_from_time(daytime_nil, &bat_data->null_value);
1147 : } else if (bat_type == TYPE_timestamp) {
1148 2 : timestamp *baseptr;
1149 2 : GENERATE_BAT_INPUT_BASE(timestamp);
1150 2 : bat_data->count = BATcount(input_bats[index]);
1151 2 : bat_data->data = bat_data->count == 0 ? NULL :
1152 2 : GDKmalloc(sizeof(bat_data->null_value) * bat_data->count);
1153 2 : if (bat_data->count > 0 && !bat_data->data) {
1154 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
1155 0 : goto wrapup;
1156 : }
1157 2 : bat_data->alloced = true;
1158 :
1159 2 : baseptr = (timestamp *)Tloc(input_bats[index], 0);
1160 6 : for (j = 0; j < bat_data->count; j++) {
1161 4 : data_from_timestamp(baseptr[j], bat_data->data + j);
1162 : }
1163 2 : data_from_timestamp(timestamp_nil, &bat_data->null_value);
1164 : } else if (bat_type == TYPE_blob) {
1165 2 : BATiter li;
1166 2 : BUN p = 0, q = 0;
1167 2 : str mprotect_retval;
1168 2 : bool can_mprotect_varheap = false;
1169 2 : GENERATE_BAT_INPUT_BASE(blob);
1170 2 : bat_data->count = BATcount(input_bats[index]);
1171 2 : bat_data->data = bat_data->count == 0 ? NULL :
1172 2 : GDKmalloc(sizeof(cudf_data_blob) * bat_data->count);
1173 2 : if (bat_data->count > 0 && !bat_data->data) {
1174 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
1175 0 : goto wrapup;
1176 : }
1177 2 : bat_data->alloced = true;
1178 2 : j = 0;
1179 :
1180 : // check if we can mprotect the varheap
1181 : // if we can't mprotect, copy the strings instead
1182 2 : assert(input_bats[index]->tvheap);
1183 2 : can_mprotect_varheap = can_mprotect_region(input_bats[index]->tvheap->base);
1184 2 : bat_data->valloced = !can_mprotect_varheap;
1185 :
1186 2 : li = bat_iterator(input_bats[index]);
1187 6 : BATloop(input_bats[index], p, q)
1188 : {
1189 4 : blob *t = (blob *)BUNtvar(li, p);
1190 4 : if (t->nitems == ~(size_t)0) {
1191 1 : bat_data->data[j].size = ~(size_t) 0;
1192 1 : bat_data->data[j].data = NULL;
1193 : } else {
1194 3 : bat_data->data[j].size = t->nitems;
1195 3 : if (can_mprotect_varheap) {
1196 0 : bat_data->data[j].data = &t->data[0];
1197 3 : } else if (t->nitems > 0) {
1198 2 : bat_data->data[j].data = GDKmalloc(t->nitems);
1199 2 : if (!bat_data->data[j].data) {
1200 0 : bat_iterator_end(&li);
1201 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
1202 0 : goto wrapup;
1203 : }
1204 2 : memcpy(bat_data->data[j].data, &t->data[0], t->nitems);
1205 : } else {
1206 1 : bat_data->data[j].data = NULL;
1207 : }
1208 : }
1209 4 : j++;
1210 : }
1211 2 : bat_iterator_end(&li);
1212 2 : bat_data->null_value.size = ~(size_t) 0;
1213 2 : bat_data->null_value.data = NULL;
1214 2 : if (can_mprotect_varheap) {
1215 : // for blob columns, mprotect the varheap of the BAT
1216 0 : mprotect_retval =
1217 0 : mprotect_region(input_bats[index]->tvheap->base,
1218 0 : input_bats[index]->tvheap->size, ®ions);
1219 0 : if (mprotect_retval) {
1220 0 : msg = createException(MAL, "cudf.eval",
1221 : "Failed to mprotect region: %s",
1222 : mprotect_retval);
1223 0 : goto wrapup;
1224 : }
1225 : }
1226 : } else {
1227 : // unsupported type: convert to string
1228 1 : BATiter li;
1229 1 : BUN p = 0, q = 0;
1230 1 : GENERATE_BAT_INPUT_BASE(str);
1231 1 : bat_data->count = BATcount(input_bats[index]);
1232 1 : bat_data->null_value = NULL;
1233 1 : bat_data->data = bat_data->count == 0 ? NULL :
1234 1 : GDKzalloc(sizeof(char *) * bat_data->count);
1235 1 : if (bat_data->count > 0 && !bat_data->data) {
1236 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
1237 0 : goto wrapup;
1238 : }
1239 1 : bat_data->alloced = true;
1240 1 : j = 0;
1241 :
1242 1 : li = bat_iterator(input_bats[index]);
1243 3 : BATloop(input_bats[index], p, q)
1244 : {
1245 2 : void *t = BUNtail(li, p);
1246 4 : if (BATatoms[bat_type].atomNull &&
1247 2 : BATatoms[bat_type].atomCmp(
1248 : t, BATatoms[bat_type].atomNull) == 0) {
1249 1 : bat_data->data[j] = NULL;
1250 : } else {
1251 1 : char *result = NULL;
1252 1 : size_t length = 0;
1253 1 : if (BATatoms[bat_type].atomToStr(&result, &length, t, false) ==
1254 : 0) {
1255 0 : bat_iterator_end(&li);
1256 0 : msg = createException(
1257 : MAL, "cudf.eval",
1258 : "Failed to convert element to string");
1259 0 : goto wrapup;
1260 : }
1261 1 : bat_data->data[j] = result;
1262 : }
1263 2 : j++;
1264 : }
1265 1 : bat_iterator_end(&li);
1266 1 : bat_data->valloced = true;
1267 : }
1268 53 : input_size = BATcount(input_bats[index]) > input_size
1269 : ? BATcount(input_bats[index])
1270 : : input_size;
1271 53 : argnode = argnode ? argnode->next : NULL;
1272 : }
1273 :
1274 44 : index = input_count;
1275 44 : if (non_grouped_aggregate) {
1276 5 : GENERATE_BAT_INPUT_BASE(oid);
1277 5 : bat_data->count = input_size;
1278 5 : bat_data->null_value = oid_nil;
1279 10 : bat_data->data =
1280 5 : GDKzalloc(bat_data->count * sizeof(bat_data->null_value));
1281 5 : bat_data->alloced = true;
1282 5 : if (!bat_data->data) {
1283 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
1284 0 : goto wrapup;
1285 : }
1286 : }
1287 :
1288 44 : argnode = sqlfun ? sqlfun->res->h : NULL;
1289 : // output types
1290 90 : for (i = 0; i < output_count; i++) {
1291 46 : index = i;
1292 46 : bat_type = getBatType(getArgType(mb, pci, i));
1293 46 : if (bat_type == TYPE_bit) {
1294 0 : GENERATE_BAT_OUTPUT(bit);
1295 : } else if (bat_type == TYPE_bte) {
1296 0 : GENERATE_BAT_OUTPUT(bte);
1297 : } else if (bat_type == TYPE_sht) {
1298 0 : GENERATE_BAT_OUTPUT(sht);
1299 : } else if (bat_type == TYPE_int) {
1300 16 : GENERATE_BAT_OUTPUT(int);
1301 : } else if (bat_type == TYPE_oid) {
1302 0 : GENERATE_BAT_OUTPUT(oid);
1303 : } else if (bat_type == TYPE_lng) {
1304 9 : GENERATE_BAT_OUTPUT(lng);
1305 : } else if (bat_type == TYPE_flt) {
1306 0 : GENERATE_BAT_OUTPUT(flt);
1307 : } else if (bat_type == TYPE_dbl) {
1308 5 : GENERATE_BAT_OUTPUT(dbl);
1309 : } else if (bat_type == TYPE_str) {
1310 7 : GENERATE_BAT_OUTPUT_BASE(str);
1311 7 : bat_data->null_value = NULL;
1312 : } else if (bat_type == TYPE_date) {
1313 2 : GENERATE_BAT_OUTPUT_BASE(date);
1314 2 : data_from_date(date_nil, &bat_data->null_value);
1315 : } else if (bat_type == TYPE_daytime) {
1316 2 : GENERATE_BAT_OUTPUT_BASE(time);
1317 2 : data_from_time(daytime_nil, &bat_data->null_value);
1318 : } else if (bat_type == TYPE_timestamp) {
1319 2 : GENERATE_BAT_OUTPUT_BASE(timestamp);
1320 2 : data_from_timestamp(timestamp_nil, &bat_data->null_value);
1321 : } else if (bat_type == TYPE_blob) {
1322 2 : GENERATE_BAT_OUTPUT_BASE(blob);
1323 2 : bat_data->null_value.size = ~(size_t) 0;
1324 2 : bat_data->null_value.data = NULL;
1325 : } else {
1326 : // unsupported type, convert from string output
1327 1 : GENERATE_BAT_OUTPUT_BASE(str);
1328 1 : bat_data->null_value = NULL;
1329 : }
1330 46 : argnode = argnode ? argnode->next : NULL;
1331 : }
1332 :
1333 : // set up a longjmp point
1334 : // this longjmp point is used for some error handling in the C function
1335 : // such as failed mallocs
1336 44 : if (option_enable_longjmp) {
1337 0 : struct capi_tls_s *tls = MT_tls_get(capi_tls_key);
1338 0 : ret = setjmp(tls->jb);
1339 0 : if (ret < 0) {
1340 : // error value
1341 0 : msg = createException(MAL, "cudf.eval", "Failed setjmp: %s",
1342 0 : GDKstrerror(errno, (char[128]){0}, 128));
1343 0 : errno = 0;
1344 0 : goto wrapup;
1345 0 : } else if (ret > 0) {
1346 0 : if (ret == 1) {
1347 0 : msg = createException(MAL, "cudf.eval", "Attempting to write to "
1348 : "the input or triggered a "
1349 : "segfault/bus error");
1350 0 : } else if (ret == 2) {
1351 0 : msg = createException(MAL, "cudf.eval",
1352 : "Malloc failure in internal function!");
1353 : } else {
1354 : // we jumped here
1355 0 : msg = createException(MAL, "cudf.eval", "We longjumped here "
1356 : "because of an error, but "
1357 : "we don't know which!");
1358 : }
1359 0 : goto wrapup;
1360 : }
1361 : }
1362 :
1363 : // set up the signal handler for catching segfaults
1364 44 : if (option_enable_mprotect) {
1365 0 : sa = (struct sigaction) {
1366 : .sa_flags = SA_SIGINFO,
1367 : .sa_sigaction = handler,
1368 : };
1369 0 : (void) sigfillset(&sa.sa_mask);
1370 0 : if (sigaction(SIGSEGV, &sa, &oldsa) == -1 ||
1371 0 : sigaction(SIGBUS, &sa, &oldsb) == -1) {
1372 0 : msg = createException(MAL, "cudf.eval",
1373 : "Failed to set signal handler: %s",
1374 0 : GDKstrerror(errno, (char[128]){0}, 128));
1375 0 : errno = 0;
1376 0 : goto wrapup;
1377 : }
1378 : // actually mprotect the regions now that the signal handlers are set
1379 0 : region_iter = regions;
1380 0 : while (region_iter) {
1381 0 : if (mprotect(region_iter->addr, region_iter->len, PROT_READ) < 0) {
1382 0 : goto wrapup;
1383 : }
1384 0 : region_iter = region_iter->next;
1385 : }
1386 : }
1387 : // call the actual jitted function
1388 44 : msg = func(inputs, outputs, wrapped_GDK_malloc, wrapped_GDK_free);
1389 :
1390 :
1391 44 : if (option_enable_mprotect) {
1392 : // clear any mprotected regions
1393 0 : while (regions) {
1394 0 : mprotected_region *next = regions->next;
1395 0 : clear_mprotect(regions->addr, regions->len);
1396 0 : GDKfree(regions);
1397 0 : regions = next;
1398 : }
1399 : // clear the signal handlers
1400 0 : if (sigaction(SIGSEGV, &oldsa, NULL) == -1 ||
1401 0 : sigaction(SIGBUS, &oldsb, NULL) == -1) {
1402 0 : msg = createException(MAL, "cudf.eval",
1403 : "Failed to unset signal handler: %s",
1404 0 : GDKstrerror(errno, (char[128]){0}, 128));
1405 0 : errno = 0;
1406 0 : goto wrapup;
1407 : }
1408 0 : sa = (struct sigaction) {.sa_flags = 0};
1409 : }
1410 :
1411 44 : if (msg) {
1412 : // failure in function
1413 1 : msg = createException(MAL, "cudf.eval", "%s", msg);
1414 1 : goto wrapup;
1415 : }
1416 :
1417 : // create the output bats from the returned results
1418 86 : for (i = 0; i < (size_t)pci->retc; i++) {
1419 45 : size_t count;
1420 45 : void *data;
1421 45 : BAT *b;
1422 45 : bat_type = getBatType(getArgType(mb, pci, i));
1423 :
1424 45 : if (!outputs[i]) {
1425 0 : msg = createException(MAL, "cudf.eval", "No data returned.");
1426 0 : goto wrapup;
1427 : }
1428 45 : count = GetTypeCount(bat_type, outputs[i]);
1429 45 : data = GetTypeData(bat_type, outputs[i]);
1430 45 : if (!data) {
1431 1 : msg = createException(MAL, "cudf.eval", "No data returned.");
1432 1 : goto wrapup;
1433 : }
1434 44 : if (initial_output_count < 0) {
1435 42 : initial_output_count = count;
1436 2 : } else if ((size_t)initial_output_count != count) {
1437 1 : msg = createException(MAL, "cudf.eval",
1438 : "Data has different cardinalities.");
1439 1 : goto wrapup;
1440 : }
1441 43 : if (bat_type == TYPE_bit || bat_type == TYPE_bte ||
1442 43 : bat_type == TYPE_sht || bat_type == TYPE_int ||
1443 43 : bat_type == TYPE_oid || bat_type == TYPE_lng ||
1444 21 : bat_type == TYPE_flt || bat_type == TYPE_dbl) {
1445 27 : b = GetTypeBat(bat_type, outputs[i]);
1446 27 : if (!b) {
1447 0 : msg = createException(MAL, "cudf.eval", "Output column was not properly initialized.");
1448 0 : goto wrapup;
1449 : }
1450 : } else {
1451 16 : assert(GetTypeBat(bat_type, outputs[i]) == NULL);
1452 16 : b = COLnew(0, bat_type, count, TRANSIENT);
1453 16 : if (!b) {
1454 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
1455 0 : goto wrapup;
1456 : }
1457 16 : if (bat_type == TYPE_date) {
1458 2 : date *baseptr = (date *)Tloc(b, 0);
1459 2 : cudf_data_date *source_base = (cudf_data_date *)data;
1460 7 : for (j = 0; j < count; j++) {
1461 5 : baseptr[j] = date_from_data(source_base + j);
1462 : }
1463 2 : BATsetcount(b, count);
1464 2 : GDKfree(data);
1465 : } else if (bat_type == TYPE_daytime) {
1466 2 : daytime *baseptr = (daytime *)Tloc(b, 0);
1467 2 : cudf_data_time *source_base = (cudf_data_time *)data;
1468 6 : for (j = 0; j < count; j++) {
1469 4 : baseptr[j] = time_from_data(source_base + j);
1470 : }
1471 2 : BATsetcount(b, count);
1472 2 : GDKfree(data);
1473 : } else if (bat_type == TYPE_timestamp) {
1474 2 : timestamp *baseptr = (timestamp *)Tloc(b, 0);
1475 2 : cudf_data_timestamp *source_base = (cudf_data_timestamp *)data;
1476 6 : for (j = 0; j < count; j++) {
1477 4 : baseptr[j] = timestamp_from_data(source_base + j);
1478 : }
1479 2 : BATsetcount(b, count);
1480 2 : GDKfree(data);
1481 : } else if (bat_type == TYPE_str) {
1482 : char **source_base = (char **)data;
1483 304 : for (j = 0; j < count; j++) {
1484 297 : const char *ptr = source_base[j];
1485 297 : if (!ptr) {
1486 2 : ptr = str_nil;
1487 : }
1488 297 : if (BUNappend(b, ptr, false) != GDK_SUCCEED) {
1489 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
1490 0 : goto wrapup;
1491 : }
1492 : }
1493 7 : GDKfree(data);
1494 : } else if (bat_type == TYPE_blob) {
1495 : cudf_data_blob *source_base = (cudf_data_blob *)data;
1496 : blob *current_blob = NULL;
1497 : size_t current_blob_maxsize = 0;
1498 6 : for (j = 0; j < count; j++) {
1499 4 : const cudf_data_blob blob = source_base[j];
1500 :
1501 4 : if (blob.size == ~(size_t) 0) {
1502 1 : current_blob->nitems = ~(size_t)0;
1503 : } else {
1504 3 : if (!current_blob || current_blob_maxsize < blob.size) {
1505 0 : if (current_blob) {
1506 0 : GDKfree(current_blob);
1507 : }
1508 2 : current_blob_maxsize = blob.size;
1509 2 : current_blob = GDKmalloc(sizeof(size_t) + blob.size);
1510 2 : if (!current_blob) {
1511 0 : msg =
1512 0 : createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
1513 0 : goto wrapup;
1514 : }
1515 : }
1516 :
1517 3 : current_blob->nitems = blob.size;
1518 3 : if (blob.size > 0)
1519 2 : memcpy(¤t_blob->data[0], blob.data, blob.size);
1520 : }
1521 :
1522 4 : if (BUNappend(b, current_blob, false) != GDK_SUCCEED) {
1523 0 : if (current_blob) {
1524 0 : GDKfree(current_blob);
1525 : }
1526 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
1527 0 : goto wrapup;
1528 : }
1529 : }
1530 2 : if (current_blob) {
1531 2 : GDKfree(current_blob);
1532 : }
1533 2 : GDKfree(data);
1534 : } else {
1535 1 : char **source_base = (char **)data;
1536 1 : size_t len = 0;
1537 1 : void *element = NULL;
1538 3 : for (j = 0; j < count; j++) {
1539 2 : const char *ptr = source_base[j];
1540 2 : const void *appended_element;
1541 2 : if (strNil(ptr)) {
1542 1 : appended_element = (void *)BATatoms[bat_type].atomNull;
1543 : } else {
1544 1 : if (BATatoms[bat_type].atomFromStr(ptr, &len, &element, false) ==
1545 : 0) {
1546 0 : msg = createException(MAL, "cudf.eval",
1547 : "Failed to convert output "
1548 : "element from string: %s",
1549 : ptr);
1550 0 : goto wrapup;
1551 : }
1552 1 : appended_element = element;
1553 : }
1554 2 : if (BUNappend(b, appended_element, false) != GDK_SUCCEED) {
1555 0 : if (element) {
1556 0 : GDKfree(element);
1557 : }
1558 0 : msg = createException(MAL, "cudf.eval", MAL_MALLOC_FAIL);
1559 0 : goto wrapup;
1560 : }
1561 : }
1562 1 : if (element) {
1563 1 : GDKfree(element);
1564 : }
1565 1 : GDKfree(data);
1566 : }
1567 : }
1568 43 : b->tnil = false;
1569 43 : b->tnonil = false;
1570 43 : b->tkey = false;
1571 43 : b->tsorted = false;
1572 43 : b->trevsorted = false;
1573 :
1574 : // free the output value right now to prevent the internal data from
1575 : // being freed later
1576 : // as the internal data is now part of the bat we just created
1577 43 : GDKfree(outputs[i]);
1578 43 : outputs[i] = NULL;
1579 :
1580 : // return the BAT from the function
1581 43 : if (isaBatType(getArgType(mb, pci, i))) {
1582 28 : *getArgReference_bat(stk, pci, i) = b->batCacheid;
1583 28 : BBPkeepref(b);
1584 : } else {
1585 15 : BATiter li = bat_iterator(b);
1586 15 : if (VALinit(&stk->stk[pci->argv[i]], bat_type,
1587 15 : BUNtail(li, 0)) == NULL) {
1588 0 : msg = createException(MAL, "cudf.eval", SQLSTATE(HY013) MAL_MALLOC_FAIL);
1589 : }
1590 15 : bat_iterator_end(&li);
1591 15 : BBPunfix(b->batCacheid);
1592 : }
1593 : }
1594 :
1595 41 : wrapup:
1596 : // cleanup
1597 : // remove the signal handler, if any was set
1598 47 : GDKfree(fname);
1599 47 : GDKfree(oname);
1600 47 : GDKfree(libname);
1601 47 : MT_tls_set(capi_tls_key, NULL);
1602 47 : if (option_enable_mprotect) {
1603 0 : if (sa.sa_sigaction) {
1604 0 : (void) sigaction(SIGSEGV, &oldsa, NULL);
1605 0 : (void) sigaction(SIGBUS, &oldsb, NULL);
1606 :
1607 0 : sa = (struct sigaction) {.sa_flags = 0,};
1608 : }
1609 : // clear any mprotected regions
1610 0 : while (regions) {
1611 0 : mprotected_region *next = regions->next;
1612 0 : clear_mprotect(regions->addr, regions->len);
1613 0 : GDKfree(regions);
1614 0 : regions = next;
1615 : }
1616 : }
1617 341 : while (tls.ar != NULL) {
1618 294 : allocated_region *next = tls.ar->next;
1619 294 : GDKfree(tls.ar);
1620 294 : tls.ar = next;
1621 : }
1622 47 : if (option_enable_mprotect) {
1623 : // block segfaults and bus errors again after we exit
1624 0 : (void)pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
1625 : }
1626 : // argument names (input)
1627 47 : if (args) {
1628 299 : for (i = 0; i < (size_t)limit_argc; i++) {
1629 252 : if (args[i]) {
1630 62 : GDKfree(args[i]);
1631 : }
1632 : }
1633 47 : GDKfree(args);
1634 : }
1635 : // output names
1636 47 : if (output_names) {
1637 96 : for (i = 0; i < (size_t)pci->retc; i++) {
1638 49 : if (output_names[i]) {
1639 49 : GDKfree(output_names[i]);
1640 : }
1641 : }
1642 47 : GDKfree(output_names);
1643 : }
1644 47 : if (input_bats) {
1645 114 : for(i = 0; i < input_count + extra_inputs; i++) {
1646 124 : BBPreclaim(input_bats[i]);
1647 : }
1648 46 : GDKfree(input_bats);
1649 : }
1650 : // input data
1651 47 : if (inputs) {
1652 114 : for (i = 0; i < input_count + extra_inputs; i++) {
1653 68 : if (inputs[i]) {
1654 58 : int arg = i + pci->retc + ARG_OFFSET;
1655 58 : bat_type = getArgType(mb, pci, arg);
1656 58 : if (isaBatType(bat_type)) {
1657 40 : bat_type = getBatType(bat_type);
1658 : }
1659 58 : if (i == input_count) /* non grouped aggr case */
1660 : bat_type = TYPE_oid;
1661 53 : if (bat_type < 0)
1662 0 : continue;
1663 58 : if (isAlloced(bat_type, inputs[i])) {
1664 58 : char **data = (char **)GetTypeData(bat_type, inputs[i]);
1665 116 : if (isValloced(bat_type, inputs[i])) {
1666 9 : size_t count = GetTypeCount(bat_type, inputs[i]);
1667 9 : if (bat_type == TYPE_blob) {
1668 : cudf_data_blob *bd = (cudf_data_blob*)data;
1669 6 : for (j = 0; j < count; j++)
1670 4 : if (bd[j].data)
1671 2 : GDKfree(bd[j].data);
1672 : } else {
1673 305 : for (j = 0; j < count; j++)
1674 298 : if (data[j])
1675 295 : GDKfree(data[j]);
1676 : }
1677 : }
1678 58 : if (data)
1679 58 : GDKfree(data);
1680 : }
1681 58 : GDKfree(inputs[i]);
1682 : }
1683 : }
1684 46 : GDKfree(inputs);
1685 : }
1686 : // output data
1687 47 : if (outputs) {
1688 96 : for (i = 0; i < (size_t)output_count; i++) {
1689 98 : bat_type = isaBatType(getArgType(mb, pci, i))
1690 : ? getBatType(getArgType(mb, pci, i))
1691 49 : : getArgType(mb, pci, i);
1692 49 : if (outputs[i]) {
1693 3 : void* b = GetTypeBat(bat_type, outputs[i]);
1694 3 : if (b) {
1695 1 : BBPunfix(((BAT*)b)->batCacheid);
1696 : } else {
1697 2 : void *data = GetTypeData(bat_type, outputs[i]);
1698 2 : if (data) {
1699 0 : GDKfree(data);
1700 : }
1701 : }
1702 3 : GDKfree(outputs[i]);
1703 : }
1704 : }
1705 47 : GDKfree(outputs);
1706 : }
1707 47 : if (function_parameters) {
1708 12 : GDKfree(function_parameters);
1709 : }
1710 : // close the file handle
1711 47 : if (f) {
1712 0 : fclose(f);
1713 : }
1714 : // close the dll
1715 47 : if (handle) {
1716 0 : dlclose(handle);
1717 : }
1718 : // close the compiler stream
1719 47 : if (compiler) {
1720 0 : pclose(compiler);
1721 : }
1722 47 : if (extra_cflags) {
1723 0 : GDKfree(extra_cflags);
1724 : }
1725 47 : if (extra_ldflags) {
1726 0 : GDKfree(extra_ldflags);
1727 : }
1728 47 : return msg;
1729 : }
1730 :
1731 90 : static const char *GetTypeName(int type)
1732 : {
1733 90 : const char *tpe = NULL;
1734 90 : if (type == TYPE_bit || type == TYPE_bte) {
1735 : tpe = "bte";
1736 : } else if (type == TYPE_sht) {
1737 : tpe = "sht";
1738 : } else if (type == TYPE_int) {
1739 : tpe = "int";
1740 : } else if (type == TYPE_oid) {
1741 : tpe = "oid";
1742 : } else if (type == TYPE_lng) {
1743 : tpe = "lng";
1744 : } else if (type == TYPE_flt) {
1745 : tpe = "flt";
1746 : } else if (type == TYPE_dbl) {
1747 : tpe = "dbl";
1748 : } else if (type == TYPE_str) {
1749 : tpe = "str";
1750 : } else if (type == TYPE_date) {
1751 : tpe = "date";
1752 : } else if (type == TYPE_daytime) {
1753 : tpe = "time";
1754 : } else if (type == TYPE_timestamp) {
1755 : tpe = "timestamp";
1756 : } else if (type == TYPE_blob) {
1757 : tpe = "blob";
1758 : } else {
1759 : // unsupported type: string
1760 90 : tpe = "str";
1761 : }
1762 90 : return tpe;
1763 : }
1764 :
1765 : static bool
1766 58 : isAlloced(int type, void *struct_ptr)
1767 : {
1768 58 : bool alloced = false;
1769 :
1770 58 : if (type == TYPE_bit || type == TYPE_bte) {
1771 0 : alloced = ((struct cudf_data_struct_bte *)struct_ptr)->alloced;
1772 : } else if (type == TYPE_sht) {
1773 0 : alloced = ((struct cudf_data_struct_sht *)struct_ptr)->alloced;
1774 : } else if (type == TYPE_int) {
1775 27 : alloced = ((struct cudf_data_struct_int *)struct_ptr)->alloced;
1776 : } else if (type == TYPE_oid) {
1777 9 : alloced = ((struct cudf_data_struct_oid *)struct_ptr)->alloced;
1778 : } else if (type == TYPE_lng) {
1779 2 : alloced = ((struct cudf_data_struct_lng *)struct_ptr)->alloced;
1780 : } else if (type == TYPE_flt) {
1781 1 : alloced = ((struct cudf_data_struct_flt *)struct_ptr)->alloced;
1782 : } else if (type == TYPE_dbl) {
1783 4 : alloced = ((struct cudf_data_struct_dbl *)struct_ptr)->alloced;
1784 : } else if (type == TYPE_str) {
1785 6 : alloced = ((struct cudf_data_struct_str *)struct_ptr)->alloced;
1786 : } else if (type == TYPE_date) {
1787 2 : alloced = ((struct cudf_data_struct_date *)struct_ptr)->alloced;
1788 : } else if (type == TYPE_daytime) {
1789 2 : alloced = ((struct cudf_data_struct_time *)struct_ptr)->alloced;
1790 : } else if (type == TYPE_timestamp) {
1791 2 : alloced = ((struct cudf_data_struct_timestamp *)struct_ptr)->alloced;
1792 : } else if (type == TYPE_blob) {
1793 2 : alloced = ((struct cudf_data_struct_blob *)struct_ptr)->alloced;
1794 : } else {
1795 : // unsupported type: string
1796 1 : alloced = ((struct cudf_data_struct_str *)struct_ptr)->alloced;
1797 : }
1798 58 : return alloced;
1799 : }
1800 :
1801 : static bool
1802 58 : isValloced(int type, void *struct_ptr)
1803 : {
1804 58 : bool alloced = false;
1805 :
1806 58 : if (type == TYPE_str) {
1807 6 : alloced = ((struct cudf_data_struct_str *)struct_ptr)->valloced;
1808 52 : } else if (type == TYPE_blob) {
1809 2 : alloced = ((struct cudf_data_struct_blob *)struct_ptr)->valloced;
1810 : } else {
1811 : // unsupported type: string
1812 50 : alloced = ((struct cudf_data_struct_str *)struct_ptr)->valloced;
1813 : }
1814 58 : return alloced;
1815 : }
1816 : void *
1817 105 : GetTypeData(int type, void *struct_ptr)
1818 : {
1819 105 : void *data = NULL;
1820 :
1821 105 : if (type == TYPE_bit || type == TYPE_bte) {
1822 0 : data = ((struct cudf_data_struct_bte *)struct_ptr)->data;
1823 : } else if (type == TYPE_sht) {
1824 0 : data = ((struct cudf_data_struct_sht *)struct_ptr)->data;
1825 : } else if (type == TYPE_int) {
1826 44 : data = ((struct cudf_data_struct_int *)struct_ptr)->data;
1827 : } else if (type == TYPE_oid) {
1828 9 : data = ((struct cudf_data_struct_oid *)struct_ptr)->data;
1829 : } else if (type == TYPE_lng) {
1830 11 : data = ((struct cudf_data_struct_lng *)struct_ptr)->data;
1831 : } else if (type == TYPE_flt) {
1832 1 : data = ((struct cudf_data_struct_flt *)struct_ptr)->data;
1833 : } else if (type == TYPE_dbl) {
1834 9 : data = ((struct cudf_data_struct_dbl *)struct_ptr)->data;
1835 : } else if (type == TYPE_str) {
1836 13 : data = ((struct cudf_data_struct_str *)struct_ptr)->data;
1837 : } else if (type == TYPE_date) {
1838 4 : data = ((struct cudf_data_struct_date *)struct_ptr)->data;
1839 : } else if (type == TYPE_daytime) {
1840 4 : data = ((struct cudf_data_struct_time *)struct_ptr)->data;
1841 : } else if (type == TYPE_timestamp) {
1842 4 : data = ((struct cudf_data_struct_timestamp *)struct_ptr)->data;
1843 : } else if (type == TYPE_blob) {
1844 4 : data = ((struct cudf_data_struct_blob *)struct_ptr)->data;
1845 : } else {
1846 : // unsupported type: string
1847 2 : data = ((struct cudf_data_struct_str *)struct_ptr)->data;
1848 : }
1849 105 : return data;
1850 : }
1851 :
1852 46 : void *GetTypeBat(int type, void *struct_ptr)
1853 : {
1854 46 : void *bat = NULL;
1855 :
1856 46 : if (type == TYPE_bit || type == TYPE_bte) {
1857 0 : bat = ((struct cudf_data_struct_bte *)struct_ptr)->bat;
1858 : } else if (type == TYPE_sht) {
1859 0 : bat = ((struct cudf_data_struct_sht *)struct_ptr)->bat;
1860 : } else if (type == TYPE_int) {
1861 16 : bat = ((struct cudf_data_struct_int *)struct_ptr)->bat;
1862 : } else if (type == TYPE_oid) {
1863 0 : bat = ((struct cudf_data_struct_oid *)struct_ptr)->bat;
1864 : } else if (type == TYPE_lng) {
1865 9 : bat = ((struct cudf_data_struct_lng *)struct_ptr)->bat;
1866 : } else if (type == TYPE_flt) {
1867 0 : bat = ((struct cudf_data_struct_flt *)struct_ptr)->bat;
1868 : } else if (type == TYPE_dbl) {
1869 5 : bat = ((struct cudf_data_struct_dbl *)struct_ptr)->bat;
1870 : } else if (type == TYPE_str) {
1871 7 : bat = ((struct cudf_data_struct_str *)struct_ptr)->bat;
1872 : } else if (type == TYPE_date) {
1873 2 : bat = ((struct cudf_data_struct_date *)struct_ptr)->bat;
1874 : } else if (type == TYPE_daytime) {
1875 2 : bat = ((struct cudf_data_struct_time *)struct_ptr)->bat;
1876 : } else if (type == TYPE_timestamp) {
1877 2 : bat = ((struct cudf_data_struct_timestamp *)struct_ptr)->bat;
1878 : } else if (type == TYPE_blob) {
1879 2 : bat = ((struct cudf_data_struct_blob *)struct_ptr)->bat;
1880 : } else {
1881 : // unsupported type: string
1882 1 : bat = ((struct cudf_data_struct_str *)struct_ptr)->bat;
1883 : }
1884 46 : return bat;
1885 : }
1886 :
1887 54 : size_t GetTypeCount(int type, void *struct_ptr)
1888 : {
1889 54 : size_t count = 0;
1890 54 : if (type == TYPE_bit || type == TYPE_bte) {
1891 0 : count = ((struct cudf_data_struct_bte *)struct_ptr)->count;
1892 : } else if (type == TYPE_sht) {
1893 0 : count = ((struct cudf_data_struct_sht *)struct_ptr)->count;
1894 : } else if (type == TYPE_int) {
1895 15 : count = ((struct cudf_data_struct_int *)struct_ptr)->count;
1896 : } else if (type == TYPE_oid) {
1897 0 : count = ((struct cudf_data_struct_oid *)struct_ptr)->count;
1898 : } else if (type == TYPE_lng) {
1899 9 : count = ((struct cudf_data_struct_lng *)struct_ptr)->count;
1900 : } else if (type == TYPE_flt) {
1901 0 : count = ((struct cudf_data_struct_flt *)struct_ptr)->count;
1902 : } else if (type == TYPE_dbl) {
1903 5 : count = ((struct cudf_data_struct_dbl *)struct_ptr)->count;
1904 : } else if (type == TYPE_str) {
1905 13 : count = ((struct cudf_data_struct_str *)struct_ptr)->count;
1906 : } else if (type == TYPE_date) {
1907 2 : count = ((struct cudf_data_struct_date *)struct_ptr)->count;
1908 : } else if (type == TYPE_daytime) {
1909 2 : count = ((struct cudf_data_struct_time *)struct_ptr)->count;
1910 : } else if (type == TYPE_timestamp) {
1911 2 : count = ((struct cudf_data_struct_timestamp *)struct_ptr)->count;
1912 : } else if (type == TYPE_blob) {
1913 4 : count = ((struct cudf_data_struct_blob *)struct_ptr)->count;
1914 : } else {
1915 : // unsupported type: string
1916 2 : count = ((struct cudf_data_struct_str *)struct_ptr)->count;
1917 : }
1918 54 : return count;
1919 : }
1920 :
1921 14 : void data_from_date(date d, cudf_data_date *ptr)
1922 : {
1923 14 : ptr->day = date_day(d);
1924 14 : ptr->month = date_month(d);
1925 14 : ptr->year = date_year(d);
1926 14 : }
1927 :
1928 5 : date date_from_data(cudf_data_date *ptr)
1929 : {
1930 5 : return date_create(ptr->year, ptr->month, ptr->day);
1931 : }
1932 :
1933 12 : void data_from_time(daytime d, cudf_data_time *ptr)
1934 : {
1935 12 : ptr->hours = daytime_hour(d);
1936 12 : ptr->minutes = daytime_min(d);
1937 12 : ptr->seconds = daytime_sec(d);
1938 12 : ptr->ms = daytime_usec(d) / 1000;
1939 12 : }
1940 :
1941 4 : daytime time_from_data(cudf_data_time *ptr)
1942 : {
1943 4 : return daytime_create(ptr->hours, ptr->minutes, ptr->seconds,
1944 4 : ptr->ms * 1000);
1945 : }
1946 :
1947 8 : void data_from_timestamp(timestamp d, cudf_data_timestamp *ptr)
1948 : {
1949 8 : daytime tm = timestamp_daytime(d);
1950 8 : date dt = timestamp_date(d);
1951 :
1952 8 : ptr->date.day = date_day(dt);
1953 8 : ptr->date.month = date_month(dt);
1954 8 : ptr->date.year = date_year(dt);
1955 8 : ptr->time.hours = daytime_hour(tm);
1956 8 : ptr->time.minutes = daytime_min(tm);
1957 8 : ptr->time.seconds = daytime_sec(tm);
1958 8 : ptr->time.ms = daytime_usec(tm) / 1000;
1959 8 : }
1960 :
1961 8 : timestamp timestamp_from_data(cudf_data_timestamp *ptr)
1962 : {
1963 8 : return timestamp_create(date_create(ptr->date.year,
1964 8 : ptr->date.month,
1965 8 : ptr->date.day),
1966 8 : daytime_create(ptr->time.hours,
1967 8 : ptr->time.minutes,
1968 8 : ptr->time.seconds,
1969 8 : ptr->time.ms * 1000));
1970 : }
1971 :
1972 5 : int date_is_null(cudf_data_date value)
1973 : {
1974 5 : cudf_data_date null_value;
1975 5 : data_from_date(date_nil, &null_value);
1976 5 : return value.year == null_value.year && value.month == null_value.month &&
1977 : value.day == null_value.day;
1978 : }
1979 :
1980 4 : int time_is_null(cudf_data_time value)
1981 : {
1982 4 : cudf_data_time null_value;
1983 4 : data_from_time(daytime_nil, &null_value);
1984 4 : return value.hours == null_value.hours &&
1985 4 : value.minutes == null_value.minutes &&
1986 4 : value.seconds == null_value.seconds && value.ms == null_value.ms;
1987 : }
1988 :
1989 4 : int timestamp_is_null(cudf_data_timestamp value)
1990 : {
1991 4 : return is_timestamp_nil(timestamp_from_data(&value));
1992 : }
1993 :
1994 10 : int str_is_null(char *value) { return value == NULL; }
1995 :
1996 4 : int blob_is_null(cudf_data_blob value) { return value.size == ~(size_t) 0; }
1997 :
1998 2 : void blob_initialize(struct cudf_data_struct_blob *self,
1999 : size_t count) {
2000 2 : self->count = count;
2001 2 : self->data = jump_GDK_malloc(count * sizeof(self->null_value));
2002 2 : memset(self->data, 0, count * sizeof(self->null_value));
2003 2 : }
2004 :
2005 : #include "mel.h"
2006 : static mel_func capi_init_funcs[] = {
2007 : pattern("capi", "eval", CUDFevalStd, false, "Execute a simple CUDF script returning a single value", args(1,4, argany("",0),arg("fptr",ptr),arg("cpp",bit),arg("expr",str))),
2008 : pattern("capi", "eval", CUDFevalStd, false, "Execute a simple CUDF script value", args(1,5, varargany("",0),arg("fptr",ptr),arg("cpp",bit),arg("expr",str),varargany("arg",0))),
2009 : pattern("capi", "subeval_aggr", CUDFevalAggr, false, "grouped aggregates through CUDF", args(1,5, varargany("",0),arg("fptr",ptr),arg("cpp",bit),arg("expr",str),varargany("arg",0))),
2010 : pattern("capi", "eval_aggr", CUDFevalAggr, false, "grouped aggregates through CUDF", args(1,5, varargany("",0),arg("fptr",ptr),arg("cpp",bit),arg("expr",str),varargany("arg",0))),
2011 : pattern("batcapi", "eval", CUDFevalStd, false, "Execute a simple CUDF script value", args(1,5, varargany("",0),arg("fptr",ptr),arg("cpp",bit),arg("expr",str),varargany("arg",0))),
2012 : { .imp=NULL }
2013 : };
2014 : #include "mal_import.h"
2015 : #ifdef _MSC_VER
2016 : #undef read
2017 : #pragma section(".CRT$XCU",read)
2018 : #endif
2019 5 : LIB_STARTUP_FUNC(init_capi_mal)
2020 5 : { mal_module2("capi", NULL, capi_init_funcs, CUDFprelude, NULL); }
|