LCOV - code coverage report
Current view: top level - monetdb5/mal - mal_session.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 362 479 75.6 %
Date: 2024-10-07 21:21:43 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         329 : malBootstrap(char *modules[], bool embedded, const char *initpasswd)
      37             : {
      38         329 :         Client c;
      39         329 :         str msg = MAL_SUCCEED;
      40             : 
      41         329 :         c = MCinitClient(MAL_ADMIN, NULL, NULL);
      42         329 :         if (c == NULL) {
      43           0 :                 throw(MAL, "malBootstrap", "Failed to initialize client");
      44             :         }
      45         329 :         MT_thread_set_qry_ctx(NULL);
      46         329 :         assert(c != NULL);
      47         329 :         c->curmodule = c->usermodule = userModule();
      48         329 :         if (c->usermodule == NULL) {
      49           0 :                 MCcloseClient(c);
      50           0 :                 throw(MAL, "malBootstrap", "Failed to initialize client MAL module");
      51             :         }
      52         329 :         if ((msg = defaultScenario(c))) {
      53           0 :                 MCcloseClient(c);
      54           0 :                 return msg;
      55             :         }
      56         329 :         if ((msg = MSinitClientPrg(c, "user", "main")) != MAL_SUCCEED) {
      57           0 :                 MCcloseClient(c);
      58           0 :                 return msg;
      59             :         }
      60             : 
      61         329 :         if (MCinitClientThread(c) < 0) {
      62           0 :                 MCcloseClient(c);
      63           0 :                 throw(MAL, "malBootstrap", "Failed to create client thread");
      64             :         }
      65         329 :         if ((msg = malIncludeModules(c, modules, 0, embedded, initpasswd)) != MAL_SUCCEED) {
      66           0 :                 MCcloseClient(c);
      67           0 :                 return msg;
      68             :         }
      69         328 :         MCcloseClient(c);
      70         328 :         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      304602 : MSresetClientPrg(Client cntxt, const char *mod, const char *fcn)
      89             : {
      90      304602 :         MalBlkPtr mb;
      91      304602 :         InstrPtr p;
      92             : 
      93      304602 :         mb = cntxt->curprg->def;
      94      304602 :         mb->stop = 1;
      95      304602 :         mb->errors = MAL_SUCCEED;
      96      304602 :         p = mb->stmt[0];
      97             : 
      98      304602 :         p->gc = 0;
      99      304602 :         p->retc = 1;
     100      304602 :         p->argc = 1;
     101      304602 :         p->argv[0] = 0;
     102             : 
     103      304602 :         setModuleId(p, mod);
     104      304602 :         setFunctionId(p, fcn);
     105      304602 :         if (findVariable(mb, fcn) < 0)
     106      304592 :                 if ((p->argv[0] = newVariable(mb, fcn, strlen(fcn), TYPE_void)) < 0)
     107           0 :                         throw(MAL, "resetClientPrg", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     108             : 
     109      304602 :         setVarType(mb, findVariable(mb, fcn), TYPE_void);
     110      304602 :         return MAL_SUCCEED;
     111             : }
     112             : 
     113             : /*
     114             :  * Create a new container block
     115             :  */
     116             : 
     117             : str
     118      353808 : MSinitClientPrg(Client cntxt, const char *mod, const char *nme)
     119             : {
     120      353808 :         int idx;
     121             : 
     122      353808 :         if (cntxt->curprg && idcmp(nme, cntxt->curprg->name) == 0)
     123      304602 :                 return MSresetClientPrg(cntxt, putName(mod), putName(nme));
     124       49206 :         cntxt->curprg = newFunction(putName(mod), putName(nme), FUNCTIONsymbol);
     125       49197 :         if (cntxt->curprg == 0)
     126           0 :                 throw(MAL, "initClientPrg", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     127       49197 :         if ((idx = findVariable(cntxt->curprg->def, "main")) >= 0)
     128       38787 :                 setVarType(cntxt->curprg->def, idx, TYPE_void);
     129       49202 :         insertSymbol(cntxt->usermodule, cntxt->curprg);
     130             : 
     131       49196 :         if (cntxt->glb == NULL)
     132       38803 :                 cntxt->glb = newGlobalStack(MAXGLOBALS + cntxt->curprg->def->vsize);
     133       49206 :         if (cntxt->glb == NULL)
     134           0 :                 throw(MAL, "initClientPrg", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     135       49206 :         assert(cntxt->curprg->def != NULL);
     136       49206 :         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         165 : exit_streams(bstream *fin, stream *fout)
     150             : {
     151         165 :         if (fout && fout != GDKstdout) {
     152         165 :                 mnstr_flush(fout, MNSTR_FLUSH_DATA);
     153         165 :                 close_stream(fout);
     154             :         }
     155         165 :         if (fin)
     156         165 :                 bstream_destroy(fin);
     157         165 : }
     158             : 
     159             : static const char mal_enableflag[] = "mal_for_all";
     160             : 
     161             : static bool
     162        1974 : is_exiting(void *data)
     163             : {
     164        1974 :         (void) data;
     165        1974 :         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       38624 : MSscheduleClient(str command, str peer, str challenge, bstream *fin, stream *fout,
     190             :                                  protocol_version protocol, size_t blocksize)
     191             : {
     192       38624 :         char *user = command, *algo = NULL, *passwd = NULL, *lang = NULL,
     193       38624 :                 *handshake_opts = NULL;
     194       38624 :         char *database = NULL, *s;
     195       38624 :         const char *dbname;
     196       38624 :         str msg = MAL_SUCCEED;
     197       38624 :         bool filetrans = false;
     198       38624 :         Client c;
     199             : 
     200       38624 :         MT_thread_set_qry_ctx(NULL);
     201             : 
     202             :         /* decode BIG/LIT:user:{cypher}passwordchal:lang:database: line */
     203             : 
     204             :         /* byte order */
     205       38608 :         s = strchr(user, ':');
     206       38608 :         if (s) {
     207       38443 :                 *s = 0;
     208       38443 :                 mnstr_set_bigendian(fin->s, strcmp(user, "BIG") == 0);
     209       38434 :                 user = s + 1;
     210             :         } else {
     211         165 :                 mnstr_printf(fout, "!incomplete challenge '%s'\n", user);
     212         165 :                 exit_streams(fin, fout);
     213         165 :                 GDKfree(command);
     214         342 :                 return;
     215             :         }
     216             : 
     217             :         /* passwd */
     218       38434 :         s = strchr(user, ':');
     219       38434 :         if (s) {
     220       38434 :                 *s = 0;
     221       38434 :                 passwd = s + 1;
     222             :                 /* decode algorithm, i.e. {plain}mypasswordchallenge */
     223       38434 :                 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       38434 :                 algo = passwd + 1;
     230       38434 :                 s = strchr(algo, '}');
     231       38434 :                 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       38434 :                 *s = 0;
     238       38434 :                 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       38434 :         s = strchr(passwd, ':');
     248       38434 :         if (s) {
     249       38434 :                 *s = 0;
     250       38434 :                 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       38434 :         s = strchr(lang, ':');
     260       38434 :         if (s) {
     261       38434 :                 *s = 0;
     262       38434 :                 database = s + 1;
     263             :                 /* we can have stuff following, make it void */
     264       38434 :                 s = strchr(database, ':');
     265       38434 :                 if (s)
     266       38434 :                         *s++ = 0;
     267             :         }
     268             : 
     269       38434 :         if (s && strncmp(s, "FILETRANS:", 10) == 0) {
     270       38127 :                 s += 10;
     271       38127 :                 filetrans = true;
     272         307 :         } else if (s && s[0] == ':') {
     273           0 :                 s += 1;
     274           0 :                 filetrans = false;
     275             :         }
     276             : 
     277       38434 :         if (s && strchr(s, ':') != NULL) {
     278       38127 :                 handshake_opts = s;
     279       38127 :                 s = strchr(s, ':');
     280       38127 :                 *s++ = '\0';
     281             :         }
     282       38434 :         dbname = GDKgetenv("gdk_dbname");
     283       38466 :         if (database != NULL && database[0] != '\0' &&
     284       37451 :                 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       38466 :                 c = MCinitClient(0, fin, fout);
     295       38466 :                 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       38466 :                 c->filetrans = filetrans;
     307       38466 :                 c->handshake_options = handshake_opts ? strdup(handshake_opts) : NULL;
     308             :                 /* move this back !! */
     309       38466 :                 if (c->usermodule == 0) {
     310       38465 :                         c->curmodule = c->usermodule = userModule();
     311       38466 :                         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       38467 :                 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       38464 :                 if (!GDKgetenv_isyes(mal_enableflag)
     325       38466 :                         && 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       38463 :         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       38457 :         c->username = GDKstrdup(user);
     344       38451 :         if (peer)
     345       38451 :                 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       38460 :         c->protocol = protocol;
     362       38460 :         c->blocksize = blocksize;
     363             : 
     364       38460 :         if (c->initClient) {
     365       38460 :                 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       38453 :         GDKfree(command);
     375             : 
     376       38454 :         mnstr_settimeout(c->fdin->s, 50, is_exiting, NULL);
     377       38448 :         msg = MSserveClient(c);
     378       38454 :         if (msg != MAL_SUCCEED) {
     379           0 :                 mnstr_printf(fout, "!could not serve client\n");
     380           0 :                 exit_streams(fin, fout);
     381           0 :                 freeException(msg);
     382             :         }
     383             : }
     384             : 
     385             : /*
     386             :  * After the client initialization has been finished, we can start the
     387             :  * interaction protocol. This involves parsing the input in the context
     388             :  * of an already defined procedure and upon success, its execution.
     389             :  *
     390             :  * In essence, this calls for an incremental parsing operation, because
     391             :  * we should wait until a complete basic block has been detected.  Test,
     392             :  * first collect the instructions before we take them all.
     393             :  *
     394             :  * In interactive mode, we should remove the instructions before
     395             :  * accepting new ones. The function signature remains the same and the
     396             :  * symbol table should also not be affected.  Aside from removing
     397             :  * instruction, we should also condense the variable stack, i.e.
     398             :  * removing at least the temporary variables, but maybe everything
     399             :  * beyond a previous defined point.
     400             :  *
     401             :  * Beware that we have to cleanup the global stack as well. This to
     402             :  * avoid subsequent calls to find garbage information.  However, this
     403             :  * action is only required after a successful execution.  Otherwise,
     404             :  * garbage collection is not needed.
     405             :  */
     406             : void
     407      310826 : MSresetInstructions(MalBlkPtr mb, int start)
     408             : {
     409      310826 :         int i;
     410      310826 :         InstrPtr p;
     411             : 
     412    76016853 :         for (i = start; i < mb->ssize; i++) {
     413    75705792 :                 p = getInstrPtr(mb, i);
     414    75705792 :                 if (p)
     415       38723 :                         freeInstruction(p);
     416    75706027 :                 mb->stmt[i] = NULL;
     417             :         }
     418      311061 :         mb->stop = start;
     419      311061 : }
     420             : 
     421             : /*
     422             :  * MAL instructions generate variables.
     423             :  * The values of temporary variables should be cleaned at the end of a call
     424             :  * The values of global variables are retained.
     425             :  * Global variables should not start with C_ or X_
     426             :  */
     427             : void
     428        9979 : MSresetStack(Client cntxt, MalBlkPtr mb, MalStkPtr glb)
     429             : {
     430        9979 :         InstrPtr sig = getInstrPtr(mb, 0);
     431        9979 :         int i, k = sig->argc;
     432             : 
     433        9979 :         if (mb->errors == MAL_SUCCEED) {
     434     1251873 :                 for (i = sig->argc; i < mb->vtop; i++) {
     435     1241894 :                         if (glb && i < glb->stktop && isTmpVar(mb, i) && !glb->keepTmps) {
     436        7516 :                                 if (mb->var[i].name)
     437           2 :                                         GDKfree(mb->var[i].name);
     438             :                                 /* clean stack entry */
     439        7516 :                                 garbageElement(cntxt, &glb->stk[i]);
     440        7516 :                                 glb->stk[i].vtype = TYPE_int;
     441        7516 :                                 glb->stk[i].len = 0;
     442        7516 :                                 glb->stk[i].val.pval = 0;
     443        7516 :                                 if (isVarConstant(mb, i))
     444        4843 :                                         garbageElement(cntxt, &mb->var[i].value);
     445             :                         } else {
     446             :                                 /* compress the global variable list and stack */
     447     1234378 :                                 mb->var[k] = mb->var[i];
     448     1234378 :                                 glb->stk[k] = glb->stk[i];
     449     1234378 :                                 setVarUsed(mb, k);
     450     1234378 :                                 setVarInit(mb, k);
     451     1234378 :                                 if (i != k) {
     452          68 :                                         glb->stk[i].vtype = TYPE_int;
     453          68 :                                         glb->stk[i].len = 0;
     454          68 :                                         glb->stk[i].val.pval = 0;
     455          68 :                                         clrVarConstant(mb, i);
     456          68 :                                         clrVarCleanup(mb, i);
     457             :                                 }
     458     1234378 :                                 k++;
     459             :                         }
     460             :                 }
     461             :         }
     462        9979 :         assert(k <= mb->vsize);
     463        9979 :         mb->vtop = k;
     464        9979 : }
     465             : 
     466             : /* The symbol table be become filled with constant values to be garbage collected
     467             : * The signature is always left behind.
     468             : */
     469             : 
     470             : void
     471           0 : MSresetVariables(MalBlkPtr mb)
     472             : {
     473           0 :         InstrPtr sig = getInstrPtr(mb, 0);
     474           0 :         int i;
     475             : 
     476           0 :         if (mb->errors == MAL_SUCCEED)
     477           0 :                 for (i = sig->argc; i < mb->vtop; i++)
     478           0 :                         if (isVarConstant(mb, i)) {
     479           0 :                                 VALclear(&getVarConstant(mb, i));
     480           0 :                                 clrVarConstant(mb, i);
     481             :                         }
     482           0 : }
     483             : 
     484             : /*
     485             :  * Here we start the client.  We need to initialize and allocate space
     486             :  * for the global variables.  Thereafter it is up to the scenario
     487             :  * interpreter to process input.
     488             :  */
     489             : static str
     490       38437 : MSserveClient(Client c)
     491             : {
     492       38437 :         MalBlkPtr mb;
     493       38437 :         str msg = 0;
     494             : 
     495       38437 :         if (MCinitClientThread(c) < 0) {
     496           0 :                 MCcloseClient(c);
     497           0 :                 return MAL_SUCCEED;
     498             :         }
     499             :         /*
     500             :          * A stack frame is initialized to keep track of global variables.
     501             :          * The scenarios are run until we finally close the last one.
     502             :          */
     503       38450 :         mb = c->curprg->def;
     504       38450 :         if (c->glb == NULL)
     505           0 :                 c->glb = newGlobalStack(MAXGLOBALS + mb->vsize);
     506       38450 :         if (c->glb == NULL) {
     507           0 :                 MCcloseClient(c);
     508           0 :                 throw(MAL, "serveClient", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     509             :         } else {
     510       38450 :                 c->glb->stktop = mb->vtop;
     511       38450 :                 c->glb->blk = mb;
     512             :         }
     513             : 
     514       38450 :         if (c->scenario == 0)
     515           0 :                 msg = defaultScenario(c);
     516       38450 :         if (msg) {
     517           0 :                 MCcloseClient(c);
     518           0 :                 return msg;
     519             :         } else {
     520       38450 :                 do {
     521       38450 :                         do {
     522       38450 :                                 MT_thread_setworking("running scenario");
     523       38451 :                                 msg = runScenario(c);
     524       38453 :                                 freeException(msg);
     525       38454 :                                 if (c->mode == FINISHCLIENT)
     526             :                                         break;
     527           0 :                                 resetScenario(c);
     528           0 :                         } while (c->scenario && !GDKexiting());
     529       38454 :                 } while (c->scenario && c->mode != FINISHCLIENT && !GDKexiting());
     530             :         }
     531       38454 :         MT_thread_setworking("exiting");
     532             :         /* pre announce our exiting: cleaning up may take a while and we
     533             :          * don't want to get killed during that time for fear of
     534             :          * deadlocks */
     535       38454 :         MT_exiting_thread();
     536             :         /*
     537             :          * At this stage we should clean out the MAL block
     538             :          */
     539       38454 :         if (c->backup) {
     540           0 :                 assert(0);
     541             :                 freeSymbol(c->backup);
     542             :                 c->backup = 0;
     543             :         }
     544             : 
     545       38454 :         if (c->curprg && c->curprg->def)
     546           0 :                 resetMalBlk(c->curprg->def);
     547             :         /*
     548             :            if (c->curprg) {
     549             :            freeSymbol(c->curprg);
     550             :            c->curprg = 0;
     551             :            }
     552             :          */
     553             : 
     554       38454 :         MCcloseClient(c);
     555       38454 :         return MAL_SUCCEED;
     556             : }
     557             : 
     558             : /*
     559             :  * The stages of processing user requests are controlled by a scenario.
     560             :  * The routines below are the default implementation.  The main issues
     561             :  * to deal after parsing it to clean out the Admin.main function from
     562             :  * any information added erroneously.
     563             :  *
     564             :  * Ideally this involves resetting the state of the client 'main'
     565             :  * function, i.e. the symbol table is reset and any instruction added
     566             :  * should be cleaned. Beware that the instruction table may have grown
     567             :  * in size.
     568             :  */
     569             : str
     570         313 : MALinitClient(Client c)
     571             : {
     572         313 :         (void) c;
     573         313 :         return MAL_SUCCEED;
     574             : }
     575             : 
     576             : str
     577       38486 : MALexitClient(Client c)
     578             : {
     579       38486 :         if (c->glb && c->curprg->def && c->curprg->def->errors == MAL_SUCCEED)
     580       38485 :                 garbageCollector(c, c->curprg->def, c->glb, TRUE);
     581       38486 :         c->mode = FINISHCLIENT;
     582       38486 :         if (c->backup) {
     583           0 :                 assert(0);
     584             :                 freeSymbol(c->backup);
     585             :                 c->backup = NULL;
     586             :         }
     587             :         /* should be in the usermodule */
     588       38486 :         c->curprg = NULL;
     589       38486 :         if (c->usermodule) {
     590       38485 :                 freeModule(c->usermodule);
     591       38486 :                 c->usermodule = NULL;
     592             :         }
     593       38487 :         return NULL;
     594             : }
     595             : 
     596             : static str
     597       10970 : MALreader(Client c)
     598             : {
     599       10970 :         if (MCreadClient(c) > 0)
     600             :                 return MAL_SUCCEED;
     601         498 :         MT_lock_set(&mal_contextLock);
     602         498 :         c->mode = FINISHCLIENT;
     603         498 :         MT_lock_unset(&mal_contextLock);
     604         498 :         if (c->fdin)
     605         498 :                 c->fdin->buf[c->fdin->pos] = 0;
     606             :         return MAL_SUCCEED;
     607             : }
     608             : 
     609             : /* Before compiling a large string, it makes sense to allocate
     610             :  * approximately enough space to keep the intermediate
     611             :  * code. Otherwise, we end up with a repeated extend on the MAL block,
     612             :  * which really consumes a lot of memcpy resources. The average MAL
     613             :  * string length could been derived from the test cases. An error in
     614             :  * the estimate is more expensive than just counting the lines.
     615             :  */
     616             : static int
     617       10472 : prepareMalBlk(MalBlkPtr mb, const char *s)
     618             : {
     619       10472 :         int cnt = STMT_INCREMENT;
     620             : 
     621       10472 :         if (s && *s) {
     622       24500 :                 while ((s = strchr(s + 1, '\n')) != NULL)
     623       14028 :                         cnt++;
     624             :         }
     625       10472 :         cnt = (int) (cnt * 1.1);
     626       10472 :         return resizeMalBlk(mb, cnt);
     627             : }
     628             : 
     629             : str
     630       10472 : MALparser(Client c)
     631             : {
     632       10472 :         InstrPtr p;
     633       10472 :         str msg = MAL_SUCCEED;
     634             : 
     635       10472 :         assert(c->curprg->def->errors == NULL);
     636       10472 :         c->curprg->def->errors = 0;
     637             : 
     638       10472 :         if (prepareMalBlk(c->curprg->def, CURRENT(c)) < 0)
     639           0 :                 throw(MAL, "mal.parser", "Failed to prepare");
     640       10466 :         parseMAL(c, c->curprg, 0, INT_MAX, 0);
     641             : 
     642             :         /* now the parsing is done we should advance the stream */
     643       10467 :         c->fdin->pos += c->yycur;
     644       10467 :         c->yycur = 0;
     645       10467 :         c->qryctx.starttime = GDKusec();
     646       10464 :         c->qryctx.endtime = c->querytimeout ? c->qryctx.starttime + c->querytimeout : 0;
     647             : 
     648             :         /* check for unfinished blocks */
     649       10464 :         if (!c->curprg->def->errors && c->blkmode)
     650             :                 return MAL_SUCCEED;
     651             :         /* empty files should be skipped as well */
     652       10214 :         if (c->curprg->def->stop == 1) {
     653         243 :                 if ((msg = c->curprg->def->errors))
     654          53 :                         c->curprg->def->errors = 0;
     655         243 :                 return msg;
     656             :         }
     657             : 
     658        9971 :         p = getInstrPtr(c->curprg->def, 0);
     659        9971 :         if (p->token != FUNCTIONsymbol) {
     660           0 :                 msg = c->curprg->def->errors;
     661           0 :                 c->curprg->def->errors = 0;
     662           0 :                 MSresetStack(c, c->curprg->def, c->glb);
     663           0 :                 resetMalTypes(c->curprg->def, 1);
     664           0 :                 return msg;
     665             :         }
     666        9971 :         pushEndInstruction(c->curprg->def);
     667        9977 :         msg = chkProgram(c->usermodule, c->curprg->def);
     668        9972 :         if (msg != MAL_SUCCEED || (msg = c->curprg->def->errors)) {
     669          47 :                 c->curprg->def->errors = 0;
     670          47 :                 MSresetStack(c, c->curprg->def, c->glb);
     671          47 :                 resetMalTypes(c->curprg->def, 1);
     672          47 :                 return msg;
     673             :         }
     674             :         return MAL_SUCCEED;
     675             : }
     676             : 
     677             : int
     678      263388 : MALcommentsOnly(MalBlkPtr mb)
     679             : {
     680      263388 :         int i;
     681             : 
     682      263476 :         for (i = 1; i < mb->stop; i++)
     683      263949 :                 if (mb->stmt[i]->token != REMsymbol)
     684             :                         return 0;
     685             :         return 1;
     686             : }
     687             : 
     688             : /*
     689             :  * The default MAL optimizer includes a final call to
     690             :  * the multiplex expander.
     691             :  * We should take care of functions marked as 'inline',
     692             :  * because they should be kept in raw form.
     693             :  * Their optimization takes place after inlining.
     694             :  */
     695             : static str
     696       10107 : MALoptimizer(Client c)
     697             : {
     698       10107 :         str msg;
     699             : 
     700       10107 :         if (c->curprg->def->inlineProp)
     701             :                 return MAL_SUCCEED;
     702             :         // only a signature statement can be skipped
     703       10107 :         if (c->curprg->def->stop == 1)
     704             :                 return MAL_SUCCEED;
     705        9917 :         msg = optimizeMALBlock(c, c->curprg->def);
     706             :         /*
     707             :            if( msg == MAL_SUCCEED)
     708             :            msg = OPTmultiplexSimple(c, c->curprg->def);
     709             :          */
     710        9917 :         return msg;
     711             : }
     712             : 
     713             : static str
     714       10720 : MALengine_(Client c)
     715             : {
     716       10720 :         Symbol prg;
     717       10720 :         str msg = MAL_SUCCEED;
     718             : 
     719       10968 :         do {
     720       10968 :                 if ((msg = MALreader(c)) != MAL_SUCCEED)
     721           0 :                         return msg;
     722       10967 :                 if (c->mode == FINISHCLIENT)
     723             :                         return msg;
     724       10469 :                 if ((msg = MALparser(c)) != MAL_SUCCEED)
     725         100 :                         return msg;
     726       10356 :         } while (c->blkmode);
     727             :         /*
     728             :            if (c->blkmode)
     729             :            return MAL_SUCCEED;
     730             :          */
     731       10108 :         if ((msg = MALoptimizer(c)) != MAL_SUCCEED)
     732             :                 return msg;
     733       10113 :         prg = c->curprg;
     734       10113 :         if (prg == NULL)
     735           0 :                 throw(SYNTAX, "mal.engine", SYNTAX_SIGNATURE);
     736       10113 :         if (prg->def == NULL)
     737           0 :                 throw(SYNTAX, "mal.engine", SYNTAX_SIGNATURE);
     738             : 
     739       10113 :         if (prg->def->errors != MAL_SUCCEED) {
     740           0 :                 msg = prg->def->errors;
     741           0 :                 prg->def->errors = NULL;
     742           0 :                 MSresetStack(c, c->curprg->def, c->glb);
     743           0 :                 resetMalTypes(c->curprg->def, 1);
     744           0 :                 return msg;
     745             :         }
     746       10113 :         if (prg->def->stop == 1 || MALcommentsOnly(prg->def))
     747         190 :                 return 0;                               /* empty block */
     748        9925 :         if (c->glb) {
     749        9925 :                 if (prg->def && c->glb->stksize < prg->def->vsize) {
     750           4 :                         c->glb = reallocGlobalStack(c->glb, prg->def->vsize);
     751           4 :                         if (c->glb == NULL)
     752           0 :                                 throw(MAL, "mal.engine", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     753             :                 }
     754        9925 :                 c->glb->stktop = prg->def->vtop;
     755        9925 :                 c->glb->blk = prg->def;
     756             :         }
     757             : 
     758             :         /*
     759             :          * In interactive mode we should avoid early garbage collection of values.
     760             :          * This can be controlled by the clean up control at the instruction level
     761             :          * and marking all non-temporary variables as being (potentially) used.
     762             :          */
     763        9925 :         if (c->glb) {
     764        9925 :                 c->glb->pcup = 0;
     765        9925 :                 c->glb->keepAlive = TRUE; /* no garbage collection */
     766             :         }
     767        9925 :         if (prg->def->errors == MAL_SUCCEED)
     768        9923 :                 msg = (str) runMAL(c, prg->def, 0, c->glb);
     769        9934 :         if (msg) {
     770             :                 /* ignore "internal" exceptions */
     771          32 :                 if (strstr(msg, "client.quit")) {
     772           0 :                         freeException(msg);
     773           0 :                         msg = MAL_SUCCEED;
     774             :                 }
     775             :         }
     776        9934 :         MSresetStack(c, prg->def, c->glb);
     777        9932 :         resetMalTypes(prg->def, 1);
     778        9932 :         if (c->glb) {
     779             :                 /* for global stacks avoid reinitialization from this point */
     780        9932 :                 c->glb->stkbot = prg->def->vtop;
     781             :         }
     782             : 
     783        9932 :         if (prg->def->errors)
     784           0 :                 freeException(prg->def->errors);
     785        9932 :         prg->def->errors = NULL;
     786        9932 :         return msg;
     787             : }
     788             : 
     789             : void
     790       10720 : MALengine(Client c)
     791             : {
     792       10720 :         str msg = MALengine_(c);
     793       10720 :         if (msg) {
     794             :                 /* don't print exception decoration, just the message */
     795         324 :                 char *n = NULL;
     796             :                 char *o = msg;
     797         324 :                 while ((n = strchr(o, '\n')) != NULL) {
     798         194 :                         if (*o == '!')
     799           0 :                                 o++;
     800         194 :                         mnstr_printf(c->fdout, "!%.*s\n", (int) (n - o), o);
     801         194 :                         o = ++n;
     802             :                 }
     803         130 :                 if (*o != 0) {
     804          33 :                         if (*o == '!')
     805           4 :                                 o++;
     806          33 :                         mnstr_printf(c->fdout, "!%s\n", o);
     807             :                 }
     808         130 :                 freeException(msg);
     809             :         }
     810       10720 : }
     811             : 
     812             : /* Hypothetical, optimizers may massage the plan in such a way
     813             :  * that multiple passes are needed.
     814             :  * However, the current SQL driven approach only expects a single
     815             :  * non-repeating pipeline of optimizer steps stored at the end of the MAL block.
     816             :  * A single scan forward over the MAL plan is assumed.
     817             :  */
     818             : str
     819      574434 : optimizeMALBlock(Client cntxt, MalBlkPtr mb)
     820             : {
     821      574434 :         InstrPtr p;
     822      574434 :         int pc, oldstop;
     823      574434 :         str msg = MAL_SUCCEED;
     824      574434 :         int cnt = 0;
     825      574434 :         int actions = 0;
     826      574434 :         lng clk = GDKusec();
     827             : 
     828             :         /* assume the type and flow have been checked already */
     829             :         /* SQL functions intended to be inlined should not be optimized */
     830      574355 :         if (mb->inlineProp)
     831             :                 return 0;
     832             : 
     833      574194 :         mb->optimize = 0;
     834      574194 :         if (mb->errors)
     835           0 :                 throw(MAL, "optimizer.MALoptimizer",
     836             :                           SQLSTATE(42000) "Start with inconsistent MAL plan");
     837             : 
     838             :         // strong defense line, assure that MAL plan is initially correct
     839      574194 :         if (mb->errors == 0 && mb->stop > 1) {
     840      574212 :                 resetMalTypes(mb, mb->stop);
     841      574288 :                 msg = chkTypes(cntxt->usermodule, mb, FALSE);
     842      573533 :                 if (!msg)
     843      573558 :                         msg = chkFlow(mb);
     844      574098 :                 if (!msg)
     845      574144 :                         msg = chkDeclarations(mb);
     846      574084 :                 if (msg)
     847           0 :                         return msg;
     848      574038 :                 if (mb->errors != MAL_SUCCEED) {
     849           0 :                         msg = mb->errors;
     850           0 :                         mb->errors = MAL_SUCCEED;
     851           0 :                         return msg;
     852             :                 }
     853             :         }
     854             : 
     855      574020 :         oldstop = mb->stop;
     856    42012759 :         for (pc = 0; pc < mb->stop; pc++) {
     857    41438495 :                 p = getInstrPtr(mb, pc);
     858    41438495 :                 if (getModuleId(p) == optimizerRef && p->fcn && p->token != REMsymbol) {
     859    13845876 :                         actions++;
     860    13845876 :                         msg = (*(str (*)(Client, MalBlkPtr, MalStkPtr, InstrPtr)) p->fcn) (cntxt, mb, 0, p);
     861    13846120 :                         if (mb->errors) {
     862           0 :                                 freeException(msg);
     863           0 :                                 msg = mb->errors;
     864           0 :                                 mb->errors = NULL;
     865             :                         }
     866    13846120 :                         if (msg) {
     867           0 :                                 str place = getExceptionPlace(msg);
     868           0 :                                 str nmsg = NULL;
     869           0 :                                 if (place) {
     870           0 :                                         nmsg = createException(getExceptionType(msg), place, "%s",
     871             :                                                                                    getExceptionMessageAndState(msg));
     872           0 :                                         GDKfree(place);
     873             :                                 }
     874           0 :                                 if (nmsg) {
     875           0 :                                         freeException(msg);
     876           0 :                                         msg = nmsg;
     877             :                                 }
     878           0 :                                 goto wrapup;
     879             :                         }
     880    13846120 :                         if (cntxt->mode == FINISHCLIENT) {
     881           0 :                                 mb->optimize = GDKusec() - clk;
     882           0 :                                 throw(MAL, "optimizeMALBlock",
     883             :                                           SQLSTATE(42000) "prematurely stopped client");
     884             :                         }
     885             :                         /* the MAL block may have changed */
     886    13846120 :                         pc += mb->stop - oldstop - 1;
     887    13846120 :                         oldstop = mb->stop;
     888             :                 }
     889             :         }
     890             : 
     891      574264 :   wrapup:
     892             :         /* Keep the total time spent on optimizing the plan for inspection */
     893      574264 :         if (actions > 0 && msg == MAL_SUCCEED) {
     894      564444 :                 mb->optimize = GDKusec() - clk;
     895      564472 :                 p = newStmt(mb, optimizerRef, totalRef);
     896      564483 :                 if (p == NULL) {
     897           0 :                         throw(MAL, "optimizer.MALoptimizer",
     898             :                                   SQLSTATE(HY013) MAL_MALLOC_FAIL);
     899             :                 }
     900      564483 :                 p->token = REMsymbol;
     901      564483 :                 p = pushInt(mb, p, actions);
     902      564455 :                 p = pushLng(mb, p, mb->optimize);
     903      564407 :                 pushInstruction(mb, p);
     904             :         }
     905      574162 :         if (cnt >= mb->stop)
     906           0 :                 throw(MAL, "optimizer.MALoptimizer", SQLSTATE(42000) OPTIMIZER_CYCLE);
     907             :         return msg;
     908             : }

Generated by: LCOV version 1.14