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 : /*
14 : * (author) M. L. Kersten
15 : * All symbols are collected in modules. Modules are either global
16 : * or private for the user. The latter are known as 'user' module functions
17 : * and reside within the Client record.
18 : */
19 :
20 : #include "monetdb_config.h"
21 : #include "mal_module.h"
22 : #include "mal_function.h" /* for printFunction() */
23 : #include "mal_namespace.h"
24 : #include "mal_client.h"
25 : #include "mal_interpreter.h"
26 : #include "mal_listing.h"
27 : #include "mal_private.h"
28 :
29 : /*
30 : * Definition of a new module may interfere with concurrent actions.
31 : * A jump table is mainted to provide a quick start in the module
32 : * table to find the correct one.
33 : *
34 : * All modules are persistent during a server session
35 : */
36 :
37 : #define MODULE_HASH_SIZE 1024
38 : static Module moduleIndex[MODULE_HASH_SIZE] = { NULL };
39 :
40 : MALfcn
41 14 : findFunctionImplementation(const char *cname)
42 : {
43 12140 : for (int i = 0; i < MODULE_HASH_SIZE; i++) {
44 12137 : if (moduleIndex[i] != NULL) {
45 210319 : for (int j = 0; j < MAXSCOPE; j++) {
46 209503 : Symbol s;
47 209503 : if ((s = moduleIndex[i]->space[j]) != NULL) {
48 121437 : do {
49 121437 : if (s->def &&
50 121437 : strcmp(s->def->binding, cname) == 0 &&
51 14 : s->def->stmt &&s->def->stmt[0] &&
52 14 : s->def->stmt[0]->fcn) {
53 11 : return s->def->stmt[0]->fcn;
54 : }
55 121426 : } while ((s = s->peer) != NULL);
56 : }
57 : }
58 : }
59 : }
60 : return NULL;
61 : }
62 :
63 : BAT *
64 0 : getModules(void)
65 : {
66 0 : BAT *b = COLnew(0, TYPE_str, 100, TRANSIENT);
67 0 : int i;
68 0 : Module s, n;
69 :
70 0 : if (!b)
71 : return NULL;
72 0 : for (i = 0; i < MODULE_HASH_SIZE; i++) {
73 0 : s = moduleIndex[i];
74 0 : while (s) {
75 0 : if (BUNappend(b, s->name, FALSE) != GDK_SUCCEED) {
76 0 : BBPreclaim(b);
77 0 : return NULL;
78 : }
79 0 : n = s->link;
80 0 : while (n)
81 0 : n = n->link;
82 : s = s->link;
83 : }
84 : }
85 : return b;
86 : }
87 :
88 : // perform sanity check on duplicate occurrences as well
89 : void
90 0 : dumpModules(stream *out)
91 : {
92 0 : int i;
93 0 : Module s, n;
94 0 : for (i = 0; i < MODULE_HASH_SIZE; i++) {
95 0 : s = moduleIndex[i];
96 0 : while (s) {
97 0 : mnstr_printf(out, "[%d] module %s\n", i, s->name);
98 0 : n = s->link;
99 0 : while (n) {
100 0 : if (n == s)
101 0 : mnstr_printf(out,
102 : "ASSERTION error, double occurrence of symbol in symbol table\n");
103 0 : n = n->link;
104 : }
105 0 : s = s->link;
106 : }
107 : }
108 0 : }
109 :
110 : /* Remove all globally known functions */
111 : void
112 334 : mal_module_reset(void)
113 : {
114 334 : int i;
115 334 : Module m;
116 :
117 342350 : for (i = 0; i < MODULE_HASH_SIZE; i++) {
118 342016 : m = moduleIndex[i];
119 342016 : moduleIndex[i] = 0;
120 364699 : while (m) {
121 22683 : Module next = m->link;
122 22683 : freeModule(m);
123 22683 : m = next;
124 : }
125 : }
126 334 : }
127 :
128 : static int
129 58759606 : getModuleIndex(const char *name)
130 : {
131 58759606 : return (int) (strHash(name) % MODULE_HASH_SIZE);
132 : }
133 :
134 : static void
135 22683 : clrModuleIndex(Module cur)
136 : {
137 22683 : int index = getModuleIndex(cur->name);
138 22683 : Module prev = NULL;
139 22683 : Module m = moduleIndex[index];
140 22683 : while (m) {
141 0 : if (m == cur) {
142 0 : if (!prev) {
143 0 : moduleIndex[index] = m->link;
144 : } else {
145 0 : prev->link = m->link;
146 : }
147 0 : return;
148 : }
149 0 : prev = m;
150 0 : m = m->link;
151 : }
152 : }
153 :
154 : static void
155 22811 : addModuleToIndex(Module cur)
156 : {
157 22811 : int index = getModuleIndex(cur->name);
158 22811 : cur->link = moduleIndex[index];
159 22811 : moduleIndex[index] = cur;
160 22811 : }
161 :
162 : Module
163 58714112 : getModule(const char *name)
164 : {
165 58714112 : int index = getModuleIndex(name);
166 58714112 : Module m = moduleIndex[index];
167 58715148 : while (m) {
168 58680137 : if (name == m->name)
169 58679101 : return m;
170 1036 : m = m->link;
171 : }
172 : return NULL;
173 : }
174 :
175 : void
176 12 : getModuleList(Module **out, int *length)
177 : {
178 12 : int i;
179 12 : int moduleCount = 0;
180 12 : int currentIndex = 0;
181 12300 : for (i = 0; i < MODULE_HASH_SIZE; i++) {
182 12288 : Module m = moduleIndex[i];
183 13110 : while (m) {
184 822 : moduleCount++;
185 822 : m = m->link;
186 : }
187 : }
188 12 : *out = GDKzalloc(moduleCount * sizeof(Module));
189 12 : if (*out == NULL) {
190 : return;
191 : }
192 12 : *length = moduleCount;
193 :
194 12300 : for (i = 0; i < MODULE_HASH_SIZE; i++) {
195 12288 : Module m = moduleIndex[i];
196 13110 : while (m) {
197 822 : (*out)[currentIndex++] = m;
198 822 : m = m->link;
199 : }
200 : }
201 : }
202 :
203 : void
204 12 : freeModuleList(Module *list)
205 : {
206 12 : GDKfree(list);
207 12 : }
208 :
209 : /*
210 : * Module scope management
211 : * It will contain the symbol table of all globally accessible functions.
212 : */
213 : Module
214 22811 : globalModule(const char *nme)
215 : {
216 22811 : Module cur;
217 :
218 : // Global modules are not named 'user'
219 22811 : assert(strcmp(nme, "user"));
220 22811 : nme = putName(nme);
221 22811 : if (nme == NULL)
222 : return NULL;
223 22811 : cur = (Module) GDKzalloc(sizeof(ModuleRecord));
224 22811 : if (cur == NULL)
225 : return NULL;
226 22811 : cur->name = nme;
227 22811 : cur->link = NULL;
228 22811 : addModuleToIndex(cur);
229 22811 : return cur;
230 : }
231 :
232 : /* Every client record has a private module name 'user'
233 : * for keeping around non-shared functions */
234 : Module
235 38562 : userModule(void)
236 : {
237 38562 : Module cur;
238 :
239 38562 : cur = (Module) GDKzalloc(sizeof(ModuleRecord));
240 38562 : if (cur == NULL)
241 : return NULL;
242 38562 : cur->name = putName("user");
243 38562 : if (cur->name == NULL) {
244 0 : GDKfree(cur);
245 0 : return NULL;
246 : }
247 38562 : cur->link = NULL;
248 38562 : return cur;
249 : }
250 :
251 : /*
252 : * The scope can be fixed. This is used by the parser.
253 : * Reading a module often calls for creation first.
254 : */
255 : Module
256 8 : fixModule(const char *nme)
257 : {
258 8 : Module m;
259 :
260 8 : m = getModule(nme);
261 8 : if (m)
262 : return m;
263 4 : return globalModule(nme);
264 : }
265 :
266 : /*
267 : * The freeModule operation throws away a symbol without
268 : * concerns on it whereabouts in the scope structure.
269 : */
270 : static void
271 61244 : freeSubScope(Module scope)
272 : {
273 61244 : int i;
274 61244 : Symbol s;
275 :
276 15739125 : for (i = 0; i < MAXSCOPE; i++) {
277 15677881 : if (scope->space[i]) {
278 200317 : s = scope->space[i];
279 200317 : scope->space[i] = NULL;
280 200317 : freeSymbolList(s);
281 : }
282 : }
283 61244 : }
284 :
285 : void
286 61244 : freeModule(Module m)
287 : {
288 61244 : Symbol s;
289 :
290 61244 : if (m == NULL)
291 : return;
292 61244 : if ((s = findSymbolInModule(m, "epilogue")) != NULL) {
293 2326 : InstrPtr pci = getInstrPtr(s->def, 0);
294 2326 : if (pci && pci->token == COMMANDsymbol && pci->argc == 1) {
295 2326 : int status = 0;
296 2326 : str ret = MAL_SUCCEED;
297 :
298 2326 : assert(pci->fcn != NULL);
299 2326 : ret = (*(str (*)(int *)) pci->fcn) (&status);
300 2326 : freeException(ret);
301 2326 : (void) status;
302 : }
303 : }
304 61244 : freeSubScope(m);
305 61243 : if (strcmp(m->name, "user")) {
306 22683 : clrModuleIndex(m);
307 : }
308 61243 : if (m->help)
309 0 : GDKfree(m->help);
310 61243 : GDKfree(m);
311 : }
312 :
313 : /*
314 : * After filling in a structure it is added to the multi-level symbol
315 : * table. We keep a skip list of similarly named function symbols.
316 : * This speeds up searching provided the modules adhere to the
317 : * structure and group the functions as well.
318 : */
319 : void
320 3486613 : insertSymbol(Module scope, Symbol prg)
321 : {
322 3486613 : InstrPtr sig;
323 3486613 : int t;
324 3486613 : Module c;
325 :
326 3486613 : assert(scope);
327 3486613 : sig = getSignature(prg);
328 3486613 : if (getModuleId(sig) && getModuleId(sig) != scope->name) {
329 : /* move the definition to the proper place */
330 : /* default scope is the last resort */
331 0 : c = findModule(scope, getModuleId(sig));
332 0 : if (c)
333 3486613 : scope = c;
334 : }
335 3486613 : t = getSymbolIndex(getFunctionId(sig));
336 3486613 : if (scope->space[t] == prg) {
337 : /* already known, last inserted */
338 : } else {
339 3486603 : prg->peer = scope->space[t];
340 3486603 : scope->space[t] = prg;
341 3486603 : if (prg->peer && idcmp(prg->name, prg->peer->name) == 0)
342 2897889 : prg->skip = prg->peer->skip;
343 : else
344 588714 : prg->skip = prg->peer;
345 : }
346 3486613 : assert(prg != prg->peer);
347 3486613 : }
348 :
349 : /*
350 : * Removal of elements from the symbol table should be
351 : * done with care. For, it should be assured that
352 : * there are no references to the definition at the
353 : * moment of removal. This situation can not easily
354 : * checked at runtime, without tremendous overhead.
355 : */
356 : void
357 549 : deleteSymbol(Module scope, Symbol prg)
358 : {
359 549 : InstrPtr sig;
360 549 : int t;
361 :
362 549 : sig = getSignature(prg);
363 549 : if (getModuleId(sig) && getModuleId(sig) != scope->name) {
364 : /* move the definition to the proper place */
365 : /* default scope is the last resort */
366 0 : Module c = findModule(scope, getModuleId(sig));
367 0 : if (c)
368 549 : scope = c;
369 : }
370 549 : t = getSymbolIndex(getFunctionId(sig));
371 549 : if (scope->space[t] == prg) {
372 514 : scope->space[t] = scope->space[t]->peer;
373 514 : freeSymbol(prg);
374 : } else {
375 : Symbol nxt = scope->space[t];
376 69 : while (nxt->peer != NULL) {
377 69 : if (nxt->peer == prg) {
378 35 : nxt->peer = prg->peer;
379 35 : nxt->skip = prg->peer;
380 35 : freeSymbol(prg);
381 35 : return;
382 : }
383 : nxt = nxt->peer;
384 : }
385 : }
386 : }
387 :
388 : /*
389 : * Searching the scope structure.
390 : * Finding a scope is unrestricted. For modules we explicitly look for
391 : * the start of a new module scope.
392 : * All core modules are accessed through the jumptable.
393 : * The 'user' module is an alias for the scope attached
394 : * to the current user.
395 : */
396 : Module
397 54967813 : findModule(Module scope, const char *name)
398 : {
399 54967813 : Module def = scope;
400 54967813 : Module m;
401 54967813 : if (name == NULL)
402 : return scope;
403 54967813 : m = getModule(name);
404 54966723 : if (m)
405 : return m;
406 :
407 : /* default is always matched with current */
408 7551 : if (def->name == NULL)
409 : return NULL;
410 : return def;
411 : }
412 :
413 : /*
414 : * The routine findSymbolInModule starts at a MAL scope level and searches
415 : * an element amongst the peers.
416 : *
417 : * In principal, external variables are subject to synchronization actions
418 : * to avoid concurrency conflicts. This also implies, that any parallel
419 : * block introduces a temporary scope.
420 : *
421 : * The variation on this routine is to dump the definition of
422 : * all matching definitions.
423 : */
424 : Symbol
425 776087 : findSymbolInModule(Module v, const char *fcn)
426 : {
427 776087 : Symbol s;
428 776087 : if (v == NULL || fcn == NULL)
429 : return NULL;
430 776053 : s = v->space[(int) (*fcn)];
431 1912818 : while (s != NULL) {
432 1842000 : if (idcmp(s->name, fcn) == 0)
433 705235 : return s;
434 1136765 : s = s->skip;
435 : }
436 : return NULL;
437 : }
438 :
439 : Symbol
440 91 : findSymbol(Module usermodule, const char *mod, const char *fcn)
441 : {
442 91 : Module m = findModule(usermodule, mod);
443 91 : return findSymbolInModule(m, fcn);
444 : }
|