1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V.
7 */
8
9/*
10 * @f sql_gencode
11 * @t SQL to MAL code generation.
12 * @a N. Nes, M. Kersten
13 * @+ MAL Code generation
14 * This module contains the actions to construct a MAL program, ready for
15 * optimization and execution by the Monet V5 kernel.
16 *
17 * The code base is modeled directly after its MIL variant, replacing
18 * each IO request by instructions to initialize the corresponding MAL data
19 * structure.
20 * To speed up the compilation, we may consider keeping a cache of pre-compiled
21 * statements.
22 *
23 * MAL extensions needed. A temporary variable used as an argument
24 * should be printed (done). Consider replacing modname/fcnname by
25 * an integer constant and a global lookup table. This should
26 * reduce the cost to prepare MAL statements significantly.
27 *
28 * A dummy module is needed to load properly.
29 */
30#include "monetdb_config.h"
31#include "sql_gencode.h"
32#include "sql_optimizer.h"
33#include "sql_scenario.h"
34#include "sql_mvc.h"
35#include "sql_qc.h"
36#include "mal_namespace.h"
37#include "opt_prelude.h"
38#include "querylog.h"
39#include "mal_builder.h"
40#include "mal_debugger.h"
41
42#include "rel_select.h"
43#include "rel_unnest.h"
44#include "rel_optimizer.h"
45#include "rel_distribute.h"
46#include "rel_partition.h"
47#include "rel_prop.h"
48#include "rel_rel.h"
49#include "rel_exp.h"
50#include "rel_psm.h"
51#include "rel_bin.h"
52#include "rel_dump.h"
53#include "rel_remote.h"
54
55#include "msabaoth.h" /* msab_getUUID */
56#include "muuid.h"
57
58int
59constantAtom(backend *sql, MalBlkPtr mb, atom *a)
60{
61 int idx;
62 ValPtr vr = (ValPtr) &a->data;
63 ValRecord cst;
64
65 (void) sql;
66 cst.vtype = 0;
67 if (VALcopy(&cst, vr) == NULL)
68 return -1;
69 idx = defConstant(mb, vr->vtype, &cst);
70 return idx;
71}
72
73InstrPtr
74table_func_create_result(MalBlkPtr mb, InstrPtr q, sql_func *f, list *restypes)
75{
76 node *n;
77 int i;
78
79 if (q == NULL)
80 return NULL;
81 if (f->varres) {
82 for (i = 0, n = restypes->h; n; n = n->next, i++) {
83 sql_subtype *st = n->data;
84 int type = st->type->localtype;
85
86 type = newBatType(type);
87 if (i) {
88 if ((q = pushReturn(mb, q, newTmpVariable(mb, type))) == NULL)
89 return NULL;
90 } else
91 setVarType(mb, getArg(q, 0), type);
92 setVarUDFtype(mb, getArg(q, i));
93 }
94 } else {
95 for (i = 0, n = f->res->h; n; n = n->next, i++) {
96 sql_arg *a = n->data;
97 int type = a->type.type->localtype;
98
99 type = newBatType(type);
100 if (i) {
101 if ((q = pushReturn(mb, q, newTmpVariable(mb, type))) == NULL)
102 return NULL;
103 } else
104 setVarType(mb, getArg(q, 0), type);
105 setVarUDFtype(mb, getArg(q, i));
106 }
107 }
108 return q;
109}
110
111InstrPtr
112relational_func_create_result(mvc *sql, MalBlkPtr mb, InstrPtr q, sql_rel *f)
113{
114 sql_rel *r = f;
115 node *n;
116 int i;
117
118 if (q == NULL)
119 return NULL;
120 if (is_topn(r->op))
121 r = r->l;
122 if (!is_project(r->op))
123 r = rel_project(sql->sa, r, rel_projections(sql, r, NULL, 1, 1));
124 q->argc = q->retc = 0;
125 for (i = 0, n = r->exps->h; n; n = n->next, i++) {
126 sql_exp *e = n->data;
127 int type = exp_subtype(e)->type->localtype;
128
129 type = newBatType(type);
130 q = pushReturn(mb, q, newTmpVariable(mb, type));
131 }
132 return q;
133}
134
135static int
136_create_relational_function(mvc *m, const char *mod, const char *name, sql_rel *r, stmt *call, list *rel_ops, int inline_func)
137{
138 Client c = MCgetClient(m->clientid);
139 backend *be = (backend *) c->sqlcontext;
140 MalBlkPtr curBlk = 0;
141 InstrPtr curInstr = 0;
142 Symbol backup = NULL, curPrg = NULL;
143 int old_argc = be->mvc->argc, res = 0;
144 str msg = MAL_SUCCEED;
145
146 backup = c->curprg;
147 curPrg = c->curprg = newFunction(putName(mod), putName(name), FUNCTIONsymbol);
148 if( curPrg == NULL)
149 return -1;
150
151 curBlk = c->curprg->def;
152 curInstr = getInstrPtr(curBlk, 0);
153
154 curInstr = relational_func_create_result(m, curBlk, curInstr, r);
155 if( curInstr == NULL)
156 return -1;
157 setVarUDFtype(curBlk, 0);
158
159 /* ops */
160 if (call && call->type == st_list) {
161 node *n;
162 list *ops = call->op4.lval;
163
164 for (n = ops->h; n; n = n->next) {
165 stmt *op = n->data;
166 sql_subtype *t = tail_type(op);
167 int type = t->type->localtype;
168 int varid = 0;
169 const char *nme = (op->op3)?op->op3->op4.aval->data.val.sval:op->cname;
170 char buf[64];
171
172 if (nme[0] != 'A')
173 snprintf(buf,64,"A%s",nme);
174 else
175 snprintf(buf,64,"%s",nme);
176 varid = newVariable(curBlk, buf, strlen(buf), type);
177 curInstr = pushArgument(curBlk, curInstr, varid);
178 setVarType(curBlk, varid, type);
179 setVarUDFtype(curBlk, varid);
180 }
181 } else if (rel_ops) {
182 node *n;
183
184 for (n = rel_ops->h; n; n = n->next) {
185 sql_exp *e = n->data;
186 sql_subtype *t = &e->tpe;
187 int type = t->type->localtype;
188 int varid = 0;
189 char buf[64];
190
191 if (e->type == e_atom)
192 snprintf(buf,64,"A%d",e->flag);
193 else
194 snprintf(buf,64,"A%s",exp_name(e));
195 varid = newVariable(curBlk, (char *)buf, strlen(buf), type);
196 curInstr = pushArgument(curBlk, curInstr, varid);
197 setVarType(curBlk, varid, type);
198 setVarUDFtype(curBlk, varid);
199 }
200 }
201
202 /* add return statement */
203 sql_exp *e;
204 r = rel_psm_stmt(m->sa, e = exp_return(m->sa, exp_rel(m, r), 0));
205 e->card = CARD_MULTI;
206 be->mvc->argc = 0;
207 if (backend_dumpstmt(be, curBlk, r, 0, 1, NULL) < 0) {
208 freeSymbol(curPrg);
209 if (backup)
210 c->curprg = backup;
211 return -1;
212 }
213 be->mvc->argc = old_argc;
214 /* SQL function definitions meant for inlining should not be optimized before */
215 if (inline_func)
216 curBlk->inlineProp = 1;
217 /* optimize the code */
218 SQLaddQueryToCache(c);
219 if (curBlk->inlineProp == 0 && !c->curprg->def->errors) {
220 msg = SQLoptimizeQuery(c, c->curprg->def);
221 } else if (curBlk->inlineProp != 0) {
222 chkProgram(c->usermodule, c->curprg->def);
223 if (!c->curprg->def->errors)
224 msg = SQLoptimizeFunction(c,c->curprg->def);
225 }
226 if (msg) {
227 if (c->curprg->def->errors)
228 GDKfree(msg);
229 else
230 c->curprg->def->errors = msg;
231 }
232 if (c->curprg->def->errors) {
233 freeSymbol(curPrg);
234 res = -1;
235 }
236 if (backup)
237 c->curprg = backup;
238 return res;
239}
240
241static str
242rel2str( mvc *sql, sql_rel *rel)
243{
244 buffer *b = NULL;
245 stream *s = NULL;
246 list *refs = NULL;
247 char *res = NULL;
248
249 b = buffer_create(1024);
250 if(b == NULL) {
251 goto cleanup;
252 }
253 s = buffer_wastream(b, "rel_dump");
254 if(s == NULL) {
255 goto cleanup;
256 }
257 refs = sa_list(sql->sa);
258 if (!refs) {
259 goto cleanup;
260 }
261
262 rel_print_refs(sql, s, rel, 0, refs, 0);
263 rel_print_(sql, s, rel, 0, refs, 0);
264 mnstr_printf(s, "\n");
265 res = buffer_get_buf(b);
266
267cleanup:
268 if(b)
269 buffer_destroy(b);
270 if(s)
271 close_stream(s);
272 return res;
273}
274
275/* stub and remote function */
276static int
277_create_relational_remote(mvc *m, const char *mod, const char *name, sql_rel *rel, stmt *call, prop *prp)
278{
279 Client c = MCgetClient(m->clientid);
280 MalBlkPtr curBlk = 0;
281 InstrPtr curInstr = 0, p, o;
282 Symbol backup = NULL;
283 const char *local_tbl = prp->value;
284 node *n;
285 int i, q, v, res = 0;
286 int *lret, *rret;
287 char *lname;
288 sql_rel *r = rel;
289
290 if(local_tbl == NULL)
291 return -1;
292
293 lname = GDKstrdup(name);
294 if(lname == NULL)
295 return -1;
296
297 if (is_topn(r->op))
298 r = r->l;
299 if (!is_project(r->op))
300 r = rel_project(m->sa, r, rel_projections(m, r, NULL, 1, 1));
301 lret = SA_NEW_ARRAY(m->sa, int, list_length(r->exps));
302 if(lret == NULL) {
303 GDKfree(lname);
304 return -1;
305 }
306 rret = SA_NEW_ARRAY(m->sa, int, list_length(r->exps));
307 if(rret == NULL) {
308 GDKfree(lname);
309 return -1;
310 }
311
312 /* create stub */
313 backup = c->curprg;
314 c->curprg = newFunction(putName(mod), putName(name), FUNCTIONsymbol);
315 if( c->curprg == NULL) {
316 GDKfree(lname);
317 return -1;
318 }
319 lname[0] = 'l';
320 curBlk = c->curprg->def;
321 curInstr = getInstrPtr(curBlk, 0);
322
323 curInstr = relational_func_create_result(m, curBlk, curInstr, rel);
324 if( curInstr == NULL) {
325 GDKfree(lname);
326 return -1;
327 }
328 setVarUDFtype(curBlk, 0);
329
330 /* ops */
331 if (call && call->type == st_list) {
332 node *n;
333
334 for (n = call->op4.lval->h; n; n = n->next) {
335 stmt *op = n->data;
336 sql_subtype *t = tail_type(op);
337 int type = t->type->localtype;
338 int varid = 0;
339 const char *nme = (op->op3)?op->op3->op4.aval->data.val.sval:op->cname;
340 char buf[64];
341
342 snprintf(buf,64,"A%s",nme);
343 varid = newVariable(curBlk, buf,strlen(buf), type);
344 curInstr = pushArgument(curBlk, curInstr, varid);
345 setVarType(curBlk, varid, type);
346 setVarUDFtype(curBlk, varid);
347 }
348 }
349
350 /* declare return variables */
351 for (i = 0, n = r->exps->h; n; n = n->next, i++) {
352 sql_exp *e = n->data;
353 int type = exp_subtype(e)->type->localtype;
354
355 type = newBatType(type);
356 p = newFcnCall(curBlk, batRef, newRef);
357 p = pushType(curBlk, p, getBatType(type));
358 setArgType(curBlk, p, 0, type);
359 lret[i] = getArg(p, 0);
360 }
361
362 /* q := remote.connect("schema.table", "msql"); */
363 p = newStmt(curBlk, remoteRef, connectRef);
364 p = pushStr(curBlk, p, local_tbl);
365 p = pushStr(curBlk, p, "msql");
366 q = getArg(p, 0);
367
368 /* remote.exec(q, "sql", "register", "mod", "name", "relational_plan", "signature"); */
369 p = newInstruction(curBlk, remoteRef, execRef);
370 p = pushArgument(curBlk, p, q);
371 p = pushStr(curBlk, p, sqlRef);
372 p = pushStr(curBlk, p, registerRef);
373
374 o = newFcnCall(curBlk, remoteRef, putRef);
375 o = pushArgument(curBlk, o, q);
376 o = pushInt(curBlk, o, TYPE_str); /* dummy result type */
377 p = pushReturn(curBlk, p, getArg(o, 0));
378
379 o = newFcnCall(curBlk, remoteRef, putRef);
380 o = pushArgument(curBlk, o, q);
381 o = pushStr(curBlk, o, mod);
382 p = pushArgument(curBlk, p, getArg(o,0));
383
384 o = newFcnCall(curBlk, remoteRef, putRef);
385 o = pushArgument(curBlk, o, q);
386 o = pushStr(curBlk, o, lname);
387 p = pushArgument(curBlk, p, getArg(o,0));
388
389 {
390 int len = 1024, nr = 0;
391 char *s, *buf = GDKmalloc(len);
392 if (!buf) {
393 GDKfree(lname);
394 return -1;
395 }
396 s = rel2str(m, rel);
397 if (!s) {
398 GDKfree(lname);
399 GDKfree(buf);
400 return -1;
401 }
402 o = newFcnCall(curBlk, remoteRef, putRef);
403 o = pushArgument(curBlk, o, q);
404 o = pushStr(curBlk, o, s); /* relational plan */
405 p = pushArgument(curBlk, p, getArg(o,0));
406 free(s);
407
408 s = "";
409 if (call && call->type == st_list) {
410 node *n;
411
412 buf[0] = 0;
413 for (n = call->op4.lval->h; n; n = n->next) {
414 stmt *op = n->data;
415 sql_subtype *t = tail_type(op);
416 const char *nme = (op->op3)?op->op3->op4.aval->data.val.sval:op->cname;
417
418 if ((nr + 100) > len) {
419 buf = GDKrealloc(buf, len*=2);
420 if(buf == NULL)
421 break;
422 }
423
424 nr += snprintf(buf+nr, len-nr, "%s %s(%u,%u)%c", nme, t->type->sqlname, t->digits, t->scale, n->next?',':' ');
425 }
426 s = buf;
427 }
428 if(buf) {
429 o = newFcnCall(curBlk, remoteRef, putRef);
430 o = pushArgument(curBlk, o, q);
431 o = pushStr(curBlk, o, s); /* signature */
432 p = pushArgument(curBlk, p, getArg(o,0));
433 GDKfree(buf);
434 } else {
435 GDKfree(lname);
436 return -1;
437 }
438 }
439 pushInstruction(curBlk, p);
440
441 char *mal_session_uuid, *err = NULL;
442 if (!GDKinmemory() && (err = msab_getUUID(&mal_session_uuid)) == NULL) {
443 str rsupervisor_session = GDKstrdup(mal_session_uuid);
444 if (rsupervisor_session == NULL) {
445 free(mal_session_uuid);
446 return -1;
447 }
448
449 str lsupervisor_session = GDKstrdup(mal_session_uuid);
450 if (lsupervisor_session == NULL) {
451 free(mal_session_uuid);
452 GDKfree(rsupervisor_session);
453 return -1;
454 }
455 free(mal_session_uuid);
456
457 str rworker_plan_uuid = generateUUID();
458 if (rworker_plan_uuid == NULL) {
459 GDKfree(rsupervisor_session);
460 GDKfree(lsupervisor_session);
461 return -1;
462 }
463 str lworker_plan_uuid = GDKstrdup(rworker_plan_uuid);
464 if (lworker_plan_uuid == NULL) {
465 free(rworker_plan_uuid);
466 GDKfree(lsupervisor_session);
467 GDKfree(rsupervisor_session);
468 return -1;
469 }
470
471 /* remote.supervisor_register(connection, supervisor_uuid, plan_uuid) */
472 p = newInstruction(curBlk, remoteRef, execRef);
473 p = pushArgument(curBlk, p, q);
474 p = pushStr(curBlk, p, remoteRef);
475 p = pushStr(curBlk, p, register_supervisorRef);
476 getArg(p, 0) = -1;
477
478 /* We don't really care about the return value of supervisor_register,
479 * but I have not found a good way to remotely execute a void mal function
480 */
481 o = newFcnCall(curBlk, remoteRef, putRef);
482 o = pushArgument(curBlk, o, q);
483 o = pushInt(curBlk, o, TYPE_int);
484 p = pushReturn(curBlk, p, getArg(o, 0));
485
486 o = newFcnCall(curBlk, remoteRef, putRef);
487 o = pushArgument(curBlk, o, q);
488 o = pushStr(curBlk, o, rsupervisor_session);
489 p = pushArgument(curBlk, p, getArg(o, 0));
490
491 o = newFcnCall(curBlk, remoteRef, putRef);
492 o = pushArgument(curBlk, o, q);
493 o = pushStr(curBlk, o, rworker_plan_uuid);
494 p = pushArgument(curBlk, p, getArg(o, 0));
495
496 pushInstruction(curBlk, p);
497
498 /* Execute the same instruction locally */
499 p = newStmt(curBlk, remoteRef, register_supervisorRef);
500 p = pushStr(curBlk, p, lsupervisor_session);
501 p = pushStr(curBlk, p, lworker_plan_uuid);
502
503 GDKfree(lworker_plan_uuid);
504 free(rworker_plan_uuid); /* This was created with strdup */
505 GDKfree(lsupervisor_session);
506 GDKfree(rsupervisor_session);
507 } else if (err)
508 free(err);
509
510 /* (x1, x2, ..., xn) := remote.exec(q, "mod", "fcn"); */
511 p = newInstruction(curBlk, remoteRef, execRef);
512 p = pushArgument(curBlk, p, q);
513 p = pushStr(curBlk, p, mod);
514 p = pushStr(curBlk, p, lname);
515 getArg(p, 0) = -1;
516
517 for (i = 0, n = r->exps->h; n; n = n->next, i++) {
518 /* x1 := remote.put(q, :type) */
519 o = newFcnCall(curBlk, remoteRef, putRef);
520 o = pushArgument(curBlk, o, q);
521 o = pushArgument(curBlk, o, lret[i]);
522 v = getArg(o, 0);
523 p = pushReturn(curBlk, p, v);
524 rret[i] = v;
525 }
526
527 /* send arguments to remote */
528 for (i = curInstr->retc; i < curInstr->argc; i++) {
529 /* x1 := remote.put(q, A0); */
530 o = newStmt(curBlk, remoteRef, putRef);
531 o = pushArgument(curBlk, o, q);
532 o = pushArgument(curBlk, o, getArg(curInstr, i));
533 p = pushArgument(curBlk, p, getArg(o, 0));
534 }
535 pushInstruction(curBlk, p);
536
537 /* return results */
538 for (i = 0; i < curInstr->retc; i++) {
539 /* y1 := remote.get(q, x1); */
540 p = newFcnCall(curBlk, remoteRef, getRef);
541 p = pushArgument(curBlk, p, q);
542 p = pushArgument(curBlk, p, rret[i]);
543 getArg(p, 0) = lret[i];
544 }
545
546 /* remote.disconnect(q); */
547 p = newStmt(curBlk, remoteRef, disconnectRef);
548 p = pushArgument(curBlk, p, q);
549
550 p = newInstruction(curBlk, NULL, NULL);
551 p->barrier= RETURNsymbol;
552 p->retc = p->argc = 0;
553 for (i = 0; i < curInstr->retc; i++)
554 p = pushArgument(curBlk, p, lret[i]);
555 p->retc = p->argc;
556 /* assignment of return */
557 for (i = 0; i < curInstr->retc; i++)
558 p = pushArgument(curBlk, p, lret[i]);
559 pushInstruction(curBlk, p);
560
561 /* catch exceptions */
562 p = newCatchStmt(curBlk,"MALexception");
563 p = newExitStmt(curBlk,"MALexception");
564 p = newCatchStmt(curBlk,"SQLexception");
565 p = newExitStmt(curBlk,"SQLexception");
566 /* remote.disconnect(q); */
567 p = newStmt(curBlk, remoteRef, disconnectRef);
568 p = pushArgument(curBlk, p, q);
569
570 pushEndInstruction(curBlk);
571
572 /* SQL function definitions meant for inlineing should not be optimized before */
573 //for now no inline of the remote function, this gives garbage collection problems
574 //curBlk->inlineProp = 1;
575
576 SQLaddQueryToCache(c);
577 //chkProgram(c->usermodule, c->curprg->def);
578 if (!c->curprg->def->errors)
579 c->curprg->def->errors = SQLoptimizeFunction(c, c->curprg->def);
580 if (c->curprg->def->errors)
581 res = -1;
582 if (backup)
583 c->curprg = backup;
584 GDKfree(lname); /* make sure stub is called */
585 return res;
586}
587
588int
589monet5_create_relational_function(mvc *m, const char *mod, const char *name, sql_rel *rel, stmt *call, list *rel_ops, int inline_func)
590{
591 prop *p = NULL;
592
593 if (rel && (p = find_prop(rel->p, PROP_REMOTE)) != NULL)
594 return _create_relational_remote(m, mod, name, rel, call, p);
595 else
596 return _create_relational_function(m, mod, name, rel, call, rel_ops, inline_func);
597}
598
599/*
600 * The kernel uses two calls to procedures defined in SQL.
601 * They have to be initialized, which is currently hacked
602 * by using the SQLstatment.
603 */
604static stmt *
605sql_relation2stmt(backend *be, sql_rel *r)
606{
607 mvc *c = be->mvc;
608 stmt *s = NULL;
609
610 if (!r) {
611 return NULL;
612 } else {
613 if (c->emode == m_plan) {
614 rel_print(c, r, 0);
615 } else {
616 s = output_rel_bin(be, r);
617 }
618 }
619 return s;
620}
621
622int
623backend_dumpstmt(backend *be, MalBlkPtr mb, sql_rel *r, int top, int add_end, const char *query)
624{
625 mvc *c = be->mvc;
626 InstrPtr q, querylog = NULL;
627 int old_mv = be->mvc_var;
628 MalBlkPtr old_mb = be->mb;
629 stmt *s;
630
631 // Always keep the SQL query around for monitoring
632
633 if (query) {
634 while (*query && isspace((unsigned char) *query))
635 query++;
636
637 querylog = q = newStmt(mb, querylogRef, defineRef);
638 if (q == NULL) {
639 return -1;
640 }
641 setVarType(mb, getArg(q, 0), TYPE_void);
642 setVarUDFtype(mb, getArg(q, 0));
643 q = pushStr(mb, q, query);
644 q = pushStr(mb, q, getSQLoptimizer(be->mvc));
645 if (q == NULL) {
646 return -1;
647 }
648
649/* Crashes
650 q = newStmt(mb, querylogRef, contextRef);
651 if (q == NULL) {
652 return -1;
653 }
654 setVarType(mb, getArg(q, 0), TYPE_void);
655 setVarUDFtype(mb, getArg(q, 0));
656 q = pushStr(mb, q, GDKgetenv("monet_release"));
657 q = pushStr(mb, q, GDKgetenv("monet_version"));
658 q = pushStr(mb, q, GDKgetenv("revision"));
659 q = pushStr(mb, q, GDKgetenv("merovingian_uri"));
660*/
661 }
662
663 /* announce the transaction mode */
664 q = newStmt(mb, sqlRef, "mvc");
665 if (q == NULL)
666 return -1;
667 be->mvc_var = getDestVar(q);
668 be->mb = mb;
669 s = sql_relation2stmt(be, r);
670 if (!s) {
671 if (querylog)
672 (void) pushInt(mb, querylog, mb->stop);
673 return (be->mvc->errstr[0] == '\0') ? 0 : -1;
674 }
675
676 be->mvc_var = old_mv;
677 be->mb = old_mb;
678 if (top && !be->depth && (c->type == Q_SCHEMA || c->type == Q_TRANS)) {
679 q = newStmt(mb, sqlRef, exportOperationRef);
680 if (q == NULL)
681 return -1;
682 }
683 /* generate a dummy return assignment for functions */
684 if (getArgType(mb, getInstrPtr(mb, 0), 0) != TYPE_void && getInstrPtr(mb, mb->stop - 1)->barrier != RETURNsymbol) {
685 q = newAssignment(mb);
686 if (q == NULL)
687 return -1;
688 getArg(q, 0) = getArg(getInstrPtr(mb, 0), 0);
689 q->barrier = RETURNsymbol;
690 }
691 if (add_end)
692 pushEndInstruction(mb);
693 if (querylog)
694 (void) pushInt(mb, querylog, mb->stop);
695 return 0;
696}
697
698/* Generate the assignments of the query arguments to the query template*/
699int
700backend_callinline(backend *be, Client c)
701{
702 mvc *m = be->mvc;
703 InstrPtr curInstr = 0;
704 MalBlkPtr curBlk = c->curprg->def;
705
706 setVarType(curBlk, 0, 0);
707 if (m->argc) {
708 int argc = 0;
709
710 for (; argc < m->argc; argc++) {
711 atom *a = m->args[argc];
712 int type = atom_type(a)->type->localtype;
713 int varid = 0;
714
715 curInstr = newAssignment(curBlk);
716 if (curInstr == NULL)
717 return -1;
718 a->varid = varid = getDestVar(curInstr);
719 setVarType(curBlk, varid, type);
720 setVarUDFtype(curBlk, varid);
721
722 if (atom_null(a)) {
723 sql_subtype *t = atom_type(a);
724 (void) pushNil(curBlk, curInstr, t->type->localtype);
725 } else {
726 int _t;
727 if((_t = constantAtom(be, curBlk, a)) == -1)
728 return -1;
729 (void) pushArgument(curBlk, curInstr, _t);
730 }
731 }
732 }
733 c->curprg->def = curBlk;
734 return 0;
735}
736
737/* SQL procedures, functions and PREPARE statements are compiled into a parameterised plan */
738Symbol
739backend_dumpproc(backend *be, Client c, cq *cq, sql_rel *r)
740{
741 mvc *m = be->mvc;
742 MalBlkPtr mb = 0;
743 Symbol curPrg = 0, backup = NULL;
744 InstrPtr curInstr = 0;
745 int argc = 0;
746 char arg[IDLENGTH];
747 node *n;
748
749 backup = c->curprg;
750 if (cq)
751 c->curprg = newFunction(userRef, putName(cq->name), FUNCTIONsymbol);
752 else
753 c->curprg = newFunction(userRef, "tmp", FUNCTIONsymbol);
754 if (c->curprg == NULL)
755 return NULL;
756
757 curPrg = c->curprg;
758 curPrg->def->keephistory = backup->def->keephistory;
759 mb = curPrg->def;
760 curInstr = getInstrPtr(mb, 0);
761 /* we do not return anything */
762 setVarType(mb, 0, TYPE_void);
763 setVarUDFtype(mb, 0);
764 setModuleId(curInstr, userRef);
765
766 if (m->argc) {
767 for (argc = 0; argc < m->argc; argc++) {
768 atom *a = m->args[argc];
769 sql_type *tpe = atom_type(a)->type;
770 int type, varid = 0;
771
772 if (!tpe) {
773 sql_error(m, 003, SQLSTATE(42000) "Could not determine type for argument number %d\n", argc+1);
774 goto cleanup;
775 }
776 type = tpe->localtype;
777 snprintf(arg, IDLENGTH, "A%d", argc);
778 a->varid = varid = newVariable(mb, arg,strlen(arg), type);
779 curInstr = pushArgument(mb, curInstr, varid);
780 assert(curInstr);
781 if (curInstr == NULL)
782 goto cleanup;
783 setVarType(mb, varid, type);
784 setVarUDFtype(mb, 0);
785 }
786 } else if (m->params) { /* needed for prepare statements */
787
788 for (n = m->params->h; n; n = n->next, argc++) {
789 sql_arg *a = n->data;
790 sql_type *tpe = a->type.type;
791 int type, varid = 0;
792
793 if (!tpe) {
794 sql_error(m, 003, SQLSTATE(42000) "Could not determine type for argument number %d\n", argc+1);
795 goto cleanup;
796 }
797 type = tpe->localtype;
798 snprintf(arg, IDLENGTH, "A%d", argc);
799 varid = newVariable(mb, arg,strlen(arg), type);
800 curInstr = pushArgument(mb, curInstr, varid);
801 assert(curInstr);
802 if (curInstr == NULL)
803 goto cleanup;
804 setVarType(mb, varid, type);
805 setVarUDFtype(mb, varid);
806 }
807 }
808
809 if (backend_dumpstmt(be, mb, r, 1, 1, be->q?be->q->codestring:NULL) < 0)
810 goto cleanup;
811
812 if (cq) {
813 SQLaddQueryToCache(c);
814 // optimize this code the 'old' way
815 if ((m->emode == m_prepare || !qc_isaquerytemplate(getFunctionId(getInstrPtr(c->curprg->def,0)))) && !c->curprg->def->errors)
816 c->curprg->def->errors = SQLoptimizeFunction(c,c->curprg->def);
817 }
818 if (c->curprg->def->errors)
819 goto cleanup;
820
821 // restore the context for the wrapper code
822 curPrg = c->curprg;
823 if (backup)
824 c->curprg = backup;
825 return curPrg;
826
827cleanup:
828 freeSymbol(curPrg);
829 if (backup)
830 c->curprg = backup;
831 return NULL;
832}
833
834void
835backend_call(backend *be, Client c, cq *cq)
836{
837 mvc *m = be->mvc;
838 InstrPtr q;
839 MalBlkPtr mb = c->curprg->def;
840
841 q = newStmt(mb, userRef, cq->name);
842 if (!q) {
843 m->session->status = -3;
844 return;
845 }
846 if (m->emode == m_execute && be->q->paramlen != m->argc) {
847 sql_error(m, 003, SQLSTATE(42000) "EXEC called with wrong number of arguments: expected %d, got %d", be->q->paramlen, m->argc);
848 return;
849 }
850 /* cached (factorized queries return bit??) */
851 if (cq->code && getInstrPtr(((Symbol)cq->code)->def, 0)->token == FACTORYsymbol) {
852 setVarType(mb, getArg(q, 0), TYPE_bit);
853 setVarUDFtype(mb, getArg(q, 0));
854 } else {
855 setVarType(mb, getArg(q, 0), TYPE_void);
856 setVarUDFtype(mb, getArg(q, 0));
857 }
858 if (m->argc) {
859 int i;
860
861 for (i = 0; i < m->argc; i++) {
862 atom *a = m->args[i];
863 sql_subtype *pt = cq->params + i;
864
865 if (!atom_cast(m->sa, a, pt)) {
866 sql_error(m, 003, SQLSTATE(42000) "wrong type for argument %d of function call: %s, expected %s\n", i + 1, atom_type(a)->type->sqlname, pt->type->sqlname);
867 break;
868 }
869 if (atom_null(a)) {
870 sql_subtype *t = cq->params + i;
871 /* need type from the prepared argument */
872 q = pushNil(mb, q, t->type->localtype);
873 } else {
874 int _t;
875 if((_t = constantAtom(be, mb, a)) == -1) {
876 (void) sql_error(m, 02, SQLSTATE(HY001) "Allocation failure during function call: %s\n", atom_type(a)->type->sqlname);
877 break;
878 }
879 q = pushArgument(mb, q, _t);
880 }
881 }
882 }
883}
884
885int
886monet5_resolve_function(ptr M, sql_func *f)
887{
888 Client c;
889 Module m;
890 mvc *sql = (mvc *) M;
891 str mname = getName(f->mod), fname = getName(f->imp);
892
893 if (!mname || !fname)
894 return 0;
895
896 /* Some SQL functions MAL mapping such as count(*) aggregate, the number or arguments don't match */
897 if (mname == calcRef && fname == getName("="))
898 return 1;
899 if (mname == aggrRef && fname == countRef)
900 return 1;
901 if (mname == sqlRef && (fname == first_valueRef || fname == lagRef || fname == leadRef || fname == nth_valueRef || fname == ntileRef ||
902 fname == minRef || fname == maxRef || fname == countRef || fname == prodRef || fname == sumRef || fname == avgRef))
903 return 1;
904
905 c = MCgetClient(sql->clientid);
906 for (m = findModule(c->usermodule, mname); m; m = m->link) {
907 for (Symbol s = findSymbolInModule(m, fname); s; s = s->peer) {
908 InstrPtr sig = getSignature(s);
909 int argc = sig->argc - sig->retc, nfargs = list_length(f->ops), nfres = list_length(f->res);
910
911 if ((sig->varargs & VARARGS) == VARARGS || f->vararg || f->varres)
912 return 1;
913 else if (nfargs == argc && (nfres == sig->retc || (sig->retc == 1 && (IS_FILT(f) || IS_PROC(f))))) {
914 /* I removed this code because, it was triggering many errors on te SQL <-> MAL translation */
915 /* Check for types of inputs and outputs. SQL procedures and filter functions always return 1 value in the MAL implementation
916 bool all_match = true;
917 if (nfres != 0) { if function has output variables, test types are equivalent
918 int i = 0;
919 for (node *n = f->res->h; n && all_match; n = n->next, i++) {
920 sql_arg *arg = (sql_arg *) n->data;
921 int nsql_tpe = arg->type.type->localtype;
922 int nmal_tpe = getArgType(s->def, sig, i);
923 if (isaBatType(nmal_tpe) || (nmal_tpe & 0377) == TYPE_any) any type is excluded from isaBatType
924 nmal_tpe = getBatType(nmal_tpe);
925
926 any/void types allways match
927 if (nsql_tpe != TYPE_any && nmal_tpe != TYPE_any && nsql_tpe != TYPE_void && nmal_tpe != TYPE_void)
928 all_match = nsql_tpe == nmal_tpe;
929 }
930 }
931
932 if (all_match && nfargs != 0) { if function has arguments, test types are equivalent
933 int i = sig->retc;
934 for (node *n = f->ops->h; n && all_match; n = n->next, i++) {
935 sql_arg *arg = (sql_arg *) n->data;
936 int nsql_tpe = arg->type.type->localtype;
937 int nmal_tpe = getArgType(s->def, sig, i);
938 if (isaBatType(nmal_tpe) || (nmal_tpe & 0377) == TYPE_any) any type is excluded from isaBatType
939 nmal_tpe = getBatType(nmal_tpe);
940
941 any/void types allways match
942 if (nsql_tpe != TYPE_any && nmal_tpe != TYPE_any && nsql_tpe != TYPE_void && nmal_tpe != TYPE_void)
943 all_match = nsql_tpe == nmal_tpe;
944 }
945 }
946 if (all_match)*/
947 return 1;
948 }
949 }
950 }
951 return 0;
952}
953
954static int
955backend_create_r_func(backend *be, sql_func *f)
956{
957 (void)be;
958 switch(f->type) {
959 case F_AGGR:
960 f->mod = "rapi";
961 f->imp = "eval_aggr";
962 break;
963 case F_PROC: /* no output */
964 case F_FUNC:
965 default: /* ie also F_FILT and F_UNION for now */
966 f->mod = "rapi";
967 f->imp = "eval";
968 break;
969 }
970 return 0;
971}
972
973#define pyapi_enableflag "embedded_py"
974
975// returns the currently enabled python version, if any
976// defaults to python 2 if none is enabled
977static int
978enabled_python_version(void) {
979 const char* env = GDKgetenv(pyapi_enableflag);
980 if (env && strncmp(env, "3", 1) == 0) {
981 return 3;
982 }
983 return 2;
984}
985
986/* Create the MAL block for a registered function and optimize it */
987static int
988backend_create_py_func(backend *be, sql_func *f)
989{
990 (void)be;
991 switch(f->type) {
992 case F_AGGR:
993 f->mod = "pyapi";
994 f->imp = "eval_aggr";
995 break;
996 case F_LOADER:
997 f->mod = "pyapi";
998 f->imp = "eval_loader";
999 break;
1000 case F_PROC: /* no output */
1001 case F_FUNC:
1002 default: /* ie also F_FILT and F_UNION for now */
1003 f->mod = "pyapi";
1004 f->imp = "eval";
1005 break;
1006 }
1007 if (enabled_python_version() == 3) {
1008 f->mod = "pyapi3";
1009 }
1010 return 0;
1011}
1012
1013static int
1014backend_create_map_py_func(backend *be, sql_func *f)
1015{
1016 (void)be;
1017 switch(f->type) {
1018 case F_AGGR:
1019 f->mod = "pyapimap";
1020 f->imp = "eval_aggr";
1021 break;
1022 case F_PROC: /* no output */
1023 case F_FUNC:
1024 default: /* ie also F_FILT and F_UNION for now */
1025 f->mod = "pyapimap";
1026 f->imp = "eval";
1027 break;
1028 }
1029 if (enabled_python_version() == 3) {
1030 f->mod = "pyapi3map";
1031 }
1032 return 0;
1033}
1034
1035static int
1036backend_create_py2_func(backend *be, sql_func *f)
1037{
1038 backend_create_py_func(be, f);
1039 f->mod = "pyapi";
1040 return 0;
1041}
1042
1043static int
1044backend_create_map_py2_func(backend *be, sql_func *f)
1045{
1046 backend_create_map_py_func(be, f);
1047 f->mod = "pyapimap";
1048 return 0;
1049}
1050static int
1051backend_create_py3_func(backend *be, sql_func *f)
1052{
1053 backend_create_py_func(be, f);
1054 f->mod = "pyapi3";
1055 return 0;
1056}
1057
1058static int
1059backend_create_map_py3_func(backend *be, sql_func *f)
1060{
1061 backend_create_map_py_func(be, f);
1062 f->mod = "pyapi3map";
1063 return 0;
1064}
1065
1066/* Create the MAL block for a registered function and optimize it */
1067static int
1068backend_create_c_func(backend *be, sql_func *f)
1069{
1070 (void)be;
1071 switch(f->type) {
1072 case F_AGGR:
1073 f->mod = "capi";
1074 f->imp = "eval_aggr";
1075 break;
1076 case F_LOADER:
1077 case F_PROC: /* no output */
1078 case F_FUNC:
1079 default: /* ie also F_FILT and F_UNION for now */
1080 f->mod = "capi";
1081 f->imp = "eval";
1082 break;
1083 }
1084 return 0;
1085}
1086
1087/* Parse the SQL query from the function, and extract the MAL function from the generated abstract syntax tree */
1088static int
1089mal_function_find_implementation_address(mvc *m, sql_func *f)
1090{
1091 mvc *o = m;
1092 buffer *b = NULL;
1093 bstream *bs = NULL;
1094 stream *buf = NULL;
1095 char *n = NULL;
1096 int len = _strlen(f->query);
1097 sql_schema *s = cur_schema(m);
1098 dlist *l, *ext_name;
1099
1100 if (!(m = ZNEW(mvc))) {
1101 (void) sql_error(o, 02, SQLSTATE(HY001) MAL_MALLOC_FAIL);
1102 goto bailout;
1103 }
1104 m->type = Q_PARSE;
1105 m->user_id = m->role_id = USER_MONETDB;
1106
1107 if (!(m->session = sql_session_create(0, 0))) {
1108 (void) sql_error(o, 02, SQLSTATE(HY001) MAL_MALLOC_FAIL);
1109 goto bailout;
1110 }
1111 if (s)
1112 m->session->schema = s;
1113
1114 if (!(m->sa = sa_create())) {
1115 (void) sql_error(o, 02, SQLSTATE(HY001) MAL_MALLOC_FAIL);
1116 goto bailout;
1117 }
1118 if (!(b = (buffer*)GDKmalloc(sizeof(buffer)))) {
1119 (void) sql_error(o, 02, SQLSTATE(HY001) MAL_MALLOC_FAIL);
1120 goto bailout;
1121 }
1122 if (!(n = GDKmalloc(len + 2))) {
1123 (void) sql_error(o, 02, SQLSTATE(HY001) MAL_MALLOC_FAIL);
1124 goto bailout;
1125 }
1126 snprintf(n, len + 2, "%s\n", f->query);
1127 len++;
1128 buffer_init(b, n, len);
1129 if (!(buf = buffer_rastream(b, "sqlstatement"))) {
1130 (void) sql_error(o, 02, SQLSTATE(HY001) MAL_MALLOC_FAIL);
1131 goto bailout;
1132 }
1133 if (!(bs = bstream_create(buf, b->len))) {
1134 (void) sql_error(o, 02, SQLSTATE(HY001) MAL_MALLOC_FAIL);
1135 goto bailout;
1136 }
1137 scanner_init(&m->scanner, bs, NULL);
1138 m->scanner.mode = LINE_1;
1139 bstream_next(m->scanner.rs);
1140
1141 (void) sqlparse(m); /* blindly ignore errors */
1142 assert(m->sym->token == SQL_CREATE_FUNC);
1143 l = m->sym->data.lval;
1144 ext_name = l->h->next->next->next->data.lval;
1145 f->imp = sa_strdup(f->sa, qname_fname(ext_name)); /* found the implementation, set it */
1146
1147bailout:
1148 if (m) {
1149 bstream_destroy(m->scanner.rs);
1150 if (m->session)
1151 sql_session_destroy(m->session);
1152 if (m->sa)
1153 sa_destroy(m->sa);
1154 _DELETE(m);
1155 }
1156 m = o;
1157 if (n)
1158 GDKfree(n);
1159 if (b)
1160 GDKfree(b);
1161 return m->errstr[0] == '\0'; /* m was set back to o */
1162}
1163
1164static int
1165backend_create_sql_func(backend *be, sql_func *f, list *restypes, list *ops)
1166{
1167 mvc *m = be->mvc;
1168 MalBlkPtr curBlk = NULL;
1169 InstrPtr curInstr = NULL;
1170 Client c = be->client;
1171 Symbol backup = NULL, curPrg = NULL;
1172 int i, retseen = 0, sideeffects = 0, vararg = (f->varres || f->vararg), no_inline = 0;
1173 sql_rel *r;
1174 str msg = MAL_SUCCEED;
1175
1176 /* nothing to do for internal and ready (not recompiling) functions, besides finding respective MAL implementation */
1177 if (!f->sql && (f->lang == FUNC_LANG_INT || f->lang == FUNC_LANG_MAL)) {
1178 if (f->lang == FUNC_LANG_MAL && !f->imp && !mal_function_find_implementation_address(m, f))
1179 return -1;
1180 if (!backend_resolve_function(be->mvc, f)) {
1181 if (f->lang == FUNC_LANG_INT)
1182 (void) sql_error(m, 02, SQLSTATE(HY005) "Implementation for function %s.%s not found", f->mod, f->imp);
1183 else
1184 (void) sql_error(m, 02, SQLSTATE(HY005) "Implementation for function %s.%s not found (%s.%s)", f->mod, f->imp, f->s->base.name, f->base.name);
1185 return -1;
1186 }
1187 }
1188 if (!f->sql || (!vararg && f->sql > 1))
1189 return 0;
1190 if (!vararg)
1191 f->sql++;
1192 r = rel_parse(m, f->s, f->query, m_instantiate);
1193 if (r) {
1194 r = rel_unnest(m, r);
1195 r = rel_optimizer(m, r, 1);
1196 r = rel_distribute(m, r);
1197 r = rel_partition(m, r);
1198 }
1199 if (r && !f->sql) /* native function */
1200 return 0;
1201
1202 if (!r) {
1203 if (!vararg)
1204 f->sql--;
1205 return -1;
1206 }
1207 assert(r);
1208
1209 backup = c->curprg;
1210 curPrg = c->curprg = newFunction(userRef, putName(f->base.name), FUNCTIONsymbol);
1211 if( curPrg == NULL)
1212 goto cleanup;
1213
1214 curBlk = c->curprg->def;
1215 curInstr = getInstrPtr(curBlk, 0);
1216
1217 if (f->res) {
1218 sql_arg *res = f->res->h->data;
1219 if (f->type == F_UNION) {
1220 curInstr = table_func_create_result(curBlk, curInstr, f, restypes);
1221 if( curInstr == NULL)
1222 goto cleanup;
1223 } else {
1224 setArgType(curBlk, curInstr, 0, res->type.type->localtype);
1225 }
1226 } else {
1227 setArgType(curBlk, curInstr, 0, TYPE_void);
1228 }
1229 setVarUDFtype(curBlk, 0);
1230
1231 if (f->vararg && ops) {
1232 int argc = 0;
1233 node *n;
1234
1235 for (n = ops->h; n; n = n->next, argc++) {
1236 stmt *s = n->data;
1237 int type = tail_type(s)->type->localtype;
1238 int varid = 0;
1239 char buf[IDLENGTH];
1240
1241 (void) snprintf(buf, IDLENGTH, "A%d", argc);
1242 varid = newVariable(curBlk, buf, strlen(buf), type);
1243 curInstr = pushArgument(curBlk, curInstr, varid);
1244 setVarType(curBlk, varid, type);
1245 setVarUDFtype(curBlk, varid);
1246 }
1247 } else if (f->ops) {
1248 int argc = 0;
1249 node *n;
1250
1251 for (n = f->ops->h; n; n = n->next, argc++) {
1252 sql_arg *a = n->data;
1253 int type = a->type.type->localtype;
1254 int varid = 0;
1255 char buf[IDLENGTH];
1256
1257 if (a->name)
1258 (void) snprintf(buf, IDLENGTH, "A%s", a->name);
1259 else
1260 (void) snprintf(buf, IDLENGTH, "A%d", argc);
1261 varid = newVariable(curBlk, buf, strlen(buf), type);
1262 curInstr = pushArgument(curBlk, curInstr, varid);
1263 setVarType(curBlk, varid, type);
1264 setVarUDFtype(curBlk, varid);
1265 }
1266 }
1267 /* announce the transaction mode */
1268 if (backend_dumpstmt(be, curBlk, r, 0, 1, NULL) < 0)
1269 goto cleanup;
1270 /* selectively make functions available for inlineing */
1271 /* for the time being we only inline scalar functions */
1272 /* and only if we see a single return value */
1273 /* check the function for side effects and make that explicit */
1274 sideeffects = f->side_effect;
1275 for (i = 1; i < curBlk->stop; i++) {
1276 InstrPtr p = getInstrPtr(curBlk, i);
1277 if (getFunctionId(p) == bindRef || getFunctionId(p) == bindidxRef)
1278 continue;
1279 sideeffects = sideeffects || hasSideEffects(curBlk, p, FALSE);
1280 no_inline |= (getModuleId(p) == malRef && getFunctionId(p) == multiplexRef);
1281 if (p->token == RETURNsymbol || p->token == YIELDsymbol || p->barrier == RETURNsymbol || p->barrier == YIELDsymbol)
1282 retseen++;
1283 }
1284 if (i == curBlk->stop && retseen == 1 && f->type != F_UNION && !no_inline)
1285 curBlk->inlineProp = 1;
1286 if (sideeffects)
1287 curBlk->unsafeProp = 1;
1288 /* optimize the code */
1289 SQLaddQueryToCache(c);
1290 if (curBlk->inlineProp == 0 && !c->curprg->def->errors) {
1291 msg = SQLoptimizeFunction(c, c->curprg->def);
1292 } else if (curBlk->inlineProp != 0) {
1293 chkProgram(c->usermodule, c->curprg->def);
1294 if (!c->curprg->def->errors)
1295 msg = SQLoptimizeFunction(c,c->curprg->def);
1296 }
1297 if (msg) {
1298 if (c->curprg->def->errors)
1299 GDKfree(msg);
1300 else
1301 c->curprg->def->errors = msg;
1302 }
1303 if (c->curprg->def->errors)
1304 goto cleanup;
1305 if (backup)
1306 c->curprg = backup;
1307 return 0;
1308cleanup:
1309 freeSymbol(curPrg);
1310 if (backup)
1311 c->curprg = backup;
1312 return -1;
1313}
1314
1315/* TODO handle aggr */
1316int
1317backend_create_func(backend *be, sql_func *f, list *restypes, list *ops)
1318{
1319 switch(f->lang) {
1320 case FUNC_LANG_INT:
1321 case FUNC_LANG_MAL:
1322 case FUNC_LANG_SQL:
1323 return backend_create_sql_func(be, f, restypes, ops);
1324 case FUNC_LANG_R:
1325 return backend_create_r_func(be, f);
1326 case FUNC_LANG_PY:
1327 return backend_create_py_func(be, f);
1328 case FUNC_LANG_MAP_PY:
1329 return backend_create_map_py_func(be, f);
1330 case FUNC_LANG_PY2:
1331 return backend_create_py2_func(be, f);
1332 case FUNC_LANG_MAP_PY2:
1333 return backend_create_map_py2_func(be, f);
1334 case FUNC_LANG_PY3:
1335 return backend_create_py3_func(be, f);
1336 case FUNC_LANG_MAP_PY3:
1337 return backend_create_map_py3_func(be, f);
1338 case FUNC_LANG_C:
1339 case FUNC_LANG_CPP:
1340 return backend_create_c_func(be, f);
1341 case FUNC_LANG_J:
1342 default:
1343 return -1;
1344 }
1345}
1346
1347int
1348backend_create_subfunc(backend *be, sql_subfunc *f, list *ops)
1349{
1350 int res;
1351 MalBlkPtr mb = be->mb;
1352
1353 be->mb = NULL;
1354 res = backend_create_func(be, f->func, f->res, ops);
1355 be->mb = mb;
1356 return res;
1357}
1358
1359int
1360backend_create_subaggr(backend *be, sql_subaggr *f)
1361{
1362 int res;
1363 MalBlkPtr mb = be->mb;
1364
1365 be->mb = NULL;
1366 res = backend_create_func(be, f->aggr, f->res, NULL);
1367 be->mb = mb;
1368 return res;
1369}
1370
1371void
1372_rel_print(mvc *sql, sql_rel *rel)
1373{
1374 list *refs = sa_list(sql->sa);
1375 rel_print_refs(sql, GDKstdout, rel, 0, refs, 1);
1376 rel_print_(sql, GDKstdout, rel, 0, refs, 1);
1377 mnstr_printf(GDKstdout, "\n");
1378}
1379
1380void
1381rel_print(mvc *sql, sql_rel *rel, int depth)
1382{
1383 list *refs = sa_list(sql->sa);
1384 size_t pos;
1385 size_t nl = 0;
1386 size_t len = 0, lastpos = 0;
1387 stream *fd = sql->scanner.ws;
1388 stream *s;
1389 buffer *b = buffer_create(16364); /* hopefully enough */
1390 if (!b)
1391 return; /* signal somehow? */
1392 s = buffer_wastream(b, "SQL Plan");
1393 if (!s) {
1394 buffer_destroy(b);
1395 return; /* signal somehow? */
1396 }
1397
1398 rel_print_refs(sql, s, rel, depth, refs, 1);
1399 rel_print_(sql, s, rel, depth, refs, 1);
1400 mnstr_printf(s, "\n");
1401
1402 /* count the number of lines in the output, skip the leading \n */
1403 for (pos = 1; pos < b->pos; pos++) {
1404 if (b->buf[pos] == '\n') {
1405 nl++;
1406 if (len < pos - lastpos)
1407 len = pos - lastpos;
1408 lastpos = pos + 1;
1409 }
1410 }
1411 b->buf[b->pos - 1] = '\0'; /* should always end with a \n, can overwrite */
1412
1413 /* craft a semi-professional header */
1414 mnstr_printf(fd, "&1 0 %zu 1 %zu\n", /* type id rows columns tuples */
1415 nl, nl);
1416 mnstr_printf(fd, "%% .plan # table_name\n");
1417 mnstr_printf(fd, "%% rel # name\n");
1418 mnstr_printf(fd, "%% clob # type\n");
1419 mnstr_printf(fd, "%% %zu # length\n", len - 1 /* remove = */);
1420
1421 /* output the data */
1422 mnstr_printf(fd, "%s\n", b->buf + 1 /* omit starting \n */);
1423
1424 close_stream(s);
1425 buffer_destroy(b);
1426}
1427