LCOV - code coverage report
Current view: top level - sql/backends/monet5 - sql_gencode.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 582 832 70.0 %
Date: 2021-10-13 02:24:04 Functions: 20 22 90.9 %

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

Generated by: LCOV version 1.14