LCOV - code coverage report
Current view: top level - monetdb5/mal - mal_session.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 267 385 69.4 %
Date: 2021-10-13 02:24:04 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      235623 : MSresetClientPrg(Client cntxt, const char *mod, const char *fcn)
      88             : {
      89             :         MalBlkPtr mb;
      90             :         InstrPtr p;
      91             : 
      92      235623 :         cntxt->itrace = 0;  /* turn off any debugging */
      93      235623 :         mb = cntxt->curprg->def;
      94      235623 :         mb->stop = 1;
      95      235623 :         mb->errors = MAL_SUCCEED;
      96      235623 :         p = mb->stmt[0];
      97             : 
      98      235623 :         p->gc = 0;
      99      235623 :         p->retc = 1;
     100      235623 :         p->argc = 1;
     101      235623 :         p->argv[0] = 0;
     102             : 
     103      235623 :         setModuleId(p, mod);
     104      235623 :         setFunctionId(p, fcn);
     105      235623 :         if( findVariable(mb,fcn) < 0)
     106           0 :                 p->argv[0] = newVariable(mb, fcn, strlen(fcn), TYPE_void);
     107             : 
     108      235623 :         setVarType(mb, findVariable(mb, fcn), TYPE_void);
     109             :         /* remove any MAL history */
     110      235623 :         if (mb->history) {
     111           0 :                 freeMalBlk(mb->history);
     112           0 :                 mb->history = 0;
     113             :         }
     114      235623 :         return MAL_SUCCEED;
     115             : }
     116             : 
     117             : /*
     118             :  * Create a new container block
     119             :  */
     120             : 
     121             : str
     122      250639 : MSinitClientPrg(Client cntxt, const char *mod, const char *nme)
     123             : {
     124             :         int idx;
     125             : 
     126      250639 :         if (cntxt->curprg  && idcmp(nme, cntxt->curprg->name) == 0)
     127      235623 :                 return MSresetClientPrg(cntxt, putName(mod), putName(nme));
     128       15016 :         cntxt->curprg = newFunction(putName(mod), putName(nme), FUNCTIONsymbol);
     129       15016 :         if( cntxt->curprg == 0)
     130           0 :                 throw(MAL, "initClientPrg", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     131       15016 :         if( (idx= findVariable(cntxt->curprg->def,"main")) >=0)
     132        7787 :                 setVarType(cntxt->curprg->def, idx, TYPE_void);
     133       15016 :         insertSymbol(cntxt->usermodule,cntxt->curprg);
     134             : 
     135       15016 :         if (cntxt->glb == NULL )
     136        7809 :                 cntxt->glb = newGlobalStack(MAXGLOBALS + cntxt->curprg->def->vsize);
     137       15016 :         if( cntxt->glb == NULL)
     138           0 :                 throw(MAL,"initClientPrg", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     139       15016 :         assert(cntxt->curprg->def != NULL);
     140       15016 :         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        2350 : is_exiting(void *data)
     167             : {
     168             :         (void) data;
     169        2350 :         return GDKexiting();
     170             : }
     171             : 
     172             : void
     173        5514 : 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        5514 :         s = strchr(user, ':');
     186        5514 :         if (s) {
     187        5375 :                 *s = 0;
     188        5375 :                 mnstr_set_bigendian(fin->s, strcmp(user, "BIG") == 0);
     189        5375 :                 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        5375 :         s = strchr(user, ':');
     199        5375 :         if (s) {
     200        5375 :                 *s = 0;
     201             :                 passwd = s + 1;
     202             :                 /* decode algorithm, i.e. {plain}mypasswordchallenge */
     203        5375 :                 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        5375 :                 algo = passwd + 1;
     210        5375 :                 s = strchr(algo, '}');
     211        5375 :                 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        5375 :                 *s = 0;
     218        5375 :                 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        5375 :         s = strchr(passwd, ':');
     228        5375 :         if (s) {
     229        5375 :                 *s = 0;
     230        5375 :                 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        5375 :         s = strchr(lang, ':');
     240        5375 :         if (s) {
     241        5375 :                 *s = 0;
     242        5375 :                 database = s + 1;
     243             :                 /* we can have stuff following, make it void */
     244        5375 :                 s = strchr(database, ':');
     245        5375 :                 if (s)
     246        5375 :                         *s++ = 0;
     247             :         }
     248             : 
     249        5375 :         if (s && strncmp(s, "FILETRANS:", 10) == 0) {
     250        1356 :                 s += 10;
     251        1356 :                 filetrans = true;
     252        4019 :         } else if (s && s[0] == ':') {
     253         162 :                 s += 1;
     254             :                 filetrans = false;
     255             :         }
     256             : 
     257        5375 :         if (s && strchr(s, ':') != NULL) {
     258             :                 handshake_opts = s;
     259             :                 s = strchr(s, ':');
     260        1518 :                 *s++ = '\0';
     261             :         }
     262        5375 :         dbname = GDKgetenv("gdk_dbname");
     263        5375 :         if (database != NULL && database[0] != '\0' &&
     264        4355 :                 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        5375 :                 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        5375 :                 err = AUTHcheckCredentials(&uid, NULL, user, passwd, challenge, algo);
     283        5375 :                 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        5367 :                 if (!GDKinmemory(0) && !GDKembedded()) {
     292        5367 :                         err = msab_getMyStatus(&stats);
     293        5367 :                         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        5367 :                         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        5367 :                         msab_freeStatus(&stats);
     317             :                 }
     318             : 
     319        5367 :                 c = MCinitClient(uid, fin, fout);
     320        5367 :                 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        5367 :                 c->filetrans = filetrans;
     331        5367 :                 c->handshake_options = handshake_opts ? strdup(handshake_opts) : NULL;
     332             :                 /* move this back !! */
     333        5367 :                 if (c->usermodule == 0) {
     334        5367 :                         c->curmodule = c->usermodule = userModule();
     335        5367 :                         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        5367 :                 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        5367 :                 if (!GDKgetenv_isyes(mal_enableflag) &&
     350        5367 :                                 (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        5367 :         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        5367 :         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        5367 :         c->protocol = protocol;
     385        5367 :         c->blocksize = blocksize;
     386             : 
     387        5367 :         mnstr_settimeout(c->fdin->s, 50, is_exiting, NULL);
     388        5367 :         msg = MSserveClient(c);
     389        5367 :         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      405890 : MSresetInstructions(MalBlkPtr mb, int start)
     419             : {
     420             :         int i;
     421             :         InstrPtr p;
     422             : 
     423   100209716 :         for (i = start; i < mb->ssize; i++) {
     424    99803826 :                 p = getInstrPtr(mb, i);
     425    99803826 :                 if (p)
     426    19249760 :                         freeInstruction(p);
     427    99803826 :                 mb->stmt[i] = NULL;
     428             :         }
     429      405890 :         mb->stop = start;
     430      405890 : }
     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        9354 : MSresetStack(Client cntxt, MalBlkPtr mb, MalStkPtr glb)
     440             : {
     441        9354 :         InstrPtr sig = getInstrPtr(mb, 0);
     442        9354 :         int i, k = sig->argc;
     443             : 
     444        9354 :         if (mb->errors == MAL_SUCCEED){
     445      839533 :                 for (i = sig->argc; i < mb->vtop; i++) {
     446      830179 :                         if (glb && i < glb->stktop && isTmpVar(mb,i) ) {
     447             :                                 /* clean stack entry */
     448       13108 :                                 garbageElement(cntxt, &glb->stk[i]);
     449       13108 :                                 glb->stk[i].vtype = TYPE_int;
     450       13108 :                                 glb->stk[i].len = 0;
     451       13108 :                                 glb->stk[i].val.pval = 0;
     452       13108 :                                 if (isVarConstant(mb, i))
     453        8872 :                                         garbageElement(cntxt, &mb->var[i].value);
     454             :                         } else {
     455             :                                 /* compress the global variable list and stack */
     456      817071 :                                 mb->var[k] = mb->var[i];
     457      817071 :                                 glb->stk[k] = glb->stk[i];
     458      817071 :                                 setVarUsed(mb, k);
     459      817071 :                                 setVarInit(mb, k);
     460      817071 :                                 if( i != k){
     461         121 :                                         glb->stk[i].vtype = TYPE_int;
     462         121 :                                         glb->stk[i].len = 0;
     463         121 :                                         glb->stk[i].val.pval = 0;
     464         121 :                                         clrVarConstant(mb,i);
     465         121 :                                         clrVarCleanup(mb,i);
     466             :                                 }
     467      817071 :                                 k++;
     468             :                         }
     469             :                 }
     470             :         }
     471        9354 :         mb->vtop = k;
     472        9354 : }
     473             : 
     474             : /* The symbol table be become filled with constant values to be garbage collected
     475             : * The signature is always left behind.
     476             : */
     477             : 
     478             : void
     479           0 : MSresetVariables(MalBlkPtr mb)
     480             : {
     481           0 :         InstrPtr sig = getInstrPtr(mb, 0);
     482             :         int i;
     483             : 
     484           0 :         if (mb->errors == MAL_SUCCEED)
     485           0 :                 for (i = sig->argc; i < mb->vtop; i++)
     486           0 :                         if( isVarConstant(mb,i)){
     487           0 :                                 VALclear(&getVarConstant(mb,i));
     488           0 :                                 clrVarConstant(mb, i);
     489             :                         }
     490           0 : }
     491             : 
     492             : /*
     493             :  * Here we start the client.  We need to initialize and allocate space
     494             :  * for the global variables.  Thereafter it is up to the scenario
     495             :  * interpreter to process input.
     496             :  */
     497             : str
     498        5367 : MSserveClient(Client c)
     499             : {
     500             :         MalBlkPtr mb;
     501             :         str msg = 0;
     502             : 
     503        5367 :         if (MCinitClientThread(c) < 0) {
     504           0 :                 MCcloseClient(c);
     505           0 :                 return MAL_SUCCEED;
     506             :         }
     507             :         /*
     508             :          * A stack frame is initialized to keep track of global variables.
     509             :          * The scenarios are run until we finally close the last one.
     510             :          */
     511        5367 :         mb = c->curprg->def;
     512        5367 :         if (c->glb == NULL)
     513           0 :                 c->glb = newGlobalStack(MAXGLOBALS + mb->vsize);
     514        5367 :         if (c->glb == NULL) {
     515           0 :                 c->mode = RUNCLIENT;
     516           0 :                 throw(MAL, "serveClient", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     517             :         } else {
     518        5367 :                 c->glb->stktop = mb->vtop;
     519        5367 :                 c->glb->blk = mb;
     520             :         }
     521             : 
     522        5367 :         if (c->scenario == 0)
     523           0 :                 msg = defaultScenario(c);
     524           0 :         if (msg) {
     525           0 :                 c->mode = RUNCLIENT;
     526           0 :                 return msg;
     527             :         } else {
     528             :                 do {
     529             :                         do {
     530        5367 :                                 MT_thread_setworking("running scenario");
     531        5367 :                                 msg = runScenario(c,0);
     532        5367 :                                 freeException(msg);
     533        5367 :                                 if (c->mode == FINISHCLIENT)
     534             :                                         break;
     535           0 :                                 resetScenario(c);
     536           0 :                         } while (c->scenario && !GDKexiting());
     537        5367 :                 } while (c->scenario && c->mode != FINISHCLIENT && !GDKexiting());
     538             :         }
     539        5367 :         MT_thread_setworking("exiting");
     540             :         /* pre announce our exiting: cleaning up may take a while and we
     541             :          * don't want to get killed during that time for fear of
     542             :          * deadlocks */
     543        5367 :         MT_exiting_thread();
     544             :         /*
     545             :          * At this stage we should clean out the MAL block
     546             :          */
     547        5367 :         if (c->backup) {
     548           0 :                 assert(0);
     549             :                 freeSymbol(c->backup);
     550             :                 c->backup = 0;
     551             :         }
     552             : 
     553        5367 :         if( c->curprg && c->curprg->def)
     554           0 :                 resetMalBlk(c->curprg->def);
     555             :         /*
     556             :         if (c->curprg) {
     557             :                 freeSymbol(c->curprg);
     558             :                 c->curprg = 0;
     559             :         }
     560             :         */
     561             : 
     562        5367 :         MCcloseClient(c);
     563        5367 :         if (c->usermodule /*&& strcmp(c->usermodule->name, "user") == 0*/) {
     564           0 :                 freeModule(c->usermodule);
     565           0 :                 c->usermodule = NULL;
     566             :         }
     567             :         return MAL_SUCCEED;
     568             : }
     569             : 
     570             : /*
     571             :  * The stages of processing user requests are controlled by a scenario.
     572             :  * The routines below are the default implementation.  The main issues
     573             :  * to deal after parsing it to clean out the Admin.main function from
     574             :  * any information added erroneously.
     575             :  *
     576             :  * Ideally this involves resetting the state of the client 'main'
     577             :  * function, i.e. the symbol table is reset and any instruction added
     578             :  * should be cleaned. Beware that the instruction table may have grown
     579             :  * in size.
     580             :  */
     581             : str
     582         360 : MALinitClient(Client c)
     583             : {
     584         360 :         assert(c->state[0] == NULL);
     585         360 :         c->state[0] = c;
     586         360 :         return NULL;
     587             : }
     588             : 
     589             : str
     590        5390 : MALexitClient(Client c)
     591             : {
     592        5390 :         if (c->glb && c->curprg->def->errors == MAL_SUCCEED)
     593        5389 :                 garbageCollector(c, c->curprg->def, c->glb, TRUE);
     594        5390 :         c->mode = FINISHCLIENT;
     595        5390 :         if (c->backup) {
     596           0 :                 assert(0);
     597             :                 freeSymbol(c->backup);
     598             :                 c->backup = NULL;
     599             :         }
     600             :         /* should be in the usermodule */
     601        5390 :         c->curprg = NULL;
     602        5390 :         if (c->usermodule){
     603        5390 :                 freeModule(c->usermodule);
     604        5390 :                 c->usermodule = NULL;
     605             :         }
     606        5390 :         return NULL;
     607             : }
     608             : 
     609             : str
     610       10201 : MALreader(Client c)
     611             : {
     612       10201 :         if (MCreadClient(c) > 0)
     613             :                 return MAL_SUCCEED;
     614         473 :         MT_lock_set(&mal_contextLock);
     615         473 :         c->mode = FINISHCLIENT;
     616         473 :         MT_lock_unset(&mal_contextLock);
     617         473 :         if (c->fdin)
     618         473 :                 c->fdin->buf[c->fdin->pos] = 0;
     619             :         return MAL_SUCCEED;
     620             : }
     621             : 
     622             : /* Before compiling a large string, it makes sense to allocate
     623             :  * approximately enough space to keep the intermediate
     624             :  * code. Otherwise, we end up with a repeated extend on the MAL block,
     625             :  * which really consumes a lot of memcpy resources. The average MAL
     626             :  * string length could been derived from the test cases. An error in
     627             :  * the estimate is more expensive than just counting the lines.
     628             :  */
     629             : static int
     630       11872 : prepareMalBlk(MalBlkPtr mb, str s)
     631             : {
     632             :         int cnt = STMT_INCREMENT;
     633             : 
     634       50704 :         while (s) {
     635       26960 :                 s = strchr(s, '\n');
     636       26960 :                 if (s) {
     637       15088 :                         s++;
     638       15088 :                         cnt++;
     639             :                 }
     640             :         }
     641       11872 :         cnt = (int) (cnt * 1.1);
     642       11872 :         return resizeMalBlk(mb, cnt);
     643             : }
     644             : 
     645             : str
     646       11872 : MALparser(Client c)
     647             : {
     648             :         InstrPtr p;
     649             :         str msg= MAL_SUCCEED;
     650             : 
     651       11872 :         assert(c->curprg->def->errors == NULL);
     652       11872 :         c->curprg->def->errors = 0;
     653             : 
     654       11872 :         if( prepareMalBlk(c->curprg->def, CURRENT(c)) < 0)
     655           0 :                 throw(MAL, "mal.parser", "Failed to prepare");
     656       11872 :         parseMAL(c, c->curprg, 0, INT_MAX, 0);
     657             : 
     658             :         /* now the parsing is done we should advance the stream */
     659       11872 :         c->fdin->pos += c->yycur;
     660       11872 :         c->yycur = 0;
     661             : 
     662             :         /* check for unfinished blocks */
     663       11872 :         if(!c->curprg->def->errors && c->blkmode)
     664             :                 return MAL_SUCCEED;
     665             :         /* empty files should be skipped as well */
     666       11509 :         if (c->curprg->def->stop == 1){
     667        2419 :                 if ( (msg =c->curprg->def->errors) )
     668          53 :                         c->curprg->def->errors = 0;
     669        2419 :                 return msg;
     670             :         }
     671             : 
     672        9090 :         p = getInstrPtr(c->curprg->def, 0);
     673        9090 :         if (p->token != FUNCTIONsymbol) {
     674             :                 msg =c->curprg->def->errors;
     675           0 :                 c->curprg->def->errors = 0;
     676           0 :                 MSresetStack(c, c->curprg->def, c->glb);
     677           0 :                 resetMalTypes(c->curprg->def, 1);
     678           0 :                 return msg;
     679             :         }
     680        9090 :         pushEndInstruction(c->curprg->def);
     681        9090 :         msg = chkProgram(c->usermodule, c->curprg->def);
     682        9090 :         if (msg !=MAL_SUCCEED || (msg =c->curprg->def->errors) ){
     683          46 :                 c->curprg->def->errors = 0;
     684          46 :                 MSresetStack(c, c->curprg->def, c->glb);
     685          46 :                 resetMalTypes(c->curprg->def, 1);
     686          46 :                 return msg;
     687             :         }
     688             :         return MAL_SUCCEED;
     689             : }
     690             : 
     691             : int
     692      128329 : MALcommentsOnly(MalBlkPtr mb)
     693             : {
     694             :         int i;
     695             : 
     696      128417 :         for (i = 1; i < mb->stop; i++)
     697      128417 :                 if (mb->stmt[i]->token != REMsymbol)
     698             :                         return 0;
     699             :         return 1;
     700             : }
     701             : 
     702             : str
     703         131 : MALcallback(Client c, str msg)
     704             : {
     705         131 :         if (msg) {
     706             :                 /* don't print exception decoration, just the message */
     707             :                 char *n = NULL;
     708             :                 char *o = msg;
     709         262 :                 while ((n = strchr(o, '\n')) != NULL) {
     710         131 :                         if (*o == '!')
     711           0 :                                 o++;
     712         131 :                         mnstr_printf(c->fdout, "!%.*s\n", (int) (n - o), o);
     713         131 :                         o = ++n;
     714             :                 }
     715         131 :                 if (*o != 0) {
     716          73 :                         if (*o == '!')
     717           5 :                                 o++;
     718          73 :                         mnstr_printf(c->fdout, "!%s\n", o);
     719             :                 }
     720         131 :                 freeException(msg);
     721             :         }
     722         131 :         return MAL_SUCCEED;
     723             : }
     724             : 
     725             : str
     726        9532 : MALengine(Client c)
     727             : {
     728             :         Symbol prg;
     729             :         str msg = MAL_SUCCEED;
     730             : 
     731        9532 :         if (c->blkmode)
     732             :                 return MAL_SUCCEED;
     733        9532 :         prg = c->curprg;
     734        9532 :         if (prg == NULL)
     735           0 :                 throw(SYNTAX, "mal.engine", SYNTAX_SIGNATURE);
     736        9532 :         if (prg->def == NULL)
     737           0 :                 throw(SYNTAX, "mal.engine", SYNTAX_SIGNATURE);
     738             : 
     739        9532 :         if (prg->def->errors != MAL_SUCCEED) {
     740             :                 msg = prg->def->errors;
     741           0 :                 prg->def->errors = NULL;
     742           0 :                 MSresetStack(c, c->curprg->def, c->glb);
     743           0 :                 resetMalTypes(c->curprg->def, 1);
     744           0 :                 return msg;
     745             :         }
     746        9532 :         if (prg->def->stop == 1 || MALcommentsOnly(prg->def))
     747         224 :                 return 0;   /* empty block */
     748        9308 :         if (c->glb) {
     749        9308 :                 if (prg->def && c->glb->stksize < prg->def->vsize){
     750           0 :                         c->glb = reallocGlobalStack(c->glb, prg->def->vsize);
     751           0 :                         if( c->glb == NULL)
     752           0 :                                 throw(MAL, "mal.engine", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     753             :                 }
     754        9308 :                 c->glb->stktop = prg->def->vtop;
     755        9308 :                 c->glb->blk = prg->def;
     756       18616 :                 c->glb->cmd = (c->itrace && c->itrace != 'C') ? 'n' : 0;
     757             :         }
     758             : 
     759             :         /*
     760             :          * In interactive mode we should avoid early garbage collection of values.
     761             :          * This can be controlled by the clean up control at the instruction level
     762             :          * and marking all non-temporary variables as being (potentially) used.
     763             :          */
     764        9308 :         if (c->glb) {
     765        9308 :                 c->glb->pcup = 0;
     766        9308 :                 c->glb->keepAlive = TRUE; /* no garbage collection */
     767             :         }
     768        9308 :         if (prg->def->errors == MAL_SUCCEED)
     769        9308 :                 msg = (str) runMAL(c, prg->def, 0, c->glb);
     770        9308 :         if (msg) {
     771             :                 /* ignore "internal" exceptions */
     772          33 :                 if (strstr(msg, "client.quit") ) {
     773           0 :                         freeException(msg);
     774             :                         msg = MAL_SUCCEED;
     775             :                 }
     776             :         }
     777        9308 :         MSresetStack(c, prg->def, c->glb);
     778        9308 :         resetMalTypes(prg->def, 1);
     779        9308 :         if (c->glb) {
     780             :                 /* for global stacks avoid reinitialization from this point */
     781        9308 :                 c->glb->stkbot = prg->def->vtop;
     782             :         }
     783             : 
     784        9308 :         if (prg->def->errors)
     785           0 :                 freeException(prg->def->errors);
     786        9308 :         prg->def->errors = NULL;
     787        9308 :         if (c->itrace)
     788           0 :                 mnstr_printf(c->fdout, "mdb>#EOD\n");
     789             :         return msg;
     790             : }

Generated by: LCOV version 1.14