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 : /*
14 : * @f sql_gencode
15 : * @t SQL to MAL code generation.
16 : * @a N. Nes, M. Kersten
17 : * @+ MAL Code generation
18 : * This module contains the actions to construct a MAL program, ready for
19 : * optimization and execution by the Monet V5 kernel.
20 : *
21 : * The code base is modeled directly after its MIL variant, replacing
22 : * each IO request by instructions to initialize the corresponding MAL data
23 : * structure.
24 : * To speed up the compilation, we may consider keeping a cache of pre-compiled
25 : * statements.
26 : *
27 : * MAL extensions needed. A temporary variable used as an argument
28 : * should be printed (done). Consider replacing modname/fcnname by
29 : * an integer constant and a global lookup table. This should
30 : * reduce the cost to prepare MAL statements significantly.
31 : *
32 : * A dummy module is needed to load properly.
33 : */
34 : #include "monetdb_config.h"
35 : #include "sql_gencode.h"
36 : #include "sql_optimizer.h"
37 : #include "sql_scenario.h"
38 : #include "sql_mvc.h"
39 : #include "sql_qc.h"
40 : #include "mal_namespace.h"
41 : #include "opt_prelude.h"
42 : #include "querylog.h"
43 : #include "mal_builder.h"
44 :
45 : #include "rel_select.h"
46 : #include "rel_prop.h"
47 : #include "rel_rel.h"
48 : #include "rel_exp.h"
49 : #include "rel_psm.h"
50 : #include "rel_bin.h"
51 : #include "rel_dump.h"
52 :
53 : #include "msabaoth.h" /* msab_getUUID */
54 : #include "muuid.h"
55 : #include "rel_remote.h"
56 : #include "rel_physical.h"
57 : #include "sql_user.h"
58 :
59 : int
60 2962809 : constantAtom(backend *sql, MalBlkPtr mb, atom *a)
61 : {
62 2962809 : int idx;
63 2962809 : ValPtr vr = (ValPtr) &a->data;
64 2962809 : ValRecord cst;
65 :
66 2962809 : (void) sql;
67 2962809 : cst.vtype = 0;
68 2962809 : if (VALcopy(&cst, vr) == NULL)
69 : return -1;
70 2963235 : idx = defConstant(mb, vr->vtype, &cst);
71 2963235 : return idx;
72 : }
73 :
74 : InstrPtr
75 2354 : table_func_create_result(MalBlkPtr mb, InstrPtr q, sql_func *f, list *restypes)
76 : {
77 2354 : node *n;
78 2354 : int i;
79 :
80 2354 : if (q == NULL)
81 : return NULL;
82 2354 : if (f->varres) {
83 11120 : for (i = 0, n = restypes->h; n; n = n->next, i++) {
84 10048 : sql_subtype *st = n->data;
85 10048 : int type = st->type->localtype;
86 :
87 10048 : type = newBatType(type);
88 10048 : if (i) {
89 8976 : if ((q = pushReturn(mb, q, newTmpVariable(mb, type))) == NULL)
90 : return NULL;
91 : } else
92 1072 : setVarType(mb, getArg(q, 0), type);
93 : }
94 : } else {
95 11793 : for (i = 0, n = f->res->h; n; n = n->next, i++) {
96 10511 : sql_arg *a = n->data;
97 10511 : int type = a->type.type->localtype;
98 :
99 10511 : type = newBatType(type);
100 10511 : if (i) {
101 9229 : if ((q = pushReturn(mb, q, newTmpVariable(mb, type))) == NULL)
102 : return NULL;
103 : } else
104 1282 : setVarType(mb, getArg(q, 0), type);
105 : }
106 : }
107 : return q;
108 : }
109 :
110 : sql_rel *
111 536 : relational_func_create_result_part1(mvc *sql, sql_rel *r, int *nargs)
112 : {
113 536 : if (is_topn(r->op) || is_sample(r->op))
114 0 : r = r->l;
115 536 : if (!is_project(r->op))
116 0 : r = rel_project(sql->sa, r, rel_projections(sql, r, NULL, 1, 1));
117 536 : *nargs = list_length(r->exps);
118 535 : return r;
119 : }
120 :
121 : InstrPtr
122 538 : relational_func_create_result_part2(MalBlkPtr mb, InstrPtr q, sql_rel *r)
123 : {
124 538 : node *n;
125 538 : int i;
126 :
127 538 : if (q == NULL)
128 : return NULL;
129 538 : q->argc = q->retc = 0;
130 4734 : for (i = 0, n = r->exps->h; n; n = n->next, i++) {
131 4196 : sql_exp *e = n->data;
132 4196 : int type = exp_subtype(e)->type->localtype;
133 :
134 4195 : type = newBatType(type);
135 4195 : q = pushReturn(mb, q, newTmpVariable(mb, type));
136 : }
137 : return q;
138 : }
139 :
140 : static int
141 177 : _create_relational_function_body(mvc *m, sql_rel *r, stmt *call, list *rel_ops, int inline_func)
142 : {
143 177 : Client c = MCgetClient(m->clientid);
144 176 : backend *be = (backend *) c->sqlcontext;
145 176 : MalBlkPtr curBlk = 0;
146 176 : InstrPtr curInstr = 0;
147 176 : int res = 0, added_to_cache = 0;
148 176 : str msg = MAL_SUCCEED;
149 :
150 176 : curBlk = c->curprg->def;
151 176 : curInstr = getInstrPtr(curBlk, 0);
152 :
153 176 : curInstr = relational_func_create_result_part2(curBlk, curInstr, r);
154 178 : if( curInstr == NULL) {
155 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
156 0 : res = -1;
157 0 : goto cleanup;
158 : }
159 :
160 : /* ops */
161 178 : if (call && call->type == st_list) {
162 0 : list *ops = call->op4.lval;
163 :
164 0 : for (node *n = ops->h; n && !curBlk->errors; n = n->next) {
165 0 : stmt *op = n->data;
166 0 : sql_subtype *t = tail_type(op);
167 0 : int type = t->type->localtype;
168 0 : int varid = 0;
169 0 : const char *nme = (op->op3)?op->op3->op4.aval->data.val.sval:op->cname;
170 0 : char *buf;
171 :
172 0 : if (nme[0] != 'A') {
173 0 : buf = SA_NEW_ARRAY(m->sa, char, strlen(nme) + 2);
174 0 : if (buf)
175 0 : stpcpy(stpcpy(buf, "A"), nme);
176 : } else {
177 0 : buf = sa_strdup(m->sa, nme);
178 : }
179 0 : if (!buf) {
180 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
181 0 : res = -1;
182 0 : goto cleanup;
183 : }
184 0 : if ((varid = newVariable(curBlk, buf, strlen(buf), type)) < 0) {
185 0 : sql_error(m, 10, SQLSTATE(42000) "Internal error while compiling statement: variable id too long");
186 0 : res = -1;
187 0 : goto cleanup;
188 : }
189 0 : curInstr = pushArgument(curBlk, curInstr, varid);
190 0 : setVarType(curBlk, varid, type);
191 : }
192 178 : } else if (rel_ops) {
193 203 : for (node *n = rel_ops->h; n && !curBlk->errors; n = n->next) {
194 28 : sql_exp *e = n->data;
195 28 : sql_subtype *t = exp_subtype(e);
196 28 : int type = t->type->localtype;
197 28 : int varid = 0;
198 28 : char *buf;
199 :
200 28 : if (e->type == e_atom) {
201 0 : buf = SA_NEW_ARRAY(m->sa, char, IDLENGTH);
202 0 : if (buf)
203 0 : snprintf(buf, IDLENGTH, "A%u", e->flag);
204 : } else {
205 28 : const char *nme = exp_name(e);
206 28 : buf = SA_NEW_ARRAY(m->sa, char, strlen(nme) + 2);
207 28 : if (buf)
208 28 : stpcpy(stpcpy(buf, "A"), nme);
209 : }
210 28 : if (!buf) {
211 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
212 0 : res = -1;
213 0 : goto cleanup;
214 : }
215 28 : if ((varid = newVariable(curBlk, (char *)buf, strlen(buf), type)) < 0) {
216 0 : sql_error(m, 10, SQLSTATE(42000) "Internal error while compiling statement: variable id too long");
217 0 : res = -1;
218 0 : goto cleanup;
219 : }
220 27 : curInstr = pushArgument(curBlk, curInstr, varid);
221 27 : setVarType(curBlk, varid, type);
222 : }
223 : }
224 177 : if (curBlk->errors) {
225 0 : sql_error(m, 10, SQLSTATE(42000) "Internal error while compiling statement: %s", curBlk->errors);
226 0 : res = -1;
227 0 : goto cleanup;
228 : }
229 :
230 : /* add return statement */
231 177 : sql_exp *e;
232 177 : r = rel_psm_stmt(m->sa, e = exp_return(m->sa, exp_rel(m, r), 0));
233 175 : e->card = CARD_MULTI;
234 175 : if ((res = backend_dumpstmt(be, curBlk, r, 0, 1, NULL) < 0))
235 0 : goto cleanup;
236 : /* SQL function definitions meant for inlining should not be optimized before */
237 176 : if (inline_func)
238 0 : curBlk->inlineProp = 1;
239 : /* optimize the code */
240 176 : SQLaddQueryToCache(c);
241 177 : added_to_cache = 1;
242 177 : if (curBlk->inlineProp == 0 && !c->curprg->def->errors) {
243 177 : msg = SQLoptimizeQuery(c, c->curprg->def);
244 0 : } else if (curBlk->inlineProp != 0) {
245 0 : if( msg == MAL_SUCCEED)
246 0 : msg = chkProgram(c->usermodule, c->curprg->def);
247 1 : if (msg == MAL_SUCCEED && !c->curprg->def->errors)
248 0 : msg = SQLoptimizeFunction(c,c->curprg->def);
249 : }
250 176 : if (msg) {
251 0 : if (c->curprg->def->errors)
252 0 : freeException(msg);
253 : else
254 0 : c->curprg->def->errors = msg;
255 : }
256 177 : if (c->curprg->def->errors) {
257 0 : sql_error(m, 10, SQLSTATE(42000) "Internal error while compiling statement: %s", c->curprg->def->errors);
258 0 : res = -1;
259 : }
260 :
261 177 : cleanup:
262 0 : if (res < 0) {
263 0 : if (!added_to_cache)
264 0 : freeSymbol(c->curprg);
265 : else
266 0 : SQLremoveQueryFromCache(c);
267 : }
268 177 : return res;
269 : }
270 :
271 : static int
272 174 : _create_relational_function(mvc *m, const char *mod, const char *name, sql_rel *r, stmt *call, list *rel_ops, int inline_func)
273 : {
274 174 : Client c = MCgetClient(m->clientid);
275 175 : backend *be = (backend *) c->sqlcontext;
276 175 : Symbol symbackup = c->curprg;
277 175 : backend bebackup = *be; /* backup current backend */
278 175 : exception_buffer ebsave = m->sa->eb;
279 :
280 175 : if (strlen(mod) >= IDLENGTH) {
281 0 : (void) sql_error(m, 10, SQLSTATE(42000) "Module name '%s' too large for the backend", mod);
282 0 : goto bailout;
283 : }
284 175 : if (strlen(name) >= IDLENGTH) {
285 0 : (void) sql_error(m, 10, SQLSTATE(42000) "Function name '%s' too large for the backend", name);
286 0 : goto bailout;
287 : }
288 :
289 175 : backend_reset(be);
290 :
291 173 : int nargs;
292 173 : sql_rel *nr = relational_func_create_result_part1(m, r, &nargs);
293 172 : nargs += (call && call->type == st_list) ? list_length(call->op4.lval) : rel_ops ? list_length(rel_ops) : 0;
294 :
295 171 : c->curprg = newFunctionArgs(putName(mod), putName(name), FUNCTIONsymbol, nargs);
296 176 : if (c->curprg == NULL) {
297 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
298 0 : goto bailout;
299 177 : } else if (eb_savepoint(&m->sa->eb)) {
300 0 : sql_error(m, 10, "%s", m->sa->eb.msg);
301 0 : freeSymbol(c->curprg);
302 0 : goto bailout;
303 177 : } else if (_create_relational_function_body(m, nr, call, rel_ops, inline_func) < 0) {
304 0 : goto bailout;
305 : }
306 176 : *be = bebackup;
307 176 : c->curprg = symbackup;
308 176 : m->sa->eb = ebsave;
309 176 : return 0;
310 0 : bailout:
311 0 : *be = bebackup;
312 0 : c->curprg = symbackup;
313 0 : m->sa->eb = ebsave;
314 0 : if (m->sa->eb.enabled)
315 0 : eb_error(&m->sa->eb, m->errstr[0] ? m->errstr : be->mb->errors ? be->mb->errors : *GDKerrbuf ? GDKerrbuf : "out of memory", 1000);
316 : return -1;
317 : }
318 :
319 : static str
320 181 : rel2str( mvc *sql, sql_rel *rel)
321 : {
322 181 : buffer *b = NULL;
323 181 : stream *s = NULL;
324 181 : list *refs = NULL;
325 181 : char *res = NULL;
326 :
327 181 : b = buffer_create(1024);
328 181 : if(b == NULL)
329 0 : goto cleanup;
330 181 : s = buffer_wastream(b, "rel_dump");
331 181 : if(s == NULL)
332 0 : goto cleanup;
333 181 : refs = sa_list(sql->sa);
334 181 : if (!refs)
335 0 : goto cleanup;
336 :
337 181 : rel_print_refs(sql, s, rel, 0, refs, 0);
338 181 : rel_print_(sql, s, rel, 0, refs, 0);
339 181 : mnstr_printf(s, "\n");
340 181 : res = buffer_get_buf(b);
341 :
342 181 : cleanup:
343 181 : if(b)
344 181 : buffer_destroy(b);
345 181 : if(s)
346 181 : close_stream(s);
347 181 : return res;
348 : }
349 :
350 : /* stub and remote function */
351 : static int
352 181 : _create_relational_remote_body(mvc *m, const char *mod, const char *name, sql_rel *rel, sql_rel *rel2, stmt *call, prop *prp)
353 : {
354 181 : Client c = MCgetClient(m->clientid);
355 181 : MalBlkPtr curBlk = 0;
356 181 : InstrPtr curInstr = 0, p, o;
357 181 : tid_uri *tu = ((list*)prp->value.pval)->h->data;
358 181 : sqlid table_id = tu->id;
359 181 : assert(table_id);
360 181 : node *n;
361 181 : int i, q, v, res = -1, added_to_cache = 0, *lret, *rret;
362 181 : size_t len = 1024, nr, pwlen = 0;
363 181 : char *lname = NULL, *buf = NULL, *mal_session_uuid, *err = NULL, *pwhash = NULL;
364 181 : str username = NULL, password = NULL, msg = NULL;
365 181 : sql_rel *r = rel;
366 :
367 181 : lname = sa_strdup(m->ta, name);
368 181 : if (lname == NULL) {
369 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
370 0 : goto cleanup;
371 : }
372 :
373 181 : if (is_topn(r->op) || is_sample(r->op))
374 0 : r = r->l;
375 181 : if (!is_project(r->op))
376 0 : r = rel_project(m->sa, r, rel_projections(m, r, NULL, 1, 1));
377 181 : lret = SA_NEW_ARRAY(m->sa, int, list_length(r->exps));
378 181 : if (lret == NULL) {
379 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
380 0 : goto cleanup;
381 : }
382 181 : rret = SA_NEW_ARRAY(m->sa, int, list_length(r->exps));
383 181 : if (rret == NULL) {
384 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
385 0 : goto cleanup;
386 : }
387 :
388 181 : lname[0] = 'l';
389 181 : curBlk = c->curprg->def;
390 181 : curInstr = getInstrPtr(curBlk, 0);
391 :
392 181 : sql_table *rt = sql_trans_find_table(m->session->tr, table_id);
393 181 : const char *uri = mapiuri_uri(rt->query, m->sa);
394 181 : assert(strcmp(tu->uri, uri) == 0);
395 181 : if (!rt) {
396 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
397 : goto cleanup;
398 : }
399 181 : curInstr = relational_func_create_result_part2(curBlk, curInstr, rel2);
400 181 : if( curInstr == NULL) {
401 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
402 0 : goto cleanup;
403 : }
404 :
405 : /* ops */
406 181 : if (call && call->type == st_list) {
407 181 : char nbuf[IDLENGTH];
408 181 : int i = 0;
409 :
410 203 : for (node *n = call->op4.lval->h; n; n = n->next) {
411 22 : stmt *op = n->data;
412 22 : sql_subtype *t = tail_type(op);
413 22 : int type = t->type->localtype, varid = 0;
414 :
415 22 : sprintf(nbuf, "A%d", i++);
416 22 : if ((varid = newVariable(curBlk, nbuf, strlen(nbuf), type)) < 0) {
417 0 : sql_error(m, 10, SQLSTATE(42000) "Internal error while compiling statement: variable id too long");
418 0 : goto cleanup;
419 : }
420 22 : curInstr = pushArgument(curBlk, curInstr, varid);
421 22 : setVarType(curBlk, varid, type);
422 : }
423 : }
424 :
425 : /* declare return variables */
426 181 : if (!list_empty(r->exps)) {
427 1581 : for (i = 0, n = r->exps->h; n; n = n->next, i++) {
428 1400 : sql_exp *e = n->data;
429 1400 : int type = exp_subtype(e)->type->localtype;
430 :
431 1400 : type = newBatType(type);
432 1400 : p = newFcnCall(curBlk, batRef, newRef);
433 1400 : if (p == NULL) {
434 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
435 0 : goto cleanup;
436 : }
437 1400 : p = pushType(curBlk, p, getBatType(type));
438 1400 : setArgType(curBlk, p, 0, type);
439 1400 : lret[i] = getArg(p, 0);
440 1400 : pushInstruction(curBlk, p);
441 : }
442 : }
443 :
444 : /* get username / password */
445 181 : msg = remote_get(m, table_id, &username, &password);
446 181 : if (msg) {
447 0 : sql_error(m, 10, "%s", msg);
448 0 : freeException(msg);
449 0 : msg = NULL;
450 0 : goto cleanup;
451 : }
452 : /* q := remote.connect("uri", "username", "password", "msql"); */
453 181 : p = newStmt(curBlk, remoteRef, connectRef);
454 181 : if (p == NULL) {
455 0 : GDKfree(username);
456 0 : GDKfree(password);
457 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
458 0 : goto cleanup;
459 : }
460 181 : p = pushStr(curBlk, p, uri);
461 181 : p = pushStr(curBlk, p, username);
462 181 : GDKfree(username);
463 181 : pwlen = strlen(password);
464 181 : pwhash = (char*)GDKmalloc(pwlen + 2);
465 181 : if (pwhash == NULL) {
466 0 : GDKfree(password);
467 0 : goto cleanup;
468 : }
469 181 : strconcat_len(pwhash, pwlen + 2, "\1", password, NULL);
470 181 : GDKfree(password);
471 181 : p = pushStr(curBlk, p, pwhash);
472 181 : GDKfree(pwhash);
473 181 : p = pushStr(curBlk, p, "msql");
474 181 : q = getArg(p, 0);
475 181 : pushInstruction(curBlk, p);
476 :
477 : /* remote.exec(q, "sql", "register", "mod", "name", "relational_plan", "signature"); */
478 181 : p = newInstructionArgs(curBlk, remoteRef, execRef, 10);
479 181 : if (p == NULL) {
480 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
481 0 : goto cleanup;
482 : }
483 181 : p = pushArgument(curBlk, p, q);
484 181 : p = pushStr(curBlk, p, sqlRef);
485 181 : p = pushStr(curBlk, p, registerRef);
486 :
487 181 : o = newFcnCall(curBlk, remoteRef, putRef);
488 181 : if (o == NULL) {
489 0 : freeInstruction(p);
490 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
491 0 : goto cleanup;
492 : }
493 181 : o = pushArgument(curBlk, o, q);
494 181 : o = pushInt(curBlk, o, TYPE_str); /* dummy result type */
495 181 : pushInstruction(curBlk, o);
496 181 : p = pushReturn(curBlk, p, getArg(o, 0));
497 :
498 181 : o = newFcnCall(curBlk, remoteRef, putRef);
499 181 : if (o == NULL) {
500 0 : freeInstruction(p);
501 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
502 0 : goto cleanup;
503 : }
504 181 : o = pushArgument(curBlk, o, q);
505 181 : o = pushStr(curBlk, o, mod);
506 181 : pushInstruction(curBlk, o);
507 181 : p = pushArgument(curBlk, p, getArg(o,0));
508 :
509 181 : o = newFcnCall(curBlk, remoteRef, putRef);
510 181 : if (o == NULL) {
511 0 : freeInstruction(p);
512 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
513 0 : goto cleanup;
514 : }
515 181 : o = pushArgument(curBlk, o, q);
516 181 : o = pushStr(curBlk, o, lname);
517 181 : pushInstruction(curBlk, o);
518 181 : p = pushArgument(curBlk, p, getArg(o,0));
519 :
520 181 : if (!(buf = rel2str(m, rel))) {
521 0 : freeInstruction(p);
522 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
523 0 : goto cleanup;
524 : }
525 181 : o = newFcnCall(curBlk, remoteRef, putRef);
526 181 : if (o == NULL) {
527 0 : free(buf);
528 0 : freeInstruction(p);
529 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
530 0 : goto cleanup;
531 : }
532 181 : o = pushArgument(curBlk, o, q);
533 181 : o = pushStr(curBlk, o, buf); /* relational plan */
534 181 : pushInstruction(curBlk, o);
535 181 : p = pushArgument(curBlk, p, getArg(o,0));
536 181 : free(buf);
537 :
538 181 : if (!(buf = sa_alloc(m->ta, len))) {
539 0 : freeInstruction(p);
540 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
541 0 : goto cleanup;
542 : }
543 :
544 181 : buf[0] = 0;
545 181 : if (call && call->type == st_list) { /* Send existing variables in the plan */
546 181 : char dbuf[32], sbuf[32];
547 :
548 181 : nr = 0;
549 203 : for (node *n = call->op4.lval->h; n; n = n->next) {
550 22 : stmt *op = n->data;
551 22 : sql_subtype *t = tail_type(op);
552 22 : const char *nme = (op->op3)?op->op3->op4.aval->data.val.sval:op->cname;
553 :
554 22 : sprintf(dbuf, "%u", t->digits);
555 22 : sprintf(sbuf, "%u", t->scale);
556 22 : size_t nlen = strlen(nme) + strlen(t->type->base.name) + strlen(dbuf) + strlen(sbuf) + 6;
557 :
558 22 : if ((nr + nlen) > len) {
559 0 : buf = sa_realloc(m->ta, buf, (len + nlen) * 2, len);
560 0 : if (buf == NULL) {
561 0 : freeInstruction(p);
562 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
563 0 : goto cleanup;
564 : }
565 : len = (len + nlen) * 2;
566 : }
567 :
568 32 : nr += snprintf(buf+nr, len-nr, "%s %s(%s,%s)%c", nme, t->type->base.name, dbuf, sbuf, n->next?',':' ');
569 : }
570 : }
571 181 : o = newFcnCall(curBlk, remoteRef, putRef);
572 181 : if (o == NULL) {
573 0 : freeInstruction(p);
574 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
575 0 : goto cleanup;
576 : }
577 181 : o = pushArgument(curBlk, o, q);
578 181 : o = pushStr(curBlk, o, buf); /* signature */
579 181 : pushInstruction(curBlk, o);
580 181 : p = pushArgument(curBlk, p, getArg(o,0));
581 :
582 181 : buf[0] = 0;
583 181 : if (!list_empty(r->exps)) {
584 181 : nr = 0;
585 1581 : for (n = r->exps->h; n; n = n->next) { /* Send SQL types of the projection's expressions */
586 1400 : sql_exp *e = n->data;
587 1400 : sql_subtype *t = exp_subtype(e);
588 1400 : str next = sql_subtype_string(m->ta, t);
589 :
590 1400 : if (!next) {
591 0 : freeInstruction(p);
592 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
593 0 : goto cleanup;
594 : }
595 :
596 1400 : size_t nlen = strlen(next) + 2;
597 1400 : if ((nr + nlen) > len) {
598 2 : buf = sa_realloc(m->ta, buf, (len + nlen) * 2, len);
599 2 : if (buf == NULL) {
600 0 : freeInstruction(p);
601 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
602 0 : goto cleanup;
603 : }
604 : len = (len + nlen) * 2;
605 : }
606 :
607 1581 : nr += snprintf(buf+nr, len-nr, "%s%s", next, n->next?"%":"");
608 : }
609 : }
610 181 : o = newFcnCall(curBlk, remoteRef, putRef);
611 181 : if (o == NULL) {
612 0 : freeInstruction(p);
613 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
614 0 : goto cleanup;
615 : }
616 181 : o = pushArgument(curBlk, o, q);
617 181 : o = pushStr(curBlk, o, buf); /* SQL types as a single string */
618 181 : pushInstruction(curBlk, o);
619 181 : p = pushArgument(curBlk, p, getArg(o,0));
620 181 : pushInstruction(curBlk, p);
621 :
622 362 : if (!GDKinmemory(0) && !GDKembedded() && (err = msab_getUUID(&mal_session_uuid)) == NULL) {
623 181 : str lsupervisor_session = GDKstrdup(mal_session_uuid);
624 181 : str rsupervisor_session = GDKstrdup(mal_session_uuid);
625 181 : free(mal_session_uuid);
626 181 : if (lsupervisor_session == NULL || rsupervisor_session == NULL) {
627 0 : GDKfree(lsupervisor_session);
628 0 : GDKfree(rsupervisor_session);
629 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
630 0 : goto cleanup;
631 : }
632 :
633 181 : str rworker_plan_uuid = generateUUID();
634 181 : str lworker_plan_uuid = GDKstrdup(rworker_plan_uuid);
635 :
636 : /* remote.supervisor_register(connection, supervisor_uuid, plan_uuid) */
637 181 : p = newInstruction(curBlk, remoteRef, execRef);
638 181 : if (rworker_plan_uuid == NULL || lworker_plan_uuid == NULL || p == NULL) {
639 0 : free(rworker_plan_uuid);
640 0 : GDKfree(lworker_plan_uuid);
641 0 : freeInstruction(p);
642 0 : GDKfree(lsupervisor_session);
643 0 : GDKfree(rsupervisor_session);
644 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
645 0 : goto cleanup;
646 : }
647 181 : p = pushArgument(curBlk, p, q);
648 181 : p = pushStr(curBlk, p, remoteRef);
649 181 : p = pushStr(curBlk, p, register_supervisorRef);
650 181 : getArg(p, 0) = -1;
651 :
652 : /* We don't really care about the return value of supervisor_register,
653 : * but I have not found a good way to remotely execute a void mal function
654 : */
655 181 : o = newFcnCall(curBlk, remoteRef, putRef);
656 181 : if (o == NULL) {
657 0 : freeInstruction(p);
658 0 : free(rworker_plan_uuid);
659 0 : GDKfree(lworker_plan_uuid);
660 0 : GDKfree(lsupervisor_session);
661 0 : GDKfree(rsupervisor_session);
662 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
663 0 : goto cleanup;
664 : }
665 181 : o = pushArgument(curBlk, o, q);
666 181 : o = pushInt(curBlk, o, TYPE_int);
667 181 : pushInstruction(curBlk, o);
668 181 : p = pushReturn(curBlk, p, getArg(o, 0));
669 :
670 181 : o = newFcnCall(curBlk, remoteRef, putRef);
671 181 : if (o == NULL) {
672 0 : freeInstruction(p);
673 0 : free(rworker_plan_uuid);
674 0 : GDKfree(lworker_plan_uuid);
675 0 : GDKfree(lsupervisor_session);
676 0 : GDKfree(rsupervisor_session);
677 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
678 0 : goto cleanup;
679 : }
680 181 : o = pushArgument(curBlk, o, q);
681 181 : o = pushStr(curBlk, o, rsupervisor_session);
682 181 : pushInstruction(curBlk, o);
683 181 : p = pushArgument(curBlk, p, getArg(o, 0));
684 :
685 181 : o = newFcnCall(curBlk, remoteRef, putRef);
686 181 : if (o == NULL) {
687 0 : freeInstruction(p);
688 0 : free(rworker_plan_uuid);
689 0 : GDKfree(lworker_plan_uuid);
690 0 : GDKfree(lsupervisor_session);
691 0 : GDKfree(rsupervisor_session);
692 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
693 0 : goto cleanup;
694 : }
695 181 : o = pushArgument(curBlk, o, q);
696 181 : o = pushStr(curBlk, o, rworker_plan_uuid);
697 181 : pushInstruction(curBlk, o);
698 181 : p = pushArgument(curBlk, p, getArg(o, 0));
699 :
700 181 : pushInstruction(curBlk, p);
701 :
702 : /* Execute the same instruction locally */
703 181 : p = newStmt(curBlk, remoteRef, register_supervisorRef);
704 181 : if (p == NULL) {
705 0 : free(rworker_plan_uuid);
706 0 : GDKfree(lworker_plan_uuid);
707 0 : GDKfree(lsupervisor_session);
708 0 : GDKfree(rsupervisor_session);
709 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
710 0 : goto cleanup;
711 : }
712 181 : p = pushStr(curBlk, p, lsupervisor_session);
713 181 : p = pushStr(curBlk, p, lworker_plan_uuid);
714 181 : pushInstruction(curBlk, p);
715 :
716 181 : GDKfree(lworker_plan_uuid);
717 181 : free(rworker_plan_uuid); /* This was created with strdup */
718 181 : GDKfree(lsupervisor_session);
719 181 : GDKfree(rsupervisor_session);
720 0 : } else if (err)
721 0 : free(err);
722 :
723 : /* (x1, x2, ..., xn) := remote.exec(q, "mod", "fcn"); */
724 181 : p = newInstructionArgs(curBlk, remoteRef, execRef, list_length(r->exps) + curInstr->argc - curInstr->retc + 4);
725 181 : if (p == NULL) {
726 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
727 0 : goto cleanup;
728 : }
729 181 : p = pushArgument(curBlk, p, q);
730 181 : p = pushStr(curBlk, p, mod);
731 181 : p = pushStr(curBlk, p, lname);
732 181 : getArg(p, 0) = -1;
733 :
734 181 : if (!list_empty(r->exps)) {
735 1581 : for (i = 0, n = r->exps->h; n; n = n->next, i++) {
736 : /* x1 := remote.put(q, :type) */
737 1400 : o = newFcnCall(curBlk, remoteRef, putRef);
738 1400 : if (o == NULL) {
739 0 : freeInstruction(p);
740 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
741 0 : goto cleanup;
742 : }
743 1400 : o = pushArgument(curBlk, o, q);
744 1400 : o = pushArgument(curBlk, o, lret[i]);
745 1400 : pushInstruction(curBlk, o);
746 1400 : v = getArg(o, 0);
747 1400 : p = pushReturn(curBlk, p, v);
748 1400 : rret[i] = v;
749 : }
750 : }
751 :
752 : /* send arguments to remote */
753 203 : for (i = curInstr->retc; i < curInstr->argc; i++) {
754 : /* x1 := remote.put(q, A0); */
755 22 : o = newStmt(curBlk, remoteRef, putRef);
756 22 : if (o == NULL) {
757 0 : freeInstruction(p);
758 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
759 0 : goto cleanup;
760 : }
761 22 : o = pushArgument(curBlk, o, q);
762 22 : o = pushArgument(curBlk, o, getArg(curInstr, i));
763 22 : pushInstruction(curBlk, o);
764 22 : p = pushArgument(curBlk, p, getArg(o, 0));
765 : }
766 181 : pushInstruction(curBlk, p);
767 :
768 : /* return results */
769 1762 : for (i = 0; i < curInstr->retc; i++) {
770 : /* y1 := remote.get(q, x1); */
771 1400 : p = newFcnCall(curBlk, remoteRef, getRef);
772 1400 : if (p == NULL) {
773 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
774 0 : goto cleanup;
775 : }
776 1400 : p = pushArgument(curBlk, p, q);
777 1400 : p = pushArgument(curBlk, p, rret[i]);
778 1400 : pushInstruction(curBlk, p);
779 1400 : getArg(p, 0) = lret[i];
780 : }
781 :
782 : /* end remote transaction */
783 181 : p = newInstruction(curBlk, remoteRef, execRef);
784 181 : if (p == NULL) {
785 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
786 0 : goto cleanup;
787 : }
788 181 : p = pushArgument(curBlk, p, q);
789 181 : p = pushStr(curBlk, p, sqlRef);
790 181 : p = pushStr(curBlk, p, deregisterRef);
791 181 : getArg(p, 0) = -1;
792 :
793 181 : o = newFcnCall(curBlk, remoteRef, putRef);
794 181 : if (o == NULL) {
795 0 : freeInstruction(p);
796 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
797 0 : goto cleanup;
798 : }
799 181 : o = pushArgument(curBlk, o, q);
800 181 : o = pushInt(curBlk, o, TYPE_int);
801 181 : pushInstruction(curBlk, o);
802 181 : p = pushReturn(curBlk, p, getArg(o, 0));
803 181 : pushInstruction(curBlk, p);
804 :
805 : /* remote.disconnect(q); */
806 181 : p = newStmt(curBlk, remoteRef, disconnectRef);
807 181 : if (p == NULL) {
808 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
809 0 : goto cleanup;
810 : }
811 181 : p = pushArgument(curBlk, p, q);
812 181 : pushInstruction(curBlk, p);
813 :
814 181 : p = newInstructionArgs(curBlk, NULL, NULL, 2 * curInstr->retc);
815 181 : if (p == NULL) {
816 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
817 0 : goto cleanup;
818 : }
819 181 : p->barrier= RETURNsymbol;
820 181 : p->retc = p->argc = 0;
821 1581 : for (i = 0; i < curInstr->retc; i++)
822 1400 : p = pushArgument(curBlk, p, lret[i]);
823 181 : p->retc = p->argc;
824 : /* assignment of return */
825 1581 : for (i = 0; i < curInstr->retc; i++)
826 1400 : p = pushArgument(curBlk, p, lret[i]);
827 181 : pushInstruction(curBlk, p);
828 :
829 : /* catch exceptions */
830 181 : p = newCatchStmt(curBlk, "ANYexception");
831 181 : if (p == NULL) {
832 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
833 0 : goto cleanup;
834 : }
835 181 : pushInstruction(curBlk, p);
836 181 : p = newExitStmt(curBlk, "ANYexception");
837 181 : if (p == NULL) {
838 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
839 0 : goto cleanup;
840 : }
841 181 : pushInstruction(curBlk, p);
842 :
843 : /* end remote transaction */
844 181 : p = newInstruction(curBlk, remoteRef, execRef);
845 181 : if (p == NULL) {
846 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
847 0 : goto cleanup;
848 : }
849 181 : p = pushArgument(curBlk, p, q);
850 181 : p = pushStr(curBlk, p, sqlRef);
851 181 : p = pushStr(curBlk, p, deregisterRef);
852 181 : getArg(p, 0) = -1;
853 :
854 181 : o = newFcnCall(curBlk, remoteRef, putRef);
855 181 : if (o == NULL) {
856 0 : freeInstruction(p);
857 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
858 0 : goto cleanup;
859 : }
860 181 : o = pushArgument(curBlk, o, q);
861 181 : o = pushInt(curBlk, o, TYPE_int);
862 181 : pushInstruction(curBlk, o);
863 181 : p = pushReturn(curBlk, p, getArg(o, 0));
864 181 : pushInstruction(curBlk, p);
865 :
866 : /* remote.disconnect(q); */
867 181 : p = newStmt(curBlk, remoteRef, disconnectRef);
868 181 : if (p == NULL) {
869 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
870 0 : goto cleanup;
871 : }
872 181 : p = pushArgument(curBlk, p, q);
873 181 : pushInstruction(curBlk, p);
874 :
875 : /* the connection may not start (eg bad credentials),
876 : so calling 'disconnect' on the catch block may throw another exception, add another catch */
877 181 : p = newCatchStmt(curBlk, "ANYexception");
878 181 : if (p == NULL) {
879 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
880 0 : goto cleanup;
881 : }
882 181 : pushInstruction(curBlk, p);
883 181 : p = newExitStmt(curBlk, "ANYexception");
884 181 : if (p == NULL) {
885 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
886 0 : goto cleanup;
887 : }
888 181 : pushInstruction(curBlk, p);
889 :
890 : /* throw the exception back */
891 181 : p = newRaiseStmt(curBlk, "RemoteException");
892 181 : if (p == NULL) {
893 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
894 0 : goto cleanup;
895 : }
896 181 : p = pushStr(curBlk, p, "Exception occurred in the remote server, please check the log there");
897 181 : pushInstruction(curBlk, p);
898 :
899 181 : pushEndInstruction(curBlk);
900 :
901 : /* SQL function definitions meant for inlineing should not be optimized before */
902 : //for now no inline of the remote function, this gives garbage collection problems
903 : //curBlk->inlineProp = 1;
904 :
905 181 : SQLaddQueryToCache(c);
906 181 : added_to_cache = 1;
907 : // (str) chkProgram(c->usermodule, c->curprg->def);
908 181 : if (!c->curprg->def->errors)
909 181 : c->curprg->def->errors = SQLoptimizeFunction(c, c->curprg->def);
910 181 : if (c->curprg->def->errors) {
911 0 : sql_error(m, 10, SQLSTATE(42000) "Internal error while compiling statement: %s", c->curprg->def->errors);
912 : } else {
913 : res = 0;
914 : }
915 :
916 : cleanup:
917 0 : if (res < 0 && c->curprg) {
918 0 : if (!added_to_cache) /* on error, remove generated symbol from cache */
919 0 : freeSymbol(c->curprg);
920 : else
921 0 : SQLremoveQueryFromCache(c);
922 : }
923 181 : return res;
924 : }
925 :
926 : static int
927 181 : _create_relational_remote(mvc *m, const char *mod, const char *name, sql_rel *rel, stmt *call, prop *prp)
928 : {
929 181 : Client c = MCgetClient(m->clientid);
930 181 : backend *be = (backend *) c->sqlcontext;
931 181 : Symbol symbackup = c->curprg;
932 181 : exception_buffer ebsave = m->sa->eb;
933 :
934 181 : if (list_empty(prp->value.pval)) {
935 0 : sql_error(m, 003, SQLSTATE(42000) "Missing REMOTE property on the input relation");
936 0 : goto bailout;
937 : }
938 181 : if (list_length(prp->value.pval) != 1) {
939 0 : sql_error(m, 003, SQLSTATE(42000) "REMOTE property on the input relation is NOT unique");
940 0 : goto bailout;
941 : }
942 181 : if (strlen(mod) >= IDLENGTH) {
943 0 : (void) sql_error(m, 10, SQLSTATE(42000) "Module name '%s' too large for the backend", mod);
944 0 : goto bailout;
945 : }
946 181 : if (strlen(name) >= IDLENGTH) {
947 0 : (void) sql_error(m, 10, SQLSTATE(42000) "Function name '%s' too large for the backend", name);
948 0 : goto bailout;
949 : }
950 :
951 : /* create stub */
952 181 : int nargs;
953 181 : sql_rel *rel2 = relational_func_create_result_part1(m, rel, &nargs);
954 181 : if (call && call->type == st_list)
955 181 : nargs += list_length(call->op4.lval);
956 181 : c->curprg = newFunctionArgs(putName(mod), putName(name), FUNCTIONsymbol, nargs);
957 181 : if (c->curprg == NULL) {
958 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
959 0 : goto bailout;
960 181 : } else if (eb_savepoint(&m->sa->eb)) {
961 0 : sql_error(m, 10, "%s", m->sa->eb.msg);
962 0 : freeSymbol(c->curprg);
963 0 : goto bailout;
964 181 : } else if (_create_relational_remote_body(m, mod, name, rel, rel2, call, prp) < 0) {
965 0 : goto bailout;
966 : }
967 181 : sa_reset(m->ta);
968 181 : c->curprg = symbackup;
969 181 : m->sa->eb = ebsave;
970 181 : return 0;
971 0 : bailout:
972 0 : sa_reset(m->ta);
973 0 : c->curprg = symbackup;
974 0 : m->sa->eb = ebsave;
975 0 : if (m->sa->eb.enabled)
976 0 : eb_error(&m->sa->eb, m->errstr[0] ? m->errstr : be->mb->errors ? be->mb->errors : *GDKerrbuf ? GDKerrbuf : "out of memory", 1000);
977 : return -1;
978 : }
979 :
980 : int
981 355 : monet5_create_relational_function(mvc *m, const char *mod, const char *name, sql_rel *rel, stmt *call, list *rel_ops, int inline_func)
982 : {
983 355 : prop *p = NULL;
984 :
985 355 : if (rel && (p = find_prop(rel->p, PROP_REMOTE)) != NULL)
986 181 : return _create_relational_remote(m, mod, name, rel, call, p);
987 : else
988 176 : return _create_relational_function(m, mod, name, rel, call, rel_ops, inline_func);
989 : }
990 :
991 : /*
992 : * The kernel uses two calls to procedures defined in SQL.
993 : * They have to be initialized, which is currently hacked
994 : * by using the SQLstatment.
995 : */
996 : static stmt *
997 554416 : sql_relation2stmt(backend *be, sql_rel *r, int top)
998 : {
999 554416 : mvc *c = be->mvc;
1000 554416 : stmt *s = NULL;
1001 :
1002 554416 : if (!r) {
1003 0 : sql_error(c, 003, SQLSTATE(42000) "Missing relation to convert into statements");
1004 0 : return NULL;
1005 : } else {
1006 554416 : if (c->emode == m_plan) {
1007 266 : rel_print(c, r, 0);
1008 : } else {
1009 554150 : s = output_rel_bin(be, r, top);
1010 : }
1011 : }
1012 : return s;
1013 : }
1014 :
1015 : static int
1016 : #if defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 8
1017 : /* bug on CentOS 7 (gnuc 4.8.5) where this function gets inlined and
1018 : * the compiler then complains about query getting modified after the
1019 : * setjmp call; fix is to explicitly prevent inlining */
1020 : __attribute__((__noinline__))
1021 : #endif
1022 554350 : backend_dumpstmt_body(backend *be, MalBlkPtr mb, sql_rel *r, int top, int add_end, const char *query)
1023 : {
1024 554350 : mvc *m = be->mvc;
1025 554350 : InstrPtr q, querylog = NULL;
1026 554350 : int old_mv = be->mvc_var;
1027 554350 : MalBlkPtr old_mb = be->mb;
1028 :
1029 : /* Always keep the SQL query around for monitoring */
1030 554350 : if (query) {
1031 247893 : while (*query && isspace((unsigned char) *query))
1032 0 : query++;
1033 :
1034 247893 : querylog = q = newStmt(mb, querylogRef, defineRef);
1035 247862 : if (q == NULL) {
1036 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1037 0 : return -1;
1038 : }
1039 247862 : setVarType(mb, getArg(q, 0), TYPE_void);
1040 247862 : q = pushStr(mb, q, query);
1041 247440 : q = pushStr(mb, q, getSQLoptimizer(be->mvc));
1042 247764 : pushInstruction(mb, q);
1043 : }
1044 :
1045 : /* announce the transaction mode */
1046 554281 : q = newStmt(mb, sqlRef, mvcRef);
1047 554418 : if (q == NULL) {
1048 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1049 0 : return -1;
1050 : }
1051 554418 : pushInstruction(mb, q);
1052 554418 : be->mvc_var = getDestVar(q);
1053 554418 : be->mb = mb;
1054 554418 : if (!sql_relation2stmt(be, r, top)) {
1055 274 : if (querylog)
1056 274 : (void) pushInt(mb, querylog, mb->stop);
1057 274 : return (be->mvc->errstr[0] == '\0') ? 0 : -1;
1058 : }
1059 :
1060 554075 : be->mvc_var = old_mv;
1061 554075 : be->mb = old_mb;
1062 554075 : if (top && !be->depth && (m->type == Q_SCHEMA || m->type == Q_TRANS) && !GDKembedded()) {
1063 18413 : q = newStmt(mb, sqlRef, exportOperationRef);
1064 18413 : if (q == NULL) {
1065 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1066 0 : return -1;
1067 : }
1068 18413 : pushInstruction(mb, q);
1069 : }
1070 554075 : if (add_end)
1071 306456 : pushEndInstruction(mb);
1072 554077 : if (querylog)
1073 247619 : (void) pushInt(mb, querylog, mb->stop);
1074 : return 0;
1075 : }
1076 :
1077 : int
1078 554395 : backend_dumpstmt(backend *be, MalBlkPtr mb, sql_rel *r, int top, int add_end, const char *query)
1079 : {
1080 554395 : mvc *m = be->mvc;
1081 554395 : exception_buffer ebsave = {.enabled = 0};
1082 :
1083 554395 : if (m->sa) {
1084 554395 : ebsave = m->sa->eb;
1085 554395 : if (eb_savepoint(&m->sa->eb)) {
1086 2 : (void) sql_error(m, 10, "%s", m->sa->eb.msg);
1087 2 : goto bailout;
1088 : }
1089 : }
1090 554393 : if (backend_dumpstmt_body(be, mb, r, top, add_end, query) < 0)
1091 5 : goto bailout;
1092 554347 : if (m->sa)
1093 554347 : m->sa->eb = ebsave;
1094 : return 0;
1095 7 : bailout:
1096 7 : if (m->sa)
1097 7 : m->sa->eb = ebsave;
1098 : return -1;
1099 : }
1100 :
1101 : /* SQL procedures, functions and PREPARE statements are compiled into a parameterised plan */
1102 : static int
1103 335 : backend_dumpproc_body(backend *be, Client c, sql_rel *r)
1104 : {
1105 335 : mvc *m = be->mvc;
1106 335 : MalBlkPtr mb = 0;
1107 335 : InstrPtr curInstr = 0;
1108 335 : char arg[IDLENGTH];
1109 335 : int res = -1, added_to_cache = 0;
1110 :
1111 335 : backend_reset(be);
1112 :
1113 335 : mb = c->curprg->def;
1114 335 : curInstr = getInstrPtr(mb, 0);
1115 : /* we do not return anything */
1116 335 : setVarType(mb, 0, TYPE_void);
1117 :
1118 335 : if (m->params) { /* needed for prepare statements */
1119 243 : int argc = 0;
1120 1796 : for (node *n = m->params->h; n; n = n->next, argc++) {
1121 1554 : sql_arg *a = n->data;
1122 1554 : sql_type *tpe = a->type.type;
1123 1554 : int type, varid = 0;
1124 :
1125 1554 : if (!tpe || tpe->eclass == EC_ANY) {
1126 1 : sql_error(m, 10, SQLSTATE(42000) "Could not determine type for argument number %d", argc+1);
1127 1 : goto cleanup;
1128 : }
1129 1553 : type = tpe->localtype;
1130 1553 : snprintf(arg, IDLENGTH, "A%d", argc);
1131 1553 : if ((varid = newVariable(mb, arg,strlen(arg), type)) < 0) {
1132 0 : sql_error(m, 10, SQLSTATE(42000) "Internal error while compiling statement: variable id too long");
1133 0 : goto cleanup;
1134 : }
1135 1553 : curInstr = pushArgument(mb, curInstr, varid);
1136 1553 : if (c->curprg == NULL) {
1137 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1138 0 : goto cleanup;
1139 : }
1140 1553 : if (mb->errors) {
1141 0 : sql_error(m, 10, SQLSTATE(42000) "Internal error while compiling statement: %s", mb->errors);
1142 0 : goto cleanup;
1143 : }
1144 1553 : setVarType(mb, varid, type);
1145 : }
1146 : }
1147 :
1148 334 : if ((res = backend_dumpstmt(be, mb, r, m->emode == m_prepare, 1, be->q ? be->q->f->query : NULL)) < 0)
1149 0 : goto cleanup;
1150 :
1151 334 : SQLaddQueryToCache(c);
1152 334 : added_to_cache = 1;
1153 : // optimize this code the 'old' way
1154 334 : if (m->emode == m_prepare && !c->curprg->def->errors)
1155 334 : c->curprg->def->errors = SQLoptimizeFunction(c,c->curprg->def);
1156 334 : if (c->curprg->def->errors) {
1157 0 : sql_error(m, 10, SQLSTATE(42000) "Internal error while compiling statement: %s", c->curprg->def->errors);
1158 : } else {
1159 : res = 0; /* success */
1160 : }
1161 :
1162 0 : cleanup:
1163 2 : if (res < 0 && c->curprg) {
1164 1 : if (!added_to_cache)
1165 1 : freeSymbol(c->curprg);
1166 : else
1167 : SQLremoveQueryFromCache(c);
1168 : }
1169 335 : return res;
1170 : }
1171 :
1172 : int
1173 335 : backend_dumpproc(backend *be, Client c, cq *cq, sql_rel *r)
1174 : {
1175 335 : mvc *m = be->mvc;
1176 335 : Symbol symbackup = c->curprg;
1177 335 : backend bebackup = *be; /* backup current backend */
1178 335 : exception_buffer ebsave = m->sa->eb;
1179 335 : int argc = 1;
1180 335 : const char *sql_private_module = putName(sql_private_module_name);
1181 :
1182 335 : if (m->params)
1183 243 : argc += list_length(m->params);
1184 243 : if (argc < MAXARG)
1185 : argc = MAXARG;
1186 335 : assert(cq && strlen(cq->name) < IDLENGTH);
1187 335 : c->curprg = newFunctionArgs(sql_private_module, cq->name = putName(cq->name), FUNCTIONsymbol, argc);
1188 335 : if (c->curprg == NULL) {
1189 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1190 0 : goto bailout;
1191 335 : } else if (eb_savepoint(&m->sa->eb)) {
1192 0 : sql_error(m, 10, "%s", m->sa->eb.msg);
1193 0 : freeSymbol(c->curprg);
1194 0 : goto bailout;
1195 335 : } else if (backend_dumpproc_body(be, c, r) < 0) {
1196 1 : goto bailout;
1197 : }
1198 334 : *be = bebackup;
1199 334 : c->curprg = symbackup;
1200 334 : m->sa->eb = ebsave;
1201 334 : return 0;
1202 1 : bailout:
1203 1 : *be = bebackup;
1204 1 : c->curprg = symbackup;
1205 1 : m->sa->eb = ebsave;
1206 1 : return -1;
1207 : }
1208 :
1209 : int
1210 552 : monet5_has_module(ptr M, char *module)
1211 : {
1212 552 : Client c;
1213 552 : int clientID = *(int*) M;
1214 552 : c = MCgetClient(clientID);
1215 :
1216 552 : Module m = findModule(c->usermodule, putName(module));
1217 552 : if (m && m != c->usermodule)
1218 540 : return 1;
1219 : return 0;
1220 : }
1221 :
1222 : static MT_Lock sql_gencodeLock = MT_LOCK_INITIALIZER(sql_gencodeLock);
1223 :
1224 : static str
1225 672 : monet5_cache_remove(Module m, const char *nme)
1226 : {
1227 : /* Warning, this function doesn't do any locks, so be careful with concurrent symbol insert/deletes */
1228 672 : Symbol s = findSymbolInModule(m, nme);
1229 672 : if (s == NULL)
1230 165 : throw(MAL, "cache.remove", SQLSTATE(42000) "internal error, symbol missing\n");
1231 507 : deleteSymbol(m, s);
1232 507 : return MAL_SUCCEED;
1233 : }
1234 :
1235 : /* if 'mod' not NULL, use it otherwise get the module from the client id */
1236 : void
1237 775 : monet5_freecode(const char *mod, int clientid, const char *name)
1238 : {
1239 775 : Module m = NULL;
1240 775 : str msg = MAL_SUCCEED;
1241 :
1242 775 : if (mod) {
1243 276 : m = getModule(putName(mod));
1244 : } else {
1245 499 : Client c = MCgetClient(clientid);
1246 499 : if (c)
1247 499 : m = c->usermodule;
1248 : }
1249 775 : if (m) {
1250 672 : if (mod)
1251 173 : MT_lock_set(&sql_gencodeLock);
1252 672 : msg = monet5_cache_remove(m, name);
1253 672 : if (mod)
1254 173 : MT_lock_unset(&sql_gencodeLock);
1255 672 : freeException(msg); /* do something with error? */
1256 : }
1257 775 : }
1258 :
1259 : /* the function 'f' may not have the 'imp' field set yet */
1260 : int
1261 425831 : monet5_resolve_function(ptr M, sql_func *f, const char *fimp, bool *side_effect)
1262 : {
1263 425831 : Client c;
1264 425831 : Module m;
1265 425831 : int clientID = *(int*) M;
1266 425831 : const char *mname = putName(sql_func_mod(f)), *fname = putName(fimp);
1267 :
1268 425831 : if (!mname || !fname)
1269 : return 0;
1270 :
1271 : /* Some SQL functions MAL mapping such as count(*) aggregate, the number of arguments don't match */
1272 425831 : if (mname == calcRef && fname == getName("=")) {
1273 340 : *side_effect = 0;
1274 340 : return 1;
1275 : }
1276 425491 : if (mname == aggrRef && (fname == countRef || fname == count_no_nilRef)) {
1277 680 : *side_effect = 0;
1278 680 : return 1;
1279 : }
1280 424811 : if (f->type == F_ANALYTIC) {
1281 48728 : *side_effect = 0;
1282 48728 : return 1;
1283 : }
1284 376083 : if (strcmp(fname, "timestamp_to_str") == 0 ||
1285 375861 : strcmp(fname, "time_to_str") == 0 ||
1286 375624 : strcmp(fname, "str_to_timestamp") == 0 ||
1287 375403 : strcmp(fname, "str_to_time") == 0 ||
1288 375182 : strcmp(fname, "str_to_date") == 0) {
1289 1122 : *side_effect = 0;
1290 1122 : return 1;
1291 : }
1292 :
1293 374961 : c = MCgetClient(clientID);
1294 374961 : MT_lock_set(&sql_gencodeLock);
1295 374965 : for (m = findModule(c->usermodule, mname); m; m = m->link) {
1296 3763851 : for (Symbol s = findSymbolInModule(m, fname); s; s = s->peer) {
1297 3763847 : int argc = 0, retc = 0, varargs = 0, unsafe = 0;
1298 3763847 : if (s->kind == FUNCTIONsymbol) {
1299 0 : InstrPtr sig = getSignature(s);
1300 0 : retc = sig->retc;
1301 0 : argc = sig->argc - sig->retc;
1302 0 : varargs = (sig->varargs & VARARGS) == VARARGS;
1303 0 : unsafe = s->def->unsafeProp;
1304 : } else {
1305 3763847 : retc = s->func->retc;
1306 3763847 : argc = s->func->argc - s->func->retc;
1307 3763847 : varargs = s->func->vargs;
1308 3763847 : unsafe = s->func->unsafe;
1309 : }
1310 3763847 : int nfargs = list_length(f->ops), nfres = list_length(f->res);
1311 :
1312 3763847 : if (varargs || f->vararg || f->varres) {
1313 1 : *side_effect = (bool) unsafe;
1314 1 : MT_lock_unset(&sql_gencodeLock);
1315 1 : return 1;
1316 3763846 : } else if (nfargs == argc && (nfres == retc || (retc == 1 && (IS_FILT(f) || IS_PROC(f))))) {
1317 : /* I removed this code because, it was triggering many errors on te SQL <-> MAL translation */
1318 : /* Check for types of inputs and outputs. SQL procedures and filter functions always return 1 value in the MAL implementation
1319 : bool all_match = true;
1320 : if (nfres != 0) { if function has output variables, test types are equivalent
1321 : int i = 0;
1322 : for (node *n = f->res->h; n && all_match; n = n->next, i++) {
1323 : sql_arg *arg = (sql_arg *) n->data;
1324 : int nsql_tpe = arg->type.type->localtype;
1325 : int nmal_tpe = getArgType(s->def, sig, i);
1326 : if (isaBatType(nmal_tpe) || (nmal_tpe & 0377) == TYPE_any) any type is excluded from isaBatType
1327 : nmal_tpe = getBatType(nmal_tpe);
1328 :
1329 : any/void types allways match
1330 : if (nsql_tpe != TYPE_any && nmal_tpe != TYPE_any && nsql_tpe != TYPE_void && nmal_tpe != TYPE_void)
1331 : all_match = nsql_tpe == nmal_tpe;
1332 : }
1333 : }
1334 :
1335 : if (all_match && nfargs != 0) { if function has arguments, test types are equivalent
1336 : int i = sig->retc;
1337 : for (node *n = f->ops->h; n && all_match; n = n->next, i++) {
1338 : sql_arg *arg = (sql_arg *) n->data;
1339 : int nsql_tpe = arg->type.type->localtype;
1340 : int nmal_tpe = getArgType(s->def, sig, i);
1341 : if (isaBatType(nmal_tpe) || (nmal_tpe & 0377) == TYPE_any) any type is excluded from isaBatType
1342 : nmal_tpe = getBatType(nmal_tpe);
1343 :
1344 : any/void types allways match
1345 : if (nsql_tpe != TYPE_any && nmal_tpe != TYPE_any && nsql_tpe != TYPE_void && nmal_tpe != TYPE_void)
1346 : all_match = nsql_tpe == nmal_tpe;
1347 : }
1348 : }
1349 : if (all_match)*/
1350 374956 : *side_effect = (bool) unsafe;
1351 374956 : MT_lock_unset(&sql_gencodeLock);
1352 374956 : return 1;
1353 : }
1354 : }
1355 : }
1356 4 : MT_lock_unset(&sql_gencodeLock);
1357 4 : return 0;
1358 : }
1359 :
1360 : /* Parse the SQL query from the function, and extract the MAL function from the generated abstract syntax tree */
1361 : static str
1362 173 : mal_function_find_implementation_address(mvc *m, sql_func *f)
1363 : {
1364 173 : buffer *b = NULL;
1365 173 : bstream *bs = NULL;
1366 173 : stream *buf = NULL;
1367 173 : char *n = NULL;
1368 173 : int len = _strlen(f->query);
1369 173 : dlist *l, *ext_name;
1370 173 : str fimp = NULL;
1371 :
1372 173 : if (!(b = (buffer*)malloc(sizeof(buffer))))
1373 0 : return sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1374 173 : if (!(n = malloc(len + 2))) {
1375 0 : free(b);
1376 0 : return sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1377 : }
1378 173 : snprintf(n, len + 2, "%s\n", f->query);
1379 173 : len++;
1380 173 : buffer_init(b, n, len);
1381 173 : if (!(buf = buffer_rastream(b, "sqlstatement"))) {
1382 0 : buffer_destroy(b);
1383 0 : return sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1384 : }
1385 173 : if (!(bs = bstream_create(buf, b->len))) {
1386 0 : buffer_destroy(b);
1387 0 : return sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1388 : }
1389 173 : mvc o = *m;
1390 173 : scanner_init(&m->scanner, bs, NULL);
1391 173 : m->scanner.mode = LINE_1;
1392 173 : bstream_next(m->scanner.rs);
1393 :
1394 173 : m->type = Q_PARSE;
1395 173 : m->user_id = m->role_id = USER_MONETDB;
1396 173 : m->params = NULL;
1397 173 : m->sym = NULL;
1398 173 : m->errstr[0] = '\0';
1399 173 : m->session->status = 0;
1400 173 : (void) sqlparse(m);
1401 173 : if (m->session->status || m->errstr[0] || !m->sym || m->sym->token != SQL_CREATE_FUNC) {
1402 0 : if (m->errstr[0] == '\0')
1403 0 : (void) sql_error(m, 10, SQLSTATE(42000) "Could not parse CREATE SQL MAL function statement");
1404 : } else {
1405 173 : l = m->sym->data.lval;
1406 173 : ext_name = l->h->next->next->next->data.lval;
1407 173 : const char *imp = qname_schema_object(ext_name);
1408 :
1409 173 : if (strlen(imp) >= IDLENGTH)
1410 0 : (void) sql_error(m, 10, SQLSTATE(42000) "MAL function name '%s' too large for the backend", imp);
1411 173 : else if (!(fimp = _STRDUP(imp))) /* found the implementation, set it */
1412 0 : (void) sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1413 : }
1414 :
1415 173 : buffer_destroy(b);
1416 173 : bstream_destroy(m->scanner.rs);
1417 :
1418 173 : m->sym = NULL;
1419 173 : o.frames = m->frames; /* may have been realloc'ed */
1420 173 : o.sizeframes = m->sizeframes;
1421 173 : if (m->session->status || m->errstr[0]) {
1422 0 : int status = m->session->status;
1423 :
1424 0 : strcpy(o.errstr, m->errstr);
1425 0 : *m = o;
1426 0 : m->session->status = status;
1427 : } else {
1428 173 : unsigned int label = m->label;
1429 :
1430 173 : while (m->topframes > o.topframes)
1431 0 : clear_frame(m, m->frames[--m->topframes]);
1432 173 : *m = o;
1433 173 : m->label = label;
1434 : }
1435 : return fimp;
1436 : }
1437 :
1438 : int
1439 12441 : backend_create_mal_func(mvc *m, sql_subfunc *sf)
1440 : {
1441 12441 : char *F = NULL, *fn = NULL;
1442 12441 : sql_func *f = sf->func;
1443 12441 : bool old_side_effect = f->side_effect, new_side_effect = 0;
1444 12441 : int clientid = m->clientid;
1445 12441 : str fimp = NULL;
1446 :
1447 12441 : if (f->instantiated)
1448 : return 0;
1449 173 : FUNC_TYPE_STR(f->type, F, fn)
1450 173 : (void) F;
1451 173 : if (strlen(f->mod) >= IDLENGTH) {
1452 0 : (void) sql_error(m, 10, SQLSTATE(42000) "MAL module name '%s' too large for the backend", f->mod);
1453 0 : return -1;
1454 : }
1455 173 : if (!(fimp = mal_function_find_implementation_address(m, f)))
1456 : return -1;
1457 173 : if (!backend_resolve_function(&clientid, f, fimp, &new_side_effect)) {
1458 0 : (void) sql_error(m, 10, SQLSTATE(3F000) "MAL external name %s.%s not bound (%s.%s)", f->mod, fimp, f->s->base.name, f->base.name);
1459 0 : return -1;
1460 : }
1461 173 : if (old_side_effect != new_side_effect) {
1462 0 : (void) sql_error(m, 10, SQLSTATE(42000) "Side-effect value from the SQL %s %s.%s doesn't match the MAL definition %s.%s\n"
1463 0 : "Either re-create the %s, or fix the MAL definition and restart the database", fn, f->s->base.name, f->base.name, f->mod, fimp, fn);
1464 0 : return -1;
1465 : }
1466 173 : MT_lock_set(&sql_gencodeLock);
1467 173 : if (!f->instantiated) {
1468 173 : f->imp = fimp;
1469 173 : f->instantiated = TRUE; /* make sure 'instantiated' gets set after 'imp' */
1470 : } else {
1471 0 : _DELETE(fimp);
1472 : }
1473 173 : MT_lock_unset(&sql_gencodeLock);
1474 173 : return 0;
1475 : }
1476 :
1477 : static int
1478 277 : backend_create_sql_func_body(backend *be, sql_func *f, list *restypes, list *ops, Module mod, char *fimp, bool prepare)
1479 : {
1480 277 : mvc *m = be->mvc;
1481 277 : Client c = be->client;
1482 277 : MalBlkPtr curBlk = c->curprg->def;
1483 277 : InstrPtr curInstr = getInstrPtr(curBlk, 0);
1484 277 : int res = -1, i, retseen = 0, sideeffects = 0, no_inline = 0, added_to_cache = 0;
1485 277 : str msg = MAL_SUCCEED;
1486 277 : sql_func *pf = NULL;
1487 277 : sql_rel *r;
1488 :
1489 554 : r = rel_parse(m, f->s, f->query, prepare?m_prepare:m_instantiate);
1490 277 : if (r) {
1491 277 : r = sql_processrelation(m, r, 0, 1, 1, 0);
1492 277 : r = rel_physical(m, r);
1493 : }
1494 277 : if (!r) {
1495 0 : goto cleanup;
1496 : }
1497 :
1498 277 : backend_reset(be);
1499 :
1500 277 : if (f->res && !prepare) {
1501 249 : sql_arg *fres = f->res->h->data;
1502 249 : if (f->type == F_UNION) {
1503 21 : curInstr = table_func_create_result(curBlk, curInstr, f, restypes);
1504 21 : if( curInstr == NULL) {
1505 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1506 0 : goto cleanup;
1507 : }
1508 : } else {
1509 228 : setArgType(curBlk, curInstr, 0, fres->type.type->localtype);
1510 : }
1511 : } else {
1512 28 : setArgType(curBlk, curInstr, 0, TYPE_void);
1513 : }
1514 :
1515 277 : if (f->vararg && ops) {
1516 0 : int argc = 0;
1517 :
1518 0 : for (node *n = ops->h; n; n = n->next, argc++) {
1519 0 : stmt *s = n->data;
1520 0 : int type = tail_type(s)->type->localtype;
1521 0 : int varid = 0;
1522 0 : char buf[IDLENGTH];
1523 :
1524 0 : (void) snprintf(buf, IDLENGTH, "A%d", argc);
1525 0 : if ((varid = newVariable(curBlk, buf, strlen(buf), type)) < 0) {
1526 0 : sql_error(m, 10, SQLSTATE(42000) "Internal error while compiling statement: variable id too long");
1527 0 : goto cleanup;
1528 : }
1529 0 : curInstr = pushArgument(curBlk, curInstr, varid);
1530 0 : setVarType(curBlk, varid, type);
1531 : }
1532 277 : } else if (f->ops) {
1533 277 : int argc = 0;
1534 :
1535 704 : for (node *n = f->ops->h; n; n = n->next, argc++) {
1536 427 : sql_arg *a = n->data;
1537 427 : int type = a->type.type->localtype;
1538 427 : int varid = 0;
1539 427 : char *buf;
1540 :
1541 427 : if (a->name) {
1542 427 : buf = SA_NEW_ARRAY(m->sa, char, strlen(a->name) + 4);
1543 427 : if (buf)
1544 427 : stpcpy(stpcpy(buf, "A1%"), a->name); /* mangle variable name */
1545 : } else {
1546 0 : buf = SA_NEW_ARRAY(m->sa, char, IDLENGTH);
1547 0 : if (buf)
1548 0 : (void) snprintf(buf, IDLENGTH, "A%d", argc);
1549 : }
1550 427 : if (!buf) {
1551 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1552 0 : goto cleanup;
1553 : }
1554 427 : if ((varid = newVariable(curBlk, buf, strlen(buf), type)) < 0) {
1555 0 : sql_error(m, 10, SQLSTATE(42000) "Internal error while compiling statement: variable id too long");
1556 0 : goto cleanup;
1557 : }
1558 427 : curInstr = pushArgument(curBlk, curInstr, varid);
1559 427 : setVarType(curBlk, varid, type);
1560 : }
1561 : }
1562 : /* for recursive functions, avoid infinite loops */
1563 277 : pf = m->forward;
1564 277 : m->forward = f;
1565 277 : be->fimp = fimp; /* for recursive functions keep the generated name */
1566 277 : res = backend_dumpstmt(be, curBlk, r, prepare, 1, NULL);
1567 277 : m->forward = pf;
1568 277 : if (res < 0)
1569 1 : goto cleanup;
1570 : /* selectively make functions available for inlineing */
1571 : /* for the time being we only inline scalar functions */
1572 : /* and only if we see a single return value */
1573 : /* check the function for side effects and make that explicit */
1574 276 : sideeffects = f->side_effect;
1575 41119 : for (i = 1; i < curBlk->stop; i++) {
1576 40843 : InstrPtr p = getInstrPtr(curBlk, i);
1577 40843 : if (getFunctionId(p) == bindRef || getFunctionId(p) == bindidxRef)
1578 4482 : continue;
1579 36361 : sideeffects = sideeffects || hasSideEffects(curBlk, p, FALSE);
1580 36361 : no_inline |= (getModuleId(p) == malRef && getFunctionId(p) == multiplexRef);
1581 36361 : if (p->token == RETURNsymbol || p->barrier == RETURNsymbol)
1582 392 : retseen++;
1583 : }
1584 276 : if (i == curBlk->stop && retseen == 1 && f->type != F_UNION && !no_inline)
1585 165 : curBlk->inlineProp = 1;
1586 276 : if (sideeffects)
1587 99 : curBlk->unsafeProp = 1;
1588 : /* optimize the code, but beforehand add it to the cache, so recursive functions will be found */
1589 : /* 'sql' module is shared, so adquire mal context lock to avoid race conditions while adding new function symbols */
1590 276 : MT_lock_set(&sql_gencodeLock);
1591 276 : if (!f->instantiated) {
1592 276 : insertSymbol(mod, c->curprg);
1593 276 : added_to_cache = 1;
1594 276 : if (curBlk->inlineProp == 0 && !c->curprg->def->errors) {
1595 111 : msg = SQLoptimizeFunction(c, c->curprg->def);
1596 165 : } else if (curBlk->inlineProp != 0) {
1597 165 : if( msg == MAL_SUCCEED)
1598 165 : msg = chkProgram(c->usermodule, c->curprg->def);
1599 165 : if (msg == MAL_SUCCEED && !c->curprg->def->errors)
1600 165 : msg = SQLoptimizeFunction(c,c->curprg->def);
1601 : }
1602 276 : if (msg) {
1603 0 : if (c->curprg->def->errors)
1604 0 : freeException(msg);
1605 : else
1606 0 : c->curprg->def->errors = msg;
1607 : }
1608 276 : if (c->curprg->def->errors) {
1609 0 : MT_lock_unset(&sql_gencodeLock);
1610 0 : sql_error(m, 10, SQLSTATE(42000) "Internal error while compiling statement: %s", c->curprg->def->errors);
1611 0 : res = -1;
1612 0 : goto cleanup;
1613 : }
1614 276 : f->imp = fimp;
1615 276 : f->instantiated = TRUE; /* make sure 'instantiated' gets set after 'imp' */
1616 : }
1617 276 : MT_lock_unset(&sql_gencodeLock);
1618 :
1619 276 : cleanup:
1620 277 : if (res < 0) {
1621 1 : if (!added_to_cache) {
1622 1 : freeSymbol(c->curprg);
1623 : } else {
1624 0 : MT_lock_set(&sql_gencodeLock);
1625 0 : deleteSymbol(mod, c->curprg);
1626 0 : MT_lock_unset(&sql_gencodeLock);
1627 : }
1628 : }
1629 277 : return res;
1630 : }
1631 :
1632 : static int
1633 6878 : backend_create_sql_func(backend *be, sql_subfunc *sf, list *restypes, list *ops)
1634 : {
1635 6878 : mvc *m = be->mvc;
1636 6878 : Client c = be->client;
1637 6878 : Symbol symbackup = c->curprg;
1638 6878 : backend bebackup = *be; /* backup current backend */
1639 6878 : sql_func *f = sf->func;
1640 6878 : bool prepare = f->imp;
1641 6878 : const char *sql_shared_module = putName(sql_shared_module_name);
1642 6878 : const char *sql_private_module = putName(sql_private_module_name);
1643 6878 : const char *modname = prepare?sql_private_module:sql_shared_module;
1644 6878 : exception_buffer ebsave = m->sa->eb;
1645 6878 : char befname[IDLENGTH];
1646 6878 : int nargs;
1647 6878 : char *fimp;
1648 :
1649 : /* already instantiated or instantiating a recursive function */
1650 6878 : if (f->instantiated || (m->forward && m->forward->base.id == f->base.id))
1651 : return 0;
1652 :
1653 277 : (void) snprintf(befname, IDLENGTH, "f_" LLFMT, store_function_counter(m->store));
1654 277 : TRC_INFO(SQL_PARSER, "Mapping SQL name '%s' to MAL name '%s'\n", f->base.name, befname);
1655 277 : nargs = (f->res && f->type == F_UNION ? list_length(f->res) : 1) + (f->vararg && ops ? list_length(ops) : f->ops ? list_length(f->ops) : 0);
1656 277 : c->curprg = newFunctionArgs(modname, putName(befname), FUNCTIONsymbol, nargs);
1657 :
1658 277 : if ((fimp = _STRDUP(befname)) == NULL) {
1659 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1660 0 : goto bailout;
1661 277 : } else if (c->curprg == NULL) {
1662 0 : sql_error(m, 10, SQLSTATE(HY013) MAL_MALLOC_FAIL);
1663 0 : goto bailout;
1664 277 : } else if (eb_savepoint(&m->sa->eb)) {
1665 0 : sql_error(m, 10, "%s", m->sa->eb.msg);
1666 0 : freeSymbol(c->curprg);
1667 0 : goto bailout;
1668 277 : } else if (backend_create_sql_func_body(be, f, restypes, ops, prepare ? c->usermodule : getModule(modname), fimp, prepare) < 0) {
1669 1 : goto bailout;
1670 : }
1671 276 : *be = bebackup;
1672 276 : c->curprg = symbackup;
1673 276 : m->sa->eb = ebsave;
1674 276 : return 0;
1675 1 : bailout:
1676 1 : _DELETE(fimp);
1677 1 : *be = bebackup;
1678 1 : c->curprg = symbackup;
1679 1 : m->sa->eb = ebsave;
1680 1 : return -1;
1681 : }
1682 :
1683 : static int
1684 285991 : backend_create_func(backend *be, sql_subfunc *sf, list *restypes, list *ops)
1685 : {
1686 285991 : switch(sf->func->lang) {
1687 : case FUNC_LANG_INT:
1688 : case FUNC_LANG_R:
1689 : case FUNC_LANG_PY:
1690 : case FUNC_LANG_PY3:
1691 : case FUNC_LANG_C:
1692 : case FUNC_LANG_CPP:
1693 : return 0; /* these languages don't require internal instantiation */
1694 6439 : case FUNC_LANG_MAL:
1695 6439 : return backend_create_mal_func(be->mvc, sf);
1696 6878 : case FUNC_LANG_SQL:
1697 6878 : return backend_create_sql_func(be, sf, restypes, ops);
1698 0 : default:
1699 0 : sql_error(be->mvc, 10, SQLSTATE(42000) "Function language without a MAL backend");
1700 0 : return -1;
1701 : }
1702 : }
1703 :
1704 : int
1705 285985 : backend_create_subfunc(backend *be, sql_subfunc *f, list *ops)
1706 : {
1707 285985 : return backend_create_func(be, f, f->res, ops);
1708 : }
1709 :
1710 : void
1711 0 : _rel_print(mvc *sql, sql_rel *rel)
1712 : {
1713 0 : list *refs = sa_list(sql->sa);
1714 0 : rel_print_refs(sql, GDKstdout, rel, 0, refs, 1);
1715 0 : rel_print_(sql, GDKstdout, rel, 0, refs, 1);
1716 0 : mnstr_printf(GDKstdout, "\n");
1717 0 : }
1718 :
1719 : void
1720 0 : _exp_print(mvc *sql, sql_exp *e) {
1721 0 : exp_print(sql, GDKstdout, e, 0, NULL, 1, 0, 1);
1722 0 : mnstr_printf(GDKstdout, "\n");
1723 0 : }
1724 :
1725 : void
1726 266 : rel_print(mvc *sql, sql_rel *rel, int depth)
1727 : {
1728 266 : list *refs = sa_list(sql->sa);
1729 266 : size_t pos;
1730 266 : size_t nl = 0;
1731 266 : size_t len = 0, lastpos = 0;
1732 266 : stream *fd = sql->scanner.ws;
1733 266 : stream *s;
1734 266 : buffer *b = buffer_create(16364); /* hopefully enough */
1735 266 : if (!b)
1736 : return; /* signal somehow? */
1737 266 : s = buffer_wastream(b, "SQL Plan");
1738 266 : if (!s) {
1739 0 : buffer_destroy(b);
1740 0 : return; /* signal somehow? */
1741 : }
1742 :
1743 266 : rel_print_refs(sql, s, rel, depth, refs, 1);
1744 266 : rel_print_(sql, s, rel, depth, refs, 1);
1745 266 : mnstr_printf(s, "\n");
1746 :
1747 : /* count the number of lines in the output, skip the leading \n */
1748 111243 : for (pos = 1; pos < b->pos; pos++) {
1749 110711 : if (b->buf[pos] == '\n') {
1750 1982 : nl++;
1751 1982 : if (len < pos - lastpos)
1752 : len = pos - lastpos;
1753 1982 : lastpos = pos + 1;
1754 : }
1755 : }
1756 266 : b->buf[b->pos - 1] = '\0'; /* should always end with a \n, can overwrite */
1757 :
1758 : /* craft a semi-professional header */
1759 266 : mnstr_printf(fd, "&1 0 %zu 1 %zu\n", /* type id rows columns tuples */
1760 : nl, nl);
1761 266 : mnstr_printf(fd, "%% .plan # table_name\n");
1762 266 : mnstr_printf(fd, "%% rel # name\n");
1763 266 : mnstr_printf(fd, "%% varchar # type\n");
1764 266 : mnstr_printf(fd, "%% %zu # length\n", len - 1 /* remove = */);
1765 :
1766 : /* output the data */
1767 266 : mnstr_printf(fd, "%s\n", b->buf + 1 /* omit starting \n */);
1768 :
1769 266 : close_stream(s);
1770 266 : buffer_destroy(b);
1771 : }
|