LCOV - code coverage report
Current view: top level - sql/backends/monet5 - sql_gencode.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 828 1172 70.6 %
Date: 2024-04-25 23:25:41 Functions: 26 28 92.9 %

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

Generated by: LCOV version 1.14