Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : /* Author(s) M.L. Kersten
14 : * Module import
15 : * The import statement simple switches the parser to a new input file, which
16 : * takes precedence. The context for which the file should be interpreted
17 : * is determined by the module name supplied.
18 : * Typically this involves a module, whose definitions are stored at
19 : * a known location.
20 : * The import context is located. If the module already exists,
21 : * we should silently skip parsing the file. This is handled at the parser level.
22 : * The files are extracted from a default location,
23 : * namely the DBHOME/modules directory.
24 : *
25 : * If the string starts with '/' or '~' the context is not changed.
26 : *
27 : * Every IMPORT statement denotes a possible dynamic load library.
28 : * Make sure it is loaded as well.
29 : */
30 :
31 : #include "monetdb_config.h"
32 : #include "mal_import.h"
33 : #include "mal_interpreter.h" /* for showErrors() */
34 : #include "mal_linker.h" /* for loadModuleLibrary() */
35 : #include "mal_scenario.h"
36 : #include "mal_parser.h"
37 : #include "mal_authorize.h"
38 : #include "mal_private.h"
39 : #include "mal_internal.h"
40 : #include "mal_session.h"
41 : #include "mal_utils.h"
42 :
43 : void
44 1 : slash_2_dir_sep(str fname)
45 : {
46 1 : char *s;
47 :
48 15 : for (s = fname; *s; s++)
49 14 : if (*s == '/')
50 0 : *s = DIR_SEP;
51 1 : }
52 :
53 : static str
54 1 : malResolveFile(const char *fname)
55 : {
56 1 : char path[FILENAME_MAX];
57 1 : str script;
58 1 : int written;
59 :
60 1 : written = snprintf(path, FILENAME_MAX, "%s", fname);
61 1 : if (written == -1 || written >= FILENAME_MAX)
62 : return NULL;
63 1 : slash_2_dir_sep(path);
64 1 : if ((script = MSP_locate_script(path)) == NULL) {
65 : /* this function is also called for scripts that are not located
66 : * in the modpath, so if we can't find it, just default to
67 : * whatever was given, as it can be in current dir, or an
68 : * absolute location to somewhere */
69 1 : script = GDKstrdup(fname);
70 : }
71 : return script;
72 : }
73 :
74 : static stream *
75 1 : malOpenSource(str file)
76 : {
77 1 : stream *fd = NULL;
78 :
79 1 : if (file)
80 1 : fd = open_rastream(file);
81 1 : return fd;
82 : }
83 :
84 : /*
85 : * The malLoadScript routine merely reads the contents of a file into
86 : * the input buffer of the client. It is typically used in situations
87 : * where an intermediate file is used to pass commands around.
88 : * Since the parser needs access to the complete block, we first have
89 : * to find out how long the input is.
90 : */
91 : static str
92 1 : malLoadScript(str name, bstream **fdin)
93 : {
94 1 : stream *fd;
95 1 : size_t sz;
96 :
97 1 : fd = malOpenSource(name);
98 1 : if (fd == NULL || mnstr_errnr(fd) == MNSTR_OPEN_ERROR) {
99 1 : close_stream(fd);
100 1 : throw(MAL, "malInclude", "could not open file: %s: %s", name,
101 : mnstr_peek_error(NULL));
102 : }
103 0 : sz = getFileSize(fd);
104 0 : if (sz > (size_t) 1 << 29) {
105 0 : close_stream(fd);
106 0 : throw(MAL, "malInclude", "file %s too large to process", name);
107 : }
108 0 : *fdin = bstream_create(fd, sz == 0 ? (size_t) (2 * 128 * BLOCK) : sz);
109 0 : if (*fdin == NULL) {
110 0 : close_stream(fd);
111 0 : throw(MAL, "malInclude", SQLSTATE(HY013) MAL_MALLOC_FAIL);
112 : }
113 0 : if (bstream_next(*fdin) < 0) {
114 0 : bstream_destroy(*fdin);
115 0 : *fdin = NULL;
116 0 : throw(MAL, "malInclude", "could not read %s", name);
117 : }
118 : return MAL_SUCCEED;
119 : }
120 :
121 : /*
122 : * Beware that we have to isolate the execution of the source file
123 : * in its own environment. E.g. we have to remove the execution
124 : * state until we are finished.
125 : * The script being read may contain errors, such as non-balanced
126 : * brackets as indicated by blkmode.
127 : * It should be reset before continuing.
128 : */
129 : #define restoreClient1 \
130 : if (c->fdin) \
131 : bstream_destroy(c->fdin); \
132 : c->fdin = oldfdin; \
133 : c->qryctx.bs = oldfdin; \
134 : c->yycur = oldyycur; \
135 : c->listing = oldlisting; \
136 : c->mode = oldmode; \
137 : c->blkmode = oldblkmode; \
138 : c->bak = oldbak; \
139 : c->srcFile = oldsrcFile; \
140 : c->prompt = oldprompt; \
141 : c->promptlength = strlen(c->prompt);
142 : #define restoreClient2 \
143 : assert(c->glb == 0 || c->glb == oldglb); /* detect leak */ \
144 : c->glb = oldglb; \
145 : c->usermodule = oldusermodule; \
146 : c->curmodule = oldcurmodule; \
147 : c->curprg = oldprg;
148 : #define restoreClient \
149 : restoreClient1 \
150 : restoreClient2
151 :
152 : str
153 0 : malIncludeString(Client c, const char *name, str mal, int listing,
154 : MALfcn address)
155 : {
156 0 : str msg = MAL_SUCCEED;
157 :
158 0 : bstream *oldfdin = c->fdin;
159 0 : size_t oldyycur = c->yycur;
160 0 : int oldlisting = c->listing;
161 0 : enum clientmode oldmode = c->mode;
162 0 : int oldblkmode = c->blkmode;
163 0 : ClientInput *oldbak = c->bak;
164 0 : const char *oldprompt = c->prompt;
165 0 : const char *oldsrcFile = c->srcFile;
166 :
167 0 : MalStkPtr oldglb = c->glb;
168 0 : Module oldusermodule = c->usermodule;
169 0 : Module oldcurmodule = c->curmodule;
170 0 : Symbol oldprg = c->curprg;
171 :
172 0 : c->prompt = ""; /* do not produce visible prompts */
173 0 : c->promptlength = 0;
174 0 : c->listing = listing;
175 0 : c->fdin = NULL;
176 0 : c->qryctx.bs = NULL;
177 :
178 0 : size_t mal_len = strlen(mal);
179 0 : buffer *mal_buf;
180 0 : stream *mal_stream;
181 :
182 0 : if ((mal_buf = GDKmalloc(sizeof(buffer))) == NULL)
183 0 : throw(MAL, "malIncludeString", SQLSTATE(HY013) MAL_MALLOC_FAIL);
184 0 : if ((mal_stream = buffer_rastream(mal_buf, name)) == NULL) {
185 0 : GDKfree(mal_buf);
186 0 : throw(MAL, "malIncludeString", SQLSTATE(HY013) MAL_MALLOC_FAIL);
187 : }
188 0 : buffer_init(mal_buf, mal, mal_len);
189 0 : c->srcFile = name;
190 0 : c->yycur = 0;
191 0 : c->bak = NULL;
192 0 : if ((c->fdin = bstream_create(mal_stream, mal_len)) == NULL) {
193 0 : mnstr_destroy(mal_stream);
194 0 : GDKfree(mal_buf);
195 0 : throw(MAL, "malIncludeString", SQLSTATE(HY013) MAL_MALLOC_FAIL);
196 : }
197 0 : c->qryctx.bs = c->fdin;
198 0 : bstream_next(c->fdin);
199 0 : parseMAL(c, c->curprg, 1, INT_MAX, address);
200 0 : bstream_destroy(c->fdin);
201 0 : c->fdin = NULL;
202 0 : c->qryctx.bs = NULL;
203 0 : GDKfree(mal_buf);
204 :
205 0 : restoreClient;
206 0 : return msg;
207 : }
208 :
209 : /*
210 : * The include operation parses the file indentified and
211 : * leaves the MAL code behind in the 'main' function.
212 : */
213 : str
214 1 : malInclude(Client c, const char *name, int listing)
215 : {
216 1 : str msg = MAL_SUCCEED;
217 1 : str filename;
218 1 : str p;
219 :
220 1 : bstream *oldfdin = c->fdin;
221 1 : size_t oldyycur = c->yycur;
222 1 : int oldlisting = c->listing;
223 1 : enum clientmode oldmode = c->mode;
224 1 : int oldblkmode = c->blkmode;
225 1 : ClientInput *oldbak = c->bak;
226 1 : const char *oldprompt = c->prompt;
227 1 : const char *oldsrcFile = c->srcFile;
228 :
229 1 : MalStkPtr oldglb = c->glb;
230 1 : Module oldusermodule = c->usermodule;
231 1 : Module oldcurmodule = c->curmodule;
232 1 : Symbol oldprg = c->curprg;
233 :
234 1 : c->prompt = ""; /* do not produce visible prompts */
235 1 : c->promptlength = 0;
236 1 : c->listing = listing;
237 1 : c->fdin = NULL;
238 1 : c->qryctx.bs = NULL;
239 :
240 1 : if ((filename = malResolveFile(name)) != NULL) {
241 : char *fname = filename;
242 1 : do {
243 1 : p = strchr(filename, PATH_SEP);
244 1 : if (p)
245 0 : *p = '\0';
246 1 : c->srcFile = filename;
247 1 : c->yycur = 0;
248 1 : c->bak = NULL;
249 1 : if ((msg = malLoadScript(filename, &c->fdin)) == MAL_SUCCEED) {
250 0 : parseMAL(c, c->curprg, 1, INT_MAX, 0);
251 0 : bstream_destroy(c->fdin);
252 : } else {
253 : /* TODO output msg ? */
254 1 : freeException(msg);
255 1 : msg = MAL_SUCCEED;
256 : }
257 1 : if (p)
258 0 : filename = p + 1;
259 1 : } while (p);
260 1 : GDKfree(fname);
261 1 : c->fdin = NULL;
262 1 : c->qryctx.bs = NULL;
263 : }
264 1 : restoreClient;
265 1 : return msg;
266 : }
267 :
268 :
269 : /* patch a newline character if needed */
270 : static str
271 0 : mal_cmdline(char *s, size_t *len)
272 : {
273 0 : if (*len && s[*len - 1] != '\n') {
274 0 : char *n = GDKmalloc(*len + 2);
275 0 : if (n == NULL)
276 : return s;
277 0 : memcpy(n, s, *len);
278 0 : n[*len] = '\n';
279 0 : n[*len + 1] = 0;
280 0 : (*len)++;
281 0 : return n;
282 : }
283 : return s;
284 : }
285 :
286 : str
287 0 : compileString(Symbol *fcn, Client cntxt, str s)
288 : {
289 0 : Client c;
290 0 : QryCtx *qc_old;
291 0 : size_t len = strlen(s);
292 0 : buffer *b;
293 0 : str msg = MAL_SUCCEED;
294 0 : str qry;
295 0 : str old = s;
296 0 : stream *bs;
297 0 : bstream *fdin = NULL;
298 :
299 0 : s = mal_cmdline(s, &len);
300 0 : qry = s;
301 0 : if (old == s) {
302 0 : qry = GDKstrdup(s);
303 0 : if (!qry)
304 0 : throw(MAL, "mal.eval", SQLSTATE(HY013) MAL_MALLOC_FAIL);
305 : }
306 :
307 0 : mal_unquote(qry);
308 0 : b = (buffer *) GDKzalloc(sizeof(buffer));
309 0 : if (b == NULL) {
310 0 : GDKfree(qry);
311 0 : throw(MAL, "mal.eval", SQLSTATE(HY013) MAL_MALLOC_FAIL);
312 : }
313 :
314 0 : buffer_init(b, qry, len);
315 0 : bs = buffer_rastream(b, "compileString");
316 0 : if (bs == NULL) {
317 0 : GDKfree(qry);
318 0 : GDKfree(b);
319 0 : throw(MAL, "mal.eval", SQLSTATE(HY013) MAL_MALLOC_FAIL);
320 : }
321 0 : fdin = bstream_create(bs, b->len);
322 0 : if (fdin == NULL) {
323 0 : GDKfree(qry);
324 0 : GDKfree(b);
325 0 : throw(MAL, "mal.eval", SQLSTATE(HY013) MAL_MALLOC_FAIL);
326 : }
327 0 : strncpy(fdin->buf, qry, len + 1);
328 :
329 0 : qc_old = MT_thread_get_qry_ctx();
330 : // compile in context of called for
331 0 : c = MCinitClient(MAL_ADMIN, fdin, 0);
332 0 : if (c == NULL) {
333 0 : GDKfree(qry);
334 0 : GDKfree(b);
335 0 : MT_thread_set_qry_ctx(qc_old);
336 0 : throw(MAL, "mal.eval", "Can not create user context");
337 : }
338 0 : c->curmodule = c->usermodule = cntxt->usermodule;
339 0 : c->promptlength = 0;
340 0 : c->listing = 0;
341 :
342 0 : if ((msg = defaultScenario(c))) {
343 0 : GDKfree(qry);
344 0 : GDKfree(b);
345 0 : c->usermodule = 0;
346 0 : MCcloseClient(c);
347 0 : MT_thread_set_qry_ctx(qc_old);
348 0 : return msg;
349 : }
350 :
351 0 : msg = MSinitClientPrg(c, "user", "main"); /* create new context */
352 0 : if (msg == MAL_SUCCEED)
353 0 : msg = MALparser(c);
354 : /*
355 : if(msg == MAL_SUCCEED && c->phase[MAL_SCENARIO_PARSER])
356 : msg = (str) (*c->phase[MAL_SCENARIO_PARSER])(c);
357 : if(msg == MAL_SUCCEED && c->phase[MAL_SCENARIO_OPTIMIZE])
358 : msg = (str) (*c->phase[MAL_SCENARIO_OPTIMIZE])(c);
359 : */
360 :
361 0 : *fcn = c->curprg;
362 0 : c->curprg = 0;
363 0 : c->usermodule = 0;
364 : /* restore IO channel */
365 0 : MCcloseClient(c);
366 0 : MT_thread_set_qry_ctx(qc_old);
367 0 : GDKfree(qry);
368 0 : GDKfree(b);
369 0 : return msg;
370 : }
|