LCOV - code coverage report
Current view: top level - sql/backends/monet5/UDF/capi - capi.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 759 998 76.1 %
Date: 2021-10-13 02:24:04 Functions: 39 52 75.0 %

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

Generated by: LCOV version 1.14