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

Generated by: LCOV version 1.14