LCOV - code coverage report
Current view: top level - monetdb5/mal - mal_import.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 166 259 64.1 %
Date: 2021-09-14 22:17:06 Functions: 8 9 88.9 %

          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(s) M.L. Kersten
      10             :  * Module import
      11             :  * The import statement simple switches the parser to a new input file, which
      12             :  * takes precedence. The context for which the file should be interpreted
      13             :  * is determined by the module name supplied.
      14             :  * Typically this involves a module, whose definitions are stored at
      15             :  * a known location.
      16             :  * The import context is located. If the module already exists,
      17             :  * we should silently skip parsing the file. This is handled at the parser level.
      18             :  * The files are extracted from a default location,
      19             :  * namely the DBHOME/modules directory.
      20             :  *
      21             :  * If the string starts with '/' or '~' the context is not changed.
      22             :  *
      23             :  * Every IMPORT statement denotes a possible dynamic load library.
      24             :  * Make sure it is loaded as well.
      25             : */
      26             : 
      27             : #include "monetdb_config.h"
      28             : #include "mal_import.h"
      29             : #include "mal_interpreter.h"  /* for showErrors() */
      30             : #include "mal_linker.h"               /* for loadModuleLibrary() */
      31             : #include "mal_scenario.h"
      32             : #include "mal_parser.h"
      33             : #include "mal_authorize.h"
      34             : #include "mal_private.h"
      35             : #include "mal_session.h"
      36             : #include "mal_utils.h"
      37             : 
      38             : void
      39           5 : slash_2_dir_sep(str fname)
      40             : {
      41             :         char *s;
      42             : 
      43          55 :         for (s = fname; *s; s++)
      44          50 :                 if (*s == '/')
      45           0 :                         *s = DIR_SEP;
      46           5 : }
      47             : 
      48             : static str
      49           5 : malResolveFile(const char *fname)
      50             : {
      51             :         char path[FILENAME_MAX];
      52             :         str script;
      53             :         int written;
      54             : 
      55           5 :         written = snprintf(path, FILENAME_MAX, "%s", fname);
      56           5 :         if (written == -1 || written >= FILENAME_MAX)
      57             :                 return NULL;
      58           5 :         slash_2_dir_sep(path);
      59           5 :         if ((script = MSP_locate_script(path)) == NULL) {
      60             :                 /* this function is also called for scripts that are not located
      61             :                  * in the modpath, so if we can't find it, just default to
      62             :                  * whatever was given, as it can be in current dir, or an
      63             :                  * absolute location to somewhere */
      64           1 :                 script = GDKstrdup(fname);
      65             :         }
      66             :         return script;
      67             : }
      68             : 
      69             : static stream *
      70             : malOpenSource(str file)
      71             : {
      72             :         stream *fd = NULL;
      73             : 
      74           5 :         if (file)
      75           5 :                 fd = open_rastream(file);
      76             :         return fd;
      77             : }
      78             : 
      79             : /*
      80             :  * The malLoadScript routine merely reads the contents of a file into
      81             :  * the input buffer of the client. It is typically used in situations
      82             :  * where an intermediate file is used to pass commands around.
      83             :  * Since the parser needs access to the complete block, we first have
      84             :  * to find out how long the input is.
      85             : */
      86             : static str
      87           5 : malLoadScript(str name, bstream **fdin)
      88             : {
      89             :         stream *fd;
      90             :         size_t sz;
      91             : 
      92             :         fd = malOpenSource(name);
      93           5 :         if (fd == NULL || mnstr_errnr(fd) == MNSTR_OPEN_ERROR) {
      94           1 :                 close_stream(fd);
      95           1 :                 throw(MAL, "malInclude", "could not open file: %s: %s", name, mnstr_peek_error(NULL));
      96             :         }
      97           4 :         sz = getFileSize(fd);
      98           4 :         if (sz > (size_t) 1 << 29) {
      99           0 :                 close_stream(fd);
     100           0 :                 throw(MAL, "malInclude", "file %s too large to process", name);
     101             :         }
     102           4 :         *fdin = bstream_create(fd, sz == 0 ? (size_t) (2 * 128 * BLOCK) : sz);
     103           4 :         if(*fdin == NULL) {
     104           0 :                 close_stream(fd);
     105           0 :                 throw(MAL, "malInclude", MAL_MALLOC_FAIL);
     106             :         }
     107           4 :         if (bstream_next(*fdin) < 0) {
     108           0 :                 bstream_destroy(*fdin);
     109           0 :                 *fdin = NULL;
     110           0 :                 throw(MAL, "malInclude", "could not read %s", name);
     111             :         }
     112             :         return MAL_SUCCEED;
     113             : }
     114             : 
     115             : /*
     116             :  * Beware that we have to isolate the execution of the source file
     117             :  * in its own environment. E.g. we have to remove the execution
     118             :  * state until we are finished.
     119             :  * The script being read may contain errors, such as non-balanced
     120             :  * brackets as indicated by blkmode.
     121             :  * It should be reset before continuing.
     122             : */
     123             : #define restoreClient1 \
     124             :         if (c->fdin)  \
     125             :                 bstream_destroy(c->fdin); \
     126             :         c->fdin = oldfdin;  \
     127             :         c->yycur = oldyycur;  \
     128             :         c->listing = oldlisting; \
     129             :         c->mode = oldmode; \
     130             :         c->blkmode = oldblkmode; \
     131             :         c->bak = oldbak; \
     132             :         c->srcFile = oldsrcFile; \
     133             :         if(c->prompt) GDKfree(c->prompt); \
     134             :         c->prompt = oldprompt; \
     135             :         c->promptlength = strlen(c->prompt);
     136             : #define restoreClient2 \
     137             :         assert(c->glb == 0 || c->glb == oldglb); /* detect leak */ \
     138             :         c->glb = oldglb; \
     139             :         c->usermodule = oldusermodule; \
     140             :         c->curmodule = oldcurmodule; \
     141             :         c->curprg = oldprg;
     142             : #define restoreClient \
     143             :         restoreClient1 \
     144             :         restoreClient2
     145             : 
     146             : str
     147         254 : malIncludeString(Client c, const char *name, str mal, int listing, MALfcn address)
     148             : {
     149             :         str msg = MAL_SUCCEED;
     150             : 
     151         254 :         bstream *oldfdin = c->fdin;
     152         254 :         size_t oldyycur = c->yycur;
     153         254 :         int oldlisting = c->listing;
     154         254 :         enum clientmode oldmode = c->mode;
     155         254 :         int oldblkmode = c->blkmode;
     156         254 :         ClientInput *oldbak = c->bak;
     157         254 :         str oldprompt = c->prompt;
     158         254 :         const char *oldsrcFile = c->srcFile;
     159             : 
     160         254 :         MalStkPtr oldglb = c->glb;
     161         254 :         Module oldusermodule = c->usermodule;
     162         254 :         Module oldcurmodule = c->curmodule;
     163         254 :         Symbol oldprg = c->curprg;
     164             : 
     165         254 :         c->prompt = GDKstrdup(""); /* do not produce visible prompts */
     166         254 :         c->promptlength = 0;
     167         254 :         c->listing = listing;
     168         254 :         c->fdin = NULL;
     169             : 
     170         254 :         size_t mal_len = strlen(mal);
     171             :         buffer* mal_buf;
     172             :         stream* mal_stream;
     173             : 
     174         254 :         if ((mal_buf = GDKmalloc(sizeof(buffer))) == NULL)
     175           0 :                 throw(MAL, "malIncludeString", MAL_MALLOC_FAIL);
     176         254 :         if ((mal_stream = buffer_rastream(mal_buf, name)) == NULL) {
     177           0 :                 GDKfree(mal_buf);
     178           0 :                 throw(MAL, "malIncludeString", MAL_MALLOC_FAIL);
     179             :         }
     180         254 :         buffer_init(mal_buf, mal, mal_len);
     181         254 :         c->srcFile = name;
     182         254 :         c->yycur = 0;
     183         254 :         c->bak = NULL;
     184         254 :         if ((c->fdin = bstream_create(mal_stream, mal_len)) == NULL) {
     185           0 :                 mnstr_destroy(mal_stream);
     186           0 :                 GDKfree(mal_buf);
     187           0 :                 throw(MAL, "malIncludeString", MAL_MALLOC_FAIL);
     188             :         }
     189         254 :         bstream_next(c->fdin);
     190         254 :         parseMAL(c, c->curprg, 1, INT_MAX, address);
     191         254 :         bstream_destroy(c->fdin);
     192         254 :         c->fdin = NULL;
     193         254 :         GDKfree(mal_buf);
     194             : 
     195         254 :         restoreClient;
     196         254 :         return msg;
     197             : }
     198             : 
     199             : /*
     200             :  * The include operation parses the file indentified and
     201             :  * leaves the MAL code behind in the 'main' function.
     202             :  */
     203             : str
     204           5 : malInclude(Client c, const char *name, int listing)
     205             : {
     206             :         str msg = MAL_SUCCEED;
     207             :         str filename;
     208             :         str p;
     209             : 
     210           5 :         bstream *oldfdin = c->fdin;
     211           5 :         size_t oldyycur = c->yycur;
     212           5 :         int oldlisting = c->listing;
     213           5 :         enum clientmode oldmode = c->mode;
     214           5 :         int oldblkmode = c->blkmode;
     215           5 :         ClientInput *oldbak = c->bak;
     216           5 :         str oldprompt = c->prompt;
     217           5 :         const char *oldsrcFile = c->srcFile;
     218             : 
     219           5 :         MalStkPtr oldglb = c->glb;
     220           5 :         Module oldusermodule = c->usermodule;
     221           5 :         Module oldcurmodule = c->curmodule;
     222           5 :         Symbol oldprg = c->curprg;
     223             : 
     224           5 :         c->prompt = GDKstrdup(""); /* do not produce visible prompts */
     225           5 :         c->promptlength = 0;
     226           5 :         c->listing = listing;
     227           5 :         c->fdin = NULL;
     228             : 
     229           5 :         if ((filename = malResolveFile(name)) != NULL) {
     230             :                 char *fname = filename;
     231             :                 do {
     232           5 :                         p = strchr(filename, PATH_SEP);
     233           5 :                         if (p)
     234           0 :                                 *p = '\0';
     235           5 :                         c->srcFile = filename;
     236           5 :                         c->yycur = 0;
     237           5 :                         c->bak = NULL;
     238           5 :                         if ((msg = malLoadScript(filename, &c->fdin)) == MAL_SUCCEED) {
     239           4 :                                 parseMAL(c, c->curprg, 1, INT_MAX, 0);
     240           4 :                                 bstream_destroy(c->fdin);
     241             :                         } else {
     242             :                                 /* TODO output msg ? */
     243           1 :                                 freeException(msg);
     244             :                                 msg = MAL_SUCCEED;
     245             :                         }
     246           5 :                         if (p)
     247           0 :                                 filename = p + 1;
     248           5 :                 } while (p);
     249           5 :                 GDKfree(fname);
     250           5 :                 c->fdin = NULL;
     251             :         }
     252           5 :         restoreClient;
     253           5 :         return msg;
     254             : }
     255             : 
     256             : /*File and input processing
     257             :  * A recurring situation is to execute a stream of simple MAL instructions
     258             :  * stored on a file or comes from standard input. We parse one MAL
     259             :  * instruction line at a time and attempt to execute it immediately.
     260             :  * Note, this precludes entering complex MAL structures on the primary
     261             :  * input channel, because 1) this requires complex code to keep track
     262             :  * that we are in 'definition mode' 2) this requires (too) careful
     263             :  * typing by the user, because he cannot make a typing error
     264             :  *
     265             :  * Therefore, all compound code fragments should be loaded and executed
     266             :  * using the evalFile and callString command. It will parse the complete
     267             :  * file into a MAL program block and execute it.
     268             :  *
     269             :  * Running looks much like an Import operation, except for the execution
     270             :  * phase. This is performed in the context of an a priori defined
     271             :  * stack frame. Life becomes a little complicated when the script contains
     272             :  * a definition.
     273             :  */
     274             : str
     275           0 : evalFile(str fname, int listing)
     276             : {
     277             :         Client c;
     278             :         stream *fd;
     279             :         str filename;
     280             :         str msg = MAL_SUCCEED;
     281             :         bstream *bs;
     282             : 
     283           0 :         filename = malResolveFile(fname);
     284           0 :         if (filename == NULL)
     285           0 :                 throw(MAL, "mal.eval","could not open file: %s\n", fname);
     286             :         fd = malOpenSource(filename);
     287           0 :         GDKfree(filename);
     288           0 :         if (fd == 0 || mnstr_errnr(fd) == MNSTR_OPEN_ERROR) {
     289           0 :                 if (fd)
     290           0 :                         close_stream(fd);
     291           0 :                 throw(MAL,"mal.eval", "WARNING: could not open file '%s'\n", fname);
     292             :         }
     293           0 :         if (!(bs = bstream_create(fd, 128 * BLOCK))) {
     294             :                 if (fd)
     295           0 :                         close_stream(fd);
     296           0 :                 throw(MAL,"mal.eval",SQLSTATE(HY013) MAL_MALLOC_FAIL);
     297             :         }
     298           0 :         c= MCinitClient(MAL_ADMIN, bs, 0);
     299           0 :         if( c == NULL){
     300           0 :                 throw(MAL,"mal.eval","Can not create user context");
     301             :         }
     302           0 :         c->curmodule = c->usermodule = userModule();
     303           0 :         if(c->curmodule == NULL) {
     304           0 :                 MCcloseClient(c);
     305           0 :                 throw(MAL,"mal.eval",SQLSTATE(HY013) MAL_MALLOC_FAIL);
     306             :         }
     307           0 :         c->promptlength = 0;
     308           0 :         c->listing = listing;
     309             : 
     310           0 :         if ( (msg = defaultScenario(c)) ) {
     311           0 :                 MCcloseClient(c);
     312           0 :                 return msg;
     313             :         }
     314           0 :         if((msg = MSinitClientPrg(c, "user", "main")) != MAL_SUCCEED) {
     315           0 :                 MCcloseClient(c);
     316           0 :                 return msg;
     317             :         }
     318             : 
     319           0 :         msg = runScenario(c,0);
     320           0 :         MCcloseClient(c);
     321           0 :         return msg;
     322             : }
     323             : 
     324             : /* patch a newline character if needed */
     325             : static str
     326        2154 : mal_cmdline(char *s, size_t *len)
     327             : {
     328        2154 :         if (*len && s[*len - 1] != '\n') {
     329        2153 :                 char *n = GDKmalloc(*len + 2);
     330        2153 :                 if (n == NULL)
     331             :                         return s;
     332        2153 :                 memcpy(n, s, *len);
     333        2153 :                 n[*len] = '\n';
     334        2153 :                 n[*len + 1] = 0;
     335        2153 :                 (*len)++;
     336        2153 :                 return n;
     337             :         }
     338             :         return s;
     339             : }
     340             : 
     341             : str
     342        2144 : compileString(Symbol *fcn, Client cntxt, str s)
     343             : {
     344             :         Client c;
     345        2144 :         size_t len = strlen(s);
     346             :         buffer *b;
     347             :         str msg = MAL_SUCCEED;
     348             :         str qry;
     349             :         str old = s;
     350             :         stream *bs;
     351             :         bstream *fdin = NULL;
     352             : 
     353        2144 :         s = mal_cmdline(s, &len);
     354             :         qry = s;
     355        2144 :         if (old == s) {
     356           0 :                 qry = GDKstrdup(s);
     357           0 :                 if(!qry)
     358           0 :                         throw(MAL,"mal.eval",SQLSTATE(HY013) MAL_MALLOC_FAIL);
     359             :         }
     360             : 
     361        2144 :         mal_unquote(qry);
     362        2144 :         b = (buffer *) GDKzalloc(sizeof(buffer));
     363        2144 :         if (b == NULL) {
     364           0 :                 GDKfree(qry);
     365           0 :                 throw(MAL,"mal.eval",SQLSTATE(HY013) MAL_MALLOC_FAIL);
     366             :         }
     367             : 
     368        2144 :         buffer_init(b, qry, len);
     369        2144 :         bs = buffer_rastream(b, "compileString");
     370        2144 :         if (bs == NULL) {
     371           0 :                 GDKfree(qry);
     372           0 :                 GDKfree(b);
     373           0 :                 throw(MAL,"mal.eval",SQLSTATE(HY013) MAL_MALLOC_FAIL);
     374             :         }
     375        2144 :         fdin = bstream_create(bs, b->len);
     376        2144 :         if (fdin == NULL) {
     377           0 :                 GDKfree(qry);
     378           0 :                 GDKfree(b);
     379           0 :                 throw(MAL,"mal.eval",SQLSTATE(HY013) MAL_MALLOC_FAIL);
     380             :         }
     381        2144 :         strncpy(fdin->buf, qry, len+1);
     382             : 
     383             :         // compile in context of called for
     384        2144 :         c= MCinitClient(MAL_ADMIN, fdin, 0);
     385        2144 :         if( c == NULL){
     386           0 :                 GDKfree(qry);
     387           0 :                 GDKfree(b);
     388           0 :                 throw(MAL,"mal.eval","Can not create user context");
     389             :         }
     390        2144 :         c->curmodule = c->usermodule = cntxt->usermodule;
     391        2144 :         c->promptlength = 0;
     392        2144 :         c->listing = 0;
     393             : 
     394        2144 :         if ( (msg = defaultScenario(c)) ) {
     395           0 :                 GDKfree(qry);
     396           0 :                 GDKfree(b);
     397           0 :                 c->usermodule= 0;
     398           0 :                 MCcloseClient(c);
     399           0 :                 return msg;
     400             :         }
     401             : 
     402        2144 :         msg = MSinitClientPrg(c, "user", "main");/* create new context */
     403        2144 :         if(msg == MAL_SUCCEED && c->phase[MAL_SCENARIO_PARSER])
     404        2144 :                 msg = (str) (*c->phase[MAL_SCENARIO_PARSER])(c);
     405        2144 :         if(msg == MAL_SUCCEED && c->phase[MAL_SCENARIO_OPTIMIZE])
     406        2143 :                 msg = (str) (*c->phase[MAL_SCENARIO_OPTIMIZE])(c);
     407             : 
     408        2144 :         *fcn = c->curprg;
     409        2144 :         c->curprg = 0;
     410        2144 :         c->usermodule= 0;
     411             :         /* restore IO channel */
     412        2144 :         MCcloseClient(c);
     413        2144 :         GDKfree(qry);
     414        2144 :         GDKfree(b);
     415        2144 :         return msg;
     416             : }
     417             : 
     418             : str
     419          10 : callString(Client cntxt, str s, int listing)
     420             : {
     421             :         Client c;
     422             :         int i;
     423          10 :         size_t len = strlen(s);
     424             :         buffer *b;
     425             :         str old =s;
     426             :         str msg = MAL_SUCCEED, qry;
     427             :         bstream *bs;
     428             : 
     429          10 :         s = mal_cmdline(s, &len);
     430             :         qry = s;
     431          10 :         if (old == s) {
     432           1 :                 qry = GDKstrdup(s);
     433           1 :                 if(!qry)
     434           0 :                         throw(MAL,"callstring", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     435             :         }
     436             : 
     437          10 :         mal_unquote(qry);
     438          10 :         b = (buffer *) GDKzalloc(sizeof(buffer));
     439          10 :         if (b == NULL){
     440           0 :                 GDKfree(qry);
     441           0 :                 throw(MAL,"callstring", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     442             :         }
     443             : 
     444          10 :         buffer_init(b, qry, len);
     445          10 :         bs = bstream_create(buffer_rastream(b, "callString"), b->len);
     446          10 :         if (bs == NULL){
     447           0 :                 GDKfree(b);
     448           0 :                 GDKfree(qry);
     449           0 :                 throw(MAL,"callstring", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     450             :         }
     451          10 :         c= MCinitClient(MAL_ADMIN, bs, cntxt->fdout);
     452          10 :         if( c == NULL){
     453           0 :                 GDKfree(b);
     454           0 :                 GDKfree(qry);
     455           0 :                 throw(MAL,"mal.call","Can not create user context");
     456             :         }
     457          10 :         strncpy(c->fdin->buf, qry, len+1);
     458          10 :         c->curmodule = c->usermodule =  cntxt->usermodule;
     459          10 :         c->promptlength = 0;
     460          10 :         c->listing = listing;
     461             : 
     462          10 :         if ( (msg = defaultScenario(c)) ) {
     463           0 :                 c->usermodule = 0;
     464           0 :                 GDKfree(b);
     465           0 :                 GDKfree(qry);
     466           0 :                 MCcloseClient(c);
     467           0 :                 return msg;
     468             :         }
     469             : 
     470          10 :         if((msg = MSinitClientPrg(c, "user", "main")) != MAL_SUCCEED) {/* create new context */
     471           0 :                 c->usermodule = 0;
     472           0 :                 GDKfree(b);
     473           0 :                 GDKfree(qry);
     474           0 :                 c->fdout = GDKstdout;
     475           0 :                 MCcloseClient(c);
     476           0 :                 return msg;
     477             :         }
     478          10 :         msg = runScenario(c,1);
     479          10 :         c->fdout = GDKstdout;
     480          10 :         if (msg != MAL_SUCCEED) {
     481           0 :                 c->usermodule = 0;
     482           0 :                 GDKfree(b);
     483           0 :                 GDKfree(qry);
     484           0 :                 MCcloseClient(c);
     485           0 :                 return msg;
     486             :         }
     487             :         // The command may have changed the environment of the calling client.
     488             :         // These settings should be propagated for further use.
     489             :         //if( msg == MAL_SUCCEED){
     490          10 :                 cntxt->scenario = c->scenario;
     491          10 :                 c->scenario = 0;
     492          10 :                 cntxt->sqlcontext = c->sqlcontext;
     493          10 :                 c->sqlcontext = 0;
     494          80 :                 for(i=1; i< SCENARIO_PROPERTIES; i++){
     495          70 :                         cntxt->state[i] = c->state[i];
     496          70 :                         c->state[i]  = 0;
     497          70 :                         cntxt->phase[i] = c->phase[i];
     498          70 :                         c->phase[i]  = 0;
     499             :                 }
     500          10 :                 if(msg == MAL_SUCCEED && cntxt->phase[0] != c->phase[0]){
     501           0 :                         cntxt->phase[0] = c->phase[0];
     502           0 :                         cntxt->state[0] = c->state[0];
     503           0 :                         msg = (str) (*cntxt->phase[0])(cntxt);       // force re-initialize client context
     504             :                 }
     505             :         //}
     506          10 :         c->usermodule = 0; // keep it around
     507          10 :         bstream_destroy(c->fdin);
     508          10 :         c->fdin = 0;
     509          10 :         MCcloseClient(c);
     510          10 :         GDKfree(qry);
     511          10 :         GDKfree(b);
     512          10 :         return msg;
     513             : }

Generated by: LCOV version 1.14