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 : * M.L. Kersten
15 : */
16 : #include "monetdb_config.h"
17 : #include "manifold.h"
18 : #include "mal_resolve.h"
19 : #include "mal_builder.h"
20 :
21 : /* The default iterator over known scalar commands.
22 : * It can be less efficient then the vector based implementations,
23 : * but saves quite some hacking in non-essential cases or
24 : * expensive user defined functions.
25 : *
26 : * To keep things simple and reasonably performant we limit the
27 : * implementation to those cases where a single BAT is returned.
28 : * Arguments may be of any type. The MAL signature should be a COMMAND.
29 : *
30 : * The functionality has been extended to also perform the manifold
31 : * over aligned BATs, provided the underlying scalar function carries
32 : * the 'manifold' property.
33 : */
34 :
35 : typedef struct {
36 : BAT *b;
37 : void *first;
38 : void *last;
39 : int size;
40 : int type;
41 : BUN cnt;
42 : BATiter bi;
43 : BUN o;
44 : BUN q;
45 : str *s;
46 : } MULTIarg;
47 :
48 : typedef struct {
49 : Client cntxt;
50 : MalBlkPtr mb;
51 : MalStkPtr stk;
52 : InstrPtr pci;
53 : int fvar, lvar;
54 : MULTIarg *args;
55 : } MULTItask;
56 :
57 :
58 : // Loop through the first BAT
59 : // keep the last error message received
60 : #define MALfcn1(Type) (str (*) (Type *, void *))
61 : #define MALfcn2(Type) (str (*) (Type *, void *, void *))
62 : #define MALfcn3(Type) (str (*) (Type *, void *, void *, void *))
63 : #define MALfcn4(Type) (str (*) (Type *, void *, void *, void *, void *))
64 : #define MALfcn5(Type) (str (*) (Type *, void *, void *, void *, void *, void *))
65 : #define ManifoldLoop(N, Type, ...) \
66 : do { \
67 : Type *v = (Type *) mut->args[0].first; \
68 : for (;;) { \
69 : msg = (*(MALfcn##N(Type) mut->pci->fcn))(v, __VA_ARGS__); \
70 : if (msg) \
71 : break; \
72 : if (++oo == olimit) \
73 : break; \
74 : for (i = mut->fvar; i <= mut->lvar; i++) { \
75 : if (ATOMstorage(mut->args[i].type) == TYPE_void) { \
76 : args[i] = (void *) &mut->args[i].o; \
77 : mut->args[i].o++; \
78 : } else if (mut->args[i].size == 0) { \
79 : ; \
80 : } else if (ATOMstorage(mut->args[i].type) < TYPE_str) { \
81 : args[i] += mut->args[i].size; \
82 : } else if (ATOMvarsized(mut->args[i].type)) { \
83 : mut->args[i].o++; \
84 : mut->args[i].s = (str *) BUNtvar(mut->args[i].bi, mut->args[i].o); \
85 : args[i] = (void *) &mut->args[i].s; \
86 : } else { \
87 : mut->args[i].o++; \
88 : mut->args[i].s = (str *) BUNtloc(mut->args[i].bi, mut->args[i].o); \
89 : args[i] = (void *) &mut->args[i].s; \
90 : } \
91 : } \
92 : v++; \
93 : } \
94 : } while (0)
95 :
96 : // The target BAT tail type determines the result variable
97 : #ifdef HAVE_HGE
98 : #define Manifoldbody_hge(N,...) \
99 : case TYPE_hge: ManifoldLoop(N,hge,__VA_ARGS__); break
100 : #else
101 : #define Manifoldbody_hge(N,...)
102 : #endif
103 : #define Manifoldbody(N,...) \
104 : do { \
105 : switch (ATOMstorage(mut->args[0].b->ttype)) { \
106 : case TYPE_bte: ManifoldLoop(N,bte,__VA_ARGS__); break; \
107 : case TYPE_sht: ManifoldLoop(N,sht,__VA_ARGS__); break; \
108 : case TYPE_int: ManifoldLoop(N,int,__VA_ARGS__); break; \
109 : case TYPE_lng: ManifoldLoop(N,lng,__VA_ARGS__); break; \
110 : Manifoldbody_hge(N,__VA_ARGS__); \
111 : case TYPE_oid: ManifoldLoop(N,oid,__VA_ARGS__); break; \
112 : case TYPE_flt: ManifoldLoop(N,flt,__VA_ARGS__); break; \
113 : case TYPE_dbl: ManifoldLoop(N,dbl,__VA_ARGS__); break; \
114 : case TYPE_uuid: ManifoldLoop(N,uuid,__VA_ARGS__); break; \
115 : case TYPE_str: \
116 : default: { \
117 : for (;;) { \
118 : msg = (*(MALfcn##N(str) mut->pci->fcn))(&y, __VA_ARGS__); \
119 : if (msg) \
120 : break; \
121 : if (bunfastapp(mut->args[0].b, (void*) y) != GDK_SUCCEED) \
122 : goto bunins_failed; \
123 : GDKfree(y); \
124 : y = NULL; \
125 : if (++oo == olimit) \
126 : break; \
127 : for (i = mut->fvar; i <= mut->lvar; i++) { \
128 : if (ATOMstorage(mut->args[i].type) == TYPE_void) { \
129 : args[i] = (void *) &mut->args[i].o; \
130 : mut->args[i].o++; \
131 : } else if(mut->args[i].size == 0) { \
132 : ; \
133 : } else if (ATOMstorage(mut->args[i].type) < TYPE_str) { \
134 : args[i] += mut->args[i].size; \
135 : } else if (ATOMvarsized(mut->args[i].type)) { \
136 : mut->args[i].o++; \
137 : mut->args[i].s = (str *) BUNtvar(mut->args[i].bi, mut->args[i].o); \
138 : args[i] = (void *) &mut->args[i].s; \
139 : } else { \
140 : mut->args[i].o++; \
141 : mut->args[i].s = (str *) BUNtloc(mut->args[i].bi, mut->args[i].o); \
142 : args[i] = (void*) &mut->args[i].s; \
143 : } \
144 : } \
145 : } \
146 : break; \
147 : } \
148 : } \
149 : mut->args[0].b->theap->dirty = true; \
150 : } while (0)
151 :
152 : // single argument is preparatory step for GDK_mapreduce
153 : // Only the last error message is returned, the value of
154 : // an erroneous call depends on the operator itself.
155 : static str
156 658 : MANIFOLDjob(MULTItask *mut)
157 : {
158 658 : int i;
159 658 : char **args;
160 658 : str y = NULL, msg = MAL_SUCCEED;
161 658 : oid oo = 0, olimit = mut->args[mut->fvar].cnt;
162 :
163 658 : if (olimit == 0)
164 : return msg; /* nothing to do */
165 :
166 609 : args = (char **) GDKzalloc(sizeof(char *) * mut->pci->argc);
167 609 : if (args == NULL)
168 0 : throw(MAL, "mal.manifold", SQLSTATE(HY013) MAL_MALLOC_FAIL);
169 :
170 : // the mod.fcn arguments are ignored from the call
171 1830 : for (i = mut->pci->retc + 2; i < mut->pci->argc; i++) {
172 1221 : if (mut->args[i].b) {
173 811 : if (ATOMstorage(mut->args[i].type) < TYPE_str) {
174 56 : args[i] = (char *) mut->args[i].first;
175 755 : } else if (ATOMvarsized(mut->args[i].type)) {
176 737 : mut->args[i].s = BUNtvar(mut->args[i].bi, mut->args[i].o);
177 737 : args[i] = (void *) &mut->args[i].s;
178 : } else {
179 18 : mut->args[i].s = BUNtloc(mut->args[i].bi, mut->args[i].o);
180 18 : args[i] = (void *) &mut->args[i].s;
181 : }
182 : } else {
183 410 : args[i] = (char *) getArgReference(mut->stk, mut->pci, i);
184 : }
185 : }
186 :
187 : /* TRC_DEBUG(MAL_SERVER, "fvar %d lvar %d type %d\n", mut->fvar,mut->lvar, ATOMstorage(mut->args[mut->fvar].b->ttype)); */
188 :
189 : // use limited argument list expansion.
190 609 : switch (mut->pci->argc) {
191 145 : case 4:
192 220673 : Manifoldbody(1, args[3]);
193 145 : break;
194 348 : case 5:
195 1841 : Manifoldbody(2, args[3], args[4]);
196 348 : break;
197 87 : case 6:
198 54609 : Manifoldbody(3, args[3], args[4], args[5]);
199 87 : break;
200 26 : case 7:
201 476 : Manifoldbody(4, args[3], args[4], args[5], args[6]);
202 26 : break;
203 3 : case 8:
204 45 : Manifoldbody(5, args[3], args[4], args[5], args[6], args[7]);
205 3 : break;
206 0 : default:
207 0 : msg = createException(MAL, "mal.manifold", "manifold call limitation ");
208 : }
209 609 : if (ATOMextern(mut->args[0].type) && y)
210 0 : GDKfree(y);
211 609 : bunins_failed:
212 609 : GDKfree(args);
213 609 : return msg;
214 : }
215 :
216 : /* The manifold optimizer should check for the possibility
217 : * to use this implementation instead of the MAL loop.
218 : */
219 : MALfcn
220 4444 : MANIFOLDtypecheck(Client cntxt, MalBlkPtr mb, InstrPtr pci, int checkprops)
221 : {
222 4444 : int i, k, tpe = 0;
223 4444 : InstrPtr q = 0;
224 4444 : MalBlkPtr nmb;
225 4444 : MALfcn fcn;
226 :
227 4444 : if (mb->errors)
228 : return NULL;
229 4444 : if (getArgType(mb, pci, pci->retc) == TYPE_lng) {
230 : // TODO: trivial case
231 : return NULL;
232 : }
233 :
234 4444 : if (pci->retc > 1 || pci->argc > 8 || getModuleId(pci) == NULL) // limitation on MANIFOLDjob
235 : return NULL;
236 : // We need a private MAL context to resolve the function call
237 4402 : nmb = newMalBlk(2);
238 4402 : if (nmb == NULL) {
239 0 : mb->errors = createException(MAL, "mal.manifold", SQLSTATE(HY013) MAL_MALLOC_FAIL);
240 0 : return NULL;
241 : }
242 : // the scalar function
243 8804 : q = newStmt(nmb,
244 4402 : getVarConstant(mb, getArg(pci, pci->retc)).val.sval,
245 4402 : getVarConstant(mb, getArg(pci, pci->retc + 1)).val.sval);
246 4402 : if (q == NULL) {
247 0 : goto bailout;
248 : }
249 : // Prepare the single result variable
250 4402 : tpe = getBatType(getArgType(mb, pci, 0));
251 4402 : k = getArg(q, 0);
252 4402 : setVarType(nmb, k, tpe);
253 4402 : if (isVarFixed(nmb, k))
254 0 : setVarFixed(nmb, k);
255 :
256 : // extract their scalar argument type
257 12957 : for (i = pci->retc + 2; i < pci->argc; i++) {
258 8555 : tpe = getBatType(getArgType(mb, pci, i));
259 8555 : k = newTmpVariable(nmb, tpe);
260 8555 : if (k < 0) {
261 0 : freeInstruction(q);
262 0 : goto bailout;
263 : }
264 8555 : q = pushArgument(nmb, q, k);
265 8555 : setVarFixed(nmb, k);
266 : }
267 4402 : pushInstruction(nmb, q);
268 :
269 4402 : if (nmb->errors)
270 0 : goto bailout;
271 :
272 : /*
273 : TRC_DEBUG(MAL_SERVER, "Manifold operation\n");
274 : traceInstruction(MAL_SERVER, mb, 0, pci, LIST_MAL_ALL);
275 : traceInstruction(MAL_SERVER, nmb, 0, q, LIST_MAL_ALL);
276 : */
277 : // Localize the underlying scalar operator
278 4402 : typeChecker(cntxt->usermodule, nmb, q, getPC(nmb, q), TRUE);
279 4402 : if (nmb->errors)
280 0 : goto bailout;
281 4402 : if (q->fcn == NULL || q->token != CMDcall ||
282 1242 : (checkprops && q->blk && q->blk->unsafeProp))
283 : fcn = NULL;
284 : else {
285 3126 : fcn = q->fcn;
286 : // retain the type detected
287 3126 : if (!isVarFixed(mb, getArg(pci, 0)))
288 0 : setVarType(mb, getArg(pci, 0), newBatType(getArgType(nmb, q, 0)));
289 : }
290 :
291 : /*
292 : TRC_DEBUG(MAL_SERVER, "Success? %s\n", (fcn == NULL? "no":"yes"));
293 : traceInstruction(MAL_SERVER, nmb, 0, q, LIST_MAL_ALL);
294 : */
295 :
296 4402 : freeMalBlk(nmb);
297 4402 : return fcn;
298 :
299 0 : bailout:
300 : /* there was an error, perhaps it's in nmb->errors */
301 0 : assert(mb->errors == NULL);
302 0 : if ((mb->errors = nmb->errors) == NULL)
303 0 : mb->errors = createException(MAL, "mal.manifold", SQLSTATE(HY013) MAL_MALLOC_FAIL);
304 0 : nmb->errors = NULL;
305 0 : freeMalBlk(nmb);
306 0 : return NULL;
307 : }
308 :
309 : /*
310 : * The manifold should support aligned BATs as well
311 : */
312 : static str
313 658 : MANIFOLDevaluate(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
314 : {
315 658 : MULTItask mut;
316 658 : MULTIarg *mat;
317 658 : int i, tpe = 0;
318 658 : BUN cnt = 0;
319 658 : oid o = 0;
320 658 : str msg = MAL_SUCCEED;
321 658 : MALfcn fcn;
322 :
323 658 : fcn = MANIFOLDtypecheck(cntxt, mb, pci, 0);
324 658 : if (fcn == NULL) {
325 0 : if (mb->errors) {
326 0 : msg = mb->errors;
327 0 : mb->errors = NULL;
328 0 : return msg;
329 : }
330 0 : throw(MAL, "mal.manifold", "Illegal manifold function call");
331 : }
332 :
333 658 : mat = (MULTIarg *) GDKzalloc(sizeof(MULTIarg) * pci->argc);
334 658 : if (mat == NULL)
335 0 : throw(MAL, "mal.manifold", SQLSTATE(HY013) MAL_MALLOC_FAIL);
336 :
337 : // mr-job structure preparation
338 658 : mut.fvar = mut.lvar = 0;
339 658 : mut.cntxt = cntxt;
340 658 : mut.mb = mb;
341 658 : mut.stk = stk;
342 658 : mut.args = mat;
343 658 : mut.pci = pci;
344 :
345 : // prepare iterators
346 1962 : for (i = pci->retc + 2; i < pci->argc; i++) {
347 1304 : if (isaBatType(getArgType(mb, pci, i))) {
348 877 : mat[i].b = BATdescriptor(*getArgReference_bat(stk, pci, i));
349 876 : if (mat[i].b == NULL) {
350 0 : msg = createException(MAL, "mal.manifold",
351 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
352 0 : goto wrapup;
353 : }
354 876 : mat[i].bi = bat_iterator(mat[i].b);
355 877 : mat[i].type = tpe = getBatType(getArgType(mb, pci, i));
356 877 : if (mut.fvar == 0) {
357 658 : mut.fvar = i;
358 658 : cnt = BATcount(mat[i].b);
359 219 : } else if (BATcount(mat[i].b) != cnt) {
360 0 : msg = createException(MAL, "mal.manifold",
361 : "Columns must be of same length");
362 0 : goto wrapup;
363 : }
364 877 : mut.lvar = i;
365 877 : mat[i].size = mat[i].bi.width;
366 877 : mat[i].cnt = cnt;
367 877 : if (mat[i].b->ttype == TYPE_void) {
368 0 : o = mat[i].b->tseqbase;
369 0 : mat[i].first = mat[i].last = (void *) &o;
370 : } else {
371 877 : mat[i].first = (void *) mat[i].bi.base;
372 877 : mat[i].last = (void *) ((char *) mat[i].bi.base + (BATcount(mat[i].b) << mat[i].bi.shift));
373 : }
374 877 : mat[i].o = 0;
375 877 : mat[i].q = BATcount(mat[i].b);
376 : } else {
377 427 : mat[i].last = mat[i].first = (void *) getArgReference(stk, pci, i);
378 427 : mat[i].type = getArgType(mb, pci, i);
379 : }
380 : }
381 :
382 : // Then iterator over all BATs
383 658 : if (mut.fvar == 0) {
384 0 : msg = createException(MAL, "mal.manifold",
385 : "At least one column required");
386 0 : goto wrapup;
387 : }
388 : // prepare result variable
389 1316 : mat[0].b = COLnew(mat[mut.fvar].b->hseqbase,
390 658 : getBatType(getArgType(mb, pci, 0)), cnt, TRANSIENT);
391 658 : if (mat[0].b == NULL) {
392 0 : msg = createException(MAL, "mal.manifold",
393 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
394 0 : goto wrapup;
395 : }
396 658 : mat[0].b->tnonil = false;
397 658 : mat[0].b->tsorted = false;
398 658 : mat[0].b->trevsorted = false;
399 658 : mat[0].bi = (BATiter) {.b = NULL, };
400 658 : mat[0].first = (void *) Tloc(mat[0].b, 0);
401 658 : mat[0].last = (void *) Tloc(mat[0].b, BATcount(mat[0].b));
402 :
403 658 : mut.pci = copyInstruction(pci);
404 658 : if (mut.pci == NULL) {
405 0 : msg = createException(MAL, "mal.manifold",
406 : SQLSTATE(HY013) MAL_MALLOC_FAIL);
407 0 : goto wrapup;
408 : }
409 658 : mut.pci->fcn = fcn;
410 658 : msg = MANIFOLDjob(&mut);
411 658 : freeInstruction(mut.pci);
412 :
413 658 : wrapup:
414 : // restore the argument types
415 3278 : for (i = pci->retc; i < pci->argc; i++) {
416 2620 : if (mat[i].b) {
417 877 : bat_iterator_end(&mat[i].bi);
418 877 : BBPunfix(mat[i].b->batCacheid);
419 : }
420 : }
421 658 : if (msg) {
422 6 : BBPreclaim(mat[0].b);
423 652 : } else if (!msg) {
424 : // consolidate the properties
425 652 : if (ATOMstorage(mat[0].b->ttype) < TYPE_str)
426 416 : BATsetcount(mat[0].b, cnt);
427 652 : BATsettrivprop(mat[0].b);
428 652 : *getArgReference_bat(stk, pci, 0) = mat[0].b->batCacheid;
429 652 : BBPkeepref(mat[0].b);
430 : }
431 658 : GDKfree(mat);
432 658 : return msg;
433 : }
434 :
435 : // The old code
436 : static str
437 1 : MANIFOLDremapMultiplex(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
438 : {
439 1 : (void) mb;
440 1 : (void) cntxt;
441 4 : throw(MAL, "mal.multiplex", "Function '%s.%s' not defined",
442 1 : *getArgReference_str(stk, p, p->retc),
443 1 : *getArgReference_str(stk, p, p->retc + 1));
444 : }
445 :
446 : #include "mel.h"
447 : mel_func manifold_init_funcs[] = {
448 : pattern("mal", "multiplex", MANIFOLDremapMultiplex, false, "", args(1,4, varargany("",0),arg("mod",str),arg("fcn",str),varargany("a",0))),
449 : pattern("mal", "multiplex", MANIFOLDremapMultiplex, false, "", args(1,4, varargany("",0),arg("card", lng), arg("mod",str),arg("fcn",str))),
450 : pattern("mal", "multiplex", MANIFOLDremapMultiplex, false, "", args(1,5, varargany("",0),arg("card", lng), arg("mod",str),arg("fcn",str),varargany("a",0))),
451 : pattern("batmal", "multiplex", MANIFOLDremapMultiplex, false, "", args(1,4, varargany("",0),arg("mod",str),arg("fcn",str),varargany("a",0))),
452 : pattern("mal", "manifold", MANIFOLDevaluate, false, "", args(1,4, batargany("",0),arg("mod",str),arg("fcn",str),varargany("a",0))),
453 : { .imp=NULL }
454 : };
455 : #include "mal_import.h"
456 : #ifdef _MSC_VER
457 : #undef read
458 : #pragma section(".CRT$XCU",read)
459 : #endif
460 329 : LIB_STARTUP_FUNC(init_manifold_mal)
461 329 : { mal_module("manifold", NULL, manifold_init_funcs); }
|