LCOV - code coverage report
Current view: top level - monetdb5/mal - mal_session.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 265 383 69.2 %
Date: 2021-09-14 22:17:06 Functions: 17 18 94.4 %

          Line data    Source code
       1             : /*
       2             :  * This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       5             :  *
       6             :  * Copyright 1997 - July 2008 CWI, August 2008 - 2021 MonetDB B.V.
       7             :  */
       8             : 
       9             : /* (author) M.L. Kersten
      10             :  */
      11             : #include "monetdb_config.h"
      12             : #include "mal_session.h"
      13             : #include "mal_instruction.h" /* for pushEndInstruction() */
      14             : #include "mal_interpreter.h" /* for runMAL(), garbageElement() */
      15             : #include "mal_parser.h"            /* for parseMAL() */
      16             : #include "mal_namespace.h"
      17             : #include "mal_authorize.h"
      18             : #include "mal_builder.h"
      19             : #include "msabaoth.h"
      20             : #include "mal_private.h"
      21             : #include "gdk.h"      /* for opendir and friends */
      22             : 
      23             : /*
      24             :  * The MonetDB server uses a startup script to boot the system.
      25             :  * This script is an ordinary MAL program, but will mostly
      26             :  * consist of include statements to load modules of general interest.
      27             :  * The startup script is run as user Admin.
      28             :  */
      29             : str
      30         265 : malBootstrap(char *modules[], int embedded)
      31             : {
      32             :         Client c;
      33             :         str msg = MAL_SUCCEED;
      34             : 
      35         265 :         c = MCinitClient(MAL_ADMIN, NULL, NULL);
      36         265 :         if(c == NULL) {
      37           0 :                 throw(MAL, "malBootstrap", "Failed to initialize client");
      38             :         }
      39             :         assert(c != NULL);
      40         265 :         c->curmodule = c->usermodule = userModule();
      41         265 :         if(c->usermodule == NULL) {
      42           0 :                 MCfreeClient(c);
      43           0 :                 throw(MAL, "malBootstrap", "Failed to initialize client MAL module");
      44             :         }
      45         265 :         if ( (msg = defaultScenario(c)) ) {
      46           0 :                 MCfreeClient(c);
      47           0 :                 return msg;
      48             :         }
      49         265 :         if((msg = MSinitClientPrg(c, "user", "main")) != MAL_SUCCEED) {
      50           0 :                 MCfreeClient(c);
      51           0 :                 return msg;
      52             :         }
      53         265 :         if( MCinitClientThread(c) < 0){
      54           0 :                 MCfreeClient(c);
      55           0 :                 throw(MAL, "malBootstrap", "Failed to create client thread");
      56             :         }
      57         265 :         if ((msg = malIncludeModules(c, modules, 0, embedded)) != MAL_SUCCEED) {
      58           0 :                 MCfreeClient(c);
      59           0 :                 return msg;
      60             :         }
      61         264 :         pushEndInstruction(c->curprg->def);
      62         264 :         msg = chkProgram(c->usermodule, c->curprg->def);
      63         264 :         if ( msg != MAL_SUCCEED || (msg= c->curprg->def->errors) != MAL_SUCCEED ) {
      64           0 :                 MCfreeClient(c);
      65           0 :                 return msg;
      66             :         }
      67         264 :         msg = MALengine(c);
      68         264 :         MCfreeClient(c);
      69         264 :         return msg;
      70             : }
      71             : 
      72             : /*
      73             :  * Every client has a 'main' function to collect the statements.  Once
      74             :  * the END instruction has been found, it is added to the symbol table
      75             :  * and a fresh container is being constructed.  Note, this scheme makes
      76             :  * testing for recursive function calls a little more difficult.
      77             :  * Therefore, type checking should be performed afterwards.
      78             :  *
      79             :  * In interactive mode,  the closing statement is never reached.  The
      80             :  * 'main' procedure is typically cleaned between successive external
      81             :  * messages except for its variables, which are considerd global.  This
      82             :  * storage container is re-used when during the previous call nothing
      83             :  * was added.  At the end of the session we have to garbage collect the
      84             :  * BATs introduced.
      85             :  */
      86             : static str
      87      235014 : MSresetClientPrg(Client cntxt, const char *mod, const char *fcn)
      88             : {
      89             :         MalBlkPtr mb;
      90             :         InstrPtr p;
      91             : 
      92      235014 :         cntxt->itrace = 0;  /* turn off any debugging */
      93      235014 :         mb = cntxt->curprg->def;
      94      235014 :         mb->stop = 1;
      95      235014 :         mb->errors = MAL_SUCCEED;
      96      235014 :         p = mb->stmt[0];
      97             : 
      98      235014 :         p->gc = 0;
      99      235014 :         p->retc = 1;
     100      235014 :         p->argc = 1;
     101      235014 :         p->argv[0] = 0;
     102             : 
     103      235014 :         setModuleId(p, mod);
     104      235014 :         setFunctionId(p, fcn);
     105      235014 :         if( findVariable(mb,fcn) < 0)
     106           0 :                 p->argv[0] = newVariable(mb, fcn, strlen(fcn), TYPE_void);
     107             : 
     108      235014 :         setVarType(mb, findVariable(mb, fcn), TYPE_void);
     109             :         /* remove any MAL history */
     110      235014 :         if (mb->history) {
     111           0 :                 freeMalBlk(mb->history);
     112           0 :                 mb->history = 0;
     113             :         }
     114      235014 :         return MAL_SUCCEED;
     115             : }
     116             : 
     117             : /*
     118             :  * Create a new container block
     119             :  */
     120             : 
     121             : str
     122      248958 : MSinitClientPrg(Client cntxt, const char *mod, const char *nme)
     123             : {
     124             :         int idx;
     125             : 
     126      248958 :         if (cntxt->curprg  && idcmp(nme, cntxt->curprg->name) == 0)
     127      235014 :                 return MSresetClientPrg(cntxt, putName(mod), putName(nme));
     128       13944 :         cntxt->curprg = newFunction(putName(mod), putName(nme), FUNCTIONsymbol);
     129       13944 :         if( cntxt->curprg == 0)
     130           0 :                 throw(MAL, "initClientPrg", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     131       13944 :         if( (idx= findVariable(cntxt->curprg->def,"main")) >=0)
     132        6713 :                 setVarType(cntxt->curprg->def, idx, TYPE_void);
     133       13944 :         insertSymbol(cntxt->usermodule,cntxt->curprg);
     134             : 
     135       13944 :         if (cntxt->glb == NULL )
     136        6735 :                 cntxt->glb = newGlobalStack(MAXGLOBALS + cntxt->curprg->def->vsize);
     137       13944 :         if( cntxt->glb == NULL)
     138           0 :                 throw(MAL,"initClientPrg", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     139       13944 :         assert(cntxt->curprg->def != NULL);
     140       13944 :         assert(cntxt->curprg->def->vtop >0);
     141             :         return MAL_SUCCEED;
     142             : }
     143             : 
     144             : /*
     145             :  * The default method to interact with the database server is to connect
     146             :  * using a port number. The first line received should contain
     147             :  * authorization information, such as user name.
     148             :  *
     149             :  * The scheduleClient receives a challenge response consisting of
     150             :  * endian:user:password:lang:database:
     151             :  */
     152             : static void
     153         147 : exit_streams( bstream *fin, stream *fout )
     154             : {
     155         147 :         if (fout && fout != GDKstdout) {
     156         147 :                 mnstr_flush(fout, MNSTR_FLUSH_DATA);
     157         147 :                 close_stream(fout);
     158             :         }
     159         147 :         if (fin)
     160         147 :                 bstream_destroy(fin);
     161         147 : }
     162             : 
     163             : const char* mal_enableflag = "mal_for_all";
     164             : 
     165             : static bool
     166         971 : is_exiting(void *data)
     167             : {
     168             :         (void) data;
     169         971 :         return GDKexiting();
     170             : }
     171             : 
     172             : void
     173        4440 : MSscheduleClient(str command, str challenge, bstream *fin, stream *fout, protocol_version protocol, size_t blocksize)
     174             : {
     175             :         char *user = command, *algo = NULL, *passwd = NULL, *lang = NULL, *handshake_opts = NULL;
     176             :         char *database = NULL, *s;
     177             :         const char *dbname;
     178             :         str msg = MAL_SUCCEED;
     179             :         bool filetrans = false;
     180             :         Client c;
     181             : 
     182             :         /* decode BIG/LIT:user:{cypher}passwordchal:lang:database: line */
     183             : 
     184             :         /* byte order */
     185        4440 :         s = strchr(user, ':');
     186        4440 :         if (s) {
     187        4301 :                 *s = 0;
     188        4301 :                 mnstr_set_bigendian(fin->s, strcmp(user, "BIG") == 0);
     189        4301 :                 user = s + 1;
     190             :         } else {
     191         139 :                 mnstr_printf(fout, "!incomplete challenge '%s'\n", user);
     192         139 :                 exit_streams(fin, fout);
     193         139 :                 GDKfree(command);
     194         139 :                 return;
     195             :         }
     196             : 
     197             :         /* passwd */
     198        4301 :         s = strchr(user, ':');
     199        4301 :         if (s) {
     200        4301 :                 *s = 0;
     201             :                 passwd = s + 1;
     202             :                 /* decode algorithm, i.e. {plain}mypasswordchallenge */
     203        4301 :                 if (*passwd != '{') {
     204           0 :                         mnstr_printf(fout, "!invalid password entry\n");
     205           0 :                         exit_streams(fin, fout);
     206           0 :                         GDKfree(command);
     207           0 :                         return;
     208             :                 }
     209        4301 :                 algo = passwd + 1;
     210        4301 :                 s = strchr(algo, '}');
     211        4301 :                 if (!s) {
     212           0 :                         mnstr_printf(fout, "!invalid password entry\n");
     213           0 :                         exit_streams(fin, fout);
     214           0 :                         GDKfree(command);
     215           0 :                         return;
     216             :                 }
     217        4301 :                 *s = 0;
     218        4301 :                 passwd = s + 1;
     219             :         } else {
     220           0 :                 mnstr_printf(fout, "!incomplete challenge '%s'\n", user);
     221           0 :                 exit_streams(fin, fout);
     222           0 :                 GDKfree(command);
     223           0 :                 return;
     224             :         }
     225             : 
     226             :         /* lang */
     227        4301 :         s = strchr(passwd, ':');
     228        4301 :         if (s) {
     229        4301 :                 *s = 0;
     230        4301 :                 lang = s + 1;
     231             :         } else {
     232           0 :                 mnstr_printf(fout, "!incomplete challenge, missing language\n");
     233           0 :                 exit_streams(fin, fout);
     234           0 :                 GDKfree(command);
     235           0 :                 return;
     236             :         }
     237             : 
     238             :         /* database */
     239        4301 :         s = strchr(lang, ':');
     240        4301 :         if (s) {
     241        4301 :                 *s = 0;
     242        4301 :                 database = s + 1;
     243             :                 /* we can have stuff following, make it void */
     244        4301 :                 s = strchr(database, ':');
     245        4301 :                 if (s)
     246        4301 :                         *s++ = 0;
     247             :         }
     248             : 
     249        4301 :         if (s && strncmp(s, "FILETRANS:", 10) == 0) {
     250        1294 :                 s += 10;
     251        1294 :                 filetrans = true;
     252        3007 :         } else if (s && s[0] == ':') {
     253         162 :                 s += 1;
     254             :                 filetrans = false;
     255             :         }
     256             : 
     257        4301 :         if (s && strchr(s, ':') != NULL) {
     258             :                 handshake_opts = s;
     259             :                 s = strchr(s, ':');
     260        1456 :                 *s++ = '\0';
     261             :         }
     262        4301 :         dbname = GDKgetenv("gdk_dbname");
     263        4301 :         if (database != NULL && database[0] != '\0' &&
     264        3281 :                 strcmp(database, dbname) != 0)
     265             :         {
     266           0 :                 mnstr_printf(fout, "!request for database '%s', "
     267             :                                                    "but this is database '%s', "
     268             :                                                    "did you mean to connect to monetdbd instead?\n",
     269             :                                 database, dbname);
     270             :                 /* flush the error to the client, and abort further execution */
     271           0 :                 exit_streams(fin, fout);
     272           0 :                 GDKfree(command);
     273           0 :                 return;
     274             :         } else {
     275             :                 str err;
     276             :                 oid uid;
     277        4301 :                 sabdb *stats = NULL;
     278             : 
     279             :                 /* access control: verify the credentials supplied by the user,
     280             :                  * no need to check for database stuff, because that is done per
     281             :                  * database itself (one gets a redirect) */
     282        4301 :                 err = AUTHcheckCredentials(&uid, NULL, user, passwd, challenge, algo);
     283        4301 :                 if (err != MAL_SUCCEED) {
     284           8 :                         mnstr_printf(fout, "!%s\n", err);
     285           8 :                         exit_streams(fin, fout);
     286           8 :                         freeException(err);
     287           8 :                         GDKfree(command);
     288           8 :                         return;
     289             :                 }
     290             : 
     291        4293 :                 if (!GDKinmemory(0) && !GDKembedded()) {
     292        4293 :                         err = msab_getMyStatus(&stats);
     293        4293 :                         if (err != NULL) {
     294             :                                 /* this is kind of awful, but we need to get rid of this
     295             :                                  * message */
     296           0 :                                 free(err);
     297           0 :                                 mnstr_printf(fout, "!internal server error, "
     298             :                                                          "please try again later\n");
     299           0 :                                 exit_streams(fin, fout);
     300           0 :                                 GDKfree(command);
     301           0 :                                 return;
     302             :                         }
     303        4293 :                         if (stats->locked) {
     304           0 :                                 if (uid == 0) {
     305           0 :                                         mnstr_printf(fout, "#server is running in "
     306             :                                                                  "maintenance mode\n");
     307             :                                 } else {
     308           0 :                                         mnstr_printf(fout, "!server is running in "
     309             :                                                                  "maintenance mode, please try again later\n");
     310           0 :                                         exit_streams(fin, fout);
     311           0 :                                         msab_freeStatus(&stats);
     312           0 :                                         GDKfree(command);
     313           0 :                                         return;
     314             :                                 }
     315             :                         }
     316        4293 :                         msab_freeStatus(&stats);
     317             :                 }
     318             : 
     319        4293 :                 c = MCinitClient(uid, fin, fout);
     320        4293 :                 if (c == NULL) {
     321           0 :                         if ( MCshutdowninprogress())
     322           0 :                                 mnstr_printf(fout, "!system shutdown in progress, please try again later\n");
     323             :                         else
     324           0 :                                 mnstr_printf(fout, "!maximum concurrent client limit reached "
     325             :                                                                    "(%d), please try again later\n", MAL_MAXCLIENTS);
     326           0 :                         exit_streams(fin, fout);
     327           0 :                         GDKfree(command);
     328           0 :                         return;
     329             :                 }
     330        4293 :                 c->filetrans = filetrans;
     331        4293 :                 c->handshake_options = handshake_opts ? strdup(handshake_opts) : NULL;
     332             :                 /* move this back !! */
     333        4293 :                 if (c->usermodule == 0) {
     334        4293 :                         c->curmodule = c->usermodule = userModule();
     335        4293 :                         if(c->curmodule  == NULL) {
     336           0 :                                 mnstr_printf(fout, "!could not allocate space\n");
     337           0 :                                 exit_streams(fin, fout);
     338           0 :                                 GDKfree(command);
     339           0 :                                 return;
     340             :                         }
     341             :                 }
     342             : 
     343        4293 :                 if ((s = setScenario(c, lang)) != NULL) {
     344           0 :                         mnstr_printf(c->fdout, "!%s\n", s);
     345           0 :                         mnstr_flush(c->fdout, MNSTR_FLUSH_DATA);
     346           0 :                         GDKfree(s);
     347           0 :                         c->mode = FINISHCLIENT;
     348             :                 }
     349        4293 :                 if (!GDKgetenv_isyes(mal_enableflag) &&
     350        4293 :                                 (strncasecmp("sql", lang, 3) != 0 && uid != 0)) {
     351             : 
     352           0 :                         mnstr_printf(fout, "!only the 'monetdb' user can use non-sql languages. "
     353             :                                                    "run mserver5 with --set %s=yes to change this.\n", mal_enableflag);
     354           0 :                         exit_streams(fin, fout);
     355           0 :                         GDKfree(command);
     356           0 :                         return;
     357             :                 }
     358             :         }
     359             : 
     360        4293 :         if((msg = MSinitClientPrg(c, "user", "main")) != MAL_SUCCEED) {
     361           0 :                 mnstr_printf(fout, "!could not allocate space\n");
     362           0 :                 exit_streams(fin, fout);
     363           0 :                 freeException(msg);
     364           0 :                 GDKfree(command);
     365           0 :                 return;
     366             :         }
     367             : 
     368        4293 :         GDKfree(command);
     369             : 
     370             :         /* NOTE ABOUT STARTING NEW THREADS
     371             :          * At this point we have conducted experiments (Jun 2012) with
     372             :          * reusing threads.  The implementation used was a lockless array of
     373             :          * semaphores to wake up threads to do work.  Experimentation on
     374             :          * Linux, Solaris and Darwin showed no significant improvements, in
     375             :          * most cases no improvements at all.  Hence the following
     376             :          * conclusion: thread reuse doesn't save up on the costs of just
     377             :          * forking new threads.  Since the latter means no difficulties of
     378             :          * properly maintaining a pool of threads and picking the workers
     379             :          * out of them, it is favourable just to start new threads on
     380             :          * demand. */
     381             : 
     382             :         /* fork a new thread to handle this client */
     383             : 
     384        4293 :         c->protocol = protocol;
     385        4293 :         c->blocksize = blocksize;
     386             : 
     387        4293 :         mnstr_settimeout(c->fdin->s, 50, is_exiting, NULL);
     388        4293 :         msg = MSserveClient(c);
     389        4293 :         if (msg != MAL_SUCCEED) {
     390           0 :                 mnstr_printf(fout, "!could not serve client\n");
     391           0 :                 exit_streams(fin, fout);
     392           0 :                 freeException(msg);
     393             :         }
     394             : }
     395             : 
     396             : /*
     397             :  * After the client initialization has been finished, we can start the
     398             :  * interaction protocol. This involves parsing the input in the context
     399             :  * of an already defined procedure and upon success, its execution.
     400             :  *
     401             :  * In essence, this calls for an incremental parsing operation, because
     402             :  * we should wait until a complete basic block has been detected.  Test,
     403             :  * first collect the instructions before we take them all.
     404             :  *
     405             :  * In interactive mode, we should remove the instructions before
     406             :  * accepting new ones. The function signature remains the same and the
     407             :  * symbol table should also not be affected.  Aside from removing
     408             :  * instruction, we should also condense the variable stack, i.e.
     409             :  * removing at least the temporary variables, but maybe everything
     410             :  * beyond a previous defined point.
     411             :  *
     412             :  * Beware that we have to cleanup the global stack as well. This to
     413             :  * avoid subsequent calls to find garbage information.  However, this
     414             :  * action is only required after a successful execution.  Otherwise,
     415             :  * garbage collection is not needed.
     416             :  */
     417             : void
     418      401598 : MSresetInstructions(MalBlkPtr mb, int start)
     419             : {
     420             :         int i;
     421             :         InstrPtr p;
     422             : 
     423    99128623 :         for (i = start; i < mb->ssize; i++) {
     424    98727025 :                 p = getInstrPtr(mb, i);
     425    98727025 :                 if (p)
     426    19985594 :                         freeInstruction(p);
     427    98727025 :                 mb->stmt[i] = NULL;
     428             :         }
     429      401598 :         mb->stop = start;
     430      401598 : }
     431             : 
     432             : /*
     433             :  * MAL instructions generate variables.
     434             :  * The values of temporary variables should be cleaned at the end of a call
     435             :  * The values of global variables are retained.
     436             :  * Global variables should not start with C_ or X_
     437             :  */
     438             : void
     439        6248 : MSresetStack(Client cntxt, MalBlkPtr mb, MalStkPtr glb)
     440             : {
     441        6248 :         InstrPtr sig = getInstrPtr(mb, 0);
     442        6248 :         int i, k = sig->argc;
     443             : 
     444        6248 :         if (mb->errors == MAL_SUCCEED){
     445       50310 :                 for (i = sig->argc; i < mb->vtop; i++) {
     446       44062 :                         if (glb && i < glb->stktop && isTmpVar(mb,i) ) {
     447             :                                 /* clean stack entry */
     448        9174 :                                 garbageElement(cntxt, &glb->stk[i]);
     449        9174 :                                 glb->stk[i].vtype = TYPE_int;
     450        9174 :                                 glb->stk[i].len = 0;
     451        9174 :                                 glb->stk[i].val.pval = 0;
     452             :                         } else {
     453             :                                 /* compress the global variable list and stack */
     454       34888 :                                 mb->var[k] = mb->var[i];
     455       34888 :                                 glb->stk[k] = glb->stk[i];
     456       34888 :                                 setVarUsed(mb, k);
     457       34888 :                                 setVarInit(mb, k);
     458       34888 :                                 if( i != k){
     459         119 :                                         glb->stk[i].vtype = TYPE_int;
     460         119 :                                         glb->stk[i].len = 0;
     461         119 :                                         glb->stk[i].val.pval = 0;
     462         119 :                                         clrVarConstant(mb,i);
     463         119 :                                         clrVarCleanup(mb,i);
     464             :                                 }
     465       34888 :                                 k++;
     466             :                         }
     467             :                 }
     468             :         }
     469        6248 :         mb->vtop = k;
     470        6248 : }
     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             :         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             : str
     496        4293 : MSserveClient(Client c)
     497             : {
     498             :         MalBlkPtr mb;
     499             :         str msg = 0;
     500             : 
     501        4293 :         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        4293 :         mb = c->curprg->def;
     510        4293 :         if (c->glb == NULL)
     511           0 :                 c->glb = newGlobalStack(MAXGLOBALS + mb->vsize);
     512        4293 :         if (c->glb == NULL) {
     513           0 :                 c->mode = RUNCLIENT;
     514           0 :                 throw(MAL, "serveClient", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     515             :         } else {
     516        4293 :                 c->glb->stktop = mb->vtop;
     517        4293 :                 c->glb->blk = mb;
     518             :         }
     519             : 
     520        4293 :         if (c->scenario == 0)
     521           0 :                 msg = defaultScenario(c);
     522           0 :         if (msg) {
     523           0 :                 c->mode = RUNCLIENT;
     524           0 :                 return msg;
     525             :         } else {
     526             :                 do {
     527             :                         do {
     528        4293 :                                 MT_thread_setworking("running scenario");
     529        4293 :                                 msg = runScenario(c,0);
     530        4293 :                                 freeException(msg);
     531        4293 :                                 if (c->mode == FINISHCLIENT)
     532             :                                         break;
     533           0 :                                 resetScenario(c);
     534           0 :                         } while (c->scenario && !GDKexiting());
     535        4293 :                 } while (c->scenario && c->mode != FINISHCLIENT && !GDKexiting());
     536             :         }
     537        4293 :         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        4293 :         MT_exiting_thread();
     542             :         /*
     543             :          * At this stage we should clean out the MAL block
     544             :          */
     545        4293 :         if (c->backup) {
     546           0 :                 assert(0);
     547             :                 freeSymbol(c->backup);
     548             :                 c->backup = 0;
     549             :         }
     550             : 
     551        4293 :         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        4293 :         MCcloseClient(c);
     561        4293 :         if (c->usermodule /*&& strcmp(c->usermodule->name, "user") == 0*/) {
     562           0 :                 freeModule(c->usermodule);
     563           0 :                 c->usermodule = NULL;
     564             :         }
     565             :         return MAL_SUCCEED;
     566             : }
     567             : 
     568             : /*
     569             :  * The stages of processing user requests are controlled by a scenario.
     570             :  * The routines below are the default implementation.  The main issues
     571             :  * to deal after parsing it to clean out the Admin.main function from
     572             :  * any information added erroneously.
     573             :  *
     574             :  * Ideally this involves resetting the state of the client 'main'
     575             :  * function, i.e. the symbol table is reset and any instruction added
     576             :  * should be cleaned. Beware that the instruction table may have grown
     577             :  * in size.
     578             :  */
     579             : str
     580         359 : MALinitClient(Client c)
     581             : {
     582         359 :         assert(c->state[0] == NULL);
     583         359 :         c->state[0] = c;
     584         359 :         return NULL;
     585             : }
     586             : 
     587             : str
     588        4316 : MALexitClient(Client c)
     589             : {
     590        4316 :         if (c->glb && c->curprg->def->errors == MAL_SUCCEED)
     591        4315 :                 garbageCollector(c, c->curprg->def, c->glb, TRUE);
     592        4316 :         c->mode = FINISHCLIENT;
     593        4316 :         if (c->backup) {
     594           0 :                 assert(0);
     595             :                 freeSymbol(c->backup);
     596             :                 c->backup = NULL;
     597             :         }
     598             :         /* should be in the usermodule */
     599        4316 :         c->curprg = NULL;
     600        4316 :         if (c->usermodule){
     601        4316 :                 freeModule(c->usermodule);
     602        4316 :                 c->usermodule = NULL;
     603             :         }
     604        4316 :         return NULL;
     605             : }
     606             : 
     607             : str
     608        7032 : MALreader(Client c)
     609             : {
     610        7032 :         if (MCreadClient(c) > 0)
     611             :                 return MAL_SUCCEED;
     612         410 :         MT_lock_set(&mal_contextLock);
     613         410 :         c->mode = FINISHCLIENT;
     614         410 :         MT_lock_unset(&mal_contextLock);
     615         410 :         if (c->fdin)
     616         410 :                 c->fdin->buf[c->fdin->pos] = 0;
     617             :         return MAL_SUCCEED;
     618             : }
     619             : 
     620             : /* Before compiling a large string, it makes sense to allocate
     621             :  * approximately enough space to keep the intermediate
     622             :  * code. Otherwise, we end up with a repeated extend on the MAL block,
     623             :  * which really consumes a lot of memcpy resources. The average MAL
     624             :  * string length could been derived from the test cases. An error in
     625             :  * the estimate is more expensive than just counting the lines.
     626             :  */
     627             : static int
     628        8766 : prepareMalBlk(MalBlkPtr mb, str s)
     629             : {
     630             :         int cnt = STMT_INCREMENT;
     631             : 
     632       37633 :         while (s) {
     633       20101 :                 s = strchr(s, '\n');
     634       20101 :                 if (s) {
     635       11335 :                         s++;
     636       11335 :                         cnt++;
     637             :                 }
     638             :         }
     639        8766 :         cnt = (int) (cnt * 1.1);
     640        8766 :         return resizeMalBlk(mb, cnt);
     641             : }
     642             : 
     643             : str
     644        8766 : MALparser(Client c)
     645             : {
     646             :         InstrPtr p;
     647             :         str msg= MAL_SUCCEED;
     648             : 
     649        8766 :         assert(c->curprg->def->errors == NULL);
     650        8766 :         c->curprg->def->errors = 0;
     651             : 
     652        8766 :         if( prepareMalBlk(c->curprg->def, CURRENT(c)) < 0)
     653           0 :                 throw(MAL, "mal.parser", "Failed to prepare");
     654        8766 :         parseMAL(c, c->curprg, 0, INT_MAX, 0);
     655             : 
     656             :         /* now the parsing is done we should advance the stream */
     657        8766 :         c->fdin->pos += c->yycur;
     658        8766 :         c->yycur = 0;
     659             : 
     660             :         /* check for unfinished blocks */
     661        8766 :         if(!c->curprg->def->errors && c->blkmode)
     662             :                 return MAL_SUCCEED;
     663             :         /* empty files should be skipped as well */
     664        8403 :         if (c->curprg->def->stop == 1){
     665        2419 :                 if ( (msg =c->curprg->def->errors) )
     666          53 :                         c->curprg->def->errors = 0;
     667        2419 :                 return msg;
     668             :         }
     669             : 
     670        5984 :         p = getInstrPtr(c->curprg->def, 0);
     671        5984 :         if (p->token != FUNCTIONsymbol) {
     672             :                 msg =c->curprg->def->errors;
     673           0 :                 c->curprg->def->errors = 0;
     674           0 :                 MSresetStack(c, c->curprg->def, c->glb);
     675           0 :                 resetMalTypes(c->curprg->def, 1);
     676           0 :                 return msg;
     677             :         }
     678        5984 :         pushEndInstruction(c->curprg->def);
     679        5984 :         msg = chkProgram(c->usermodule, c->curprg->def);
     680        5984 :         if (msg !=MAL_SUCCEED || (msg =c->curprg->def->errors) ){
     681          46 :                 c->curprg->def->errors = 0;
     682          46 :                 MSresetStack(c, c->curprg->def, c->glb);
     683          46 :                 resetMalTypes(c->curprg->def, 1);
     684          46 :                 return msg;
     685             :         }
     686             :         return MAL_SUCCEED;
     687             : }
     688             : 
     689             : int
     690      121796 : MALcommentsOnly(MalBlkPtr mb)
     691             : {
     692             :         int i;
     693             : 
     694      121884 :         for (i = 1; i < mb->stop; i++)
     695      121885 :                 if (mb->stmt[i]->token != REMsymbol)
     696             :                         return 0;
     697             :         return 1;
     698             : }
     699             : 
     700             : str
     701         128 : MALcallback(Client c, str msg)
     702             : {
     703         128 :         if (msg) {
     704             :                 /* don't print exception decoration, just the message */
     705             :                 char *n = NULL;
     706             :                 char *o = msg;
     707         260 :                 while ((n = strchr(o, '\n')) != NULL) {
     708         132 :                         if (*o == '!')
     709           0 :                                 o++;
     710         132 :                         mnstr_printf(c->fdout, "!%.*s\n", (int) (n - o), o);
     711         132 :                         o = ++n;
     712             :                 }
     713         128 :                 if (*o != 0) {
     714          69 :                         if (*o == '!')
     715           5 :                                 o++;
     716          69 :                         mnstr_printf(c->fdout, "!%s\n", o);
     717             :                 }
     718         128 :                 freeException(msg);
     719             :         }
     720         128 :         return MAL_SUCCEED;
     721             : }
     722             : 
     723             : str
     724        6426 : MALengine(Client c)
     725             : {
     726             :         Symbol prg;
     727             :         str msg = MAL_SUCCEED;
     728             : 
     729        6426 :         if (c->blkmode)
     730             :                 return MAL_SUCCEED;
     731        6426 :         prg = c->curprg;
     732        6426 :         if (prg == NULL)
     733           0 :                 throw(SYNTAX, "mal.engine", SYNTAX_SIGNATURE);
     734        6426 :         if (prg->def == NULL)
     735           0 :                 throw(SYNTAX, "mal.engine", SYNTAX_SIGNATURE);
     736             : 
     737        6426 :         if (prg->def->errors != MAL_SUCCEED) {
     738             :                 msg = prg->def->errors;
     739           0 :                 prg->def->errors = NULL;
     740           0 :                 MSresetStack(c, c->curprg->def, c->glb);
     741           0 :                 resetMalTypes(c->curprg->def, 1);
     742           0 :                 return msg;
     743             :         }
     744        6426 :         if (prg->def->stop == 1 || MALcommentsOnly(prg->def))
     745         224 :                 return 0;   /* empty block */
     746        6202 :         if (c->glb) {
     747        6202 :                 if (prg->def && c->glb->stksize < prg->def->vsize){
     748           0 :                         c->glb = reallocGlobalStack(c->glb, prg->def->vsize);
     749           0 :                         if( c->glb == NULL)
     750           0 :                                 throw(MAL, "mal.engine", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     751             :                 }
     752        6202 :                 c->glb->stktop = prg->def->vtop;
     753        6202 :                 c->glb->blk = prg->def;
     754       12404 :                 c->glb->cmd = (c->itrace && c->itrace != 'C') ? 'n' : 0;
     755             :         }
     756             : 
     757             :         /*
     758             :          * In interactive mode we should avoid early garbage collection of values.
     759             :          * This can be controlled by the clean up control at the instruction level
     760             :          * and marking all non-temporary variables as being (potentially) used.
     761             :          */
     762        6202 :         if (c->glb) {
     763        6202 :                 c->glb->pcup = 0;
     764        6202 :                 c->glb->keepAlive = TRUE; /* no garbage collection */
     765             :         }
     766        6202 :         if (prg->def->errors == MAL_SUCCEED)
     767        6202 :                 msg = (str) runMAL(c, prg->def, 0, c->glb);
     768        6202 :         if (msg) {
     769             :                 /* ignore "internal" exceptions */
     770          30 :                 if (strstr(msg, "client.quit") ) {
     771           0 :                         freeException(msg);
     772             :                         msg = MAL_SUCCEED;
     773             :                 }
     774             :         }
     775        6202 :         MSresetStack(c, prg->def, c->glb);
     776        6202 :         resetMalTypes(prg->def, 1);
     777        6202 :         if (c->glb) {
     778             :                 /* for global stacks avoid reinitialization from this point */
     779        6202 :                 c->glb->stkbot = prg->def->vtop;
     780             :         }
     781             : 
     782        6202 :         if (prg->def->errors)
     783           0 :                 GDKfree(prg->def->errors);
     784        6202 :         prg->def->errors = NULL;
     785        6202 :         if (c->itrace)
     786           0 :                 mnstr_printf(c->fdout, "mdb>#EOD\n");
     787             :         return msg;
     788             : }

Generated by: LCOV version 1.14