LCOV - code coverage report
Current view: top level - tools/monetdbe - monetdbe.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 440 1768 24.9 %
Date: 2024-10-03 20:03:20 Functions: 29 70 41.4 %

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

Generated by: LCOV version 1.14