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->yycur = oldyycur; \
134 : c->listing = oldlisting; \
135 : c->mode = oldmode; \
136 : c->blkmode = oldblkmode; \
137 : c->bak = oldbak; \
138 : c->srcFile = oldsrcFile; \
139 : c->prompt = oldprompt; \
140 : c->promptlength = strlen(c->prompt);
141 : #define restoreClient2 \
142 : assert(c->glb == 0 || c->glb == oldglb); /* detect leak */ \
143 : c->glb = oldglb; \
144 : c->usermodule = oldusermodule; \
145 : c->curmodule = oldcurmodule; \
146 : c->curprg = oldprg;
147 : #define restoreClient \
148 : restoreClient1 \
149 : restoreClient2
150 :
151 : str
152 0 : malIncludeString(Client c, const char *name, str mal, int listing,
153 : MALfcn address)
154 : {
155 0 : str msg = MAL_SUCCEED;
156 :
157 0 : bstream *oldfdin = c->fdin;
158 0 : size_t oldyycur = c->yycur;
159 0 : int oldlisting = c->listing;
160 0 : enum clientmode oldmode = c->mode;
161 0 : int oldblkmode = c->blkmode;
162 0 : ClientInput *oldbak = c->bak;
163 0 : const char *oldprompt = c->prompt;
164 0 : const char *oldsrcFile = c->srcFile;
165 :
166 0 : MalStkPtr oldglb = c->glb;
167 0 : Module oldusermodule = c->usermodule;
168 0 : Module oldcurmodule = c->curmodule;
169 0 : Symbol oldprg = c->curprg;
170 :
171 0 : c->prompt = ""; /* do not produce visible prompts */
172 0 : c->promptlength = 0;
173 0 : c->listing = listing;
174 0 : c->fdin = NULL;
175 :
176 0 : size_t mal_len = strlen(mal);
177 0 : buffer *mal_buf;
178 0 : stream *mal_stream;
179 :
180 0 : if ((mal_buf = GDKmalloc(sizeof(buffer))) == NULL)
181 0 : throw(MAL, "malIncludeString", SQLSTATE(HY013) MAL_MALLOC_FAIL);
182 0 : if ((mal_stream = buffer_rastream(mal_buf, name)) == NULL) {
183 0 : GDKfree(mal_buf);
184 0 : throw(MAL, "malIncludeString", SQLSTATE(HY013) MAL_MALLOC_FAIL);
185 : }
186 0 : buffer_init(mal_buf, mal, mal_len);
187 0 : c->srcFile = name;
188 0 : c->yycur = 0;
189 0 : c->bak = NULL;
190 0 : if ((c->fdin = bstream_create(mal_stream, mal_len)) == NULL) {
191 0 : mnstr_destroy(mal_stream);
192 0 : GDKfree(mal_buf);
193 0 : throw(MAL, "malIncludeString", SQLSTATE(HY013) MAL_MALLOC_FAIL);
194 : }
195 0 : bstream_next(c->fdin);
196 0 : parseMAL(c, c->curprg, 1, INT_MAX, address);
197 0 : bstream_destroy(c->fdin);
198 0 : c->fdin = NULL;
199 0 : GDKfree(mal_buf);
200 :
201 0 : restoreClient;
202 0 : return msg;
203 : }
204 :
205 : /*
206 : * The include operation parses the file indentified and
207 : * leaves the MAL code behind in the 'main' function.
208 : */
209 : str
210 1 : malInclude(Client c, const char *name, int listing)
211 : {
212 1 : str msg = MAL_SUCCEED;
213 1 : str filename;
214 1 : str p;
215 :
216 1 : bstream *oldfdin = c->fdin;
217 1 : size_t oldyycur = c->yycur;
218 1 : int oldlisting = c->listing;
219 1 : enum clientmode oldmode = c->mode;
220 1 : int oldblkmode = c->blkmode;
221 1 : ClientInput *oldbak = c->bak;
222 1 : const char *oldprompt = c->prompt;
223 1 : const char *oldsrcFile = c->srcFile;
224 :
225 1 : MalStkPtr oldglb = c->glb;
226 1 : Module oldusermodule = c->usermodule;
227 1 : Module oldcurmodule = c->curmodule;
228 1 : Symbol oldprg = c->curprg;
229 :
230 1 : c->prompt = ""; /* do not produce visible prompts */
231 1 : c->promptlength = 0;
232 1 : c->listing = listing;
233 1 : c->fdin = NULL;
234 :
235 1 : if ((filename = malResolveFile(name)) != NULL) {
236 : char *fname = filename;
237 1 : do {
238 1 : p = strchr(filename, PATH_SEP);
239 1 : if (p)
240 0 : *p = '\0';
241 1 : c->srcFile = filename;
242 1 : c->yycur = 0;
243 1 : c->bak = NULL;
244 1 : if ((msg = malLoadScript(filename, &c->fdin)) == MAL_SUCCEED) {
245 0 : parseMAL(c, c->curprg, 1, INT_MAX, 0);
246 0 : bstream_destroy(c->fdin);
247 : } else {
248 : /* TODO output msg ? */
249 1 : freeException(msg);
250 1 : msg = MAL_SUCCEED;
251 : }
252 1 : if (p)
253 0 : filename = p + 1;
254 1 : } while (p);
255 1 : GDKfree(fname);
256 1 : c->fdin = NULL;
257 : }
258 1 : restoreClient;
259 1 : return msg;
260 : }
261 :
262 :
263 : /* patch a newline character if needed */
264 : static str
265 0 : mal_cmdline(char *s, size_t *len)
266 : {
267 0 : if (*len && s[*len - 1] != '\n') {
268 0 : char *n = GDKmalloc(*len + 2);
269 0 : if (n == NULL)
270 : return s;
271 0 : memcpy(n, s, *len);
272 0 : n[*len] = '\n';
273 0 : n[*len + 1] = 0;
274 0 : (*len)++;
275 0 : return n;
276 : }
277 : return s;
278 : }
279 :
280 : str
281 0 : compileString(Symbol *fcn, Client cntxt, str s)
282 : {
283 0 : Client c;
284 0 : QryCtx *qc_old;
285 0 : size_t len = strlen(s);
286 0 : buffer *b;
287 0 : str msg = MAL_SUCCEED;
288 0 : str qry;
289 0 : str old = s;
290 0 : stream *bs;
291 0 : bstream *fdin = NULL;
292 :
293 0 : s = mal_cmdline(s, &len);
294 0 : qry = s;
295 0 : if (old == s) {
296 0 : qry = GDKstrdup(s);
297 0 : if (!qry)
298 0 : throw(MAL, "mal.eval", SQLSTATE(HY013) MAL_MALLOC_FAIL);
299 : }
300 :
301 0 : mal_unquote(qry);
302 0 : b = (buffer *) GDKzalloc(sizeof(buffer));
303 0 : if (b == NULL) {
304 0 : GDKfree(qry);
305 0 : throw(MAL, "mal.eval", SQLSTATE(HY013) MAL_MALLOC_FAIL);
306 : }
307 :
308 0 : buffer_init(b, qry, len);
309 0 : bs = buffer_rastream(b, "compileString");
310 0 : if (bs == NULL) {
311 0 : GDKfree(qry);
312 0 : GDKfree(b);
313 0 : throw(MAL, "mal.eval", SQLSTATE(HY013) MAL_MALLOC_FAIL);
314 : }
315 0 : fdin = bstream_create(bs, b->len);
316 0 : if (fdin == NULL) {
317 0 : GDKfree(qry);
318 0 : GDKfree(b);
319 0 : throw(MAL, "mal.eval", SQLSTATE(HY013) MAL_MALLOC_FAIL);
320 : }
321 0 : strncpy(fdin->buf, qry, len + 1);
322 :
323 0 : qc_old = MT_thread_get_qry_ctx();
324 : // compile in context of called for
325 0 : c = MCinitClient(MAL_ADMIN, fdin, 0);
326 0 : if (c == NULL) {
327 0 : GDKfree(qry);
328 0 : GDKfree(b);
329 0 : MT_thread_set_qry_ctx(qc_old);
330 0 : throw(MAL, "mal.eval", "Can not create user context");
331 : }
332 0 : c->curmodule = c->usermodule = cntxt->usermodule;
333 0 : c->promptlength = 0;
334 0 : c->listing = 0;
335 :
336 0 : if ((msg = defaultScenario(c))) {
337 0 : GDKfree(qry);
338 0 : GDKfree(b);
339 0 : c->usermodule = 0;
340 0 : MCcloseClient(c);
341 0 : MT_thread_set_qry_ctx(qc_old);
342 0 : return msg;
343 : }
344 :
345 0 : msg = MSinitClientPrg(c, "user", "main"); /* create new context */
346 0 : if (msg == MAL_SUCCEED)
347 0 : msg = MALparser(c);
348 : /*
349 : if(msg == MAL_SUCCEED && c->phase[MAL_SCENARIO_PARSER])
350 : msg = (str) (*c->phase[MAL_SCENARIO_PARSER])(c);
351 : if(msg == MAL_SUCCEED && c->phase[MAL_SCENARIO_OPTIMIZE])
352 : msg = (str) (*c->phase[MAL_SCENARIO_OPTIMIZE])(c);
353 : */
354 :
355 0 : *fcn = c->curprg;
356 0 : c->curprg = 0;
357 0 : c->usermodule = 0;
358 : /* restore IO channel */
359 0 : MCcloseClient(c);
360 0 : MT_thread_set_qry_ctx(qc_old);
361 0 : GDKfree(qry);
362 0 : GDKfree(b);
363 0 : return msg;
364 : }
|