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