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 33316437 : 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 33316437 : return (ptr) &stk->stk[pci->argv[k]].val;
43 : }
44 :
45 : static str
46 30844378 : malCommandCall(MalStkPtr stk, InstrPtr pci)
47 : {
48 30844378 : str ret = MAL_SUCCEED;
49 :
50 30844378 : switch (pci->argc) {
51 0 : case 0:
52 0 : ret = (*(str (*) (void)) pci->fcn)();
53 0 : break;
54 10320 : case 1:
55 10320 : ret = (*(str (*) (void *)) pci->fcn)(
56 : getArgReference(stk, pci, 0));
57 10320 : break;
58 145663 : case 2:
59 145663 : ret = (*(str (*) (void *, void *)) pci->fcn)(
60 : getArgReference(stk, pci, 0),
61 : getArgReference(stk, pci, 1));
62 145663 : break;
63 28930620 : case 3:
64 28930620 : ret = (*(str (*) (void *, void *, void *)) pci->fcn)(
65 : getArgReference(stk, pci, 0),
66 : getArgReference(stk, pci, 1),
67 : getArgReference(stk, pci, 2));
68 28930620 : break;
69 569888 : case 4:
70 569888 : 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 569888 : break;
76 747265 : case 5:
77 747265 : 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 747265 : break;
84 4066 : case 6:
85 4066 : ret = (*(str (*) (void *, void *, void *, void *, void *,
86 4066 : 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 4066 : break;
94 94183 : case 7:
95 94183 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
96 94183 : 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 94183 : break;
105 326612 : case 8:
106 326612 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
107 326612 : 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 326612 : break;
117 15578 : case 9:
118 15578 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
119 15578 : 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 15578 : 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 49 : case 11:
145 49 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
146 49 : 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 49 : break;
159 102 : case 12:
160 102 : ret = (* (str (*) (void *, void *, void *, void *, void *, void *,
161 : void *, void *, void *, void *, void *,
162 102 : 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 102 : 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 7685854 : isNotUsedIn(InstrPtr p, int start, int a)
284 : {
285 19275267 : for (int k = start; k < p->argc; k++)
286 11599847 : if (getArg(p, k) == a)
287 : return false;
288 : return true;
289 : }
290 :
291 : MalStkPtr
292 673938 : prepareMALstack(MalBlkPtr mb, int size)
293 : {
294 673938 : MalStkPtr stk = NULL;
295 673938 : int res = 1;
296 673938 : ValPtr lhs, rhs;
297 :
298 673938 : stk = newGlobalStack(size);
299 673936 : if (!stk)
300 : return NULL;
301 673936 : stk->stktop = mb->vtop;
302 673936 : stk->blk = mb;
303 673936 : stk->memory = 0;
304 113855554 : initStack(0, res);
305 673929 : if (!res) {
306 0 : freeStack(stk);
307 0 : return NULL;
308 : }
309 : return stk;
310 : }
311 :
312 : str
313 581584 : runMAL(Client cntxt, MalBlkPtr mb, MalBlkPtr mbcaller, MalStkPtr env)
314 : {
315 581584 : MalStkPtr stk = NULL;
316 581584 : ValPtr lhs, rhs;
317 581584 : str ret;
318 581584 : (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 581584 : cntxt->lastcmd = time(0);
335 581584 : ATOMIC_SET(&cntxt->lastprint, GDKusec());
336 581584 : if (env != NULL) {
337 9932 : int res = 1;
338 9932 : stk = env;
339 9932 : if (mb != stk->blk)
340 0 : throw(MAL, "mal.interpreter", "misalignment of symbols");
341 9932 : if (mb->vtop > stk->stksize)
342 0 : throw(MAL, "mal.interpreter", "stack too small");
343 26073 : initStack(env->stkbot, res);
344 9932 : if (!res)
345 0 : throw(MAL, "mal.interpreter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
346 : } else {
347 571652 : stk = prepareMALstack(mb, mb->vsize);
348 571649 : if (stk == 0)
349 0 : throw(MAL, "mal.interpreter", MAL_STACK_FAIL);
350 571649 : stk->blk = mb;
351 : /*safeguardStack */
352 571649 : 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 581581 : ret = runMALsequence(cntxt, mb, 1, 0, stk, env, 0);
369 :
370 581584 : if (!stk->keepAlive && garbageControl(getInstrPtr(mb, 0)))
371 571652 : garbageCollector(cntxt, mb, stk, env != stk);
372 581567 : if (stk && stk != env)
373 571634 : freeStack(stk);
374 581583 : if (ret == MAL_SUCCEED) {
375 563846 : 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 105013 : reenterMAL(Client cntxt, MalBlkPtr mb, int startpc, int stoppc, MalStkPtr stk)
398 : {
399 105013 : str ret;
400 105013 : int keepAlive;
401 :
402 105013 : if (stk == NULL)
403 0 : throw(MAL, "mal.interpreter", MAL_STACK_FAIL);
404 105013 : keepAlive = stk->keepAlive;
405 105013 : ret = runMALsequence(cntxt, mb, startpc, stoppc, stk, 0, 0);
406 :
407 105014 : 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 9478486 : runMALsequence(Client cntxt, MalBlkPtr mb, int startpc,
492 : int stoppc, MalStkPtr stk, MalStkPtr env, InstrPtr pcicaller)
493 : {
494 9478486 : ValPtr lhs, rhs, v;
495 9478486 : InstrPtr pci = 0;
496 9478486 : int exceptionVar;
497 9478486 : str ret = MAL_SUCCEED, localGDKerrbuf = GDKerrbuf;
498 9477975 : ValRecord backups[16];
499 9477975 : ValPtr backup;
500 9477975 : int garbages[16], *garbage;
501 9477975 : int stkpc = 0;
502 9477975 : RuntimeProfileRecord runtimeProfile, runtimeProfileFunction;
503 9477975 : lng lastcheck = 0;
504 9477975 : bool startedProfileQueue = false;
505 : #define CHECKINTERVAL 1000 /* how often do we check for client disconnect */
506 9477975 : runtimeProfile.ticks = runtimeProfileFunction.ticks = 0;
507 :
508 9477975 : if (stk == NULL)
509 0 : throw(MAL, "mal.interpreter", MAL_STACK_FAIL);
510 :
511 : /* prepare extended backup and garbage structures */
512 9477975 : if (startpc + 1 == stoppc) {
513 8821803 : pci = getInstrPtr(mb, startpc);
514 8821803 : if (pci->argc > 16) {
515 68247 : backup = GDKmalloc(pci->argc * sizeof(ValRecord));
516 68241 : garbage = (int *) GDKzalloc(pci->argc * sizeof(int));
517 68235 : 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 8753556 : backup = backups;
524 8753556 : garbage = garbages;
525 8753556 : memset(garbages, 0, sizeof(garbages));
526 : }
527 656172 : } else if (mb->maxarg > 16) {
528 113495 : backup = GDKmalloc(mb->maxarg * sizeof(ValRecord));
529 113495 : garbage = (int *) GDKzalloc(mb->maxarg * sizeof(int));
530 113495 : 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 542677 : backup = backups;
537 542677 : garbage = garbages;
538 542677 : memset(garbages, 0, sizeof(garbages));
539 : }
540 :
541 : /* also produce event record for start of function */
542 9477963 : if (startpc == 1 && startpc < mb->stop) {
543 656181 : startedProfileQueue = true;
544 656181 : runtimeProfileInit(cntxt, mb, stk);
545 656183 : runtimeProfileBegin(cntxt, mb, stk, getInstrPtr(mb, 0),
546 : &runtimeProfileFunction);
547 656182 : if (cntxt->sessiontimeout
548 126217 : && 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 9477964 : stkpc = startpc;
560 9477964 : exceptionVar = -1;
561 :
562 59628780 : while (stkpc < mb->stop && stkpc != stoppc) {
563 : // incomplete block being executed, requires at least signature and end statement
564 50156637 : MT_thread_setalgorithm(NULL);
565 50153887 : pci = getInstrPtr(mb, stkpc);
566 50153887 : 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 50153886 : freeException(ret);
575 50156991 : ret = MAL_SUCCEED;
576 :
577 50156991 : if (stk->status) {
578 : /* pause procedure from SYSMON */
579 1 : if (stk->status == 'p') {
580 0 : while (stk->status == 'p')
581 0 : MT_sleep_ms(50);
582 0 : continue;
583 : }
584 : /* stop procedure from SYSMON */
585 1 : if (stk->status == 'q') {
586 1 : stkpc = mb->stop;
587 1 : ret = createException(MAL, "mal.interpreter",
588 : "Query with tag " OIDFMT
589 : " received stop signal", mb->tag);
590 1 : break;
591 : }
592 : }
593 : //Ensure we spread system resources over multiple users as well.
594 50156990 : runtimeProfileBegin(cntxt, mb, stk, pci, &runtimeProfile);
595 50154855 : if (runtimeProfile.ticks > lastcheck + CHECKINTERVAL) {
596 9583791 : if (cntxt->fdin && TIMEOUT_TEST(&cntxt->qryctx)) {
597 2 : if (cntxt->qryctx.endtime != QRY_INTERRUPT && cntxt->qryctx.endtime != QRY_TIMEOUT)
598 0 : cntxt->mode = FINISHCLIENT;
599 2 : switch (cntxt->qryctx.endtime) {
600 2 : case QRY_TIMEOUT:
601 2 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
602 2 : break;
603 0 : case QRY_INTERRUPT:
604 0 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_INTERRUPT);
605 0 : break;
606 0 : default:
607 0 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) "Client disconnected");
608 0 : cntxt->mode = FINISHCLIENT;
609 0 : break;
610 : }
611 : break;
612 : }
613 9583755 : lastcheck = runtimeProfile.ticks;
614 : }
615 :
616 50154819 : if (qptimeout > 0) {
617 0 : lng t = GDKusec();
618 0 : ATOMIC_BASE_TYPE lp = ATOMIC_GET(&cntxt->lastprint);
619 0 : if ((lng) lp + qptimeout < t) {
620 : /* if still the same, replace lastprint with current
621 : * time and print the query */
622 0 : if (ATOMIC_CAS(&cntxt->lastprint, &lp, t)) {
623 0 : const char *q = cntxt->query ? cntxt->query : NULL;
624 0 : TRC_INFO(MAL_SERVER,
625 : "%s: query already running " LLFMT "s: %.200s\n",
626 : cntxt->mythread ? cntxt->mythread : "?",
627 : (lng) (time(0) - cntxt->lastcmd), q ? q : "");
628 : }
629 : }
630 : }
631 :
632 : /* The interpreter loop
633 : * The interpreter is geared towards execution a MAL
634 : * procedure together with all its descendant
635 : * invocations. As such, it provides the MAL abstract
636 : * machine processor.
637 : *
638 : * The value-stack frame of the surrounding scope is
639 : * needed to resolve binding values. Getting (putting) a
640 : * value from (into) a surrounding scope should be guarded
641 : * with the exclusive access lock. This situation is
642 : * encapsulated by a bind() function call, whose
643 : * parameters contain the access mode required.
644 : *
645 : * The formal procedure arguments are assumed to always
646 : * occupy the first elements in the value stack.
647 : *
648 : * Before we execute an instruction the variables to be
649 : * garbage collected are identified. In the post-execution
650 : * phase they are removed.
651 : */
652 100593912 : for (int i = 0; i < pci->retc; i++)
653 50439093 : backup[i] = stk->stk[getArg(pci, i)];
654 :
655 50154819 : if (garbageControl(pci)) {
656 119238262 : for (int i = 0; i < pci->argc; i++) {
657 92935535 : int a = getArg(pci, i);
658 :
659 92935535 : if (stk->stk[a].bat && getEndScope(mb, a) == stkpc
660 15371708 : && isNotUsedIn(pci, i + 1, a))
661 7675965 : garbage[i] = a;
662 : else
663 85259570 : garbage[i] = -1;
664 : }
665 : }
666 :
667 50154819 : switch (pci->token) {
668 3548647 : case ASSIGNsymbol:
669 : /* Assignment command
670 : * The assignment statement copies values around on
671 : * the stack frame, including multiple assignments.
672 : *
673 : * Pushing constants/initial values onto the stack is
674 : * a separate operation. It takes the constant value
675 : * discovered at compile time and stored in the symbol
676 : * table and moves it to the stackframe location. This
677 : * activity is made part of the start-up procedure.
678 : *
679 : * The before after calls should be reconsidered here,
680 : * because their. They seem superfluous and the way
681 : * they are used will cause errors in multi-assignment
682 : * statements.
683 : */
684 7050979 : for (int k = 0, i = pci->retc; k < pci->retc && i < pci->argc;
685 3502332 : i++, k++) {
686 3502200 : lhs = &stk->stk[pci->argv[k]];
687 3502200 : assert(lhs->bat == isaBatType(getArgType(mb, pci, k)));
688 3502200 : rhs = &stk->stk[pci->argv[i]];
689 3502200 : assert(rhs->bat == isaBatType(getArgType(mb, pci, i)));
690 3502200 : if (VALcopy(lhs, rhs) == NULL) {
691 0 : ret = createException(MAL, "mal.interpreter",
692 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
693 0 : break;
694 3502332 : } else if (lhs->bat && !is_bat_nil(lhs->val.bval))
695 5784 : BBPretain(lhs->val.bval);
696 : }
697 : break;
698 15118179 : case PATcall:
699 15118179 : if (pci->fcn == NULL) {
700 0 : ret = createException(MAL, "mal.interpreter",
701 : "address of pattern %s.%s missing",
702 : pci->modname, pci->fcnname);
703 : } else {
704 15118179 : TRC_DEBUG(ALGO, "calling %s.%s\n",
705 : pci->modname ? pci->modname : "<null>",
706 : pci->fcnname ? pci->fcnname : "<null>");
707 15118179 : ret = (*(str (*) (Client, MalBlkPtr, MalStkPtr, InstrPtr)) pci->
708 : fcn) (cntxt, mb, stk, pci);
709 : #ifndef NDEBUG
710 15112721 : if (ret == MAL_SUCCEED) {
711 : /* check that the types of actual results match
712 : * expected results */
713 30707940 : for (int i = 0; i < pci->retc; i++) {
714 15614429 : int a = getArg(pci, i);
715 15614429 : int t = getArgType(mb, pci, i);
716 :
717 15614429 : if (isaBatType(t)) {
718 4387276 : bat bid = stk->stk[a].val.bval;
719 4387276 : BAT *_b;
720 4387276 : t = getBatType(t);
721 4387276 : assert(stk->stk[a].bat);
722 12656048 : assert(is_bat_nil(bid) ||
723 : t == TYPE_any ||
724 : ((_b = BBP_desc(bid)) != NULL &&
725 : ATOMtype(_b->ttype) == ATOMtype(t)));
726 : } else {
727 11227153 : assert(t == stk->stk[a].vtype);
728 : }
729 : }
730 : }
731 : #endif
732 : }
733 : break;
734 30843499 : case CMDcall:
735 30843499 : TRC_DEBUG(ALGO, "calling %s.%s\n",
736 : pci->modname ? pci->modname : "<null>",
737 : pci->fcnname ? pci->fcnname : "<null>");
738 30843499 : ret = malCommandCall(stk, pci);
739 : #ifndef NDEBUG
740 30842851 : if (ret == MAL_SUCCEED) {
741 : /* check that the types of actual results match
742 : * expected results */
743 62015815 : for (int i = 0; i < pci->retc; i++) {
744 31174174 : int a = getArg(pci, i);
745 31174174 : int t = getArgType(mb, pci, i);
746 :
747 31174174 : if (isaBatType(t)) {
748 : //bat bid = stk->stk[a].val.bval;
749 17461576 : t = getBatType(t);
750 17461576 : assert(stk->stk[a].bat);
751 : //assert( !is_bat_nil(bid));
752 17461576 : assert(t != TYPE_any);
753 : //assert( ATOMtype(BBP_desc(bid)->ttype) == ATOMtype(t));
754 : } else {
755 13712598 : assert(t == stk->stk[a].vtype);
756 : }
757 : }
758 : }
759 : #endif
760 : break;
761 74548 : case FCNcall: {
762 : /*
763 : * MAL function calls are relatively expensive,
764 : * because they have to assemble a new stack frame and
765 : * do housekeeping, such as garbagecollection of all
766 : * non-returned values.
767 : */
768 74548 : MalStkPtr nstk;
769 74548 : InstrPtr q;
770 74548 : int ii, arg;
771 :
772 74548 : nstk = prepareMALstack(pci->blk, pci->blk->vsize);
773 74548 : if (nstk == 0) {
774 0 : ret = createException(MAL, "mal.interpreter", MAL_STACK_FAIL);
775 0 : break;
776 : }
777 74548 : nstk->pcup = stkpc;
778 :
779 : /*safeguardStack */
780 74548 : nstk->stkdepth = nstk->stksize + stk->stkdepth;
781 74548 : nstk->calldepth = stk->calldepth + 1;
782 74548 : nstk->up = stk;
783 74548 : if (nstk->calldepth > 256) {
784 1 : ret = createException(MAL, "mal.interpreter",
785 : MAL_CALLDEPTH_FAIL);
786 1 : GDKfree(nstk);
787 1 : break;
788 : }
789 74547 : if ((unsigned) nstk->stkdepth >
790 46488 : THREAD_STACK_SIZE / sizeof(mb->var[0]) / 4 && THRhighwater()) {
791 : /* we are running low on stack space */
792 0 : ret = createException(MAL, "mal.interpreter", MAL_STACK_FAIL);
793 0 : GDKfree(nstk);
794 0 : break;
795 : }
796 :
797 : /* copy arguments onto destination stack */
798 74547 : q = getInstrPtr(pci->blk, 0);
799 74547 : arg = q->retc;
800 382298 : for (ii = pci->retc; ii < pci->argc; ii++, arg++) {
801 307751 : lhs = &nstk->stk[q->argv[arg]];
802 307751 : rhs = &stk->stk[pci->argv[ii]];
803 307751 : if (VALcopy(lhs, rhs) == NULL) {
804 0 : GDKfree(nstk);
805 0 : ret = createException(MAL, "mal.interpreter",
806 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
807 0 : break;
808 307751 : } else if (lhs->bat)
809 18 : BBPretain(lhs->val.bval);
810 : }
811 74547 : if (ret == MAL_SUCCEED && ii == pci->argc) {
812 74547 : ret = runMALsequence(cntxt, pci->blk, 1, pci->blk->stop, nstk,
813 : stk, pci);
814 74547 : garbageCollector(cntxt, pci->blk, nstk, 0);
815 74547 : arg = q->retc;
816 382298 : for (ii = pci->retc; ii < pci->argc; ii++, arg++) {
817 307751 : lhs = &nstk->stk[q->argv[arg]];
818 307751 : if (lhs->bat)
819 0 : BBPrelease(lhs->val.bval);
820 : }
821 74547 : GDKfree(nstk);
822 : }
823 : break;
824 : }
825 : case REMsymbol:
826 : break;
827 569848 : case ENDsymbol:
828 569848 : runtimeProfileExit(cntxt, mb, stk, pci, &runtimeProfile);
829 569849 : runtimeProfileExit(cntxt, mb, stk, getInstrPtr(mb, 0),
830 : &runtimeProfileFunction);
831 569846 : if (pcicaller && garbageControl(getInstrPtr(mb, 0)))
832 5998 : garbageCollector(cntxt, mb, stk, TRUE);
833 569846 : if (cntxt->qryctx.endtime == QRY_TIMEOUT) {
834 0 : freeException(ret); /* overrule exception */
835 0 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
836 0 : break;
837 569846 : } else if (cntxt->qryctx.endtime == QRY_INTERRUPT) {
838 0 : freeException(ret); /* overrule exception */
839 0 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_INTERRUPT);
840 0 : break;
841 : }
842 569846 : stkpc = mb->stop; // force end of loop
843 569846 : continue;
844 0 : default: {
845 0 : str w;
846 0 : if (pci->token < 0) {
847 : /* temporary NOOP instruction */
848 : break;
849 : }
850 0 : w = instruction2str(mb, 0, pci, FALSE);
851 0 : if (w) {
852 0 : ret = createException(MAL, "interpreter", "unknown operation:%s",
853 : w);
854 0 : GDKfree(w);
855 : } else {
856 0 : ret = createException(MAL, "interpreter",
857 : "failed instruction2str");
858 : }
859 : // runtimeProfileBegin already sets the time in the instruction
860 0 : if (cntxt->qryctx.endtime == QRY_TIMEOUT) {
861 0 : freeException(ret); /* overrule exception */
862 0 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
863 0 : break;
864 0 : } else if (cntxt->qryctx.endtime == QRY_INTERRUPT) {
865 0 : freeException(ret); /* overrule exception */
866 0 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_INTERRUPT);
867 0 : break;
868 : }
869 :
870 0 : stkpc = mb->stop;
871 0 : continue;
872 : }
873 : }
874 :
875 : /* monitoring information should reflect the input arguments,
876 : which may be removed by garbage collection */
877 : /* BEWARE, the SQL engine or MAL function could zap the block, leaving garbage behind in pci */
878 : /* this hack means we loose a closing event */
879 49578997 : if (mb->stop <= 1)
880 0 : continue;
881 49578997 : runtimeProfileExit(cntxt, mb, stk, pci, &runtimeProfile);
882 : /* when we find a timeout situation, then the result is already known
883 : * and assigned, the backup version is not removed*/
884 49589905 : if (ret == MAL_SUCCEED) {
885 100002937 : for (int i = 0; i < pci->retc; i++) {
886 50441558 : lhs = &backup[i];
887 50441558 : if (lhs->bat) {
888 21880448 : BBPrelease(lhs->val.bval);
889 28561110 : } else if (ATOMextern(lhs->vtype) &&
890 1860765 : lhs->val.pval &&
891 141119 : lhs->val.pval != ATOMnilptr(lhs->vtype) &&
892 141119 : lhs->val.pval != stk->stk[getArg(pci, i)].val.pval)
893 138808 : GDKfree(lhs->val.pval);
894 : }
895 49561379 : if (ATOMIC_GET(&GDKdebug) & CHECKMASK && exceptionVar < 0) {
896 : BAT *b;
897 :
898 44484454 : for (int i = 0; i < pci->retc; i++) {
899 22679643 : if (garbage[i] == -1
900 20902808 : && stk->stk[getArg(pci, i)].bat
901 7228664 : && !is_bat_nil(stk->stk[getArg(pci, i)].val.bval)) {
902 7169884 : assert(stk->stk[getArg(pci, i)].val.bval > 0);
903 7169884 : b = BATdescriptor(stk->stk[getArg(pci, i)].val.bval);
904 7172177 : if (b == NULL) {
905 0 : if (ret == MAL_SUCCEED)
906 0 : ret = createException(MAL, "mal.propertyCheck",
907 : SQLSTATE(HY002)
908 : RUNTIME_OBJECT_MISSING);
909 0 : continue;
910 : }
911 7172177 : BATassertProps(b);
912 7170492 : BBPunfix(b->batCacheid);
913 : }
914 : }
915 : }
916 :
917 : /* general garbage collection */
918 49555673 : if (ret == MAL_SUCCEED && garbageControl(pci)) {
919 119328293 : for (int i = 0; i < pci->argc; i++) {
920 93047355 : int a = getArg(pci, i);
921 :
922 93047355 : if (isaBatType(getArgType(mb, pci, i))) {
923 50946568 : bat bid = stk->stk[a].val.bval;
924 :
925 50946568 : if (garbage[i] >= 0) {
926 7669028 : bid = stk->stk[garbage[i]].val.bval;
927 7669028 : if (!is_bat_nil(bid)) {
928 7214104 : stk->stk[garbage[i]].val.bval = bat_nil;
929 7214104 : BBPcold(bid);
930 7208246 : BBPrelease(bid);
931 : }
932 : }
933 : }
934 : }
935 : }
936 : }
937 :
938 : /* Exception handling */
939 49580970 : if (localGDKerrbuf && localGDKerrbuf[0]) {
940 2 : if (ret == MAL_SUCCEED)
941 2 : ret = createException(MAL, "mal.interpreter", GDK_EXCEPTION);
942 : // TODO take properly care of the GDK exception
943 2 : localGDKerrbuf[0] = 0;
944 : }
945 :
946 49580970 : if (ret != MAL_SUCCEED) {
947 19018 : str msg = 0;
948 :
949 : /* Detect any exception received from the implementation. */
950 : /* The first identifier is an optional exception name */
951 19018 : if (strstr(ret, "!skip-to-end")) {
952 0 : freeException(ret);
953 0 : ret = MAL_SUCCEED;
954 0 : stkpc = mb->stop;
955 0 : continue;
956 : }
957 : /*
958 : * Exceptions are caught based on their name, which is part of the
959 : * exception message. The ANYexception variable catches all.
960 : */
961 19018 : exceptionVar = -1;
962 19018 : msg = strchr(ret, ':');
963 19018 : if (msg) {
964 19018 : exceptionVar = findVariableLength(mb, ret, (int) (msg - ret));
965 : }
966 19018 : if (exceptionVar == -1)
967 19016 : exceptionVar = findVariableLength(mb, "ANYexception", 12);
968 :
969 : /* unknown exceptions lead to propagation */
970 19016 : if (exceptionVar == -1) {
971 19000 : if (cntxt->qryctx.endtime == QRY_TIMEOUT) {
972 3 : freeException(ret); /* overrule exception */
973 3 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
974 18997 : } else if (cntxt->qryctx.endtime == QRY_INTERRUPT) {
975 0 : freeException(ret); /* overrule exception */
976 0 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_INTERRUPT);
977 : }
978 19000 : stkpc = mb->stop;
979 19000 : continue;
980 : }
981 : /* assure correct variable type */
982 18 : if (getVarType(mb, exceptionVar) == TYPE_str) {
983 : /* watch out for concurrent access */
984 18 : MT_lock_set(&mal_contextLock);
985 18 : v = &stk->stk[exceptionVar];
986 18 : if (v->val.sval)
987 5 : freeException(v->val.sval); /* old exception */
988 18 : VALset(v, TYPE_str, ret);
989 18 : ret = MAL_SUCCEED;
990 18 : MT_lock_unset(&mal_contextLock);
991 : } else {
992 0 : mnstr_printf(cntxt->fdout, "%s", ret);
993 0 : freeException(ret);
994 0 : ret = MAL_SUCCEED;
995 : }
996 : /* position yourself at the catch instruction for further decisions */
997 : /* skipToCatch(exceptionVar,@2,@3) */
998 : /* skip to catch block or end */
999 201 : for (; stkpc < mb->stop; stkpc++) {
1000 201 : InstrPtr l = getInstrPtr(mb, stkpc);
1001 201 : if (l->barrier == CATCHsymbol) {
1002 : int j;
1003 18 : for (j = 0; j < l->retc; j++)
1004 18 : if (getArg(l, j) == exceptionVar)
1005 : break;
1006 0 : else if (getArgName(mb, l, j) && strcmp(getArgName(mb, l, j), "ANYexception") == 0)
1007 : break;
1008 18 : if (j < l->retc)
1009 : break;
1010 : }
1011 : }
1012 18 : if (stkpc == mb->stop) {
1013 0 : if (cntxt->qryctx.endtime == QRY_TIMEOUT) {
1014 0 : freeException(ret); /* overrule exception */
1015 0 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
1016 0 : stkpc = mb->stop;
1017 0 : } else if (cntxt->qryctx.endtime == QRY_INTERRUPT) {
1018 0 : freeException(ret); /* overrule exception */
1019 0 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_INTERRUPT);
1020 0 : stkpc = mb->stop;
1021 : }
1022 0 : continue;
1023 : }
1024 18 : pci = getInstrPtr(mb, stkpc);
1025 : }
1026 :
1027 : /*
1028 : * After the expression has been evaluated we should check for
1029 : * a possible change in the control flow.
1030 : */
1031 49561970 : switch (pci->barrier) {
1032 1386786 : case BARRIERsymbol:
1033 1386786 : v = &stk->stk[getDestVar(pci)];
1034 : /* skip to end of barrier, depends on the type */
1035 1386786 : switch (v->vtype) {
1036 1384376 : case TYPE_bit:
1037 1384376 : if (v->val.btval == FALSE || is_bit_nil(v->val.btval))
1038 1335599 : stkpc = pci->jump;
1039 : break;
1040 0 : case TYPE_bte:
1041 0 : if (is_bte_nil(v->val.btval))
1042 0 : stkpc = pci->jump;
1043 : break;
1044 2386 : case TYPE_oid:
1045 2386 : if (is_oid_nil(v->val.oval))
1046 2183 : stkpc = pci->jump;
1047 : break;
1048 0 : case TYPE_sht:
1049 0 : if (is_sht_nil(v->val.shval))
1050 0 : stkpc = pci->jump;
1051 : break;
1052 9 : case TYPE_int:
1053 9 : if (is_int_nil(v->val.ival))
1054 0 : stkpc = pci->jump;
1055 : break;
1056 14 : case TYPE_lng:
1057 14 : if (is_lng_nil(v->val.lval))
1058 0 : stkpc = pci->jump;
1059 : break;
1060 : #ifdef HAVE_HGE
1061 0 : case TYPE_hge:
1062 0 : if (is_hge_nil(v->val.hval))
1063 0 : stkpc = pci->jump;
1064 : break;
1065 : #endif
1066 1 : case TYPE_flt:
1067 1 : if (is_flt_nil(v->val.fval))
1068 0 : stkpc = pci->jump;
1069 : break;
1070 0 : case TYPE_dbl:
1071 0 : if (is_dbl_nil(v->val.dval))
1072 0 : stkpc = pci->jump;
1073 : break;
1074 0 : case TYPE_str:
1075 0 : if (strNil(v->val.sval))
1076 0 : stkpc = pci->jump;
1077 : break;
1078 0 : default: {
1079 0 : char name[IDLENGTH] = { 0 };
1080 0 : ret = createException(MAL, "mal.interpreter",
1081 : "%s: Unknown barrier type", getVarNameIntoBuffer(mb,
1082 : getDestVar
1083 : (pci), name));
1084 : }
1085 : }
1086 1386786 : stkpc++;
1087 1386786 : break;
1088 13255928 : case LEAVEsymbol:
1089 : case REDOsymbol:
1090 13255928 : v = &stk->stk[getDestVar(pci)];
1091 : /* skip to end of barrier, depending on the type */
1092 13255928 : switch (v->vtype) {
1093 863 : case TYPE_bit:
1094 863 : if (v->val.btval == TRUE)
1095 445 : stkpc = pci->jump;
1096 : else
1097 418 : stkpc++;
1098 : break;
1099 0 : case TYPE_str:
1100 0 : if (!strNil(v->val.sval))
1101 0 : stkpc = pci->jump;
1102 : else
1103 0 : stkpc++;
1104 : break;
1105 44691 : case TYPE_oid:
1106 44691 : if (!is_oid_nil(v->val.oval))
1107 44494 : stkpc = pci->jump;
1108 : else
1109 197 : stkpc++;
1110 : break;
1111 0 : case TYPE_sht:
1112 0 : if (!is_sht_nil(v->val.shval))
1113 0 : stkpc = pci->jump;
1114 : else
1115 0 : stkpc++;
1116 : break;
1117 119 : case TYPE_int:
1118 119 : if (!is_int_nil(v->val.ival))
1119 114 : stkpc = pci->jump;
1120 : else
1121 5 : stkpc++;
1122 : break;
1123 0 : case TYPE_bte:
1124 0 : if (!is_bte_nil(v->val.btval))
1125 0 : stkpc = pci->jump;
1126 : else
1127 0 : stkpc++;
1128 : break;
1129 13210253 : case TYPE_lng:
1130 13210253 : if (!is_lng_nil(v->val.lval))
1131 13210239 : stkpc = pci->jump;
1132 : else
1133 14 : stkpc++;
1134 : break;
1135 : #ifdef HAVE_HGE
1136 0 : case TYPE_hge:
1137 0 : if (!is_hge_nil(v->val.hval))
1138 0 : stkpc = pci->jump;
1139 : else
1140 0 : stkpc++;
1141 : break;
1142 : #endif
1143 2 : case TYPE_flt:
1144 2 : if (!is_flt_nil(v->val.fval))
1145 1 : stkpc = pci->jump;
1146 : else
1147 1 : stkpc++;
1148 : break;
1149 0 : case TYPE_dbl:
1150 0 : if (!is_dbl_nil(v->val.dval))
1151 0 : stkpc = pci->jump;
1152 : else
1153 0 : stkpc++;
1154 : break;
1155 : default:
1156 : break;
1157 : }
1158 : break;
1159 41 : case CATCHsymbol:
1160 : /* catch blocks are skipped unless
1161 : searched for explicitly */
1162 41 : if (exceptionVar < 0) {
1163 20 : stkpc = pci->jump;
1164 20 : break;
1165 : }
1166 21 : exceptionVar = -1;
1167 21 : stkpc++;
1168 21 : break;
1169 48893 : case EXITsymbol:
1170 48893 : if (getDestVar(pci) == exceptionVar)
1171 0 : exceptionVar = -1;
1172 48893 : stkpc++;
1173 48893 : break;
1174 17 : case RAISEsymbol:
1175 17 : exceptionVar = getDestVar(pci);
1176 : //freeException(ret);
1177 17 : ret = MAL_SUCCEED;
1178 17 : if (getVarType(mb, getDestVar(pci)) == TYPE_str) {
1179 15 : char nme[256];
1180 15 : snprintf(nme, 256, "%s.%s[%d]", getModuleId(getInstrPtr(mb, 0)),
1181 15 : getFunctionId(getInstrPtr(mb, 0)), stkpc);
1182 15 : ret = createException(MAL, nme, "%s",
1183 15 : stk->stk[getDestVar(pci)].val.sval);
1184 : }
1185 : /* skipToCatch(exceptionVar, @2, stk) */
1186 : /* skip to catch block or end */
1187 396 : for (; stkpc < mb->stop; stkpc++) {
1188 382 : InstrPtr l = getInstrPtr(mb, stkpc);
1189 382 : if (l->barrier == CATCHsymbol) {
1190 : int j;
1191 3 : for (j = 0; j < l->retc; j++)
1192 3 : if (getArg(l, j) == exceptionVar)
1193 : break;
1194 0 : else if (getArgName(mb, l, j) && strcmp(getArgName(mb, l, j), "ANYexception") == 0)
1195 : break;
1196 3 : if (j < l->retc)
1197 : break;
1198 : }
1199 : }
1200 17 : if (stkpc == mb->stop) {
1201 14 : runtimeProfileExit(cntxt, mb, stk, pci, &runtimeProfile);
1202 14 : runtimeProfileExit(cntxt, mb, stk, getInstrPtr(mb, 0),
1203 : &runtimeProfileFunction);
1204 14 : break;
1205 : }
1206 : break;
1207 68299 : case RETURNsymbol:
1208 : /* a fake multi-assignment */
1209 68299 : if (env != NULL && pcicaller != NULL) {
1210 : InstrPtr pp = pci;
1211 139064 : pci = pcicaller;
1212 139064 : for (int i = 0; i < pci->retc; i++) {
1213 70767 : rhs = &stk->stk[pp->argv[i]];
1214 70767 : lhs = &env->stk[pci->argv[i]];
1215 70767 : if (VALcopy(lhs, rhs) == NULL) {
1216 0 : ret = createException(MAL, "mal.interpreter",
1217 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
1218 0 : break;
1219 70767 : } else if (lhs->bat)
1220 2931 : BBPretain(lhs->val.bval);
1221 : }
1222 68297 : if (garbageControl(getInstrPtr(mb, 0)))
1223 68258 : garbageCollector(cntxt, mb, stk, TRUE);
1224 : /* reset the clock */
1225 68297 : runtimeProfileExit(cntxt, mb, stk, pp, &runtimeProfile);
1226 68297 : runtimeProfileExit(cntxt, mb, stk, getInstrPtr(mb, 0),
1227 : &runtimeProfileFunction);
1228 : }
1229 68299 : stkpc = mb->stop;
1230 68299 : continue;
1231 34802006 : default:
1232 34802006 : stkpc++;
1233 : }
1234 49493671 : if (cntxt->qryctx.endtime == QRY_TIMEOUT) {
1235 2 : if (ret == MAL_SUCCEED)
1236 2 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_TIMEOUT);
1237 2 : stkpc = mb->stop;
1238 49493669 : } else if (cntxt->qryctx.endtime == QRY_INTERRUPT) {
1239 0 : if (ret == MAL_SUCCEED)
1240 0 : ret = createException(MAL, "mal.interpreter", SQLSTATE(HYT00) RUNTIME_QRY_INTERRUPT);
1241 0 : stkpc = mb->stop;
1242 : }
1243 : }
1244 :
1245 : /* if we could not find the exception variable, cascade a new one */
1246 : /* don't add 'exception not caught' extra message for MAL sequences besides main function calls */
1247 9472147 : if (exceptionVar >= 0 && (ret == MAL_SUCCEED || !pcicaller)) {
1248 1 : char nme[256];
1249 1 : snprintf(nme, 256, "%s.%s[%d]", getModuleId(getInstrPtr(mb, 0)),
1250 1 : getFunctionId(getInstrPtr(mb, 0)), stkpc);
1251 1 : if (ret != MAL_SUCCEED) {
1252 0 : str new, n;
1253 0 : n = createException(MAL, nme, "exception not caught");
1254 0 : if (n) {
1255 0 : new = GDKzalloc(strlen(ret) + strlen(n) + 16);
1256 0 : if (new) {
1257 0 : strcpy(new, ret);
1258 0 : if (new[strlen(new) - 1] != '\n')
1259 0 : strcat(new, "\n");
1260 0 : strcat(new, "!");
1261 0 : strcat(new, n);
1262 0 : freeException(n);
1263 0 : freeException(ret);
1264 0 : ret = new;
1265 : } else {
1266 0 : freeException(ret);
1267 0 : ret = n;
1268 : }
1269 : }
1270 : } else {
1271 1 : ret = createException(MAL, nme, "Exception not caught");
1272 : }
1273 : }
1274 9472147 : if (startedProfileQueue)
1275 656178 : runtimeProfileFinish(cntxt, mb, stk);
1276 9472152 : if (backup != backups)
1277 181704 : GDKfree(backup);
1278 9471213 : if (garbage != garbages)
1279 181725 : GDKfree(garbage);
1280 : return ret;
1281 : }
1282 :
1283 :
1284 : /*
1285 : * MAL API
1286 : * The linkage between MAL interpreter and compiled C-routines
1287 : * is kept as simple as possible.
1288 : * Basically we distinguish four kinds of calling conventions:
1289 : * CMDcall, FCNcall and PATcall.
1290 : * The FCNcall indicates calling a MAL procedure, which leads
1291 : * to a recursive call to the interpreter.
1292 : *
1293 : * CMDcall initiates calling a linked function, passing pointers
1294 : * to the parameters and result variable, i.e. f(ptr a0,..., ptr aN)
1295 : * The function returns a MAL-SUCCEED upon success and a pointer
1296 : * to an exception string upon failure.
1297 : * Failure leads to raise-ing an exception in the interpreter loop,
1298 : * by either looking up the relevant exception message in the module
1299 : * administration or construction of a standard string.
1300 : *
1301 : * The PATcall initiates a call which contains the MAL context,
1302 : * i.e. f(MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
1303 : * The mb provides access to the code definitions. It is primarilly
1304 : * used by routines intended to manipulate the code base itself, such
1305 : * as the optimizers. The Mal stack frame pointer provides access
1306 : * to the values maintained. The arguments passed are offsets
1307 : * into the stack frame rather than pointers to the actual value.
1308 : *
1309 : * BAT parameters require some care. Ideally, a BAT should not be kept
1310 : * around long. This would mean that each time we access a BAT it has to be
1311 : * pinned in memory and upon leaving the function, it is unpinned.
1312 : * This degrades performance significantly.
1313 : * After the parameters are fixed, we can safely free the destination
1314 : * variable and re-initialize it to nil.
1315 : *
1316 : */
1317 :
1318 : /*
1319 : * The type dispatching table in getArgReference can be removed if we
1320 : * determine at compile time the address offset within a ValRecord.
1321 : * We leave this optimization for the future, it leads to about 10%
1322 : * improvement (100ms for 1M calls).
1323 : *
1324 : * Flow of control statements
1325 : * Each assignment (function call) may be part of the initialization
1326 : * of a barrier- block. In that case we have to test the
1327 : * outcome of the operation and possibly skip the block altogether.
1328 : * The latter is implemented as a linear scan for the corresponding
1329 : * labeled statemtent. This might be optimized later.
1330 : *
1331 : * You can skip to a catch block by searching for the corresponding 'lab'
1332 : * The return value should be set to pass the error automatically upon
1333 : * reaching end of function block.
1334 : */
1335 :
1336 : /*
1337 : * Each time we enter a barrier block, we could keep its position in the
1338 : * interpreter stack frame. It forms the starting point to issue a redo.
1339 : * Unfortunately, this does not easily work in the presence of optimizers, which
1340 : * may change the order/block structure. Therefore, we simple have to search
1341 : * the beginning or ensure that during chkProgram the barrier/redo/leave/catch
1342 : * jumps are re-established.
1343 : *
1344 : * Exception handling
1345 : * Calling a built-in or user-defined routine may lead to an error or a
1346 : * cached status message to be dealt with in MAL.
1347 : * To improve error handling in MAL, an exception handling
1348 : * scheme based on @sc{catch}-@sc{exit} blocks. The @sc{catch}
1349 : * statement identifies a (string-valued) variable, which carries the
1350 : * exception message from
1351 : * the originally failed routine or @sc{raise} exception assignment.
1352 : * During normal processing @sc{catch}-@sc{exit} blocks are simply skipped.
1353 : * Upon receiving an exception status from a function call, we set the
1354 : * exception variable and skip to the first associated @sc{catch}-@sc{exit}
1355 : * block.
1356 : * MAL interpretation then continues until it reaches the end of the block.
1357 : * If no exception variable was defined, we should abandon the function
1358 : * altogether searching for a catch block at a higher layer.
1359 : *
1360 : * For the time being we have ignored cascaded/stacked exceptions.
1361 : * The policy is to pass the first recognized exception to a context
1362 : * in which it can be handled.
1363 : *
1364 : * Exceptions raised within a linked-in function requires some care.
1365 : * First, the called procedure does not know anything about the MAL
1366 : * interpreter context. Thus, we need to return all relevant information
1367 : * upon leaving the linked library routine.
1368 : *
1369 : * Second, exceptional cases can be handled deeply in the recursion, where they
1370 : * may also be handled, i.e. by issuing an GDKerror message. The upper layers
1371 : * merely receive a negative integer value to indicate occurrence of an
1372 : * error somewhere in the calling sequence.
1373 : * We then have to also look into GDKerrbuf to see if there was
1374 : * an error raised deeply inside the system.
1375 : *
1376 : * The policy is to require all C-functions to return a string-pointer.
1377 : * Upon a successful call, it is a NULL string. Otherwise it contains an
1378 : * encoding of the exceptional state encountered. This message
1379 : * starts with the exception identifier, followed by contextual details.
1380 : */
1381 :
1382 : /*
1383 : * Garbage collection
1384 : * Garbage collection is relatively straightforward, because most values are
1385 : * retained on the stackframe of an interpreter call. However, two storage
1386 : * types and possibly user-defined type garbage collector definitions
1387 : * require attention: BATs and strings.
1388 : *
1389 : * A key issue is to deal with temporary BATs in an efficient way.
1390 : * References to bats in the buffer pool may cause dangling references
1391 : * at the language level. This appears as soons as your share
1392 : * a reference and delete the BAT from one angle. If not careful, the
1393 : * dangling pointer may subsequently be associated with another BAT
1394 : *
1395 : * All string values are private to the VALrecord, which means they
1396 : * have to be freed explicitly before a MAL function returns.
1397 : * The first step is to always safe the destination variable
1398 : * before a function call is made.
1399 : */
1400 : void
1401 162328580 : garbageElement(Client cntxt, ValPtr v)
1402 : {
1403 162328580 : (void) cntxt;
1404 162328580 : if (v->bat) {
1405 : /*
1406 : * All operations are responsible to properly set the
1407 : * reference count of the BATs being produced or destroyed.
1408 : * The libraries should not leave the
1409 : * physical reference count being set. This is only
1410 : * allowed during the execution of a GDK operation.
1411 : * All references should be logical.
1412 : */
1413 15472737 : bat bid = v->val.bval;
1414 : /* printf("garbage collecting: %d lrefs=%d refs=%d\n",
1415 : bid, BBP_lrefs(bid),BBP_refs(bid)); */
1416 15472737 : v->val.bval = bat_nil;
1417 15472737 : v->bat = false;
1418 15472737 : if (is_bat_nil(bid))
1419 : return;
1420 271900 : BBPcold(bid);
1421 271897 : BBPrelease(bid);
1422 146855843 : } else if (ATOMstorage(v->vtype) == TYPE_str) {
1423 37927977 : GDKfree(v->val.sval);
1424 37928051 : v->val.sval = NULL;
1425 37928051 : v->len = 0;
1426 108927866 : } else if (0 < v->vtype && v->vtype < MAXATOMS && ATOMextern(v->vtype)) {
1427 2616 : GDKfree(v->val.pval);
1428 2616 : v->val.pval = 0;
1429 2616 : v->len = 0;
1430 : }
1431 : }
1432 :
1433 : /*
1434 : * Before we return from the interpreter, we should free all
1435 : * dynamically allocated objects and adjust the BAT reference counts.
1436 : * Early experience shows that for small stack frames the overhead
1437 : * is about 200 ms for a 1M function call loop (tst400e). This means that
1438 : * for the time being we do not introduce more complex garbage
1439 : * administration code.
1440 : *
1441 : * Also note that for top-level stack frames (no environment available),
1442 : * we should retain the value stack because it acts as a global variables.
1443 : * This situation is indicated by the 'global' in the stack frame.
1444 : * Upon termination of the session, the stack should be cleared.
1445 : * Beware that variables may be know polymorphic, their actual
1446 : * type should be saved for variables that reside on a global
1447 : * stack frame.
1448 : */
1449 : void
1450 757844 : garbageCollector(Client cntxt, MalBlkPtr mb, MalStkPtr stk, int flag)
1451 : {
1452 757844 : assert(mb->vtop <= mb->vsize);
1453 757844 : assert(stk->stktop <= stk->stksize);
1454 : (void) flag;
1455 : (void) mb;
1456 : (void) cntxt;
1457 163069839 : for (int k = 0; k < stk->stktop; k++) {
1458 : // if (isVarCleanup(mb, k) ){
1459 162312016 : ValPtr v = &stk->stk[k];
1460 162312016 : garbageElement(cntxt, v);
1461 162311995 : *v = (ValRecord) {
1462 : .vtype = TYPE_int,
1463 : .val.ival = int_nil,
1464 : .bat = false,
1465 : };
1466 : // }
1467 : }
1468 757823 : }
|