LCOV - code coverage report
Current view: top level - monetdb5/mal - mal_session.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 358 476 75.2 %
Date: 2024-04-25 20:03:45 Functions: 20 21 95.2 %

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

Generated by: LCOV version 1.14