static int OPTallConstant(Client cntxt, MalBlkPtr mb, InstrPtr p) { int i; (void)cntxt; if ( !(p->token == ASSIGNsymbol || getModuleId(p) == calcRef || getModuleId(p) == strRef || getModuleId(p) == mtimeRef || getModuleId(p) == mmathRef)) return FALSE; if (getModuleId(p) == mmathRef && strcmp(getFunctionId(p), "rand") == 0) return FALSE; for (i = p->retc; i < p->argc; i++) if (isVarConstant(mb, getArg(p, i)) == FALSE) return FALSE; for (i = 0; i < p->retc; i++) { if (isaBatType(getArgType(mb, p, i))) return FALSE; if ( mb->unsafeProp ) return FALSE; } return TRUE; }
/* * At any point we should be able to construct an ascii representation of * the type descriptor. Including the variable references. */ str getTypeName(malType tpe) { char buf[PATHLENGTH], *s; size_t l = PATHLENGTH; int k; if (tpe == TYPE_any) return GDKstrdup("any"); if (isaBatType(tpe)) { snprintf(buf, l, "bat["); l -= strlen(buf); s = buf + strlen(buf); k = getColumnIndex(tpe); if (k) snprintf(s, l, ":any%c%d]",TMPMARKER, k); else if (getColumnType(tpe) == TYPE_any) snprintf(s, l, ":any]"); else snprintf(s, l, ":%s]", ATOMname(getColumnType(tpe))); return GDKstrdup(buf); } if (isAnyExpression(tpe)) { strncpy(buf, "any", 4); if (isAnyExpression(tpe)) snprintf(buf + 3, PATHLENGTH - 3, "%c%d", TMPMARKER, getColumnIndex(tpe)); return GDKstrdup(buf); } return GDKstrdup(ATOMname(tpe)); }
inline int findGDKtype(int type) { if (type == TYPE_any || type== TYPE_void) return TYPE_void; if (isaBatType(type)) return TYPE_bat; return ATOMtype(type); }
static int lastbat_arg(MalBlkPtr mb, InstrPtr p) { int i = 0; for (i=p->retc; i<p->argc; i++) { int type = getArgType(mb, p, i); if (!isaBatType(type) && type != TYPE_bat) break; } if (i < p->argc) return i-1; return 0; }
/* the MAL beautifier is meant to simplify correlation of MAL variables and * the columns in the underlying database. * If the status is set, then we consider the instruction DONE and the result variables * should be shown as well. */ static str shortRenderingTerm(MalBlkPtr mb, MalStkPtr stk, InstrPtr p, int idx) { str s, nme; BAT *b; ValRecord *val; char *cv =0; int varid = getArg(p,idx); size_t len = BUFSIZ; s= GDKmalloc(len); if( s == NULL) return NULL; *s = 0; if( isVarConstant(mb,varid) ){ val =&getVarConstant(mb, varid); if ((cv = VALformat(val)) == NULL) { GDKfree(s); return NULL; } if (strlen(cv) >= len) { char *nbuf; len = strlen(cv); nbuf = GDKrealloc(s, len + 1); if (nbuf == NULL) { GDKfree(s); GDKfree(cv); return NULL; } s = nbuf; } snprintf(s,len + 1,"%s",cv); } else { val = &stk->stk[varid]; if ((cv = VALformat(val)) == NULL) { GDKfree(s); return NULL; } nme = getVarName(mb, varid); if ( isaBatType(getArgType(mb,p,idx))){ b = BBPquickdesc(stk->stk[varid].val.bval, true); snprintf(s,BUFSIZ,"%s["BUNFMT"]" ,nme, b?BATcount(b):0); } else snprintf(s,BUFSIZ,"%s=%s ",nme,cv); } GDKfree(cv); return s; }
int OPTmatpackImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { int v, i, j, limit, slimit; InstrPtr p,q; int actions = 0; InstrPtr *old; char *packIncrementRef = putName("packIncrement", 13); (void) pci; (void) cntxt; (void) stk; /* to fool compilers */ old= mb->stmt; limit= mb->stop; slimit = mb->ssize; if ( newMalBlkStmt(mb,mb->stop) < 0) return 0; for (i = 0; i < limit; i++) { p = old[i]; if( getModuleId(p) == matRef && getFunctionId(p) == packRef && isaBatType(getArgType(mb,p,1))) { q = newStmt(mb, matRef, packIncrementRef); v = getArg(q,0); setVarType(mb,v,getArgType(mb,p,1)); q = pushArgument(mb, q, getArg(p,1)); q = pushInt(mb,q, p->argc - p->retc); for ( j = 2; j < p->argc; j++) { q = newStmt(mb,matRef, packIncrementRef); q = pushArgument(mb, q, v); q = pushArgument(mb, q, getArg(p,j)); setVarType(mb,getArg(q,0),getVarType(mb,v)); v = getArg(q,0); } getArg(q,0) = getArg(p,0); freeInstruction(p); continue; } pushInstruction(mb,p); } for(; i<slimit; i++) if (old[i]) freeInstruction(old[i]); GDKfree(old); return actions; }
static int OPTallConstant(Client cntxt, MalBlkPtr mb, InstrPtr p) { int i; (void)cntxt; if ( !( p->token == ASSIGNsymbol || getModuleId(p) == calcRef || getModuleId(p) == strRef || getModuleId(p) == mmathRef )) return FALSE; for (i = p->retc; i < p->argc; i++) if (isVarConstant(mb, getArg(p, i)) == FALSE) return FALSE; for (i = 0; i < p->retc; i++) if (isaBatType(getArgType(mb, p, i))) return FALSE; return p->argc != p->retc; }
str SQLdiff(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { (void)cntxt; if (isaBatType(getArgType(mb, pci, 1))) { bat *res = getArgReference_bat(stk, pci, 0); bat *bid = getArgReference_bat(stk, pci, 1); BAT *b = BATdescriptor(*bid), *c, *r; gdk_return gdk_code; if (!b) throw(SQL, "sql.diff", SQLSTATE(HY005) "Cannot access column descriptor"); voidresultBAT(r, TYPE_bit, BATcount(b), b, "sql.diff"); if (pci->argc > 2) { c = b; bid = getArgReference_bat(stk, pci, 2); b = BATdescriptor(*bid); if (!b) { BBPunfix(c->batCacheid); throw(SQL, "sql.diff", SQLSTATE(HY005) "Cannot access column descriptor"); } gdk_code = GDKanalyticaldiff(r, b, c, b->ttype); BBPunfix(c->batCacheid); } else { gdk_code = GDKanalyticaldiff(r, b, NULL, b->ttype); } BBPunfix(b->batCacheid); if(gdk_code == GDK_SUCCEED) BBPkeepref(*res = r->batCacheid); else throw(SQL, "sql.diff", GDK_EXCEPTION); } else { bit *res = getArgReference_bit(stk, pci, 0); *res = FALSE; } return MAL_SUCCEED; }
/* the MAL beautifier is meant to simplify correlation of MAL variables and * the columns in the underlying database. * If the status is set, then we consider the instruction DONE and the result variables * should be shown as well. */ static str shortRenderingTerm(MalBlkPtr mb, MalStkPtr stk, InstrPtr p, int idx) { str s, nme; BAT *b; ValRecord *val; char *cv =0; int varid = getArg(p,idx); s= GDKmalloc(BUFSIZ); if( s == NULL) return NULL; *s = 0; if( isVarConstant(mb,varid) ){ val =&getVarConstant(mb, varid); VALformat(&cv, val); snprintf(s,BUFSIZ,"%s",cv); } else { val = &stk->stk[varid]; VALformat(&cv, val); nme = getSTC(mb, varid); if( nme == NULL) nme = getVarName(mb, varid); if ( isaBatType(getArgType(mb,p,idx))){ b = BBPquickdesc(abs(stk->stk[varid].val.ival),TRUE); snprintf(s,BUFSIZ,"%s["BUNFMT"]" ,nme, b?BATcount(b):0); } else if( cv) snprintf(s,BUFSIZ,"%s=%s ",nme,cv); else snprintf(s,BUFSIZ,"%s ",nme); } GDKfree(cv); return s; }
static str renderTerm(MalBlkPtr mb, MalStkPtr stk, InstrPtr p, int idx, int flg) { char *buf =0; char *nme =0; int nameused = 0; size_t len = 0, maxlen = BUFSIZ; ValRecord *val = 0; char *cv =0; str tpe; int showtype = 0, closequote=0; int varid = getArg(p,idx); buf = GDKzalloc(maxlen); if( buf == NULL) { addMalException(mb, "renderTerm:Failed to allocate"); return NULL; } // show the name when required or is used if ((flg & LIST_MAL_NAME) && !isVarConstant(mb,varid) && !isVarTypedef(mb,varid)) { nme = getVarName(mb,varid); len +=snprintf(buf, maxlen, "%s", nme); nameused =1; } // show the value when required or being a constant if( ((flg & LIST_MAL_VALUE) && stk != 0) || isVarConstant(mb,varid) ){ if (nameused){ strcat(buf + len,"="); len++; } // locate value record if (isVarConstant(mb,varid)){ val = &getVarConstant(mb, varid); showtype= getVarType(mb,varid) != TYPE_str && getVarType(mb,varid) != TYPE_bit; } else if( stk) val = &stk->stk[varid]; if ((cv = VALformat(val)) == NULL) { addMalException(mb, "renderTerm:Failed to allocate"); GDKfree(buf); return NULL; } if (len + strlen(cv) >= maxlen) { char *nbuf= GDKrealloc(buf, maxlen =len + strlen(cv) + BUFSIZ); if( nbuf == 0){ GDKfree(buf); GDKfree(cv); addMalException(mb,"renderTerm:Failed to allocate"); return NULL; } buf = nbuf; } if( strcmp(cv,"nil") == 0){ strcat(buf+len,cv); len += strlen(buf+len); GDKfree(cv); showtype = showtype || getBatType(getVarType(mb,varid)) > TYPE_str || ((isVarUDFtype(mb,varid) || isVarTypedef(mb,varid)) && isVarConstant(mb,varid)) || isaBatType(getVarType(mb,varid)); } else{ if ( !isaBatType(getVarType(mb,varid)) && getBatType(getVarType(mb,varid)) > TYPE_str ){ closequote = 1; strcat(buf+len,"\""); len++; } strcat(buf+len,cv); len += strlen(buf+len); GDKfree(cv); if( closequote ){ strcat(buf+len,"\""); len++; } showtype = showtype || closequote > TYPE_str || ((isVarUDFtype(mb,varid) || isVarTypedef(mb,varid) || (flg & (LIST_MAL_REMOTE | LIST_MAL_TYPE))) && isVarConstant(mb,varid)) || (isaBatType(getVarType(mb,varid)) && idx < p->retc); if (stk && isaBatType(getVarType(mb,varid)) && stk->stk[varid].val.bval ){ BAT *d= BBPquickdesc(stk->stk[varid].val.bval, true); if( d) len += snprintf(buf+len,maxlen-len,"[" BUNFMT "]", BATcount(d)); } } } // show the type when required or frozen by the user // special care should be taken with constants, they may have been casted if ((flg & LIST_MAL_TYPE) || (isVarUDFtype(mb, varid) && idx < p->retc) || isVarTypedef(mb,varid) || showtype){ strcat(buf + len,":"); len++; tpe = getTypeName(getVarType(mb, varid)); len += snprintf(buf+len,maxlen-len,"%s",tpe); GDKfree(tpe); } if( len >= maxlen) addMalException(mb,"renderTerm:Value representation too large"); return buf; }
str RAPIeval(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci, bit grouped) { sql_func * sqlfun = NULL; str exprStr = *getArgReference_str(stk, pci, pci->retc + 1); SEXP x, env, retval; SEXP varname = R_NilValue; SEXP varvalue = R_NilValue; ParseStatus status; int i = 0; char argbuf[64]; char *argnames = NULL; size_t argnameslen; size_t pos; char* rcall = NULL; size_t rcalllen; int ret_cols = 0; /* int because pci->retc is int, too*/ str *args; int evalErr; char *msg = MAL_SUCCEED; BAT *b; node * argnode; int seengrp = FALSE; rapiClient = cntxt; if (!RAPIEnabled()) { throw(MAL, "rapi.eval", "Embedded R has not been enabled. Start server with --set %s=true", rapi_enableflag); } if (!rapiInitialized) { throw(MAL, "rapi.eval", "Embedded R initialization has failed"); } if (!grouped) { sql_subfunc *sqlmorefun = (*(sql_subfunc**) getArgReference(stk, pci, pci->retc)); if (sqlmorefun) sqlfun = (*(sql_subfunc**) getArgReference(stk, pci, pci->retc))->func; } else { sqlfun = *(sql_func**) getArgReference(stk, pci, pci->retc); } args = (str*) GDKzalloc(sizeof(str) * pci->argc); if (args == NULL) { throw(MAL, "rapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); } // get the lock even before initialization of the R interpreter, as this can take a second and must be done only once. MT_lock_set(&rapiLock); env = PROTECT(eval(lang1(install("new.env")), R_GlobalEnv)); assert(env != NULL); // first argument after the return contains the pointer to the sql_func structure // NEW macro temporarily renamed to MNEW to allow including sql_catalog.h if (sqlfun != NULL && sqlfun->ops->cnt > 0) { int carg = pci->retc + 2; argnode = sqlfun->ops->h; while (argnode) { char* argname = ((sql_arg*) argnode->data)->name; args[carg] = GDKstrdup(argname); carg++; argnode = argnode->next; } } // the first unknown argument is the group, we don't really care for the rest. argnameslen = 2; for (i = pci->retc + 2; i < pci->argc; i++) { if (args[i] == NULL) { if (!seengrp && grouped) { args[i] = GDKstrdup("aggr_group"); seengrp = TRUE; } else { snprintf(argbuf, sizeof(argbuf), "arg%i", i - pci->retc - 1); args[i] = GDKstrdup(argbuf); } } argnameslen += strlen(args[i]) + 2; /* extra for ", " */ } // install the MAL variables into the R environment // we can basically map values to int ("INTEGER") or double ("REAL") for (i = pci->retc + 2; i < pci->argc; i++) { int bat_type = getBatType(getArgType(mb,pci,i)); // check for BAT or scalar first, keep code left if (!isaBatType(getArgType(mb,pci,i))) { b = COLnew(0, getArgType(mb, pci, i), 0, TRANSIENT); if (b == NULL) { msg = createException(MAL, "rapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); goto wrapup; } if ( getArgType(mb,pci,i) == TYPE_str) { if (BUNappend(b, *getArgReference_str(stk, pci, i), false) != GDK_SUCCEED) { BBPreclaim(b); b = NULL; msg = createException(MAL, "rapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); goto wrapup; } } else { if (BUNappend(b, getArgReference(stk, pci, i), false) != GDK_SUCCEED) { BBPreclaim(b); b = NULL; msg = createException(MAL, "rapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); goto wrapup; } } } else { b = BATdescriptor(*getArgReference_bat(stk, pci, i)); if (b == NULL) { msg = createException(MAL, "rapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); goto wrapup; } } // check the BAT count, if it is bigger than RAPI_MAX_TUPLES, fail if (BATcount(b) > RAPI_MAX_TUPLES) { msg = createException(MAL, "rapi.eval", "Got "BUNFMT" rows, but can only handle "LLFMT". Sorry.", BATcount(b), (lng) RAPI_MAX_TUPLES); BBPunfix(b->batCacheid); goto wrapup; } varname = PROTECT(Rf_install(args[i])); varvalue = bat_to_sexp(b, bat_type); if (varvalue == NULL) { msg = createException(MAL, "rapi.eval", "unknown argument type "); goto wrapup; } BBPunfix(b->batCacheid); // install vector into R environment Rf_defineVar(varname, varvalue, env); UNPROTECT(2); } /* we are going to evaluate the user function within an anonymous function call: * ret <- (function(arg1){return(arg1*2)})(42) * the user code is put inside the {}, this keeps our environment clean (TM) and gives * a clear path for return values, namely using the builtin return() function * this is also compatible with PL/R */ pos = 0; argnames = malloc(argnameslen); if (argnames == NULL) { msg = createException(MAL, "rapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); goto wrapup; } argnames[0] = '\0'; for (i = pci->retc + 2; i < pci->argc; i++) { pos += snprintf(argnames + pos, argnameslen - pos, "%s%s", args[i], i < pci->argc - 1 ? ", " : ""); } rcalllen = 2 * pos + strlen(exprStr) + 100; rcall = malloc(rcalllen); if (rcall == NULL) { msg = createException(MAL, "rapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); goto wrapup; } snprintf(rcall, rcalllen, "ret <- as.data.frame((function(%s){%s})(%s), nm=NA, stringsAsFactors=F)\n", argnames, exprStr, argnames); free(argnames); argnames = NULL; #ifdef _RAPI_DEBUG_ printf("# R call %s\n",rcall); #endif x = R_ParseVector(mkString(rcall), 1, &status, R_NilValue); if (LENGTH(x) != 1 || status != PARSE_OK) { msg = createException(MAL, "rapi.eval", "Error parsing R expression '%s'. ", exprStr); goto wrapup; } retval = R_tryEval(VECTOR_ELT(x, 0), env, &evalErr); if (evalErr != FALSE) { char* errormsg = strdup(R_curErrorBuf()); size_t c; if (errormsg == NULL) { msg = createException(MAL, "rapi.eval", "Error running R expression."); goto wrapup; } // remove newlines from error message so it fits into a MAPI error (lol) for (c = 0; c < strlen(errormsg); c++) { if (errormsg[c] == '\r' || errormsg[c] == '\n') { errormsg[c] = ' '; } } msg = createException(MAL, "rapi.eval", "Error running R expression: %s", errormsg); free(errormsg); goto wrapup; } // ret should be a data frame with exactly as many columns as we need from retc ret_cols = LENGTH(retval); if (ret_cols != pci->retc) { msg = createException(MAL, "rapi.eval", "Expected result of %d columns, got %d", pci->retc, ret_cols); goto wrapup; } // collect the return values for (i = 0; i < pci->retc; i++) { SEXP ret_col = VECTOR_ELT(retval, i); int bat_type = getBatType(getArgType(mb,pci,i)); if (bat_type == TYPE_any || bat_type == TYPE_void) { getArgType(mb,pci,i) = bat_type; msg = createException(MAL, "rapi.eval", "Unknown return value, possibly projecting with no parameters."); goto wrapup; } // hand over the vector into a BAT b = sexp_to_bat(ret_col, bat_type); if (b == NULL) { msg = createException(MAL, "rapi.eval", "Failed to convert column %i", i); goto wrapup; } // bat return if (isaBatType(getArgType(mb,pci,i))) { *getArgReference_bat(stk, pci, i) = b->batCacheid; } else { // single value return, only for non-grouped aggregations BATiter li = bat_iterator(b); if (VALinit(&stk->stk[pci->argv[i]], bat_type, BUNtail(li, 0)) == NULL) { // TODO BUNtail here msg = createException(MAL, "rapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); goto wrapup; } } msg = MAL_SUCCEED; } /* unprotect environment, so it will be eaten by the GC. */ UNPROTECT(1); wrapup: MT_lock_unset(&rapiLock); if (argnames) free(argnames); if (rcall) free(rcall); for (i = 0; i < pci->argc; i++) GDKfree(args[i]); GDKfree(args); return msg; }
int OPTrecyclerImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p) { int i, j, cnt, tp, c, actions = 0, marks = 0, delta = 0; Lifespan span; InstrPtr *old, q; int limit, updstmt = 0; char *recycled; short app_sc = -1, in = 0; ValRecord cst; (void) cntxt; (void) stk; limit = mb->stop; old = mb->stmt; for (i = 1; i < limit; i++) { p = old[i]; if (getModuleId(p) == sqlRef && (getFunctionId(p) == affectedRowsRef || getFunctionId(p) == exportOperationRef || getFunctionId(p) == appendRef || getFunctionId(p) == updateRef || getFunctionId(p) == deleteRef)) updstmt = 1; } span = setLifespan(mb); if (span == NULL) return 0; /* watch out, newly created instructions may introduce new variables */ recycled = GDKzalloc(sizeof(char) * mb->vtop * 2); if (recycled == NULL) return 0; if (newMalBlkStmt(mb, mb->ssize) < 0) { GDKfree(recycled); return 0; } pushInstruction(mb, old[0]); mb->recid = recycleSeq++; /* create a handle for recycler */ (void) newFcnCall(mb, "recycle", "prelude"); in = 1; for (i = 1; i < limit; i++) { p = old[i]; if (hasSideEffects(p, TRUE) || isUpdateInstruction(p) || isUnsafeFunction(p)) { if (getModuleId(p) == recycleRef) { /*don't inline recycle instr. */ freeInstruction(p); continue; } pushInstruction(mb, p); /* update instructions are not recycled but monitored*/ if (isUpdateInstruction(p)) { if (getModuleId(p) == batRef && (getArgType(mb, p, 1) == TYPE_bat || isaBatType(getArgType(mb, p, 1)))) { recycled[getArg(p, 1)] = 0; q = newFcnCall(mb, "recycle", "reset"); pushArgument(mb, q, getArg(p, 1)); actions++; } if (getModuleId(p) == sqlRef) { if (getFunctionId(p) == appendRef) { if (app_sc >= 0) continue; else app_sc = getArg(p, 2); } VALset(&cst, TYPE_int, &delta); c = defConstant(mb, TYPE_int, &cst); q = newFcnCall(mb, "recycle", "reset"); pushArgument(mb, q, c); pushArgument(mb, q, getArg(p, 2)); pushArgument(mb, q, getArg(p, 3)); if (getFunctionId(p) == updateRef) pushArgument(mb, q, getArg(p, 4)); actions++; } } /* take care of SQL catalog update instructions */ if (getModuleId(p) == sqlRef && getFunctionId(p) == catalogRef) { tp = *(int *) getVarValue(mb, getArg(p, 1)); if (tp == 22 || tp == 25) { delta = 2; VALset(&cst, TYPE_int, &delta); c = defConstant(mb, TYPE_int, &cst); q = newFcnCall(mb, "recycle", "reset"); pushArgument(mb, q, c); pushArgument(mb, q, getArg(p, 2)); if (tp == 25) pushArgument(mb, q, getArg(p, 3)); actions++; } } continue; } if (p->token == ENDsymbol || p->barrier == RETURNsymbol) { if (in) { /* if (updstmt && app_sc >= 0) { q = newFcnCall(mb, "recycle", "reset"); pushArgument(mb, q, app_sc); pushArgument(mb, q, app_tbl); } */ (void) newFcnCall(mb, "recycle", "epilogue"); in = 0; } pushInstruction(mb, p); continue; } if (p->barrier && p->token != CMDcall) { /* never save a barrier unless it is a command and side-effect free */ pushInstruction(mb, p); continue; } /* don't change instructions in update statements */ if (updstmt) { pushInstruction(mb, p); continue; } /* skip simple assignments */ if (p->token == ASSIGNsymbol) { pushInstruction(mb, p); continue; } if (getModuleId(p) == octopusRef && (getFunctionId(p) == bindRef || getFunctionId(p) == bindidxRef)) { recycled[getArg(p, 0)] = 1; p->recycle = recycleMaxInterest; marks++; } /* During base table recycling skip marking instructions other than octopus.bind */ if (baseTableMode) { pushInstruction(mb, p); continue; } /* general rule: all arguments are constants or recycled, ignore C pointer arguments from mvc */ cnt = 0; for (j = p->retc; j < p->argc; j++) if (recycled[getArg(p, j)] || isVarConstant(mb, getArg(p, j)) || ignoreVar(mb, getArg(p, j))) cnt++; if (cnt == p->argc - p->retc) { OPTDEBUGrecycle { mnstr_printf(cntxt->fdout, "#recycle instruction\n"); printInstruction(cntxt->fdout, mb, 0, p, LIST_MAL_ALL); } marks++; p->recycle = recycleMaxInterest; /* this instruction is to be monitored */ for (j = 0; j < p->retc; j++) if (getLastUpdate(span, getArg(p, j)) == i) recycled[getArg(p, j)] = 1; } /* * The expected gain is largest if we can re-use selections * on the base tables in SQL. These, however, are marked as * uselect() calls, which only produce the oid head. * For cheap types we preselect using select() and re-map uselect() back * over this temporary. * For the time being for all possible selects encountered * are marked for re-use. */ /* take care of semantic driven recyling */ /* for selections check the bat argument only the range is often template parameter*/ if ((getFunctionId(p) == selectRef || getFunctionId(p) == antiuselectRef || getFunctionId(p) == likeselectRef || getFunctionId(p) == likeRef || getFunctionId(p) == thetaselectRef) && recycled[getArg(p, 1)]) { p->recycle = recycleMaxInterest; marks++; if (getLastUpdate(span, getArg(p, 0)) == i) recycled[getArg(p, 0)] = 1; } if ((getFunctionId(p) == uselectRef || getFunctionId(p) == thetauselectRef) && recycled[getArg(p, 1)]) { if (!ATOMvarsized(getGDKType(getArgType(mb, p, 2)))) { q = copyInstruction(p); getArg(q, 0) = newTmpVariable(mb, TYPE_any); if (getFunctionId(p) == uselectRef) setFunctionId(q, selectRef); else setFunctionId(q, thetaselectRef); q->recycle = recycleMaxInterest; marks++; recycled[getArg(q, 0)] = 1; pushInstruction(mb, q); getArg(p, 1) = getArg(q, 0); setFunctionId(p, projectRef); p->argc = 2; } p->recycle = recycleMaxInterest; marks++; if (getLastUpdate(span, getArg(p, 0)) == i) recycled[getArg(p, 0)] = 1; } if (getModuleId(p) == pcreRef) { if ((getFunctionId(p) == selectRef && recycled[getArg(p, 2)]) || (getFunctionId(p) == uselectRef && recycled[getArg(p, 2)])) { p->recycle = recycleMaxInterest; marks++; if (getLastUpdate(span, getArg(p, 0)) == i) recycled[getArg(p, 0)] = 1; } else if (getFunctionId(p) == likeuselectRef && recycled[getArg(p, 1)]) { q = copyInstruction(p); getArg(q, 0) = newTmpVariable(mb, TYPE_any); setFunctionId(q, likeselectRef); q->recycle = recycleMaxInterest; recycled[getArg(q, 0)] = 1; pushInstruction(mb, q); getArg(p, 1) = getArg(q, 0); setFunctionId(p, projectRef); setModuleId(p, algebraRef); p->argc = 2; p->recycle = recycleMaxInterest; marks += 2; if (getLastUpdate(span, getArg(p, 0)) == i) recycled[getArg(p, 0)] = 1; } } /* * The sql.bind instructions should be handled carefully * The delete and update BATs should not be recycled, * because they may lead to view dependencies that later interferes * with the transaction commits. */ /* enable recycling of delta-bats if (getModuleId(p) == sqlRef && (((getFunctionId(p) == bindRef || getFunctionId(p) == putName("bind_idxbat", 11)) && getVarConstant(mb, getArg(p, 5)).val.ival != 0) || getFunctionId(p) == binddbatRef)) { recycled[getArg(p, 0)] = 0; p->recycle = REC_NO_INTEREST; } */ /* * The sql.bind instructions should be handled carefully * The delete and update BATs should not be recycled, * because they may lead to view dependencies that later interferes * with the transaction commits. */ /* enable recycling of delta-bats if (getModuleId(p)== sqlRef && (((getFunctionId(p)==bindRef || getFunctionId(p) == putName("bind_idxbat",11)) && getVarConstant(mb, getArg(p,5)).val.ival != 0) || getFunctionId(p)== binddbatRef) ) { recycled[getArg(p,0)]=0; p->recycle = REC_NO_INTEREST; } */ pushInstruction(mb, p); }
str OPTgeneratorImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { InstrPtr p,q, *old, *series; int i, k, limit, slimit, actions=0; str m; str bteRef = getName("bte"); str shtRef = getName("sht"); str intRef = getName("int"); str lngRef = getName("lng"); str fltRef = getName("flt"); str dblRef = getName("dbl"); char buf[256]; lng usec= GDKusec(); (void) cntxt; (void) stk; (void) pci; series = (InstrPtr*) GDKzalloc(sizeof(InstrPtr) * mb->vtop); if(series == NULL) throw(MAL,"optimizer.generator", SQLSTATE(HY001) MAL_MALLOC_FAIL); old = mb->stmt; limit = mb->stop; slimit = mb->ssize; // check applicability first for( i=0; i < limit; i++){ p = old[i]; if ( getModuleId(p) == generatorRef && getFunctionId(p) == seriesRef) break; } if (i == limit) { GDKfree(series); return 0; } if (newMalBlkStmt(mb, mb->ssize) < 0) { GDKfree(series); throw(MAL,"optimizer.generator", SQLSTATE(HY001) MAL_MALLOC_FAIL); } for( i=0; i < limit; i++){ p = old[i]; if (p->token == ENDsymbol){ pushInstruction(mb,p); break; } if ( getModuleId(p) == generatorRef && getFunctionId(p) == seriesRef){ series[getArg(p,0)] = p; setModuleId(p, generatorRef); setFunctionId(p, parametersRef); typeChecker(cntxt->usermodule, mb, p, TRUE); pushInstruction(mb,p); } else if ( getModuleId(p) == algebraRef && getFunctionId(p) == selectRef && series[getArg(p,1)]){ errorCheck(p,algebraRef,getArg(p,1)); } else if ( getModuleId(p) == algebraRef && getFunctionId(p) == thetaselectRef && series[getArg(p,1)]){ errorCheck(p,algebraRef,getArg(p,1)); } else if ( getModuleId(p) == algebraRef && getFunctionId(p) == projectionRef && series[getArg(p,2)]){ errorCheck(p,algebraRef,getArg(p,2)); } else if ( getModuleId(p) == sqlRef && getFunctionId(p) == putName("exportValue") && isaBatType(getArgType(mb,p,0)) ){ // interface expects scalar type only, not expressable in MAL signature mb->errors=createException(MAL, "generate_series", SQLSTATE(42000) "internal error, generate_series is a table producing function"); }else if ( getModuleId(p) == batcalcRef && getFunctionId(p) == bteRef && series[getArg(p,1)] && p->argc == 2 ){ casting(bte); } else if ( getModuleId(p) == batcalcRef && getFunctionId(p) == shtRef && series[getArg(p,1)] && p->argc == 2 ){ casting(sht); } else if ( getModuleId(p) == batcalcRef && getFunctionId(p) == intRef && series[getArg(p,1)] && p->argc == 2 ){ casting(int); } else if ( getModuleId(p) == batcalcRef && getFunctionId(p) == lngRef && series[getArg(p,1)] && p->argc == 2 ){ casting(lng); } else if ( getModuleId(p) == batcalcRef && getFunctionId(p) == fltRef && series[getArg(p,1)] && p->argc == 2 ){ casting(flt); } else if ( getModuleId(p) == batcalcRef && getFunctionId(p) == dblRef && series[getArg(p,1)] && p->argc == 2 ){ casting(dbl); } else if ( getModuleId(p) == languageRef && getFunctionId(p) == passRef ) pushInstruction(mb,p); else { // check for use without conversion for(k = p->retc; k < p->argc; k++) if( series[getArg(p,k)]){ m = getModuleId(p); setModuleId(p, generatorRef); typeChecker(cntxt->usermodule, mb, p, TRUE); if(p->typechk == TYPE_UNKNOWN){ setModuleId(p,m); typeChecker(cntxt->usermodule, mb, p, TRUE); setModuleId(series[getArg(p,k)], generatorRef); setFunctionId(series[getArg(p,k)], seriesRef); typeChecker(cntxt->usermodule, mb, series[getArg(p,k)], TRUE); } } pushInstruction(mb,p); } } for (i++; i < limit; i++) pushInstruction(mb, old[i]); for (; i < slimit; i++) if (old[i]) freeInstruction(old[i]); GDKfree(old); GDKfree(series); #ifdef VLT_DEBUG fprintFunction(stderr,mb,0,LIST_MAL_ALL); #endif /* Defense line against incorrect plans */ /* all new/modified statements are already checked */ //chkTypes(cntxt->usermodule, mb, FALSE); //chkFlow(mb); //chkDeclarations(mb); /* keep all actions taken as a post block comment */ usec = GDKusec()- usec; snprintf(buf,256,"%-20s actions=%2d time=" LLFMT " usec","generator",actions, usec); newComment(mb,buf); if( actions >= 0) addtoMalBlkHistory(mb); return MAL_SUCCEED; }
int OPTrecyclerImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { int i, j, cnt, cand, actions = 1, marks = 0; InstrPtr *old, q,p; int limit; char *recycled; (void) cntxt; (void) stk; (void) pci; limit = mb->stop; old = mb->stmt; /* watch out, newly created instructions may introduce new variables */ recycled = GDKzalloc(sizeof(char) * mb->vtop * 2); if (recycled == NULL) return 0; if (newMalBlkStmt(mb, mb->ssize) < 0) { GDKfree(recycled); return 0; } pushInstruction(mb, old[0]); for (i = 1; i < limit; i++) { p = old[i]; if (p->token == ENDsymbol ) break; /* the first non-dataflow barrier breaks the recycler code*/ if (blockStart(p) && !(getFunctionId(p) && getFunctionId(p) == dataflowRef) ) break; if ( isUpdateInstruction(p) || hasSideEffects(p,TRUE)){ /* update instructions are not recycled but monitored*/ pushInstruction(mb, p); if (isUpdateInstruction(p)) { if (getModuleId(p) == batRef && isaBatType(getArgType(mb, p, 1))) { q = newFcnCall(mb, "recycle", "reset"); pushArgument(mb, q, getArg(p, 1)); actions++; } if (getModuleId(p) == sqlRef) { q= copyInstruction(p); getModuleId(q) = recycleRef; actions++; } } continue; } // Not all instruction may be recycled. In particular, we should avoid // MAL function with implicit/recursive side effects. // This can not always be detected easily. Likewise, we ignore cheap operations // Therefore, we use a safe subset to start with if ( ! (getModuleId(p) == sqlRef || getModuleId(p)== batRef || getModuleId(p) == algebraRef || getModuleId(p)==batcalcRef || getModuleId(p)== aggrRef || getModuleId(p)== groupRef || getModuleId(p)== batstrRef || getModuleId(p)== batmmathRef || getModuleId(p)== arrayRef || getModuleId(p)== batmtimeRef || getModuleId(p)== batcalcRef || getModuleId(p)== pcreRef || getModuleId(p)== mtimeRef || getModuleId(p) == calcRef || getModuleId(p)== dateRef || getModuleId(p) == timestampRef || getModuleId(p)== matRef ) ){ pushInstruction(mb,p); continue; } /* general rule: all arguments should be constants or recycled*/ cnt = 0; for (j = p->retc; j < p->argc; j++) if (recycled[getArg(p, j)] || isVarConstant(mb, getArg(p, j)) || isFunctionArgument(mb,getArg(p,j)) ) cnt++; cand = 0; for (j =0; j< p->retc; j++) if (recycled[getArg(p, j)] ==0) cand++; if (cnt == p->argc - p->retc && cand == p->retc) { marks++; p->recycle = RECYCLING; /* this instruction is to be monitored */ for (j = 0; j < p->retc; j++) recycled[getArg(p, j)] = 1; } pushInstruction(mb, p); } for (; i < limit; i++) pushInstruction(mb, old[i]); GDKfree(old); GDKfree(recycled); mb->recycle = marks > 0; OPTDEBUGrecycle { mnstr_printf(cntxt->fdout, "#recycle optimizer: "); printFunction(cntxt->fdout,mb, 0, LIST_MAL_ALL); } return actions + marks; }
str CMDifthen(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { BAT *b = NULL, *b1 = NULL, *b2 = NULL, *bn; int tp0, tp1, tp2; bat *ret; BUN cnt = BUN_NONE; (void) cntxt; (void) mb; if (pci->argc != 4) throw(MAL, "batcalc.ifthen", "Operation not supported."); ret = getArgReference_bat(stk, pci, 0); tp0 = stk->stk[getArg(pci, 1)].vtype; tp1 = stk->stk[getArg(pci, 2)].vtype; tp2 = stk->stk[getArg(pci, 3)].vtype; if (tp0 == TYPE_bat || isaBatType(tp0)) { b = BATdescriptor(* getArgReference_bat(stk, pci, 1)); if (b == NULL) throw(MAL, "batcalc.ifthenelse", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); cnt = BATcount(b); } if (tp1 == TYPE_bat || isaBatType(tp1)) { b1 = BATdescriptor(* getArgReference_bat(stk, pci, 2)); if (b1 == NULL) { if (b) BBPunfix(b->batCacheid); throw(MAL, "batcalc.ifthenelse", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); } if (cnt == BUN_NONE) cnt = BATcount(b1); else if (BATcount(b1) != cnt) { BBPunfix(b->batCacheid); throw(MAL, "batcalc.ifthenelse", ILLEGAL_ARGUMENT); } } if (tp2 == TYPE_bat || isaBatType(tp2)) { b2 = BATdescriptor(* getArgReference_bat(stk, pci, 3)); if (b2 == NULL) { if (b) BBPunfix(b->batCacheid); if (b1) BBPunfix(b1->batCacheid); throw(MAL, "batcalc.ifthenelse", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); } if (cnt == BUN_NONE) cnt = BATcount(b2); else if (BATcount(b2) != cnt) { if (b) BBPunfix(b->batCacheid); if (b1) BBPunfix(b1->batCacheid); throw(MAL, "batcalc.ifthenelse", ILLEGAL_ARGUMENT); } } if (b == NULL && b1 == NULL && b2 == NULL) { /* at least one BAT required */ throw(MAL, "batcalc.ifthenelse", ILLEGAL_ARGUMENT); } if (b != NULL) { if (b1 != NULL) { if (b2 != NULL) { bn = BATcalcifthenelse(b, b1, b2); } else { bn = BATcalcifthenelsecst(b, b1, &stk->stk[getArg(pci, 3)]); } } else { if (b2 != NULL) { bn = BATcalcifthencstelse(b, &stk->stk[getArg(pci, 2)], b2); } else { bn = BATcalcifthencstelsecst(b, &stk->stk[getArg(pci, 2)], &stk->stk[getArg(pci, 3)]); } } } else { bit v = *getArgReference_bit(stk, pci, 1); if (is_bit_nil(v)) { if (b1 != NULL) bn = BATconstant(b1->hseqbase, b1->ttype, ATOMnilptr(b1->ttype), BATcount(b1), TRANSIENT); else bn = BATconstant(b2->hseqbase, b2->ttype, ATOMnilptr(b2->ttype), BATcount(b2), TRANSIENT); } else if (v) { if (b1 != NULL) bn = COLcopy(b1, b1->ttype, 0, TRANSIENT); else bn = BATconstant(b2->hseqbase, b2->ttype, VALptr(&stk->stk[getArg(pci, 2)]), BATcount(b2), TRANSIENT); } else { if (b2 != NULL) bn = COLcopy(b2, b2->ttype, 0, TRANSIENT); else bn = BATconstant(b1->hseqbase, b1->ttype, VALptr(&stk->stk[getArg(pci, 3)]), BATcount(b1), TRANSIENT); } } if (b) BBPunfix(b->batCacheid); if (b1) BBPunfix(b1->batCacheid); if (b2) BBPunfix(b2->batCacheid); if (bn == NULL) { return mythrow(MAL, "batcalc.ifthenelse", OPERATION_FAILED); } BBPkeepref(*ret = bn->batCacheid); return MAL_SUCCEED; }
static str CMDbatBINARY2(MalBlkPtr mb, MalStkPtr stk, InstrPtr pci, BAT *(*batfunc)(BAT *, BAT *, BAT *, int, int), BAT *(batfunc1)(BAT *, const ValRecord *, BAT *, int, int), BAT *(batfunc2)(const ValRecord *, BAT *, BAT *, int, int), int (*typefunc)(int, int), int abort_on_error, const char *malfunc) { bat *bid; BAT *bn, *b, *s = NULL; int tp1, tp2, tp3; tp1 = stk->stk[getArg(pci, 1)].vtype; tp2 = stk->stk[getArg(pci, 2)].vtype; tp3 = getArgType(mb, pci, 0); assert(isaBatType(tp3)); tp3 = getBatType(tp3); if (pci->argc == 4) { bat *sid = getArgReference_bat(stk, pci, 3); if (*sid && (s = BATdescriptor(*sid)) == NULL) throw(MAL, malfunc, SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); } if (tp1 == TYPE_bat || isaBatType(tp1)) { BAT *b2 = NULL; bid = getArgReference_bat(stk, pci, 1); b = BATdescriptor(*bid); if (b == NULL) { if (s) BBPunfix(s->batCacheid); throw(MAL, malfunc, SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); } if (tp2 == TYPE_bat || isaBatType(tp2)) { bid = getArgReference_bat(stk, pci, 2); b2 = BATdescriptor(*bid); if (b2 == NULL) { BBPunfix(b->batCacheid); if (s) BBPunfix(s->batCacheid); throw(MAL, malfunc, SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); } } if (b2) { if (tp3 == TYPE_any) tp3 = (*typefunc)(b->ttype, b2->ttype); bn = (*batfunc)(b, b2, s, tp3, abort_on_error); BBPunfix(b2->batCacheid); } else { if (tp3 == TYPE_any) tp3 = (*typefunc)(b->ttype, tp2); bn = (*batfunc1)(b, &stk->stk[getArg(pci, 2)], s, tp3, abort_on_error); } } else { assert(tp1 != TYPE_bat && !isaBatType(tp1)); assert(tp2 == TYPE_bat || isaBatType(tp2)); bid = getArgReference_bat(stk, pci, 2); b = BATdescriptor(*bid); if (b == NULL) { if (s) BBPunfix(s->batCacheid); throw(MAL, malfunc, SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); } if (tp3 == TYPE_any) tp3 = (*typefunc)(tp1, b->ttype); bn = (*batfunc2)(&stk->stk[getArg(pci, 1)], b, s, tp3, abort_on_error); } BBPunfix(b->batCacheid); if (bn == NULL) { return mythrow(MAL, malfunc, OPERATION_FAILED); } bid = getArgReference_bat(stk, pci, 0); BBPkeepref(*bid = bn->batCacheid); return MAL_SUCCEED; }
/* * The generic solution to the multiplex operators is to translate * them to a MAL loop. * The call optimizer.multiplex(MOD,FCN,A1,...An) introduces the following code * structure: * * resB:= bat.new(A1); * barrier (h,t1):= iterator.new(A1); * t2:= algebra.fetch(A2,h) * ... * cr:= MOD.FCN(t1,...,tn); * bat.append(resB,cr); * redo (h,t):= iterator.next(A1); * end h; * * The algorithm consists of two phases: phase one deals with * collecting the relevant information, phase two is the actual * code construction. */ static str OPTexpandMultiplex(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { int i = 2, iter = 0; int hvar, tvar; str mod, fcn; int *alias, *resB; InstrPtr q; int tt; int bat = (getModuleId(pci) == batmalRef) ; //if ( optimizerIsApplied(mb,"multiplex")) //return 0; (void) cntxt; (void) stk; for (i = 0; i < pci->retc; i++) { tt = getBatType(getArgType(mb, pci, i)); if (tt== TYPE_any) throw(MAL, "optimizer.multiplex", SQLSTATE(HY002) "Target tail type is missing"); if (isAnyExpression(getArgType(mb, pci, i))) throw(MAL, "optimizer.multiplex", SQLSTATE(HY002) "Target type is missing"); } mod = VALget(&getVar(mb, getArg(pci, pci->retc))->value); mod = putName(mod); fcn = VALget(&getVar(mb, getArg(pci, pci->retc+1))->value); fcn = putName(fcn); if(mod == NULL || fcn == NULL) throw(MAL, "optimizer.multiplex", SQLSTATE(HY001) MAL_MALLOC_FAIL); #ifndef NDEBUG fprintf(stderr,"#WARNING To speedup %s.%s a bulk operator implementation is needed\n#", mod,fcn); fprintInstruction(stderr, mb, stk, pci, LIST_MAL_DEBUG); #endif /* search the iterator bat */ for (i = pci->retc+2; i < pci->argc; i++) if (isaBatType(getArgType(mb, pci, i))) { iter = getArg(pci, i); break; } if( i == pci->argc) throw(MAL, "optimizer.multiplex", SQLSTATE(HY002) "Iterator BAT type is missing"); #ifdef DEBUG_OPT_MULTIPLEX { char *tpenme; fprintf(stderr,"#calling the optimize multiplex script routine\n"); fprintFunction(stderr,mb, 0, LIST_MAL_ALL ); tpenme = getTypeName(getVarType(mb,iter)); fprintf(stderr,"#multiplex against operator %d %s\n",iter, tpenme); GDKfree(tpenme); fprintInstruction(stderr,mb, 0, pci,LIST_MAL_ALL); } #endif /* * Beware, the operator constant (arg=1) is passed along as well, * because in the end we issue a recursive function call that should * find the actual arguments at the proper place of the callee. */ alias= (int*) GDKmalloc(sizeof(int) * pci->maxarg); resB = (int*) GDKmalloc(sizeof(int) * pci->retc); if (alias == NULL || resB == NULL) { GDKfree(alias); GDKfree(resB); return NULL; } /* resB := new(refBat) */ for (i = 0; i < pci->retc; i++) { q = newFcnCall(mb, batRef, newRef); resB[i] = getArg(q, 0); tt = getBatType(getArgType(mb, pci, i)); setVarType(mb, getArg(q, 0), newBatType(tt)); q = pushType(mb, q, tt); } /* barrier (h,r) := iterator.new(refBat); */ q = newFcnCall(mb, iteratorRef, newRef); q->barrier = BARRIERsymbol; hvar = newTmpVariable(mb, TYPE_any); getArg(q,0) = hvar; tvar = newTmpVariable(mb, TYPE_any); q= pushReturn(mb, q, tvar); (void) pushArgument(mb,q,iter); /* $1:= algebra.fetch(Ai,h) or constant */ for (i = pci->retc+2; i < pci->argc; i++) { if (getArg(pci, i) != iter && isaBatType(getArgType(mb, pci, i))) { q = newFcnCall(mb, algebraRef, "fetch"); alias[i] = newTmpVariable(mb, getBatType(getArgType(mb, pci, i))); getArg(q, 0) = alias[i]; q= pushArgument(mb, q, getArg(pci, i)); (void) pushArgument(mb, q, hvar); } } /* cr:= mod.CMD($1,...,$n); */ q = newFcnCall(mb, mod, fcn); for (i = 0; i < pci->retc; i++) { int nvar = 0; if (bat) { tt = getBatType(getArgType(mb, pci, i)); nvar = newTmpVariable(mb, newBatType(tt)); } else { nvar = newTmpVariable(mb, TYPE_any); } if (i) q = pushReturn(mb, q, nvar); else getArg(q, 0) = nvar; } for (i = pci->retc+2; i < pci->argc; i++) { if (getArg(pci, i) == iter) { q = pushArgument(mb, q, tvar); } else if (isaBatType(getArgType(mb, pci, i))) { q = pushArgument(mb, q, alias[i]); } else { q = pushArgument(mb, q, getArg(pci, i)); } } for (i = 0; i < pci->retc; i++) { InstrPtr a = newFcnCall(mb, batRef, appendRef); a = pushArgument(mb, a, resB[i]); (void) pushArgument(mb, a, getArg(q,i)); } /* redo (h,r):= iterator.next(refBat); */ q = newFcnCall(mb, iteratorRef, nextRef); q->barrier = REDOsymbol; getArg(q,0) = hvar; q= pushReturn(mb, q, tvar); (void) pushArgument(mb,q,iter); q = newAssignment(mb); q->barrier = EXITsymbol; getArg(q,0) = hvar; (void) pushReturn(mb, q, tvar); for (i = 0; i < pci->retc; i++) { q = newAssignment(mb); getArg(q, 0) = getArg(pci, i); (void) pushArgument(mb, q, resB[i]); } GDKfree(alias); GDKfree(resB); return MAL_SUCCEED; }
int OPTaccumulatorsImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { int i, limit,slimit; InstrPtr p,q; Module scope = cntxt->nspace; int actions = 0; InstrPtr *old; Lifespan span; (void) pci; (void) stk; /* to fool compilers */ span = setLifespan(mb); if( span == NULL) return 0; old= mb->stmt; limit= mb->stop; slimit= mb->ssize; if ( newMalBlkStmt(mb,mb->stop) < 0){ GDKfree(span); return 0; } for (i = 0; i < limit; i++) { p = old[i]; if( getModuleId(p) != batcalcRef ) { pushInstruction(mb,p); continue; } OPTDEBUGaccumulators printInstruction(cntxt->fdout, mb, 0, p, LIST_MAL_ALL); if (p->retc==1 && p->argc == 2) { /* unary operation, avoid clash with binary */ pushInstruction(mb,p); continue; } if( getLastUpdate(span,getArg(p,0)) != i ) { /* only consider the last update to this variable */ pushInstruction(mb,p); continue; } if (p->retc==1 && p->argc == 3 && isaBatType(getArgType(mb,p,0))) { int b1 =getEndLifespan(span,getArg(p,1))<=i && getArgType(mb,p,1) == getArgType(mb,p,0); int b2 =getEndLifespan(span,getArg(p,2))<=i && getArgType(mb,p,2) == getArgType(mb,p,0) ; if ( b1 == 0 && b2 == 0){ pushInstruction(mb,p); continue; } /* binary/unary operation, check arguments for being candidates */ q= copyInstruction(p); p= pushBit(mb,p, b1); p= pushBit(mb,p, b2); typeChecker(cntxt->fdout, scope, mb, p, TRUE); if (mb->errors || p->typechk == TYPE_UNKNOWN) { OPTDEBUGaccumulators{ mnstr_printf(cntxt->fdout,"# Failed typecheck"); printInstruction(cntxt->fdout, mb, 0, p, LIST_MAL_ALL); } /* reset instruction error buffer */ cntxt->errbuf[0]=0; mb->errors = 0; freeInstruction(p); p=q; /* restore */ } else {
/* * The cost will be used in many places to make decisions. * Access should be fast. * The SQL front-end also makes the BAT index available as the * property bid. This can be used to access the BAT and involve * more properties into the decision procedure. * [to be done] * Also make sure you don't re-use variables, because then the * row count becomes non-deterministic. */ int OPTcostModelImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { int i; BUN c1, c2; InstrPtr p; (void) cntxt; (void) stk; (void) pci; if ( mb->inlineProp ) return 0; for (i = 0; i < mb->stop; i++) { p = getInstrPtr(mb, i); if (getModuleId(p)==algebraRef) { if (getFunctionId(p) == subselectRef || getFunctionId(p) == thetasubselectRef) { newRows(1,2, (c1 > 2 ? c2 / 2 +1: c1/2+1),0); } else if ( getFunctionId(p) == selectNotNilRef || getFunctionId(p) == sortRef || getFunctionId(p) == subsortRef || getFunctionId(p) == projectRef ){ newRows(1,1,c1,0); } else if (getFunctionId(p) == subjoinRef || getFunctionId(p) == projectionRef || getFunctionId(p) == subbandjoinRef || getFunctionId(p) == projectionpathRef ) { /* assume 1-1 joins */ newRows(1,2,(c1 < c2 ? c1 : c2),0); } else if (getFunctionId(p) == crossRef) { newRows(1,2,((log((double) c1) + log((double) c2) > log(INT_MAX) ? INT_MAX : c1 * c2 +1)),0); /* log sets errno if it cannot compute the log. This will then screw with code that checks errno */ if (errno == ERANGE || errno == EDOM) { errno = 0; } } } else if (getModuleId(p) == batcalcRef) { if( getFunctionId(p) == ifthenelseRef) { if( isaBatType(getArgType(mb,p,2) ) ) { newRows(2,2, c1,0); } else { newRows(3,3, c1,0); } } else if( isaBatType(getArgType(mb,p,1)) ){ newRows(1,1, c1,0); } else { newRows(2, 2, c2,0); } } else if (getModuleId(p) == batstrRef) { newRows(1,1, c1,0); } else if (getModuleId(p) == batRef) { if (getFunctionId(p) == appendRef || getFunctionId(p) == insertRef ){ /* * Updates are a little more complicated, because you have to * propagate changes in the expected size up the expression tree. * For example, the SQL snippet: * _49:bat[:oid,:oid]{rows=0,bid=622} := sql.bind_dbat("sys","example",3); * _54 := bat.setWriteMode(_49); * bat.append(_54,_47,true); * shows what is produced when it encounters a deletion. If a non-empty * append is not properly passed back to _49, the emptySet * optimizer might remove the complete deletion code. * The same holds for replacement operations, which add information to * an initially empty insertion BAT. */ if( isaBatType(getArgType(mb,p,2)) ){ /* insert BAT */ newRows(1,2, (c1 + c2+1),1); } else { /* insert scalars */ newRows(1,1, (c1 +1),1); } } else if (getFunctionId(p) == deleteRef){ if( isaBatType(getArgType(mb,p,2)) ){ /* delete BAT */ newRows(1, 2, (c2 >= c1 ? 1 : c1 - c2), 1); } else { /* insert scalars */ newRows(1, 1, (c1 <= 1 ? 1 : c1 - 1), 1); } } else if (getFunctionId(p) == insertRef){ newRows(1,1,( c1 + 1),0); /* faked */ } } else if (getModuleId(p)==groupRef) { if (getFunctionId(p) ==subgroupRef ) { newRows(1,1,( c1 / 10+1),0); } else { newRows(1,1, c1,0); } } else if (getModuleId(p)== aggrRef) { if (getFunctionId(p) == sumRef || getFunctionId(p) == minRef || getFunctionId(p) == maxRef || getFunctionId(p) == avgRef) { newRows(1, 1, (c1 != 0 ? c1 : 1), 0); } else if (getFunctionId(p) == countRef){ newRows(1,1, 1,0); } } else if( p->token == ASSIGNsymbol && p->argc== 2){ /* copy the rows property */ c1 = getRowCnt(mb, getArg(p,1)); /* just to ensure that rowcnt was/is never set to -1 */ assert(c1 != (BUN) -1); if (c1 != BUN_NONE) setRowCnt(mb, getArg(p,0), c1); } } return 1; }
/* * The generic solution to the multiplex operators is to translate * them to a MAL loop. * The call optimizer.multiplex(MOD,FCN,A1,...An) introduces the following code * structure: * * @verbatim * A1rev:=bat.reverse(A1); * resB:= bat.new(A1); * barrier (h,t):= iterator.new(A1); * $1:= algebra.fetch(A1,h); * $2:= A2; # in case of constant? * ... * cr:= MOD.FCN($1,...,$n); * y:=algebra.fetch(A1rev,h); * bat.insert(resB,y,cr); * redo (h,t):= iterator.next(A1); * end h; * @end verbatim * * The algorithm consists of two phases: phase one deals with * collecting the relevant information, phase two is the actual * code construction. */ static str OPTexpandMultiplex(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { int i = 2, resB, iter = 0, cr; int hvar, tvar; int x, y; str mod, fcn; int *alias; InstrPtr q; int ht, tt; (void) cntxt; (void) stk; ht = getHeadType(getArgType(mb, pci, 0)); if (ht != TYPE_oid) throw(MAL, "optimizer.multiplex", "Target head type is missing"); tt = getTailType(getArgType(mb, pci, 0)); if (tt== TYPE_any) throw(MAL, "optimizer.multiplex", "Target tail type is missing"); if (isAnyExpression(getArgType(mb, pci, 0))) throw(MAL, "optimizer.multiplex", "Target type is missing"); mod = VALget(&getVar(mb, getArg(pci, 1))->value); mod = putName(mod,strlen(mod)); fcn = VALget(&getVar(mb, getArg(pci, 2))->value); fcn = putName(fcn,strlen(fcn)); /* search the iterator bat */ for (i = 3; i < pci->argc; i++) if (isaBatType(getArgType(mb, pci, i))) { iter = getArg(pci, i); if (getHeadType(getVarType(mb,iter)) != TYPE_oid) throw(MAL, "optimizer.multiplex", "Iterator BAT is not OID-headed"); break; } if( i == pci->argc) throw(MAL, "optimizer.multiplex", "Iterator BAT type is missing"); OPTDEBUGmultiplex { mnstr_printf(cntxt->fdout,"#calling the optimize multiplex script routine\n"); printFunction(cntxt->fdout,mb, 0, LIST_MAL_ALL ); mnstr_printf(cntxt->fdout,"#multiplex against operator %d %s\n",iter, getTypeName(getVarType(mb,iter))); printInstruction(cntxt->fdout,mb, 0, pci,LIST_MAL_ALL); } /* * Beware, the operator constant (arg=1) is passed along as well, * because in the end we issue a recursive function call that should * find the actual arguments at the proper place of the callee. */ alias= (int*) GDKmalloc(sizeof(int) * pci->maxarg); if (alias == NULL) return NULL; /* x := bat.reverse(A1); */ x = newTmpVariable(mb, newBatType(getTailType(getVarType(mb,iter)), getHeadType(getVarType(mb,iter)))); q = newFcnCall(mb, batRef, reverseRef); getArg(q, 0) = x; q = pushArgument(mb, q, iter); /* resB := new(refBat) */ q = newFcnCall(mb, batRef, newRef); resB = getArg(q, 0); setVarType(mb, getArg(q, 0), newBatType(ht, tt)); q = pushType(mb, q, ht); q = pushType(mb, q, tt); /* barrier (h,r) := iterator.new(refBat); */ q = newFcnCall(mb, iteratorRef, newRef); q->barrier = BARRIERsymbol; hvar = newTmpVariable(mb, TYPE_any); getArg(q,0) = hvar; tvar = newTmpVariable(mb, TYPE_any); q= pushReturn(mb, q, tvar); (void) pushArgument(mb,q,iter); /* $1:= algebra.fetch(Ai,h) or constant */ alias[i] = tvar; for (i++; i < pci->argc; i++) if (isaBatType(getArgType(mb, pci, i))) { q = newFcnCall(mb, algebraRef, "fetch"); alias[i] = newTmpVariable(mb, getTailType(getArgType(mb, pci, i))); getArg(q, 0) = alias[i]; q= pushArgument(mb, q, getArg(pci, i)); (void) pushArgument(mb, q, hvar); } /* cr:= mod.CMD($1,...,$n); */ q = newFcnCall(mb, mod, fcn); cr = getArg(q, 0) = newTmpVariable(mb, TYPE_any); for (i = 3; i < pci->argc; i++) if (isaBatType(getArgType(mb, pci, i))) { q= pushArgument(mb, q, alias[i]); } else { q = pushArgument(mb, q, getArg(pci, i)); } /* y := algebra.fetch(x,h); */ y = newTmpVariable(mb, getHeadType(getVarType(mb,iter))); q = newFcnCall(mb, algebraRef, "fetch"); getArg(q, 0) = y; q = pushArgument(mb, q, x); q = pushArgument(mb, q, hvar); /* insert(resB,h,cr); not append(resB, cr); the head type (oid) may dynamically change */ q = newFcnCall(mb, batRef, insertRef); q= pushArgument(mb, q, resB); q= pushArgument(mb, q, y); (void) pushArgument(mb, q, cr); /* redo (h,r):= iterator.next(refBat); */ q = newFcnCall(mb, iteratorRef, nextRef); q->barrier = REDOsymbol; getArg(q,0) = hvar; q= pushReturn(mb, q, tvar); (void) pushArgument(mb,q,iter); q = newAssignment(mb); q->barrier = EXITsymbol; getArg(q,0) = hvar; (void) pushReturn(mb, q, tvar); q = newAssignment(mb); getArg(q, 0) = getArg(pci, 0); (void) pushArgument(mb, q, resB); GDKfree(alias); return MAL_SUCCEED; }
static str callbatBETWEEN(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci, int sym) { bat *bid; BAT *bn, *b, *lo = NULL, *hi = NULL, *s = NULL; int tp1, tp2, tp3; (void) cntxt; (void) mb; tp1 = stk->stk[getArg(pci, 1)].vtype; tp2 = stk->stk[getArg(pci, 2)].vtype; tp3 = stk->stk[getArg(pci, 3)].vtype; if (pci->argc == 5) { bat *sid = getArgReference_bat(stk, pci, 4); if (*sid && (s = BATdescriptor(*sid)) == NULL) throw(MAL, "batcalc.between", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); } if (tp1 != TYPE_bat && !isaBatType(tp1)) { if (s) BBPunfix(s->batCacheid); throw(MAL, "batcalc.between", ILLEGAL_ARGUMENT); } bid = getArgReference_bat(stk, pci, 1); b = BATdescriptor(*bid); if (b == NULL) { if (s) BBPunfix(s->batCacheid); throw(MAL, "batcalc.between", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); } if (tp2 == TYPE_bat || isaBatType(tp2)) { bid = getArgReference_bat(stk, pci, 2); lo = BATdescriptor(*bid); if (lo == NULL) { BBPunfix(b->batCacheid); if (s) BBPunfix(s->batCacheid); throw(MAL, "batcalc.between", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); } } if (tp3 == TYPE_bat || isaBatType(tp3)) { bid = getArgReference_bat(stk, pci, 3); hi = BATdescriptor(*bid); if (hi == NULL) { BBPunfix(b->batCacheid); if (lo) BBPunfix(lo->batCacheid); if (s) BBPunfix(s->batCacheid); throw(MAL, "batcalc.between", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); } } if (lo == NULL) { if (hi == NULL) { bn = BATcalcbetweencstcst(b, &stk->stk[getArg(pci, 2)], &stk->stk[getArg(pci, 3)], s, sym); } else { bn = BATcalcbetweencstbat(b, &stk->stk[getArg(pci, 2)], hi, s, sym); } } else { if (hi == NULL) { bn = BATcalcbetweenbatcst(b, lo, &stk->stk[getArg(pci, 3)], s, sym); } else { bn = BATcalcbetween(b, lo, hi, s, sym); } } BBPunfix(b->batCacheid); if (lo) BBPunfix(lo->batCacheid); if (hi) BBPunfix(hi->batCacheid); if (s) BBPunfix(s->batCacheid); if (bn == NULL) { return mythrow(MAL, "batcalc.between", OPERATION_FAILED); } bid = getArgReference_bat(stk, pci, 0); BBPkeepref(*bid = bn->batCacheid); return MAL_SUCCEED; }
static int OPTsql_appendImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { InstrPtr *old = NULL; int i, limit, slimit, actions = 0; (void) pci; /* Tell compilers that we know that we do not */ (void) stk; /* use these function parameters, here. */ /* In general, a MAL optimizer transforms a given MAL program into a * modified one by sequentially walking through the given program * and concurrently creating a new one from scratch by * (1) copying statements as is, modified, or in a different order, * or (2) omitting statements or (3) introducing new statements. */ /* check for logical error: mb must never be NULL */ assert (mb != NULL); /* save the old stage of the MAL block */ old = mb->stmt; limit= mb->stop; slimit = mb->ssize; /* initialize the statement list. Notice, the symbol table remains intact */ if (newMalBlkStmt(mb, mb->ssize) < 0) return 0; /* the plan signature can be copied safely */ pushInstruction(mb, old[0]); /* iterate over the instructions of the input MAL program */ for (i = 1; i < limit; i++) { InstrPtr p = old[i]; /* check for * v3 := sql.append( ..., ..., ..., ..., v0 ); */ if (getModuleId(p) == sqlRef && getFunctionId(p) == appendRef && p->argc > 5 && p->retc == 1 && isaBatType(getArgType(mb, p, 5))) { /* found * v3 := sql.append( ..., ..., ..., ..., v0 ); */ int j = 0, k = 0; InstrPtr q1 = NULL, q2 = NULL; bit found = FALSE; /* check whether next is * v4 := aggr.count(v0); */ if (i+1 < limit) { InstrPtr q = old[i+1]; if (getModuleId(q) == aggrRef && getFunctionId(q) == countRef && q->argc == 2 && q->retc == 1 && getArg(q, 1) == getArg(p, 5)) { /* found * v3 := sql.append( ..., ..., ..., ..., v0 ); * v4 := aggr.count(v0); */ /* issue/execute * v4 := aggr.count(v0); * before * v3 := sql.append( ..., ..., ..., ..., v0 ); */ pushInstruction(mb, q); q1 = q; i++; actions++; /* to keep track if anything has been done */ } } /* look for * v5 := ... v0 ...; */ /* an expensive loop, better would be to remember that v0 * has a different role. A typical method is to keep a * map from variable -> instruction where it was * detected. Then you can check each assignment for use of * v0 */ for (j = i+1; !found && j < limit; j++) for (k = old[j]->retc; !found && k < old[j]->argc; k++) found = (getArg(old[j], k) == getArg(p, 5)); if (found) { /* replace * v3 := sql.append( ..., ..., ..., ..., v0 ); * with * v1 := aggr.count( v0 ); * v2 := algebra.slice( v0, 0, v1 ); * v3 := sql.append( ..., ..., ..., ..., v2 ); */ /* push new v1 := aggr.count( v0 ); unless already available */ if (q1 == NULL) { /* use mal_builder.h primitives * q1 = newStmt(mb, aggrRef,countRef); * setArgType(mb,q1,TYPE_wrd) */ /* it will be added to the block and even my * re-use MAL instructions */ q1 = newInstruction(mb,ASSIGNsymbol); getArg(q1,0) = newTmpVariable(mb, TYPE_wrd); setModuleId(q1, aggrRef); setFunctionId(q1, countRef); q1 = pushArgument(mb, q1, getArg(p, 5)); pushInstruction(mb, q1); } /* push new v2 := algebra.slice( v0, 0, v1 ); */ /* use mal_builder.h primitives * q1 = newStmt(mb, algebraRef,sliceRef); */ q2 = newInstruction(mb,ASSIGNsymbol); getArg(q2,0) = newTmpVariable(mb, TYPE_any); setModuleId(q2, algebraRef); setFunctionId(q2, sliceRef); q2 = pushArgument(mb, q2, getArg(p, 5)); q2 = pushWrd(mb, q2, 0); q2 = pushArgument(mb, q2, getArg(q1, 0)); pushInstruction(mb, q2); /* push modified v3 := sql.append( ..., ..., ..., ..., v2 ); */ getArg(p, 5) = getArg(q2, 0); pushInstruction(mb, p); actions++; continue; } } pushInstruction(mb, p); if (p->token == ENDsymbol) break; } /* We would like to retain everything from the ENDsymbol * up to the end of the plan, because after the ENDsymbol * the remaining optimizer steps are stored. */ for(i++; i<limit; i++) if (old[i]) pushInstruction(mb, old[i]); /* any remaining MAL instruction records are removed */ for(; i<slimit; i++) if (old[i]) freeInstruction(old[i]); GDKfree(old); /* for statistics we return if/how many patches have been made */ DEBUGoptimizers mnstr_printf(cntxt->fdout,"#opt_sql_append: %d statements added\n", actions); return actions; }
str SQLwindow_bound(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { str msg = MAL_SUCCEED; bool preceding; lng first_half; int unit, bound, excl, part_offset = (pci->argc > 6); if ((pci->argc != 6 && pci->argc != 7) || getArgType(mb, pci, part_offset + 2) != TYPE_int || getArgType(mb, pci, part_offset + 3) != TYPE_int || getArgType(mb, pci, part_offset + 4) != TYPE_int) { throw(SQL, "sql.window_bound", SQLSTATE(42000) "Invalid arguments"); } unit = *getArgReference_int(stk, pci, part_offset + 2); bound = *getArgReference_int(stk, pci, part_offset + 3); excl = *getArgReference_int(stk, pci, part_offset + 4); assert(unit >= 0 && unit <= 3); assert(bound >= 0 && bound <= 5); assert(excl >= 0 && excl <= 2); preceding = (bound % 2 == 0); first_half = (bound < 2 || bound == 4); (void)cntxt; if (isaBatType(getArgType(mb, pci, 1))) { bat *res = getArgReference_bat(stk, pci, 0); BAT *b = BATdescriptor(*getArgReference_bat(stk, pci, part_offset + 1)), *p = NULL, *r, *l = NULL; int tp1 = getBatType(getArgType(mb, pci, part_offset + 1)), tp2 = getArgType(mb, pci, part_offset + 5); void* limit = NULL; bool is_negative = false, is_null = false, is_a_bat; gdk_return gdk_code; if (!b) throw(SQL, "sql.window_bound", SQLSTATE(HY005) "Cannot access column descriptor"); if (excl != 0) { BBPunfix(b->batCacheid); throw(SQL, "sql.window_bound", SQLSTATE(42000) "Only EXCLUDE NO OTHERS exclusion is currently implemented"); } is_a_bat = isaBatType(tp2); if(is_a_bat) tp2 = getBatType(tp2); voidresultBAT(r, TYPE_lng, BATcount(b), b, "sql.window_bound"); if(is_a_bat) { //SQL_CURRENT_ROW shall never fall in limit validation l = BATdescriptor(*getArgReference_bat(stk, pci, part_offset + 5)); if (!l) { BBPunfix(b->batCacheid); throw(SQL, "sql.window_bound", SQLSTATE(HY005) "Cannot access column descriptor"); } switch (tp2) { case TYPE_bte: CHECK_NULLS_AND_NEGATIVES_COLUMN(bte) break; case TYPE_sht: CHECK_NULLS_AND_NEGATIVES_COLUMN(sht) break; case TYPE_int: CHECK_NULLS_AND_NEGATIVES_COLUMN(int) break; case TYPE_lng: CHECK_NULLS_AND_NEGATIVES_COLUMN(lng) break; case TYPE_flt: CHECK_NULLS_AND_NEGATIVES_COLUMN(flt) break; case TYPE_dbl: CHECK_NULLS_AND_NEGATIVES_COLUMN(dbl) break; #ifdef HAVE_HGE case TYPE_hge: CHECK_NULLS_AND_NEGATIVES_COLUMN(hge) break; #endif default: { BBPunfix(b->batCacheid); BBPunfix(l->batCacheid); throw(SQL, "sql.window_bound", SQLSTATE(42000) "%s limit not available for %s", "sql.window_bound", ATOMname(tp2)); } } if(is_null || is_negative) { BBPunfix(b->batCacheid); BBPunfix(l->batCacheid); if(is_null) throw(SQL, "sql.window_bound", SQLSTATE(HY005) "All values on %s boundary must be non-null", preceding ? "PRECEDING" : "FOLLOWING"); throw(SQL, "sql.window_bound", SQLSTATE(HY005) "All values on %s boundary must be non-negative", preceding ? "PRECEDING" : "FOLLOWING"); } } else {
int OPTpushselectImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { int i, j, limit, slimit, actions=0, *vars, push_down_delta = 0, nr_topn = 0, nr_likes = 0; InstrPtr p, *old; subselect_t subselects; memset(&subselects, 0, sizeof(subselects)); if( mb->errors) return 0; OPTDEBUGpushselect mnstr_printf(cntxt->fdout,"#Push select optimizer started\n"); (void) stk; (void) pci; vars= (int*) GDKzalloc(sizeof(int)* mb->vtop); if( vars == NULL) return 0; limit = mb->stop; slimit= mb->ssize; old = mb->stmt; /* check for bailout conditions */ for (i = 1; i < limit; i++) { int lastbat; p = old[i]; for (j = 0; j<p->retc; j++) { int res = getArg(p, j); vars[res] = i; } if (getModuleId(p) == algebraRef && (getFunctionId(p) == tintersectRef || getFunctionId(p) == tinterRef || getFunctionId(p) == tdifferenceRef || getFunctionId(p) == tdiffRef)) { GDKfree(vars); return 0; } if (getModuleId(p) == algebraRef && getFunctionId(p) == sliceRef) nr_topn++; if (isLikeOp(p)) nr_likes++; if (getModuleId(p) == sqlRef && getFunctionId(p) == deltaRef) push_down_delta++; if (getModuleId(p) == sqlRef && getFunctionId(p) == tidRef) { /* rewrite equal table ids */ int sname = getArg(p, 2), tname = getArg(p, 3), s; for (s = 0; s < subselects.nr; s++) { InstrPtr q = old[vars[subselects.tid[s]]]; int Qsname = getArg(q, 2), Qtname = getArg(q, 3); if (no_updates(old, vars, getArg(q,1), getArg(p,1)) && ((sname == Qsname && tname == Qtname) || (0 && strcmp(getVarConstant(mb, sname).val.sval, getVarConstant(mb, Qsname).val.sval) == 0 && strcmp(getVarConstant(mb, tname).val.sval, getVarConstant(mb, Qtname).val.sval) == 0))) { clrFunction(p); p->retc = 1; p->argc = 2; getArg(p, 1) = getArg(q, 0); break; } } } lastbat = lastbat_arg(mb, p); if (isSubSelect(p) && p->retc == 1 && /* no cand list */ getArgType(mb, p, lastbat) != newBatType(TYPE_oid, TYPE_oid)) { int i1 = getArg(p, 1), tid = 0; InstrPtr q = old[vars[i1]]; /* find the table ids */ while(!tid) { if (getModuleId(q) == algebraRef && getFunctionId(q) == leftfetchjoinRef) { int i1 = getArg(q, 1); InstrPtr s = old[vars[i1]]; if (getModuleId(s) == sqlRef && getFunctionId(s) == tidRef) tid = getArg(q, 1); if (s->argc == 2 && s->retc == 1) { int i1 = getArg(s, 1); InstrPtr s = old[vars[i1]]; if (getModuleId(s) == sqlRef && getFunctionId(s) == tidRef) tid = getArg(q, 1); } break; } else if (isMapOp(q) && q->argc >= 2 && isaBatType(getArgType(mb, q, 1))) { int i1 = getArg(q, 1); q = old[vars[i1]]; } else if (isMapOp(q) && q->argc >= 3 && isaBatType(getArgType(mb, q, 2))) { int i2 = getArg(q, 2); q = old[vars[i2]]; } else { break; } } if (tid && subselect_add(&subselects, tid, getArg(p, 0)) < 0) { GDKfree(vars); return 0; } } /* left hand side */ if ( (GDKdebug & (1<<15)) && isMatJoinOp(p) && p->retc == 2) { int i1 = getArg(p, 2), tid = 0; InstrPtr q = old[vars[i1]]; /* find the table ids */ while(!tid) { if (getModuleId(q) == algebraRef && getFunctionId(q) == leftfetchjoinRef) { int i1 = getArg(q, 1); InstrPtr s = old[vars[i1]]; if (getModuleId(s) == sqlRef && getFunctionId(s) == tidRef) tid = getArg(q, 1); break; } else if (isMapOp(q) && q->argc >= 2 && isaBatType(getArgType(mb, q, 1))) { int i1 = getArg(q, 1); q = old[vars[i1]]; } else if (isMapOp(q) && q->argc >= 3 && isaBatType(getArgType(mb, q, 2))) { int i2 = getArg(q, 2); q = old[vars[i2]]; } else { break; } } if (tid && subselect_add(&subselects, tid, getArg(p, 0)) < 0) { GDKfree(vars); return 0; } } /* right hand side */ if ( (GDKdebug & (1<<15)) && isMatJoinOp(p) && p->retc == 2) { int i1 = getArg(p, 3), tid = 0; InstrPtr q = old[vars[i1]]; /* find the table ids */ while(!tid) { if (getModuleId(q) == algebraRef && getFunctionId(q) == leftfetchjoinRef) { int i1 = getArg(q, 1); InstrPtr s = old[vars[i1]]; if (getModuleId(s) == sqlRef && getFunctionId(s) == tidRef) tid = getArg(q, 1); break; } else if (isMapOp(q) && q->argc >= 2 && isaBatType(getArgType(mb, q, 1))) { int i1 = getArg(q, 1); q = old[vars[i1]]; } else if (isMapOp(q) && q->argc >= 3 && isaBatType(getArgType(mb, q, 2))) { int i2 = getArg(q, 2); q = old[vars[i2]]; } else { break; } } if (tid && subselect_add(&subselects, tid, getArg(p, 1)) < 0) { GDKfree(vars); return 0; } } } if ((!subselects.nr && !nr_topn && !nr_likes) || newMalBlkStmt(mb, mb->ssize) <0 ) { GDKfree(vars); return 0; } pushInstruction(mb,old[0]); for (i = 1; i < limit; i++) { p = old[i]; /* rewrite batalgebra.like + subselect -> likesubselect */ if (getModuleId(p) == algebraRef && p->retc == 1 && getFunctionId(p) == subselectRef) { int var = getArg(p, 1); InstrPtr q = mb->stmt[vars[var]]; /* BEWARE: the optimizer may not add or remove statements ! */ if (isLikeOp(q)) { /* TODO check if getArg(p, 3) value == TRUE */ InstrPtr r = newInstruction(mb, ASSIGNsymbol); int has_cand = (getArgType(mb, p, 2) == newBatType(TYPE_oid, TYPE_oid)); int a, anti = (getFunctionId(q)[0] == 'n'), ignore_case = (getFunctionId(q)[anti?4:0] == 'i'); setModuleId(r, algebraRef); setFunctionId(r, likesubselectRef); getArg(r,0) = getArg(p,0); r = pushArgument(mb, r, getArg(q, 1)); if (has_cand) r = pushArgument(mb, r, getArg(p, 2)); for(a = 2; a<q->argc; a++) r = pushArgument(mb, r, getArg(q, a)); if (r->argc < (4+has_cand)) r = pushStr(mb, r, ""); /* default esc */ if (r->argc < (5+has_cand)) r = pushBit(mb, r, ignore_case); if (r->argc < (6+has_cand)) r = pushBit(mb, r, anti); freeInstruction(p); p = r; actions++; } } /* inject table ids into subselect * s = subselect(c, C1..) => subselect(c, t, C1..) */ if (isSubSelect(p) && p->retc == 1) { int tid = 0; if ((tid = subselect_find_tids(&subselects, getArg(p, 0))) >= 0) { int lastbat = lastbat_arg(mb, p); if (getArgType(mb, p, lastbat) == TYPE_bat) /* empty candidate list bat_nil */ getArg(p, lastbat) = tid; else p = PushArgument(mb, p, tid, lastbat+1); /* make sure to resolve again */ p->token = ASSIGNsymbol; p->typechk = TYPE_UNKNOWN; p->fcn = NULL; p->blk = NULL; actions++; } } else if ( (GDKdebug & (1<<15)) && isMatJoinOp(p) && p->retc == 2 && !(getFunctionId(p) == joinRef && p->argc > 4) ) { int ltid = 0, rtid = 0, done = 0; int range = 0; if(getFunctionId(p) == joinRef) range = (p->argc >= 4); if ((ltid = subselect_find_tids(&subselects, getArg(p, 0))) >= 0 && (rtid = subselect_find_tids(&subselects, getArg(p, 1))) >= 0) { p = PushArgument(mb, p, ltid, 4+range); p = PushArgument(mb, p, rtid, 5+range); done = 1; } else if ((ltid = subselect_find_tids(&subselects, getArg(p, 0))) >= 0) { p = PushArgument(mb, p, ltid, 4+range); p = PushNil(mb, p, 5+range, TYPE_bat); done = 1; } else if ((rtid = subselect_find_tids(&subselects, getArg(p, 1))) >= 0) { p = PushNil(mb, p, 4+range, TYPE_bat); p = PushArgument(mb, p, rtid, 5+range); done = 1; } if (done) { if(getFunctionId(p) == antijoinRef) p = pushInt(mb, p, JOIN_NE); p = pushBit(mb, p, FALSE); /* do not match nils */ p = pushNil(mb, p, TYPE_lng); /* no estimate */ /* TODO join* -> subjoin* */ if(getFunctionId(p) == joinRef) getFunctionId(p) = subjoinRef; else if(getFunctionId(p) == antijoinRef) getFunctionId(p) = subthetajoinRef; else if(getFunctionId(p) == thetajoinRef) getFunctionId(p) = subthetajoinRef; else if(getFunctionId(p) == bandjoinRef) getFunctionId(p) = subbandjoinRef; /* make sure to resolve again */ p->token = ASSIGNsymbol; p->typechk = TYPE_UNKNOWN; p->fcn = NULL; p->blk = NULL; actions++; } } /* Leftfetchjoins involving rewriten table ids need to be flattend * l = leftfetchjoin(t, c); => l = c; * and * l = leftfetchjoin(s, ntids); => l = s; */ else if (getModuleId(p) == algebraRef && getFunctionId(p) == leftfetchjoinRef) { int var = getArg(p, 1); if (subselect_find_subselect(&subselects, var) > 0) { InstrPtr q = newAssignment(mb); getArg(q, 0) = getArg(p, 0); (void) pushArgument(mb, q, getArg(p, 2)); actions++; freeInstruction(p); continue; } else { /* deletes/updates use table ids */ int var = getArg(p, 2); InstrPtr q = mb->stmt[vars[var]]; /* BEWARE: the optimizer may not add or remove statements ! */ if (q->token == ASSIGNsymbol) { var = getArg(q, 1); q = mb->stmt[vars[var]]; } if (subselect_find_subselect(&subselects, var) > 0) { InstrPtr qq = newAssignment(mb); /* TODO: check result */ getArg(qq, 0) = getArg(p, 0); (void) pushArgument(mb, qq, getArg(p, 1)); actions++; freeInstruction(p); continue; } /* c = sql.delta(b,uid,uval,ins); * l = leftfetchjoin(x, c); * into * l = sql.projectdelta(x,b,uid,uval,ins); */ else if (getModuleId(q) == sqlRef && getFunctionId(q) == deltaRef && q->argc == 5) { q = copyInstruction(q); setFunctionId(q, projectdeltaRef); getArg(q, 0) = getArg(p, 0); q = PushArgument(mb, q, getArg(p, 1), 1); freeInstruction(p); p = q; actions++; } } } pushInstruction(mb,p); } for (; i<limit; i++) if (old[i]) pushInstruction(mb,old[i]); for (; i<slimit; i++) if (old[i]) freeInstruction(old[i]); GDKfree(old); if (!push_down_delta) { GDKfree(vars); return actions; } /* now push selects through delta's */ limit = mb->stop; slimit= mb->ssize; old = mb->stmt; if (newMalBlkStmt(mb, mb->stop+(5*push_down_delta)) <0 ) { mb->stmt = old; GDKfree(vars); return actions; } pushInstruction(mb,old[0]); for (i = 1; i < limit; i++) { int lastbat; p = old[i]; for (j = 0; j<p->retc; j++) { int res = getArg(p, j); vars[res] = i; } /* push subslice under projectdelta */ if (isSlice(p) && p->retc == 1) { int var = getArg(p, 1); InstrPtr q = old[vars[var]]; if (getModuleId(q) == sqlRef && getFunctionId(q) == projectdeltaRef) { InstrPtr r = copyInstruction(p); InstrPtr s = copyInstruction(q); ValRecord cst; /* slice the candidates */ setFunctionId(r, sliceRef); getArg(r, 0) = newTmpVariable(mb, newBatType(TYPE_oid, TYPE_oid)); getArg(r, 1) = getArg(s, 1); cst.vtype = getArgType(mb, r, 2); cst.val.wval = 0; getArg(r, 2) = defConstant(mb, cst.vtype, &cst); /* start from zero */ pushInstruction(mb,r); /* dummy result for the old q, will be removed by deadcode optimizer */ getArg(q, 0) = newTmpVariable(mb, getArgType(mb, q, 0)); getArg(s, 1) = getArg(r, 0); /* use result of subslice */ pushInstruction(mb, s); } } /* c = delta(b, uid, uvl, ins) * s = subselect(c, C1..) * * nc = subselect(b, C1..) * ni = subselect(ins, C1..) * nu = subselect(uvl, C1..) * s = subdelta(nc, uid, nu, ni); * * doesn't handle Xsubselect(x, .. z, C1.. cases) ie multicolumn selects */ lastbat = lastbat_arg(mb, p); if (isSubSelect(p) && p->retc == 1 && lastbat == 2) { int var = getArg(p, 1); InstrPtr q = old[vars[var]]; if (q->token == ASSIGNsymbol) { var = getArg(q, 1); q = old[vars[var]]; } if (getModuleId(q) == sqlRef && getFunctionId(q) == deltaRef) { InstrPtr r = copyInstruction(p); InstrPtr s = copyInstruction(p); InstrPtr t = copyInstruction(p); InstrPtr u = copyInstruction(q); getArg(r, 0) = newTmpVariable(mb, newBatType(TYPE_oid, TYPE_oid)); getArg(r, 1) = getArg(q, 1); /* column */ pushInstruction(mb,r); getArg(s, 0) = newTmpVariable(mb, newBatType(TYPE_oid, TYPE_oid)); getArg(s, 1) = getArg(q, 3); /* updates */ s = ReplaceWithNil(mb, s, 2, TYPE_bat); /* no candidate list */ setArgType(mb, s, 2, newBatType(TYPE_oid,TYPE_oid)); /* make sure to resolve again */ s->token = ASSIGNsymbol; s->typechk = TYPE_UNKNOWN; s->fcn = NULL; s->blk = NULL; pushInstruction(mb,s); getArg(t, 0) = newTmpVariable(mb, newBatType(TYPE_oid, TYPE_oid)); getArg(t, 1) = getArg(q, 4); /* inserts */ pushInstruction(mb,t); setFunctionId(u, subdeltaRef); getArg(u, 0) = getArg(p,0); getArg(u, 1) = getArg(r,0); getArg(u, 2) = getArg(p,2); /* pre-cands */ getArg(u, 3) = getArg(q,2); /* update ids */ getArg(u, 4) = getArg(s,0); u = pushArgument(mb, u, getArg(t,0)); pushInstruction(mb,u); freeInstruction(p); continue; } } pushInstruction(mb,p); } for (; i<limit; i++) if (old[i]) pushInstruction(mb,old[i]); GDKfree(vars); GDKfree(old); return actions; }
int OPTquerylogImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { int i, limit, slimit; InstrPtr p = 0, *old= mb->stmt, q,r; int argc, io, user,nice,sys,idle,iowait,load, arg, start,finish, name; int xtime=0, rtime = 0, tuples=0; InstrPtr defineQuery = NULL; // query log needed? if ( !QLOGisset() ) return 0; (void) pci; (void) stk; /* to fool compilers */ (void) cntxt; /* gather information */ for (i = 1; i < mb->stop; i++) { p = getInstrPtr(mb,i); if ( getModuleId(p) && idcmp(getModuleId(p), "querylog") == 0 && idcmp(getFunctionId(p),"define")==0){ defineQuery= p; getVarConstant(mb,getArg(p,3)).val.lval = GDKusec()-getVarConstant(mb,getArg(p,3)).val.lval ; } } if ( defineQuery == NULL) /* nothing to do */ return 0; limit= mb->stop; slimit= mb->ssize; if ( newMalBlkStmt(mb, mb->ssize) < 0) return 0; pushInstruction(mb, old[0]); /* run the querylog.define operation */ defineQuery = copyInstruction(defineQuery); setFunctionId(defineQuery, insertRef); getArg(defineQuery,0) = newTmpVariable(mb,TYPE_any); defineQuery->token = ASSIGNsymbol; setModuleId(defineQuery,querylogRef); /* collect the initial statistics */ q = newStmt(mb, "clients", "getUsername"); name= getArg(q,0)= newVariable(mb,GDKstrdup("name"),TYPE_str); defineQuery = pushArgument(mb,defineQuery,name); q = newStmt(mb, "mtime", "current_timestamp"); start= getArg(q,0)= newVariable(mb,GDKstrdup("start"),TYPE_timestamp); defineQuery = pushArgument(mb,defineQuery,start); pushInstruction(mb, defineQuery); q = newStmt1(mb, sqlRef, "argRecord"); for ( argc=1; argc < old[0]->argc; argc++) q = pushArgument(mb, q, getArg(old[0],argc)); arg= getArg(q,0)= newVariable(mb,GDKstrdup("args"),TYPE_str); q = newStmt(mb, "alarm", "usec"); xtime = getArg(q,0)= newVariable(mb,GDKstrdup("xtime"),TYPE_lng); user = newVariable(mb,GDKstrdup("user"),TYPE_lng); nice = newVariable(mb,GDKstrdup("nice"),TYPE_lng); sys = newVariable(mb,GDKstrdup("sys"),TYPE_lng); idle = newVariable(mb,GDKstrdup("idle"),TYPE_lng); iowait = newVariable(mb,GDKstrdup("iowait"),TYPE_lng); q = newStmt(mb, "profiler", "cpustats"); q->retc= q->argc =0; q = pushReturn(mb,q,user); q = pushReturn(mb,q,nice); q = pushReturn(mb,q,sys); q = pushReturn(mb,q,idle); q = pushReturn(mb,q,iowait); q = newAssignment(mb); tuples= getArg(q,0) = newVariable(mb,GDKstrdup("tuples"),TYPE_wrd); (void) pushWrd(mb,q,1); for (i = 1; i < limit; i++) { p = old[i]; if (getModuleId(p)==sqlRef && (idcmp(getFunctionId(p),"exportValue")==0 || idcmp(getFunctionId(p),"exportResult")==0 ) ) { q = newStmt(mb, "alarm", "usec"); r = newStmt1(mb, calcRef, "-"); r = pushArgument(mb, r, getArg(q,0)); r = pushArgument(mb, r, xtime); getArg(r,0)=xtime; q = newStmt(mb, "alarm", "usec"); rtime= getArg(q,0)= newVariable(mb,GDKstrdup("rtime"),TYPE_lng); pushInstruction(mb,p); continue; } if ( getModuleId(p) == sqlRef && idcmp(getFunctionId(p),"resultSet")==0 && isaBatType(getVarType(mb,getArg(p,3)))){ q = newStmt(mb, "aggr", "count"); getArg(q,0) = tuples; (void) pushArgument(mb,q, getArg(p,3)); pushInstruction(mb,p); continue; } if ( p->token== ENDsymbol || p->barrier == RETURNsymbol || p->barrier == YIELDsymbol){ if ( rtime == 0){ q = newStmt(mb, "alarm", "usec"); r = newStmt1(mb, calcRef, "-"); r = pushArgument(mb, r, getArg(q,0)); r = pushArgument(mb, r, xtime); getArg(r,0)=xtime; q = newStmt(mb, "alarm", "usec"); rtime= getArg(q,0)= newVariable(mb,GDKstrdup("rtime"),TYPE_lng); } q = newStmt(mb, "alarm", "usec"); r = newStmt1(mb, calcRef, "-"); r = pushArgument(mb, r, getArg(q,0)); r = pushArgument(mb, r, rtime); getArg(r,0)=rtime; /* * Post execution statistics gathering */ q = newStmt(mb, "mtime", "current_timestamp"); finish= getArg(q,0)= newVariable(mb,GDKstrdup("finish"),TYPE_any); q = newStmt(mb, "profiler", "cpuload"); load = newVariable(mb,GDKstrdup("load"),TYPE_int); getArg(q,0)= load; io = newVariable(mb,GDKstrdup("io"),TYPE_int); q= pushReturn(mb,q,io); q = pushArgument(mb,q,user); q = pushArgument(mb,q,nice); q = pushArgument(mb,q,sys); q = pushArgument(mb,q,idle); q = pushArgument(mb,q,iowait); q = newStmt(mb, querylogRef, "call"); q = pushArgument(mb, q, start); q = pushArgument(mb, q, finish); q = pushArgument(mb, q, arg); q = pushArgument(mb, q, tuples); q = pushArgument(mb, q, xtime); q = pushArgument(mb, q, rtime); q = pushArgument(mb, q, load); q = pushArgument(mb, q, io); pushInstruction(mb,p); continue; } pushInstruction(mb,p); if (p->barrier == YIELDsymbol){ /* the factory yield may return */ q = newStmt(mb, "mtime", "current_timestamp"); start= getArg(q,0)= newVariable(mb,GDKstrdup("start"),TYPE_any); q = newStmt1(mb, sqlRef, "argRecord"); for ( argc=1; argc < old[0]->argc; argc++) q = pushArgument(mb, q, getArg(old[0],argc)); arg= getArg(q,0)= newVariable(mb,GDKstrdup("args"),TYPE_str); q = newAssignment(mb); q = pushLng(mb,q,0); q = newAssignment(mb); q = pushWrd(mb,q,0); tuples= getArg(q,0)= newVariable(mb,GDKstrdup("tuples"),TYPE_wrd); newFcnCall(mb,"profiler","setMemoryFlag"); q->argc--; pushWrd(mb,q,1); q = newStmt(mb, "alarm", "usec"); xtime = getArg(q,0)= newVariable(mb,GDKstrdup("xtime"),TYPE_lng); } } for( ; i<slimit; i++) if(old[i]) freeInstruction(old[i]); GDKfree(old); return 1; }
static str CMDbatBINARY0(MalStkPtr stk, InstrPtr pci, BAT *(*batfunc)(BAT *, BAT *, BAT *), BAT *(*batfunc1)(BAT *, const ValRecord *, BAT *), BAT *(*batfunc2)(const ValRecord *, BAT *, BAT *), const char *malfunc) { bat *bid; BAT *bn, *b, *s = NULL; int tp1, tp2; tp1 = stk->stk[getArg(pci, 1)].vtype; tp2 = stk->stk[getArg(pci, 2)].vtype; if (pci->argc == 4) { bat *sid = getArgReference_bat(stk, pci, 3); if (*sid && (s = BATdescriptor(*sid)) == NULL) throw(MAL, malfunc, SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); } if (tp1 == TYPE_bat || isaBatType(tp1)) { BAT *b2 = NULL; bid = getArgReference_bat(stk, pci, 1); b = BATdescriptor(*bid); if (b == NULL) { if (s) BBPunfix(s->batCacheid); throw(MAL, malfunc, SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); } if (tp2 == TYPE_bat || isaBatType(tp2)) { bid = getArgReference_bat(stk, pci, 2); b2 = BATdescriptor(*bid); if (b2 == NULL) { BBPunfix(b->batCacheid); if (s) BBPunfix(s->batCacheid); throw(MAL, malfunc, SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); } } if (b2) { bn = (*batfunc)(b, b2, s); BBPunfix(b2->batCacheid); } else if (batfunc1 == NULL) { BBPunfix(b->batCacheid); if (s) BBPunfix(s->batCacheid); throw(MAL, malfunc, PROGRAM_NYI); } else { bn = (*batfunc1)(b, &stk->stk[getArg(pci, 2)], s); } } else if (batfunc2 == NULL) { throw(MAL, malfunc, PROGRAM_NYI); } else { assert(tp1 != TYPE_bat && !isaBatType(tp1)); assert(tp2 == TYPE_bat || isaBatType(tp2)); bid = getArgReference_bat(stk, pci, 2); b = BATdescriptor(*bid); if (b == NULL) { if (s) BBPunfix(s->batCacheid); throw(MAL, malfunc, SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); } bn = (*batfunc2)(&stk->stk[getArg(pci, 1)], b, s); } BBPunfix(b->batCacheid); if (s) BBPunfix(s->batCacheid); if (bn == NULL) { return mythrow(MAL, malfunc, OPERATION_FAILED); } bid = getArgReference_bat(stk, pci, 0); BBPkeepref(*bid = bn->batCacheid); return MAL_SUCCEED; }
/* * Keeping variables around beyond their end-of-life-span * can be marked with the proper 'keep'. */ int OPTgarbageCollectorImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { int i, j, k, n = 0, limit, vlimit, depth=0, slimit; InstrPtr p, q, *old; int actions = 0; Lifespan span; (void) pci; (void) cntxt; (void) stk; if (varGetProp(mb, getArg(mb->stmt[0], 0), inlineProp) != NULL) return 0; span = setLifespan(mb); if ( span == NULL) return 0; old= mb->stmt; limit = mb->stop; slimit = mb->ssize; vlimit = mb->vtop; if ( newMalBlkStmt(mb,mb->ssize) < 0) { GDKfree(span); return 0; } p = NULL; for (i = 0; i < limit; i++) { p = old[i]; p->gc &= ~GARBAGECONTROL; if ( p->barrier == RETURNsymbol){ pushInstruction(mb, p); continue; } if (blockStart(p) ) depth++; if ( p->token == ENDsymbol) break; pushInstruction(mb, p); n = mb->stop-1; for (j = 0; j < p->argc; j++) { if (getEndLifespan(span,getArg(p,j)) == i && isaBatType(getArgType(mb, p, j)) ){ mb->var[getArg(p,j)]->eolife = n; p->gc |= GARBAGECONTROL; } } if (blockExit(p) ){ /* force garbage collection of all within upper block */ depth--; for (k = 0; k < vlimit; k++) { if (getBeginLifespan(span,k) > 0 && getEndLifespan(span,k) == i && isaBatType(getVarType(mb,k)) && varGetProp(mb, k, keepProp) == NULL){ q= newAssignment(mb); getArg(q,0) = k; setVarUDFtype(mb,k); setVarFixed(mb,k); q= pushNil(mb,q, getVarType(mb,k)); q->gc |= GARBAGECONTROL; mb->var[k]->eolife = mb->stop-1; actions++; } } } } assert(p); assert( p->token == ENDsymbol); pushInstruction(mb, p); for (i++; i < limit; i++) pushInstruction(mb, old[i]); for (; i < slimit; i++) if (old[i]) freeInstruction(old[i]); getInstrPtr(mb,0)->gc |= GARBAGECONTROL; GDKfree(old); OPTDEBUGgarbageCollector{ int k; mnstr_printf(cntxt->fdout, "#Garbage collected BAT variables \n"); for ( k =0; k < vlimit; k++) mnstr_printf(cntxt->fdout,"%10s eolife %3d begin %3d lastupd %3d end %3d\n", getVarName(mb,k), mb->var[k]->eolife, getBeginLifespan(span,k), getLastUpdate(span,k), getEndLifespan(span,k)); mnstr_printf(cntxt->fdout, "End of GCoptimizer\n"); } GDKfree(span); return actions+1; }