LCOV - code coverage report
Current view: top level - sql/backends/monet5 - sql_gencode.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 807 1151 70.1 %
Date: 2024-10-07 21:21:43 Functions: 25 28 89.3 %

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

Generated by: LCOV version 1.14