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 36720084 : 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 36720084 : return (ptr) &stk->stk[pci->argv[k]].val;
43 : }
44 :
45 : static str
46 34631947 : malCommandCall(MalStkPtr stk, InstrPtr pci)
47 : {
48 34631947 : str ret = MAL_SUCCEED;
49 :
50 34631947 : switch (pci->argc) {
51 0 : case 0:
52 0 : ret = (*(str (*) (void)) pci->fcn)();
53 0 : break;
54 1010300 : case 1:
55 1010300 : ret = (*(str (*) (void *)) pci->fcn)(
56 : getArgReference(stk, pci, 0));
57 1010300 : break;
58 140343 : case 2:
59 140343 : ret = (*(str (*) (void *, void *)) pci->fcn)(
60 : getArgReference(stk, pci, 0),
61 : getArgReference(stk, pci, 1));
62 140343 : break;
63 31368906 : case 3:
64 31368906 : ret = (*(str (*) (void *, void *, void *)) pci->fcn)(
65 : getArgReference(stk, pci, 0),
66 : getArgReference(stk, pci, 1),
67 : getArgReference(stk, pci, 2));
68 31368906 : break;
69 639033 : case 4:
70 639033 : 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 639033 : break;
76 817669 : case 5:
77 817669 : 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 817669 : break;
84 4077 : case 6:
85 4077 : ret = (*(str (*) (void *, void *, void *, void *, void *,
86 4077 : 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 4077 : break;
94 145194 : case 7:
95 145194 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
96 145194 : 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 145194 : break;
105 484693 : case 8:
106 484693 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
107 484693 : 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 484693 : break;
117 21418 : case 9:
118 21418 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
119 21418 : 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 21418 : break;
130 30 : case 10:
131 30 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
132 30 : 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 30 : break;
144 177 : case 11:
145 177 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
146 177 : 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 177 : break;
159 107 : case 12:
160 107 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
161 : void *, void *, void *, void *, void *,
162 107 : 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 107 : 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 : lhs->bat = isaBatType(getVarType(mb, i)); \
278 : } \
279 : } \
280 : } while (0)
281 :
282 : static inline bool
283 10298555 : isNotUsedIn(InstrPtr p, int start, int a)
284 : {
285 25311216 : for (int k = start; k < p->argc; k++)
286 15018094 : if (getArg(p, k) == a)
287 : return false;
288 : return true;
289 : }
290 :
291 : MalStkPtr
292 654177 : prepareMALstack(MalBlkPtr mb, int size)
293 : {
294 654177 : MalStkPtr stk = NULL;
295 654177 : int res = 1;
296 654177 : ValPtr lhs, rhs;
297 :
298 654177 : stk = newGlobalStack(size);
299 654271 : if (!stk)
300 : return NULL;
301 654271 : stk->stktop = mb->vtop;
302 654271 : stk->blk = mb;
303 654271 : stk->memory = 0;
304 115555452 : initStack(0, res);
305 654258 : if (!res) {
306 0 : freeStack(stk);
307 0 : return NULL;
308 : }
309 : return stk;
310 : }
311 :
312 : str
313 563414 : runMAL(Client cntxt, MalBlkPtr mb, MalBlkPtr mbcaller, MalStkPtr env)
314 : {
315 563414 : MalStkPtr stk = NULL;
316 563414 : ValPtr lhs, rhs;
317 563414 : str ret;
318 563414 : (void) mbcaller;
319 :
320 : /* Prepare a new interpreter call. This involves two steps, (1)
321 : * allocate the minimum amount of stack space needed, some slack
322 : * resources are included to permit code optimizers to add a few
323 : * variables at run time, (2) copying the arguments into the new
324 : * stack frame.
325 : *
326 : * The env stackframe is set when a MAL function is called
327 : * recursively. Alternatively, there is no caller but a stk to be
328 : * re-used for interpretation. We assume here that it aligns with
329 : * the variable table of the routine being called.
330 : *
331 : * allocate space for value stack the global stack should be large
332 : * enough
333 : */
334 563414 : cntxt->lastcmd = time(0);
335 563417 : ATOMIC_SET(&cntxt->lastprint, GDKusec());
336 563422 : if (env != NULL) {
337 9868 : int res = 1;
338 9868 : stk = env;
339 9868 : if (mb != stk->blk)
340 0 : throw(MAL, "mal.interpreter", "misalignment of symbols");
341 9868 : if (mb->vtop > stk->stksize)
342 0 : throw(MAL, "mal.interpreter", "stack too small");
343 25910 : initStack(env->stkbot, res);
344 9868 : if (!res)
345 0 : throw(MAL, "mal.interpreter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
346 : } else {
347 553554 : stk = prepareMALstack(mb, mb->vsize);
348 553543 : if (stk == 0)
349 0 : throw(MAL, "mal.interpreter", MAL_STACK_FAIL);
350 553543 : stk->blk = mb;
351 : /*safeguardStack */
352 553543 : if (env) {
353 : stk->stkdepth = stk->stksize + env->stkdepth;
354 : stk->calldepth = env->calldepth + 1;
355 : stk->up = env;
356 : if (stk->calldepth > 256)
357 : throw(MAL, "mal.interpreter", MAL_CALLDEPTH_FAIL);
358 : }
359 : /*
360 : * An optimization is to copy all constant variables used in
361 : * functions immediately onto the value stack. Then we do not
362 : * have to check for their location later on any more. At some
363 : * point, the effect is optimal, if at least several constants
364 : * are referenced in a function (a gain on tst400a of 20% has
365 : * been observed due the small size of the function).
366 : */
367 : }
368 563411 : ret = runMALsequence(cntxt, mb, 1, 0, stk, env, 0);
369 :
370 563423 : if (!stk->keepAlive && garbageControl(getInstrPtr(mb, 0)))
371 553555 : garbageCollector(cntxt, mb, stk, env != stk);
372 563401 : if (stk && stk != env)
373 553529 : freeStack(stk);
374 563419 : if (ret == MAL_SUCCEED) {
375 545387 : switch (cntxt->qryctx.endtime) {
376 0 : case QRY_TIMEOUT:
377 0 : throw(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
378 0 : case QRY_INTERRUPT:
379 0 : throw(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_INTERRUPT);
380 : default:
381 : break;
382 : }
383 : }
384 : return ret;
385 : }
386 :
387 : /* Single instruction
388 : * It is possible to re-enter the interpreter at a specific place.
389 : * This is used in the area where we need to support co-routines.
390 : *
391 : * A special case for MAL interpretation is to execute just one instruction.
392 : * This is typically used by optimizers and schedulers that need part of the
393 : * answer to direct their actions. Or, a dataflow scheduler could step in
394 : * to enforce a completely different execution order.
395 : */
396 : str
397 60168 : reenterMAL(Client cntxt, MalBlkPtr mb, int startpc, int stoppc, MalStkPtr stk)
398 : {
399 60168 : str ret;
400 60168 : int keepAlive;
401 :
402 60168 : if (stk == NULL)
403 0 : throw(MAL, "mal.interpreter", MAL_STACK_FAIL);
404 60168 : keepAlive = stk->keepAlive;
405 60168 : ret = runMALsequence(cntxt, mb, startpc, stoppc, stk, 0, 0);
406 :
407 60159 : if (keepAlive == 0 && garbageControl(getInstrPtr(mb, 0)))
408 0 : garbageCollector(cntxt, mb, stk, stk != 0);
409 : return ret;
410 : }
411 :
412 : /*
413 : * Front ends may benefit from a more direct call to any of the MAL
414 : * procedural abstractions. The argument list points to the arguments
415 : * for the block to be executed. An old stack frame may be re-used,
416 : * but it is then up to the caller to ensure it is properly
417 : * initialized.
418 : * The call does not return values, they are ignored.
419 : */
420 : str
421 0 : callMAL(Client cntxt, MalBlkPtr mb, MalStkPtr *env, ValPtr argv[])
422 : {
423 0 : MalStkPtr stk = NULL;
424 0 : str ret = MAL_SUCCEED;
425 0 : ValPtr lhs;
426 0 : InstrPtr pci = getInstrPtr(mb, 0);
427 :
428 0 : cntxt->lastcmd = time(0);
429 :
430 0 : switch (pci->token) {
431 0 : case FUNCTIONsymbol:
432 : case FCNcall:
433 : /*
434 : * Prepare the stack frame for this operation. Copy all the arguments
435 : * in place. We assume that the caller has supplied pointers for
436 : * all arguments and return values.
437 : */
438 0 : if (*env == NULL) {
439 0 : stk = prepareMALstack(mb, mb->vsize);
440 0 : if (stk == NULL)
441 0 : throw(MAL, "mal.interpreter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
442 0 : stk->up = 0;
443 0 : *env = stk;
444 : } else {
445 : ValPtr lhs, rhs;
446 : int res = 1;
447 :
448 0 : stk = *env;
449 0 : initStack(0, res);
450 0 : if (!res)
451 0 : throw(MAL, "mal.interpreter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
452 : }
453 0 : assert(stk);
454 0 : for (int i = pci->retc; i < pci->argc; i++) {
455 0 : lhs = &stk->stk[pci->argv[i]];
456 0 : if (VALcopy(lhs, argv[i]) == NULL)
457 0 : throw(MAL, "mal.interpreter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
458 0 : if (lhs->bat)
459 0 : BBPretain(lhs->val.bval);
460 : }
461 0 : ret = runMALsequence(cntxt, mb, 1, 0, stk, 0, 0);
462 0 : break;
463 0 : case PATcall:
464 : case CMDcall:
465 : default:
466 0 : throw(MAL, "mal.interpreter", RUNTIME_UNKNOWN_INSTRUCTION);
467 : }
468 0 : if (stk)
469 0 : garbageCollector(cntxt, mb, stk, TRUE);
470 0 : if (ret == MAL_SUCCEED) {
471 0 : switch (cntxt->qryctx.endtime) {
472 0 : case QRY_TIMEOUT:
473 0 : throw(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
474 0 : case QRY_INTERRUPT:
475 0 : throw(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_INTERRUPT);
476 : default:
477 : break;
478 : }
479 : }
480 : return ret;
481 : }
482 :
483 : /*
484 : * The core of the interpreter is presented next. It takes the context
485 : * information and starts the interpretation at the designated
486 : * instruction. Note that the stack frame is aligned and initialized
487 : * in the enclosing routine. When we start executing the first
488 : * instruction, we take the wall-clock time for resource management.
489 : */
490 : str
491 12287954 : runMALsequence(Client cntxt, MalBlkPtr mb, int startpc,
492 : int stoppc, MalStkPtr stk, MalStkPtr env, InstrPtr pcicaller)
493 : {
494 12287954 : ValPtr lhs, rhs, v;
495 12287954 : InstrPtr pci = 0;
496 12287954 : int exceptionVar;
497 12287954 : str ret = MAL_SUCCEED, localGDKerrbuf = GDKerrbuf;
498 12286758 : ValRecord backups[16];
499 12286758 : ValPtr backup;
500 12286758 : int garbages[16], *garbage;
501 12286758 : int stkpc = 0;
502 12286758 : RuntimeProfileRecord runtimeProfile, runtimeProfileFunction;
503 12286758 : lng lastcheck = 0;
504 12286758 : bool startedProfileQueue = false;
505 : #define CHECKINTERVAL 1000 /* how often do we check for client disconnect */
506 12286758 : runtimeProfile.ticks = runtimeProfileFunction.ticks = 0;
507 :
508 12286758 : if (stk == NULL)
509 0 : throw(MAL, "mal.interpreter", MAL_STACK_FAIL);
510 :
511 : /* prepare extended backup and garbage structures */
512 12286758 : if (startpc + 1 == stoppc) {
513 11649538 : pci = getInstrPtr(mb, startpc);
514 11649538 : if (pci->argc > 16) {
515 128635 : backup = GDKmalloc(pci->argc * sizeof(ValRecord));
516 128394 : garbage = (int *) GDKzalloc(pci->argc * sizeof(int));
517 128470 : 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 11520903 : backup = backups;
524 11520903 : garbage = garbages;
525 11520903 : memset(garbages, 0, sizeof(garbages));
526 : }
527 637220 : } else if (mb->maxarg > 16) {
528 91779 : backup = GDKmalloc(mb->maxarg * sizeof(ValRecord));
529 91779 : garbage = (int *) GDKzalloc(mb->maxarg * sizeof(int));
530 91779 : if (backup == NULL || garbage == NULL) {
531 0 : GDKfree(backup);
532 0 : GDKfree(garbage);
533 0 : throw(MAL, "mal.interpreter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
534 : }
535 : } else {
536 545441 : backup = backups;
537 545441 : garbage = garbages;
538 545441 : memset(garbages, 0, sizeof(garbages));
539 : }
540 :
541 : /* also produce event record for start of function */
542 12286593 : if (startpc == 1 && startpc < mb->stop) {
543 637217 : startedProfileQueue = true;
544 637217 : runtimeProfileInit(cntxt, mb, stk);
545 637249 : runtimeProfileBegin(cntxt, mb, stk, getInstrPtr(mb, 0),
546 : &runtimeProfileFunction);
547 637250 : if (cntxt->sessiontimeout
548 122039 : && cntxt->qryctx.starttime - cntxt->session >
549 : cntxt->sessiontimeout) {
550 0 : runtimeProfileFinish(cntxt, mb, stk);
551 0 : if (backup != backups)
552 0 : GDKfree(backup);
553 0 : if (garbage != garbages)
554 0 : GDKfree(garbage);
555 0 : throw(MAL, "mal.interpreter",
556 : SQLSTATE(HYT00) RUNTIME_SESSION_TIMEOUT);
557 : }
558 : }
559 12286626 : stkpc = startpc;
560 12286626 : exceptionVar = -1;
561 :
562 69025056 : while (stkpc < mb->stop && stkpc != stoppc) {
563 : // incomplete block being executed, requires at least signature and end statement
564 56873064 : MT_thread_setalgorithm(NULL);
565 56781687 : pci = getInstrPtr(mb, stkpc);
566 56781687 : if (cntxt->mode == FINISHCLIENT) {
567 1 : stkpc = stoppc;
568 1 : if (ret == MAL_SUCCEED)
569 1 : ret = createException(MAL, "mal.interpreter",
570 : "prematurely stopped client");
571 : break;
572 : }
573 :
574 56781686 : if (stk->status) {
575 : /* pause procedure from SYSMON */
576 1 : if (stk->status == 'p') {
577 0 : while (stk->status == 'p')
578 0 : MT_sleep_ms(50);
579 0 : continue;
580 : }
581 : /* stop procedure from SYSMON */
582 1 : if (stk->status == 'q') {
583 1 : stkpc = mb->stop;
584 1 : ret = createException(MAL, "mal.interpreter",
585 : "Query with tag " OIDFMT
586 : " received stop signal", mb->tag);
587 1 : break;
588 : }
589 : }
590 : //Ensure we spread system resources over multiple users as well.
591 56781685 : runtimeProfileBegin(cntxt, mb, stk, pci, &runtimeProfile);
592 56763077 : if (runtimeProfile.ticks > lastcheck + CHECKINTERVAL) {
593 12310841 : if (cntxt->fdin && !mnstr_isalive(cntxt->fdin->s)) {
594 0 : cntxt->mode = FINISHCLIENT;
595 0 : stkpc = stoppc;
596 0 : ret = createException(MAL, "mal.interpreter",
597 : "prematurely stopped client");
598 0 : break;
599 : }
600 12204248 : lastcheck = runtimeProfile.ticks;
601 : }
602 :
603 56656484 : if (qptimeout > 0) {
604 0 : lng t = GDKusec();
605 0 : ATOMIC_BASE_TYPE lp = ATOMIC_GET(&cntxt->lastprint);
606 0 : if ((lng) lp + qptimeout < t) {
607 : /* if still the same, replace lastprint with current
608 : * time and print the query */
609 0 : if (ATOMIC_CAS(&cntxt->lastprint, &lp, t)) {
610 0 : const char *q = cntxt->query ? cntxt->query : NULL;
611 0 : TRC_INFO(MAL_SERVER,
612 : "%s: query already running " LLFMT "s: %.200s\n",
613 : cntxt->mythread ? cntxt->mythread : "?",
614 : (lng) (time(0) - cntxt->lastcmd), q ? q : "");
615 : }
616 : }
617 : }
618 :
619 : /* The interpreter loop
620 : * The interpreter is geared towards execution a MAL
621 : * procedure together with all its descendant
622 : * invocations. As such, it provides the MAL abtract
623 : * machine processor.
624 : *
625 : * The value-stack frame of the surrounding scope is
626 : * needed to resolve binding values. Getting (putting) a
627 : * value from (into) a surrounding scope should be guarded
628 : * with the exclusive access lock. This situation is
629 : * encapsulated by a bind() function call, whose
630 : * parameters contain the access mode required.
631 : *
632 : * The formal procedure arguments are assumed to always
633 : * occupy the first elements in the value stack.
634 : *
635 : * Before we execute an instruction the variables to be
636 : * garbage collected are identified. In the post-execution
637 : * phase they are removed.
638 : */
639 113899434 : for (int i = 0; i < pci->retc; i++)
640 57242950 : backup[i] = stk->stk[getArg(pci, i)];
641 :
642 56656484 : if (garbageControl(pci)) {
643 140302569 : for (int i = 0; i < pci->argc; i++) {
644 110279358 : int a = getArg(pci, i);
645 :
646 110279358 : if (stk->stk[a].bat && getEndScope(mb, a) == stkpc
647 20597110 : && isNotUsedIn(pci, i + 1, a))
648 10297225 : garbage[i] = a;
649 : else
650 99982133 : garbage[i] = -1;
651 : }
652 : }
653 :
654 56656484 : freeException(ret);
655 56689432 : ret = MAL_SUCCEED;
656 56689432 : switch (pci->token) {
657 3511161 : case ASSIGNsymbol:
658 : /* Assignment command
659 : * The assignment statement copies values around on
660 : * the stack frame, including multiple assignments.
661 : *
662 : * Pushing constants/initial values onto the stack is
663 : * a separate operation. It takes the constant value
664 : * discovered at compile time and stored in the symbol
665 : * table and moves it to the stackframe location. This
666 : * activity is made part of the start-up procedure.
667 : *
668 : * The before after calls should be reconsidered here,
669 : * because their. They seem superflous and the way
670 : * they are used will cause errors in multi-assignment
671 : * statements.
672 : */
673 6976451 : for (int k = 0, i = pci->retc; k < pci->retc && i < pci->argc;
674 3465290 : i++, k++) {
675 3465205 : lhs = &stk->stk[pci->argv[k]];
676 3465205 : assert(lhs->bat == isaBatType(getArgType(mb, pci, k)));
677 3465205 : rhs = &stk->stk[pci->argv[i]];
678 3465205 : assert(rhs->bat == isaBatType(getArgType(mb, pci, i)));
679 3465205 : if (VALcopy(lhs, rhs) == NULL) {
680 0 : ret = createException(MAL, "mal.interpreter",
681 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
682 0 : break;
683 3465289 : } else if (lhs->bat && !is_bat_nil(lhs->val.bval))
684 4177 : BBPretain(lhs->val.bval);
685 : }
686 : break;
687 17924369 : case PATcall:
688 17924369 : if (pci->fcn == NULL) {
689 0 : ret = createException(MAL, "mal.interpreter",
690 : "address of pattern %s.%s missing",
691 : pci->modname, pci->fcnname);
692 : } else {
693 17924369 : TRC_DEBUG(ALGO, "calling %s.%s\n",
694 : pci->modname ? pci->modname : "<null>",
695 : pci->fcnname ? pci->fcnname : "<null>");
696 17924369 : ret = (*(str (*) (Client, MalBlkPtr, MalStkPtr, InstrPtr)) pci->
697 : fcn) (cntxt, mb, stk, pci);
698 : #ifndef NDEBUG
699 17931506 : if (ret == MAL_SUCCEED) {
700 : /* check that the types of actual results match
701 : * expected results */
702 36520223 : for (int i = 0; i < pci->retc; i++) {
703 18562184 : int a = getArg(pci, i);
704 18562184 : int t = getArgType(mb, pci, i);
705 :
706 18562184 : if (isaBatType(t)) {
707 6072353 : bat bid = stk->stk[a].val.bval;
708 6072353 : BAT *_b;
709 6072353 : t = getBatType(t);
710 6072353 : assert(stk->stk[a].bat);
711 17679439 : assert(is_bat_nil(bid) ||
712 : t == TYPE_any ||
713 : ((_b = BBP_desc(bid)) != NULL &&
714 : ATOMtype(_b->ttype) == ATOMtype(t)));
715 : } else {
716 12489831 : assert(t == stk->stk[a].vtype);
717 : }
718 : }
719 : }
720 : #endif
721 : }
722 : break;
723 34628999 : case CMDcall:
724 34628999 : TRC_DEBUG(ALGO, "calling %s.%s\n",
725 : pci->modname ? pci->modname : "<null>",
726 : pci->fcnname ? pci->fcnname : "<null>");
727 34628999 : ret = malCommandCall(stk, pci);
728 : #ifndef NDEBUG
729 34625320 : if (ret == MAL_SUCCEED) {
730 : /* check that the types of actual results match
731 : * expected results */
732 69739092 : for (int i = 0; i < pci->retc; i++) {
733 35102587 : int a = getArg(pci, i);
734 35102587 : int t = getArgType(mb, pci, i);
735 :
736 35102587 : if (isaBatType(t)) {
737 : //bat bid = stk->stk[a].val.bval;
738 19400938 : t = getBatType(t);
739 19400938 : assert(stk->stk[a].bat);
740 : //assert( !is_bat_nil(bid));
741 19400938 : assert(t != TYPE_any);
742 : //assert( ATOMtype(BBP_desc(bid)->ttype) == ATOMtype(t));
743 : } else {
744 15701649 : assert(t == stk->stk[a].vtype);
745 : }
746 : }
747 : }
748 : #endif
749 : break;
750 73777 : case FCNcall: {
751 : /*
752 : * MAL function calls are relatively expensive,
753 : * because they have to assemble a new stack frame and
754 : * do housekeeping, such as garbagecollection of all
755 : * non-returned values.
756 : */
757 73777 : MalStkPtr nstk;
758 73777 : InstrPtr q;
759 73777 : int ii, arg;
760 :
761 73777 : nstk = prepareMALstack(pci->blk, pci->blk->vsize);
762 73784 : if (nstk == 0) {
763 0 : ret = createException(MAL, "mal.interpreter", MAL_STACK_FAIL);
764 0 : break;
765 : }
766 73784 : nstk->pcup = stkpc;
767 :
768 : /*safeguardStack */
769 73784 : nstk->stkdepth = nstk->stksize + stk->stkdepth;
770 73784 : nstk->calldepth = stk->calldepth + 1;
771 73784 : nstk->up = stk;
772 73784 : if (nstk->calldepth > 256) {
773 1 : ret = createException(MAL, "mal.interpreter",
774 : MAL_CALLDEPTH_FAIL);
775 1 : GDKfree(nstk);
776 1 : break;
777 : }
778 73783 : if ((unsigned) nstk->stkdepth >
779 46006 : THREAD_STACK_SIZE / sizeof(mb->var[0]) / 4 && THRhighwater()) {
780 : /* we are running low on stack space */
781 0 : ret = createException(MAL, "mal.interpreter", MAL_STACK_FAIL);
782 0 : GDKfree(nstk);
783 0 : break;
784 : }
785 :
786 : /* copy arguments onto destination stack */
787 73783 : q = getInstrPtr(pci->blk, 0);
788 73783 : arg = q->retc;
789 378253 : for (ii = pci->retc; ii < pci->argc; ii++, arg++) {
790 304470 : lhs = &nstk->stk[q->argv[arg]];
791 304470 : rhs = &stk->stk[pci->argv[ii]];
792 304470 : if (VALcopy(lhs, rhs) == NULL) {
793 0 : GDKfree(nstk);
794 0 : ret = createException(MAL, "mal.interpreter",
795 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
796 0 : break;
797 304470 : } else if (lhs->bat)
798 18 : BBPretain(lhs->val.bval);
799 : }
800 73783 : if (ret == MAL_SUCCEED && ii == pci->argc) {
801 73783 : ret = runMALsequence(cntxt, pci->blk, 1, pci->blk->stop, nstk,
802 : stk, pci);
803 73783 : garbageCollector(cntxt, pci->blk, nstk, 0);
804 73783 : arg = q->retc;
805 378253 : for (ii = pci->retc; ii < pci->argc; ii++, arg++) {
806 304470 : lhs = &nstk->stk[q->argv[arg]];
807 304470 : if (lhs->bat)
808 0 : BBPrelease(lhs->val.bval);
809 : }
810 73783 : GDKfree(nstk);
811 : }
812 : break;
813 : }
814 : case REMsymbol:
815 : break;
816 551028 : case ENDsymbol:
817 551028 : runtimeProfileExit(cntxt, mb, stk, pci, &runtimeProfile);
818 550981 : runtimeProfileExit(cntxt, mb, stk, getInstrPtr(mb, 0),
819 : &runtimeProfileFunction);
820 551157 : if (pcicaller && garbageControl(getInstrPtr(mb, 0)))
821 5994 : garbageCollector(cntxt, mb, stk, TRUE);
822 551157 : if (cntxt->qryctx.endtime == QRY_TIMEOUT) {
823 0 : freeException(ret); /* overrule exception */
824 0 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
825 0 : break;
826 551157 : } else if (cntxt->qryctx.endtime == QRY_INTERRUPT) {
827 0 : freeException(ret); /* overrule exception */
828 0 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_INTERRUPT);
829 0 : break;
830 : }
831 551157 : stkpc = mb->stop; // force end of loop
832 551157 : continue;
833 0 : default: {
834 0 : str w;
835 0 : if (pci->token < 0) {
836 : /* temporary NOOP instruction */
837 : break;
838 : }
839 0 : w = instruction2str(mb, 0, pci, FALSE);
840 0 : if (w) {
841 0 : ret = createException(MAL, "interpreter", "unkown operation:%s",
842 : w);
843 0 : GDKfree(w);
844 : } else {
845 0 : ret = createException(MAL, "interpreter",
846 : "failed instruction2str");
847 : }
848 : // runtimeProfileBegin already sets the time in the instruction
849 0 : if (cntxt->qryctx.endtime == QRY_TIMEOUT) {
850 0 : freeException(ret); /* overrule exception */
851 0 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
852 0 : break;
853 0 : } else if (cntxt->qryctx.endtime == QRY_INTERRUPT) {
854 0 : freeException(ret); /* overrule exception */
855 0 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_INTERRUPT);
856 0 : break;
857 : }
858 :
859 0 : stkpc = mb->stop;
860 0 : continue;
861 : }
862 : }
863 :
864 : /* monitoring information should reflect the input arguments,
865 : which may be removed by garbage collection */
866 : /* BEWARE, the SQL engine or MAL function could zap the block, leaving garbage behind in pci */
867 : /* this hack means we loose a closing event */
868 56141954 : if (mb->stop <= 1)
869 0 : continue;
870 56141954 : runtimeProfileExit(cntxt, mb, stk, pci, &runtimeProfile);
871 : /* when we find a timeout situation, then the result is already known
872 : * and assigned, the backup version is not removed*/
873 56173127 : if (ret == MAL_SUCCEED) {
874 113416197 : for (int i = 0; i < pci->retc; i++) {
875 57344642 : lhs = &backup[i];
876 57344642 : if (lhs->bat) {
877 25544160 : BBPrelease(lhs->val.bval);
878 31800482 : } else if (ATOMextern(lhs->vtype) &&
879 1836003 : lhs->val.pval &&
880 139379 : lhs->val.pval != ATOMnilptr(lhs->vtype) &&
881 139379 : lhs->val.pval != stk->stk[getArg(pci, i)].val.pval)
882 137093 : GDKfree(lhs->val.pval);
883 : }
884 56071555 : if (ATOMIC_GET(&GDKdebug) & CHECKMASK && exceptionVar < 0) {
885 : BAT *b;
886 :
887 113258173 : for (int i = 0; i < pci->retc; i++) {
888 57208125 : if (garbage[i] == -1
889 55411897 : && stk->stk[getArg(pci, i)].bat
890 25009527 : && !is_bat_nil(stk->stk[getArg(pci, i)].val.bval)) {
891 24959962 : assert(stk->stk[getArg(pci, i)].val.bval > 0);
892 24959962 : b = BATdescriptor(stk->stk[getArg(pci, i)].val.bval);
893 25041574 : if (b == NULL) {
894 0 : if (ret == MAL_SUCCEED)
895 0 : ret = createException(MAL, "mal.propertyCheck",
896 : SQLSTATE(HY002)
897 : RUNTIME_OBJECT_MISSING);
898 0 : continue;
899 : }
900 25041574 : BATassertProps(b);
901 25027875 : BBPunfix(b->batCacheid);
902 : }
903 : }
904 : }
905 :
906 : /* general garbage collection */
907 56098192 : if (ret == MAL_SUCCEED && garbageControl(pci)) {
908 141016256 : for (int i = 0; i < pci->argc; i++) {
909 111014518 : int a = getArg(pci, i);
910 :
911 111014518 : if (isaBatType(getArgType(mb, pci, i))) {
912 62220255 : bat bid = stk->stk[a].val.bval;
913 :
914 62220255 : if (garbage[i] >= 0) {
915 10280301 : bid = stk->stk[garbage[i]].val.bval;
916 10280301 : if (!is_bat_nil(bid)) {
917 9883856 : stk->stk[garbage[i]].val.bval = bat_nil;
918 9883856 : BBPcold(bid);
919 9863199 : BBPrelease(bid);
920 : }
921 : }
922 : }
923 : }
924 : }
925 : }
926 :
927 : /* Exception handling */
928 56187270 : if (localGDKerrbuf && localGDKerrbuf[0]) {
929 2 : if (ret == MAL_SUCCEED)
930 2 : ret = createException(MAL, "mal.interpreter", GDK_EXCEPTION);
931 : // TODO take properly care of the GDK exception
932 2 : localGDKerrbuf[0] = 0;
933 : }
934 :
935 56187270 : if (ret != MAL_SUCCEED) {
936 19267 : str msg = 0;
937 :
938 : /* Detect any exception received from the implementation. */
939 : /* The first identifier is an optional exception name */
940 19267 : if (strstr(ret, "!skip-to-end")) {
941 0 : freeException(ret);
942 0 : ret = MAL_SUCCEED;
943 0 : stkpc = mb->stop;
944 0 : continue;
945 : }
946 : /*
947 : * Exceptions are caught based on their name, which is part of the
948 : * exception message. The ANYexception variable catches all.
949 : */
950 19267 : exceptionVar = -1;
951 19267 : msg = strchr(ret, ':');
952 19267 : if (msg) {
953 19267 : exceptionVar = findVariableLength(mb, ret, (int) (msg - ret));
954 : }
955 19270 : if (exceptionVar == -1)
956 19268 : exceptionVar = findVariableLength(mb, "ANYexception", 12);
957 :
958 : /* unknown exceptions lead to propagation */
959 19268 : if (exceptionVar == -1) {
960 19253 : if (cntxt->qryctx.endtime == QRY_TIMEOUT) {
961 11 : freeException(ret); /* overrule exception */
962 11 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
963 19242 : } else if (cntxt->qryctx.endtime == QRY_INTERRUPT) {
964 0 : freeException(ret); /* overrule exception */
965 0 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_INTERRUPT);
966 : }
967 19253 : stkpc = mb->stop;
968 19253 : continue;
969 : }
970 : /* assure correct variable type */
971 17 : if (getVarType(mb, exceptionVar) == TYPE_str) {
972 : /* watch out for concurrent access */
973 17 : MT_lock_set(&mal_contextLock);
974 17 : v = &stk->stk[exceptionVar];
975 17 : if (v->val.sval)
976 5 : freeException(v->val.sval); /* old exception */
977 17 : VALset(v, TYPE_str, ret);
978 17 : ret = MAL_SUCCEED;
979 17 : MT_lock_unset(&mal_contextLock);
980 : } else {
981 0 : mnstr_printf(cntxt->fdout, "%s", ret);
982 0 : freeException(ret);
983 0 : ret = MAL_SUCCEED;
984 : }
985 : /* position yourself at the catch instruction for further decisions */
986 : /* skipToCatch(exceptionVar,@2,@3) */
987 : /* skip to catch block or end */
988 187 : for (; stkpc < mb->stop; stkpc++) {
989 187 : InstrPtr l = getInstrPtr(mb, stkpc);
990 187 : if (l->barrier == CATCHsymbol) {
991 : int j;
992 17 : for (j = 0; j < l->retc; j++)
993 17 : if (getArg(l, j) == exceptionVar)
994 : break;
995 0 : else if (getArgName(mb, l, j) && strcmp(getArgName(mb, l, j), "ANYexception") == 0)
996 : break;
997 17 : if (j < l->retc)
998 : break;
999 : }
1000 : }
1001 17 : if (stkpc == mb->stop) {
1002 0 : if (cntxt->qryctx.endtime == QRY_TIMEOUT) {
1003 0 : freeException(ret); /* overrule exception */
1004 0 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
1005 0 : stkpc = mb->stop;
1006 0 : } else if (cntxt->qryctx.endtime == QRY_INTERRUPT) {
1007 0 : freeException(ret); /* overrule exception */
1008 0 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_INTERRUPT);
1009 0 : stkpc = mb->stop;
1010 : }
1011 0 : continue;
1012 : }
1013 17 : pci = getInstrPtr(mb, stkpc);
1014 : }
1015 :
1016 : /*
1017 : * After the expression has been evaluated we should check for
1018 : * a possible change in the control flow.
1019 : */
1020 56168020 : switch (pci->barrier) {
1021 1363930 : case BARRIERsymbol:
1022 1363930 : v = &stk->stk[getDestVar(pci)];
1023 : /* skip to end of barrier, depends on the type */
1024 1363930 : switch (v->vtype) {
1025 1363096 : case TYPE_bit:
1026 1363096 : if (v->val.btval == FALSE || is_bit_nil(v->val.btval))
1027 1314865 : stkpc = pci->jump;
1028 : break;
1029 0 : case TYPE_bte:
1030 0 : if (is_bte_nil(v->val.btval))
1031 0 : stkpc = pci->jump;
1032 : break;
1033 809 : case TYPE_oid:
1034 809 : if (is_oid_nil(v->val.oval))
1035 599 : stkpc = pci->jump;
1036 : break;
1037 0 : case TYPE_sht:
1038 0 : if (is_sht_nil(v->val.shval))
1039 0 : stkpc = pci->jump;
1040 : break;
1041 9 : case TYPE_int:
1042 9 : if (is_int_nil(v->val.ival))
1043 0 : stkpc = pci->jump;
1044 : break;
1045 15 : case TYPE_lng:
1046 15 : if (is_lng_nil(v->val.lval))
1047 0 : stkpc = pci->jump;
1048 : break;
1049 : #ifdef HAVE_HGE
1050 0 : case TYPE_hge:
1051 0 : if (is_hge_nil(v->val.hval))
1052 0 : stkpc = pci->jump;
1053 : break;
1054 : #endif
1055 1 : case TYPE_flt:
1056 1 : if (is_flt_nil(v->val.fval))
1057 0 : stkpc = pci->jump;
1058 : break;
1059 0 : case TYPE_dbl:
1060 0 : if (is_dbl_nil(v->val.dval))
1061 0 : stkpc = pci->jump;
1062 : break;
1063 0 : case TYPE_str:
1064 0 : if (strNil(v->val.sval))
1065 0 : stkpc = pci->jump;
1066 : break;
1067 0 : default: {
1068 0 : char name[IDLENGTH] = { 0 };
1069 0 : ret = createException(MAL, "mal.interpreter",
1070 : "%s: Unknown barrier type", getVarNameIntoBuffer(mb,
1071 : getDestVar
1072 : (pci), name));
1073 : }
1074 : }
1075 1363930 : stkpc++;
1076 1363930 : break;
1077 14255341 : case LEAVEsymbol:
1078 : case REDOsymbol:
1079 14255341 : v = &stk->stk[getDestVar(pci)];
1080 : /* skip to end of barrier, depending on the type */
1081 14255341 : switch (v->vtype) {
1082 852 : case TYPE_bit:
1083 852 : if (v->val.btval == TRUE)
1084 439 : stkpc = pci->jump;
1085 : else
1086 413 : stkpc++;
1087 : break;
1088 0 : case TYPE_str:
1089 0 : if (!strNil(v->val.sval))
1090 0 : stkpc = pci->jump;
1091 : else
1092 0 : stkpc++;
1093 : break;
1094 44115 : case TYPE_oid:
1095 44115 : if (!is_oid_nil(v->val.oval))
1096 43910 : stkpc = pci->jump;
1097 : else
1098 205 : stkpc++;
1099 : break;
1100 0 : case TYPE_sht:
1101 0 : if (!is_sht_nil(v->val.shval))
1102 0 : stkpc = pci->jump;
1103 : else
1104 0 : stkpc++;
1105 : break;
1106 119 : case TYPE_int:
1107 119 : if (!is_int_nil(v->val.ival))
1108 114 : stkpc = pci->jump;
1109 : else
1110 5 : stkpc++;
1111 : break;
1112 0 : case TYPE_bte:
1113 0 : if (!is_bte_nil(v->val.btval))
1114 0 : stkpc = pci->jump;
1115 : else
1116 0 : stkpc++;
1117 : break;
1118 14210253 : case TYPE_lng:
1119 14210253 : if (!is_lng_nil(v->val.lval))
1120 14210238 : stkpc = pci->jump;
1121 : else
1122 15 : stkpc++;
1123 : break;
1124 : #ifdef HAVE_HGE
1125 0 : case TYPE_hge:
1126 0 : if (!is_hge_nil(v->val.hval))
1127 0 : stkpc = pci->jump;
1128 : else
1129 0 : stkpc++;
1130 : break;
1131 : #endif
1132 2 : case TYPE_flt:
1133 2 : if (!is_flt_nil(v->val.fval))
1134 1 : stkpc = pci->jump;
1135 : else
1136 1 : stkpc++;
1137 : break;
1138 0 : case TYPE_dbl:
1139 0 : if (!is_dbl_nil(v->val.dval))
1140 0 : stkpc = pci->jump;
1141 : else
1142 0 : stkpc++;
1143 : break;
1144 : default:
1145 : break;
1146 : }
1147 : break;
1148 39 : case CATCHsymbol:
1149 : /* catch blocks are skipped unless
1150 : searched for explicitly */
1151 39 : if (exceptionVar < 0) {
1152 19 : stkpc = pci->jump;
1153 19 : break;
1154 : }
1155 20 : exceptionVar = -1;
1156 20 : stkpc++;
1157 20 : break;
1158 48357 : case EXITsymbol:
1159 48357 : if (getDestVar(pci) == exceptionVar)
1160 0 : exceptionVar = -1;
1161 48357 : stkpc++;
1162 48357 : break;
1163 16 : case RAISEsymbol:
1164 16 : exceptionVar = getDestVar(pci);
1165 : //freeException(ret);
1166 16 : ret = MAL_SUCCEED;
1167 16 : if (getVarType(mb, getDestVar(pci)) == TYPE_str) {
1168 14 : char nme[256];
1169 14 : snprintf(nme, 256, "%s.%s[%d]", getModuleId(getInstrPtr(mb, 0)),
1170 14 : getFunctionId(getInstrPtr(mb, 0)), stkpc);
1171 14 : ret = createException(MAL, nme, "%s",
1172 14 : stk->stk[getDestVar(pci)].val.sval);
1173 : }
1174 : /* skipToCatch(exceptionVar, @2, stk) */
1175 : /* skip to catch block or end */
1176 362 : for (; stkpc < mb->stop; stkpc++) {
1177 349 : InstrPtr l = getInstrPtr(mb, stkpc);
1178 349 : if (l->barrier == CATCHsymbol) {
1179 : int j;
1180 3 : for (j = 0; j < l->retc; j++)
1181 3 : if (getArg(l, j) == exceptionVar)
1182 : break;
1183 0 : else if (getArgName(mb, l, j) && strcmp(getArgName(mb, l, j), "ANYexception") == 0)
1184 : break;
1185 3 : if (j < l->retc)
1186 : break;
1187 : }
1188 : }
1189 16 : if (stkpc == mb->stop) {
1190 13 : runtimeProfileExit(cntxt, mb, stk, pci, &runtimeProfile);
1191 13 : runtimeProfileExit(cntxt, mb, stk, getInstrPtr(mb, 0),
1192 : &runtimeProfileFunction);
1193 13 : break;
1194 : }
1195 : break;
1196 67535 : case RETURNsymbol:
1197 : /* a fake multi-assignment */
1198 67535 : if (env != NULL && pcicaller != NULL) {
1199 : InstrPtr pp = pci;
1200 137531 : pci = pcicaller;
1201 137531 : for (int i = 0; i < pci->retc; i++) {
1202 69998 : rhs = &stk->stk[pp->argv[i]];
1203 69998 : lhs = &env->stk[pci->argv[i]];
1204 69998 : if (VALcopy(lhs, rhs) == NULL) {
1205 0 : ret = createException(MAL, "mal.interpreter",
1206 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
1207 0 : break;
1208 69998 : } else if (lhs->bat)
1209 2916 : BBPretain(lhs->val.bval);
1210 : }
1211 67533 : if (garbageControl(getInstrPtr(mb, 0)))
1212 67494 : garbageCollector(cntxt, mb, stk, TRUE);
1213 : /* reset the clock */
1214 67533 : runtimeProfileExit(cntxt, mb, stk, pp, &runtimeProfile);
1215 67533 : runtimeProfileExit(cntxt, mb, stk, getInstrPtr(mb, 0),
1216 : &runtimeProfileFunction);
1217 : }
1218 67535 : stkpc = mb->stop;
1219 67535 : continue;
1220 40432802 : default:
1221 40432802 : stkpc++;
1222 : }
1223 56100485 : if (cntxt->qryctx.endtime == QRY_TIMEOUT) {
1224 0 : if (ret == MAL_SUCCEED)
1225 0 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
1226 0 : stkpc = mb->stop;
1227 56100485 : } else if (cntxt->qryctx.endtime == QRY_INTERRUPT) {
1228 0 : if (ret == MAL_SUCCEED)
1229 0 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_INTERRUPT);
1230 0 : stkpc = mb->stop;
1231 : }
1232 : }
1233 :
1234 : /* if we could not find the exception variable, cascade a new one */
1235 : /* don't add 'exception not caught' extra message for MAL sequences besides main function calls */
1236 12151994 : if (exceptionVar >= 0 && (ret == MAL_SUCCEED || !pcicaller)) {
1237 1 : char nme[256];
1238 1 : snprintf(nme, 256, "%s.%s[%d]", getModuleId(getInstrPtr(mb, 0)),
1239 1 : getFunctionId(getInstrPtr(mb, 0)), stkpc);
1240 1 : if (ret != MAL_SUCCEED) {
1241 0 : str new, n;
1242 0 : n = createException(MAL, nme, "exception not caught");
1243 0 : if (n) {
1244 0 : new = GDKzalloc(strlen(ret) + strlen(n) + 16);
1245 0 : if (new) {
1246 0 : strcpy(new, ret);
1247 0 : if (new[strlen(new) - 1] != '\n')
1248 0 : strcat(new, "\n");
1249 0 : strcat(new, "!");
1250 0 : strcat(new, n);
1251 0 : freeException(n);
1252 0 : freeException(ret);
1253 0 : ret = new;
1254 : } else {
1255 0 : freeException(ret);
1256 0 : ret = n;
1257 : }
1258 : }
1259 : } else {
1260 1 : ret = createException(MAL, nme, "Exception not caught");
1261 : }
1262 : }
1263 12151994 : if (startedProfileQueue)
1264 636603 : runtimeProfileFinish(cntxt, mb, stk);
1265 12152643 : if (backup != backups)
1266 218591 : GDKfree(backup);
1267 12145400 : if (garbage != garbages)
1268 220174 : GDKfree(garbage);
1269 : return ret;
1270 : }
1271 :
1272 :
1273 : /*
1274 : * MAL API
1275 : * The linkage between MAL interpreter and compiled C-routines
1276 : * is kept as simple as possible.
1277 : * Basically we distinguish four kinds of calling conventions:
1278 : * CMDcall, FCNcall and PATcall.
1279 : * The FCNcall indicates calling a MAL procedure, which leads
1280 : * to a recursive call to the interpreter.
1281 : *
1282 : * CMDcall initiates calling a linked function, passing pointers
1283 : * to the parameters and result variable, i.e. f(ptr a0,..., ptr aN)
1284 : * The function returns a MAL-SUCCEED upon success and a pointer
1285 : * to an exception string upon failure.
1286 : * Failure leads to raise-ing an exception in the interpreter loop,
1287 : * by either looking up the relevant exception message in the module
1288 : * administration or construction of a standard string.
1289 : *
1290 : * The PATcall initiates a call which contains the MAL context,
1291 : * i.e. f(MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
1292 : * The mb provides access to the code definitions. It is primarilly
1293 : * used by routines intended to manipulate the code base itself, such
1294 : * as the optimizers. The Mal stack frame pointer provides access
1295 : * to the values maintained. The arguments passed are offsets
1296 : * into the stack frame rather than pointers to the actual value.
1297 : *
1298 : * BAT parameters require some care. Ideally, a BAT should not be kept
1299 : * around long. This would mean that each time we access a BAT it has to be
1300 : * pinned in memory and upon leaving the function, it is unpinned.
1301 : * This degrades performance significantly.
1302 : * After the parameters are fixed, we can safely free the destination
1303 : * variable and re-initialize it to nil.
1304 : *
1305 : */
1306 :
1307 : /*
1308 : * The type dispatching table in getArgReference can be removed if we
1309 : * determine at compile time the address offset within a ValRecord.
1310 : * We leave this optimization for the future, it leads to about 10%
1311 : * improvement (100ms for 1M calls).
1312 : *
1313 : * Flow of control statements
1314 : * Each assignment (function call) may be part of the initialization
1315 : * of a barrier- block. In that case we have to test the
1316 : * outcome of the operation and possibly skip the block altogether.
1317 : * The latter is implemented as a linear scan for the corresponding
1318 : * labeled statemtent. This might be optimized later.
1319 : *
1320 : * You can skip to a catch block by searching for the corresponding 'lab'
1321 : * The return value should be set to pass the error automatically upon
1322 : * reaching end of function block.
1323 : */
1324 :
1325 : /*
1326 : * Each time we enter a barrier block, we could keep its position in the
1327 : * interpreter stack frame. It forms the starting point to issue a redo.
1328 : * Unfortunately, this does not easily work in the presence of optimizers, which
1329 : * may change the order/block structure. Therefore, we simple have to search
1330 : * the beginning or ensure that during chkProgram the barrier/redo/leave/catch
1331 : * jumps are re-established.
1332 : *
1333 : * Exception handling
1334 : * Calling a built-in or user-defined routine may lead to an error or a
1335 : * cached status message to be dealt with in MAL.
1336 : * To improve error handling in MAL, an exception handling
1337 : * scheme based on @sc{catch}-@sc{exit} blocks. The @sc{catch}
1338 : * statement identifies a (string-valued) variable, which carries the
1339 : * exception message from
1340 : * the originally failed routine or @sc{raise} exception assignment.
1341 : * During normal processing @sc{catch}-@sc{exit} blocks are simply skipped.
1342 : * Upon receiving an exception status from a function call, we set the
1343 : * exception variable and skip to the first associated @sc{catch}-@sc{exit}
1344 : * block.
1345 : * MAL interpretation then continues until it reaches the end of the block.
1346 : * If no exception variable was defined, we should abandon the function
1347 : * alltogether searching for a catch block at a higher layer.
1348 : *
1349 : * For the time being we have ignored cascaded/stacked exceptions.
1350 : * The policy is to pass the first recognized exception to a context
1351 : * in which it can be handled.
1352 : *
1353 : * Exceptions raised within a linked-in function requires some care.
1354 : * First, the called procedure does not know anything about the MAL
1355 : * interpreter context. Thus, we need to return all relevant information
1356 : * upon leaving the linked library routine.
1357 : *
1358 : * Second, exceptional cases can be handled deeply in the recursion, where they
1359 : * may also be handled, i.e. by issueing an GDKerror message. The upper layers
1360 : * merely receive a negative integer value to indicate occurrence of an
1361 : * error somewhere in the calling sequence.
1362 : * We then have to also look into GDKerrbuf to see if there was
1363 : * an error raised deeply inside the system.
1364 : *
1365 : * The policy is to require all C-functions to return a string-pointer.
1366 : * Upon a successful call, it is a NULL string. Otherwise it contains an
1367 : * encoding of the exceptional state encountered. This message
1368 : * starts with the exception identifer, followed by contextual details.
1369 : */
1370 :
1371 : /*
1372 : * Garbage collection
1373 : * Garbage collection is relatively straightforward, because most values are
1374 : * retained on the stackframe of an interpreter call. However, two storage
1375 : * types and possibly user-defined type garbage collector definitions
1376 : * require attention: BATs and strings.
1377 : *
1378 : * A key issue is to deal with temporary BATs in an efficient way.
1379 : * References to bats in the buffer pool may cause dangling references
1380 : * at the language level. This appears as soons as your share
1381 : * a reference and delete the BAT from one angle. If not careful, the
1382 : * dangling pointer may subsequently be associated with another BAT
1383 : *
1384 : * All string values are private to the VALrecord, which means they
1385 : * have to be freed explicitly before a MAL function returns.
1386 : * The first step is to always safe the destination variable
1387 : * before a function call is made.
1388 : */
1389 : void
1390 164032384 : garbageElement(Client cntxt, ValPtr v)
1391 : {
1392 164032384 : (void) cntxt;
1393 164032384 : if (v->bat) {
1394 : /*
1395 : * All operations are responsible to properly set the
1396 : * reference count of the BATs being produced or destroyed.
1397 : * The libraries should not leave the
1398 : * physical reference count being set. This is only
1399 : * allowed during the execution of a GDK operation.
1400 : * All references should be logical.
1401 : */
1402 20145145 : bat bid = v->val.bval;
1403 : /* printf("garbage collecting: %d lrefs=%d refs=%d\n",
1404 : bid, BBP_lrefs(bid),BBP_refs(bid)); */
1405 20145145 : v->val.bval = bat_nil;
1406 20145145 : v->bat = false;
1407 20145145 : if (is_bat_nil(bid))
1408 : return;
1409 316624 : BBPcold(bid);
1410 316622 : BBPrelease(bid);
1411 143887239 : } else if (ATOMstorage(v->vtype) == TYPE_str) {
1412 36807077 : GDKfree(v->val.sval);
1413 36807400 : v->val.sval = NULL;
1414 36807400 : v->len = 0;
1415 107080162 : } else if (0 < v->vtype && v->vtype < MAXATOMS && ATOMextern(v->vtype)) {
1416 2640 : GDKfree(v->val.pval);
1417 2640 : v->val.pval = 0;
1418 2640 : v->len = 0;
1419 : }
1420 : }
1421 :
1422 : /*
1423 : * Before we return from the interpreter, we should free all
1424 : * dynamically allocated objects and adjust the BAT reference counts.
1425 : * Early experience shows that for small stack frames the overhead
1426 : * is about 200 ms for a 1M function call loop (tst400e). This means that
1427 : * for the time being we do not introduce more complex garbage
1428 : * administration code.
1429 : *
1430 : * Also note that for top-level stack frames (no environment available),
1431 : * we should retain the value stack because it acts as a global variables.
1432 : * This situation is indicated by the 'global' in the stack frame.
1433 : * Upon termination of the session, the stack should be cleared.
1434 : * Beware that variables may be know polymorphic, their actual
1435 : * type should be saved for variables that recide on a global
1436 : * stack frame.
1437 : */
1438 : void
1439 738816 : garbageCollector(Client cntxt, MalBlkPtr mb, MalStkPtr stk, int flag)
1440 : {
1441 738816 : assert(mb->vtop <= mb->vsize);
1442 738816 : assert(stk->stktop <= stk->stksize);
1443 : (void) flag;
1444 : (void) mb;
1445 : (void) cntxt;
1446 164743834 : for (int k = 0; k < stk->stktop; k++) {
1447 : // if (isVarCleanup(mb, k) ){
1448 164005035 : ValPtr v = &stk->stk[k];
1449 164005035 : garbageElement(cntxt, v);
1450 164005018 : *v = (ValRecord) {
1451 : .vtype = TYPE_int,
1452 : .val.ival = int_nil,
1453 : .bat = false,
1454 : };
1455 : // }
1456 : }
1457 738799 : }
|