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 | |
58 | int |
59 | constantAtom(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 | |
73 | InstrPtr |
74 | table_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 | |
111 | InstrPtr |
112 | relational_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 | |
135 | static 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 | |
241 | static str |
242 | rel2str( 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 | |
267 | cleanup: |
268 | if(b) |
269 | buffer_destroy(b); |
270 | if(s) |
271 | close_stream(s); |
272 | return res; |
273 | } |
274 | |
275 | /* stub and remote function */ |
276 | static 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 | |
588 | int |
589 | monet5_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 | */ |
604 | static stmt * |
605 | sql_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 | |
622 | int |
623 | backend_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*/ |
699 | int |
700 | backend_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 */ |
738 | Symbol |
739 | backend_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 | |
827 | cleanup: |
828 | freeSymbol(curPrg); |
829 | if (backup) |
830 | c->curprg = backup; |
831 | return NULL; |
832 | } |
833 | |
834 | void |
835 | backend_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 | |
885 | int |
886 | monet5_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 | |
954 | static int |
955 | backend_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 |
977 | static int |
978 | enabled_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 */ |
987 | static int |
988 | backend_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 | |
1013 | static int |
1014 | backend_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 | |
1035 | static int |
1036 | backend_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 | |
1043 | static int |
1044 | backend_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 | } |
1050 | static int |
1051 | backend_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 | |
1058 | static int |
1059 | backend_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 */ |
1067 | static int |
1068 | backend_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 */ |
1088 | static int |
1089 | mal_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 | |
1147 | bailout: |
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 | |
1164 | static int |
1165 | backend_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; |
1308 | cleanup: |
1309 | freeSymbol(curPrg); |
1310 | if (backup) |
1311 | c->curprg = backup; |
1312 | return -1; |
1313 | } |
1314 | |
1315 | /* TODO handle aggr */ |
1316 | int |
1317 | backend_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 | |
1347 | int |
1348 | backend_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 | |
1359 | int |
1360 | backend_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 | |
1371 | void |
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 | |
1380 | void |
1381 | rel_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 | |