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