LCOV - code coverage report
Current view: top level - tools/monetdbe - monetdbe.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 438 1758 24.9 %
Date: 2024-04-25 20:03:45 Functions: 29 69 42.0 %

          Line data    Source code
       1             : /*
       2             :  * SPDX-License-Identifier: MPL-2.0
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       7             :  *
       8             :  * Copyright 2024 MonetDB Foundation;
       9             :  * Copyright August 2008 - 2023 MonetDB B.V.;
      10             :  * Copyright 1997 - July 2008 CWI.
      11             :  */
      12             : 
      13             : #include "monetdb_config.h"
      14             : 
      15             : #include "monetdbe.h"
      16             : #include "gdk.h"
      17             : #include "mal.h"
      18             : #include "mal_client.h"
      19             : #include "mal_embedded.h"
      20             : #include "mal_backend.h"
      21             : #include "mal_builder.h"
      22             : #include "opt_prelude.h"
      23             : #include "sql_mvc.h"
      24             : #include "sql_catalog.h"
      25             : #include "sql_gencode.h"
      26             : #include "sql_semantic.h"
      27             : #include "sql_scenario.h"
      28             : #include "sql_optimizer.h"
      29             : #include "rel_exp.h"
      30             : #include "rel_rel.h"
      31             : #include "rel_updates.h"
      32             : #include "monet_options.h"
      33             : #include "mapi.h"
      34             : #include "monetdbe_mapi.h"
      35             : #include "remote.h"
      36             : #include "sql.h"
      37             : #include "sql_result.h"
      38             : #include "mutils.h"
      39             : 
      40             : #define UNUSED(x) (void)(x)
      41             : 
      42             : static int
      43           0 : monetdbe_2_gdk_type(monetdbe_types t) {
      44           0 :         switch(t) {
      45             :         case monetdbe_bool: return TYPE_bit;
      46             :         case monetdbe_int8_t: return TYPE_bte;
      47             :         case monetdbe_int16_t: return TYPE_sht;
      48             :         case monetdbe_int32_t: return TYPE_int;
      49             :         case monetdbe_int64_t: return TYPE_lng;
      50             : #ifdef HAVE_HGE
      51             :         case monetdbe_int128_t: return TYPE_hge;
      52             : #endif
      53             :         case monetdbe_size_t: return TYPE_oid;
      54             :         case monetdbe_float: return TYPE_flt;
      55             :         case monetdbe_double: return TYPE_dbl;
      56             :         case monetdbe_str: return TYPE_str;
      57             :         case monetdbe_blob: return TYPE_blob;
      58             :         case monetdbe_date: return TYPE_date;
      59             :         case monetdbe_time: return TYPE_daytime;
      60             :         case monetdbe_timestamp: return TYPE_timestamp;
      61             :         default:
      62             :                 return -1;
      63             :         }
      64             : }
      65             : 
      66             : static monetdbe_types
      67           0 : embedded_type(int t) {
      68           0 :         switch(t) {
      69             :         case TYPE_bit: return monetdbe_bool;
      70           0 :         case TYPE_bte: return monetdbe_int8_t;
      71           0 :         case TYPE_sht: return monetdbe_int16_t;
      72           0 :         case TYPE_int: return monetdbe_int32_t;
      73           0 :         case TYPE_lng: return monetdbe_int64_t;
      74             : #ifdef HAVE_HGE
      75           0 :         case TYPE_hge: return monetdbe_int128_t;
      76             : #endif
      77           0 :         case TYPE_oid: return monetdbe_size_t;
      78           0 :         case TYPE_flt: return monetdbe_float;
      79           0 :         case TYPE_dbl: return monetdbe_double;
      80           0 :         case TYPE_str: return monetdbe_str;
      81           0 :         case TYPE_date: return monetdbe_date;
      82           0 :         case TYPE_daytime: return monetdbe_time;
      83           0 :         case TYPE_timestamp: return monetdbe_timestamp;
      84           0 :         default:
      85           0 :                 if (t==TYPE_blob)
      86           0 :                         return monetdbe_blob;
      87             :                 return monetdbe_type_unknown;
      88             :         }
      89             : }
      90             : 
      91             : typedef struct {
      92             :         Client c;
      93             :         char *msg;
      94             :         int registered_thread;  /* 1 = registered in monetdbe_open, 2 = done by GDK (also deregister done there) */
      95             :         monetdbe_data_blob blob_null;
      96             :         monetdbe_data_date date_null;
      97             :         monetdbe_data_time time_null;
      98             :         monetdbe_data_timestamp timestamp_null;
      99             :         str mid;
     100             : } monetdbe_database_internal;
     101             : 
     102             : typedef struct {
     103             :         monetdbe_result res;
     104             :         int type;
     105             :         res_table *monetdbe_resultset;
     106             :         monetdbe_column **converted_columns;
     107             :         monetdbe_database_internal *mdbe;
     108             : } monetdbe_result_internal;
     109             : 
     110             : typedef struct {
     111             :         monetdbe_statement res;
     112             :         ValRecord *data;
     113             :         ValPtr *args;   /* only used during calls */
     114             :         int retc;
     115             :         monetdbe_database_internal *mdbe;
     116             :         cq *q;
     117             : } monetdbe_stmt_internal;
     118             : 
     119             : static MT_Lock embedded_lock = MT_LOCK_INITIALIZER(embedded_lock);
     120             : static bool monetdbe_embedded_initialized = false;
     121             : static char *monetdbe_embedded_url = NULL;
     122             : static int open_dbs = 0;
     123             : 
     124             : static void data_from_date(date d, monetdbe_data_date *ptr);
     125             : static void data_from_time(daytime d, monetdbe_data_time *ptr);
     126             : static void data_from_timestamp(timestamp d, monetdbe_data_timestamp *ptr);
     127             : static timestamp timestamp_from_data(monetdbe_data_timestamp *ptr);
     128             : static date date_from_data(monetdbe_data_date *ptr);
     129             : static daytime time_from_data(monetdbe_data_time *ptr);
     130             : 
     131             : static char* monetdbe_cleanup_result_internal(monetdbe_database_internal *mdbe, monetdbe_result_internal* res);
     132             : 
     133             : static int
     134           0 : date_is_null(monetdbe_data_date *value)
     135             : {
     136           0 :         monetdbe_data_date null_value;
     137           0 :         data_from_date(date_nil, &null_value);
     138           0 :         return value->year == null_value.year && value->month == null_value.month &&
     139             :                    value->day == null_value.day;
     140             : }
     141             : 
     142             : static int
     143           0 : time_is_null(monetdbe_data_time *value)
     144             : {
     145           0 :         monetdbe_data_time null_value;
     146           0 :         data_from_time(daytime_nil, &null_value);
     147           0 :         return value->hours == null_value.hours &&
     148           0 :                    value->minutes == null_value.minutes &&
     149           0 :                    value->seconds == null_value.seconds && value->ms == null_value.ms;
     150             : }
     151             : 
     152             : static int
     153           0 : timestamp_is_null(monetdbe_data_timestamp *value)
     154             : {
     155           0 :         return is_timestamp_nil(timestamp_from_data(value));
     156             : }
     157             : 
     158             : static int
     159           2 : str_is_null(char **value)
     160             : {
     161           2 :         return !value || *value == NULL;
     162             : }
     163             : 
     164             : static int
     165           0 : blob_is_null(monetdbe_data_blob *value)
     166             : {
     167           0 :         return !value || value->data == NULL;
     168             : }
     169             : 
     170             : const char *
     171           0 : monetdbe_version(void)
     172             : {
     173           0 :         return MONETDBE_VERSION;
     174             : }
     175             : 
     176             : static void
     177           6 : clear_error( monetdbe_database_internal *mdbe)
     178             : {
     179           6 :         if (mdbe->msg)
     180           0 :                 freeException(mdbe->msg);
     181           6 :         mdbe->msg = NULL;
     182           0 : }
     183             : 
     184             : static char*
     185           0 : set_error( monetdbe_database_internal *mdbe, char *err)
     186             : {
     187           0 :         if (!err)
     188             :                 return err;
     189           0 :         if (mdbe->msg) /* keep first error */
     190           0 :                 freeException(err);
     191             :         else
     192           0 :                 mdbe->msg = err;
     193           0 :         return mdbe->msg;
     194             : }
     195             : 
     196             : static char*
     197           3 : commit_action(mvc* m, monetdbe_database_internal *mdbe, monetdbe_result **result, monetdbe_result_internal *res_internal)
     198             : {
     199           3 :         char *commit_msg = MAL_SUCCEED;
     200             : 
     201             :         /* if an error already exists from MonetDBe set the session status to dirty */
     202           3 :         if (mdbe->msg != MAL_SUCCEED && m->session->tr->active && !m->session->status)
     203           0 :                 m->session->status = -1;
     204           3 :         commit_msg = SQLautocommit(m); /* handle autocommit */
     205             : 
     206           3 :         if (mdbe->msg != MAL_SUCCEED || commit_msg != MAL_SUCCEED) {
     207           0 :                 if (res_internal) {
     208           0 :                         char* other = monetdbe_cleanup_result_internal(mdbe, res_internal);
     209           0 :                         if (other)
     210           0 :                                 freeException(other);
     211             :                 }
     212           0 :                 if (result)
     213           0 :                         *result = NULL;
     214           0 :                 (void)set_error(mdbe, commit_msg);
     215             :         }
     216           3 :         return mdbe->msg;
     217             : }
     218             : 
     219             : static int
     220           1 : validate_database_handle_noerror(monetdbe_database_internal *mdbe)
     221             : {
     222           1 :         if (!monetdbe_embedded_initialized || !MCvalid(mdbe->c))
     223           0 :                 return 0;
     224           1 :         assert(mdbe->c);
     225           1 :         MT_thread_set_qry_ctx(&mdbe->c->qryctx);
     226           1 :         clear_error(mdbe);
     227           1 :         return 1;
     228             : }
     229             : 
     230             : // Call this function always inside the embedded_lock
     231             : static char*
     232           5 : validate_database_handle(monetdbe_database_internal *mdbe, const char* call)
     233             : {
     234           5 :         if (!monetdbe_embedded_initialized)
     235           0 :                 return createException(MAL, call, "MonetDBe has not yet started");
     236           5 :         if (!MCvalid(mdbe->c))
     237           0 :                 return createException(MAL, call, "Invalid database handle");
     238           5 :         clear_error(mdbe);
     239           5 :         return MAL_SUCCEED;
     240             : }
     241             : 
     242             : static void
     243           2 : monetdbe_destroy_column(monetdbe_column* column)
     244             : {
     245           2 :         size_t j;
     246             : 
     247           2 :         if (!column)
     248             :                 return;
     249             : 
     250           2 :         if (column->type == monetdbe_str) {
     251             :                 // FIXME: clean up individual strings
     252           1 :                 char** data = (char**)column->data;
     253           3 :                 for(j = 0; j < column->count; j++) {
     254           2 :                         if (data[j])
     255           2 :                                 GDKfree(data[j]);
     256             :                 }
     257           1 :         } else if (column->type == monetdbe_blob) {
     258           0 :                 monetdbe_data_blob* data = (monetdbe_data_blob*)column->data;
     259           0 :                 for(j = 0; j < column->count; j++) {
     260           0 :                         if (data[j].data)
     261           0 :                                 GDKfree(data[j].data);
     262             :                 }
     263             :         }
     264           2 :         GDKfree(column->sql_type.name);
     265           2 :         GDKfree(column->data);
     266           2 :         GDKfree(column);
     267             : }
     268             : 
     269             : static char*
     270           1 : monetdbe_cleanup_result_internal(monetdbe_database_internal *mdbe, monetdbe_result_internal* result)
     271             : {
     272           1 :         mvc *m = NULL;
     273             : 
     274           1 :         assert(!result || !result->mdbe || result->mdbe == mdbe);
     275           1 :         if ((mdbe->msg = validate_database_handle(mdbe, "monetdbe.monetdbe_cleanup_result_internal")) != MAL_SUCCEED)
     276             :                 return mdbe->msg;
     277           1 :         if ((mdbe->msg = getSQLContext(mdbe->c, NULL, &m, NULL)) != MAL_SUCCEED)
     278           0 :                 goto cleanup;
     279             : 
     280           1 :         if (result->monetdbe_resultset)
     281           1 :                 res_tables_destroy(result->monetdbe_resultset);
     282             : 
     283           1 :         if (result->converted_columns) {
     284           3 :                 for (size_t i = 0; i < result->res.ncols; i++)
     285           2 :                         monetdbe_destroy_column(result->converted_columns[i]);
     286           1 :                 GDKfree(result->converted_columns);
     287             :         }
     288           1 :         GDKfree(result);
     289           1 : cleanup:
     290           1 :         return commit_action(m, mdbe, NULL, NULL);
     291             : }
     292             : 
     293             : static char*
     294           1 : monetdbe_get_results(monetdbe_result** result, monetdbe_database_internal *mdbe)
     295             : {
     296           1 :         backend *be = NULL;
     297             : 
     298           1 :         *result = NULL;
     299           1 :         if ((mdbe->msg = getBackendContext(mdbe->c, &be)) != NULL)
     300             :                 return mdbe->msg;
     301             : 
     302           1 :         mvc *m = be->mvc;
     303           1 :         monetdbe_result_internal* res_internal;
     304             : 
     305           1 :         if (!(res_internal = GDKzalloc(sizeof(monetdbe_result_internal)))) {
     306           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_get_results", MAL_MALLOC_FAIL));
     307           0 :                 return mdbe->msg;
     308             :         }
     309             :         // TODO: set type of result outside.
     310           1 :         res_internal->res.last_id = be->last_id;
     311           1 :         res_internal->mdbe = mdbe;
     312           1 :         *result = (monetdbe_result*) res_internal;
     313           1 :         m->reply_size = -2; /* do not clean up result tables */
     314             : 
     315           1 :         if (be->results) {
     316           1 :                 res_internal->res.ncols = (size_t) be->results->nr_cols;
     317           1 :                 res_internal->monetdbe_resultset = be->results;
     318           1 :                 if (be->results->nr_cols > 0)
     319           1 :                         res_internal->res.nrows = be->results->nr_rows;
     320           1 :                 be->results = NULL;
     321           1 :                 res_internal->converted_columns = GDKzalloc(sizeof(monetdbe_column*) * res_internal->res.ncols);
     322           1 :                 if (!res_internal->converted_columns) {
     323           0 :                         GDKfree(res_internal);
     324           0 :                         *result = NULL;
     325           0 :                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_get_results", MAL_MALLOC_FAIL));
     326           0 :                         return mdbe->msg;
     327             :                 }
     328             :         }
     329             : 
     330             :         return MAL_SUCCEED;
     331             : }
     332             : 
     333             : static char*
     334           0 : monetdbe_query_internal(monetdbe_database_internal *mdbe, char* query, monetdbe_result** result, monetdbe_cnt* affected_rows, int *prepare_id, char language)
     335             : {
     336           0 :         char *nq = NULL;
     337           0 :         Client c = mdbe->c;
     338           0 :         mvc* m = NULL;
     339           0 :         backend *b;
     340           0 :         size_t query_len, input_query_len, prep_len = 0;
     341           0 :         buffer query_buf;
     342           0 :         stream *query_stream = NULL;
     343           0 :         bstream *old_bstream = c->fdin;
     344           0 :         stream *fdout = c->fdout;
     345           0 :         bool fdin_changed = false;
     346             : 
     347           0 :         if (result)
     348           0 :                 *result = NULL;
     349             : 
     350           0 :         if ((mdbe->msg = validate_database_handle(mdbe, "monetdbe.monetdbe_query_internal")) != MAL_SUCCEED)
     351             :                 return mdbe->msg;
     352             : 
     353           0 :         if ((mdbe->msg = getSQLContext(c, NULL, &m, NULL)) != MAL_SUCCEED)
     354           0 :                 goto cleanup;
     355           0 :         b = (backend *) c->sqlcontext;
     356             : 
     357           0 :         if (!query) {
     358           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_query_internal", "Query missing"));
     359           0 :                 goto cleanup;
     360             :         }
     361           0 :         if (!(query_stream = buffer_rastream(&query_buf, "sqlstatement"))) {
     362           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_query_internal", "Could not setup query stream"));
     363           0 :                 goto cleanup;
     364             :         }
     365           0 :         input_query_len = strlen(query);
     366           0 :         query_len = input_query_len + 3;
     367           0 :         if (prepare_id) {
     368           0 :                 prep_len = sizeof("PREPARE ")-1;
     369           0 :                 query_len += prep_len;
     370             :         }
     371           0 :         if (!(nq = GDKmalloc(query_len))) {
     372           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_query_internal", MAL_MALLOC_FAIL));
     373           0 :                 goto cleanup;
     374             :         }
     375           0 :         if (prepare_id)
     376           0 :                 strcpy(nq, "PREPARE ");
     377           0 :         strcpy(nq + prep_len, query);
     378           0 :         strcpy(nq + prep_len + input_query_len, "\n;");
     379             : 
     380           0 :         query_buf.pos = 0;
     381           0 :         query_buf.len = query_len;
     382           0 :         query_buf.buf = nq;
     383             : 
     384           0 :         fdin_changed = true;
     385           0 :         if (!(c->fdin = bstream_create(query_stream, query_len))) {
     386           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_query_internal", "Could not setup query stream"));
     387           0 :                 goto cleanup;
     388             :         }
     389           0 :         query_stream = NULL;
     390           0 :         if (bstream_next(c->fdin) < 0) {
     391           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_query_internal", "Internal error while starting the query"));
     392           0 :                 goto cleanup;
     393             :         }
     394             : 
     395           0 :         assert(language);
     396           0 :         b->language = language;
     397           0 :         b->output_format = OFMT_NONE;
     398           0 :         b->no_mitosis = 0;
     399           0 :         m->user_id = m->role_id = USER_MONETDB;
     400           0 :         m->errstr[0] = '\0';
     401           0 :         m->params = NULL;
     402           0 :         m->sym = NULL;
     403           0 :         m->runs = NULL;
     404           0 :         m->label = 0;
     405           0 :         if (m->sa)
     406           0 :                 m->sa = sa_reset(m->sa);
     407           0 :         m->scanner.mode = LINE_N;
     408           0 :         m->scanner.rs = c->fdin;
     409           0 :         scanner_query_processed(&(m->scanner));
     410             : 
     411           0 :         if ((mdbe->msg = MSinitClientPrg(c, "user", "main")) != MAL_SUCCEED)
     412           0 :                 goto cleanup;
     413           0 :         if (prepare_id)
     414           0 :                 m->emode = m_prepare;
     415           0 :         c->fdout = NULL;
     416           0 :         if ((mdbe->msg = SQLengine_(c)) != MAL_SUCCEED)
     417           0 :                 goto cleanup;
     418           0 :         if (m->emode == m_prepare && prepare_id)
     419           0 :                 *prepare_id = b->result_id;
     420           0 :         if (!b->results && b->rowcnt >= 0 && affected_rows)
     421           0 :                 *affected_rows = b->rowcnt;
     422             : 
     423           0 :         if (result) {
     424           0 :                 if ((mdbe->msg = monetdbe_get_results(result, mdbe)) != MAL_SUCCEED) {
     425           0 :                         goto cleanup;
     426             :                 }
     427             : 
     428           0 :                 if (m->emode & m_prepare)
     429           0 :                         (*(monetdbe_result_internal**) result)->type = Q_PREPARE;
     430             :                 else
     431           0 :                         (*(monetdbe_result_internal**) result)->type = (b->results) ? b->results->query_type : m->type;
     432             :         }
     433             : 
     434           0 : cleanup:
     435           0 :         c->fdout = fdout;
     436           0 :         if (nq)
     437           0 :                 GDKfree(nq);
     438           0 :         MSresetInstructions(c->curprg->def, 1);
     439           0 :         if (fdin_changed) { //c->fdin was set
     440           0 :                 bstream_destroy(c->fdin);
     441           0 :                 c->fdin = old_bstream;
     442             :         }
     443           0 :         if (query_stream)
     444           0 :                 close_stream(query_stream);
     445             : 
     446           0 :         return commit_action(m, mdbe, result, result?*(monetdbe_result_internal**) result:NULL);
     447             : }
     448             : 
     449             : static int
     450           1 : monetdbe_close_remote(monetdbe_database_internal *mdbe)
     451             : {
     452           1 :         assert(mdbe && mdbe->mid);
     453             : 
     454           1 :         int err = 0;
     455             : 
     456           1 :         if (mdbe->msg) {
     457           0 :                 err = 1;
     458           0 :                 clear_error(mdbe);
     459             :         }
     460             : 
     461           1 :         if ( (mdbe->msg = RMTdisconnect(NULL, &(mdbe->mid))) != MAL_SUCCEED) {
     462           0 :                 err = 1;
     463           0 :                 clear_error(mdbe);
     464             :         }
     465             : 
     466           1 :         GDKfree(mdbe->mid);
     467           1 :         mdbe->mid = NULL;
     468             : 
     469           1 :         return err;
     470             : }
     471             : 
     472             : static int
     473           1 : monetdbe_close_internal(monetdbe_database_internal *mdbe)
     474             : {
     475           1 :         assert(mdbe);
     476             : 
     477           1 :         if (validate_database_handle_noerror(mdbe)) {
     478           1 :                 open_dbs--;
     479           1 :                 char *msg = SQLexitClient(mdbe->c);
     480           1 :                 if (msg)
     481           0 :                         freeException(msg);
     482           1 :                 MCcloseClient(mdbe->c);
     483             :         }
     484           1 :         GDKfree(mdbe);
     485           1 :         return 0;
     486             : }
     487             : 
     488             : static int
     489           2 : monetdbe_workers_internal(monetdbe_database_internal *mdbe, monetdbe_options *opts)
     490             : {
     491           2 :         int workers = 0;
     492           2 :         if (opts && opts->nr_threads) {
     493           0 :                 if (opts->nr_threads < 0)
     494           0 :                         set_error(mdbe,createException(MAL, "monetdbe.monetdbe_startup", "Nr_threads should be positive"));
     495             :                 else
     496             :                         workers = opts->nr_threads;
     497             :         }
     498           2 :         return workers;
     499             : }
     500             : 
     501             : static int
     502           2 : monetdbe_memory_internal(monetdbe_database_internal *mdbe, monetdbe_options *opts)
     503             : {
     504           2 :         int memory = 0;
     505           2 :         if (opts && opts->memorylimit) {
     506           0 :                 if (opts->memorylimit < 0)
     507           0 :                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_startup", "Memorylimit should be positive"));
     508             :                 else /* Memory limit is session specific */
     509             :                         memory = opts->memorylimit;
     510             :         }
     511           2 :         return memory;
     512             : }
     513             : 
     514             : static int
     515           1 : monetdbe_querytimeout_internal(monetdbe_database_internal *mdbe, monetdbe_options *opts)
     516             : {
     517           1 :         int querytimeout = 0;
     518           1 :         if (opts && opts->querytimeout) {
     519           0 :                 if (opts->querytimeout < 0)
     520           0 :                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_startup", "Query timeout should be positive (in sec)"));
     521             :                 else
     522             :                         querytimeout = opts->querytimeout;
     523             :         }
     524           1 :         return querytimeout;
     525             : }
     526             : 
     527             : static int
     528           1 : monetdbe_sessiontimeout_internal(monetdbe_database_internal *mdbe, monetdbe_options *opts)
     529             : {
     530           1 :         int sessiontimeout = 0;
     531           1 :         if (opts && opts->sessiontimeout) {
     532           0 :                 if (opts->sessiontimeout < 0)
     533           0 :                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_startup", "Session timeout should be positive (in sec)"));
     534             :                 else
     535             :                         sessiontimeout = opts->sessiontimeout;
     536             :         }
     537           1 :         return sessiontimeout;
     538             : }
     539             : 
     540             : static int
     541           1 : monetdbe_open_internal(monetdbe_database_internal *mdbe, monetdbe_options *opts )
     542             : {
     543           1 :         mvc *m;
     544             : 
     545           1 :         if (!mdbe)
     546             :                 return -1;
     547           1 :         if (!monetdbe_embedded_initialized) {
     548           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_open_internal", "Embedded MonetDB is not started"));
     549           0 :                 goto cleanup;
     550             :         }
     551           1 :         if (!mdbe->registered_thread) {
     552           0 :                 if (!MT_thread_register()) {
     553           0 :                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_open_internal", "Embedded MonetDB is not started"));
     554           0 :                         goto cleanup;
     555             :                 }
     556           0 :                 mdbe->registered_thread = 1;
     557             :         }
     558           1 :         mdbe->c = MCinitClient((oid) 0, 0, 0);
     559           1 :         if (!MCvalid(mdbe->c)) {
     560           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_open_internal", "Failed to initialize client"));
     561           0 :                 goto cleanup;
     562             :         }
     563           1 :         mdbe->c->curmodule = mdbe->c->usermodule = userModule();
     564           1 :         mdbe->c->workerlimit = monetdbe_workers_internal(mdbe, opts);
     565           1 :         mdbe->c->memorylimit = monetdbe_memory_internal(mdbe, opts);
     566           1 :         mdbe->c->qryctx.querytimeout = monetdbe_querytimeout_internal(mdbe, opts);
     567           1 :         mdbe->c->sessiontimeout = monetdbe_sessiontimeout_internal(mdbe, opts);
     568           1 :         if (mdbe->msg)
     569           0 :                 goto cleanup;
     570           1 :         if (mdbe->c->usermodule == NULL) {
     571           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_open_internal", "Failed to initialize client MAL module"));
     572           0 :                 goto cleanup;
     573             :         }
     574           1 :         if ((mdbe->msg = SQLinitClient(mdbe->c, NULL, NULL, NULL)) != MAL_SUCCEED ||
     575           1 :                 (mdbe->msg = getSQLContext(mdbe->c, NULL, &m, NULL)) != MAL_SUCCEED)
     576           0 :                 goto cleanup;
     577           1 :         m->session->auto_commit = 1;
     578           1 :         if (!m->pa)
     579           0 :                 m->pa = sa_create(NULL);
     580           1 :         if (!m->sa)
     581           1 :                 m->sa = sa_create(m->pa);
     582           1 :         if (!m->ta)
     583           0 :                 m->ta = sa_create(m->pa);
     584           1 :         if (!m->pa || !m->sa || !m->ta) {
     585           0 :                 set_error(mdbe, createException(SQL, "monetdbe.monetdbe_open_internal", MAL_MALLOC_FAIL));
     586           0 :                 goto cleanup;
     587             :         }
     588           1 : cleanup:
     589           1 :         if (mdbe->msg)
     590             :                 return -2;
     591           1 :         mdbe->blob_null.data = NULL;
     592           1 :         data_from_date(date_nil, &mdbe->date_null);
     593           1 :         data_from_time(daytime_nil, &mdbe->time_null);
     594           1 :         data_from_timestamp(timestamp_nil, &mdbe->timestamp_null);
     595           1 :         open_dbs++;
     596           1 :         return 0;
     597             : }
     598             : 
     599             : static void
     600           1 : monetdbe_shutdown_internal(void) // Call this function always inside the embedded_lock
     601             : {
     602           1 :         if (monetdbe_embedded_initialized && (open_dbs == 0)) {
     603           1 :                 malEmbeddedReset();
     604           1 :                 monetdbe_embedded_initialized = false;
     605           1 :                 if (monetdbe_embedded_url)
     606           0 :                         GDKfree(monetdbe_embedded_url);
     607           1 :                 monetdbe_embedded_url = NULL;
     608             :         }
     609           1 : }
     610             : 
     611             : static void
     612           1 : monetdbe_startup(monetdbe_database_internal *mdbe, const char* dbdir, monetdbe_options *opts)
     613             : {
     614             :         // Only call monetdbe_startup when there is no monetdb internal yet initialized.
     615           1 :         assert(!monetdbe_embedded_initialized);
     616             : 
     617           1 :         opt *set = NULL;
     618           1 :         int setlen;
     619           1 :         bool with_mapi_server;
     620           1 :         int workers, memory;
     621           1 :         gdk_return gdk_res;
     622             : 
     623           1 :         GDKfataljumpenable = 1;
     624             : 
     625           1 :         if(setjmp(GDKfataljump) != 0) {
     626           0 :                 assert(0);
     627             :                 mdbe->msg = GDKfatalmsg;
     628             :                 // we will get here if GDKfatal was called.
     629             :                 if (mdbe->msg == NULL)
     630             :                         mdbe->msg = createException(MAL, "monetdbe.monetdbe_startup", "GDKfatal() with unspecified error");
     631             :                 goto cleanup;
     632             :         }
     633             : 
     634           1 :         with_mapi_server = false;
     635             : 
     636           1 :         if (monetdbe_embedded_initialized) {
     637           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_startup", "MonetDBe is already initialized"));
     638           0 :                 GDKfataljumpenable = 0;
     639           0 :                 return;
     640             :         }
     641             : 
     642           1 :         if ((setlen = mo_builtin_settings(&set)) == 0) {
     643           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_startup", MAL_MALLOC_FAIL));
     644           0 :                 goto cleanup;
     645             :         }
     646           1 :         if (dbdir && (setlen = mo_add_option(&set, setlen, opt_cmdline, "gdk_dbpath", dbdir)) == 0) {
     647           0 :                 mo_free_options(set, setlen);
     648           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_startup", MAL_MALLOC_FAIL));
     649           0 :                 goto cleanup;
     650             :         }
     651           1 :         if (opts && opts->nr_threads == 1)
     652           0 :                 setlen = mo_add_option(&set, setlen, opt_cmdline, "sql_optimizer", "sequential_pipe");
     653             :         else
     654           1 :                 setlen = mo_add_option(&set, setlen, opt_cmdline, "sql_optimizer", "default_pipe");
     655             : 
     656           1 :         if (setlen == 0) {
     657           0 :                 mo_free_options(set, setlen);
     658           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_startup", MAL_MALLOC_FAIL));
     659           0 :                 goto cleanup;
     660             :         }
     661             : 
     662           1 :         if (opts && opts->mapi_server) {
     663             :                 /*This monetdbe instance wants to listen to external mapi client connections.*/
     664           0 :                 if (opts->mapi_server->host) {
     665           0 :                         with_mapi_server = true;
     666           0 :                         int psetlen = setlen;
     667           0 :                         setlen = mo_add_option(&set, setlen, opt_cmdline, "mapi_listenaddr", opts->mapi_server->host);
     668           0 :                         if (setlen == psetlen) {
     669           0 :                                 mo_free_options(set, setlen);
     670           0 :                                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_startup", MAL_MALLOC_FAIL));
     671           0 :                                 goto cleanup;
     672             :                         }
     673             :                 }
     674           0 :                 if (opts->mapi_server->port) {
     675           0 :                         with_mapi_server = true;
     676           0 :                         int psetlen = setlen;
     677           0 :                         setlen = mo_add_option(&set, setlen, opt_cmdline, "mapi_port", opts->mapi_server->port);
     678           0 :                         if (setlen == psetlen) {
     679           0 :                                 mo_free_options(set, setlen);
     680           0 :                                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_startup", MAL_MALLOC_FAIL));
     681           0 :                                 goto cleanup;
     682             :                         }
     683             :                 }
     684           0 :                 if (opts->mapi_server->usock) {
     685           0 :                         with_mapi_server = true;
     686           0 :                         int psetlen = setlen;
     687           0 :                         setlen = mo_add_option(&set, setlen, opt_cmdline, "mapi_usock", opts->mapi_server->usock);
     688           0 :                         if (setlen == psetlen) {
     689           0 :                                 mo_free_options(set, setlen);
     690           0 :                                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_startup", MAL_MALLOC_FAIL));
     691           0 :                                 goto cleanup;
     692             :                         }
     693             :                 }
     694             :         }
     695             : 
     696             :         /* set the output of GDKtracer logs */
     697           1 :         if (opts && opts->trace_file) {
     698             :                 /* if file specified, use it */
     699           0 :                 if (GDKtracer_set_tracefile(opts->trace_file) != GDK_SUCCEED) {
     700           0 :                         mo_free_options(set, setlen);
     701           0 :                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_startup", GDK_EXCEPTION));
     702           0 :                         goto cleanup;
     703             :                 }
     704           0 :                 GDKtracer_set_adapter("BASIC");
     705             :         } else {
     706             :                 /* otherwise no trace output */
     707           1 :                 GDKtracer_set_adapter("MBEDDED");
     708             :         }
     709             : 
     710           1 :         if ((workers = monetdbe_workers_internal(mdbe, opts))) {
     711           0 :                 int psetlen = setlen;
     712           0 :                 char workstr[16];
     713             : 
     714           0 :                 snprintf(workstr, sizeof(workstr), "%d", workers);
     715           0 :                 if ((setlen = mo_add_option(&set, setlen, opt_cmdline, "gdk_nr_threads", workstr)) == psetlen) {
     716           0 :                         mo_free_options(set, setlen);
     717           0 :                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_startup", MAL_MALLOC_FAIL));
     718           0 :                         goto cleanup;
     719             :                 }
     720             :         }
     721           1 :         if ((memory = monetdbe_memory_internal(mdbe, opts))) {
     722           0 :                 int psetlen = setlen;
     723           0 :                 char memstr[32];
     724             : 
     725           0 :                 snprintf(memstr, sizeof(memstr), "%zu", (size_t) memory << 20); /* convert from MiB to bytes */
     726           0 :                 if ((setlen = mo_add_option(&set, setlen, opt_cmdline, "gdk_vm_maxsize", memstr)) == psetlen) {
     727           0 :                         mo_free_options(set, setlen);
     728           0 :                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_startup", MAL_MALLOC_FAIL));
     729           0 :                         goto cleanup;
     730             :                 }
     731             :         }
     732           1 :         if (mdbe->msg) {
     733           0 :                 mo_free_options(set, setlen);
     734           0 :                 goto cleanup;
     735             :         }
     736             : 
     737           1 :         if (!dbdir) { /* in-memory */
     738           1 :                 if (BBPaddfarm(NULL, (1U << PERSISTENT) | (1U << TRANSIENT), false) != GDK_SUCCEED) {
     739           0 :                         mo_free_options(set, setlen);
     740           0 :                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_startup", "Cannot add in-memory farm"));
     741           0 :                         goto cleanup;
     742             :                 }
     743             :         } else {
     744           0 :                 if (BBPaddfarm(dbdir, 1U << PERSISTENT, false) != GDK_SUCCEED ||
     745           0 :                         BBPaddfarm(/*dbextra ? dbextra : */dbdir, 1U << TRANSIENT, false) != GDK_SUCCEED) {
     746           0 :                         mo_free_options(set, setlen);
     747           0 :                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_startup", "Cannot add farm %s", dbdir));
     748           0 :                         goto cleanup;
     749             :                 }
     750           0 :                 if (GDKcreatedir(dbdir) != GDK_SUCCEED) {
     751           0 :                         mo_free_options(set, setlen);
     752           0 :                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_startup", "Cannot create directory %s", dbdir));
     753           0 :                         goto cleanup;
     754             :                 }
     755             :         }
     756           1 :         gdk_res = GDKinit(set, setlen, true, mercurial_revision());
     757           1 :         mo_free_options(set, setlen);
     758           1 :         if (gdk_res != GDK_SUCCEED) {
     759           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_startup", "GDKinit() failed"));
     760           0 :                 goto cleanup;
     761             :         }
     762             : 
     763           1 :         if ((mdbe->msg = malEmbeddedBoot(workers, memory, 0, 0, with_mapi_server)) != MAL_SUCCEED)
     764           0 :                 goto cleanup;
     765             : 
     766           1 :         monetdbe_embedded_initialized = true;
     767           1 :         monetdbe_embedded_url = dbdir?GDKstrdup(dbdir):NULL;
     768           1 :         if (dbdir && !monetdbe_embedded_url)
     769           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_startup", MAL_MALLOC_FAIL));
     770           1 : cleanup:
     771           1 :         GDKfataljumpenable = 0;
     772           1 :         if (mdbe->msg)
     773           0 :                 monetdbe_shutdown_internal();
     774             : }
     775             : 
     776           0 : static bool urls_matches(const char* l, const char* r) {
     777           0 :         return (l && r && (strcmp(l, r) == 0)) || (l == NULL && r == NULL);
     778             : }
     779             : 
     780             : static inline str
     781           1 : monetdbe_create_uri(const char* host, const int port, const char* database) {
     782           1 :         const char* protocol = "mapi:monetdb://";
     783             : 
     784           1 :         const size_t sl_protocol = strlen(protocol);
     785           1 :         const size_t sl_host = strlen(host);
     786           1 :         const size_t sl_max_port = 6; // 2^16-1 < 100 000 = 10^5, i.e. always less then 6 digits.
     787           1 :         const size_t sl_database = strlen(database);
     788           1 :         const size_t sl_total = sl_protocol + sl_host + 1 /* : */ + sl_max_port + 1 + /* / */ + sl_database;
     789             : 
     790           1 :         str uri_buffer = GDKmalloc(sl_total + 1 /* terminator */);
     791           1 :         if (!uri_buffer)
     792             :                 return NULL;
     793             : 
     794           1 :         snprintf(uri_buffer, sl_total, "%s%s:%d/%s", protocol, host, port, database);
     795             : 
     796           1 :         return uri_buffer;
     797             : }
     798             : 
     799             : static int
     800           1 : monetdbe_open_remote(monetdbe_database_internal *mdbe, monetdbe_options *opts) {
     801           1 :         assert(opts);
     802             : 
     803           1 :         monetdbe_remote* remote = opts->remote;
     804           1 :         if (!remote) {
     805           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_open_remote", "Missing remote proxy settings"));
     806           0 :                 return -1;
     807             :         }
     808             : 
     809           1 :         Client c = mdbe->c;
     810             : 
     811           1 :         assert(!c->curprg);
     812             : 
     813           1 :         const char *mod = "user";
     814           1 :         char nme[16];
     815           1 :         const char *name = number2name(nme, sizeof(nme), ++((backend*)  c->sqlcontext)->remote);
     816           1 :         c->curprg = newFunction(putName(mod), putName(name), FUNCTIONsymbol);
     817             : 
     818           1 :         if (c->curprg == NULL) {
     819           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_open_remote", MAL_MALLOC_FAIL));
     820           0 :                 return -2;
     821             :         }
     822             : 
     823           1 :         char* url;
     824           1 :         if ((url = monetdbe_create_uri(remote->host, remote->port, remote->database)) == NULL) {
     825           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_open_remote", MAL_MALLOC_FAIL));
     826           0 :                 return -2;
     827             :         }
     828             : 
     829           1 :         MalBlkPtr mb = c->curprg->def;
     830             : 
     831           1 :         InstrPtr q = getInstrPtr(mb, 0);
     832           1 :         q->argc = q->retc = 0;
     833           1 :         q = pushReturn(mb, q, newTmpVariable(mb, TYPE_str));
     834             : 
     835           1 :         InstrPtr p = newFcnCall(mb, remoteRef, connectRef);
     836           1 :         p = pushStr(mb, p, url);
     837           1 :         p = pushStr(mb, p, remote->username);
     838           1 :         p = pushStr(mb, p, remote->password);
     839           1 :         p = pushStr(mb, p, "msql");
     840           1 :         p = pushBit(mb, p, 1);
     841             : 
     842           1 :         GDKfree(url);
     843           1 :         url = NULL;
     844             : 
     845           1 :         if (p == NULL) {
     846           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_open_remote", MAL_MALLOC_FAIL));
     847           0 :                 freeSymbol(c->curprg);
     848           0 :                 c->curprg= NULL;
     849           0 :                 return -2;
     850             :         }
     851           1 :         pushInstruction(mb, p);
     852             : 
     853           1 :         q = newInstruction(mb, NULL, NULL);
     854           1 :         if (q == NULL) {
     855           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_open_remote", MAL_MALLOC_FAIL));
     856           0 :                 freeSymbol(c->curprg);
     857           0 :                 c->curprg= NULL;
     858           0 :                 return -2;
     859             :         }
     860           1 :         q->barrier= RETURNsymbol;
     861           1 :         q = pushReturn(mb, q, getArg(p, 0));
     862             : 
     863           1 :         pushInstruction(mb, q);
     864             : 
     865           1 :         if ( (mdbe->msg = chkProgram(c->usermodule, mb)) != MAL_SUCCEED ) {
     866           0 :                 freeSymbol(c->curprg);
     867           0 :                 c->curprg= NULL;
     868           0 :                 return -2;
     869             :         }
     870           1 :         MalStkPtr stk = prepareMALstack(mb, mb->vsize);
     871           1 :         if (!stk) {
     872           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_open_remote", MAL_MALLOC_FAIL));
     873           0 :                 freeSymbol(c->curprg);
     874           0 :                 c->curprg= NULL;
     875           0 :                 return -2;
     876             :         }
     877           1 :         stk->keepAlive = TRUE;
     878           1 :         c->qryctx.starttime = GDKusec();
     879           1 :         if ( (mdbe->msg = runMALsequence(c, mb, 1, 0, stk, 0, 0)) != MAL_SUCCEED ) {
     880           0 :                 freeStack(stk);
     881           0 :                 freeSymbol(c->curprg);
     882           0 :                 c->curprg= NULL;
     883           0 :                 return -2;
     884             :         }
     885             : 
     886           1 :         if ((mdbe->mid = GDKstrdup(*getArgReference_str(stk, p, 0))) == NULL) {
     887           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_open_remote", MAL_MALLOC_FAIL));
     888           0 :                 freeStack(stk);
     889           0 :                 freeSymbol(c->curprg);
     890           0 :                 c->curprg= NULL;
     891           0 :                 return -2;
     892             :         }
     893             : 
     894           1 :         garbageCollector(c, mb, stk, TRUE);
     895           1 :         freeStack(stk);
     896             : 
     897           1 :         freeSymbol(c->curprg);
     898           1 :         c->curprg= NULL;
     899             : 
     900           1 :         return 0;
     901             : }
     902             : 
     903             : int
     904           1 : monetdbe_open(monetdbe_database *dbhdl, char *url, monetdbe_options *opts)
     905             : {
     906           1 :         int res = 0;
     907             : 
     908           1 :         if (!dbhdl)
     909             :                 return -1;
     910           1 :         if (url &&
     911           0 :                 (strcmp(url, "in-memory") == 0 ||
     912           0 :                  /* backward compatibility: */ strcmp(url, ":memory:") == 0))
     913           1 :                 url = NULL;
     914           1 :         MT_lock_set(&embedded_lock);
     915           1 :         monetdbe_database_internal *mdbe = (monetdbe_database_internal*)GDKzalloc(sizeof(monetdbe_database_internal));
     916           1 :         if (!mdbe) {
     917           0 :                 MT_lock_unset(&embedded_lock);
     918           0 :                 return -1;
     919             :         }
     920           1 :         *dbhdl = (monetdbe_database)mdbe;
     921           1 :         mdbe->msg = NULL;
     922           1 :         mdbe->c = NULL;
     923             : 
     924           1 :         bool is_remote = (opts && (opts->remote != NULL));
     925           1 :         if (!monetdbe_embedded_initialized) {
     926             :                 /* When used as a remote mapi proxy,
     927             :                  * it is still necessary to have an initialized monetdbe. E.g. for BAT life cycle management.
     928             :                  * Use an ephemeral/anonymous dbfarm when there is no initialized monetdbe yet.
     929             :                  */
     930           1 :                 assert(!is_remote||url==NULL);
     931           1 :                 monetdbe_startup(mdbe, url, opts);
     932           1 :                 mdbe->registered_thread = 2;
     933           0 :         } else if (!is_remote && !urls_matches(monetdbe_embedded_url, url)) {
     934           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_open", "monetdbe_open currently only one active database is supported"));
     935             :         }
     936           1 :         if (!mdbe->msg)
     937           1 :                 res = monetdbe_open_internal(mdbe, opts);
     938             : 
     939           1 :         if (res == 0 && is_remote && !mdbe->msg)
     940           1 :                 res = monetdbe_open_remote(mdbe, opts);
     941             : 
     942           1 :         MT_lock_unset(&embedded_lock);
     943           1 :         if (mdbe->msg)
     944           0 :                 return -2;
     945             :         return res;
     946             : }
     947             : 
     948             : int
     949           1 : monetdbe_close(monetdbe_database dbhdl)
     950             : {
     951           1 :         if (!dbhdl)
     952             :                 return 0;
     953             : 
     954           1 :         monetdbe_database_internal *mdbe = (monetdbe_database_internal*)dbhdl;
     955             : 
     956           1 :         int err = 0;
     957           1 :         int registered_thread = mdbe->registered_thread;
     958             : 
     959           1 :         if (mdbe->c)
     960           1 :                 MT_thread_set_qry_ctx(&mdbe->c->qryctx);
     961           1 :         MT_lock_set(&embedded_lock);
     962           1 :         if (mdbe->mid)
     963           1 :                 err = monetdbe_close_remote(mdbe);
     964             : 
     965           1 :         err = (monetdbe_close_internal(mdbe) || err);
     966             : 
     967           1 :         if (registered_thread == 1) {
     968           0 :                 MT_thread_deregister();
     969             :         }
     970           1 :         if (!open_dbs)
     971           1 :                 monetdbe_shutdown_internal();
     972           1 :         MT_lock_unset(&embedded_lock);
     973             : 
     974           1 :         if (err) {
     975             :                 return -2;
     976             :         }
     977             : 
     978             :         return 0;
     979             : }
     980             : 
     981             : char *
     982           0 : monetdbe_error(monetdbe_database dbhdl)
     983             : {
     984           0 :         if (!dbhdl)
     985             :                 return NULL;
     986             : 
     987           0 :         monetdbe_database_internal *mdbe = (monetdbe_database_internal*)dbhdl;
     988           0 :         return mdbe->msg;
     989             : }
     990             : 
     991             : char*
     992           0 : monetdbe_dump_database(monetdbe_database dbhdl, const char *filename)
     993             : {
     994           0 :         if (!dbhdl)
     995             :                 return NULL;
     996             : 
     997           0 :         monetdbe_database_internal *mdbe = (monetdbe_database_internal*)dbhdl;
     998             : 
     999           0 :         if (mdbe->mid) {
    1000           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_dump_database", PROGRAM_NYI));
    1001           0 :                 return mdbe->msg;
    1002             :         }
    1003             : 
    1004           0 :         if ((mdbe->msg = validate_database_handle(mdbe, "embedded.monetdbe_dump_database")) != MAL_SUCCEED) {
    1005             :                 return mdbe->msg;
    1006             :         }
    1007             : 
    1008           0 :         mdbe->msg = monetdbe_mapi_dump_database(dbhdl, filename);
    1009             : 
    1010           0 :         return mdbe->msg;
    1011             : }
    1012             : 
    1013             : char*
    1014           0 : monetdbe_dump_table(monetdbe_database dbhdl, const char *sname, const char *tname, const char *filename)
    1015             : {
    1016           0 :         if (!dbhdl)
    1017             :                 return NULL;
    1018             : 
    1019           0 :         monetdbe_database_internal *mdbe = (monetdbe_database_internal*)dbhdl;
    1020             : 
    1021           0 :         if (mdbe->mid) {
    1022           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_dump_database", PROGRAM_NYI));
    1023           0 :                 return mdbe->msg;
    1024             :         }
    1025             : 
    1026           0 :         if ((mdbe->msg = validate_database_handle(mdbe, "embedded.monetdbe_dump_table")) != MAL_SUCCEED) {
    1027             : 
    1028             :                 return mdbe->msg;
    1029             :         }
    1030             : 
    1031           0 :         mdbe->msg = monetdbe_mapi_dump_table(dbhdl, sname, tname, filename);
    1032             : 
    1033           0 :         return mdbe->msg;
    1034             : }
    1035             : 
    1036             : char*
    1037           0 : monetdbe_get_autocommit(monetdbe_database dbhdl, int* result)
    1038             : {
    1039           0 :         if (!dbhdl)
    1040             :                 return NULL;
    1041             : 
    1042           0 :         monetdbe_database_internal *mdbe = (monetdbe_database_internal*)dbhdl;
    1043             : 
    1044           0 :         if ((mdbe->msg = validate_database_handle(mdbe, "monetdbe.monetdbe_get_autocommit")) != MAL_SUCCEED) {
    1045             : 
    1046             :                 return mdbe->msg;
    1047             :         }
    1048             : 
    1049           0 :         if (!result) {
    1050           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_get_autocommit", "Parameter result is NULL"));
    1051             :         } else {
    1052           0 :                 mvc *m = ((backend *) mdbe->c->sqlcontext)->mvc;
    1053           0 :                 *result = m->session->auto_commit;
    1054             :         }
    1055             : 
    1056           0 :         return mdbe->msg;
    1057             : }
    1058             : 
    1059             : char*
    1060           0 : monetdbe_set_autocommit(monetdbe_database dbhdl, int value)
    1061             : {
    1062           0 :         if (!dbhdl)
    1063             :                 return NULL;
    1064             : 
    1065           0 :         monetdbe_database_internal *mdbe = (monetdbe_database_internal*)dbhdl;
    1066             : 
    1067           0 :         if (!validate_database_handle_noerror(mdbe)) {
    1068             : 
    1069             :                 return NULL;
    1070             :         }
    1071             : 
    1072           0 :         mvc *m = ((backend *) mdbe->c->sqlcontext)->mvc;
    1073           0 :         int commit = !m->session->auto_commit && value;
    1074             : 
    1075           0 :         m->session->auto_commit = value;
    1076           0 :         m->session->ac_on_commit = m->session->auto_commit;
    1077           0 :         if (m->session->tr->active) {
    1078           0 :                 if (commit) {
    1079           0 :                         mdbe->msg = mvc_commit(m, 0, NULL, true);
    1080             :                 } else {
    1081           0 :                         mdbe->msg = mvc_rollback(m, 0, NULL, true);
    1082             :                 }
    1083             :         }
    1084             : 
    1085           0 :         return mdbe->msg;
    1086             : }
    1087             : 
    1088             : int
    1089           0 : monetdbe_in_transaction(monetdbe_database dbhdl)
    1090             : {
    1091           0 :         if (!dbhdl)
    1092             :                 return 0;
    1093           0 :         monetdbe_database_internal *mdbe = (monetdbe_database_internal*)dbhdl;
    1094             : 
    1095           0 :         if (!validate_database_handle_noerror(mdbe)) {
    1096             : 
    1097             :                 return 0;
    1098             :         }
    1099             : 
    1100           0 :         mvc *m = ((backend *) mdbe->c->sqlcontext)->mvc;
    1101           0 :         int result = 0;
    1102             : 
    1103           0 :         if (m->session->tr)
    1104           0 :                 result = m->session->tr->active;
    1105             : 
    1106             :         return result;
    1107             : }
    1108             : 
    1109             : struct callback_context {
    1110             :         monetdbe_database_internal *mdbe;
    1111             : };
    1112             : 
    1113             : static str
    1114           1 : monetdbe_set_remote_results(backend *be, char* tblname, columnar_result* results, size_t nr_results) {
    1115             : 
    1116           1 :         char* error = NULL;
    1117             : 
    1118           1 :         if (nr_results == 0)
    1119             :                 return MAL_SUCCEED; // No work to do.
    1120             : 
    1121           1 :         BAT* b_0 = BATdescriptor(results[0].id); // Fetch the first column to get the count.
    1122           1 :         if (!b_0) {
    1123           0 :                 error = createException(MAL,"monetdbe.monetdbe_set_remote_results",SQLSTATE(HY005) "Cannot access column descriptor ");
    1124           0 :                 return error;
    1125             :         }
    1126             : 
    1127           1 :         int res = mvc_result_table(be, 0, (int) nr_results, Q_TABLE);
    1128           1 :         if (res < 0) {
    1129           0 :                 BBPunfix(b_0->batCacheid);
    1130           0 :                 error = createException(MAL,"monetdbe.monetdbe_set_remote_results",SQLSTATE(HY005) "Cannot create result table");
    1131           0 :                 return error;
    1132             :         }
    1133             : 
    1134           3 :         for (size_t i = 0; i < nr_results; i++) {
    1135           2 :                 BAT *b = NULL;
    1136           2 :                 if (i > 0)
    1137           1 :                         b = BATdescriptor(results[i].id);
    1138             :                 else
    1139             :                         b = b_0; // We already fetched this first column
    1140             : 
    1141           2 :                 char* colname   = results[i].colname;
    1142           2 :                 char* tpename   = results[i].tpename;
    1143           2 :                 int digits              = results[i].digits;
    1144           2 :                 int scale               = results[i].scale;
    1145             : 
    1146           2 :                 if (b == NULL) {
    1147           0 :                         error = createException(MAL,"monetdbe.monetdbe_result_cb",SQLSTATE(HY005) "Cannot access column descriptor");
    1148           0 :                         break;
    1149             :                 }
    1150             : 
    1151           2 :                 int res = mvc_result_column(be, tblname, colname, tpename, digits, scale, b);
    1152           2 :                 BBPunfix(b->batCacheid);
    1153           2 :                 if (res) {
    1154           0 :                         error = createException(MAL,"monetdbe.monetdbe_result_cb", SQLSTATE(42000) "Cannot access column descriptor %s.%s",tblname,colname);
    1155           0 :                         break;
    1156             :                 }
    1157             :         }
    1158             : 
    1159           0 :         if (error)
    1160           0 :                 res_tables_destroy(be->results);
    1161             :         return error;
    1162             : }
    1163             : 
    1164             : static str
    1165           1 : monetdbe_result_cb(void* context, char* tblname, columnar_result* results, size_t nr_results) {
    1166           1 :         monetdbe_database_internal *mdbe = ((struct callback_context*) context)->mdbe;
    1167             : 
    1168           1 :         backend *be = NULL;
    1169           1 :         if ((mdbe->msg = getBackendContext(mdbe->c, &be)) != NULL)
    1170             :                 return mdbe->msg;
    1171             : 
    1172           1 :         return monetdbe_set_remote_results(be, tblname, results, nr_results);
    1173             : }
    1174             : 
    1175             : struct prepare_callback_context {
    1176             :         int* prepare_id;
    1177             :         monetdbe_database_internal *mdbe;
    1178             : };
    1179             : 
    1180             : static str
    1181           0 : monetdbe_prepare_cb(void* context, char* tblname, columnar_result* results, size_t nr_results) {
    1182           0 :         (void) tblname;
    1183           0 :         monetdbe_database_internal *mdbe        = ((struct prepare_callback_context*) context)->mdbe;
    1184           0 :         int *prepare_id                                         = ((struct prepare_callback_context*) context)->prepare_id;
    1185             : 
    1186           0 :         if (nr_results != 7) // 1) btype 2) bdigits 3) bscale 4) bschema 5) btable 6) bcolumn 7) bimpl
    1187           0 :                 return createException(SQL, "monetdbe.monetdbe_prepare_cb", SQLSTATE(42000) "result table for prepared statement is wrong.");
    1188             : 
    1189           0 :         backend *be = NULL;
    1190           0 :         if ((mdbe->msg = getBackendContext(mdbe->c, &be)) != NULL)
    1191             :                 return mdbe->msg;
    1192             : 
    1193           0 :         if ( (mdbe->msg =  monetdbe_set_remote_results(be, tblname, results, nr_results)) != NULL)
    1194             :                 return mdbe->msg;
    1195             : 
    1196           0 :         BAT* btype = NULL;
    1197           0 :         BAT* bdigits = NULL;
    1198           0 :         BAT* bscale = NULL;
    1199           0 :         BAT* bschema = NULL;
    1200           0 :         BAT* btable = NULL;
    1201           0 :         BAT* bcolumn = NULL;
    1202           0 :         BAT* bimpl = NULL;
    1203             : 
    1204           0 :         size_t nparams = 0;
    1205           0 :         BATiter btype_iter = {0};
    1206           0 :         BATiter bcolumn_iter = {0};
    1207           0 :         BATiter btable_iter = {0};
    1208           0 :         BATiter bimpl_iter = {0};
    1209           0 :         char* function = NULL;
    1210           0 :         Symbol prg = NULL;
    1211           0 :         MalBlkPtr mb = NULL;
    1212           0 :         InstrPtr o = NULL, e = NULL, r = NULL;
    1213           0 :         sql_rel* rel = NULL;
    1214           0 :         list *args = NULL, *rets = NULL;
    1215           0 :         sql_allocator* sa = NULL;
    1216           0 :         ValRecord v = { .len=0 };
    1217           0 :         ptr vp = NULL;
    1218           0 :         struct callback_context* ccontext= NULL;
    1219           0 :         columnar_result_callback* rcb = NULL;
    1220             : 
    1221           0 :         str msg = MAL_SUCCEED;
    1222           0 :         if (!(btype             = BATdescriptor(results[0].id)) ||
    1223           0 :                 !(bdigits       = BATdescriptor(results[1].id)) ||
    1224           0 :                 !(bscale        = BATdescriptor(results[2].id)) ||
    1225           0 :                 !(bschema       = BATdescriptor(results[3].id)) ||
    1226           0 :                 !(btable        = BATdescriptor(results[4].id)) ||
    1227           0 :                 !(bcolumn       = BATdescriptor(results[5].id)) ||
    1228           0 :                 !(bimpl         = BATdescriptor(results[6].id)))
    1229             :         {
    1230           0 :                 msg = createException(SQL, "monetdbe.monetdbe_prepare_cb", SQLSTATE(HY005) "Cannot access column descriptor");
    1231           0 :                 goto cleanup;
    1232             :         }
    1233             : 
    1234           0 :         nparams = BATcount(btype);
    1235             : 
    1236           0 :         if (nparams     != BATcount(bdigits) ||
    1237           0 :                 nparams         != BATcount(bimpl) ||
    1238           0 :                 nparams         != BATcount(bscale) ||
    1239           0 :                 nparams         != BATcount(bschema) ||
    1240           0 :                 nparams         != BATcount(btable) ||
    1241           0 :                 nparams         != BATcount(bcolumn))
    1242             :         {
    1243           0 :                 msg = createException(SQL, "monetdbe.monetdbe_prepare_cb", SQLSTATE(42000) "Prepare results are incorrect");
    1244           0 :                 goto cleanup;
    1245             :         }
    1246             : 
    1247           0 :         btype_iter              = bat_iterator(btype);
    1248           0 :         bcolumn_iter            = bat_iterator(bcolumn);
    1249           0 :         btable_iter             = bat_iterator(btable);
    1250           0 :         bimpl_iter              = bat_iterator(bimpl);
    1251           0 :         function                =  BUNtvar(btable_iter, BATcount(btable)-1);
    1252             : 
    1253             :         {
    1254           0 :                 assert (((backend*)  mdbe->c->sqlcontext)->remote < INT_MAX);
    1255           0 :                 char nme[16]            = {0};
    1256           0 :                 const char* name        = number2name(nme, sizeof(nme), ++((backend*)  mdbe->c->sqlcontext)->remote);
    1257           0 :                 prg                                     = newFunctionArgs(userRef, putName(name), FUNCTIONsymbol, (int) nparams + 1);
    1258             :         }
    1259             : 
    1260           0 :         resizeMalBlk(prg->def, (int) nparams + 3 /*function declaration + remote.exec + return statement*/);
    1261           0 :         mb = prg->def;
    1262             : 
    1263           0 :         o = getInstrPtr(mb, 0);
    1264           0 :         o->retc = o->argc = 0;
    1265             : 
    1266           0 :         e = newInstructionArgs(mb, remoteRef, execRef, (int)(nparams + 5));
    1267           0 :         if (e == NULL) {
    1268           0 :                 msg = createException(MAL, "monetdbe.monetdbe_prepare_cb", MAL_MALLOC_FAIL);
    1269           0 :                 goto cleanup;
    1270             :         }
    1271           0 :         setDestVar(e, newTmpVariable(mb, TYPE_any));
    1272           0 :         e = pushStr(mb, e, mdbe->mid);
    1273           0 :         e = pushStr(mb, e, userRef);
    1274           0 :         e = pushStr(mb, e, function);
    1275             : 
    1276           0 :         rcb = GDKmalloc(sizeof(columnar_result_callback));
    1277           0 :         if (rcb == NULL) {
    1278           0 :                 msg = createException(MAL, "monetdbe.monetdbe_prepare_cb", MAL_MALLOC_FAIL);
    1279           0 :                 goto cleanup;
    1280             :         }
    1281             : 
    1282           0 :         ccontext = GDKzalloc(sizeof(struct callback_context));
    1283           0 :         if (ccontext == NULL) {
    1284           0 :                 msg = createException(MAL, "monetdbe.monetdbe_prepare_cb", MAL_MALLOC_FAIL);
    1285           0 :                 goto cleanup;
    1286             :         }
    1287             : 
    1288           0 :         ccontext->mdbe = mdbe;
    1289             : 
    1290           0 :         rcb->context = ccontext;
    1291           0 :         rcb->call = monetdbe_result_cb;
    1292             : 
    1293           0 :         vp = (ptr) rcb;
    1294             : 
    1295           0 :         VALset(&v, TYPE_ptr, &vp);
    1296           0 :         e = pushValue(mb, e, &v);
    1297             : 
    1298           0 :         r = newInstruction(mb, NULL, NULL);
    1299           0 :         if (r == NULL) {
    1300           0 :                 msg = createException(MAL, "monetdbe.monetdbe_prepare_cb", MAL_MALLOC_FAIL);
    1301           0 :                 goto cleanup;
    1302             :         }
    1303           0 :         r->barrier= RETURNsymbol;
    1304           0 :         r->argc= r->retc=0;
    1305             : 
    1306           0 :         sa = be->mvc->sa;
    1307             : 
    1308           0 :         args = new_exp_list(sa);
    1309           0 :         rets = new_exp_list(sa);
    1310             : 
    1311           0 :         for (size_t i = 0; i < nparams; i++) {
    1312             : 
    1313           0 :                 const char *table       = BUNtvar(btable_iter, i);
    1314           0 :                 sql_type *t = SA_ZNEW(sa, sql_type);
    1315           0 :                 const char *name = BUNtvar(btype_iter, i);
    1316           0 :                 t->base.name = SA_STRDUP(sa, name);
    1317           0 :                 const char *impl = BUNtvar(bimpl_iter, i);
    1318           0 :                 t->impl      = SA_STRDUP(sa, impl);
    1319           0 :                 t->localtype = ATOMindex(t->impl);
    1320             : 
    1321           0 :                 sql_subtype *st = SA_ZNEW(sa, sql_subtype);
    1322           0 :                 sql_init_subtype(st, t, (unsigned) *(int*) Tloc(bdigits, i), (unsigned) *(int*) Tloc(bscale, i));
    1323             : 
    1324           0 :                 if (strNil(table)) {
    1325             :                         // input argument
    1326             : 
    1327           0 :                         sql_arg *a = SA_ZNEW(sa, sql_arg);
    1328           0 :                         a->type = *st;
    1329           0 :                         append(args, a);
    1330             : 
    1331           0 :                         int idx = newVariable(mb, NULL, 0, t->localtype);
    1332           0 :                         o = pushArgument(mb, o, idx);
    1333             : 
    1334           0 :                         InstrPtr p = newFcnCall(mb, remoteRef, putRef);
    1335           0 :                         if (p == NULL) {
    1336           0 :                                 msg = createException(MAL, "monetdbe.monetdbe_prepare_cb", MAL_MALLOC_FAIL);
    1337           0 :                                 goto cleanup;
    1338             :                         }
    1339           0 :                         setArgType(mb, p, 0, TYPE_str);
    1340           0 :                         p = pushStr(mb, p, mdbe->mid);
    1341           0 :                         p = pushArgument(mb, p, idx);
    1342           0 :                         pushInstruction(mb, p);
    1343             : 
    1344           0 :                         e = pushArgument(mb, e, getArg(p, 0));
    1345             :                 }
    1346             :                 else {
    1347             :                         // output argument
    1348             : 
    1349           0 :                         const char *column = BUNtvar(bcolumn_iter, i);
    1350           0 :                         sql_exp * c = exp_column(sa, table, column, st, CARD_MULTI, true, false, false);
    1351           0 :                         append(rets, c);
    1352             :                 }
    1353             :         }
    1354           0 :         pushInstruction(mb, e);
    1355           0 :         pushInstruction(mb, r);
    1356           0 :         e = r = NULL;
    1357             : 
    1358           0 :         if ( (mdbe->msg = chkProgram(mdbe->c->usermodule, mb)) != MAL_SUCCEED ) {
    1359           0 :                 msg = mdbe->msg;
    1360           0 :                 goto cleanup;
    1361             :         }
    1362             : 
    1363           0 :         rel = rel_project(sa, NULL, rets);
    1364           0 :         be->q = qc_insert(be->mvc->qc, sa, rel, NULL, args, be->mvc->type, NULL, be->no_mitosis);
    1365           0 :         *prepare_id = be->q->id;
    1366             : 
    1367             :         /*
    1368             :          * HACK: we need to rename the Symbol aka MAL function to the query cache name.
    1369             :          * Basically we keep the MALblock but we destroy the containing old Symbol
    1370             :          * and create a new one with the correct name and set its MAL block pointer to
    1371             :          * point to the mal block we have created in this function.
    1372             :          */
    1373           0 :         prg->def = NULL;
    1374           0 :         freeSymbol(prg);
    1375           0 :         if ((prg = newFunctionArgs(userRef, putName(be->q->name), FUNCTIONsymbol, -1)) == NULL) {
    1376           0 :                 msg = createException(MAL, "monetdbe.monetdbe_prepare_cb", MAL_MALLOC_FAIL);
    1377           0 :                 goto cleanup;
    1378             :         }
    1379           0 :         freeMalBlk(prg->def);
    1380           0 :         prg->def = mb;
    1381           0 :         setFunctionId(getSignature(prg), be->q->name);
    1382             : 
    1383             :         // finally add this beautiful new function to the local user module.
    1384           0 :         insertSymbol(mdbe->c->usermodule, prg);
    1385             : 
    1386           0 : cleanup:
    1387           0 :         freeInstruction(e);
    1388           0 :         freeInstruction(r);
    1389           0 :         if (bcolumn) {
    1390           0 :                 bat_iterator_end(&btype_iter);
    1391           0 :                 bat_iterator_end(&bcolumn_iter);
    1392           0 :                 bat_iterator_end(&btable_iter);
    1393           0 :                 bat_iterator_end(&bimpl_iter);
    1394             :         }
    1395             :         // clean these up
    1396           0 :         BBPreclaim(btype);
    1397           0 :         BBPreclaim(bimpl);
    1398           0 :         BBPreclaim(bdigits);
    1399           0 :         BBPreclaim(bscale);
    1400           0 :         BBPreclaim(bschema);
    1401           0 :         BBPreclaim(btable);
    1402           0 :         BBPreclaim(bcolumn);
    1403             : 
    1404           0 :         if (msg && rcb) GDKfree(rcb);
    1405           0 :         if (msg && ccontext) GDKfree(ccontext);
    1406             : 
    1407             :         return msg;
    1408             : }
    1409             : 
    1410             : static char*
    1411           1 : monetdbe_query_remote(monetdbe_database_internal *mdbe, char* query, monetdbe_result** result, monetdbe_cnt* affected_rows, int *prepare_id)
    1412             : {
    1413           1 :         const char *mod = "user";
    1414           1 :         char nme[16];
    1415             : 
    1416           1 :         Client c = mdbe->c;
    1417             : 
    1418           1 :         const char *name = number2name(nme, sizeof(nme), ++((backend*)  c->sqlcontext)->remote);
    1419           1 :         Symbol prg = newFunction(putName(mod), putName(name), FUNCTIONsymbol);
    1420             : 
    1421           1 :         if (prg == NULL) {
    1422           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_query_remote", MAL_MALLOC_FAIL));
    1423           0 :                 return mdbe->msg;
    1424             :         }
    1425             : 
    1426           1 :         MalBlkPtr mb = prg->def;
    1427           1 :         ValRecord v;
    1428           1 :         ptr vp;
    1429           1 :         columnar_result_callback* rcb;
    1430           1 :         InstrPtr f = getInstrPtr(mb, 0), r, p, e, o;
    1431             : 
    1432           1 :         f->retc = f->argc = 0;
    1433           1 :         o = newStmt(mb, remoteRef, putRef);
    1434           1 :         o = pushStr(mb, o, mdbe->mid);
    1435           1 :         o = pushBit(mb, o, TRUE);
    1436           1 :         if (o == NULL) {
    1437           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_query_remote", MAL_MALLOC_FAIL));
    1438           0 :                 goto finalize;
    1439             :         }
    1440           1 :         pushInstruction(mb, o);
    1441             : 
    1442           1 :         if (prepare_id) {
    1443           0 :                 size_t query_len, input_query_len, prep_len = 0;
    1444           0 :                 input_query_len = strlen(query);
    1445           0 :                 query_len = input_query_len + 3;
    1446           0 :                 const char PREPARE[] = "PREPARE ";
    1447           0 :                 prep_len = sizeof(PREPARE)-1;
    1448           0 :                 query_len += prep_len;
    1449           0 :                 char *nq = NULL;
    1450           0 :                 if (!(nq = GDKmalloc(query_len))) {
    1451           0 :                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_query_remote", "Could not setup query stream"));
    1452           0 :                         goto finalize;
    1453             :                 }
    1454           0 :                 strcpy(nq, PREPARE);
    1455           0 :                 strcpy(nq + prep_len, query);
    1456           0 :                 strcpy(nq + prep_len + input_query_len, "\n;");
    1457           0 :                 query = nq;
    1458             :         }
    1459             : 
    1460           1 :         p = newStmt(mb, remoteRef, putRef);
    1461           1 :         p = pushStr(mb, p, mdbe->mid);
    1462           1 :         p = pushStr(mb, p, query);
    1463           1 :         if (p == NULL) {
    1464           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_query_remote", MAL_MALLOC_FAIL));
    1465           0 :                 goto finalize;
    1466             :         }
    1467           1 :         pushInstruction(mb, p);
    1468             : 
    1469             : 
    1470           1 :         e = newInstruction(mb, remoteRef, execRef);
    1471           1 :         if (e == NULL) {
    1472           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_query_remote", MAL_MALLOC_FAIL));
    1473           0 :                 goto finalize;
    1474             :         }
    1475           1 :         setDestVar(e, newTmpVariable(mb, TYPE_any));
    1476           1 :         e = pushStr(mb, e, mdbe->mid);
    1477           1 :         e = pushStr(mb, e, sqlRef);
    1478           1 :         e = pushStr(mb, e, evalRef);
    1479             : 
    1480             :         /*
    1481             :          * prepare the call back routine and its context
    1482             :          * and pass it over as a pointer to remote.exec.
    1483             :          */
    1484           1 :         rcb = GDKzalloc(sizeof(columnar_result_callback));
    1485           1 :         if (!rcb) {
    1486           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_query_remote", "Could not setup query stream"));
    1487           0 :                 goto finalize;
    1488             :         }
    1489             : 
    1490           1 :         if (!prepare_id) {
    1491           1 :                 struct callback_context* ccontext;
    1492           1 :                 ccontext = GDKzalloc(sizeof(struct callback_context));
    1493           1 :                 if (ccontext)
    1494           1 :                         ccontext->mdbe = mdbe;
    1495           1 :                 rcb->context = ccontext;
    1496           1 :                 rcb->call = monetdbe_result_cb;
    1497             :         }
    1498             :         else {
    1499           0 :                 struct prepare_callback_context* ccontext;
    1500           0 :                 ccontext = GDKzalloc(sizeof(struct prepare_callback_context));
    1501           0 :                 if (ccontext) {
    1502           0 :                         ccontext->mdbe = mdbe;
    1503           0 :                         ccontext->prepare_id = prepare_id;
    1504             :                 }
    1505           0 :                 rcb->context = ccontext;
    1506           0 :                 rcb->call = monetdbe_prepare_cb;
    1507             :         }
    1508           1 :         if (!rcb->context) {
    1509           0 :                 GDKfree(rcb);
    1510           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_query_remote", "Could not setup query stream"));
    1511           0 :                 goto finalize;
    1512             :         }
    1513             : 
    1514           1 :         vp = (ptr) rcb;
    1515           1 :         VALset(&v, TYPE_ptr, &vp);
    1516           1 :         e = pushValue(mb, e, &v);
    1517             : 
    1518           1 :         e = pushArgument(mb, e, getArg(p, 0));
    1519           1 :         e = pushArgument(mb, e, getArg(o, 0));
    1520             : 
    1521           1 :         pushInstruction(mb, e);
    1522             : 
    1523           1 :         r = newInstruction(mb, NULL, NULL);
    1524           1 :         if (r == NULL) {
    1525           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_query_remote", MAL_MALLOC_FAIL));
    1526           0 :                 goto finalize;
    1527             :         }
    1528           1 :         r->barrier= RETURNsymbol;
    1529           1 :         r->argc= r->retc=0;
    1530           1 :         pushInstruction(mb, r);
    1531             : 
    1532           1 :         if ( (mdbe->msg = chkProgram(c->usermodule, mb)) != MAL_SUCCEED )
    1533           0 :                 goto finalize;
    1534             : 
    1535           1 :         if ( (mdbe->msg = runMAL(c, mb, 0, NULL)) != MAL_SUCCEED )
    1536           0 :                 goto finalize;
    1537             : 
    1538           1 :         if (result) {
    1539           1 :                 if ((mdbe->msg = monetdbe_get_results(result, mdbe)) != MAL_SUCCEED)
    1540           0 :                         goto finalize;
    1541             : 
    1542           1 :                 mvc* m = NULL;
    1543           1 :                 backend * be = NULL;
    1544           1 :                 if ((mdbe->msg = getSQLContext(c, NULL, &m, &be)) != MAL_SUCCEED)
    1545           0 :                         goto finalize;
    1546             : 
    1547           1 :                 if (m->emode & m_prepare)
    1548           0 :                         ((monetdbe_result_internal*) *result)->type = Q_PREPARE;
    1549             :                 else
    1550           1 :                         ((monetdbe_result_internal*) *result)->type = (be->results) ? be->results->query_type : m->type;
    1551             : 
    1552           1 :                 if (!be->results && be->rowcnt >= 0 && affected_rows)
    1553           0 :                         *affected_rows = be->rowcnt;
    1554             :         }
    1555             : 
    1556           0 : finalize:
    1557           1 :         freeSymbol(prg);
    1558           1 :         return mdbe->msg;
    1559             : }
    1560             : 
    1561             : char*
    1562           1 : monetdbe_query(monetdbe_database dbhdl, char* query, monetdbe_result** result, monetdbe_cnt* affected_rows)
    1563             : {
    1564           1 :         if (!dbhdl)
    1565             :                 return NULL;
    1566           1 :         monetdbe_database_internal *mdbe = (monetdbe_database_internal*)dbhdl;
    1567             : 
    1568           1 :         assert(mdbe->c);
    1569           1 :         MT_thread_set_qry_ctx(&mdbe->c->qryctx);
    1570           1 :         if (mdbe->mid) {
    1571           1 :                 mdbe->msg = monetdbe_query_remote(mdbe, query, result, affected_rows, NULL);
    1572             :         }
    1573             :         else {
    1574           0 :                 mdbe->msg = monetdbe_query_internal(mdbe, query, result, affected_rows, NULL, 'S');
    1575             :         }
    1576             : 
    1577           1 :         return mdbe->msg;
    1578             : }
    1579             : 
    1580             : char*
    1581           0 : monetdbe_prepare(monetdbe_database dbhdl, char* query, monetdbe_statement **stmt, monetdbe_result** result)
    1582             : {
    1583           0 :         if (!dbhdl)
    1584             :                 return NULL;
    1585           0 :         monetdbe_database_internal *mdbe = (monetdbe_database_internal*)dbhdl;
    1586             : 
    1587           0 :         int prepare_id = 0;
    1588             : 
    1589           0 :         assert(mdbe->c);
    1590           0 :         MT_thread_set_qry_ctx(&mdbe->c->qryctx);
    1591           0 :         if (!stmt) {
    1592           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_prepare", "Parameter stmt is NULL"));
    1593           0 :                 assert(mdbe->msg != MAL_SUCCEED); /* help Coverity */
    1594           0 :         } else if (mdbe->mid) {
    1595           0 :                 mdbe->msg = monetdbe_query_remote(mdbe, query, result, NULL, &prepare_id);
    1596             :         } else {
    1597           0 :                 *stmt = NULL;
    1598           0 :                 mdbe->msg = monetdbe_query_internal(mdbe, query, result, NULL, &prepare_id, 'S');
    1599             :         }
    1600             : 
    1601           0 :         if (mdbe->msg == MAL_SUCCEED) {
    1602           0 :                 mvc *m = ((backend *) mdbe->c->sqlcontext)->mvc;
    1603           0 :                 monetdbe_stmt_internal *stmt_internal = (monetdbe_stmt_internal*)GDKzalloc(sizeof(monetdbe_stmt_internal));
    1604           0 :                 cq *q = qc_find(m->qc, prepare_id);
    1605             : 
    1606           0 :                 if (q && stmt_internal) {
    1607           0 :                         Symbol s = findSymbolInModule(mdbe->c->usermodule, q->f->imp);
    1608           0 :                         InstrPtr p = s->def->stmt[0];
    1609           0 :                         stmt_internal->mdbe = mdbe;
    1610           0 :                         stmt_internal->q = q;
    1611           0 :                         stmt_internal->retc = p->retc;
    1612           0 :                         stmt_internal->res.nparam = list_length(q->f->ops);
    1613           0 :                         stmt_internal->args = (ValPtr*)GDKmalloc(sizeof(ValPtr) * (stmt_internal->res.nparam + stmt_internal->retc));
    1614           0 :                         stmt_internal->data = (ValRecord*)GDKzalloc(sizeof(ValRecord) * (stmt_internal->res.nparam+1));
    1615           0 :                         stmt_internal->res.type = (monetdbe_types*)GDKmalloc(sizeof(monetdbe_types) * (stmt_internal->res.nparam+1));
    1616           0 :                         if (!stmt_internal->res.type || !stmt_internal->data || !stmt_internal->args) {
    1617           0 :                                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_prepare", MAL_MALLOC_FAIL));
    1618           0 :                         } else if (q->f->ops) {
    1619           0 :                                 int i = 0;
    1620           0 :                                 for (node *n = q->f->ops->h; n; n = n->next, i++) {
    1621           0 :                                         sql_arg *a = n->data;
    1622           0 :                                         sql_subtype *t = &a->type;
    1623           0 :                                         stmt_internal->res.type[i] = embedded_type(t->type->localtype);
    1624           0 :                                         stmt_internal->args[i+stmt_internal->retc] = &stmt_internal->data[i];
    1625             :                                 }
    1626             :                         }
    1627           0 :                 } else if (!stmt_internal)
    1628           0 :                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_prepare", MAL_MALLOC_FAIL));
    1629             : 
    1630           0 :                 if (mdbe->msg == MAL_SUCCEED)
    1631           0 :                         *stmt = (monetdbe_statement*)stmt_internal;
    1632           0 :                 else if (stmt_internal) {
    1633           0 :                         GDKfree(stmt_internal->data);
    1634           0 :                         GDKfree(stmt_internal->args);
    1635           0 :                         GDKfree(stmt_internal->res.type);
    1636           0 :                         GDKfree(stmt_internal);
    1637           0 :                         *stmt = NULL;
    1638             :                 }
    1639             :         }
    1640             : 
    1641           0 :         return mdbe->msg;
    1642             : }
    1643             : 
    1644             : char*
    1645           0 : monetdbe_bind(monetdbe_statement *stmt, void *data, size_t i)
    1646             : {
    1647           0 :         monetdbe_stmt_internal *stmt_internal = (monetdbe_stmt_internal*)stmt;
    1648             : 
    1649           0 :         if (i >= stmt->nparam)
    1650           0 :                 return createException(MAL, "monetdbe.monetdbe_bind", "Parameter %zu not bound to a value", i);
    1651           0 :         sql_arg *a = (sql_arg*)list_fetch(stmt_internal->q->f->ops, (int) i);
    1652           0 :         assert(a);
    1653           0 :         int tpe = a->type.type->localtype;
    1654           0 :         stmt_internal->data[i].vtype = tpe;
    1655             : 
    1656           0 :         const void* nil = (tpe>=0)?ATOMnilptr(tpe):NULL;
    1657           0 :         if (!data) {
    1658           0 :                 VALset(&stmt_internal->data[i], tpe, (ptr)nil);
    1659           0 :         } else if (tpe == TYPE_timestamp) {
    1660           0 :                 monetdbe_data_timestamp* ts = (monetdbe_data_timestamp*)data;
    1661           0 :                 timestamp t = *(timestamp*) nil;
    1662           0 :                 if(!timestamp_is_null(ts))
    1663           0 :                         t = timestamp_from_data(ts);
    1664           0 :                 VALset(&stmt_internal->data[i], tpe, &t);
    1665             :         } else if (tpe == TYPE_date) {
    1666           0 :                 monetdbe_data_date* de = (monetdbe_data_date*)data;
    1667           0 :                 date d = *(date*) nil;
    1668           0 :                 if(!date_is_null(de))
    1669           0 :                         d = date_from_data(de);
    1670           0 :                 VALset(&stmt_internal->data[i], tpe, &d);
    1671             :         } else if (tpe == TYPE_daytime) {
    1672           0 :                 monetdbe_data_time* t = (monetdbe_data_time*)data;
    1673           0 :                 daytime dt = *(daytime*) nil;
    1674             : 
    1675           0 :                 if(!time_is_null(t))
    1676           0 :                         dt = time_from_data(t);
    1677           0 :                 VALset(&stmt_internal->data[i], tpe, &dt);
    1678             :         } else if (tpe == TYPE_blob) {
    1679           0 :                 monetdbe_data_blob *be = (monetdbe_data_blob*)data;
    1680           0 :                 blob *b = (blob*)nil;
    1681           0 :                 if (!blob_is_null(be)) {
    1682           0 :                         size_t len = be->size;
    1683           0 :                         b = (blob*) GDKmalloc(blobsize(len));
    1684           0 :                         if (b == NULL) {
    1685           0 :                                 set_error(stmt_internal->mdbe, createException(MAL, "monetdbe.monetdbe_bind", MAL_MALLOC_FAIL));
    1686           0 :                                 return stmt_internal->mdbe->msg;
    1687             :                         }
    1688           0 :                         b->nitems = len;
    1689           0 :                         memcpy(b->data, be->data, len);
    1690             :                 }
    1691           0 :                 VALset(&stmt_internal->data[i], tpe, b);
    1692             :         } else if (tpe == TYPE_str) {
    1693           0 :                 char *val = GDKstrdup(data);
    1694             : 
    1695           0 :                 if (val == NULL) {
    1696           0 :                         set_error(stmt_internal->mdbe, createException(MAL, "monetdbe.monetdbe_bind", MAL_MALLOC_FAIL));
    1697           0 :                         return stmt_internal->mdbe->msg;
    1698             :                 }
    1699           0 :                 VALset(&stmt_internal->data[i], tpe, val);
    1700             :         } else {
    1701           0 :                 VALset(&stmt_internal->data[i], tpe, data);
    1702             :         }
    1703             :         return MAL_SUCCEED;
    1704             : }
    1705             : 
    1706             : char*
    1707           0 : monetdbe_execute(monetdbe_statement *stmt, monetdbe_result **result, monetdbe_cnt *affected_rows)
    1708             : {
    1709           0 :         monetdbe_result_internal *res_internal = NULL;
    1710           0 :         monetdbe_stmt_internal *stmt_internal = (monetdbe_stmt_internal*)stmt;
    1711           0 :         backend *b = (backend *) stmt_internal->mdbe->c->sqlcontext;
    1712           0 :         mvc *m = b->mvc;
    1713           0 :         monetdbe_database_internal *mdbe = stmt_internal->mdbe;
    1714           0 :         MalStkPtr glb = NULL;
    1715           0 :         cq *q = stmt_internal->q;
    1716           0 :         Symbol s = NULL;
    1717             : 
    1718           0 :         assert(mdbe->c);
    1719           0 :         MT_thread_set_qry_ctx(&mdbe->c->qryctx);
    1720           0 :         if ((mdbe->msg = SQLtrans(m)) != MAL_SUCCEED)
    1721             :                 return mdbe->msg;
    1722             : 
    1723             :         /* check if all inputs are bound */
    1724           0 :         for(int i = 0; i< list_length(stmt_internal->q->f->ops); i++){
    1725           0 :                 if (!stmt_internal->data[i].vtype) {
    1726           0 :                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_execute", "Parameter %d not bound to a value", i));
    1727           0 :                         goto cleanup;
    1728             :                 }
    1729             :         }
    1730             : 
    1731           0 :         s = findSymbolInModule(mdbe->c->usermodule, q->f->imp);
    1732           0 :         if ((mdbe->msg = callMAL(mdbe->c, s->def, &glb, stmt_internal->args)) != MAL_SUCCEED)
    1733           0 :                 goto cleanup;
    1734             : 
    1735           0 :         if (b->rowcnt >= 0 && affected_rows)
    1736           0 :                 *affected_rows = b->rowcnt;
    1737             : 
    1738           0 :         if (result) {
    1739           0 :                 if ((mdbe->msg = monetdbe_get_results(result, mdbe)) != MAL_SUCCEED) {
    1740           0 :                         goto cleanup;
    1741             :                 }
    1742             : 
    1743           0 :                 res_internal = *(monetdbe_result_internal**)result;
    1744           0 :                 res_internal->type = (b->results) ? Q_TABLE : Q_UPDATE;
    1745           0 :                 if (res_internal->monetdbe_resultset && res_internal->monetdbe_resultset->query_type == Q_TABLE) {
    1746           0 :                         res_internal->type = Q_TABLE;
    1747           0 :                         if (affected_rows)
    1748           0 :                                 *affected_rows = res_internal->monetdbe_resultset->nr_rows;
    1749             :                 }
    1750             :         }
    1751             : 
    1752           0 : cleanup:
    1753           0 :         GDKfree(glb);
    1754           0 :         return commit_action(m, stmt_internal->mdbe, result, res_internal);
    1755             : }
    1756             : 
    1757             : char*
    1758           0 : monetdbe_cleanup_statement(monetdbe_database dbhdl, monetdbe_statement *stmt)
    1759             : {
    1760           0 :         monetdbe_stmt_internal *stmt_internal = (monetdbe_stmt_internal*)stmt;
    1761           0 :         monetdbe_database_internal *mdbe = (monetdbe_database_internal*)dbhdl;
    1762           0 :         mvc *m = ((backend *) mdbe->c->sqlcontext)->mvc;
    1763           0 :         cq *q = stmt_internal->q;
    1764             : 
    1765           0 :         assert(!stmt_internal->mdbe || mdbe == stmt_internal->mdbe);
    1766             : 
    1767           0 :         assert(mdbe->c);
    1768           0 :         MT_thread_set_qry_ctx(&mdbe->c->qryctx);
    1769           0 :         for (size_t i = 0; i < stmt_internal->res.nparam + 1; i++) {
    1770           0 :                 ValPtr data = &stmt_internal->data[i];
    1771           0 :                 VALclear(data);
    1772             :         }
    1773             : 
    1774           0 :         GDKfree(stmt_internal->data);
    1775           0 :         GDKfree(stmt_internal->args);
    1776           0 :         GDKfree(stmt_internal->res.type);
    1777           0 :         GDKfree(stmt_internal);
    1778             : 
    1779           0 :         if (q)
    1780           0 :                 qc_delete(m->qc, q);
    1781           0 :         return MAL_SUCCEED;
    1782             : }
    1783             : 
    1784             : char*
    1785           1 : monetdbe_cleanup_result(monetdbe_database dbhdl, monetdbe_result* result)
    1786             : {
    1787           1 :         monetdbe_database_internal *mdbe = (monetdbe_database_internal*)dbhdl;
    1788           1 :         monetdbe_result_internal* res = (monetdbe_result_internal *) result;
    1789             : 
    1790           1 :         assert(mdbe->c);
    1791           1 :         MT_thread_set_qry_ctx(&mdbe->c->qryctx);
    1792           1 :         if (!result) {
    1793           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_cleanup_result", "Parameter result is NULL"));
    1794             :         } else {
    1795           1 :                 mdbe->msg = monetdbe_cleanup_result_internal(mdbe, res);
    1796             :         }
    1797             : 
    1798           1 :         return mdbe->msg;
    1799             : }
    1800             : 
    1801             : static inline void
    1802           0 : cleanup_get_columns_result(size_t column_count, monetdbe_column* columns)
    1803             : {
    1804           0 :         if (columns) {
    1805           0 :                 for (size_t c = 0; c < column_count; c++) {
    1806           0 :                         GDKfree(columns[c].name);
    1807           0 :                         GDKfree(columns[c].sql_type.name);
    1808             :                 }
    1809           0 :                 GDKfree(columns);
    1810           0 :                 columns = NULL;
    1811             :         }
    1812           0 : }
    1813             : 
    1814             : static char *
    1815           0 : escape_identifier(const char *s) /* Escapes a SQL identifier string, ie the " and \ characters */
    1816             : {
    1817           0 :         char *ret = NULL, *q;
    1818           0 :         const char *p = s;
    1819             : 
    1820             :         /* At most we will need 2*strlen(s) + 1 characters */
    1821           0 :         if (!(ret = (char *)GDKmalloc(2*strlen(s) + 1)))
    1822             :                 return NULL;
    1823             : 
    1824           0 :         for (q = ret; *p; p++, q++) {
    1825           0 :                 *q = *p;
    1826           0 :                 if (*p == '"')
    1827           0 :                         *(++q) = '"';
    1828           0 :                 else if (*p == '\\')
    1829           0 :                         *(++q) = '\\';
    1830             :         }
    1831             : 
    1832           0 :         *q = '\0';
    1833           0 :         return ret;
    1834             : }
    1835             : 
    1836             : static char*
    1837           0 : monetdbe_get_columns_remote(monetdbe_database_internal *mdbe, const char* schema_name, const char *table_name, size_t *column_count,
    1838             :                                         monetdbe_column **columns)
    1839             : {
    1840           0 :         char buf[1024], *escaped_schema_name = NULL, *escaped_table_name = NULL;
    1841             : 
    1842           0 :         if (schema_name && !(escaped_schema_name = escape_identifier(schema_name))) {
    1843           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_get_columns", MAL_MALLOC_FAIL));
    1844           0 :                 return mdbe->msg;
    1845             :         }
    1846           0 :         if (!(escaped_table_name = escape_identifier(table_name))) {
    1847           0 :                 GDKfree(escaped_schema_name);
    1848           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_get_columns", MAL_MALLOC_FAIL));
    1849           0 :                 return mdbe->msg;
    1850             :         }
    1851             : 
    1852           0 :         int len = snprintf(buf, 1024, "SELECT * FROM %s%s%s\"%s\" WHERE FALSE;",
    1853             :                                            escaped_schema_name ? "\"" : "",  escaped_schema_name ? escaped_schema_name : "",
    1854             :                                            escaped_schema_name ? escaped_schema_name : "\".", escaped_table_name);
    1855           0 :         GDKfree(escaped_schema_name);
    1856           0 :         GDKfree(escaped_table_name);
    1857           0 :         if (len == -1 || len >= 1024) {
    1858           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_get_columns", "Schema and table path is too large"));
    1859           0 :                 return mdbe->msg;
    1860             :         }
    1861             : 
    1862           0 :         monetdbe_result* result = NULL;
    1863             : 
    1864           0 :         if ((mdbe->msg = monetdbe_query_remote(mdbe, buf, &result, NULL, NULL)) != MAL_SUCCEED) {
    1865             :                 return mdbe->msg;
    1866             :         }
    1867             : 
    1868           0 :         *column_count = result->ncols;
    1869           0 :         *columns = GDKzalloc(sizeof(monetdbe_column) * result->ncols);
    1870             : 
    1871             : 
    1872           0 :         if (*columns == NULL)
    1873           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_get_columns", MAL_MALLOC_FAIL));
    1874             : 
    1875           0 :         if (!mdbe->msg)
    1876           0 :                 for (size_t c = 0; c < result->ncols; c++) {
    1877           0 :                         monetdbe_column* rcol;
    1878           0 :                         if ((mdbe->msg = monetdbe_result_fetch(result, &rcol, c)) != NULL) {
    1879             :                                 break;
    1880             :                         }
    1881             : 
    1882           0 :                         if (((*columns)[c].name = GDKstrdup(rcol->name)) == NULL) {
    1883           0 :                                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_get_columns", MAL_MALLOC_FAIL));
    1884           0 :                                 break;
    1885             :                         }
    1886           0 :                         if (((*columns)[c].sql_type.name = GDKstrdup(rcol->sql_type.name)) == NULL) {
    1887           0 :                                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_get_columns", MAL_MALLOC_FAIL));
    1888           0 :                                 break;
    1889             :                         }
    1890           0 :                         (*columns)[c].type = rcol->type;
    1891           0 :                         (*columns)[c].sql_type.scale = rcol->sql_type.scale;
    1892           0 :                         (*columns)[c].sql_type.digits = rcol->sql_type.digits;
    1893             :                 }
    1894             : 
    1895             :         // cleanup
    1896           0 :         char* msg = monetdbe_cleanup_result_internal(mdbe, (monetdbe_result_internal*) result);
    1897             : 
    1898           0 :         if (msg && mdbe->msg) {
    1899           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_get_columns", "multiple errors: %s; %s", mdbe->msg, msg));
    1900             :         }
    1901           0 :         else if (msg) {
    1902           0 :                 mdbe->msg = msg;
    1903             :         }
    1904             : 
    1905           0 :         if (mdbe->msg ) {
    1906           0 :                 cleanup_get_columns_result(*column_count, *columns);
    1907             :         }
    1908             : 
    1909           0 :         return mdbe->msg;
    1910             : }
    1911             : 
    1912             : char*
    1913           0 : monetdbe_get_columns(monetdbe_database dbhdl, const char* schema_name, const char *table_name, size_t *column_count, monetdbe_column **columns)
    1914             : {
    1915           0 :         monetdbe_database_internal *mdbe = (monetdbe_database_internal*)dbhdl;
    1916           0 :         mvc *m = NULL;
    1917           0 :         sql_table *t = NULL;
    1918             : 
    1919           0 :         if ((mdbe->msg = validate_database_handle(mdbe, "monetdbe.monetdbe_get_columns")) != MAL_SUCCEED) {
    1920             :                 return mdbe->msg;
    1921             :         }
    1922           0 :         if (!column_count) {
    1923           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_get_columns", "Parameter column_count is NULL"));
    1924           0 :                 return mdbe->msg;
    1925             :         }
    1926           0 :         if (!columns) {
    1927           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_get_columns", "Parameter columns is NULL"));
    1928           0 :                 return mdbe->msg;
    1929             :         }
    1930           0 :         if (!table_name) {
    1931           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_get_columns", "Parameter table_name is NULL"));
    1932           0 :                 return mdbe->msg;
    1933             :         }
    1934             : 
    1935           0 :         if (mdbe->mid) {
    1936           0 :                 return monetdbe_get_columns_remote(mdbe, schema_name, table_name, column_count, columns);
    1937             :         }
    1938             : 
    1939           0 :         if ((mdbe->msg = getSQLContext(mdbe->c, NULL, &m, NULL)) != MAL_SUCCEED)
    1940             :                 return mdbe->msg;
    1941           0 :         if ((mdbe->msg = SQLtrans(m)) != MAL_SUCCEED)
    1942             :                 return mdbe->msg;
    1943           0 :         if (!(t = find_table_or_view_on_scope(m, NULL, schema_name, table_name, "CATALOG", false))) {
    1944           0 :                 set_error(mdbe, createException(SQL, "monetdbe.monetdbe_get_columns", "%s", m->errstr + 6)); /* Skip error code */
    1945           0 :                 goto cleanup;
    1946             :         }
    1947             : 
    1948           0 :         *column_count = ol_length(t->columns);
    1949           0 :         if ((*columns = GDKzalloc(*column_count * sizeof(monetdbe_column))) == NULL ) {
    1950           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_get_columns", MAL_MALLOC_FAIL));
    1951           0 :                 goto cleanup;
    1952             :         }
    1953             : 
    1954           0 :         for (node *n = ol_first_node(t->columns); n; n = n->next) {
    1955           0 :                 sql_column *col = n->data;
    1956           0 :                 (*columns)[col->colnr].name = col->base.name; // TODO Shouldn't this be string dupped?
    1957           0 :                 if (((*columns)[col->colnr].sql_type.name = GDKstrdup(col->type.type->base.name)) == NULL) goto cleanup;
    1958           0 :                 (*columns)[col->colnr].type = embedded_type(col->type.type->localtype);
    1959           0 :                 (*columns)[col->colnr].sql_type.digits = col->type.type->digits;
    1960           0 :                 (*columns)[col->colnr].sql_type.scale = col->type.type->scale;
    1961             :         }
    1962             : 
    1963           0 : cleanup:
    1964           0 :         mdbe->msg = commit_action(m, mdbe, NULL, NULL);
    1965             : 
    1966           0 :         return mdbe->msg;
    1967             : }
    1968             : 
    1969             : #define GENERATE_BASE_HEADERS(type, tpename) \
    1970             :         static int tpename##_is_null(type *value)
    1971             : 
    1972             : #define GENERATE_BASE_FUNCTIONS(tpe, tpename, mname) \
    1973             :         GENERATE_BASE_HEADERS(tpe, tpename); \
    1974             :         static int tpename##_is_null(tpe *value) { return *value == mname##_nil; }
    1975             : 
    1976             : #ifdef bool
    1977             : #undef bool
    1978             : #endif
    1979             : 
    1980           0 : GENERATE_BASE_FUNCTIONS(int8_t, bool, bit)
    1981           0 : GENERATE_BASE_FUNCTIONS(int8_t, int8_t, bte)
    1982           0 : GENERATE_BASE_FUNCTIONS(int16_t, int16_t, sht)
    1983           0 : GENERATE_BASE_FUNCTIONS(int32_t, int32_t, int)
    1984           0 : GENERATE_BASE_FUNCTIONS(int64_t, int64_t, lng)
    1985             : #ifdef HAVE_HGE
    1986           0 : GENERATE_BASE_FUNCTIONS(__int128, int128_t, hge)
    1987             : #endif
    1988           0 : GENERATE_BASE_FUNCTIONS(size_t, size_t, oid)
    1989             : 
    1990           0 : GENERATE_BASE_FUNCTIONS(float, float, flt)
    1991           0 : GENERATE_BASE_FUNCTIONS(double, double, dbl)
    1992             : 
    1993             : GENERATE_BASE_HEADERS(char*, str);
    1994             : GENERATE_BASE_HEADERS(monetdbe_data_blob, blob);
    1995             : 
    1996             : GENERATE_BASE_HEADERS(monetdbe_data_date, date);
    1997             : GENERATE_BASE_HEADERS(monetdbe_data_time, time);
    1998             : GENERATE_BASE_HEADERS(monetdbe_data_timestamp, timestamp);
    1999             : 
    2000             : #define GENERATE_BAT_INPUT_BASE(tpe)                                                                    \
    2001             :         monetdbe_column_##tpe *bat_data = GDKzalloc(sizeof(monetdbe_column_##tpe));     \
    2002             :         if (!bat_data) {                                                                                                        \
    2003             :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_result_fetch", MAL_MALLOC_FAIL)); \
    2004             :                 goto cleanup;                                                                                                   \
    2005             :         }                                                                                                                                       \
    2006             :         bat_data->type = monetdbe_##tpe;                                                             \
    2007             :         if ((bat_data->sql_type.name = GDKstrdup(sqltpe->type->base.name)) == NULL) {\
    2008             :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_result_fetch", MAL_MALLOC_FAIL)); \
    2009             :                 goto cleanup;                                                                                   \
    2010             :         }                                                                                                                                       \
    2011             :         bat_data->sql_type.scale = sqltpe->scale;                                                         \
    2012             :         bat_data->sql_type.digits = sqltpe->digits;                                                               \
    2013             :         bat_data->is_null = tpe##_is_null;                                                                   \
    2014             :         if (sqltpe->type->radix == 10) bat_data->scale = pow(10, sqltpe->scale); \
    2015             :         column_result = (monetdbe_column*) bat_data;
    2016             : 
    2017             : #define GENERATE_BAT_INPUT(b, tpe, tpe_name, mtype)                                             \
    2018             :         {                                                                                                                                       \
    2019             :                 GENERATE_BAT_INPUT_BASE(tpe_name);                                                              \
    2020             :                 bat_data->count = (size_t) mres->nrows;                                                   \
    2021             :                 bat_data->null_value = mtype##_nil;                                                          \
    2022             :                 if (bat_data->count) {                                                                                       \
    2023             :                         bat_data->data = GDKzalloc(bat_data->count * sizeof(bat_data->null_value)); \
    2024             :                         if (!bat_data->data) {                                                                               \
    2025             :                                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_result_fetch", MAL_MALLOC_FAIL)); \
    2026             :                                 goto cleanup;                                                                                   \
    2027             :                         }                                                                                                                       \
    2028             :                 }                                                                                                                               \
    2029             :                 size_t it = 0;                                                                                                  \
    2030             :                 mtype* val = (mtype*)Tloc(b, 0);                                                                \
    2031             :                 /* bat is dense, materialize it */                                                              \
    2032             :                 for (it = 0; it < bat_data->count; it++, val++)                                   \
    2033             :                         bat_data->data[it] = (tpe) *val;                                                     \
    2034             :         }
    2035             : 
    2036             : static char*
    2037           0 : append_create_remote_append_mal_program(
    2038             :         Symbol* prg,
    2039             :         sql_schema **s,
    2040             :         sql_table **t,
    2041             :         Client c, const char* schema, const char* table, size_t ccount, monetdbe_column* columns) {
    2042             : 
    2043           0 :         char* msg                                       = MAL_SUCCEED;
    2044           0 :         char buf[16]                            = {0};
    2045           0 :         char* remote_program_name       = number2name(buf, sizeof(buf), ++((backend*) c->sqlcontext)->remote);
    2046             : 
    2047           0 :         assert(s && t);
    2048           0 :         assert(c->sqlcontext && ((backend *) c->sqlcontext)->mvc);
    2049           0 :         mvc* m = ((backend *) c->sqlcontext)->mvc;
    2050             : 
    2051           0 :         Symbol _prg;
    2052           0 :         MalBlkPtr mb = NULL;
    2053           0 :         InstrPtr f = NULL, v = NULL, a = NULL, r = NULL;
    2054           0 :         int mvc_id = -1;
    2055             : 
    2056           0 :         if (!(*s = mvc_bind_schema(m, "tmp"))) {
    2057           0 :                 return createException(MAL, "monetdbe.monetdbe_append", MAL_MALLOC_FAIL);
    2058             :         }
    2059             : 
    2060           0 :         switch (sql_trans_create_table(t, m->session->tr, *s, table, NULL, tt_table, false, SQL_DECLARED_TABLE, CA_COMMIT, -1, 0)) {
    2061           0 :                 case -1:
    2062           0 :                         return createException(SQL, "monetdbe.monetdbe_append", MAL_MALLOC_FAIL);
    2063           0 :                 case -2:
    2064             :                 case -3:
    2065           0 :                         return createException(SQL, "monetdbe.monetdbe_append", "Table name '%s' conflicts", table);
    2066             :                 default:
    2067           0 :                         break;
    2068             :         }
    2069             : 
    2070           0 :         assert(prg);
    2071             : 
    2072           0 :         *prg    = NULL;
    2073           0 :         _prg    = newFunctionArgs(userRef, putName(remote_program_name), FUNCTIONsymbol, (int) ccount + 1); // remote program
    2074           0 :         mb              = _prg->def;
    2075             : 
    2076             :         { // START OF HACK
    2077             :                 /*
    2078             :                  * This is a hack to make sure that the serialized remote program is correctly parsed on the remote side.
    2079             :                  * Since the mal serializer (mal_listing) on the local side will use generated variable names,
    2080             :                  * The parsing process on the remote side can and will clash with generated variable names on the remote side.
    2081             :                  * Because serialiser and the parser will both use the same namespace of generated variable names.
    2082             :                  * Adding an offset to the counter that generates the variable names on the local side
    2083             :                  * circumvents this shortcoming in the MAL parser.
    2084             :                  */
    2085             : 
    2086           0 :                 assert(mb->vid == 0);
    2087             : 
    2088             :                 /*
    2089             :                         * Comments generate variable names during parsing:
    2090             :                         * sql.mvc has one comment and for each column there is one sql.append statement plus comment.
    2091             :                         */
    2092           0 :                 const int nr_of_comments = (int) (1 + ccount);
    2093             :                 /*
    2094             :                         * constant terms generate variable names during parsing:
    2095             :                         * Each sql.append has three constant terms: schema + table + column_name.
    2096             :                         * There is one sql.append stmt for each column.
    2097             :                         */
    2098           0 :                 const int nr_of_constant_terms =  (int)  (3 * ccount);
    2099           0 :                 mb->vid = nr_of_comments + nr_of_constant_terms;
    2100             :         } // END OF HACK
    2101             : 
    2102           0 :         f = getInstrPtr(mb, 0);
    2103           0 :         f->retc = f->argc = 0;
    2104           0 :         f = pushReturn(mb, f, newTmpVariable(mb, TYPE_int));
    2105           0 :         v = newFcnCall(mb, sqlRef, mvcRef);
    2106           0 :         if (v == NULL) {
    2107           0 :                 msg = createException(SQL, "monetdbe.monetdbe_append", MAL_MALLOC_FAIL);
    2108           0 :                 goto cleanup;
    2109             :         }
    2110           0 :         pushInstruction(mb, v);
    2111           0 :         setArgType(mb, v, 0, TYPE_int);
    2112             : 
    2113           0 :         mvc_id = getArg(v, 0);
    2114             : 
    2115           0 :         sqlstore *store;
    2116           0 :         store = m->session->tr->store;
    2117           0 :         for (size_t i = 0; i < ccount; i++) {
    2118           0 :                 sql_column *col = NULL;
    2119           0 :                 sql_type *tpe = SA_ZNEW(m->sa, sql_type);
    2120           0 :                 sql_subtype *st = SA_ZNEW(m->sa, sql_subtype);
    2121           0 :                 if (tpe == NULL || st == NULL) {
    2122           0 :                         msg = createException(SQL, "monetdbe.monetdbe_append", MAL_MALLOC_FAIL);
    2123           0 :                         goto cleanup;
    2124             :                 }
    2125           0 :                 tpe->base.name = sa_strdup(m->sa, columns[i].name);
    2126           0 :                 tpe->localtype = monetdbe_2_gdk_type((monetdbe_types) columns[i].type);
    2127           0 :                 tpe->digits = columns[i].sql_type.digits;
    2128           0 :                 tpe->scale = columns[i].sql_type.scale;
    2129           0 :                 sql_init_subtype(st, tpe, columns[i].sql_type.digits, columns[i].sql_type.scale);
    2130             : 
    2131           0 :                 switch (mvc_create_column(&col, m, *t, columns[i].name, st)) {
    2132           0 :                         case -1:
    2133           0 :                                 msg = createException(SQL, "monetdbe.monetdbe_append", MAL_MALLOC_FAIL);
    2134           0 :                                 goto cleanup;
    2135           0 :                         case -2:
    2136             :                         case -3:
    2137           0 :                                 msg = createException(SQL, "monetdbe.monetdbe_append", "Column name '%s' conflicts", columns[i].name);
    2138           0 :                                 goto cleanup;
    2139             :                         default:
    2140           0 :                                 break;
    2141             :                 }
    2142             : 
    2143           0 :                 if (store->storage_api.create_col(m->session->tr, col) != LOG_OK) {
    2144           0 :                         msg = createException(MAL, "monetdbe.monetdbe_append", MAL_MALLOC_FAIL);
    2145           0 :                         goto cleanup;
    2146             :                 }
    2147             : 
    2148           0 :                 int idx = newTmpVariable(mb, newBatType(tpe->localtype));
    2149           0 :                 f = pushArgument(mb, f, idx);
    2150             : 
    2151           0 :                 a = newFcnCall(mb, sqlRef, appendRef);
    2152           0 :                 if (a == NULL) {
    2153           0 :                         msg = createException(SQL, "monetdbe.monetdbe_append", MAL_MALLOC_FAIL);
    2154           0 :                         goto cleanup;
    2155             :                 }
    2156           0 :                 setArgType(mb, a, 0, TYPE_int);
    2157           0 :                 a = pushArgument(mb, a, mvc_id);
    2158           0 :                 a = pushStr(mb, a, schema ? schema : "sys"); /* TODO this should be better */
    2159           0 :                 a = pushStr(mb, a, table);
    2160           0 :                 a = pushStr(mb, a, columns[i].name);
    2161           0 :                 a = pushArgument(mb, a, idx);
    2162           0 :                 pushInstruction(mb, a);
    2163             : 
    2164           0 :                 mvc_id = getArg(a, 0);
    2165             :         }
    2166             : 
    2167           0 :         r = newInstruction(mb, NULL, NULL);
    2168           0 :         if (r == NULL) {
    2169           0 :                 msg = createException(SQL, "monetdbe.monetdbe_append", MAL_MALLOC_FAIL);
    2170           0 :                 goto cleanup;
    2171             :         }
    2172           0 :         r->barrier= RETURNsymbol;
    2173           0 :         r->retc = r->argc = 0;
    2174           0 :         r = pushReturn(mb, r, mvc_id);
    2175           0 :         r = pushArgument(mb, r, mvc_id);
    2176           0 :         pushInstruction(mb, r);
    2177             : 
    2178           0 :         pushEndInstruction(mb);
    2179             : 
    2180           0 :         if ( (msg = chkProgram(c->usermodule, mb)) != MAL_SUCCEED ) {
    2181           0 :                 goto cleanup;
    2182             :         }
    2183             : 
    2184           0 :         assert(msg == MAL_SUCCEED);
    2185           0 :         *prg = _prg;
    2186           0 :         return msg;
    2187             : 
    2188             : cleanup:
    2189           0 :         assert(msg != MAL_SUCCEED);
    2190           0 :         freeSymbol(_prg);
    2191           0 :         *prg = NULL;
    2192           0 :         return msg;
    2193             : }
    2194             : 
    2195             : char*
    2196           0 : monetdbe_append(monetdbe_database dbhdl, const char *schema, const char *table, monetdbe_column **input, size_t column_count)
    2197             : {
    2198           0 :         monetdbe_database_internal *mdbe = (monetdbe_database_internal*)dbhdl;
    2199           0 :         mvc *m = NULL;
    2200           0 :         sql_table *t = NULL;
    2201           0 :         size_t i, cnt;
    2202           0 :         node *n;
    2203           0 :         Symbol remote_prg = NULL;
    2204           0 :         BAT *pos = NULL;
    2205           0 :         BUN offset;
    2206             : 
    2207           0 :         if ((mdbe->msg = validate_database_handle(mdbe, "monetdbe.monetdbe_append")) != MAL_SUCCEED) {
    2208             :                 return mdbe->msg;
    2209             :         }
    2210             : 
    2211           0 :         if ((mdbe->msg = getSQLContext(mdbe->c, NULL, &m, NULL)) != MAL_SUCCEED) {
    2212           0 :                 mdbe->msg = commit_action(m, mdbe, NULL, NULL);
    2213           0 :                 return mdbe->msg;
    2214             :         }
    2215           0 :         sqlstore *store = m->session->tr->store;
    2216             : 
    2217           0 :         if (table == NULL) {
    2218           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_append", "table parameter is NULL"));
    2219           0 :                 goto cleanup;
    2220             :         }
    2221           0 :         if (input == NULL) {
    2222           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_append", "input parameter is NULL"));
    2223           0 :                 goto cleanup;
    2224             :         }
    2225           0 :         if (column_count < 1) {
    2226           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_append", "column_count must be higher than 0"));
    2227           0 :                 goto cleanup;
    2228             :         }
    2229             : 
    2230           0 :         if (mdbe->mid) {
    2231             :                 // We are going to insert the data into a temporary table which is used in the coming remote logic.
    2232             : 
    2233           0 :                 size_t actual_column_count = 0;
    2234           0 :                 monetdbe_column* columns = NULL;
    2235           0 :                 sql_schema* s = NULL;
    2236             : 
    2237           0 :                 if ((mdbe->msg = monetdbe_get_columns_remote(
    2238             :                                 mdbe,
    2239             :                                 schema,
    2240             :                                 table,
    2241             :                                 &actual_column_count,
    2242             :                                 &columns)) != MAL_SUCCEED) {
    2243           0 :                         goto remote_cleanup;
    2244             :                 }
    2245             : 
    2246           0 :                 if ((mdbe->msg = SQLtrans(m)) != MAL_SUCCEED) {
    2247           0 :                         goto remote_cleanup;
    2248             :                 }
    2249             : 
    2250           0 :                 if ((mdbe->msg = append_create_remote_append_mal_program
    2251             :                                                         (&remote_prg,
    2252             :                                                         &s,
    2253             :                                                         &t,
    2254             :                                                         mdbe->c,
    2255             :                                                         schema,
    2256             :                                                         table,
    2257             :                                                         actual_column_count,
    2258             :                                                         columns)) != MAL_SUCCEED) {
    2259           0 :                         goto remote_cleanup;
    2260             :                 }
    2261             : 
    2262           0 :                 insertSymbol(mdbe->c->usermodule, remote_prg);
    2263             : 
    2264           0 : remote_cleanup:
    2265           0 :                 if (mdbe->msg) {
    2266           0 :                         cleanup_get_columns_result(actual_column_count, columns);
    2267           0 :                         freeSymbol(remote_prg);
    2268           0 :                         goto cleanup;
    2269             :                 }
    2270             :         } else {
    2271             :                 // !mdbe->mid
    2272             :                 // inserting into existing local table.
    2273           0 :                 sql_part *pt = NULL;
    2274             : 
    2275           0 :                 if ((mdbe->msg = SQLtrans(m)) != MAL_SUCCEED)
    2276           0 :                         goto cleanup;
    2277           0 :                 if (!(t = find_table_or_view_on_scope(m, NULL, schema, table, "CATALOG", false))) {
    2278           0 :                         set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "%s", m->errstr + 6)); /* Skip error code */
    2279           0 :                         goto cleanup;
    2280             :                 }
    2281           0 :                 if (!insert_allowed(m, t, t->base.name, "APPEND", "append")) {
    2282           0 :                         set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "%s", m->errstr + 6)); /* Skip error code */
    2283           0 :                         goto cleanup;
    2284             :                 }
    2285           0 :                 if ((t->s && t->s->parts && (pt = partition_find_part(m->session->tr, t, NULL))) || isRangePartitionTable(t) || isListPartitionTable(t)) {
    2286           0 :                         set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "Appending to a table from a merge table hierarchy via 'monetdbe_append' is not possible at the moment"));
    2287           0 :                         goto cleanup;
    2288             :                 }
    2289           0 :                 if (t->idxs) {
    2290           0 :                         for (node *n = ol_first_node(t->idxs); n; n = n->next) {
    2291           0 :                                 sql_idx *i = n->data;
    2292             : 
    2293           0 :                                 if (i->key) {
    2294           0 :                                         set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append",
    2295             :                                                                 "Appending to a table with key constraints via 'monetdbe_append' is not possible at the moment"));
    2296           0 :                                         goto cleanup;
    2297           0 :                                 } else if (hash_index(i->type) && list_length(i->columns) > 1) {
    2298           0 :                                         set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append",
    2299             :                                                                 "Appending to a table with hash indexes referring to more than one column via 'monetdbe_append' is not possible at the moment"));
    2300           0 :                                         goto cleanup;
    2301           0 :                                 } else if (i->type == join_idx) {
    2302           0 :                                         set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append",
    2303             :                                                                 "Appending to a table with join indexes via 'monetdbe_append' is not possible at the moment"));
    2304           0 :                                         goto cleanup;
    2305             :                                 }
    2306             :                         }
    2307             :                 }
    2308           0 :                 if (t->triggers) {
    2309           0 :                         for (n = ol_first_node(t->triggers); n; n = n->next) {
    2310           0 :                                 sql_trigger *trigger = n->data;
    2311             : 
    2312           0 :                                 if (trigger->event == 0) { /* insert event */
    2313           0 :                                         set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append",
    2314             :                                                                 "Appending to a table with triggers at the insert event via 'monetdbe_append' is not possible at the moment"));
    2315           0 :                                         goto cleanup;
    2316             :                                 }
    2317             :                         }
    2318             :                 }
    2319             :         }
    2320             : 
    2321             :         /* for now no default values, ie user should supply all columns */
    2322           0 :         if (column_count != (size_t)ol_length(t->columns)) {
    2323           0 :                 set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "Incorrect number of columns"));
    2324           0 :                 goto cleanup;
    2325             :         }
    2326             : 
    2327           0 :         cnt = input[0]->count;
    2328           0 :         if (store->storage_api.claim_tab(m->session->tr, t, cnt, &offset, &pos) != LOG_OK) {
    2329           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_append", "Claim failed"));
    2330           0 :                 goto cleanup;
    2331             :         }
    2332             :         /* signal an insert was made on the table */
    2333           0 :         if (!isNew(t) && isGlobal(t) && !isGlobalTemp(t) && sql_trans_add_dependency_change(m->session->tr, t->base.id, dml) != LOG_OK) {
    2334           0 :                 set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", MAL_MALLOC_FAIL));
    2335           0 :                 goto cleanup;
    2336             :         }
    2337             : 
    2338           0 :         for (i = 0, n = ol_first_node(t->columns); i < column_count && n; i++, n = n->next) {
    2339           0 :                 sql_column *c = n->data;
    2340           0 :                 int mtype = monetdbe_2_gdk_type(input[i]->type);
    2341           0 :                 const void* nil = (mtype>=0)?ATOMnilptr(mtype):NULL;
    2342           0 :                 char *v = input[i]->data;
    2343             : 
    2344           0 :                 if (mtype < 0) {
    2345           0 :                         set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "Cannot find type for column %zu", i));
    2346           0 :                         goto cleanup;
    2347           0 :                 } else if (input[i]->count != cnt) {
    2348           0 :                         set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "Number of values don't match between columns"));
    2349           0 :                         goto cleanup;
    2350             :                 }
    2351           0 :                 if (mtype >= TYPE_bit && mtype <=
    2352             : #ifdef HAVE_HGE
    2353             :         TYPE_hge
    2354             : #else
    2355             :         TYPE_lng
    2356             : #endif
    2357             :                 ) {
    2358             :                         //-------------------------------------
    2359           0 :                         BAT *bn = NULL;
    2360             : 
    2361           0 :                         if (mtype != c->type.type->localtype) {
    2362           0 :                                 set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "Cannot append %d into column '%s'", input[i]->type, c->base.name));
    2363           0 :                                 goto cleanup;
    2364             :                         }
    2365             : 
    2366           0 :                         if ((bn = COLnew(0, mtype, 0, TRANSIENT)) == NULL) {
    2367           0 :                                 set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "Cannot create append column"));
    2368           0 :                                 goto cleanup;
    2369             :                         }
    2370             : 
    2371             :                         //save prev heap pointer
    2372           0 :                         char *prev_base;
    2373           0 :                         size_t prev_size;
    2374           0 :                         prev_base = bn->theap->base;
    2375           0 :                         prev_size = bn->theap->free;
    2376             : 
    2377             :                         //BAT heap base to input[i]->data
    2378           0 :                         bn->theap->base = input[i]->data;
    2379           0 :                         bn->theap->free = tailsize(bn, cnt);
    2380             : 
    2381             :                         //BATsetdims(bn); called in COLnew
    2382           0 :                         BATsetcapacity(bn, cnt);
    2383           0 :                         BATsetcount(bn, cnt);
    2384             : 
    2385             :                         //set default flags
    2386           0 :                         BATsettrivprop(bn);
    2387             : 
    2388           0 :                         if (cnt > 1) {
    2389           0 :                                 bn->tsorted = bn->trevsorted = false;
    2390           0 :                                 bn->tnosorted = bn->tnorevsorted = 0;
    2391           0 :                                 bn->tkey = false;
    2392           0 :                                 bn->tnonil = false;
    2393           0 :                                 bn->tnil = false;
    2394             :                         }
    2395             : 
    2396           0 :                         if (store->storage_api.append_col(m->session->tr, c, offset, pos, bn, cnt, TYPE_bat) != 0) {
    2397           0 :                                 bn->theap->base = prev_base;
    2398           0 :                                 bn->theap->free = prev_size;
    2399           0 :                                 BBPreclaim(bn);
    2400           0 :                                 set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "Cannot append BAT"));
    2401           0 :                                 goto cleanup;
    2402             :                         }
    2403             : 
    2404           0 :                         bn->theap->base = prev_base;
    2405           0 :                         bn->theap->free = prev_size;
    2406           0 :                         BBPreclaim(bn);
    2407             :                 } else if (mtype == TYPE_str) {
    2408             :                         char **d = (char**)v;
    2409             : 
    2410           0 :                         for (size_t j=0; j<cnt; j++) {
    2411           0 :                                 if (!d[j]) {
    2412           0 :                                         d[j] = (char*) nil;
    2413           0 :                                 } else if (!checkUTF8(d[j])) {
    2414           0 :                                         set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "Incorrectly encoded UTF-8"));
    2415           0 :                                         goto cleanup;
    2416             :                                 }
    2417             :                         }
    2418           0 :                         if (store->storage_api.append_col(m->session->tr, c, offset, pos, d, cnt, mtype) != 0) {
    2419           0 :                                 set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "Cannot append values"));
    2420           0 :                                 goto cleanup;
    2421             :                         }
    2422             :                 } else if (mtype == TYPE_timestamp) {
    2423           0 :                         int err = 0;
    2424           0 :                         timestamp *d = GDKmalloc(sizeof(timestamp)*cnt);
    2425           0 :                         if (!d) {
    2426           0 :                                 set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "Cannot append values"));
    2427           0 :                                 goto cleanup;
    2428             :                         }
    2429             :                         monetdbe_data_timestamp* ts = (monetdbe_data_timestamp*)v;
    2430             : 
    2431           0 :                         for (size_t j=0; j<cnt; j++){
    2432           0 :                                 monetdbe_data_timestamp mdt = ts[j];
    2433             : 
    2434           0 :                                 if (timestamp_is_null(&mdt)) {
    2435           0 :                                         d[j] = *(timestamp*) nil;
    2436             :                                 } else {
    2437           0 :                                         d[j] = timestamp_from_data(&mdt);
    2438             :                                 }
    2439             :                         }
    2440           0 :                         if (store->storage_api.append_col(m->session->tr, c, offset, pos, d, cnt, mtype) != 0) {
    2441           0 :                                 set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "Cannot append values"));
    2442           0 :                                 err = 1;
    2443             :                         }
    2444           0 :                         GDKfree(d);
    2445           0 :                         if (err)
    2446           0 :                                 goto cleanup;
    2447             :                 } else if (mtype == TYPE_date) {
    2448           0 :                         int err = 0;
    2449           0 :                         date *d = GDKmalloc(sizeof(date)*cnt);
    2450           0 :                         if (!d) {
    2451           0 :                                 set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "Cannot append values"));
    2452           0 :                                 goto cleanup;
    2453             :                         }
    2454             :                         monetdbe_data_date* de = (monetdbe_data_date*)v;
    2455             : 
    2456           0 :                         for (size_t j=0; j<cnt; j++){
    2457           0 :                                 monetdbe_data_date mdt = de[j];
    2458             : 
    2459           0 :                                 if (date_is_null(&mdt)) {
    2460           0 :                                         d[j] = *(date*) nil;
    2461             :                                 } else {
    2462           0 :                                         d[j] = date_from_data(&mdt);
    2463             :                                 }
    2464             :                         }
    2465           0 :                         if (store->storage_api.append_col(m->session->tr, c, offset, pos, d, cnt, mtype) != 0) {
    2466           0 :                                 set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "Cannot append values"));
    2467           0 :                                 err = 1;
    2468             :                         }
    2469           0 :                         GDKfree(d);
    2470           0 :                         if (err)
    2471           0 :                                 goto cleanup;
    2472             :                 } else if (mtype == TYPE_daytime) {
    2473           0 :                         int err = 0;
    2474           0 :                         daytime *d = GDKmalloc(sizeof(daytime)*cnt);
    2475           0 :                         if (!d) {
    2476           0 :                                 set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "Cannot append values"));
    2477           0 :                                 goto cleanup;
    2478             :                         }
    2479             :                         monetdbe_data_time* t = (monetdbe_data_time*)v;
    2480             : 
    2481           0 :                         for (size_t j=0; j<cnt; j++){
    2482           0 :                                 monetdbe_data_time mdt = t[j];
    2483             : 
    2484           0 :                                 if (time_is_null(&mdt)) {
    2485           0 :                                         d[j] = *(daytime*) nil;
    2486             :                                 } else {
    2487           0 :                                         d[j] = time_from_data(&mdt);
    2488             :                                 }
    2489             :                         }
    2490           0 :                         if (store->storage_api.append_col(m->session->tr, c, offset, pos, d, cnt, mtype) != 0) {
    2491           0 :                                 set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "Cannot append values"));
    2492           0 :                                 err = 1;
    2493             :                         }
    2494           0 :                         GDKfree(d);
    2495           0 :                         if (err)
    2496           0 :                                 goto cleanup;
    2497           0 :                 } else if (mtype == TYPE_blob) {
    2498           0 :                         int err = 0;
    2499           0 :                         size_t j = 0;
    2500           0 :                         blob **d = GDKmalloc(sizeof(blob*)*cnt);
    2501           0 :                         if (!d) {
    2502           0 :                                 set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "Cannot append values"));
    2503           0 :                                 goto cleanup;
    2504             :                         }
    2505             :                         monetdbe_data_blob* be = (monetdbe_data_blob*)v;
    2506             : 
    2507           0 :                         for (j=0; j<cnt; j++){
    2508           0 :                                 if (blob_is_null(be+j)) {
    2509           0 :                                         d[j] = (blob*)nil;
    2510             :                                 } else {
    2511           0 :                                         size_t len = be[j].size;
    2512           0 :                                         size_t nlen = blobsize(len);
    2513           0 :                                         blob *b = (blob*)GDKmalloc(nlen);
    2514           0 :                                         if (!b) {
    2515           0 :                                                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_append", MAL_MALLOC_FAIL));
    2516           0 :                                                 err = 1;
    2517           0 :                                                 break;
    2518             :                                         }
    2519           0 :                                         b->nitems = len;
    2520           0 :                                         memcpy(b->data, be[j].data, len);
    2521           0 :                                         d[j] = b;
    2522             :                                 }
    2523             :                         }
    2524           0 :                         if (!err && store->storage_api.append_col(m->session->tr, c, offset, pos, d, cnt, mtype) != 0) {
    2525           0 :                                 set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "Cannot append values"));
    2526           0 :                                 err = 1;
    2527             :                         }
    2528           0 :                         for (size_t k=0; k<j; k++){
    2529           0 :                                 if (d[k] != nil)
    2530           0 :                                         GDKfree(d[k]);
    2531             :                         }
    2532           0 :                         GDKfree(d);
    2533           0 :                         if (err)
    2534           0 :                                 goto cleanup;
    2535             :                 } else {
    2536             :                         set_error(mdbe, createException(SQL, "monetdbe.monetdbe_append", "The internal type '%s' is not supported on monetdbe append at the moment", ATOMname(mtype)));
    2537             :                         goto cleanup;
    2538             :                 }
    2539             :         }
    2540             : 
    2541           0 :         if (mdbe->mid) {
    2542           0 :                 char nme[16];
    2543           0 :                 const char *name        = number2name(nme, sizeof(nme), ++((backend*)  mdbe->c->sqlcontext)->remote);
    2544           0 :                 Symbol prg; // local program
    2545             : 
    2546           0 :                 if ( (prg = newFunction(userRef, putName(name), FUNCTIONsymbol)) == NULL ) {
    2547           0 :                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_append", MAL_MALLOC_FAIL));
    2548           0 :                         goto cleanup;
    2549             :                 }
    2550             : 
    2551           0 :                 MalBlkPtr mb = prg->def;
    2552           0 :                 InstrPtr f = getInstrPtr(mb, 0);
    2553           0 :                 f->retc = f->argc = 0;
    2554             : 
    2555           0 :                 InstrPtr r = newFcnCall(mb, remoteRef, registerRef);
    2556           0 :                 if (r == NULL) {
    2557           0 :                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_append", MAL_MALLOC_FAIL));
    2558           0 :                         freeSymbol(prg);
    2559           0 :                         goto cleanup;
    2560             :                 }
    2561             : 
    2562           0 :                 setArgType(mb, r, 0, TYPE_str);
    2563           0 :                 r = pushStr(mb, r, mdbe->mid);
    2564           0 :                 r = pushStr(mb, r, userRef);
    2565           0 :                 r = pushStr(mb, r, putName(remote_prg->name));
    2566           0 :                 pushInstruction(mb, r);
    2567             : 
    2568           0 :                 InstrPtr e = newInstructionArgs(mb, remoteRef, execRef, 4 + ol_length(t->columns));
    2569           0 :                 if (e == NULL) {
    2570           0 :                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_append", MAL_MALLOC_FAIL));
    2571           0 :                         freeSymbol(prg);
    2572           0 :                         goto cleanup;
    2573             :                 }
    2574           0 :                 setDestVar(e, newTmpVariable(mb, TYPE_any));
    2575           0 :                 e = pushStr(mb, e, mdbe->mid);
    2576           0 :                 e = pushStr(mb, e, userRef);
    2577           0 :                 e = pushArgument(mb, e, getArg(r, 0));
    2578             : 
    2579           0 :                 for (i = 0, n = ol_first_node(t->columns); i < (unsigned) ol_length(t->columns); i++, n = n->next) {
    2580           0 :                         sql_column *c = n->data;
    2581           0 :                         BAT* b = store->storage_api.bind_col(m->session->tr, c, RDONLY);
    2582           0 :                         if (b == NULL) {
    2583           0 :                                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_append", MAL_MALLOC_FAIL));
    2584           0 :                                 freeSymbol(prg);
    2585           0 :                                 goto cleanup;
    2586             :                         }
    2587             : 
    2588           0 :                         int idx = newTmpVariable(mb, newBatType(c->type.type->localtype));
    2589           0 :                         ValRecord v = { .vtype = TYPE_bat, .len = ATOMlen(TYPE_bat, &b->batCacheid), .val.bval = b->batCacheid};
    2590           0 :                         getVarConstant(mb, idx) = v;
    2591           0 :                         setVarConstant(mb, idx);
    2592           0 :                         BBPunfix(b->batCacheid);
    2593             : 
    2594           0 :                         InstrPtr p = newFcnCall(mb, remoteRef, putRef);
    2595           0 :                         if (p == NULL) {
    2596           0 :                                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_append", MAL_MALLOC_FAIL));
    2597           0 :                                 freeInstruction(e);
    2598           0 :                                 freeSymbol(prg);
    2599           0 :                                 goto cleanup;
    2600             :                         }
    2601           0 :                         setArgType(mb, p, 0, TYPE_str);
    2602           0 :                         p = pushStr(mb, p, mdbe->mid);
    2603           0 :                         p = pushArgument(mb, p, idx);
    2604           0 :                         pushInstruction(mb, p);
    2605             : 
    2606           0 :                         e = pushArgument(mb, e, getArg(p, 0));
    2607             :                 }
    2608             : 
    2609           0 :                 pushInstruction(mb, e);
    2610             : 
    2611           0 :                 InstrPtr ri = newInstruction(mb, NULL, NULL);
    2612           0 :                 if (ri == NULL) {
    2613           0 :                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_append", MAL_MALLOC_FAIL));
    2614           0 :                         freeSymbol(prg);
    2615           0 :                         goto cleanup;
    2616             :                 }
    2617           0 :                 ri->barrier= RETURNsymbol;
    2618           0 :                 ri->retc = ri->argc = 0;
    2619           0 :                 pushInstruction(mb, ri);
    2620             : 
    2621           0 :                 if ( (mdbe->msg = chkProgram(mdbe->c->usermodule, mb)) != MAL_SUCCEED ) {
    2622           0 :                         freeSymbol(prg);
    2623           0 :                         goto cleanup;
    2624             :                 }
    2625             : 
    2626           0 :                 mdbe->msg = runMAL(mdbe->c, mb, 0, NULL);
    2627           0 :                 freeSymbol(prg);
    2628             :         }
    2629             : 
    2630           0 : cleanup:
    2631           0 :         if (pos)
    2632           0 :                 BBPreclaim(pos);
    2633           0 :         mdbe->msg = commit_action(m, mdbe, NULL, NULL);
    2634           0 :         return mdbe->msg;
    2635             : }
    2636             : 
    2637             : const void *
    2638           0 : monetdbe_null(monetdbe_database dbhdl, monetdbe_types t)
    2639             : {
    2640           0 :         monetdbe_database_internal *mdbe = (monetdbe_database_internal*)dbhdl;
    2641           0 :         int mtype = monetdbe_2_gdk_type(t);
    2642             : 
    2643           0 :         if (mtype < 0)
    2644             :                 return NULL;
    2645             : 
    2646           0 :         if ((mtype >= TYPE_bit && mtype <=
    2647             : #ifdef HAVE_HGE
    2648             :         TYPE_hge
    2649             : #else
    2650             :         TYPE_lng
    2651             : #endif
    2652             :                         ))
    2653           0 :                 return ATOMnilptr(mtype);
    2654             :         else if (mtype == TYPE_str)
    2655             :                 return NULL;
    2656             :         else if (mtype == TYPE_blob)
    2657           0 :                 return &mdbe->blob_null;
    2658             :         else if (mtype == TYPE_date)
    2659           0 :                 return &mdbe->date_null;
    2660             :         else if (mtype == TYPE_daytime)
    2661           0 :                 return &mdbe->time_null;
    2662             :         else if (mtype == TYPE_timestamp)
    2663           0 :                 return &mdbe->timestamp_null;
    2664             :         return NULL;
    2665             : }
    2666             : 
    2667             : char*
    2668           4 : monetdbe_result_fetch(monetdbe_result* mres, monetdbe_column** res, size_t column_index)
    2669             : {
    2670           4 :         BAT* b = NULL;
    2671           4 :         int bat_type;
    2672           4 :         mvc* m;
    2673           4 :         monetdbe_result_internal* result = (monetdbe_result_internal*) mres;
    2674           4 :         sql_subtype* sqltpe = NULL;
    2675           4 :         monetdbe_column* column_result = NULL;
    2676           4 :         size_t j = 0;
    2677           4 :         monetdbe_database_internal *mdbe = result->mdbe;
    2678           4 :         Client c = mdbe->c;
    2679             : 
    2680             : 
    2681           4 :         if ((mdbe->msg = validate_database_handle(mdbe, "monetdbe.monetdbe_result_fetch")) != MAL_SUCCEED) {
    2682             : 
    2683             :                 return mdbe->msg;
    2684             :         }
    2685             : 
    2686           4 :         if ((mdbe->msg = getSQLContext(c, NULL, &m, NULL)) != MAL_SUCCEED)
    2687           0 :                 goto cleanup;
    2688           4 :         if (!res) {
    2689           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_result_fetch", "Parameter res is NULL"));
    2690           0 :                 goto cleanup;
    2691             :         }
    2692           4 :         if (column_index >= mres->ncols) {
    2693           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_result_fetch", "Index out of range"));
    2694           0 :                 goto cleanup;
    2695             :         }
    2696             :         // check if we have the column converted already
    2697           4 :         if (result->converted_columns[column_index]) {
    2698           2 :                 *res = result->converted_columns[column_index];
    2699             : 
    2700           2 :                 return MAL_SUCCEED;
    2701             :         }
    2702             : 
    2703             :         // otherwise we have to convert the column
    2704           2 :         b = BATdescriptor(result->monetdbe_resultset->cols[column_index].b);
    2705           2 :         if (!b) {
    2706           0 :                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_result_fetch", RUNTIME_OBJECT_MISSING));
    2707           0 :                 goto cleanup;
    2708             :         }
    2709           2 :         bat_type = b->ttype;
    2710           2 :         sqltpe = &result->monetdbe_resultset->cols[column_index].type;
    2711             : 
    2712           2 :         if (bat_type == TYPE_bit) {
    2713           0 :                 GENERATE_BAT_INPUT(b, int8_t, bool, bit);
    2714             :         } else if (bat_type == TYPE_bte) {
    2715           0 :                 GENERATE_BAT_INPUT(b, int8_t, int8_t, bte);
    2716             :         } else if (bat_type == TYPE_sht) {
    2717           0 :                 GENERATE_BAT_INPUT(b, int16_t, int16_t, sht);
    2718             :         } else if (bat_type == TYPE_int) {
    2719           3 :                 GENERATE_BAT_INPUT(b, int32_t, int32_t, int);
    2720             :         } else if (bat_type == TYPE_oid) {
    2721           0 :                 GENERATE_BAT_INPUT(b, size_t, size_t, oid);
    2722             :         } else if (bat_type == TYPE_lng) {
    2723           0 :                 GENERATE_BAT_INPUT(b, int64_t, int64_t, lng);
    2724             : #ifdef HAVE_HGE
    2725             :         } else if (bat_type == TYPE_hge) {
    2726           0 :                 GENERATE_BAT_INPUT(b, __int128, int128_t, hge);
    2727             : #endif
    2728             :         } else if (bat_type == TYPE_flt) {
    2729           0 :                 GENERATE_BAT_INPUT(b, float, float, flt);
    2730             :         } else if (bat_type == TYPE_dbl) {
    2731           0 :                 GENERATE_BAT_INPUT(b, double, double, dbl);
    2732             :         } else if (bat_type == TYPE_str) {
    2733           1 :                 BATiter li;
    2734           1 :                 BUN p = 0, q = 0;
    2735           1 :                 GENERATE_BAT_INPUT_BASE(str);
    2736           1 :                 bat_data->count = (size_t) mres->nrows;
    2737           1 :                 if (bat_data->count) {
    2738           1 :                         bat_data->data = GDKzalloc(sizeof(char *) * bat_data->count);
    2739           1 :                         bat_data->null_value = NULL;
    2740           1 :                         if (!bat_data->data) {
    2741           0 :                                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_result_fetch", MAL_MALLOC_FAIL));
    2742           0 :                                 goto cleanup;
    2743             :                         }
    2744             :                 }
    2745             : 
    2746           1 :                 j = 0;
    2747           1 :                 li = bat_iterator(b);
    2748           3 :                 BATloop(b, p, q)
    2749             :                 {
    2750           2 :                         const char *t = (const char*)BUNtvar(li, p);
    2751           2 :                         if (strcmp(t, str_nil) == 0) {
    2752           0 :                                 bat_data->data[j] = NULL;
    2753             :                         } else {
    2754           2 :                                 bat_data->data[j] = GDKstrdup(t);
    2755           2 :                                 if (!bat_data->data[j]) {
    2756           0 :                                         bat_iterator_end(&li);
    2757           0 :                                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_result_fetch", MAL_MALLOC_FAIL));
    2758           0 :                                         goto cleanup;
    2759             :                                 }
    2760             :                         }
    2761           2 :                         j++;
    2762             :                 }
    2763           1 :                 bat_iterator_end(&li);
    2764             :         } else if (bat_type == TYPE_date) {
    2765           0 :                 date *baseptr;
    2766           0 :                 GENERATE_BAT_INPUT_BASE(date);
    2767           0 :                 bat_data->count = (size_t) mres->nrows;
    2768           0 :                 if (bat_data->count) {
    2769           0 :                         bat_data->data = GDKmalloc(sizeof(bat_data->null_value) * bat_data->count);
    2770           0 :                         if (!bat_data->data) {
    2771           0 :                                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_result_fetch", MAL_MALLOC_FAIL));
    2772           0 :                                 goto cleanup;
    2773             :                         }
    2774             :                 }
    2775             : 
    2776           0 :                 baseptr = (date *)Tloc(b, 0);
    2777           0 :                 for (j = 0; j < bat_data->count; j++)
    2778           0 :                         data_from_date(baseptr[j], bat_data->data + j);
    2779           0 :                 memcpy(&bat_data->null_value, &mdbe->date_null, sizeof(monetdbe_data_date));
    2780             :         } else if (bat_type == TYPE_daytime) {
    2781           0 :                 daytime *baseptr;
    2782           0 :                 GENERATE_BAT_INPUT_BASE(time);
    2783           0 :                 bat_data->count = (size_t) mres->nrows;
    2784           0 :                 if (bat_data->count) {
    2785           0 :                         bat_data->data = GDKmalloc(sizeof(bat_data->null_value) * bat_data->count);
    2786           0 :                         if (!bat_data->data) {
    2787           0 :                                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_result_fetch", MAL_MALLOC_FAIL));
    2788           0 :                                 goto cleanup;
    2789             :                         }
    2790             :                 }
    2791             : 
    2792           0 :                 baseptr = (daytime *)Tloc(b, 0);
    2793           0 :                 for (j = 0; j < bat_data->count; j++)
    2794           0 :                         data_from_time(baseptr[j], bat_data->data + j);
    2795           0 :                 memcpy(&bat_data->null_value, &mdbe->time_null, sizeof(monetdbe_data_time));
    2796             :         } else if (bat_type == TYPE_timestamp) {
    2797           0 :                 timestamp *baseptr;
    2798           0 :                 GENERATE_BAT_INPUT_BASE(timestamp);
    2799           0 :                 bat_data->count = (size_t) mres->nrows;
    2800           0 :                 if (bat_data->count) {
    2801           0 :                         bat_data->data = GDKmalloc(sizeof(bat_data->null_value) * bat_data->count);
    2802           0 :                         if (!bat_data->data) {
    2803           0 :                                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_result_fetch", MAL_MALLOC_FAIL));
    2804           0 :                                 goto cleanup;
    2805             :                         }
    2806             :                 }
    2807             : 
    2808           0 :                 baseptr = (timestamp *)Tloc(b, 0);
    2809           0 :                 for (j = 0; j < bat_data->count; j++)
    2810           0 :                         data_from_timestamp(baseptr[j], bat_data->data + j);
    2811           0 :                 memcpy(&bat_data->null_value, &mdbe->timestamp_null, sizeof(monetdbe_data_timestamp));
    2812             :         } else if (bat_type == TYPE_blob) {
    2813           0 :                 BATiter li;
    2814           0 :                 BUN p = 0, q = 0;
    2815           0 :                 GENERATE_BAT_INPUT_BASE(blob);
    2816           0 :                 bat_data->count = (size_t) mres->nrows;
    2817           0 :                 if (bat_data->count) {
    2818           0 :                         bat_data->data = GDKmalloc(sizeof(monetdbe_data_blob) * bat_data->count);
    2819           0 :                         if (!bat_data->data) {
    2820           0 :                                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_result_fetch", MAL_MALLOC_FAIL));
    2821           0 :                                 goto cleanup;
    2822             :                         }
    2823             :                 }
    2824           0 :                 j = 0;
    2825             : 
    2826           0 :                 li = bat_iterator(b);
    2827           0 :                 BATloop(b, p, q)
    2828             :                 {
    2829           0 :                         const blob *t = (const blob *)BUNtvar(li, p);
    2830           0 :                         if (t->nitems == ~(size_t)0) {
    2831           0 :                                 bat_data->data[j].size = 0;
    2832           0 :                                 bat_data->data[j].data = NULL;
    2833             :                         } else {
    2834           0 :                                 bat_data->data[j].size = t->nitems;
    2835           0 :                                 bat_data->data[j].data = GDKmalloc(t->nitems);
    2836           0 :                                 if (!bat_data->data[j].data) {
    2837           0 :                                         bat_iterator_end(&li);
    2838           0 :                                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_result_fetch", MAL_MALLOC_FAIL));
    2839           0 :                                         goto cleanup;
    2840             :                                 }
    2841           0 :                                 memcpy(bat_data->data[j].data, t->data, t->nitems);
    2842             :                         }
    2843           0 :                         j++;
    2844             :                 }
    2845           0 :                 bat_iterator_end(&li);
    2846           0 :                 bat_data->null_value.size = 0;
    2847           0 :                 bat_data->null_value.data = NULL;
    2848             :         } else {
    2849             :                 // unsupported type: convert to string
    2850           0 :                 BATiter li;
    2851           0 :                 BUN p = 0, q = 0;
    2852           0 :                 GENERATE_BAT_INPUT_BASE(str);
    2853           0 :                 bat_data->count = (size_t) mres->nrows;
    2854           0 :                 if (bat_data->count) {
    2855           0 :                         bat_data->null_value = NULL;
    2856           0 :                         bat_data->data = GDKzalloc(sizeof(char *) * bat_data->count);
    2857           0 :                         if (!bat_data->data) {
    2858           0 :                                 set_error(mdbe, createException(MAL, "monetdbe.monetdbe_result_fetch", MAL_MALLOC_FAIL));
    2859           0 :                                 goto cleanup;
    2860             :                         }
    2861             :                 }
    2862           0 :                 j = 0;
    2863             : 
    2864           0 :                 li = bat_iterator(b);
    2865           0 :                 BATloop(b, p, q)
    2866             :                 {
    2867           0 :                         const void *t = BUNtail(li, p);
    2868           0 :                         if (BATatoms[bat_type].atomCmp(t, BATatoms[bat_type].atomNull) == 0) {
    2869           0 :                                 bat_data->data[j] = NULL;
    2870             :                         } else {
    2871           0 :                                 char *sresult = NULL;
    2872           0 :                                 size_t length = 0;
    2873           0 :                                 if (BATatoms[bat_type].atomToStr(&sresult, &length, t, true) == 0) {
    2874           0 :                                         bat_iterator_end(&li);
    2875           0 :                                         set_error(mdbe, createException(MAL, "monetdbe.monetdbe_result_fetch", "Failed to convert element to string"));
    2876           0 :                                         goto cleanup;
    2877             :                                 }
    2878           0 :                                 bat_data->data[j] = sresult;
    2879             :                         }
    2880           0 :                         j++;
    2881             :                 }
    2882           0 :                 bat_iterator_end(&li);
    2883             :         }
    2884           0 :         if (column_result)
    2885           2 :                 column_result->name = result->monetdbe_resultset->cols[column_index].name;
    2886           2 : cleanup:
    2887           2 :         BBPreclaim(b);
    2888           2 :         if (mdbe->msg) {
    2889           0 :                 if (res)
    2890           0 :                         *res = NULL;
    2891           0 :                 monetdbe_destroy_column(column_result);
    2892           2 :         } else if (res) {
    2893           2 :                 result->converted_columns[column_index] = column_result;
    2894           2 :                 *res = result->converted_columns[column_index];
    2895             :         }
    2896           2 :         mdbe->msg = commit_action(m, mdbe, NULL, NULL);
    2897             : 
    2898           2 :         return mdbe->msg;
    2899             : }
    2900             : 
    2901             : static void
    2902           1 : data_from_date(date d, monetdbe_data_date *ptr)
    2903             : {
    2904           1 :         ptr->day = date_day(d);
    2905           1 :         ptr->month = date_month(d);
    2906           1 :         ptr->year = date_year(d);
    2907           1 : }
    2908             : 
    2909             : static date
    2910           0 : date_from_data(monetdbe_data_date *ptr)
    2911             : {
    2912           0 :         return date_create(ptr->year, ptr->month, ptr->day);
    2913             : }
    2914             : 
    2915             : static void
    2916           1 : data_from_time(daytime d, monetdbe_data_time *ptr)
    2917             : {
    2918           1 :         ptr->hours = daytime_hour(d);
    2919           1 :         ptr->minutes = daytime_min(d);
    2920           1 :         ptr->seconds = daytime_sec(d);
    2921           1 :         ptr->ms = daytime_usec(d) / 1000;
    2922           1 : }
    2923             : 
    2924             : static daytime
    2925           0 : time_from_data(monetdbe_data_time *ptr)
    2926             : {
    2927           0 :         return daytime_create(ptr->hours, ptr->minutes, ptr->seconds, ptr->ms * 1000);
    2928             : }
    2929             : 
    2930             : static void
    2931           1 : data_from_timestamp(timestamp d, monetdbe_data_timestamp *ptr)
    2932             : {
    2933           1 :         daytime tm = timestamp_daytime(d);
    2934           1 :         date dt = timestamp_date(d);
    2935             : 
    2936           1 :         ptr->date.day = date_day(dt);
    2937           1 :         ptr->date.month = date_month(dt);
    2938           1 :         ptr->date.year = date_year(dt);
    2939           1 :         ptr->time.hours = daytime_hour(tm);
    2940           1 :         ptr->time.minutes = daytime_min(tm);
    2941           1 :         ptr->time.seconds = daytime_sec(tm);
    2942           1 :         ptr->time.ms = daytime_usec(tm) / 1000;
    2943           1 : }
    2944             : 
    2945             : static timestamp
    2946           0 : timestamp_from_data(monetdbe_data_timestamp *ptr)
    2947             : {
    2948           0 :         return timestamp_create(
    2949           0 :                 date_create(ptr->date.year, ptr->date.month, ptr->date.day),
    2950           0 :                 daytime_create(ptr->time.hours, ptr->time.minutes, ptr->time.seconds, ptr->time.ms * 1000));
    2951             : }
    2952             : 
    2953             : const char*
    2954           0 : monetdbe_get_mapi_port(void) {
    2955           0 :         return GDKgetenv("mapi_port");
    2956             : }

Generated by: LCOV version 1.14