LCOV - code coverage report
Current view: top level - monetdb5/mal - mal_session.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 363 477 76.1 %
Date: 2024-11-15 19:37:45 Functions: 20 21 95.2 %

          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             : /* (author) M.L. Kersten
      14             :  */
      15             : #include "monetdb_config.h"
      16             : #include "mal_scenario.h"
      17             : #include "mal_session.h"
      18             : #include "mal_instruction.h"  /* for pushEndInstruction() */
      19             : #include "mal_interpreter.h"  /* for runMAL(), garbageElement() */
      20             : #include "mal_parser.h"                       /* for parseMAL() */
      21             : #include "mal_namespace.h"
      22             : #include "mal_authorize.h"
      23             : #include "mal_builder.h"
      24             : #include "msabaoth.h"
      25             : #include "mal_private.h"
      26             : #include "mal_internal.h"
      27             : #include "gdk.h"                              /* for opendir and friends */
      28             : 
      29             : /*
      30             :  * The MonetDB server uses a startup script to boot the system.
      31             :  * This script is an ordinary MAL program, but will mostly
      32             :  * consist of include statements to load modules of general interest.
      33             :  * The startup script is run as user Admin.
      34             :  */
      35             : str
      36         314 : malBootstrap(char *modules[], bool embedded, const char *initpasswd)
      37             : {
      38         314 :         Client c;
      39         314 :         str msg = MAL_SUCCEED;
      40             : 
      41         314 :         c = MCinitClient(MAL_ADMIN, NULL, NULL);
      42         314 :         if (c == NULL) {
      43           0 :                 throw(MAL, "malBootstrap", "Failed to initialize client");
      44             :         }
      45         314 :         MT_thread_set_qry_ctx(NULL);
      46         314 :         assert(c != NULL);
      47         314 :         c->curmodule = c->usermodule = userModule();
      48         314 :         if (c->usermodule == NULL) {
      49           0 :                 MCcloseClient(c);
      50           0 :                 throw(MAL, "malBootstrap", "Failed to initialize client MAL module");
      51             :         }
      52         314 :         if ((msg = defaultScenario(c))) {
      53           0 :                 MCcloseClient(c);
      54           0 :                 return msg;
      55             :         }
      56         314 :         if ((msg = MSinitClientPrg(c, "user", "main")) != MAL_SUCCEED) {
      57           0 :                 MCcloseClient(c);
      58           0 :                 return msg;
      59             :         }
      60             : 
      61         314 :         if (MCinitClientThread(c) < 0) {
      62           0 :                 MCcloseClient(c);
      63           0 :                 throw(MAL, "malBootstrap", "Failed to create client thread");
      64             :         }
      65         314 :         if ((msg = malIncludeModules(c, modules, 0, embedded, initpasswd)) != MAL_SUCCEED) {
      66           0 :                 MCcloseClient(c);
      67           0 :                 return msg;
      68             :         }
      69         297 :         MCcloseClient(c);
      70         297 :         return msg;
      71             : }
      72             : 
      73             : /*
      74             :  * Every client has a 'main' function to collect the statements.  Once
      75             :  * the END instruction has been found, it is added to the symbol table
      76             :  * and a fresh container is being constructed.  Note, this scheme makes
      77             :  * testing for recursive function calls a little more difficult.
      78             :  * Therefore, type checking should be performed afterwards.
      79             :  *
      80             :  * In interactive mode,  the closing statement is never reached.  The
      81             :  * 'main' procedure is typically cleaned between successive external
      82             :  * messages except for its variables, which are considered global.  This
      83             :  * storage container is re-used when during the previous call nothing
      84             :  * was added.  At the end of the session we have to garbage collect the
      85             :  * BATs introduced.
      86             :  */
      87             : static str
      88      262306 : MSresetClientPrg(Client cntxt, const char *mod, const char *fcn)
      89             : {
      90      262306 :         MalBlkPtr mb;
      91      262306 :         InstrPtr p;
      92             : 
      93      262306 :         mb = cntxt->curprg->def;
      94      262306 :         mb->stop = 1;
      95      262306 :         mb->errors = MAL_SUCCEED;
      96      262306 :         p = mb->stmt[0];
      97             : 
      98      262306 :         p->gc = 0;
      99      262306 :         p->retc = 1;
     100      262306 :         p->argc = 1;
     101      262306 :         p->argv[0] = 0;
     102             : 
     103      262306 :         setModuleId(p, mod);
     104      262306 :         setFunctionId(p, fcn);
     105      262306 :         if (findVariable(mb, fcn) < 0)
     106      262296 :                 if ((p->argv[0] = newVariable(mb, fcn, strlen(fcn), TYPE_void)) < 0)
     107           0 :                         throw(MAL, "resetClientPrg", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     108             : 
     109      262306 :         setVarType(mb, findVariable(mb, fcn), TYPE_void);
     110      262306 :         return MAL_SUCCEED;
     111             : }
     112             : 
     113             : /*
     114             :  * Create a new container block
     115             :  */
     116             : 
     117             : str
     118      309952 : MSinitClientPrg(Client cntxt, const char *mod, const char *nme)
     119             : {
     120      309952 :         int idx;
     121             : 
     122      309952 :         if (cntxt->curprg && idcmp(nme, cntxt->curprg->name) == 0)
     123      262306 :                 return MSresetClientPrg(cntxt, putName(mod), putName(nme));
     124       47646 :         cntxt->curprg = newFunction(putName(mod), putName(nme), FUNCTIONsymbol);
     125       47640 :         if (cntxt->curprg == 0)
     126           0 :                 throw(MAL, "initClientPrg", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     127       47640 :         if ((idx = findVariable(cntxt->curprg->def, "main")) >= 0)
     128       37644 :                 setVarType(cntxt->curprg->def, idx, TYPE_void);
     129       47641 :         insertSymbol(cntxt->usermodule, cntxt->curprg);
     130             : 
     131       47639 :         if (cntxt->glb == NULL)
     132       37664 :                 cntxt->glb = newGlobalStack(MAXGLOBALS + cntxt->curprg->def->vsize);
     133       47646 :         if (cntxt->glb == NULL)
     134           0 :                 throw(MAL, "initClientPrg", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     135       47646 :         assert(cntxt->curprg->def != NULL);
     136       47646 :         assert(cntxt->curprg->def->vtop > 0);
     137             :         return MAL_SUCCEED;
     138             : }
     139             : 
     140             : /*
     141             :  * The default method to interact with the database server is to connect
     142             :  * using a port number. The first line received should contain
     143             :  * authorization information, such as user name.
     144             :  *
     145             :  * The scheduleClient receives a challenge response consisting of
     146             :  * endian:user:password:lang:database:
     147             :  */
     148             : static void
     149         149 : exit_streams(bstream *fin, stream *fout)
     150             : {
     151         149 :         if (fout && fout != GDKstdout) {
     152         149 :                 mnstr_flush(fout, MNSTR_FLUSH_DATA);
     153         149 :                 close_stream(fout);
     154             :         }
     155         149 :         if (fin)
     156         149 :                 bstream_destroy(fin);
     157         149 : }
     158             : 
     159             : static const char mal_enableflag[] = "mal_for_all";
     160             : 
     161             : static bool
     162        1451 : is_exiting(void *data)
     163             : {
     164        1451 :         (void) data;
     165        1451 :         return GDKexiting();
     166             : }
     167             : 
     168             : static str MSserveClient(Client cntxt);
     169             : 
     170             : 
     171             : static inline void
     172          12 : cleanUpScheduleClient(Client c, str *command, str *err)
     173             : {
     174          12 :         if (c) {
     175          12 :                 MCcloseClient(c);
     176             :         }
     177          12 :         if (command) {
     178           3 :                 GDKfree(*command);
     179           3 :                 *command = NULL;
     180             :         }
     181          12 :         if (err) {
     182          12 :                 freeException(*err);
     183          12 :                 *err = NULL;
     184             :         }
     185          12 : }
     186             : 
     187             : 
     188             : void
     189       37465 : MSscheduleClient(str command, str peer, str challenge, bstream *fin, stream *fout,
     190             :                                  protocol_version protocol, size_t blocksize)
     191             : {
     192       37465 :         char *user = command, *algo = NULL, *passwd = NULL, *lang = NULL,
     193       37465 :                 *handshake_opts = NULL;
     194       37465 :         char *database = NULL, *s;
     195       37465 :         const char *dbname;
     196       37465 :         str msg = MAL_SUCCEED;
     197       37465 :         bool filetrans = false;
     198       37465 :         Client c;
     199             : 
     200       37465 :         MT_thread_set_qry_ctx(NULL);
     201             : 
     202             :         /* decode BIG/LIT:user:{cypher}passwordchal:lang:database: line */
     203             : 
     204             :         /* byte order */
     205       37471 :         s = strchr(user, ':');
     206       37471 :         if (s) {
     207       37322 :                 *s = 0;
     208       37322 :                 mnstr_set_bigendian(fin->s, strcmp(user, "BIG") == 0);
     209       37307 :                 user = s + 1;
     210             :         } else {
     211         149 :                 mnstr_printf(fout, "!incomplete challenge '%s'\n", user);
     212         149 :                 exit_streams(fin, fout);
     213         149 :                 GDKfree(command);
     214         310 :                 return;
     215             :         }
     216             : 
     217             :         /* passwd */
     218       37307 :         s = strchr(user, ':');
     219       37307 :         if (s) {
     220       37307 :                 *s = 0;
     221       37307 :                 passwd = s + 1;
     222             :                 /* decode algorithm, i.e. {plain}mypasswordchallenge */
     223       37307 :                 if (*passwd != '{') {
     224           0 :                         mnstr_printf(fout, "!invalid password entry\n");
     225           0 :                         exit_streams(fin, fout);
     226           0 :                         GDKfree(command);
     227           0 :                         return;
     228             :                 }
     229       37307 :                 algo = passwd + 1;
     230       37307 :                 s = strchr(algo, '}');
     231       37307 :                 if (!s) {
     232           0 :                         mnstr_printf(fout, "!invalid password entry\n");
     233           0 :                         exit_streams(fin, fout);
     234           0 :                         GDKfree(command);
     235           0 :                         return;
     236             :                 }
     237       37307 :                 *s = 0;
     238       37307 :                 passwd = s + 1;
     239             :         } else {
     240           0 :                 mnstr_printf(fout, "!incomplete challenge '%s'\n", user);
     241           0 :                 exit_streams(fin, fout);
     242           0 :                 GDKfree(command);
     243           0 :                 return;
     244             :         }
     245             : 
     246             :         /* lang */
     247       37307 :         s = strchr(passwd, ':');
     248       37307 :         if (s) {
     249       37307 :                 *s = 0;
     250       37307 :                 lang = s + 1;
     251             :         } else {
     252           0 :                 mnstr_printf(fout, "!incomplete challenge, missing language\n");
     253           0 :                 exit_streams(fin, fout);
     254           0 :                 GDKfree(command);
     255           0 :                 return;
     256             :         }
     257             : 
     258             :         /* database */
     259       37307 :         s = strchr(lang, ':');
     260       37307 :         if (s) {
     261       37307 :                 *s = 0;
     262       37307 :                 database = s + 1;
     263             :                 /* we can have stuff following, make it void */
     264       37307 :                 s = strchr(database, ':');
     265       37307 :                 if (s)
     266       37307 :                         *s++ = 0;
     267             :         }
     268             : 
     269       37307 :         if (s && strncmp(s, "FILETRANS:", 10) == 0) {
     270       37000 :                 s += 10;
     271       37000 :                 filetrans = true;
     272         307 :         } else if (s && s[0] == ':') {
     273           0 :                 s += 1;
     274           0 :                 filetrans = false;
     275             :         }
     276             : 
     277       37307 :         if (s && strchr(s, ':') != NULL) {
     278       37000 :                 handshake_opts = s;
     279       37000 :                 s = strchr(s, ':');
     280       37000 :                 *s++ = '\0';
     281             :         }
     282       37307 :         dbname = GDKgetenv("gdk_dbname");
     283       37337 :         if (database != NULL && database[0] != '\0' &&
     284       36322 :                 strcmp(database, dbname) != 0) {
     285           0 :                 mnstr_printf(fout, "!request for database '%s', "
     286             :                                          "but this is database '%s', "
     287             :                                          "did you mean to connect to monetdbd instead?\n",
     288             :                                          database, dbname);
     289             :                 /* flush the error to the client, and abort further execution */
     290           0 :                 exit_streams(fin, fout);
     291           0 :                 GDKfree(command);
     292           0 :                 return;
     293             :         } else {
     294       37337 :                 c = MCinitClient(0, fin, fout);
     295       37337 :                 if (c == NULL) {
     296           0 :                         if (MCshutdowninprogress())
     297           0 :                                 mnstr_printf(fout,
     298             :                                                          "!system shutdown in progress, please try again later\n");
     299             :                         else
     300           0 :                                 mnstr_printf(fout, "!maximum concurrent client limit reached "
     301             :                                                          "(%d), please try again later\n", MAL_MAXCLIENTS);
     302           0 :                         exit_streams(fin, fout);
     303           0 :                         GDKfree(command);
     304           0 :                         return;
     305             :                 }
     306       37337 :                 c->filetrans = filetrans;
     307       37337 :                 c->handshake_options = handshake_opts ? strdup(handshake_opts) : NULL;
     308             :                 /* move this back !! */
     309       37337 :                 if (c->usermodule == 0) {
     310       37337 :                         c->curmodule = c->usermodule = userModule();
     311       37337 :                         if (c->curmodule == NULL) {
     312           0 :                                 mnstr_printf(fout, "!could not allocate space\n");
     313           0 :                                 cleanUpScheduleClient(c, &command, &msg);
     314           0 :                                 return;
     315             :                         }
     316             :                 }
     317             : 
     318       37337 :                 if ((msg = setScenario(c, lang)) != NULL) {
     319           0 :                         mnstr_printf(c->fdout, "!%s\n", msg);
     320           0 :                         mnstr_flush(c->fdout, MNSTR_FLUSH_DATA);
     321           0 :                         cleanUpScheduleClient(c, &command, &msg);
     322           0 :                         return;
     323             :                 }
     324       37337 :                 if (!GDKgetenv_isyes(mal_enableflag)
     325       37337 :                         && strncasecmp("sql", lang, 3) != 0
     326         502 :                         && strcmp(user, "monetdb") != 0) {
     327           3 :                         mnstr_printf(fout,
     328             :                                                  "!only the 'monetdb' user can use non-sql languages. "
     329             :                                                  "run mserver5 with --set %s=yes to change this.\n",
     330             :                                                  mal_enableflag);
     331           3 :                         cleanUpScheduleClient(c, &command, &msg);
     332           3 :                         return;
     333             :                 }
     334             :         }
     335             : 
     336       37334 :         if ((msg = MSinitClientPrg(c, "user", "main")) != MAL_SUCCEED) {
     337           0 :                 mnstr_printf(fout, "!could not allocate space\n");
     338           0 :                 cleanUpScheduleClient(c, &command, &msg);
     339           0 :                 return;
     340             :         }
     341             : 
     342             :         // at this point username should have being verified
     343       37334 :         c->username = GDKstrdup(user);
     344       37330 :         if (peer)
     345       37330 :                 c->peer = GDKstrdup(peer);
     346             : 
     347             :         /* NOTE ABOUT STARTING NEW THREADS
     348             :          * At this point we have conducted experiments (Jun 2012) with
     349             :          * reusing threads.  The implementation used was a lockless array of
     350             :          * semaphores to wake up threads to do work.  Experimentation on
     351             :          * Linux, Solaris and Darwin showed no significant improvements, in
     352             :          * most cases no improvements at all.  Hence the following
     353             :          * conclusion: thread reuse doesn't save up on the costs of just
     354             :          * forking new threads.  Since the latter means no difficulties of
     355             :          * properly maintaining a pool of threads and picking the workers
     356             :          * out of them, it is favourable just to start new threads on
     357             :          * demand. */
     358             : 
     359             :         /* fork a new thread to handle this client */
     360             : 
     361       37334 :         c->protocol = protocol;
     362       37334 :         c->blocksize = blocksize;
     363             : 
     364       37334 :         if (c->initClient) {
     365       37334 :                 if ((msg = c->initClient(c, passwd, challenge, algo)) != MAL_SUCCEED) {
     366           9 :                         mnstr_printf(fout, "!%s\n", msg);
     367           9 :                         GDKfree(command);
     368           9 :                         if (c->exitClient)
     369           9 :                                 c->exitClient(c);
     370           9 :                         cleanUpScheduleClient(c, NULL, &msg);
     371           9 :                         return;
     372             :                 }
     373             :         }
     374       37324 :         GDKfree(command);
     375             : 
     376       37323 :         mnstr_settimeout(c->fdin->s, 50, is_exiting, NULL);
     377       37321 :         msg = MSserveClient(c);
     378       37325 :         if (msg != MAL_SUCCEED) {
     379           0 :                 freeException(msg);
     380             :         }
     381             : }
     382             : 
     383             : /*
     384             :  * After the client initialization has been finished, we can start the
     385             :  * interaction protocol. This involves parsing the input in the context
     386             :  * of an already defined procedure and upon success, its execution.
     387             :  *
     388             :  * In essence, this calls for an incremental parsing operation, because
     389             :  * we should wait until a complete basic block has been detected.  Test,
     390             :  * first collect the instructions before we take them all.
     391             :  *
     392             :  * In interactive mode, we should remove the instructions before
     393             :  * accepting new ones. The function signature remains the same and the
     394             :  * symbol table should also not be affected.  Aside from removing
     395             :  * instruction, we should also condense the variable stack, i.e.
     396             :  * removing at least the temporary variables, but maybe everything
     397             :  * beyond a previous defined point.
     398             :  *
     399             :  * Beware that we have to cleanup the global stack as well. This to
     400             :  * avoid subsequent calls to find garbage information.  However, this
     401             :  * action is only required after a successful execution.  Otherwise,
     402             :  * garbage collection is not needed.
     403             :  */
     404             : void
     405      304314 : MSresetInstructions(MalBlkPtr mb, int start)
     406             : {
     407      304314 :         int i;
     408      304314 :         InstrPtr p;
     409             : 
     410    74481621 :         for (i = start; i < mb->ssize; i++) {
     411    74177078 :                 p = getInstrPtr(mb, i);
     412    74177078 :                 if (p)
     413       37281 :                         freeInstruction(p);
     414    74177307 :                 mb->stmt[i] = NULL;
     415             :         }
     416      304543 :         mb->stop = start;
     417      304543 : }
     418             : 
     419             : /*
     420             :  * MAL instructions generate variables.
     421             :  * The values of temporary variables should be cleaned at the end of a call
     422             :  * The values of global variables are retained.
     423             :  * Global variables should not start with C_ or X_
     424             :  */
     425             : void
     426        9973 : MSresetStack(Client cntxt, MalBlkPtr mb, MalStkPtr glb)
     427             : {
     428        9973 :         InstrPtr sig = getInstrPtr(mb, 0);
     429        9973 :         int i, k = sig->argc;
     430             : 
     431        9973 :         if (mb->errors == MAL_SUCCEED) {
     432     1251845 :                 for (i = sig->argc; i < mb->vtop; i++) {
     433     1241872 :                         if (glb && i < glb->stktop && isTmpVar(mb, i) && !glb->keepTmps) {
     434        7494 :                                 if (mb->var[i].name)
     435           2 :                                         GDKfree(mb->var[i].name);
     436             :                                 /* clean stack entry */
     437        7494 :                                 garbageElement(cntxt, &glb->stk[i]);
     438        7494 :                                 glb->stk[i].vtype = TYPE_int;
     439        7494 :                                 glb->stk[i].len = 0;
     440        7494 :                                 glb->stk[i].val.pval = 0;
     441        7494 :                                 if (isVarConstant(mb, i))
     442        4827 :                                         garbageElement(cntxt, &mb->var[i].value);
     443             :                         } else {
     444             :                                 /* compress the global variable list and stack */
     445     1234378 :                                 mb->var[k] = mb->var[i];
     446     1234378 :                                 glb->stk[k] = glb->stk[i];
     447     1234378 :                                 setVarUsed(mb, k);
     448     1234378 :                                 setVarInit(mb, k);
     449     1234378 :                                 if (i != k) {
     450          68 :                                         glb->stk[i].vtype = TYPE_int;
     451          68 :                                         glb->stk[i].len = 0;
     452          68 :                                         glb->stk[i].val.pval = 0;
     453          68 :                                         clrVarConstant(mb, i);
     454          68 :                                         clrVarCleanup(mb, i);
     455             :                                 }
     456     1234378 :                                 k++;
     457             :                         }
     458             :                 }
     459             :         }
     460        9973 :         assert(k <= mb->vsize);
     461        9973 :         mb->vtop = k;
     462        9973 : }
     463             : 
     464             : /* The symbol table be become filled with constant values to be garbage collected
     465             : * The signature is always left behind.
     466             : */
     467             : 
     468             : void
     469           0 : MSresetVariables(MalBlkPtr mb)
     470             : {
     471           0 :         InstrPtr sig = getInstrPtr(mb, 0);
     472           0 :         int i;
     473             : 
     474           0 :         if (mb->errors == MAL_SUCCEED)
     475           0 :                 for (i = sig->argc; i < mb->vtop; i++)
     476           0 :                         if (isVarConstant(mb, i)) {
     477           0 :                                 VALclear(&getVarConstant(mb, i));
     478           0 :                                 clrVarConstant(mb, i);
     479             :                         }
     480           0 : }
     481             : 
     482             : /*
     483             :  * Here we start the client.  We need to initialize and allocate space
     484             :  * for the global variables.  Thereafter it is up to the scenario
     485             :  * interpreter to process input.
     486             :  */
     487             : static str
     488       37316 : MSserveClient(Client c)
     489             : {
     490       37316 :         MalBlkPtr mb;
     491       37316 :         str msg = 0;
     492             : 
     493       37316 :         if (MCinitClientThread(c) < 0) {
     494           0 :                 MCcloseClient(c);
     495           0 :                 return MAL_SUCCEED;
     496             :         }
     497             :         /*
     498             :          * A stack frame is initialized to keep track of global variables.
     499             :          * The scenarios are run until we finally close the last one.
     500             :          */
     501       37325 :         mb = c->curprg->def;
     502       37325 :         if (c->glb == NULL)
     503           0 :                 c->glb = newGlobalStack(MAXGLOBALS + mb->vsize);
     504       37325 :         if (c->glb == NULL) {
     505           0 :                 MCcloseClient(c);
     506           0 :                 throw(MAL, "serveClient", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     507             :         } else {
     508       37325 :                 c->glb->stktop = mb->vtop;
     509       37325 :                 c->glb->blk = mb;
     510             :         }
     511             : 
     512       37325 :         if (c->scenario == 0)
     513           0 :                 msg = defaultScenario(c);
     514       37325 :         if (msg) {
     515           0 :                 MCcloseClient(c);
     516           0 :                 return msg;
     517             :         } else {
     518       37325 :                 do {
     519       37325 :                         do {
     520       37325 :                                 MT_thread_setworking("running scenario");
     521       37323 :                                 msg = runScenario(c);
     522       37325 :                                 freeException(msg);
     523       37325 :                                 if (c->mode == FINISHCLIENT)
     524             :                                         break;
     525           0 :                                 resetScenario(c);
     526           0 :                         } while (c->scenario && !GDKexiting());
     527       37325 :                 } while (c->scenario && c->mode != FINISHCLIENT && !GDKexiting());
     528             :         }
     529       37325 :         MT_thread_setworking("exiting");
     530             :         /* pre announce our exiting: cleaning up may take a while and we
     531             :          * don't want to get killed during that time for fear of
     532             :          * deadlocks */
     533       37324 :         MT_exiting_thread();
     534             :         /*
     535             :          * At this stage we should clean out the MAL block
     536             :          */
     537       37325 :         if (c->backup) {
     538           0 :                 assert(0);
     539             :                 freeSymbol(c->backup);
     540             :                 c->backup = 0;
     541             :         }
     542             : 
     543       37325 :         if (c->curprg && c->curprg->def)
     544           0 :                 resetMalBlk(c->curprg->def);
     545             :         /*
     546             :            if (c->curprg) {
     547             :            freeSymbol(c->curprg);
     548             :            c->curprg = 0;
     549             :            }
     550             :          */
     551             : 
     552       37325 :         MCcloseClient(c);
     553       37325 :         return MAL_SUCCEED;
     554             : }
     555             : 
     556             : /*
     557             :  * The stages of processing user requests are controlled by a scenario.
     558             :  * The routines below are the default implementation.  The main issues
     559             :  * to deal after parsing it to clean out the Admin.main function from
     560             :  * any information added erroneously.
     561             :  *
     562             :  * Ideally this involves resetting the state of the client 'main'
     563             :  * function, i.e. the symbol table is reset and any instruction added
     564             :  * should be cleaned. Beware that the instruction table may have grown
     565             :  * in size.
     566             :  */
     567             : str
     568         313 : MALinitClient(Client c)
     569             : {
     570         313 :         (void) c;
     571         313 :         return MAL_SUCCEED;
     572             : }
     573             : 
     574             : str
     575       37357 : MALexitClient(Client c)
     576             : {
     577       37357 :         if (c->glb && c->curprg->def && c->curprg->def->errors == MAL_SUCCEED)
     578       37356 :                 garbageCollector(c, c->curprg->def, c->glb, TRUE);
     579       37357 :         c->mode = FINISHCLIENT;
     580       37357 :         if (c->backup) {
     581           0 :                 assert(0);
     582             :                 freeSymbol(c->backup);
     583             :                 c->backup = NULL;
     584             :         }
     585             :         /* should be in the usermodule */
     586       37357 :         c->curprg = NULL;
     587       37357 :         if (c->usermodule) {
     588       37357 :                 freeModule(c->usermodule);
     589       37357 :                 c->usermodule = NULL;
     590             :         }
     591       37357 :         return NULL;
     592             : }
     593             : 
     594             : static str
     595       10970 : MALreader(Client c)
     596             : {
     597       10970 :         if (MCreadClient(c) > 0)
     598             :                 return MAL_SUCCEED;
     599         498 :         MT_lock_set(&mal_contextLock);
     600         498 :         c->mode = FINISHCLIENT;
     601         498 :         MT_lock_unset(&mal_contextLock);
     602         498 :         if (c->fdin)
     603         498 :                 c->fdin->buf[c->fdin->pos] = 0;
     604             :         return MAL_SUCCEED;
     605             : }
     606             : 
     607             : /* Before compiling a large string, it makes sense to allocate
     608             :  * approximately enough space to keep the intermediate
     609             :  * code. Otherwise, we end up with a repeated extend on the MAL block,
     610             :  * which really consumes a lot of memcpy resources. The average MAL
     611             :  * string length could been derived from the test cases. An error in
     612             :  * the estimate is more expensive than just counting the lines.
     613             :  */
     614             : static int
     615       10472 : prepareMalBlk(MalBlkPtr mb, const char *s)
     616             : {
     617       10472 :         int cnt = STMT_INCREMENT;
     618             : 
     619       10472 :         if (s && *s) {
     620       24505 :                 while ((s = strchr(s + 1, '\n')) != NULL)
     621       14033 :                         cnt++;
     622             :         }
     623       10472 :         cnt = (int) (cnt * 1.1);
     624       10472 :         return resizeMalBlk(mb, cnt);
     625             : }
     626             : 
     627             : str
     628       10472 : MALparser(Client c)
     629             : {
     630       10472 :         InstrPtr p;
     631       10472 :         str msg = MAL_SUCCEED;
     632             : 
     633       10472 :         assert(c->curprg->def->errors == NULL);
     634       10472 :         c->curprg->def->errors = 0;
     635             : 
     636       10472 :         if (prepareMalBlk(c->curprg->def, CURRENT(c)) < 0)
     637           0 :                 throw(MAL, "mal.parser", "Failed to prepare");
     638       10471 :         parseMAL(c, c->curprg, 0, INT_MAX, 0);
     639             : 
     640             :         /* now the parsing is done we should advance the stream */
     641       10471 :         c->fdin->pos += c->yycur;
     642       10471 :         c->yycur = 0;
     643       10471 :         c->qryctx.starttime = GDKusec();
     644       10470 :         c->qryctx.endtime = c->querytimeout ? c->qryctx.starttime + c->querytimeout : 0;
     645             : 
     646             :         /* check for unfinished blocks */
     647       10470 :         if (!c->curprg->def->errors && c->blkmode)
     648             :                 return MAL_SUCCEED;
     649             :         /* empty files should be skipped as well */
     650       10220 :         if (c->curprg->def->stop == 1) {
     651         243 :                 if ((msg = c->curprg->def->errors))
     652          53 :                         c->curprg->def->errors = 0;
     653         243 :                 return msg;
     654             :         }
     655             : 
     656        9977 :         p = getInstrPtr(c->curprg->def, 0);
     657        9977 :         if (p->token != FUNCTIONsymbol) {
     658           0 :                 msg = c->curprg->def->errors;
     659           0 :                 c->curprg->def->errors = 0;
     660           0 :                 MSresetStack(c, c->curprg->def, c->glb);
     661           0 :                 resetMalTypes(c->curprg->def, 1);
     662           0 :                 return msg;
     663             :         }
     664        9977 :         pushEndInstruction(c->curprg->def);
     665        9977 :         msg = chkProgram(c->usermodule, c->curprg->def);
     666        9975 :         if (msg != MAL_SUCCEED || (msg = c->curprg->def->errors)) {
     667          47 :                 c->curprg->def->errors = 0;
     668          47 :                 MSresetStack(c, c->curprg->def, c->glb);
     669          47 :                 resetMalTypes(c->curprg->def, 1);
     670          47 :                 return msg;
     671             :         }
     672             :         return MAL_SUCCEED;
     673             : }
     674             : 
     675             : int
     676      258739 : MALcommentsOnly(MalBlkPtr mb)
     677             : {
     678      258739 :         int i;
     679             : 
     680      258827 :         for (i = 1; i < mb->stop; i++)
     681      259308 :                 if (mb->stmt[i]->token != REMsymbol)
     682             :                         return 0;
     683             :         return 1;
     684             : }
     685             : 
     686             : /*
     687             :  * The default MAL optimizer includes a final call to
     688             :  * the multiplex expander.
     689             :  * We should take care of functions marked as 'inline',
     690             :  * because they should be kept in raw form.
     691             :  * Their optimization takes place after inlining.
     692             :  */
     693             : static str
     694       10117 : MALoptimizer(Client c)
     695             : {
     696       10117 :         str msg;
     697             : 
     698       10117 :         if (c->curprg->def->inlineProp)
     699             :                 return MAL_SUCCEED;
     700             :         // only a signature statement can be skipped
     701       10117 :         if (c->curprg->def->stop == 1)
     702             :                 return MAL_SUCCEED;
     703        9927 :         msg = optimizeMALBlock(c, c->curprg->def);
     704             :         /*
     705             :            if( msg == MAL_SUCCEED)
     706             :            msg = OPTmultiplexSimple(c, c->curprg->def);
     707             :          */
     708        9927 :         return msg;
     709             : }
     710             : 
     711             : static str
     712       10720 : MALengine_(Client c)
     713             : {
     714       10720 :         Symbol prg;
     715       10720 :         str msg = MAL_SUCCEED;
     716             : 
     717       10971 :         do {
     718       10971 :                 if ((msg = MALreader(c)) != MAL_SUCCEED)
     719           0 :                         return msg;
     720       10970 :                 if (c->mode == FINISHCLIENT)
     721             :                         return msg;
     722       10472 :                 if ((msg = MALparser(c)) != MAL_SUCCEED)
     723         100 :                         return msg;
     724       10369 :         } while (c->blkmode);
     725             :         /*
     726             :            if (c->blkmode)
     727             :            return MAL_SUCCEED;
     728             :          */
     729       10118 :         if ((msg = MALoptimizer(c)) != MAL_SUCCEED)
     730             :                 return msg;
     731       10114 :         prg = c->curprg;
     732       10114 :         if (prg == NULL)
     733           0 :                 throw(SYNTAX, "mal.engine", SYNTAX_SIGNATURE);
     734       10114 :         if (prg->def == NULL)
     735           0 :                 throw(SYNTAX, "mal.engine", SYNTAX_SIGNATURE);
     736             : 
     737       10114 :         if (prg->def->errors != MAL_SUCCEED) {
     738           0 :                 msg = prg->def->errors;
     739           0 :                 prg->def->errors = NULL;
     740           0 :                 MSresetStack(c, c->curprg->def, c->glb);
     741           0 :                 resetMalTypes(c->curprg->def, 1);
     742           0 :                 return msg;
     743             :         }
     744       10114 :         if (prg->def->stop == 1 || MALcommentsOnly(prg->def))
     745         190 :                 return 0;                               /* empty block */
     746        9924 :         if (c->glb) {
     747        9924 :                 if (prg->def && c->glb->stksize < prg->def->vsize) {
     748           4 :                         c->glb = reallocGlobalStack(c->glb, prg->def->vsize);
     749           4 :                         if (c->glb == NULL)
     750           0 :                                 throw(MAL, "mal.engine", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     751             :                 }
     752        9924 :                 c->glb->stktop = prg->def->vtop;
     753        9924 :                 c->glb->blk = prg->def;
     754             :         }
     755             : 
     756             :         /*
     757             :          * In interactive mode we should avoid early garbage collection of values.
     758             :          * This can be controlled by the clean up control at the instruction level
     759             :          * and marking all non-temporary variables as being (potentially) used.
     760             :          */
     761        9924 :         if (c->glb) {
     762        9924 :                 c->glb->pcup = 0;
     763        9924 :                 c->glb->keepAlive = TRUE; /* no garbage collection */
     764             :         }
     765        9924 :         if (prg->def->errors == MAL_SUCCEED)
     766        9922 :                 msg = (str) runMAL(c, prg->def, 0, c->glb);
     767        9928 :         if (msg) {
     768             :                 /* ignore "internal" exceptions */
     769          26 :                 if (strstr(msg, "client.quit")) {
     770           0 :                         freeException(msg);
     771           0 :                         msg = MAL_SUCCEED;
     772             :                 }
     773             :         }
     774        9928 :         MSresetStack(c, prg->def, c->glb);
     775        9926 :         resetMalTypes(prg->def, 1);
     776        9926 :         if (c->glb) {
     777             :                 /* for global stacks avoid reinitialization from this point */
     778        9926 :                 c->glb->stkbot = prg->def->vtop;
     779             :         }
     780             : 
     781        9926 :         if (prg->def->errors)
     782           0 :                 freeException(prg->def->errors);
     783        9926 :         prg->def->errors = NULL;
     784        9926 :         return msg;
     785             : }
     786             : 
     787             : void
     788       10720 : MALengine(Client c)
     789             : {
     790       10720 :         str msg = MALengine_(c);
     791       10720 :         if (msg) {
     792             :                 /* don't print exception decoration, just the message */
     793         318 :                 char *n = NULL;
     794             :                 char *o = msg;
     795         318 :                 while ((n = strchr(o, '\n')) != NULL) {
     796         188 :                         if (*o == '!')
     797           0 :                                 o++;
     798         188 :                         mnstr_printf(c->fdout, "!%.*s\n", (int) (n - o), o);
     799         188 :                         o = ++n;
     800             :                 }
     801         130 :                 if (*o != 0) {
     802          39 :                         if (*o == '!')
     803           4 :                                 o++;
     804          39 :                         mnstr_printf(c->fdout, "!%s\n", o);
     805             :                 }
     806         130 :                 freeException(msg);
     807             :         }
     808       10720 : }
     809             : 
     810             : /* Hypothetical, optimizers may massage the plan in such a way
     811             :  * that multiple passes are needed.
     812             :  * However, the current SQL driven approach only expects a single
     813             :  * non-repeating pipeline of optimizer steps stored at the end of the MAL block.
     814             :  * A single scan forward over the MAL plan is assumed.
     815             :  */
     816             : str
     817      526939 : optimizeMALBlock(Client cntxt, MalBlkPtr mb)
     818             : {
     819      526939 :         InstrPtr p;
     820      526939 :         int pc, oldstop;
     821      526939 :         str msg = MAL_SUCCEED;
     822      526939 :         int cnt = 0;
     823      526939 :         int actions = 0;
     824      526939 :         lng clk = GDKusec();
     825             : 
     826             :         /* assume the type and flow have been checked already */
     827             :         /* SQL functions intended to be inlined should not be optimized */
     828      526994 :         if (mb->inlineProp)
     829             :                 return 0;
     830             : 
     831      526827 :         mb->optimize = 0;
     832      526827 :         if (mb->errors)
     833           0 :                 throw(MAL, "optimizer.MALoptimizer",
     834             :                           SQLSTATE(42000) "Start with inconsistent MAL plan");
     835             : 
     836             :         // strong defense line, assure that MAL plan is initially correct
     837      526827 :         if (mb->errors == 0 && mb->stop > 1) {
     838      526781 :                 resetMalTypes(mb, mb->stop);
     839      526810 :                 msg = chkTypes(cntxt->usermodule, mb, FALSE);
     840      525800 :                 if (!msg)
     841      526091 :                         msg = chkFlow(mb);
     842      526589 :                 if (!msg)
     843      526257 :                         msg = chkDeclarations(mb);
     844      526304 :                 if (msg)
     845           6 :                         return msg;
     846      526630 :                 if (mb->errors != MAL_SUCCEED) {
     847           0 :                         msg = mb->errors;
     848           0 :                         mb->errors = MAL_SUCCEED;
     849           0 :                         return msg;
     850             :                 }
     851             :         }
     852             : 
     853      526676 :         oldstop = mb->stop;
     854    37527256 :         for (pc = 0; pc < mb->stop; pc++) {
     855    37000457 :                 p = getInstrPtr(mb, pc);
     856    37000457 :                 if (getModuleId(p) == optimizerRef && p->fcn && p->token != REMsymbol) {
     857    12463775 :                         actions++;
     858    12463775 :                         msg = (*(str (*)(Client, MalBlkPtr, MalStkPtr, InstrPtr)) p->fcn) (cntxt, mb, 0, p);
     859    12463898 :                         if (mb->errors) {
     860           0 :                                 freeException(msg);
     861           0 :                                 msg = mb->errors;
     862           0 :                                 mb->errors = NULL;
     863             :                         }
     864    12463898 :                         if (msg) {
     865           0 :                                 str place = getExceptionPlace(msg);
     866           0 :                                 str nmsg = NULL;
     867           0 :                                 if (place) {
     868           0 :                                         nmsg = createException(getExceptionType(msg), place, "%s",
     869             :                                                                                    getExceptionMessageAndState(msg));
     870           0 :                                         GDKfree(place);
     871             :                                 }
     872           0 :                                 if (nmsg) {
     873           0 :                                         freeException(msg);
     874           0 :                                         msg = nmsg;
     875             :                                 }
     876           0 :                                 goto wrapup;
     877             :                         }
     878    12463898 :                         if (cntxt->mode == FINISHCLIENT) {
     879           0 :                                 mb->optimize = GDKusec() - clk;
     880           0 :                                 throw(MAL, "optimizeMALBlock",
     881             :                                           SQLSTATE(42000) "prematurely stopped client");
     882             :                         }
     883             :                         /* the MAL block may have changed */
     884    12463898 :                         pc += mb->stop - oldstop - 1;
     885    12463898 :                         oldstop = mb->stop;
     886             :                 }
     887             :         }
     888             : 
     889      526799 :   wrapup:
     890             :         /* Keep the total time spent on optimizing the plan for inspection */
     891      526799 :         if (actions > 0 && msg == MAL_SUCCEED) {
     892      516974 :                 mb->optimize = GDKusec() - clk;
     893      516995 :                 p = newStmt(mb, optimizerRef, totalRef);
     894      517006 :                 if (p == NULL) {
     895           0 :                         throw(MAL, "optimizer.MALoptimizer",
     896             :                                   SQLSTATE(HY013) MAL_MALLOC_FAIL);
     897             :                 }
     898      517006 :                 p->token = REMsymbol;
     899      517006 :                 p = pushInt(mb, p, actions);
     900      516977 :                 p = pushLng(mb, p, mb->optimize);
     901      516924 :                 pushInstruction(mb, p);
     902             :         }
     903      526691 :         if (cnt >= mb->stop)
     904           0 :                 throw(MAL, "optimizer.MALoptimizer", SQLSTATE(42000) OPTIMIZER_CYCLE);
     905             :         return msg;
     906             : }

Generated by: LCOV version 1.14