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