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