LCOV - code coverage report
Current view: top level - sql/backends/monet5/UDF/capi - capi.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 803 1061 75.7 %
Date: 2024-04-26 00:35:57 Functions: 38 51 74.5 %

          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), &regions);     \
     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, &regions);
    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, &regions);
    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(&current_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); }

Generated by: LCOV version 1.14