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