| 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 | |