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