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
33typedef 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
46typedef 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.
144static str
145MANIFOLDjob(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);
190bunins_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 */
198MALfcn
199MANIFOLDtypecheck(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 */
260str
261MANIFOLDevaluate(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);
357wrapup:
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
368str
369MANIFOLDremapMultiplex(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