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) Author M. Kersten
15 : * For documentation see website
16 : */
17 : #include "monetdb_config.h"
18 : #include "mal_instruction.h"
19 : #include "mal_function.h" /* for getPC() */
20 : #include "mal_utils.h"
21 : #include "mal_exception.h"
22 : #include "mal_private.h"
23 :
24 : /* to avoid memory fragmentation stmt and var blocks are allocated in chunks */
25 : #define MALCHUNK 256
26 :
27 : /* If we encounter an error it can be left behind in the MalBlk
28 : * for the upper layers to abandon the track
29 : */
30 : void
31 0 : addMalException(MalBlkPtr mb, str msg)
32 : {
33 0 : if (msg == NULL)
34 : return;
35 0 : if (mb->errors) {
36 0 : mb->errors = concatErrors(mb->errors, msg);
37 : } else {
38 0 : mb->errors = dupError(msg);
39 : }
40 : }
41 :
42 : Symbol
43 3517906 : newSymbol(const char *nme, int kind)
44 : {
45 3517906 : Symbol cur;
46 :
47 3517906 : assert(kind == COMMANDsymbol || kind == PATTERNsymbol || kind == FUNCTIONsymbol);
48 3517906 : if (nme == NULL)
49 : return NULL;
50 3517906 : cur = (Symbol) GDKzalloc(sizeof(SymRecord));
51 3517908 : if (cur == NULL)
52 : return NULL;
53 3517908 : cur->name = putName(nme);
54 3517908 : if (cur->name == NULL) {
55 0 : GDKfree(cur);
56 0 : return NULL;
57 : }
58 3517908 : cur->kind = kind;
59 3517908 : cur->peer = NULL;
60 3517908 : if (kind == FUNCTIONsymbol) {
61 50408 : cur->def = newMalBlk(STMT_INCREMENT);
62 50407 : if (cur->def == NULL) {
63 0 : GDKfree(cur);
64 0 : return NULL;
65 : }
66 : }
67 : return cur;
68 : }
69 :
70 : void
71 3497892 : freeSymbol(Symbol s)
72 : {
73 3497892 : if (s == NULL)
74 : return;
75 3497892 : if (s->def) {
76 50406 : freeMalBlk(s->def);
77 50407 : s->def = NULL;
78 3447486 : } else if (s->allocated && s->func) {
79 1687894 : GDKfree(s->func->comment);
80 1687894 : GDKfree((char*)s->func->cname);
81 1687894 : GDKfree(s->func->args);
82 1687894 : GDKfree(s->func);
83 : }
84 3497893 : GDKfree(s);
85 : }
86 :
87 : void
88 202516 : freeSymbolList(Symbol s)
89 : {
90 202516 : Symbol t = s;
91 :
92 3699893 : while (s) {
93 3497376 : t = s->peer;
94 3497376 : s->peer = NULL;
95 3497376 : freeSymbol(s);
96 3497376 : s = t;
97 : }
98 202517 : }
99 :
100 : int
101 3889052 : newMalBlkStmt(MalBlkPtr mb, int maxstmts)
102 : {
103 3889052 : InstrPtr *p;
104 3889052 : maxstmts = maxstmts % MALCHUNK == 0 ? maxstmts : ((maxstmts / MALCHUNK) + 1) * MALCHUNK;
105 :
106 3889052 : p = (InstrPtr *) GDKzalloc(sizeof(InstrPtr) * maxstmts);
107 3890379 : if (p == NULL)
108 : return -1;
109 3890379 : mb->stmt = p;
110 3890379 : mb->stop = 0;
111 3890379 : mb->ssize = maxstmts;
112 3890379 : return 0;
113 : }
114 :
115 : MalBlkPtr
116 55926 : newMalBlk(int elements)
117 : {
118 55926 : MalBlkPtr mb;
119 55926 : VarRecord *v;
120 :
121 55926 : mb = (MalBlkPtr) GDKmalloc(sizeof(MalBlkRecord));
122 55930 : if (mb == NULL)
123 : return NULL;
124 :
125 : /* each MAL instruction implies at least one variable
126 : * we reserve some extra for constants */
127 55930 : assert(elements >= 0);
128 55930 : elements += 8;
129 55930 : if (elements % MALCHUNK != 0)
130 55930 : elements = (elements / MALCHUNK + 1) * MALCHUNK;
131 55930 : v = (VarRecord *) GDKzalloc(sizeof(VarRecord) * elements);
132 55932 : if (v == NULL) {
133 0 : GDKfree(mb);
134 0 : return NULL;
135 : }
136 55932 : *mb = (MalBlkRecord) {
137 : .var = v,
138 : .vsize = elements,
139 : .maxarg = MAXARG, /* the minimum for each instruction */
140 : .workers = ATOMIC_VAR_INIT(1),
141 : };
142 55932 : if (newMalBlkStmt(mb, elements) < 0) {
143 0 : GDKfree(mb->var);
144 0 : GDKfree(mb);
145 0 : return NULL;
146 : }
147 : return mb;
148 : }
149 :
150 : int
151 66724 : resizeMalBlk(MalBlkPtr mb, int elements)
152 : {
153 66724 : int i;
154 66724 : assert(elements >= 0);
155 66724 : if (elements % MALCHUNK != 0)
156 10411 : elements = (elements / MALCHUNK + 1) * MALCHUNK;
157 :
158 66724 : if (elements > mb->ssize) {
159 56313 : InstrPtr *ostmt = mb->stmt;
160 56313 : mb->stmt = GDKrealloc(mb->stmt, elements * sizeof(InstrPtr));
161 56313 : if (mb->stmt) {
162 14439679 : for (i = mb->ssize; i < elements; i++)
163 14383366 : mb->stmt[i] = 0;
164 56313 : mb->ssize = elements;
165 : } else {
166 0 : mb->stmt = ostmt; /* reinstate old pointer */
167 0 : mb->errors = createMalException(mb, 0, TYPE,
168 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
169 0 : return -1;
170 : }
171 : }
172 :
173 :
174 66724 : if (elements > mb->vsize) {
175 0 : VarRecord *ovar = mb->var;
176 0 : mb->var = GDKrealloc(mb->var, elements * sizeof(VarRecord));
177 0 : if (mb->var) {
178 0 : memset(((char *) mb->var) +sizeof(VarRecord) * mb->vsize, 0,
179 0 : (elements - mb->vsize) * sizeof(VarRecord));
180 0 : mb->vsize = elements;
181 : } else {
182 0 : mb->var = ovar;
183 0 : mb->errors = createMalException(mb, 0, TYPE,
184 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
185 0 : return -1;
186 : }
187 : }
188 : return 0;
189 : }
190 :
191 : /* For a MAL session we have to keep the variables around
192 : * and only need to reset the instruction pointer
193 : */
194 : void
195 568057 : resetMalTypes(MalBlkPtr mb, int stop)
196 : {
197 568057 : int i;
198 :
199 26880707 : for (i = 0; i < stop; i++)
200 26312650 : mb->stmt[i]->typeresolved = false;
201 568057 : mb->stop = stop;
202 568057 : mb->errors = NULL;
203 568057 : }
204 :
205 : /* For SQL operations we have to cleanup variables and trim the space
206 : * A portion is retained for the next query */
207 : void
208 553808 : resetMalBlk(MalBlkPtr mb)
209 : {
210 553808 : int i;
211 553808 : InstrPtr *new;
212 553808 : VarRecord *vnew;
213 :
214 148733593 : for (i = 1/*MALCHUNK*/; i < mb->ssize; i++) {
215 148179741 : freeInstruction(mb->stmt[i]);
216 148179785 : mb->stmt[i] = NULL;
217 : }
218 553852 : if (mb->ssize != MALCHUNK) {
219 15784 : new = GDKrealloc(mb->stmt, sizeof(InstrPtr) * MALCHUNK);
220 15784 : if (new == NULL) {
221 : /* the only place to return an error signal at this stage. */
222 : /* The Client context should be passed around more deeply */
223 0 : mb->errors = createMalException(mb, 0, TYPE,
224 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
225 0 : return;
226 : }
227 15784 : mb->stmt = new;
228 15784 : mb->ssize = MALCHUNK;
229 : }
230 : /* Reuse the initial function statement */
231 553852 : mb->stop = 1;
232 :
233 61109742 : for (i = 0; i < mb->vtop; i++) {
234 60555979 : if (mb->var[i].name)
235 343615 : GDKfree(mb->var[i].name);
236 60555644 : mb->var[i].name = NULL;
237 60555644 : if (isVarConstant(mb, i))
238 17393158 : VALclear(&getVarConstant(mb, i));
239 : }
240 :
241 553763 : if (mb->vsize != MALCHUNK) {
242 24693 : vnew = GDKrealloc(mb->var, sizeof(VarRecord) * MALCHUNK);
243 24693 : if (vnew == NULL) {
244 : /* the only place to return an error signal at this stage. */
245 : /* The Client context should be passed around more deeply */
246 0 : mb->errors = createMalException(mb, 0, TYPE,
247 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
248 0 : return;
249 : }
250 24693 : mb->var = vnew;
251 24693 : mb->vsize = MALCHUNK;
252 : }
253 553763 : mb->vtop = 0;
254 : }
255 :
256 :
257 : /* The freeMalBlk code is quite defensive. It is used to localize an
258 : * illegal re-use of a MAL blk. */
259 : void
260 56152 : freeMalBlk(MalBlkPtr mb)
261 : {
262 56152 : int i;
263 :
264 14533775 : for (i = 0; i < mb->ssize; i++)
265 14477611 : if (mb->stmt[i]) {
266 171415 : freeInstruction(mb->stmt[i]);
267 171427 : mb->stmt[i] = NULL;
268 : }
269 56164 : mb->stop = 0;
270 322524 : for (i = 0; i < mb->vtop; i++) {
271 266360 : if (mb->var[i].name)
272 10067 : GDKfree(mb->var[i].name);
273 266360 : mb->var[i].name = NULL;
274 266360 : if (isVarConstant(mb, i))
275 61768 : VALclear(&getVarConstant(mb, i));
276 : }
277 56164 : mb->vtop = 0;
278 56164 : GDKfree(mb->stmt);
279 56164 : mb->stmt = 0;
280 56164 : GDKfree(mb->var);
281 56164 : mb->var = 0;
282 :
283 56164 : mb->binding[0] = 0;
284 56164 : mb->tag = 0;
285 56164 : mb->memory = 0;
286 56164 : if (mb->help)
287 0 : GDKfree(mb->help);
288 56164 : mb->help = 0;
289 56164 : mb->inlineProp = 0;
290 56164 : mb->unsafeProp = 0;
291 56164 : freeException(mb->errors);
292 56163 : GDKfree(mb);
293 56163 : }
294 :
295 : /* The routine below should assure that all referenced structures are
296 : * private. The copying is memory conservative. */
297 : MalBlkPtr
298 231 : copyMalBlk(MalBlkPtr old)
299 : {
300 231 : MalBlkPtr mb;
301 231 : int i;
302 :
303 231 : mb = (MalBlkPtr) GDKzalloc(sizeof(MalBlkRecord));
304 231 : if (mb == NULL)
305 : return NULL;
306 :
307 231 : mb->var = (VarRecord *) GDKzalloc(sizeof(VarRecord) * old->vsize);
308 231 : if (mb->var == NULL) {
309 0 : GDKfree(mb);
310 0 : return NULL;
311 : }
312 :
313 231 : mb->vsize = old->vsize;
314 :
315 : /* copy all variable records */
316 11803 : for (i = 0; i < old->vtop; i++) {
317 11572 : mb->var[i] = old->var[i];
318 11572 : if (mb->var[i].name) {
319 619 : mb->var[i].name = GDKstrdup(mb->var[i].name);
320 619 : if (!mb->var[i].name)
321 0 : goto bailout;
322 : }
323 11572 : if (VALcopy(&(mb->var[i].value), &(old->var[i].value)) == NULL) {
324 0 : mb->vtop = i;
325 0 : goto bailout;
326 : }
327 : }
328 231 : mb->vtop = old->vtop;
329 :
330 231 : mb->stmt = (InstrPtr *) GDKzalloc(sizeof(InstrPtr) * old->ssize);
331 231 : if (mb->stmt == NULL) {
332 0 : goto bailout;
333 : }
334 :
335 231 : mb->ssize = old->ssize;
336 231 : assert(old->stop < old->ssize);
337 10954 : for (i = 0; i < old->stop; i++) {
338 10723 : mb->stmt[i] = copyInstruction(old->stmt[i]);
339 10723 : if (mb->stmt[i] == NULL) {
340 0 : mb->stop = i;
341 0 : goto bailout;
342 : }
343 : }
344 231 : mb->stop = old->stop;
345 231 : if (old->help && (mb->help = GDKstrdup(old->help)) == NULL) {
346 0 : goto bailout;
347 : }
348 :
349 231 : strcpy_len(mb->binding, old->binding, sizeof(mb->binding));
350 231 : mb->errors = old->errors ? GDKstrdup(old->errors) : 0;
351 231 : mb->tag = old->tag;
352 231 : mb->runtime = old->runtime;
353 231 : mb->calls = old->calls;
354 231 : mb->optimize = old->optimize;
355 231 : mb->maxarg = old->maxarg;
356 231 : mb->inlineProp = old->inlineProp;
357 231 : mb->unsafeProp = old->unsafeProp;
358 231 : return mb;
359 :
360 : bailout:
361 0 : for (i = 0; i < old->stop; i++)
362 0 : freeInstruction(mb->stmt[i]);
363 0 : for (i = 0; i < old->vtop; i++) {
364 0 : if (mb->var[i].name)
365 0 : GDKfree(mb->var[i].name);
366 0 : VALclear(&mb->var[i].value);
367 : }
368 0 : GDKfree(mb->var);
369 0 : GDKfree(mb->stmt);
370 0 : GDKfree(mb);
371 0 : return NULL;
372 : }
373 :
374 : /* The MAL records should be managed from a pool to
375 : * avoid repeated alloc/free and reduce probability of
376 : * memory fragmentation. (todo)
377 : * The complicating factor is their variable size,
378 : * which leads to growing records as a result of pushArguments
379 : * Allocation of an instruction should always succeed.
380 : */
381 : InstrPtr
382 32030205 : newInstructionArgs(MalBlkPtr mb, const char *modnme, const char *fcnnme,
383 : int args)
384 : {
385 32030205 : InstrPtr p;
386 :
387 32030205 : if (mb && mb->errors)
388 : return NULL;
389 32030190 : if (args <= 0)
390 : args = 1;
391 32030190 : p = GDKmalloc(args * sizeof(p->argv[0]) + offsetof(InstrRecord, argv));
392 32036177 : if (p == NULL) {
393 0 : if (mb)
394 0 : mb->errors = createMalException(mb, 0, TYPE,
395 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
396 0 : return NULL;
397 : }
398 32036177 : *p = (InstrRecord) {
399 : .maxarg = args,
400 : .typeresolved = false,
401 : .modname = modnme,
402 : .fcnname = fcnnme,
403 : .argc = 1,
404 : .retc = 1,
405 : /* Flow of control instructions are always marked as an assignment
406 : * with modifier */
407 : .token = ASSIGNsymbol,
408 : };
409 32036177 : memset(p->argv, 0, args * sizeof(p->argv[0]));
410 32036177 : p->argv[0] = -1;
411 32036177 : return p;
412 : }
413 :
414 : InstrPtr
415 4059947 : newInstruction(MalBlkPtr mb, const char *modnme, const char *fcnnme)
416 : {
417 4059947 : return newInstructionArgs(mb, modnme, fcnnme, MAXARG);
418 : }
419 :
420 : InstrPtr
421 22317363 : copyInstructionArgs(const InstrRecord *p, int args)
422 : {
423 22317363 : if (args < p->maxarg)
424 : args = p->maxarg;
425 22317363 : InstrPtr new = (InstrPtr) GDKmalloc(offsetof(InstrRecord, argv) +
426 : args * sizeof(p->argv[0]));
427 22317853 : if (new == NULL)
428 : return new;
429 22317853 : memcpy(new, p,
430 22317853 : offsetof(InstrRecord, argv) + p->maxarg * sizeof(p->argv[0]));
431 22317853 : if (args > p->maxarg)
432 3304170 : memset(new->argv + p->maxarg, 0,
433 3304170 : (args - p->maxarg) * sizeof(new->argv[0]));
434 22317853 : new->typeresolved = false;
435 22317853 : new->maxarg = args;
436 22317853 : return new;
437 : }
438 :
439 : InstrPtr
440 15573583 : copyInstruction(const InstrRecord *p)
441 : {
442 15573583 : return copyInstructionArgs(p, p->maxarg);
443 : }
444 :
445 : void
446 577013 : clrFunction(InstrPtr p)
447 : {
448 577013 : p->token = ASSIGNsymbol;
449 577013 : p->fcn = 0;
450 577013 : p->blk = 0;
451 577013 : p->typeresolved = false;
452 577013 : setModuleId(p, NULL);
453 577037 : setFunctionId(p, NULL);
454 577111 : }
455 :
456 : void
457 0 : clrInstruction(InstrPtr p)
458 : {
459 0 : clrFunction(p);
460 0 : memset(p, 0, offsetof(InstrRecord, argv) + p->maxarg * sizeof(p->argv[0]));
461 0 : }
462 :
463 : void
464 173969854 : freeInstruction(InstrPtr p)
465 : {
466 173969854 : GDKfree(p);
467 173875479 : }
468 :
469 : /* Query optimizers walk their way through a MAL program block. They
470 : * require some primitives to move instructions around and to remove
471 : * superflous instructions. The removal is based on the assumption
472 : * that indeed the instruction belonged to the block. */
473 : void
474 0 : removeInstruction(MalBlkPtr mb, InstrPtr p)
475 : {
476 0 : int i;
477 0 : for (i = 0; i < mb->stop - 1; i++)
478 0 : if (mb->stmt[i] == p)
479 : break;
480 0 : if (i == mb->stop)
481 : return;
482 0 : for (; i < mb->stop - 1; i++)
483 0 : mb->stmt[i] = mb->stmt[i + 1];
484 0 : mb->stmt[i] = 0;
485 0 : mb->stop--;
486 0 : assert(i == mb->stop); /* move statement after stop */
487 0 : mb->stmt[i] = p;
488 : }
489 :
490 : void
491 0 : removeInstructionBlock(MalBlkPtr mb, int pc, int cnt)
492 : {
493 0 : int i;
494 0 : InstrPtr p;
495 0 : for (i = pc; i < pc + cnt; i++) {
496 0 : p = getInstrPtr(mb, i);
497 0 : freeInstruction(p);
498 0 : mb->stmt[i] = NULL;
499 0 : } for (i = pc; i < mb->stop - cnt; i++)
500 0 : mb->stmt[i] = mb->stmt[i + cnt];
501 0 : mb->stop -= cnt;
502 0 : for (; i < mb->stop; i++)
503 : mb->stmt[i] = 0;
504 0 : }
505 :
506 : void
507 0 : moveInstruction(MalBlkPtr mb, int pc, int target)
508 : {
509 0 : InstrPtr p;
510 0 : int i;
511 0 : p = getInstrPtr(mb, pc);
512 0 : if (pc > target) {
513 0 : for (i = pc; i > target; i--)
514 0 : mb->stmt[i] = mb->stmt[i - 1];
515 0 : mb->stmt[i] = p;
516 : } else {
517 0 : for (i = target; i > pc; i--)
518 0 : mb->stmt[i] = mb->stmt[i - 1];
519 0 : mb->stmt[i] = p;
520 : }
521 0 : }
522 :
523 : /* Beware that the first argument of a signature is reserved for the
524 : * function return type , which should be equal to the destination
525 : * variable type.
526 : */
527 : int
528 644315 : findVariable(MalBlkPtr mb, const char *name)
529 : {
530 644315 : int i;
531 644315 : if (name == NULL)
532 : return -1;
533 3402401 : for (i = mb->vtop - 1; i >= 0; i--)
534 3096007 : if (mb->var[i].name && idcmp(name, mb->var[i].name) == 0)
535 337921 : return i;
536 : return -1;
537 : }
538 :
539 : /* The second version of findVariable assumes you have not yet
540 : * allocated a private structure. This is particularly useful during
541 : * parsing, because most variables are already defined. This way we
542 : * safe GDKmalloc/GDKfree. */
543 : int
544 54824 : findVariableLength(MalBlkPtr mb, const char *name, int len)
545 : {
546 54824 : int i;
547 2632613 : for (i = mb->vtop - 1; i >= 0; i--) {
548 2589033 : const char *s = mb->var[i].name;
549 2589033 : if (s && strncmp(name, s, len) == 0 && s[len] == 0)
550 11244 : return i;
551 : }
552 : return -1;
553 : }
554 :
555 : str
556 174 : getArgDefault(MalBlkPtr mb, InstrPtr p, int idx)
557 : {
558 174 : ValPtr v = &getVarConstant(mb, getArg(p, idx));
559 174 : if (v->vtype == TYPE_str)
560 174 : return v->val.sval;
561 : return NULL;
562 : }
563 :
564 : /* Beware, the symbol table structure assumes that it is relatively
565 : * cheap to perform a linear search to a variable or constant. */
566 : static int
567 60584710 : makeVarSpace(MalBlkPtr mb)
568 : {
569 60584710 : if (mb->vtop >= mb->vsize) {
570 87844 : VarRecord *new;
571 87844 : int s = (mb->vtop / MALCHUNK + 1) * MALCHUNK;
572 87844 : new = (VarRecord *) GDKrealloc(mb->var, s * sizeof(VarRecord));
573 87844 : if (new == NULL) {
574 : /* the only place to return an error signal at this stage. */
575 : /* The Client context should be passed around more deeply */
576 0 : mb->errors = createMalException(mb, 0, TYPE, SQLSTATE(HY013) MAL_MALLOC_FAIL);
577 0 : return -1;
578 : }
579 87844 : memset(new + mb->vsize, 0, (s - mb->vsize) * sizeof(VarRecord));
580 87844 : mb->vsize = s;
581 87844 : mb->var = new;
582 : }
583 : return 0;
584 : }
585 :
586 : /* create and initialize a variable record*/
587 : void
588 60588301 : setVariableType(MalBlkPtr mb, const int n, malType type)
589 : {
590 60588301 : assert(n >= 0 && n < mb->vtop);
591 60588301 : setVarType(mb, n, type);
592 60588301 : setRowCnt(mb, n, 0);
593 60588301 : clrVarFixed(mb, n);
594 60588301 : clrVarUsed(mb, n);
595 60588301 : clrVarInit(mb, n);
596 60588301 : clrVarDisabled(mb, n);
597 60588301 : clrVarConstant(mb, n);
598 60588301 : clrVarCleanup(mb, n);
599 60588301 : }
600 :
601 : char *
602 16429 : getVarNameIntoBuffer(MalBlkPtr mb, int idx, char *buf)
603 : {
604 16429 : char *s = mb->var[idx].name;
605 16429 : if (getVarKind(mb, idx) == 0)
606 0 : setVarKind(mb, idx, REFMARKER);
607 16429 : if (s == NULL) {
608 16290 : (void) snprintf(buf, IDLENGTH, "%c_%d", getVarKind(mb, idx), idx);
609 : } else {
610 139 : (void) snprintf(buf, IDLENGTH, "%s", s);
611 : }
612 16429 : return buf;
613 : }
614 :
615 : int
616 60582108 : newVariable(MalBlkPtr mb, const char *name, size_t len, malType type)
617 : {
618 60582108 : int n;
619 60582108 : int kind = REFMARKER;
620 60582108 : if (mb->errors)
621 : return -1;
622 60582108 : if (len >= IDLENGTH) {
623 1 : mb->errors = createMalException(mb, 0, TYPE, "newVariable: id too long");
624 1 : return -1;
625 : }
626 60582107 : if (makeVarSpace(mb)) { /* no space for a new variable */
627 : return -1;
628 : }
629 60585760 : n = mb->vtop;
630 60585760 : mb->var[n].name = NULL;
631 60585760 : if (name && len > 0) {
632 353056 : char *nme = GDKmalloc(len+1);
633 353065 : if (!nme) {
634 0 : mb->errors = createMalException(mb, 0, TYPE, SQLSTATE(HY013) MAL_MALLOC_FAIL);
635 0 : return -1;
636 : }
637 353065 : mb->var[n].name = nme;
638 2935830 : for (size_t i = 0; i < len; i++)
639 2582765 : nme[i] = name[i];
640 353065 : nme[len] = 0;
641 353065 : kind = nme[0];
642 : }
643 60585769 : mb->vtop++;
644 60585769 : setVarKind(mb, n, kind);
645 60585769 : setVariableType(mb, n, type);
646 60585769 : return n;
647 : }
648 :
649 : /* Simplified cloning. */
650 : int
651 0 : cloneVariable(MalBlkPtr tm, MalBlkPtr mb, int x)
652 : {
653 0 : int res;
654 0 : if (isVarConstant(mb, x))
655 0 : res = cpyConstant(tm, getVar(mb, x));
656 : else {
657 0 : res = newTmpVariable(tm, getVarType(mb, x));
658 0 : if (mb->var[x].name)
659 0 : tm->var[x].name = GDKstrdup(mb->var[x].name);
660 : }
661 0 : if (res < 0)
662 : return res;
663 0 : if (isVarFixed(mb, x))
664 0 : setVarFixed(tm, res);
665 0 : if (isVarUsed(mb, x))
666 0 : setVarUsed(tm, res);
667 0 : if (isVarInit(mb, x))
668 0 : setVarInit(tm, res);
669 0 : if (isVarDisabled(mb, x))
670 0 : setVarDisabled(tm, res);
671 0 : if (isVarCleanup(mb, x))
672 0 : setVarCleanup(tm, res);
673 0 : getVarSTC(tm, x) = getVarSTC(mb, x);
674 0 : setVarKind(tm, x, getVarKind(mb, x));
675 0 : return res;
676 : }
677 :
678 : int
679 60231073 : newTmpVariable(MalBlkPtr mb, malType type)
680 : {
681 60231073 : return newVariable(mb, 0, 0, type);
682 : }
683 :
684 : int
685 275 : newTypeVariable(MalBlkPtr mb, malType type)
686 : {
687 275 : int n, i;
688 1288 : for (i = 0; i < mb->vtop; i++)
689 1056 : if (isVarTypedef(mb, i) && getVarType(mb, i) == type)
690 : break;
691 275 : if (i < mb->vtop)
692 : return i;
693 232 : n = newTmpVariable(mb, type);
694 232 : if (n >= 0)
695 232 : setVarTypedef(mb, n);
696 : return n;
697 : }
698 :
699 : void
700 69742 : clearVariable(MalBlkPtr mb, int varid)
701 : {
702 69742 : VarPtr v;
703 69742 : v = getVar(mb, varid);
704 69742 : if (isVarConstant(mb, varid) || isVarDisabled(mb, varid))
705 26880 : VALclear(&v->value);
706 69742 : if (v->name)
707 0 : GDKfree(v->name);
708 69742 : v->name = NULL;
709 69742 : v->type = 0;
710 69742 : v->constant = 0;
711 69742 : v->typevar = 0;
712 69742 : v->fixedtype = 0;
713 69742 : v->cleanup = 0;
714 69742 : v->initialized = 0;
715 69742 : v->used = 0;
716 69742 : v->rowcnt = 0;
717 69742 : v->eolife = 0;
718 69742 : v->stc = 0;
719 69742 : }
720 :
721 : void
722 81 : freeVariable(MalBlkPtr mb, int varid)
723 : {
724 81 : clearVariable(mb, varid);
725 81 : }
726 :
727 : /* A special action is to reduce the variable space by removing all
728 : * that do not contribute.
729 : * All temporary variables are renamed in the process to trim the varid.
730 : */
731 : void
732 3 : trimMalVariables_(MalBlkPtr mb, MalStkPtr glb)
733 : {
734 3 : int *alias, cnt = 0, i, j;
735 3 : InstrPtr q;
736 3 : if (mb->vtop == 0)
737 : return;
738 3 : alias = (int *) GDKzalloc(mb->vtop * sizeof(int));
739 3 : if (alias == NULL)
740 : return; /* forget it if we run out of memory *//* build the alias table */
741 675 : for (i = 0; i < mb->vtop; i++) {
742 672 : if (isVarUsed(mb, i) == 0) {
743 81 : if (glb && i < glb->stktop && isVarConstant(mb, i))
744 0 : VALclear(&glb->stk[i]);
745 81 : freeVariable(mb, i);
746 81 : continue;
747 : }
748 591 : if (i > cnt) { /* remap temporary variables */
749 531 : VarRecord t = mb->var[cnt];
750 531 : mb->var[cnt] = mb->var[i];
751 531 : mb->var[i] = t;
752 : } /* valgrind finds a leak when we move these variable record * pointers around. */
753 591 : alias[i] = cnt;
754 591 : if (glb && i < glb->stktop && i != cnt) {
755 0 : glb->stk[cnt] = glb->stk[i];
756 0 : VALempty(&glb->stk[i]);
757 : }
758 591 : cnt++;
759 : } /* remap all variable references to their new position. */
760 3 : if (cnt < mb->vtop) {
761 385 : for (i = 0; i < mb->stop; i++) {
762 382 : q = getInstrPtr(mb, i);
763 2271 : for (j = 0; j < q->argc; j++) {
764 1889 : getArg(q, j) = alias[getArg(q, j)];
765 : }
766 : }
767 3 : mb->vtop = cnt;
768 : }
769 3 : GDKfree(alias);
770 : }
771 :
772 : void
773 3 : trimMalVariables(MalBlkPtr mb, MalStkPtr stk)
774 : {
775 3 : int i, j;
776 3 : InstrPtr q; /* reset the use bit for all non-signature arguments */
777 675 : for (i = 0; i < mb->vtop; i++)
778 672 : clrVarUsed(mb, i); /* build the use table */
779 385 : for (i = 0; i < mb->stop; i++) {
780 382 : q = getInstrPtr(mb, i);
781 2271 : for (j = 0; j < q->argc; j++)
782 1889 : setVarUsed(mb, getArg(q, j));
783 : }
784 3 : trimMalVariables_(mb, stk);
785 3 : }
786 :
787 : /* MAL constants
788 : * Constants are stored in the symbol table and referenced by a
789 : * variable identifier. This means that per MAL instruction, we may
790 : * end up with MAXARG entries in the symbol table. This may lead to
791 : * long searches for variables. An optimization strategy deployed in
792 : * the current implementation is to look around for a similar
793 : * (constant) definition and to reuse its identifier. This avoids an
794 : * exploding symbol table with a lot of temporary variables (as in
795 : * tst400cHuge)
796 : *
797 : * But then the question becomes how far to search? Searching through
798 : * all variables is only useful when the list remains short or when
799 : * the constant-variable-name is easily derivable from its literal
800 : * value and a hash-based index leads you quickly to it.
801 : *
802 : * For the time being, we use a MAL system parameter, MAL_VAR_WINDOW,
803 : * to indicate the number of symbol table entries to consider. Setting
804 : * it to >= MAXARG will at least capture repeated use of a constant
805 : * within a single function call or repeated use within a small block
806 : * of code.
807 : *
808 : * The final step is to prepare a GDK value record, from which the
809 : * internal representation can be obtained during MAL interpretation.
810 : *
811 : * The constant values are linked together to improve searching
812 : * them. This start of the constant list is kept in the MalBlk.
813 : *
814 : * Conversion of a constant to another type is limited to well-known
815 : * coercion rules. Errors are reported and the nil value is set. */
816 :
817 : /* Converts the constant in vr to the MAL type type. Conversion is
818 : * done in the vr struct. */
819 : str
820 282102 : convertConstant(int type, ValPtr vr)
821 : {
822 282102 : if (type > GDKatomcnt)
823 0 : throw(SYNTAX, "convertConstant", "type index out of bound");
824 282102 : if (vr->vtype == type)
825 : return MAL_SUCCEED;
826 282100 : if (isaBatType(type)) { /* BAT variables can only be set to nil */
827 0 : if (vr->vtype != TYPE_void)
828 0 : throw(SYNTAX, "convertConstant", "BAT conversion error");
829 0 : VALclear(vr);
830 0 : vr->vtype = getBatType(type);
831 0 : vr->bat = true;
832 0 : vr->val.bval = bat_nil;
833 0 : return MAL_SUCCEED;
834 : }
835 282100 : if (type == TYPE_ptr) { /* all coercions should be avoided to protect against memory probing */
836 32 : if (vr->vtype == TYPE_void) {
837 32 : VALclear(vr);
838 32 : vr->vtype = type;
839 32 : vr->val.pval = NULL;
840 32 : return MAL_SUCCEED;
841 : }
842 0 : if (vr->vtype != type)
843 0 : throw(SYNTAX, "convertConstant", "pointer conversion error");
844 : return MAL_SUCCEED;
845 : }
846 282068 : if (type == TYPE_any) {
847 : #ifndef DEBUG_MAL_INSTR
848 : assert(0);
849 : #endif
850 0 : throw(SYNTAX, "convertConstant", "missing type");
851 : }
852 282068 : if (VALconvert(type, vr) == NULL) {
853 3 : if (vr->vtype == TYPE_str)
854 0 : throw(SYNTAX, "convertConstant", "parse error in '%s'", vr->val.sval);
855 3 : throw(SYNTAX, "convertConstant", "coercion failed");
856 : }
857 : return MAL_SUCCEED;
858 : }
859 :
860 : int
861 49029089 : fndConstant(MalBlkPtr mb, const ValRecord *cst, int depth)
862 : {
863 49029089 : int i, k;
864 49029089 : const void *p; /* pointers never match */
865 49029089 : if (ATOMstorage(cst->vtype) == TYPE_ptr)
866 : return -1;
867 48877087 : p = VALptr(cst);
868 48877087 : k = mb->vtop - depth;
869 48877087 : if (k < 0)
870 : k = 0;
871 553076533 : for (i = k; i < mb->vtop - 1; i++) {
872 534304541 : VarPtr v = getVar(mb, i);
873 534304541 : if (v->constant) {
874 187192948 : if (v && v->type == cst->vtype &&
875 108056605 : v->value.len == cst->len &&
876 178527194 : isaBatType(v->type) == cst->bat &&
877 89269892 : ATOMcmp(cst->vtype, VALptr(&v->value), p) == 0)
878 30097983 : return i;
879 : }
880 : }
881 : return -1;
882 : }
883 :
884 : int
885 3326 : cpyConstant(MalBlkPtr mb, VarPtr vr)
886 : {
887 3326 : int i;
888 3326 : ValRecord cst;
889 3326 : if (VALcopy(&cst, &vr->value) == NULL)
890 : return -1;
891 3326 : i = defConstant(mb, vr->type, &cst);
892 3326 : if (i < 0)
893 : return -1;
894 : return i;
895 : }
896 :
897 : int
898 44159745 : defConstant(MalBlkPtr mb, int type, ValPtr cst)
899 : {
900 44159745 : int k;
901 44159745 : str msg;
902 :
903 44159745 : assert(!isaBatType(type) || cst->bat);
904 44159745 : cst->bat = false;
905 44159745 : if (isaBatType(type)) {
906 410066 : if (cst->vtype == TYPE_void) {
907 410065 : cst->vtype = getBatType(type);
908 410065 : cst->bat = true;
909 410065 : cst->val.bval = bat_nil;
910 : } else {
911 1 : mb->errors = createMalException(mb, 0, TYPE, "BAT coercion error");
912 1 : VALclear(cst); // it could contain allocated space
913 1 : return -1;
914 : }
915 43749679 : } else if (cst->vtype != type && !isPolyType(type)) {
916 1917 : int otype = cst->vtype;
917 1917 : assert(type != TYPE_any); /* help Coverity */
918 1917 : msg = convertConstant(getBatType(type), cst);
919 1917 : if (msg) {
920 3 : str ft, tt; /* free old value */
921 3 : ft = getTypeName(otype);
922 3 : tt = getTypeName(type);
923 3 : if (ft && tt)
924 3 : mb->errors = createMalException(mb, 0, TYPE,
925 : "constant coercion error from %s to %s",
926 : ft, tt);
927 : else
928 0 : mb->errors = createMalException(mb, 0, TYPE,
929 : "constant coercion error");
930 3 : GDKfree(ft);
931 3 : GDKfree(tt);
932 3 : freeException(msg);
933 3 : VALclear(cst); /* it could contain allocated space */
934 3 : return -1;
935 : } else {
936 1914 : assert(cst->vtype == type);
937 : }
938 : }
939 44159741 : if (cst->vtype != TYPE_any) {
940 44160665 : k = fndConstant(mb, cst, MAL_VAR_WINDOW);
941 44162043 : if (k >= 0) { /* protect against leaks coming from constant reuse */
942 26718048 : VALclear(cst);
943 26718048 : return k;
944 : }
945 : }
946 17443071 : k = newTmpVariable(mb, type);
947 17485621 : if (k < 0) {
948 0 : VALclear(cst);
949 0 : return -1;
950 : }
951 17485621 : setVarConstant(mb, k);
952 17485621 : setVarFixed(mb, k);
953 17485621 : if (type >= 0 && type < GDKatomcnt && ATOMextern(type))
954 4443196 : setVarCleanup(mb, k);
955 : else
956 13042425 : clrVarCleanup(mb, k); /* if cst is external, we give its allocated buffer away, so clear * it to avoid confusion */
957 17485621 : getVarConstant(mb, k) = *cst;
958 17485621 : VALempty(cst);
959 17485621 : return k;
960 : }
961 :
962 : /* Argument handling
963 : * The number of arguments for procedures is currently
964 : * limited. Furthermore, we should assure that no variable is
965 : * referenced before being assigned. Failure to obey should mark the
966 : * instruction as type-error. */
967 : static InstrPtr
968 319 : extendInstruction(MalBlkPtr mb, InstrPtr p)
969 : {
970 319 : InstrPtr pn = p;
971 319 : if (p->argc == p->maxarg) {
972 319 : int space = p->maxarg * sizeof(p->argv[0]) + offsetof(InstrRecord, argv);
973 319 : pn = (InstrPtr) GDKrealloc(p, space + MAXARG * sizeof(p->argv[0]));
974 319 : if (pn == NULL) { /* In the exceptional case we can not allocate more space * then we show an exception, mark the block as erroneous * and leave the instruction as is. */
975 0 : mb->errors = createMalException(mb, 0, TYPE,
976 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
977 0 : return p;
978 : }
979 319 : memset(((char *) pn) + space, 0, MAXARG * sizeof(pn->argv[0]));
980 319 : pn->maxarg += MAXARG;
981 : }
982 : return pn;
983 : }
984 :
985 : InstrPtr
986 166396661 : pushArgument(MalBlkPtr mb, InstrPtr p, int varid)
987 : {
988 166396661 : if (p == NULL || mb->errors)
989 : return p;
990 166396661 : if (varid < 0) { /* leave everything as is in this exceptional programming error */
991 0 : mb->errors = createMalException(mb, 0, TYPE, "improper variable id");
992 0 : return p;
993 : }
994 166396661 : if (p->argc == p->maxarg) {
995 : #ifndef NDEBUG
996 1623 : for (int i = 0; i < mb->stop; i++)
997 1304 : assert(mb->stmt[i] != p);
998 : #endif
999 319 : p = extendInstruction(mb, p);
1000 319 : if (mb->errors)
1001 : return p;
1002 : } /* protect against the case that the instruction is malloced in isolation */
1003 166396661 : if (mb->maxarg < p->maxarg)
1004 12530 : mb->maxarg = p->maxarg;
1005 166396661 : p->argv[p->argc++] = varid;
1006 166396661 : return p;
1007 : }
1008 :
1009 : InstrPtr
1010 785208 : setArgument(MalBlkPtr mb, InstrPtr p, int idx, int varid)
1011 : {
1012 785208 : int i;
1013 785208 : if (p == NULL || mb->errors)
1014 : return p;
1015 785214 : p = pushArgument(mb, p, varid); /* make space */
1016 789153 : for (i = p->argc - 1; i > idx; i--)
1017 3946 : getArg(p, i) = getArg(p, i - 1);
1018 785207 : getArg(p, i) = varid;
1019 785207 : return p;
1020 : }
1021 :
1022 : InstrPtr
1023 786119 : pushReturn(MalBlkPtr mb, InstrPtr p, int varid)
1024 : {
1025 786119 : if (p == NULL || mb->errors)
1026 : return p;
1027 786119 : if (p->retc == 1 && p->argv[0] == -1) {
1028 906 : p->argv[0] = varid;
1029 906 : return p;
1030 : }
1031 785213 : p = setArgument(mb, p, p->retc, varid);
1032 785217 : p->retc++;
1033 785217 : return p;
1034 : }
1035 :
1036 : /* Store the information of a destination variable in the signature
1037 : * structure of each instruction. This code is largely equivalent to
1038 : * pushArgument, but it is more efficient in searching and collecting
1039 : * the information.
1040 : * TODO */
1041 : /* swallows name argument */
1042 : InstrPtr
1043 4645 : pushArgumentId(MalBlkPtr mb, InstrPtr p, const char *name)
1044 : {
1045 4645 : int v;
1046 4645 : if (p == NULL || mb->errors)
1047 : return p;
1048 4645 : v = findVariable(mb, name);
1049 4647 : if (v < 0) {
1050 361 : size_t namelen = strlen(name);
1051 361 : if ((v = newVariable(mb, name, namelen, getAtomIndex(name, namelen, TYPE_any))) < 0) {
1052 : /* set the MAL block to erroneous and simply return without
1053 : * doing anything */
1054 : /* mb->errors already set */
1055 : return p;
1056 : }
1057 : }
1058 4647 : return pushArgument(mb, p, v);
1059 : }
1060 :
1061 : /* The alternative is to remove arguments from an instruction
1062 : * record. This is typically part of instruction constructions. */
1063 : void
1064 1472689 : delArgument(InstrPtr p, int idx)
1065 : {
1066 1472689 : int i;
1067 2227794 : for (i = idx; i < p->argc - 1; i++)
1068 755105 : p->argv[i] = p->argv[i + 1];
1069 1472689 : p->argc--;
1070 1472689 : if (idx < p->retc)
1071 174864 : p->retc--;
1072 1472689 : }
1073 :
1074 : void
1075 32869 : setArgType(MalBlkPtr mb, InstrPtr p, int i, int tpe)
1076 : {
1077 32869 : assert(p->argv[i] < mb->vsize);
1078 32869 : setVarType(mb, getArg(p, i), tpe);
1079 32869 : }
1080 :
1081 : void
1082 0 : setReturnArgument(InstrPtr p, int i)
1083 : {
1084 0 : setDestVar(p, i);
1085 0 : }
1086 :
1087 : malType
1088 0 : destinationType(MalBlkPtr mb, InstrPtr p)
1089 : {
1090 0 : if (p->argc > 0)
1091 0 : return getVarType(mb, getDestVar(p));
1092 : return TYPE_any;
1093 : }
1094 :
1095 : /* For polymorphic instructions we should keep around the maximal
1096 : * index to later allocate sufficient space for type resolutions maps.
1097 : * Beware, that we should only consider the instruction polymorphic if
1098 : * it has a positive index or belongs to the signature.
1099 : * BATs can only have a polymorphic type at the tail.
1100 : */
1101 : inline void
1102 9233 : setPolymorphic(InstrPtr p, int tpe, int force /* just any isn't polymorphic */)
1103 : {
1104 9233 : int any = isAnyExpression(tpe) || tpe == TYPE_any, index = 0;
1105 9233 : if ((force == FALSE && tpe == TYPE_any) || !any)
1106 : return;
1107 34 : if (getTypeIndex(tpe) > 0)
1108 : index = getTypeIndex(tpe);
1109 34 : if (any && (index + 1) >= p->polymorphic)
1110 31 : p->polymorphic = index + 1;
1111 : }
1112 :
1113 : /* Instructions are simply appended to a MAL block. It should always succeed.
1114 : * The assumption is to push it when you are completely done with its preparation.
1115 : */
1116 : void
1117 272635968 : pushInstruction(MalBlkPtr mb, InstrPtr p)
1118 : {
1119 272635968 : int i;
1120 272635968 : int extra;
1121 272635968 : InstrPtr q;
1122 272635968 : if (p == NULL)
1123 : return;
1124 272635968 : extra = mb->vsize - mb->vtop; /* the extra variables already known */
1125 272635968 : if (mb->stop + 1 >= mb->ssize) {
1126 56313 : int s = ((mb->ssize + extra) / MALCHUNK + 1) * MALCHUNK;
1127 56313 : if (resizeMalBlk(mb, s) < 0) {
1128 : /* we are now left with the situation that the new
1129 : * instruction is dangling. The hack is to take an
1130 : * instruction out of the block that is likely not
1131 : * referenced independently. The last resort is to take the
1132 : * first, which should always be there. This assumes that
1133 : * no references are kept elsewhere to the statement. */
1134 0 : assert(mb->errors != NULL);
1135 0 : for (i = 1; i < mb->stop; i++) {
1136 0 : q = getInstrPtr(mb, i);
1137 0 : if (q->token == REMsymbol) {
1138 0 : freeInstruction(q);
1139 0 : mb->stmt[i] = p;
1140 0 : return;
1141 : }
1142 : }
1143 0 : freeInstruction(getInstrPtr(mb, 0));
1144 0 : mb->stmt[0] = p;
1145 0 : return;
1146 : }
1147 : }
1148 272635968 : if (mb->stmt[mb->stop])
1149 18987 : freeInstruction(mb->stmt[mb->stop]);
1150 272651616 : p->pc = mb->stop;
1151 272651616 : mb->stmt[mb->stop++] = p;
1152 : }
|