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