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-04-26 00:35:57 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         340 : malBootstrap(char *modules[], bool embedded, const char *initpasswd)
      37             : {
      38         340 :         Client c;
      39         340 :         str msg = MAL_SUCCEED;
      40             : 
      41         340 :         c = MCinitClient(MAL_ADMIN, NULL, NULL);
      42         340 :         if (c == NULL) {
      43           0 :                 throw(MAL, "malBootstrap", "Failed to initialize client");
      44             :         }
      45         340 :         MT_thread_set_qry_ctx(NULL);
      46         340 :         assert(c != NULL);
      47         340 :         c->curmodule = c->usermodule = userModule();
      48         340 :         if (c->usermodule == NULL) {
      49           0 :                 MCcloseClient(c);
      50           0 :                 throw(MAL, "malBootstrap", "Failed to initialize client MAL module");
      51             :         }
      52         340 :         if ((msg = defaultScenario(c))) {
      53           0 :                 MCcloseClient(c);
      54           0 :                 return msg;
      55             :         }
      56         340 :         if ((msg = MSinitClientPrg(c, "user", "main")) != MAL_SUCCEED) {
      57           0 :                 MCcloseClient(c);
      58           0 :                 return msg;
      59             :         }
      60             : 
      61         340 :         if (MCinitClientThread(c) < 0) {
      62           0 :                 MCcloseClient(c);
      63           0 :                 throw(MAL, "malBootstrap", "Failed to create client thread");
      64             :         }
      65         340 :         if ((msg = malIncludeModules(c, modules, 0, embedded, initpasswd)) != MAL_SUCCEED) {
      66           0 :                 MCcloseClient(c);
      67           0 :                 return msg;
      68             :         }
      69         339 :         MCcloseClient(c);
      70         339 :         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 considerd 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      294773 : MSresetClientPrg(Client cntxt, const char *mod, const char *fcn)
      89             : {
      90      294773 :         MalBlkPtr mb;
      91      294773 :         InstrPtr p;
      92             : 
      93      294773 :         mb = cntxt->curprg->def;
      94      294773 :         mb->stop = 1;
      95      294773 :         mb->errors = MAL_SUCCEED;
      96      294773 :         p = mb->stmt[0];
      97             : 
      98      294773 :         p->gc = 0;
      99      294773 :         p->retc = 1;
     100      294773 :         p->argc = 1;
     101      294773 :         p->argv[0] = 0;
     102             : 
     103      294773 :         setModuleId(p, mod);
     104      294773 :         setFunctionId(p, fcn);
     105      294773 :         if (findVariable(mb, fcn) < 0)
     106      294763 :                 if ((p->argv[0] = newVariable(mb, fcn, strlen(fcn), TYPE_void)) < 0)
     107           0 :                         throw(MAL, "resetClientPrg", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     108             : 
     109      294773 :         setVarType(mb, findVariable(mb, fcn), TYPE_void);
     110      294773 :         return MAL_SUCCEED;
     111             : }
     112             : 
     113             : /*
     114             :  * Create a new container block
     115             :  */
     116             : 
     117             : str
     118      343999 : MSinitClientPrg(Client cntxt, const char *mod, const char *nme)
     119             : {
     120      343999 :         int idx;
     121             : 
     122      343999 :         if (cntxt->curprg && idcmp(nme, cntxt->curprg->name) == 0)
     123      294773 :                 return MSresetClientPrg(cntxt, putName(mod), putName(nme));
     124       49226 :         cntxt->curprg = newFunction(putName(mod), putName(nme), FUNCTIONsymbol);
     125       49222 :         if (cntxt->curprg == 0)
     126           0 :                 throw(MAL, "initClientPrg", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     127       49222 :         if ((idx = findVariable(cntxt->curprg->def, "main")) >= 0)
     128       38307 :                 setVarType(cntxt->curprg->def, idx, TYPE_void);
     129       49220 :         insertSymbol(cntxt->usermodule, cntxt->curprg);
     130             : 
     131       49218 :         if (cntxt->glb == NULL)
     132       38326 :                 cntxt->glb = newGlobalStack(MAXGLOBALS + cntxt->curprg->def->vsize);
     133       49226 :         if (cntxt->glb == NULL)
     134           0 :                 throw(MAL, "initClientPrg", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     135       49226 :         assert(cntxt->curprg->def != NULL);
     136       49226 :         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         176 : exit_streams(bstream *fin, stream *fout)
     150             : {
     151         176 :         if (fout && fout != GDKstdout) {
     152         176 :                 mnstr_flush(fout, MNSTR_FLUSH_DATA);
     153         176 :                 close_stream(fout);
     154             :         }
     155         176 :         if (fin)
     156         176 :                 bstream_destroy(fin);
     157         176 : }
     158             : 
     159             : const char *mal_enableflag = "mal_for_all";
     160             : 
     161             : static bool
     162        1645 : is_exiting(void *data)
     163             : {
     164        1645 :         (void) data;
     165        1645 :         return GDKexiting();
     166             : }
     167             : 
     168             : static str MSserveClient(Client cntxt);
     169             : 
     170             : 
     171             : static inline void
     172           9 : cleanUpScheduleClient(Client c, str *command, str *err)
     173             : {
     174           9 :         if (c) {
     175           9 :                 MCcloseClient(c);
     176             :         }
     177           9 :         if (command) {
     178           3 :                 GDKfree(*command);
     179           3 :                 *command = NULL;
     180             :         }
     181           9 :         if (err) {
     182           9 :                 freeException(*err);
     183           9 :                 *err = NULL;
     184             :         }
     185           9 : }
     186             : 
     187             : 
     188             : void
     189       38141 : MSscheduleClient(str command, str challenge, bstream *fin, stream *fout,
     190             :                                  protocol_version protocol, size_t blocksize)
     191             : {
     192       38141 :         char *user = command, *algo = NULL, *passwd = NULL, *lang = NULL,
     193       38141 :                 *handshake_opts = NULL;
     194       38141 :         char *database = NULL, *s;
     195       38141 :         const char *dbname;
     196       38141 :         str msg = MAL_SUCCEED;
     197       38141 :         bool filetrans = false;
     198       38141 :         Client c;
     199             : 
     200       38141 :         MT_thread_set_qry_ctx(NULL);
     201             : 
     202             :         /* decode BIG/LIT:user:{cypher}passwordchal:lang:database: line */
     203             : 
     204             :         /* byte order */
     205       38137 :         s = strchr(user, ':');
     206       38137 :         if (s) {
     207       37961 :                 *s = 0;
     208       37961 :                 mnstr_set_bigendian(fin->s, strcmp(user, "BIG") == 0);
     209       37947 :                 user = s + 1;
     210             :         } else {
     211         176 :                 mnstr_printf(fout, "!incomplete challenge '%s'\n", user);
     212         176 :                 exit_streams(fin, fout);
     213         176 :                 GDKfree(command);
     214         361 :                 return;
     215             :         }
     216             : 
     217             :         /* passwd */
     218       37947 :         s = strchr(user, ':');
     219       37947 :         if (s) {
     220       37947 :                 *s = 0;
     221       37947 :                 passwd = s + 1;
     222             :                 /* decode algorithm, i.e. {plain}mypasswordchallenge */
     223       37947 :                 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       37947 :                 algo = passwd + 1;
     230       37947 :                 s = strchr(algo, '}');
     231       37947 :                 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       37947 :                 *s = 0;
     238       37947 :                 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       37947 :         s = strchr(passwd, ':');
     248       37947 :         if (s) {
     249       37947 :                 *s = 0;
     250       37947 :                 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       37947 :         s = strchr(lang, ':');
     260       37947 :         if (s) {
     261       37947 :                 *s = 0;
     262       37947 :                 database = s + 1;
     263             :                 /* we can have stuff following, make it void */
     264       37947 :                 s = strchr(database, ':');
     265       37947 :                 if (s)
     266       37947 :                         *s++ = 0;
     267             :         }
     268             : 
     269       37947 :         if (s && strncmp(s, "FILETRANS:", 10) == 0) {
     270       37639 :                 s += 10;
     271       37639 :                 filetrans = true;
     272         308 :         } else if (s && s[0] == ':') {
     273           0 :                 s += 1;
     274           0 :                 filetrans = false;
     275             :         }
     276             : 
     277       37947 :         if (s && strchr(s, ':') != NULL) {
     278       37639 :                 handshake_opts = s;
     279       37639 :                 s = strchr(s, ':');
     280       37639 :                 *s++ = '\0';
     281             :         }
     282       37947 :         dbname = GDKgetenv("gdk_dbname");
     283       37975 :         if (database != NULL && database[0] != '\0' &&
     284       36962 :                 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       37975 :                 c = MCinitClient(0, fin, fout);
     295       37974 :                 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       37974 :                 c->filetrans = filetrans;
     307       37974 :                 c->handshake_options = handshake_opts ? strdup(handshake_opts) : NULL;
     308             :                 /* move this back !! */
     309       37974 :                 if (c->usermodule == 0) {
     310       37975 :                         c->curmodule = c->usermodule = userModule();
     311       37975 :                         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       37974 :                 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       37974 :                 if (!GDKgetenv_isyes(mal_enableflag)
     325       37975 :                         && strncasecmp("sql", lang, 3) != 0
     326         498 :                         && 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       37972 :         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       37971 :         c->username = GDKstrdup(user);
     344             : 
     345             :         /* NOTE ABOUT STARTING NEW THREADS
     346             :          * At this point we have conducted experiments (Jun 2012) with
     347             :          * reusing threads.  The implementation used was a lockless array of
     348             :          * semaphores to wake up threads to do work.  Experimentation on
     349             :          * Linux, Solaris and Darwin showed no significant improvements, in
     350             :          * most cases no improvements at all.  Hence the following
     351             :          * conclusion: thread reuse doesn't save up on the costs of just
     352             :          * forking new threads.  Since the latter means no difficulties of
     353             :          * properly maintaining a pool of threads and picking the workers
     354             :          * out of them, it is favourable just to start new threads on
     355             :          * demand. */
     356             : 
     357             :         /* fork a new thread to handle this client */
     358             : 
     359       37965 :         c->protocol = protocol;
     360       37965 :         c->blocksize = blocksize;
     361             : 
     362       37965 :         if (c->initClient) {
     363       37965 :                 if ((msg = c->initClient(c, passwd, challenge, algo)) != MAL_SUCCEED) {
     364           6 :                         mnstr_printf(fout, "!%s\n", msg);
     365           6 :                         GDKfree(command);
     366           6 :                         if (c->exitClient)
     367           6 :                                 c->exitClient(c);
     368           6 :                         cleanUpScheduleClient(c, NULL, &msg);
     369           6 :                         return;
     370             :                 }
     371             :         }
     372       37965 :         GDKfree(command);
     373             : 
     374       37965 :         mnstr_settimeout(c->fdin->s, 50, is_exiting, NULL);
     375       37964 :         msg = MSserveClient(c);
     376       37966 :         if (msg != MAL_SUCCEED) {
     377           0 :                 mnstr_printf(fout, "!could not serve client\n");
     378           0 :                 exit_streams(fin, fout);
     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      305120 : MSresetInstructions(MalBlkPtr mb, int start)
     406             : {
     407      305120 :         int i;
     408      305120 :         InstrPtr p;
     409             : 
     410    74525577 :         for (i = start; i < mb->ssize; i++) {
     411    74220171 :                 p = getInstrPtr(mb, i);
     412    74220171 :                 if (p)
     413       37384 :                         freeInstruction(p);
     414    74220457 :                 mb->stmt[i] = NULL;
     415             :         }
     416      305406 :         mb->stop = start;
     417      305406 : }
     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        9916 : MSresetStack(Client cntxt, MalBlkPtr mb, MalStkPtr glb)
     427             : {
     428        9916 :         InstrPtr sig = getInstrPtr(mb, 0);
     429        9916 :         int i, k = sig->argc;
     430             : 
     431        9916 :         if (mb->errors == MAL_SUCCEED) {
     432     1250784 :                 for (i = sig->argc; i < mb->vtop; i++) {
     433     1240868 :                         if (glb && i < glb->stktop && isTmpVar(mb, i) && !glb->keepTmps) {
     434        7534 :                                 if (mb->var[i].name)
     435           2 :                                         GDKfree(mb->var[i].name);
     436             :                                 /* clean stack entry */
     437        7534 :                                 garbageElement(cntxt, &glb->stk[i]);
     438        7534 :                                 glb->stk[i].vtype = TYPE_int;
     439        7534 :                                 glb->stk[i].len = 0;
     440        7534 :                                 glb->stk[i].val.pval = 0;
     441        7534 :                                 if (isVarConstant(mb, i))
     442        4854 :                                         garbageElement(cntxt, &mb->var[i].value);
     443             :                         } else {
     444             :                                 /* compress the global variable list and stack */
     445     1233334 :                                 mb->var[k] = mb->var[i];
     446     1233334 :                                 glb->stk[k] = glb->stk[i];
     447     1233334 :                                 setVarUsed(mb, k);
     448     1233334 :                                 setVarInit(mb, k);
     449     1233334 :                                 if (i != k) {
     450          70 :                                         glb->stk[i].vtype = TYPE_int;
     451          70 :                                         glb->stk[i].len = 0;
     452          70 :                                         glb->stk[i].val.pval = 0;
     453          70 :                                         clrVarConstant(mb, i);
     454          70 :                                         clrVarCleanup(mb, i);
     455             :                                 }
     456     1233334 :                                 k++;
     457             :                         }
     458             :                 }
     459             :         }
     460        9916 :         assert(k <= mb->vsize);
     461        9916 :         mb->vtop = k;
     462        9916 : }
     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       37964 : MSserveClient(Client c)
     489             : {
     490       37964 :         MalBlkPtr mb;
     491       37964 :         str msg = 0;
     492             : 
     493       37964 :         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       37966 :         mb = c->curprg->def;
     502       37966 :         if (c->glb == NULL)
     503           0 :                 c->glb = newGlobalStack(MAXGLOBALS + mb->vsize);
     504       37966 :         if (c->glb == NULL) {
     505           0 :                 MCcloseClient(c);
     506           0 :                 throw(MAL, "serveClient", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     507             :         } else {
     508       37966 :                 c->glb->stktop = mb->vtop;
     509       37966 :                 c->glb->blk = mb;
     510             :         }
     511             : 
     512       37966 :         if (c->scenario == 0)
     513           0 :                 msg = defaultScenario(c);
     514       37966 :         if (msg) {
     515           0 :                 MCcloseClient(c);
     516           0 :                 return msg;
     517             :         } else {
     518       37966 :                 do {
     519       37966 :                         do {
     520       37966 :                                 MT_thread_setworking("running scenario");
     521       37966 :                                 msg = runScenario(c);
     522       37965 :                                 freeException(msg);
     523       37965 :                                 if (c->mode == FINISHCLIENT)
     524             :                                         break;
     525           0 :                                 resetScenario(c);
     526           0 :                         } while (c->scenario && !GDKexiting());
     527       37965 :                 } while (c->scenario && c->mode != FINISHCLIENT && !GDKexiting());
     528             :         }
     529       37965 :         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       37965 :         MT_exiting_thread();
     534             :         /*
     535             :          * At this stage we should clean out the MAL block
     536             :          */
     537       37966 :         if (c->backup) {
     538           0 :                 assert(0);
     539             :                 freeSymbol(c->backup);
     540             :                 c->backup = 0;
     541             :         }
     542             : 
     543       37966 :         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       37966 :         MCcloseClient(c);
     553       37966 :         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         314 : MALinitClient(Client c)
     569             : {
     570         314 :         (void) c;
     571         314 :         return MAL_SUCCEED;
     572             : }
     573             : 
     574             : str
     575       37995 : MALexitClient(Client c)
     576             : {
     577       37995 :         if (c->glb && c->curprg->def && c->curprg->def->errors == MAL_SUCCEED)
     578       37994 :                 garbageCollector(c, c->curprg->def, c->glb, TRUE);
     579       37995 :         c->mode = FINISHCLIENT;
     580       37995 :         if (c->backup) {
     581           0 :                 assert(0);
     582             :                 freeSymbol(c->backup);
     583             :                 c->backup = NULL;
     584             :         }
     585             :         /* should be in the usermodule */
     586       37995 :         c->curprg = NULL;
     587       37995 :         if (c->usermodule) {
     588       37995 :                 freeModule(c->usermodule);
     589       37995 :                 c->usermodule = NULL;
     590             :         }
     591       37995 :         return NULL;
     592             : }
     593             : 
     594             : static str
     595       10908 : MALreader(Client c)
     596             : {
     597       10908 :         if (MCreadClient(c) > 0)
     598             :                 return MAL_SUCCEED;
     599         494 :         MT_lock_set(&mal_contextLock);
     600         494 :         c->mode = FINISHCLIENT;
     601         494 :         MT_lock_unset(&mal_contextLock);
     602         494 :         if (c->fdin)
     603         494 :                 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       10414 : prepareMalBlk(MalBlkPtr mb, str s)
     616             : {
     617       10414 :         int cnt = STMT_INCREMENT;
     618             : 
     619       24340 :         while (s) {
     620       24340 :                 s = strchr(s, '\n');
     621       24340 :                 if (s) {
     622       13926 :                         s++;
     623       13926 :                         cnt++;
     624             :                 }
     625             :         }
     626       10414 :         cnt = (int) (cnt * 1.1);
     627       10414 :         return resizeMalBlk(mb, cnt);
     628             : }
     629             : 
     630             : str
     631       10414 : MALparser(Client c)
     632             : {
     633       10414 :         InstrPtr p;
     634       10414 :         str msg = MAL_SUCCEED;
     635             : 
     636       10414 :         assert(c->curprg->def->errors == NULL);
     637       10414 :         c->curprg->def->errors = 0;
     638             : 
     639       10414 :         if (prepareMalBlk(c->curprg->def, CURRENT(c)) < 0)
     640           0 :                 throw(MAL, "mal.parser", "Failed to prepare");
     641       10411 :         parseMAL(c, c->curprg, 0, INT_MAX, 0);
     642             : 
     643             :         /* now the parsing is done we should advance the stream */
     644       10409 :         c->fdin->pos += c->yycur;
     645       10409 :         c->yycur = 0;
     646       10409 :         c->qryctx.starttime = GDKusec();
     647       10410 :         c->qryctx.endtime = c->querytimeout ? c->qryctx.starttime + c->querytimeout : 0;
     648             : 
     649             :         /* check for unfinished blocks */
     650       10410 :         if (!c->curprg->def->errors && c->blkmode)
     651             :                 return MAL_SUCCEED;
     652             :         /* empty files should be skipped as well */
     653       10155 :         if (c->curprg->def->stop == 1) {
     654         243 :                 if ((msg = c->curprg->def->errors))
     655          53 :                         c->curprg->def->errors = 0;
     656         243 :                 return msg;
     657             :         }
     658             : 
     659        9912 :         p = getInstrPtr(c->curprg->def, 0);
     660        9912 :         if (p->token != FUNCTIONsymbol) {
     661           0 :                 msg = c->curprg->def->errors;
     662           0 :                 c->curprg->def->errors = 0;
     663           0 :                 MSresetStack(c, c->curprg->def, c->glb);
     664           0 :                 resetMalTypes(c->curprg->def, 1);
     665           0 :                 return msg;
     666             :         }
     667        9912 :         pushEndInstruction(c->curprg->def);
     668        9915 :         msg = chkProgram(c->usermodule, c->curprg->def);
     669        9909 :         if (msg != MAL_SUCCEED || (msg = c->curprg->def->errors)) {
     670          46 :                 c->curprg->def->errors = 0;
     671          46 :                 MSresetStack(c, c->curprg->def, c->glb);
     672          47 :                 resetMalTypes(c->curprg->def, 1);
     673          47 :                 return msg;
     674             :         }
     675             :         return MAL_SUCCEED;
     676             : }
     677             : 
     678             : int
     679      256704 : MALcommentsOnly(MalBlkPtr mb)
     680             : {
     681      256704 :         int i;
     682             : 
     683      256792 :         for (i = 1; i < mb->stop; i++)
     684      257385 :                 if (mb->stmt[i]->token != REMsymbol)
     685             :                         return 0;
     686             :         return 1;
     687             : }
     688             : 
     689             : /*
     690             :  * The default MAL optimizer includes a final call to
     691             :  * the multiplex expander.
     692             :  * We should take care of functions marked as 'inline',
     693             :  * because they should be kept in raw form.
     694             :  * Their optimization takes place after inlining.
     695             :  */
     696             : static str
     697       10050 : MALoptimizer(Client c)
     698             : {
     699       10050 :         str msg;
     700             : 
     701       10050 :         if (c->curprg->def->inlineProp)
     702             :                 return MAL_SUCCEED;
     703             :         // only a signature statement can be skipped
     704       10050 :         if (c->curprg->def->stop == 1)
     705             :                 return MAL_SUCCEED;
     706        9860 :         msg = optimizeMALBlock(c, c->curprg->def);
     707             :         /*
     708             :            if( msg == MAL_SUCCEED)
     709             :            msg = OPTmultiplexSimple(c, c->curprg->def);
     710             :          */
     711        9860 :         return msg;
     712             : }
     713             : 
     714             : static str
     715       10653 : MALengine_(Client c)
     716             : {
     717       10653 :         Symbol prg;
     718       10653 :         str msg = MAL_SUCCEED;
     719             : 
     720       10905 :         do {
     721       10905 :                 if ((msg = MALreader(c)) != MAL_SUCCEED)
     722           0 :                         return msg;
     723       10908 :                 if (c->mode == FINISHCLIENT)
     724             :                         return msg;
     725       10414 :                 if ((msg = MALparser(c)) != MAL_SUCCEED)
     726         100 :                         return msg;
     727       10303 :         } while (c->blkmode);
     728             :         /*
     729             :            if (c->blkmode)
     730             :            return MAL_SUCCEED;
     731             :          */
     732       10051 :         if ((msg = MALoptimizer(c)) != MAL_SUCCEED)
     733             :                 return msg;
     734       10053 :         prg = c->curprg;
     735       10053 :         if (prg == NULL)
     736           0 :                 throw(SYNTAX, "mal.engine", SYNTAX_SIGNATURE);
     737       10053 :         if (prg->def == NULL)
     738           0 :                 throw(SYNTAX, "mal.engine", SYNTAX_SIGNATURE);
     739             : 
     740       10053 :         if (prg->def->errors != MAL_SUCCEED) {
     741           0 :                 msg = prg->def->errors;
     742           0 :                 prg->def->errors = NULL;
     743           0 :                 MSresetStack(c, c->curprg->def, c->glb);
     744           0 :                 resetMalTypes(c->curprg->def, 1);
     745           0 :                 return msg;
     746             :         }
     747       10053 :         if (prg->def->stop == 1 || MALcommentsOnly(prg->def))
     748         190 :                 return 0;                               /* empty block */
     749        9867 :         if (c->glb) {
     750        9867 :                 if (prg->def && c->glb->stksize < prg->def->vsize) {
     751           4 :                         c->glb = reallocGlobalStack(c->glb, prg->def->vsize);
     752           4 :                         if (c->glb == NULL)
     753           0 :                                 throw(MAL, "mal.engine", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     754             :                 }
     755        9867 :                 c->glb->stktop = prg->def->vtop;
     756        9867 :                 c->glb->blk = prg->def;
     757             :         }
     758             : 
     759             :         /*
     760             :          * In interactive mode we should avoid early garbage collection of values.
     761             :          * This can be controlled by the clean up control at the instruction level
     762             :          * and marking all non-temporary variables as being (potentially) used.
     763             :          */
     764        9867 :         if (c->glb) {
     765        9867 :                 c->glb->pcup = 0;
     766        9867 :                 c->glb->keepAlive = TRUE; /* no garbage collection */
     767             :         }
     768        9867 :         if (prg->def->errors == MAL_SUCCEED)
     769        9864 :                 msg = (str) runMAL(c, prg->def, 0, c->glb);
     770        9872 :         if (msg) {
     771             :                 /* ignore "internal" exceptions */
     772          32 :                 if (strstr(msg, "client.quit")) {
     773           0 :                         freeException(msg);
     774           0 :                         msg = MAL_SUCCEED;
     775             :                 }
     776             :         }
     777        9872 :         MSresetStack(c, prg->def, c->glb);
     778        9869 :         resetMalTypes(prg->def, 1);
     779        9869 :         if (c->glb) {
     780             :                 /* for global stacks avoid reinitialization from this point */
     781        9869 :                 c->glb->stkbot = prg->def->vtop;
     782             :         }
     783             : 
     784        9869 :         if (prg->def->errors)
     785           0 :                 freeException(prg->def->errors);
     786        9869 :         prg->def->errors = NULL;
     787        9869 :         return msg;
     788             : }
     789             : 
     790             : void
     791       10653 : MALengine(Client c)
     792             : {
     793       10653 :         str msg = MALengine_(c);
     794       10653 :         if (msg) {
     795             :                 /* don't print exception decoration, just the message */
     796         322 :                 char *n = NULL;
     797             :                 char *o = msg;
     798         322 :                 while ((n = strchr(o, '\n')) != NULL) {
     799         193 :                         if (*o == '!')
     800           0 :                                 o++;
     801         193 :                         mnstr_printf(c->fdout, "!%.*s\n", (int) (n - o), o);
     802         193 :                         o = ++n;
     803             :                 }
     804         129 :                 if (*o != 0) {
     805          33 :                         if (*o == '!')
     806           4 :                                 o++;
     807          33 :                         mnstr_printf(c->fdout, "!%s\n", o);
     808             :                 }
     809         129 :                 freeException(msg);
     810             :         }
     811       10653 : }
     812             : 
     813             : /* Hypothetical, optimizers may massage the plan in such a way
     814             :  * that multiple passes are needed.
     815             :  * However, the current SQL driven approach only expects a single
     816             :  * non-repeating pipeline of optimizer steps stored at the end of the MAL block.
     817             :  * A single scan forward over the MAL plan is assumed.
     818             :  */
     819             : str
     820      558448 : optimizeMALBlock(Client cntxt, MalBlkPtr mb)
     821             : {
     822      558448 :         InstrPtr p;
     823      558448 :         int pc, oldstop;
     824      558448 :         str msg = MAL_SUCCEED;
     825      558448 :         int cnt = 0;
     826      558448 :         int actions = 0;
     827      558448 :         lng clk = GDKusec();
     828             : 
     829             :         /* assume the type and flow have been checked already */
     830             :         /* SQL functions intended to be inlined should not be optimized */
     831      558500 :         if (mb->inlineProp)
     832             :                 return 0;
     833             : 
     834      558335 :         mb->optimize = 0;
     835      558335 :         if (mb->errors)
     836           0 :                 throw(MAL, "optimizer.MALoptimizer",
     837             :                           SQLSTATE(42000) "Start with inconsistent MAL plan");
     838             : 
     839             :         // strong defense line, assure that MAL plan is initially correct
     840      558335 :         if (mb->errors == 0 && mb->stop > 1) {
     841      558217 :                 resetMalTypes(mb, mb->stop);
     842      558309 :                 msg = chkTypes(cntxt->usermodule, mb, FALSE);
     843      557174 :                 if (!msg)
     844      557378 :                         msg = chkFlow(mb);
     845      558122 :                 if (!msg)
     846      558180 :                         msg = chkDeclarations(mb);
     847      557892 :                 if (msg)
     848           0 :                         return msg;
     849      557834 :                 if (mb->errors != MAL_SUCCEED) {
     850           0 :                         msg = mb->errors;
     851           0 :                         mb->errors = MAL_SUCCEED;
     852           0 :                         return msg;
     853             :                 }
     854             :         }
     855             : 
     856      557952 :         oldstop = mb->stop;
     857    40224553 :         for (pc = 0; pc < mb->stop; pc++) {
     858    39666267 :                 p = getInstrPtr(mb, pc);
     859    39666267 :                 if (getModuleId(p) == optimizerRef && p->fcn && p->token != REMsymbol) {
     860    13368298 :                         actions++;
     861    13368298 :                         msg = (*(str (*)(Client, MalBlkPtr, MalStkPtr, InstrPtr)) p->fcn) (cntxt, mb, 0, p);
     862    13368632 :                         if (mb->errors) {
     863           0 :                                 freeException(msg);
     864           0 :                                 msg = mb->errors;
     865           0 :                                 mb->errors = NULL;
     866             :                         }
     867    13368632 :                         if (msg) {
     868           0 :                                 str place = getExceptionPlace(msg);
     869           0 :                                 str nmsg = NULL;
     870           0 :                                 if (place) {
     871           0 :                                         nmsg = createException(getExceptionType(msg), place, "%s",
     872             :                                                                                    getExceptionMessageAndState(msg));
     873           0 :                                         GDKfree(place);
     874             :                                 }
     875           0 :                                 if (nmsg) {
     876           0 :                                         freeException(msg);
     877           0 :                                         msg = nmsg;
     878             :                                 }
     879           0 :                                 goto wrapup;
     880             :                         }
     881    13368632 :                         if (cntxt->mode == FINISHCLIENT) {
     882           0 :                                 mb->optimize = GDKusec() - clk;
     883           0 :                                 throw(MAL, "optimizeMALBlock",
     884             :                                           SQLSTATE(42000) "prematurely stopped client");
     885             :                         }
     886             :                         /* the MAL block may have changed */
     887    13368632 :                         pc += mb->stop - oldstop - 1;
     888    13368632 :                         oldstop = mb->stop;
     889             :                 }
     890             :         }
     891             : 
     892      558286 :   wrapup:
     893             :         /* Keep the total time spent on optimizing the plan for inspection */
     894      558286 :         if (actions > 0 && msg == MAL_SUCCEED) {
     895      548536 :                 mb->optimize = GDKusec() - clk;
     896      548555 :                 p = newStmt(mb, optimizerRef, totalRef);
     897      548587 :                 if (p == NULL) {
     898           0 :                         throw(MAL, "optimizer.MALoptimizer",
     899             :                                   SQLSTATE(HY013) MAL_MALLOC_FAIL);
     900             :                 }
     901      548587 :                 p->token = REMsymbol;
     902      548587 :                 p = pushInt(mb, p, actions);
     903      548566 :                 p = pushLng(mb, p, mb->optimize);
     904      548536 :                 pushInstruction(mb, p);
     905             :         }
     906      558242 :         if (cnt >= mb->stop)
     907           0 :                 throw(MAL, "optimizer.MALoptimizer", SQLSTATE(42000) OPTIMIZER_CYCLE);
     908             :         return msg;
     909             : }

Generated by: LCOV version 1.14