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. Kersten
15 : * The MAL Interpreter
16 : */
17 : #include "monetdb_config.h"
18 : #include "mal_runtime.h"
19 : #include "mal_interpreter.h"
20 : #include "mal_resource.h"
21 : #include "mal_listing.h"
22 : #include "mal_type.h"
23 : #include "mal_private.h"
24 : #include "mal_internal.h"
25 : #include "mal_function.h"
26 :
27 : static lng qptimeout = 0; /* how often we print still running queries (usec) */
28 :
29 : void
30 0 : setqptimeout(lng usecs)
31 : {
32 0 : qptimeout = usecs;
33 0 : }
34 :
35 : inline ptr
36 35688355 : getArgReference(MalStkPtr stk, InstrPtr pci, int k)
37 : {
38 : /* the C standard says: "A pointer to a union object, suitably
39 : * converted, points to each of its members (or if a member is a
40 : * bit-field, then to the unit in which it resides), and vice
41 : * versa." */
42 35688355 : return (ptr) &stk->stk[pci->argv[k]].val;
43 : }
44 :
45 : static str
46 33404357 : malCommandCall(MalStkPtr stk, InstrPtr pci)
47 : {
48 33404357 : str ret = MAL_SUCCEED;
49 :
50 33404357 : switch (pci->argc) {
51 0 : case 0:
52 0 : ret = (*(str (*) (void)) pci->fcn)();
53 0 : break;
54 1010315 : case 1:
55 1010315 : ret = (*(str (*) (void *)) pci->fcn)(
56 : getArgReference(stk, pci, 0));
57 1010315 : break;
58 124580 : case 2:
59 124580 : ret = (*(str (*) (void *, void *)) pci->fcn)(
60 : getArgReference(stk, pci, 0),
61 : getArgReference(stk, pci, 1));
62 124580 : break;
63 30740189 : case 3:
64 30740189 : ret = (*(str (*) (void *, void *, void *)) pci->fcn)(
65 : getArgReference(stk, pci, 0),
66 : getArgReference(stk, pci, 1),
67 : getArgReference(stk, pci, 2));
68 30740189 : break;
69 550911 : case 4:
70 550911 : ret = (*(str (*) (void *, void *, void *, void *)) pci-> fcn)(
71 : getArgReference(stk, pci, 0),
72 : getArgReference(stk, pci, 1),
73 : getArgReference(stk, pci, 2),
74 : getArgReference(stk, pci, 3));
75 550911 : break;
76 593304 : case 5:
77 593304 : ret = (*(str (*) (void *, void *, void *, void *, void *)) pci->fcn)(
78 : getArgReference(stk, pci, 0),
79 : getArgReference(stk, pci, 1),
80 : getArgReference(stk, pci, 2),
81 : getArgReference(stk, pci, 3),
82 : getArgReference(stk, pci, 4));
83 593304 : break;
84 3656 : case 6:
85 3656 : ret = (*(str (*) (void *, void *, void *, void *, void *,
86 3656 : void *)) pci->fcn)(
87 : getArgReference(stk, pci, 0),
88 : getArgReference(stk, pci, 1),
89 : getArgReference(stk, pci, 2),
90 : getArgReference(stk, pci, 3),
91 : getArgReference(stk, pci, 4),
92 : getArgReference(stk, pci, 5));
93 3656 : break;
94 93132 : case 7:
95 93132 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
96 93132 : void *)) pci->fcn)(
97 : getArgReference(stk, pci, 0),
98 : getArgReference(stk, pci, 1),
99 : getArgReference(stk, pci, 2),
100 : getArgReference(stk, pci, 3),
101 : getArgReference(stk, pci, 4),
102 : getArgReference(stk, pci, 5),
103 : getArgReference(stk, pci, 6));
104 93132 : break;
105 273033 : case 8:
106 273033 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
107 273033 : void *, void *)) pci->fcn)(
108 : getArgReference(stk, pci, 0),
109 : getArgReference(stk, pci, 1),
110 : getArgReference(stk, pci, 2),
111 : getArgReference(stk, pci, 3),
112 : getArgReference(stk, pci, 4),
113 : getArgReference(stk, pci, 5),
114 : getArgReference(stk, pci, 6),
115 : getArgReference(stk, pci, 7));
116 273033 : break;
117 14931 : case 9:
118 14931 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
119 14931 : void *, void *, void *)) pci->fcn)(
120 : getArgReference(stk, pci, 0),
121 : getArgReference(stk, pci, 1),
122 : getArgReference(stk, pci, 2),
123 : getArgReference(stk, pci, 3),
124 : getArgReference(stk, pci, 4),
125 : getArgReference(stk, pci, 5),
126 : getArgReference(stk, pci, 6),
127 : getArgReference(stk, pci, 7),
128 : getArgReference(stk, pci, 8));
129 14931 : break;
130 32 : case 10:
131 32 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
132 32 : void *, void *, void *, void *)) pci->fcn)(
133 : getArgReference(stk, pci, 0),
134 : getArgReference(stk, pci, 1),
135 : getArgReference(stk, pci, 2),
136 : getArgReference(stk, pci, 3),
137 : getArgReference(stk, pci, 4),
138 : getArgReference(stk, pci, 5),
139 : getArgReference(stk, pci, 6),
140 : getArgReference(stk, pci, 7),
141 : getArgReference(stk, pci, 8),
142 : getArgReference(stk, pci, 9));
143 32 : break;
144 163 : case 11:
145 163 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
146 163 : void *, void *, void *, void *, void *)) pci->fcn)(
147 : getArgReference(stk, pci, 0),
148 : getArgReference(stk, pci, 1),
149 : getArgReference(stk, pci, 2),
150 : getArgReference(stk, pci, 3),
151 : getArgReference(stk, pci, 4),
152 : getArgReference(stk, pci, 5),
153 : getArgReference(stk, pci, 6),
154 : getArgReference(stk, pci, 7),
155 : getArgReference(stk, pci, 8),
156 : getArgReference(stk, pci, 9),
157 : getArgReference(stk, pci, 10));
158 163 : break;
159 111 : case 12:
160 111 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
161 : void *, void *, void *, void *, void *,
162 111 : void *)) pci->fcn)(
163 : getArgReference(stk, pci, 0),
164 : getArgReference(stk, pci, 1),
165 : getArgReference(stk, pci, 2),
166 : getArgReference(stk, pci, 3),
167 : getArgReference(stk, pci, 4),
168 : getArgReference(stk, pci, 5),
169 : getArgReference(stk, pci, 6),
170 : getArgReference(stk, pci, 7),
171 : getArgReference(stk, pci, 8),
172 : getArgReference(stk, pci, 9),
173 : getArgReference(stk, pci, 10),
174 : getArgReference(stk, pci, 11));
175 111 : break;
176 0 : case 13:
177 0 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
178 : void *, void *, void *, void *, void *, void *,
179 0 : void *)) pci->fcn)(
180 : getArgReference(stk, pci, 0),
181 : getArgReference(stk, pci, 1),
182 : getArgReference(stk, pci, 2),
183 : getArgReference(stk, pci, 3),
184 : getArgReference(stk, pci, 4),
185 : getArgReference(stk, pci, 5),
186 : getArgReference(stk, pci, 6),
187 : getArgReference(stk, pci, 7),
188 : getArgReference(stk, pci, 8),
189 : getArgReference(stk, pci, 9),
190 : getArgReference(stk, pci, 10),
191 : getArgReference(stk, pci, 11),
192 : getArgReference(stk, pci, 12));
193 0 : break;
194 0 : case 14:
195 0 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
196 : void *, void *, void *, void *, void *, void *,
197 0 : void *, void *)) pci->fcn)(
198 : getArgReference(stk, pci, 0),
199 : getArgReference(stk, pci, 1),
200 : getArgReference(stk, pci, 2),
201 : getArgReference(stk, pci, 3),
202 : getArgReference(stk, pci, 4),
203 : getArgReference(stk, pci, 5),
204 : getArgReference(stk, pci, 6),
205 : getArgReference(stk, pci, 7),
206 : getArgReference(stk, pci, 8),
207 : getArgReference(stk, pci, 9),
208 : getArgReference(stk, pci, 10),
209 : getArgReference(stk, pci, 11),
210 : getArgReference(stk, pci, 12),
211 : getArgReference(stk, pci, 13));
212 0 : break;
213 0 : case 15:
214 0 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
215 : void *, void *, void *, void *, void *, void *,
216 0 : void *, void *, void *)) pci->fcn)(
217 : getArgReference(stk, pci, 0),
218 : getArgReference(stk, pci, 1),
219 : getArgReference(stk, pci, 2),
220 : getArgReference(stk, pci, 3),
221 : getArgReference(stk, pci, 4),
222 : getArgReference(stk, pci, 5),
223 : getArgReference(stk, pci, 6),
224 : getArgReference(stk, pci, 7),
225 : getArgReference(stk, pci, 8),
226 : getArgReference(stk, pci, 9),
227 : getArgReference(stk, pci, 10),
228 : getArgReference(stk, pci, 11),
229 : getArgReference(stk, pci, 12),
230 : getArgReference(stk, pci, 13),
231 : getArgReference(stk, pci, 14));
232 0 : break;
233 0 : case 16:
234 0 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
235 : void *, void *, void *, void *, void *, void *,
236 0 : void *, void *, void *, void *)) pci->fcn)(
237 : getArgReference(stk, pci, 0),
238 : getArgReference(stk, pci, 1),
239 : getArgReference(stk, pci, 2),
240 : getArgReference(stk, pci, 3),
241 : getArgReference(stk, pci, 4),
242 : getArgReference(stk, pci, 5),
243 : getArgReference(stk, pci, 6),
244 : getArgReference(stk, pci, 7),
245 : getArgReference(stk, pci, 8),
246 : getArgReference(stk, pci, 9),
247 : getArgReference(stk, pci, 10),
248 : getArgReference(stk, pci, 11),
249 : getArgReference(stk, pci, 12),
250 : getArgReference(stk, pci, 13),
251 : getArgReference(stk, pci, 14),
252 : getArgReference(stk, pci, 15));
253 0 : break;
254 0 : default:
255 0 : throw(MAL, "mal.interpreter", "too many arguments for command call");
256 : }
257 : return ret;
258 : }
259 :
260 : /*
261 : * Copy the constant values onto the stack frame
262 : */
263 : #define initStack(S, R) \
264 : do { \
265 : for (int i = (S); i < mb->vtop; i++) { \
266 : lhs = &stk->stk[i]; \
267 : if (isVarConstant(mb, i) > 0) { \
268 : if (!isVarDisabled(mb, i)) { \
269 : rhs = &getVarConstant(mb, i); \
270 : if(VALcopy(lhs, rhs) == NULL) \
271 : R = 0; \
272 : } \
273 : } else { \
274 : lhs->vtype = getVarGDKType(mb, i); \
275 : lhs->val.pval = 0; \
276 : lhs->len = 0; \
277 : } \
278 : } \
279 : } while (0)
280 :
281 : static inline bool
282 6649255 : isNotUsedIn(InstrPtr p, int start, int a)
283 : {
284 16506485 : for (int k = start; k < p->argc; k++)
285 9885313 : if (getArg(p, k) == a)
286 : return false;
287 : return true;
288 : }
289 :
290 : MalStkPtr
291 625222 : prepareMALstack(MalBlkPtr mb, int size)
292 : {
293 625222 : MalStkPtr stk = NULL;
294 625222 : int res = 1;
295 625222 : ValPtr lhs, rhs;
296 :
297 625222 : stk = newGlobalStack(size);
298 625219 : if (!stk)
299 : return NULL;
300 625219 : stk->stktop = mb->vtop;
301 625219 : stk->blk = mb;
302 625219 : stk->memory = 0;
303 111467968 : initStack(0, res);
304 625200 : if (!res) {
305 0 : freeStack(stk);
306 0 : return NULL;
307 : }
308 : return stk;
309 : }
310 :
311 : str
312 553578 : runMAL(Client cntxt, MalBlkPtr mb, MalBlkPtr mbcaller, MalStkPtr env)
313 : {
314 553578 : MalStkPtr stk = NULL;
315 553578 : ValPtr lhs, rhs;
316 553578 : str ret;
317 553578 : (void) mbcaller;
318 :
319 : /* Prepare a new interpreter call. This involves two steps, (1)
320 : * allocate the minimum amount of stack space needed, some slack
321 : * resources are included to permit code optimizers to add a few
322 : * variables at run time, (2) copying the arguments into the new
323 : * stack frame.
324 : *
325 : * The env stackframe is set when a MAL function is called
326 : * recursively. Alternatively, there is no caller but a stk to be
327 : * re-used for interpretation. We assume here that it aligns with
328 : * the variable table of the routine being called.
329 : *
330 : * allocate space for value stack the global stack should be large
331 : * enough
332 : */
333 553578 : cntxt->lastcmd = time(0);
334 553578 : ATOMIC_SET(&cntxt->lastprint, GDKusec());
335 553578 : if (env != NULL) {
336 9916 : int res = 1;
337 9916 : stk = env;
338 9916 : if (mb != stk->blk)
339 0 : throw(MAL, "mal.interpreter", "misalignment of symbols");
340 9916 : if (mb->vtop > stk->stksize)
341 0 : throw(MAL, "mal.interpreter", "stack too small");
342 26210 : initStack(env->stkbot, res);
343 9915 : if (!res)
344 0 : throw(MAL, "mal.interpreter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
345 : } else {
346 543662 : stk = prepareMALstack(mb, mb->vsize);
347 543642 : if (stk == 0)
348 0 : throw(MAL, "mal.interpreter", MAL_STACK_FAIL);
349 543642 : stk->blk = mb;
350 : /*safeguardStack */
351 543642 : if (env) {
352 : stk->stkdepth = stk->stksize + env->stkdepth;
353 : stk->calldepth = env->calldepth + 1;
354 : stk->up = env;
355 : if (stk->calldepth > 256)
356 : throw(MAL, "mal.interpreter", MAL_CALLDEPTH_FAIL);
357 : }
358 : /*
359 : * An optimization is to copy all constant variables used in
360 : * functions immediately onto the value stack. Then we do not
361 : * have to check for their location later on any more. At some
362 : * point, the effect is optimal, if at least several constants
363 : * are referenced in a function (a gain on tst400a of 20% has
364 : * been observed due the small size of the function).
365 : */
366 : }
367 553557 : ret = runMALsequence(cntxt, mb, 1, 0, stk, env, 0);
368 :
369 553577 : if (!stk->keepAlive && garbageControl(getInstrPtr(mb, 0)))
370 543662 : garbageCollector(cntxt, mb, stk, env != stk);
371 553558 : if (stk && stk != env)
372 543643 : freeStack(stk);
373 553574 : if (ret == MAL_SUCCEED && cntxt->qryctx.querytimeout
374 31 : && cntxt->qryctx.starttime
375 18 : && GDKusec() - cntxt->qryctx.starttime > cntxt->qryctx.querytimeout)
376 0 : throw(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
377 : return ret;
378 : }
379 :
380 : /* Single instruction
381 : * It is possible to re-enter the interpreter at a specific place.
382 : * This is used in the area where we need to support co-routines.
383 : *
384 : * A special case for MAL interpretation is to execute just one instruction.
385 : * This is typically used by optimizers and schedulers that need part of the
386 : * answer to direct their actions. Or, a dataflow scheduler could step in
387 : * to enforce a completely different execution order.
388 : */
389 : str
390 104748 : reenterMAL(Client cntxt, MalBlkPtr mb, int startpc, int stoppc, MalStkPtr stk)
391 : {
392 104748 : str ret;
393 104748 : int keepAlive;
394 :
395 104748 : if (stk == NULL)
396 0 : throw(MAL, "mal.interpreter", MAL_STACK_FAIL);
397 104748 : keepAlive = stk->keepAlive;
398 104748 : ret = runMALsequence(cntxt, mb, startpc, stoppc, stk, 0, 0);
399 :
400 104748 : if (keepAlive == 0 && garbageControl(getInstrPtr(mb, 0)))
401 0 : garbageCollector(cntxt, mb, stk, stk != 0);
402 : return ret;
403 : }
404 :
405 : /*
406 : * Front ends may benefit from a more direct call to any of the MAL
407 : * procedural abstractions. The argument list points to the arguments
408 : * for the block to be executed. An old stack frame may be re-used,
409 : * but it is then up to the caller to ensure it is properly
410 : * initialized.
411 : * The call does not return values, they are ignored.
412 : */
413 : str
414 0 : callMAL(Client cntxt, MalBlkPtr mb, MalStkPtr *env, ValPtr argv[])
415 : {
416 0 : MalStkPtr stk = NULL;
417 0 : str ret = MAL_SUCCEED;
418 0 : ValPtr lhs;
419 0 : InstrPtr pci = getInstrPtr(mb, 0);
420 :
421 0 : cntxt->lastcmd = time(0);
422 :
423 0 : switch (pci->token) {
424 0 : case FUNCTIONsymbol:
425 : case FCNcall:
426 : /*
427 : * Prepare the stack frame for this operation. Copy all the arguments
428 : * in place. We assume that the caller has supplied pointers for
429 : * all arguments and return values.
430 : */
431 0 : if (*env == NULL) {
432 0 : stk = prepareMALstack(mb, mb->vsize);
433 0 : if (stk == NULL)
434 0 : throw(MAL, "mal.interpreter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
435 0 : stk->up = 0;
436 0 : *env = stk;
437 : } else {
438 : ValPtr lhs, rhs;
439 : int res = 1;
440 :
441 0 : stk = *env;
442 0 : initStack(0, res);
443 0 : if (!res)
444 0 : throw(MAL, "mal.interpreter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
445 : }
446 0 : assert(stk);
447 0 : for (int i = pci->retc; i < pci->argc; i++) {
448 0 : lhs = &stk->stk[pci->argv[i]];
449 0 : if (VALcopy(lhs, argv[i]) == NULL)
450 0 : throw(MAL, "mal.interpreter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
451 0 : if (lhs->vtype == TYPE_bat)
452 0 : BBPretain(lhs->val.bval);
453 : }
454 0 : ret = runMALsequence(cntxt, mb, 1, 0, stk, 0, 0);
455 0 : break;
456 0 : case PATcall:
457 : case CMDcall:
458 : default:
459 0 : throw(MAL, "mal.interpreter", RUNTIME_UNKNOWN_INSTRUCTION);
460 : }
461 0 : if (stk)
462 0 : garbageCollector(cntxt, mb, stk, TRUE);
463 0 : if (ret == MAL_SUCCEED && cntxt->qryctx.querytimeout
464 0 : && cntxt->qryctx.starttime
465 0 : && GDKusec() - cntxt->qryctx.starttime > cntxt->qryctx.querytimeout)
466 0 : throw(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
467 : return ret;
468 : }
469 :
470 : /*
471 : * The core of the interpreter is presented next. It takes the context
472 : * information and starts the interpretation at the designated
473 : * instruction. Note that the stack frame is aligned and initialized
474 : * in the enclosing routine. When we start executing the first
475 : * instruction, we take the wall-clock time for resource management.
476 : */
477 : str
478 8495322 : runMALsequence(Client cntxt, MalBlkPtr mb, int startpc,
479 : int stoppc, MalStkPtr stk, MalStkPtr env, InstrPtr pcicaller)
480 : {
481 8495322 : ValPtr lhs, rhs, v;
482 8495322 : InstrPtr pci = 0;
483 8495322 : int exceptionVar;
484 8495322 : str ret = MAL_SUCCEED, localGDKerrbuf = GDKerrbuf;
485 8494830 : ValRecord backups[16];
486 8494830 : ValPtr backup;
487 8494830 : int garbages[16], *garbage;
488 8494830 : int stkpc = 0;
489 8494830 : RuntimeProfileRecord runtimeProfile, runtimeProfileFunction;
490 8494830 : lng lastcheck = 0;
491 8494830 : bool startedProfileQueue = false;
492 : #define CHECKINTERVAL 1000 /* how often do we check for client disconnect */
493 8494830 : runtimeProfile.ticks = runtimeProfileFunction.ticks = 0;
494 :
495 8494830 : if (stk == NULL)
496 0 : throw(MAL, "mal.interpreter", MAL_STACK_FAIL);
497 :
498 : /* prepare extended backup and garbage structures */
499 8494830 : if (startpc + 1 == stoppc) {
500 7886611 : pci = getInstrPtr(mb, startpc);
501 7886611 : if (pci->argc > 16) {
502 67300 : backup = GDKmalloc(pci->argc * sizeof(ValRecord));
503 67289 : garbage = (int *) GDKzalloc(pci->argc * sizeof(int));
504 67292 : if (backup == NULL || garbage == NULL) {
505 0 : GDKfree(backup);
506 0 : GDKfree(garbage);
507 0 : throw(MAL, "mal.interpreter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
508 : }
509 : } else {
510 7819311 : backup = backups;
511 7819311 : garbage = garbages;
512 7819311 : memset(garbages, 0, sizeof(garbages));
513 : }
514 608219 : } else if (mb->maxarg > 16) {
515 92254 : backup = GDKmalloc(mb->maxarg * sizeof(ValRecord));
516 92254 : garbage = (int *) GDKzalloc(mb->maxarg * sizeof(int));
517 92254 : if (backup == NULL || garbage == NULL) {
518 0 : GDKfree(backup);
519 0 : GDKfree(garbage);
520 0 : throw(MAL, "mal.interpreter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
521 : }
522 : } else {
523 515965 : backup = backups;
524 515965 : garbage = garbages;
525 515965 : memset(garbages, 0, sizeof(garbages));
526 : }
527 :
528 : /* also produce event record for start of function */
529 8494822 : if (startpc == 1 && startpc < mb->stop) {
530 608232 : startedProfileQueue = true;
531 608232 : runtimeProfileInit(cntxt, mb, stk);
532 608238 : runtimeProfileBegin(cntxt, mb, stk, getInstrPtr(mb, 0),
533 : &runtimeProfileFunction);
534 608238 : if (cntxt->sessiontimeout
535 104710 : && cntxt->qryctx.starttime - cntxt->session >
536 : cntxt->sessiontimeout) {
537 0 : runtimeProfileFinish(cntxt, mb, stk);
538 0 : if (backup != backups)
539 0 : GDKfree(backup);
540 0 : if (garbage != garbages)
541 0 : GDKfree(garbage);
542 0 : throw(MAL, "mal.interpreter",
543 : SQLSTATE(HYT00) RUNTIME_SESSION_TIMEOUT);
544 : }
545 : }
546 8494828 : stkpc = startpc;
547 8494828 : exceptionVar = -1;
548 :
549 61451492 : while (stkpc < mb->stop && stkpc != stoppc) {
550 : // incomplete block being executed, requires at least signature and end statement
551 52963036 : MT_thread_setalgorithm(NULL);
552 52962971 : pci = getInstrPtr(mb, stkpc);
553 52962971 : if (cntxt->mode == FINISHCLIENT) {
554 1 : stkpc = stoppc;
555 1 : if (ret == MAL_SUCCEED)
556 1 : ret = createException(MAL, "mal.interpreter",
557 : "prematurely stopped client");
558 : break;
559 : }
560 :
561 52962970 : if (stk->status) {
562 : /* pause procedure from SYSMON */
563 1 : if (stk->status == 'p') {
564 0 : while (stk->status == 'p')
565 0 : MT_sleep_ms(50);
566 0 : continue;
567 : }
568 : /* stop procedure from SYSMON */
569 1 : if (stk->status == 'q') {
570 1 : stkpc = mb->stop;
571 1 : ret = createException(MAL, "mal.interpreter",
572 : "Query with tag " OIDFMT
573 : " received stop signal", mb->tag);
574 1 : break;
575 : }
576 : }
577 : //Ensure we spread system resources over multiple users as well.
578 52962969 : runtimeProfileBegin(cntxt, mb, stk, pci, &runtimeProfile);
579 52962139 : if (runtimeProfile.ticks > lastcheck + CHECKINTERVAL) {
580 8614901 : if (cntxt->fdin && !mnstr_isalive(cntxt->fdin->s)) {
581 0 : cntxt->mode = FINISHCLIENT;
582 0 : stkpc = stoppc;
583 0 : ret = createException(MAL, "mal.interpreter",
584 : "prematurely stopped client");
585 0 : break;
586 : }
587 8616582 : lastcheck = runtimeProfile.ticks;
588 : }
589 :
590 52963820 : if (qptimeout > 0) {
591 0 : lng t = GDKusec();
592 0 : ATOMIC_BASE_TYPE lp = ATOMIC_GET(&cntxt->lastprint);
593 0 : if ((lng) lp + qptimeout < t) {
594 : /* if still the same, replace lastprint with current
595 : * time and print the query */
596 0 : if (ATOMIC_CAS(&cntxt->lastprint, &lp, t)) {
597 0 : const char *q = cntxt->query ? cntxt->query : NULL;
598 0 : TRC_INFO(MAL_SERVER,
599 : "%s: query already running " LLFMT "s: %.200s\n",
600 : cntxt->mythread ? cntxt->mythread : "?",
601 : (lng) (time(0) - cntxt->lastcmd), q ? q : "");
602 : }
603 : }
604 : }
605 :
606 : /* The interpreter loop
607 : * The interpreter is geared towards execution a MAL
608 : * procedure together with all its descendant
609 : * invocations. As such, it provides the MAL abtract
610 : * machine processor.
611 : *
612 : * The value-stack frame of the surrounding scope is
613 : * needed to resolve binding values. Getting (putting) a
614 : * value from (into) a surrounding scope should be guarded
615 : * with the exclusive access lock. This situation is
616 : * encapsulated by a bind() function call, whose
617 : * parameters contain the access mode required.
618 : *
619 : * The formal procedure arguments are assumed to always
620 : * occupy the first elements in the value stack.
621 : *
622 : * Before we execute an instruction the variables to be
623 : * garbage collected are identified. In the post-execution
624 : * phase they are removed.
625 : */
626 106157437 : for (int i = 0; i < pci->retc; i++)
627 53193617 : backup[i] = stk->stk[getArg(pci, i)];
628 :
629 52963820 : if (garbageControl(pci)) {
630 118200871 : for (int i = 0; i < pci->argc; i++) {
631 91785673 : int a = getArg(pci, i);
632 :
633 91785673 : if (stk->stk[a].vtype == TYPE_bat && getEndScope(mb, a) == stkpc
634 13298510 : && isNotUsedIn(pci, i + 1, a))
635 6621106 : garbage[i] = a;
636 : else
637 85164567 : garbage[i] = -1;
638 : }
639 : }
640 :
641 52963820 : freeException(ret);
642 52962672 : ret = MAL_SUCCEED;
643 52962672 : switch (pci->token) {
644 3462969 : case ASSIGNsymbol:
645 : /* Assignment command
646 : * The assignment statement copies values around on
647 : * the stack frame, including multiple assignments.
648 : *
649 : * Pushing constants/initial values onto the stack is
650 : * a separate operation. It takes the constant value
651 : * discovered at compile time and stored in the symbol
652 : * table and moves it to the stackframe location. This
653 : * activity is made part of the start-up procedure.
654 : *
655 : * The before after calls should be reconsidered here,
656 : * because their. They seem superflous and the way
657 : * they are used will cause errors in multi-assignment
658 : * statements.
659 : */
660 6859456 : for (int k = 0, i = pci->retc; k < pci->retc && i < pci->argc;
661 3396487 : i++, k++) {
662 3396499 : lhs = &stk->stk[pci->argv[k]];
663 3396499 : rhs = &stk->stk[pci->argv[i]];
664 3396499 : if (VALcopy(lhs, rhs) == NULL) {
665 0 : ret = createException(MAL, "mal.interpreter",
666 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
667 0 : break;
668 3396487 : } else if (lhs->vtype == TYPE_bat && !is_bat_nil(lhs->val.bval))
669 4460 : BBPretain(lhs->val.bval);
670 : }
671 : break;
672 15499114 : case PATcall:
673 15499114 : if (pci->fcn == NULL) {
674 0 : ret = createException(MAL, "mal.interpreter",
675 : "address of pattern %s.%s missing",
676 : pci->modname, pci->fcnname);
677 : } else {
678 15499114 : TRC_DEBUG(ALGO, "calling %s.%s\n",
679 : pci->modname ? pci->modname : "<null>",
680 : pci->fcnname ? pci->fcnname : "<null>");
681 15499114 : ret = (*(str (*) (Client, MalBlkPtr, MalStkPtr, InstrPtr)) pci->
682 : fcn) (cntxt, mb, stk, pci);
683 : #ifndef NDEBUG
684 15496934 : if (ret == MAL_SUCCEED) {
685 : /* check that the types of actual results match
686 : * expected results */
687 31432239 : for (int i = 0; i < pci->retc; i++) {
688 15955337 : int a = getArg(pci, i);
689 15955337 : int t = getArgType(mb, pci, i);
690 :
691 15955337 : if (isaBatType(t)) {
692 4144603 : bat bid = stk->stk[a].val.bval;
693 4144603 : BAT *_b;
694 4144603 : t = getBatType(t);
695 4144603 : assert(stk->stk[a].vtype == TYPE_bat);
696 12018042 : assert(is_bat_nil(bid) ||
697 : t == TYPE_any ||
698 : ((_b = BBP_desc(bid)) != NULL &&
699 : ATOMtype(_b->ttype) == ATOMtype(t)));
700 : } else {
701 11810734 : assert(t == stk->stk[a].vtype);
702 : }
703 : }
704 : }
705 : #endif
706 : }
707 : break;
708 33403987 : case CMDcall:
709 33403987 : TRC_DEBUG(ALGO, "calling %s.%s\n",
710 : pci->modname ? pci->modname : "<null>",
711 : pci->fcnname ? pci->fcnname : "<null>");
712 33403987 : ret = malCommandCall(stk, pci);
713 : #ifndef NDEBUG
714 33403864 : if (ret == MAL_SUCCEED) {
715 : /* check that the types of actual results match
716 : * expected results */
717 67095802 : for (int i = 0; i < pci->retc; i++) {
718 33693302 : int a = getArg(pci, i);
719 33693302 : int t = getArgType(mb, pci, i);
720 :
721 33693302 : if (isaBatType(t)) {
722 : //bat bid = stk->stk[a].val.bval;
723 17992409 : t = getBatType(t);
724 17992409 : assert(stk->stk[a].vtype == TYPE_bat);
725 : //assert( !is_bat_nil(bid));
726 17992409 : assert(t != TYPE_any);
727 : //assert( ATOMtype(BBP_desc(bid)->ttype) == ATOMtype(t));
728 : } else {
729 15700893 : assert(t == stk->stk[a].vtype);
730 : }
731 : }
732 : }
733 : #endif
734 : break;
735 54616 : case FCNcall: {
736 : /*
737 : * MAL function calls are relatively expensive,
738 : * because they have to assemble a new stack frame and
739 : * do housekeeping, such as garbagecollection of all
740 : * non-returned values.
741 : */
742 54616 : MalStkPtr nstk;
743 54616 : InstrPtr q;
744 54616 : int ii, arg;
745 :
746 54616 : nstk = prepareMALstack(pci->blk, pci->blk->vsize);
747 54616 : if (nstk == 0) {
748 0 : ret = createException(MAL, "mal.interpreter", MAL_STACK_FAIL);
749 0 : break;
750 : }
751 54616 : nstk->pcup = stkpc;
752 :
753 : /*safeguardStack */
754 54616 : nstk->stkdepth = nstk->stksize + stk->stkdepth;
755 54616 : nstk->calldepth = stk->calldepth + 1;
756 54616 : nstk->up = stk;
757 54616 : if (nstk->calldepth > 256) {
758 1 : ret = createException(MAL, "mal.interpreter",
759 : MAL_CALLDEPTH_FAIL);
760 1 : GDKfree(nstk);
761 1 : break;
762 : }
763 54615 : if ((unsigned) nstk->stkdepth >
764 24990 : THREAD_STACK_SIZE / sizeof(mb->var[0]) / 4 && THRhighwater()) {
765 : /* we are running low on stack space */
766 0 : ret = createException(MAL, "mal.interpreter", MAL_STACK_FAIL);
767 0 : GDKfree(nstk);
768 0 : break;
769 : }
770 :
771 : /* copy arguments onto destination stack */
772 54615 : q = getInstrPtr(pci->blk, 0);
773 54615 : arg = q->retc;
774 258732 : for (ii = pci->retc; ii < pci->argc; ii++, arg++) {
775 204117 : lhs = &nstk->stk[q->argv[arg]];
776 204117 : rhs = &stk->stk[pci->argv[ii]];
777 204117 : if (VALcopy(lhs, rhs) == NULL) {
778 0 : GDKfree(nstk);
779 0 : ret = createException(MAL, "mal.interpreter",
780 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
781 0 : break;
782 204117 : } else if (lhs->vtype == TYPE_bat)
783 18 : BBPretain(lhs->val.bval);
784 : }
785 54615 : if (ret == MAL_SUCCEED && ii == pci->argc) {
786 54615 : ret = runMALsequence(cntxt, pci->blk, 1, pci->blk->stop, nstk,
787 : stk, pci);
788 54615 : garbageCollector(cntxt, pci->blk, nstk, 0);
789 54615 : arg = q->retc;
790 258733 : for (ii = pci->retc; ii < pci->argc; ii++, arg++) {
791 204118 : lhs = &nstk->stk[q->argv[arg]];
792 204118 : if (lhs->vtype == TYPE_bat)
793 0 : BBPrelease(lhs->val.bval);
794 : }
795 54615 : GDKfree(nstk);
796 : }
797 : break;
798 : }
799 : case REMsymbol:
800 : break;
801 541888 : case ENDsymbol:
802 541888 : runtimeProfileExit(cntxt, mb, stk, pci, &runtimeProfile);
803 541889 : runtimeProfileExit(cntxt, mb, stk, getInstrPtr(mb, 0),
804 : &runtimeProfileFunction);
805 541888 : if (pcicaller && garbageControl(getInstrPtr(mb, 0)))
806 5995 : garbageCollector(cntxt, mb, stk, TRUE);
807 541888 : if (cntxt->qryctx.querytimeout && cntxt->qryctx.starttime
808 18 : && GDKusec() - cntxt->qryctx.starttime >
809 18 : cntxt->qryctx.querytimeout) {
810 0 : freeException(ret); /* overrule exception */
811 0 : ret = createException(MAL, "mal.interpreter",
812 : SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
813 0 : break;
814 : }
815 541888 : stkpc = mb->stop; // force end of loop
816 541888 : continue;
817 0 : default: {
818 0 : str w;
819 0 : if (pci->token < 0) {
820 : /* temporary NOOP instruction */
821 : break;
822 : }
823 0 : w = instruction2str(mb, 0, pci, FALSE);
824 0 : if (w) {
825 0 : ret = createException(MAL, "interpreter", "unkown operation:%s",
826 : w);
827 0 : GDKfree(w);
828 : } else {
829 0 : ret = createException(MAL, "interpreter",
830 : "failed instruction2str");
831 : }
832 : // runtimeProfileBegin already sets the time in the instruction
833 0 : if (cntxt->qryctx.querytimeout && cntxt->qryctx.starttime
834 0 : && GDKusec() - cntxt->qryctx.starttime >
835 0 : cntxt->qryctx.querytimeout) {
836 0 : freeException(ret); /* in case it's set */
837 0 : ret = createException(MAL, "mal.interpreter",
838 : SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
839 0 : break;
840 : }
841 :
842 0 : stkpc = mb->stop;
843 0 : continue;
844 : }
845 : }
846 :
847 : /* monitoring information should reflect the input arguments,
848 : which may be removed by garbage collection */
849 : /* BEWARE, the SQL engine or MAL function could zap the block, leaving garbage behind in pci */
850 : /* this hack means we loose a closing event */
851 52418469 : if (mb->stop <= 1)
852 0 : continue;
853 52418469 : runtimeProfileExit(cntxt, mb, stk, pci, &runtimeProfile);
854 : /* when we find a timeout situation, then the result is already known
855 : * and assigned, the backup version is not removed*/
856 52415707 : if (ret == MAL_SUCCEED) {
857 105593265 : for (int i = 0; i < pci->retc; i++) {
858 53194584 : lhs = &backup[i];
859 75355583 : if (BATatoms[lhs->vtype].atomUnfix &&
860 22163244 : (*BATatoms[lhs->vtype].atomUnfix) (VALget(lhs)) != GDK_SUCCEED) {
861 0 : if (ret == MAL_SUCCEED)
862 0 : ret = createException(MAL, "mal.propertyCheck",
863 : GDK_EXCEPTION);
864 : }
865 53192339 : if (ATOMextern(lhs->vtype) &&
866 1839114 : lhs->val.pval &&
867 167157 : lhs->val.pval != ATOMnilptr(lhs->vtype) &&
868 167157 : lhs->val.pval != stk->stk[getArg(pci, i)].val.pval)
869 164926 : GDKfree(lhs->val.pval);
870 : }
871 52398681 : if (ATOMIC_GET(&GDKdebug) & CHECKMASK && exceptionVar < 0) {
872 : BAT *b;
873 :
874 105348393 : for (int i = 0; i < pci->retc; i++) {
875 53068678 : if (garbage[i] == -1
876 51423016 : && stk->stk[getArg(pci, i)].vtype == TYPE_bat
877 21728278 : && !is_bat_nil(stk->stk[getArg(pci, i)].val.bval)) {
878 21670152 : assert(stk->stk[getArg(pci, i)].val.bval > 0);
879 21670152 : b = BATdescriptor(stk->stk[getArg(pci, i)].val.bval);
880 21668513 : if (b == NULL) {
881 0 : if (ret == MAL_SUCCEED)
882 0 : ret = createException(MAL, "mal.propertyCheck",
883 : SQLSTATE(HY002)
884 : RUNTIME_OBJECT_MISSING);
885 0 : continue;
886 : }
887 21668513 : BATassertProps(b);
888 21670734 : BBPunfix(b->batCacheid);
889 : }
890 : }
891 : }
892 :
893 : /* general garbage collection */
894 52394796 : if (ret == MAL_SUCCEED && garbageControl(pci)) {
895 118365320 : for (int i = 0; i < pci->argc; i++) {
896 91968668 : int a = getArg(pci, i);
897 :
898 91968668 : if (isaBatType(getArgType(mb, pci, i))) {
899 49798283 : bat bid = stk->stk[a].val.bval;
900 :
901 49798283 : if (garbage[i] >= 0) {
902 6578164 : bid = stk->stk[garbage[i]].val.bval;
903 6578164 : if (!is_bat_nil(bid)) {
904 6505334 : stk->stk[garbage[i]].val.bval = bat_nil;
905 6505334 : BBPcold(bid);
906 6506811 : BBPrelease(bid);
907 : }
908 : }
909 : }
910 : }
911 : }
912 : }
913 :
914 : /* Exception handling */
915 52414776 : if (localGDKerrbuf && localGDKerrbuf[0]) {
916 2 : if (ret == MAL_SUCCEED)
917 2 : ret = createException(MAL, "mal.interpreter", GDK_EXCEPTION);
918 : // TODO take properly care of the GDK exception
919 2 : localGDKerrbuf[0] = 0;
920 : }
921 :
922 52414776 : if (ret != MAL_SUCCEED) {
923 18987 : str msg = 0;
924 :
925 : /* Detect any exception received from the implementation. */
926 : /* The first identifier is an optional exception name */
927 18987 : if (strstr(ret, "!skip-to-end")) {
928 0 : freeException(ret);
929 0 : ret = MAL_SUCCEED;
930 0 : stkpc = mb->stop;
931 0 : continue;
932 : }
933 : /*
934 : * Exceptions are caught based on their name, which is part of the
935 : * exception message. The ANYexception variable catches all.
936 : */
937 18987 : exceptionVar = -1;
938 18987 : msg = strchr(ret, ':');
939 18987 : if (msg) {
940 18987 : exceptionVar = findVariableLength(mb, ret, (int) (msg - ret));
941 : }
942 18987 : if (exceptionVar == -1)
943 18985 : exceptionVar = findVariableLength(mb, "ANYexception", 12);
944 :
945 : /* unknown exceptions lead to propagation */
946 18985 : if (exceptionVar == -1) {
947 18967 : if (cntxt->qryctx.querytimeout && cntxt->qryctx.starttime
948 5 : && GDKusec() - cntxt->qryctx.starttime >
949 5 : cntxt->qryctx.querytimeout) {
950 1 : freeException(ret);
951 1 : ret = createException(MAL, "mal.interpreter",
952 : SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
953 : }
954 18967 : stkpc = mb->stop;
955 18967 : continue;
956 : }
957 : /* assure correct variable type */
958 20 : if (getVarType(mb, exceptionVar) == TYPE_str) {
959 : /* watch out for concurrent access */
960 20 : MT_lock_set(&mal_contextLock);
961 20 : v = &stk->stk[exceptionVar];
962 20 : if (v->val.sval)
963 5 : freeException(v->val.sval); /* old exception */
964 20 : VALset(v, TYPE_str, ret);
965 20 : ret = MAL_SUCCEED;
966 20 : MT_lock_unset(&mal_contextLock);
967 : } else {
968 0 : mnstr_printf(cntxt->fdout, "%s", ret);
969 0 : freeException(ret);
970 0 : ret = MAL_SUCCEED;
971 : }
972 : /* position yourself at the catch instruction for further decisions */
973 : /* skipToCatch(exceptionVar,@2,@3) */
974 : /* skip to catch block or end */
975 229 : for (; stkpc < mb->stop; stkpc++) {
976 229 : InstrPtr l = getInstrPtr(mb, stkpc);
977 229 : if (l->barrier == CATCHsymbol) {
978 : int j;
979 20 : for (j = 0; j < l->retc; j++)
980 20 : if (getArg(l, j) == exceptionVar)
981 : break;
982 0 : else if (strcmp(getArgName(mb, l, j), "ANYexception") == 0)
983 : break;
984 20 : if (j < l->retc)
985 : break;
986 : }
987 : }
988 20 : if (stkpc == mb->stop) {
989 0 : if (cntxt->qryctx.querytimeout && cntxt->qryctx.starttime
990 0 : && GDKusec() - cntxt->qryctx.starttime >
991 0 : cntxt->qryctx.querytimeout) {
992 0 : freeException(ret);
993 0 : ret = createException(MAL, "mal.interpreter",
994 : SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
995 0 : stkpc = mb->stop;
996 : }
997 0 : continue;
998 : }
999 20 : pci = getInstrPtr(mb, stkpc);
1000 : }
1001 :
1002 : /*
1003 : * After the expression has been evaluated we should check for
1004 : * a possible change in the control flow.
1005 : */
1006 52395809 : switch (pci->barrier) {
1007 1392419 : case BARRIERsymbol:
1008 1392419 : v = &stk->stk[getDestVar(pci)];
1009 : /* skip to end of barrier, depends on the type */
1010 1392419 : switch (v->vtype) {
1011 1391796 : case TYPE_bit:
1012 1391796 : if (v->val.btval == FALSE || is_bit_nil(v->val.btval))
1013 1322723 : stkpc = pci->jump;
1014 : break;
1015 0 : case TYPE_bte:
1016 0 : if (is_bte_nil(v->val.btval))
1017 0 : stkpc = pci->jump;
1018 : break;
1019 598 : case TYPE_oid:
1020 598 : if (is_oid_nil(v->val.oval))
1021 423 : stkpc = pci->jump;
1022 : break;
1023 0 : case TYPE_sht:
1024 0 : if (is_sht_nil(v->val.shval))
1025 0 : stkpc = pci->jump;
1026 : break;
1027 9 : case TYPE_int:
1028 9 : if (is_int_nil(v->val.ival))
1029 0 : stkpc = pci->jump;
1030 : break;
1031 15 : case TYPE_lng:
1032 15 : if (is_lng_nil(v->val.lval))
1033 0 : stkpc = pci->jump;
1034 : break;
1035 : #ifdef HAVE_HGE
1036 0 : case TYPE_hge:
1037 0 : if (is_hge_nil(v->val.hval))
1038 0 : stkpc = pci->jump;
1039 : break;
1040 : #endif
1041 1 : case TYPE_flt:
1042 1 : if (is_flt_nil(v->val.fval))
1043 0 : stkpc = pci->jump;
1044 : break;
1045 0 : case TYPE_dbl:
1046 0 : if (is_dbl_nil(v->val.dval))
1047 0 : stkpc = pci->jump;
1048 : break;
1049 0 : case TYPE_str:
1050 0 : if (strNil(v->val.sval))
1051 0 : stkpc = pci->jump;
1052 : break;
1053 0 : default:
1054 0 : ret = createException(MAL, "mal.interpreter",
1055 : "%s: Unknown barrier type", getVarName(mb,
1056 : getDestVar
1057 : (pci)));
1058 : }
1059 1392419 : stkpc++;
1060 1392419 : break;
1061 14258827 : case LEAVEsymbol:
1062 : case REDOsymbol:
1063 14258827 : v = &stk->stk[getDestVar(pci)];
1064 : /* skip to end of barrier, depending on the type */
1065 14258827 : switch (v->vtype) {
1066 863 : case TYPE_bit:
1067 863 : if (v->val.btval == TRUE)
1068 445 : stkpc = pci->jump;
1069 : else
1070 418 : stkpc++;
1071 : break;
1072 0 : case TYPE_str:
1073 0 : if (!strNil(v->val.sval))
1074 0 : stkpc = pci->jump;
1075 : else
1076 0 : stkpc++;
1077 : break;
1078 47590 : case TYPE_oid:
1079 47590 : if (!is_oid_nil(v->val.oval))
1080 47421 : stkpc = pci->jump;
1081 : else
1082 169 : stkpc++;
1083 : break;
1084 0 : case TYPE_sht:
1085 0 : if (!is_sht_nil(v->val.shval))
1086 0 : stkpc = pci->jump;
1087 : else
1088 0 : stkpc++;
1089 : break;
1090 119 : case TYPE_int:
1091 119 : if (!is_int_nil(v->val.ival))
1092 114 : stkpc = pci->jump;
1093 : else
1094 5 : stkpc++;
1095 : break;
1096 0 : case TYPE_bte:
1097 0 : if (!is_bte_nil(v->val.btval))
1098 0 : stkpc = pci->jump;
1099 : else
1100 0 : stkpc++;
1101 : break;
1102 14210253 : case TYPE_lng:
1103 14210253 : if (!is_lng_nil(v->val.lval))
1104 14210238 : stkpc = pci->jump;
1105 : else
1106 15 : stkpc++;
1107 : break;
1108 : #ifdef HAVE_HGE
1109 0 : case TYPE_hge:
1110 0 : if (!is_hge_nil(v->val.hval))
1111 0 : stkpc = pci->jump;
1112 : else
1113 0 : stkpc++;
1114 : break;
1115 : #endif
1116 2 : case TYPE_flt:
1117 2 : if (!is_flt_nil(v->val.fval))
1118 1 : stkpc = pci->jump;
1119 : else
1120 1 : stkpc++;
1121 : break;
1122 0 : case TYPE_dbl:
1123 0 : if (!is_dbl_nil(v->val.dval))
1124 0 : stkpc = pci->jump;
1125 : else
1126 0 : stkpc++;
1127 : break;
1128 : default:
1129 : break;
1130 : }
1131 : break;
1132 45 : case CATCHsymbol:
1133 : /* catch blocks are skipped unless
1134 : searched for explicitly */
1135 45 : if (exceptionVar < 0) {
1136 22 : stkpc = pci->jump;
1137 22 : break;
1138 : }
1139 23 : exceptionVar = -1;
1140 23 : stkpc++;
1141 23 : break;
1142 69166 : case EXITsymbol:
1143 69166 : if (getDestVar(pci) == exceptionVar)
1144 0 : exceptionVar = -1;
1145 69166 : stkpc++;
1146 69166 : break;
1147 19 : case RAISEsymbol:
1148 19 : exceptionVar = getDestVar(pci);
1149 : //freeException(ret);
1150 19 : ret = MAL_SUCCEED;
1151 19 : if (getVarType(mb, getDestVar(pci)) == TYPE_str) {
1152 17 : char nme[256];
1153 17 : snprintf(nme, 256, "%s.%s[%d]", getModuleId(getInstrPtr(mb, 0)),
1154 17 : getFunctionId(getInstrPtr(mb, 0)), stkpc);
1155 17 : ret = createException(MAL, nme, "%s",
1156 17 : stk->stk[getDestVar(pci)].val.sval);
1157 : }
1158 : /* skipToCatch(exceptionVar, @2, stk) */
1159 : /* skip to catch block or end */
1160 464 : for (; stkpc < mb->stop; stkpc++) {
1161 448 : InstrPtr l = getInstrPtr(mb, stkpc);
1162 448 : if (l->barrier == CATCHsymbol) {
1163 : int j;
1164 3 : for (j = 0; j < l->retc; j++)
1165 3 : if (getArg(l, j) == exceptionVar)
1166 : break;
1167 0 : else if (strcmp(getArgName(mb, l, j), "ANYexception") == 0)
1168 : break;
1169 3 : if (j < l->retc)
1170 : break;
1171 : }
1172 : }
1173 19 : if (stkpc == mb->stop) {
1174 16 : runtimeProfileExit(cntxt, mb, stk, pci, &runtimeProfile);
1175 16 : runtimeProfileExit(cntxt, mb, stk, getInstrPtr(mb, 0),
1176 : &runtimeProfileFunction);
1177 16 : break;
1178 : }
1179 : break;
1180 48364 : case RETURNsymbol:
1181 : /* a fake multi-assignment */
1182 48364 : if (env != NULL && pcicaller != NULL) {
1183 : InstrPtr pp = pci;
1184 99449 : pci = pcicaller;
1185 99449 : for (int i = 0; i < pci->retc; i++) {
1186 51087 : rhs = &stk->stk[pp->argv[i]];
1187 51087 : lhs = &env->stk[pci->argv[i]];
1188 51087 : if (VALcopy(lhs, rhs) == NULL) {
1189 0 : ret = createException(MAL, "mal.interpreter",
1190 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
1191 0 : break;
1192 51087 : } else if (lhs->vtype == TYPE_bat)
1193 3394 : BBPretain(lhs->val.bval);
1194 : }
1195 48362 : if (garbageControl(getInstrPtr(mb, 0)))
1196 48323 : garbageCollector(cntxt, mb, stk, TRUE);
1197 : /* reset the clock */
1198 48362 : runtimeProfileExit(cntxt, mb, stk, pp, &runtimeProfile);
1199 48362 : runtimeProfileExit(cntxt, mb, stk, getInstrPtr(mb, 0),
1200 : &runtimeProfileFunction);
1201 : }
1202 48364 : stkpc = mb->stop;
1203 48364 : continue;
1204 36626969 : default:
1205 36626969 : stkpc++;
1206 : }
1207 52347445 : if (cntxt->qryctx.querytimeout && cntxt->qryctx.starttime
1208 122 : && GDKusec() - cntxt->qryctx.starttime >
1209 122 : cntxt->qryctx.querytimeout) {
1210 6 : if (ret == MAL_SUCCEED)
1211 6 : ret = createException(MAL, "mal.interpreter",
1212 : SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
1213 6 : stkpc = mb->stop;
1214 : }
1215 : }
1216 :
1217 : /* if we could not find the exception variable, cascade a new one */
1218 : /* don't add 'exception not caught' extra message for MAL sequences besides main function calls */
1219 8488458 : if (exceptionVar >= 0 && (ret == MAL_SUCCEED || !pcicaller)) {
1220 1 : char nme[256];
1221 1 : snprintf(nme, 256, "%s.%s[%d]", getModuleId(getInstrPtr(mb, 0)),
1222 1 : getFunctionId(getInstrPtr(mb, 0)), stkpc);
1223 1 : if (ret != MAL_SUCCEED) {
1224 0 : str new, n;
1225 0 : n = createException(MAL, nme, "exception not caught");
1226 0 : if (n) {
1227 0 : new = GDKzalloc(strlen(ret) + strlen(n) + 16);
1228 0 : if (new) {
1229 0 : strcpy(new, ret);
1230 0 : if (new[strlen(new) - 1] != '\n')
1231 0 : strcat(new, "\n");
1232 0 : strcat(new, "!");
1233 0 : strcat(new, n);
1234 0 : freeException(n);
1235 0 : freeException(ret);
1236 0 : ret = new;
1237 : } else {
1238 0 : freeException(ret);
1239 0 : ret = n;
1240 : }
1241 : }
1242 : } else {
1243 1 : ret = createException(MAL, nme, "Exception not caught");
1244 : }
1245 : }
1246 8488458 : if (startedProfileQueue)
1247 608234 : runtimeProfileFinish(cntxt, mb, stk);
1248 8488462 : if (backup != backups)
1249 159377 : GDKfree(backup);
1250 8487700 : if (garbage != garbages)
1251 159548 : GDKfree(garbage);
1252 : return ret;
1253 : }
1254 :
1255 :
1256 : /*
1257 : * MAL API
1258 : * The linkage between MAL interpreter and compiled C-routines
1259 : * is kept as simple as possible.
1260 : * Basically we distinguish four kinds of calling conventions:
1261 : * CMDcall, FCNcall and PATcall.
1262 : * The FCNcall indicates calling a MAL procedure, which leads
1263 : * to a recursive call to the interpreter.
1264 : *
1265 : * CMDcall initiates calling a linked function, passing pointers
1266 : * to the parameters and result variable, i.e. f(ptr a0,..., ptr aN)
1267 : * The function returns a MAL-SUCCEED upon success and a pointer
1268 : * to an exception string upon failure.
1269 : * Failure leads to raise-ing an exception in the interpreter loop,
1270 : * by either looking up the relevant exception message in the module
1271 : * administration or construction of a standard string.
1272 : *
1273 : * The PATcall initiates a call which contains the MAL context,
1274 : * i.e. f(MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
1275 : * The mb provides access to the code definitions. It is primarilly
1276 : * used by routines intended to manipulate the code base itself, such
1277 : * as the optimizers. The Mal stack frame pointer provides access
1278 : * to the values maintained. The arguments passed are offsets
1279 : * into the stack frame rather than pointers to the actual value.
1280 : *
1281 : * BAT parameters require some care. Ideally, a BAT should not be kept
1282 : * around long. This would mean that each time we access a BAT it has to be
1283 : * pinned in memory and upon leaving the function, it is unpinned.
1284 : * This degrades performance significantly.
1285 : * After the parameters are fixed, we can safely free the destination
1286 : * variable and re-initialize it to nil.
1287 : *
1288 : */
1289 :
1290 : /*
1291 : * The type dispatching table in getArgReference can be removed if we
1292 : * determine at compile time the address offset within a ValRecord.
1293 : * We leave this optimization for the future, it leads to about 10%
1294 : * improvement (100ms for 1M calls).
1295 : *
1296 : * Flow of control statements
1297 : * Each assignment (function call) may be part of the initialization
1298 : * of a barrier- block. In that case we have to test the
1299 : * outcome of the operation and possibly skip the block altogether.
1300 : * The latter is implemented as a linear scan for the corresponding
1301 : * labeled statemtent. This might be optimized later.
1302 : *
1303 : * You can skip to a catch block by searching for the corresponding 'lab'
1304 : * The return value should be set to pass the error automatically upon
1305 : * reaching end of function block.
1306 : */
1307 :
1308 : /*
1309 : * Each time we enter a barrier block, we could keep its position in the
1310 : * interpreter stack frame. It forms the starting point to issue a redo.
1311 : * Unfortunately, this does not easily work in the presence of optimizers, which
1312 : * may change the order/block structure. Therefore, we simple have to search
1313 : * the beginning or ensure that during chkProgram the barrier/redo/leave/catch
1314 : * jumps are re-established.
1315 : *
1316 : * Exception handling
1317 : * Calling a built-in or user-defined routine may lead to an error or a
1318 : * cached status message to be dealt with in MAL.
1319 : * To improve error handling in MAL, an exception handling
1320 : * scheme based on @sc{catch}-@sc{exit} blocks. The @sc{catch}
1321 : * statement identifies a (string-valued) variable, which carries the
1322 : * exception message from
1323 : * the originally failed routine or @sc{raise} exception assignment.
1324 : * During normal processing @sc{catch}-@sc{exit} blocks are simply skipped.
1325 : * Upon receiving an exception status from a function call, we set the
1326 : * exception variable and skip to the first associated @sc{catch}-@sc{exit}
1327 : * block.
1328 : * MAL interpretation then continues until it reaches the end of the block.
1329 : * If no exception variable was defined, we should abandon the function
1330 : * alltogether searching for a catch block at a higher layer.
1331 : *
1332 : * For the time being we have ignored cascaded/stacked exceptions.
1333 : * The policy is to pass the first recognized exception to a context
1334 : * in which it can be handled.
1335 : *
1336 : * Exceptions raised within a linked-in function requires some care.
1337 : * First, the called procedure does not know anything about the MAL
1338 : * interpreter context. Thus, we need to return all relevant information
1339 : * upon leaving the linked library routine.
1340 : *
1341 : * Second, exceptional cases can be handled deeply in the recursion, where they
1342 : * may also be handled, i.e. by issueing an GDKerror message. The upper layers
1343 : * merely receive a negative integer value to indicate occurrence of an
1344 : * error somewhere in the calling sequence.
1345 : * We then have to also look into GDKerrbuf to see if there was
1346 : * an error raised deeply inside the system.
1347 : *
1348 : * The policy is to require all C-functions to return a string-pointer.
1349 : * Upon a successful call, it is a NULL string. Otherwise it contains an
1350 : * encoding of the exceptional state encountered. This message
1351 : * starts with the exception identifer, followed by contextual details.
1352 : */
1353 :
1354 : /*
1355 : * Garbage collection
1356 : * Garbage collection is relatively straightforward, because most values are
1357 : * retained on the stackframe of an interpreter call. However, two storage
1358 : * types and possibly user-defined type garbage collector definitions
1359 : * require attention: BATs and strings.
1360 : *
1361 : * A key issue is to deal with temporary BATs in an efficient way.
1362 : * References to bats in the buffer pool may cause dangling references
1363 : * at the language level. This appears as soons as your share
1364 : * a reference and delete the BAT from one angle. If not careful, the
1365 : * dangling pointer may subsequently be associated with another BAT
1366 : *
1367 : * All string values are private to the VALrecord, which means they
1368 : * have to be freed explicitly before a MAL function returns.
1369 : * The first step is to always safe the destination variable
1370 : * before a function call is made.
1371 : */
1372 : void
1373 161950582 : garbageElement(Client cntxt, ValPtr v)
1374 : {
1375 161950582 : (void) cntxt;
1376 161950582 : if (ATOMstorage(v->vtype) == TYPE_str) {
1377 38817704 : GDKfree(v->val.sval);
1378 38817868 : v->val.sval = NULL;
1379 38817868 : v->len = 0;
1380 123132878 : } else if (v->vtype == TYPE_bat) {
1381 : /*
1382 : * All operations are responsible to properly set the
1383 : * reference count of the BATs being produced or destroyed.
1384 : * The libraries should not leave the
1385 : * physical reference count being set. This is only
1386 : * allowed during the execution of a GDK operation.
1387 : * All references should be logical.
1388 : */
1389 14317354 : bat bid = v->val.bval;
1390 : /* printf("garbage collecting: %d lrefs=%d refs=%d\n",
1391 : bid, BBP_lrefs(bid),BBP_refs(bid)); */
1392 14317354 : v->val.bval = bat_nil;
1393 14317354 : if (is_bat_nil(bid))
1394 : return;
1395 257510 : BBPcold(bid);
1396 257511 : BBPrelease(bid);
1397 108815524 : } else if (0 < v->vtype && v->vtype < MAXATOMS && ATOMextern(v->vtype)) {
1398 2634 : GDKfree(v->val.pval);
1399 2634 : v->val.pval = 0;
1400 2634 : v->len = 0;
1401 : }
1402 : }
1403 :
1404 : /*
1405 : * Before we return from the interpreter, we should free all
1406 : * dynamically allocated objects and adjust the BAT reference counts.
1407 : * Early experience shows that for small stack frames the overhead
1408 : * is about 200 ms for a 1M function call loop (tst400e). This means that
1409 : * for the time being we do not introduce more complex garbage
1410 : * administration code.
1411 : *
1412 : * Also note that for top-level stack frames (no environment available),
1413 : * we should retain the value stack because it acts as a global variables.
1414 : * This situation is indicated by the 'global' in the stack frame.
1415 : * Upon termination of the session, the stack should be cleared.
1416 : * Beware that variables may be know polymorphic, their actual
1417 : * type should be saved for variables that recide on a global
1418 : * stack frame.
1419 : */
1420 : void
1421 690818 : garbageCollector(Client cntxt, MalBlkPtr mb, MalStkPtr stk, int flag)
1422 : {
1423 690818 : assert(mb->vtop <= mb->vsize);
1424 690818 : assert(stk->stktop <= stk->stksize);
1425 : (void) flag;
1426 : (void) mb;
1427 : (void) cntxt;
1428 162625702 : for (int k = 0; k < stk->stktop; k++) {
1429 : // if (isVarCleanup(mb, k) ){
1430 161934904 : ValPtr v;
1431 161934904 : garbageElement(cntxt, v = &stk->stk[k]);
1432 161934884 : v->vtype = TYPE_int;
1433 161934884 : v->val.ival = int_nil;
1434 : // }
1435 : }
1436 690798 : }
|