LCOV - code coverage report
Current view: top level - tools/monetdbe - monetdbe.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 381 1535 24.8 %
Date: 2021-10-13 02:24:04 Functions: 29 69 42.0 %

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

Generated by: LCOV version 1.14