str db_password_wrap(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { (void) mb; if (stk->stk[pci->argv[0]].vtype == TYPE_bat) { BAT *b = BATdescriptor(*getArgReference_bat(stk, pci, 1)); if (b == NULL) throw(SQL, "sql.password", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); BAT *bn = COLnew(b->hseqbase, TYPE_str, BATcount(b), TRANSIENT); if (bn == NULL) { BBPunfix(b->batCacheid); throw(SQL, "sql.password", SQLSTATE(HY001) MAL_MALLOC_FAIL); } BATiter bi = bat_iterator(b); BUN p, q; BATloop(b, p, q) { char *hash, *msg; msg = AUTHgetPasswordHash(&hash, cntxt, BUNtvar(bi, p)); if (msg != MAL_SUCCEED) { BBPunfix(b->batCacheid); BBPreclaim(bn); return msg; } if (BUNappend(bn, hash, false) != GDK_SUCCEED) { BBPunfix(b->batCacheid); BBPreclaim(bn); throw(SQL, "sql.password", SQLSTATE(HY001) MAL_MALLOC_FAIL); } GDKfree(hash); }
str CMDcalcavg(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { dbl avg; BUN vals; bat *bid; BAT *b, *s = NULL; gdk_return ret; (void) cntxt; (void) mb; bid = getArgReference_bat(stk, pci, pci->retc + 0); if ((b = BATdescriptor(*bid)) == NULL) throw(MAL, "aggr.avg", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); if (pci->retc == pci->retc + 2) { bat *sid = getArgReference_bat(stk, pci, pci->retc + 1); if (*sid && (s = BATdescriptor(*sid)) == NULL) { BBPunfix(b->batCacheid); throw(MAL, "aggr.avg", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); } } ret = BATcalcavg(b, s, &avg, &vals); BBPunfix(b->batCacheid); if (s) BBPunfix(s->batCacheid); if (ret != GDK_SUCCEED) return mythrow(MAL, "aggr.avg", OPERATION_FAILED); * getArgReference_dbl(stk, pci, 0) = avg; if (pci->retc == 2) * getArgReference_lng(stk, pci, 1) = vals; return MAL_SUCCEED; }
static sql_exp * rel_psm_declare_table(mvc *sql, dnode *n) { sql_rel *rel = NULL, *baset = NULL; dlist *qname = n->next->data.lval; const char *name = qname_table(qname); const char *sname = qname_schema(qname); sql_table *t; if (sname) /* not allowed here */ return sql_error(sql, 02, SQLSTATE(42000) "DECLARE TABLE: qualified name not allowed"); if (frame_find_var(sql, name)) return sql_error(sql, 01, SQLSTATE(42000) "Variable '%s' already declared", name); assert(n->next->next->next->type == type_int); rel = rel_create_table(sql, cur_schema(sql), SQL_DECLARED_TABLE, NULL, name, n->next->next->data.sym, n->next->next->next->data.i_val, NULL, NULL, NULL, false, NULL, 0); if (!rel) return NULL; if(rel->op == op_ddl) { baset = rel; } else if(rel->op == op_insert) { baset = rel->l; } else { return NULL; } if(baset->flag != DDL_CREATE_TABLE) return NULL; t = (sql_table*)((atom*)((sql_exp*)baset->exps->t->data)->l)->data.val.pval; if(!stack_push_table(sql, name, baset, t)) return sql_error(sql, 02, SQLSTATE(HY001) MAL_MALLOC_FAIL); return exp_table(sql->sa, sa_strdup(sql->sa, name), t, sql->frame); }
static str CMDconvertbat(MalStkPtr stk, InstrPtr pci, int tp, int abort_on_error) { bat *bid; BAT *b, *bn, *s = NULL; bid = getArgReference_bat(stk, pci, 1); if ((b = BATdescriptor(*bid)) == NULL) throw(MAL, "batcalc.convert", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); if (pci->argc == 3) { bat *sid = getArgReference_bat(stk, pci, 2); if (*sid && (s = BATdescriptor(*sid)) == NULL) { BBPunfix(b->batCacheid); throw(MAL, "batcalc.convert", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); } } bn = BATconvert(b, s, tp, abort_on_error); BBPunfix(b->batCacheid); if (s) BBPunfix(s->batCacheid); if (bn == NULL) { char buf[20]; snprintf(buf, sizeof(buf), "batcalc.%s", ATOMname(tp)); return mythrow(MAL, buf, OPERATION_FAILED); } bid = getArgReference_bat(stk, pci, 0); BBPkeepref(*bid = bn->batCacheid); return MAL_SUCCEED; }
static str CMDbatUNARY1(MalStkPtr stk, InstrPtr pci, int abort_on_error, BAT *(*batfunc)(BAT *, BAT *, int), const char *malfunc) { bat *bid; BAT *bn, *b, *s = NULL; bid = getArgReference_bat(stk, pci, 1); if ((b = BATdescriptor(*bid)) == NULL) throw(MAL, malfunc, SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); if (pci->argc == 3) { bat *sid = getArgReference_bat(stk, pci, 2); if (*sid && (s = BATdescriptor(*sid)) == NULL) { BBPunfix(b->batCacheid); throw(MAL, malfunc, SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); } } bn = (*batfunc)(b, s, abort_on_error); 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; }
static sql_rel* create_table_from_loader(mvc *sql, dlist *qname, symbol *fcall) { sql_schema *s = NULL; char *sname = qname_schema(qname); char *tname = qname_table(qname); sql_subfunc *loader = NULL; sql_rel* rel = NULL; if (sname && !(s = mvc_bind_schema(sql, sname))) return sql_error(sql, 02, SQLSTATE(3F000) "CREATE TABLE: no such schema '%s'", sname); if (mvc_bind_table(sql, s, tname)) { return sql_error(sql, 02, SQLSTATE(42S01) "CREATE TABLE: name '%s' already in use", tname); } else if (!mvc_schema_privs(sql, s)){ return sql_error(sql, 02, SQLSTATE(42000) "CREATE TABLE: insufficient privileges for user '%s' in schema '%s'", stack_get_string(sql, "current_user"), s->base.name); } rel = rel_loader_function(sql, fcall, new_exp_list(sql->sa), &loader); if (!rel || !loader) { return NULL; } loader->sname = sname ? sa_zalloc(sql->sa, strlen(sname) + 1) : NULL; loader->tname = tname ? sa_zalloc(sql->sa, strlen(tname) + 1) : NULL; if (sname) strcpy(loader->sname, sname); if (tname) strcpy(loader->tname, tname); return rel; }
static list * rel_psm_declare(mvc *sql, dnode *n) { list *l = sa_list(sql->sa); while(n) { /* list of 'identfiers with type' */ dnode *ids = n->data.sym->data.lval->h->data.lval->h; sql_subtype *ctype = &n->data.sym->data.lval->h->next->data.typeval; while(ids) { const char *name = ids->data.sval; sql_exp *r = NULL; /* check if we overwrite a scope local variable declare x; declare x; */ if (frame_find_var(sql, name)) { return sql_error(sql, 01, SQLSTATE(42000) "Variable '%s' already declared", name); } /* variables are put on stack, * TODO make sure on plan/explain etc they only * exist during plan phase */ if(!stack_push_var(sql, name, ctype)) { return sql_error(sql, 02, SQLSTATE(HY001) MAL_MALLOC_FAIL); } r = exp_var(sql->sa, sa_strdup(sql->sa, name), ctype, sql->frame); append(l, r); ids = ids->next; } n = n->next; } return l; }
static sql_rel* rel_drop_all_func(mvc *sql, dlist *qname, int drop_action, int type) { const char *name = qname_table(qname); const char *sname = qname_schema(qname); sql_schema *s = NULL; list * list_func = NULL; char is_aggr = (type == F_AGGR); char is_func = (type != F_PROC); char *F = is_aggr?"AGGREGATE":(is_func?"FUNCTION":"PROCEDURE"); char *f = is_aggr?"aggregate":(is_func?"function":"procedure"); char *KF = type==F_FILT?"FILTER ": type==F_UNION?"UNION ": ""; char *kf = type==F_FILT?"filter ": type==F_UNION?"union ": ""; if (sname && !(s = mvc_bind_schema(sql, sname))) return sql_error(sql, 02, SQLSTATE(3F000) "DROP %s%s: no such schema '%s'", KF, F, sname); if (s == NULL) s = cur_schema(sql); list_func = schema_bind_func(sql, s, name, type); if (!list_func) return sql_error(sql, 02, SQLSTATE(3F000) "DROP ALL %s%s: no such %s%s '%s'", KF, F, kf, f, name); list_destroy(list_func); return rel_drop_function(sql->sa, s->base.name, name, -1, type, drop_action); }
PyObject *PyCodeObject_ParseString(char *string, char **msg) { size_t length = strlen(string); PyObject *code_object, *tuple, *mystr; char *code_copy = GDKmalloc(length * sizeof(char)); char hex[3]; size_t i, j; hex[2] = '\0'; if (code_copy == NULL) { *msg = createException(MAL, "pyapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); return NULL; } // decode hex codes (e.g. \x00) in the string to the actual numeric // representation for (i = 2, j = 0; i < length - 2; i++) { if (string[i] == '\\' && string[i + 1] == '\\') i++; if (string[i] == '\\' && string[i + 1] == 't') { code_copy[j++] = '\t'; i++; } else if (string[i] == '\\' && string[i + 1] == 'n') { code_copy[j++] = '\n'; i++; } else if (string[i] == '\\' && string[i + 1] == 'x') { hex[0] = string[i + 2]; hex[1] = string[i + 3]; code_copy[j++] = (char)strtol(hex, NULL, 16); i += 3; } else { code_copy[j++] = string[i]; } } code_copy[j] = '\0'; tuple = PyTuple_New(1); mystr = PyString_FromStringAndSize( code_copy, j); // use FromStringAndSize because the string is not null-terminated PyTuple_SetItem(tuple, 0, mystr); code_object = PyObject_CallObject(marshal_loads, tuple); Py_DECREF(tuple); GDKfree(code_copy); if (code_object == NULL) { PyErr_Print(); *msg = createException(MAL, "pyapi.eval", SQLSTATE(PY000) "Failed to marshal.loads() encoded object"); return NULL; } *msg = MAL_SUCCEED; return code_object; }
str malAtomDefinition(str name, int tpe) { int i; if (strlen(name) >= IDLENGTH) { throw (SYNTAX, "atomDefinition", "Atom name '%s' too long", name); } if (ATOMindex(name) >= 0) { #ifndef HAVE_EMBEDDED /* we can restart embedded MonetDB, making this an expected error */ throw(TYPE, "atomDefinition", "Redefinition of atom '%s'", name); #endif } if (tpe < 0 || tpe >= GDKatomcnt) { throw(TYPE, "atomDefinition", "Undefined atom inheritance '%s'", name); } if (strlen(name) >= sizeof(BATatoms[0].name)) throw(TYPE, "atomDefinition", "Atom name too long '%s'", name); i = ATOMallocate(name); if (is_int_nil(i)) throw(TYPE,"atomDefinition", SQLSTATE(HY001) MAL_MALLOC_FAIL); /* overload atom ? */ if (tpe) { BATatoms[i] = BATatoms[tpe]; strncpy(BATatoms[i].name, name, sizeof(BATatoms[i].name)); BATatoms[i].name[sizeof(BATatoms[i].name) - 1] = 0; /* make coverity happy */ BATatoms[i].storage = ATOMstorage(tpe); } else { /* cannot overload void atoms */ BATatoms[i].storage = i; BATatoms[i].linear = false; } return MAL_SUCCEED; }
static sql_rel* rel_drop_func(mvc *sql, dlist *qname, dlist *typelist, int drop_action, int type, int if_exists) { const char *name = qname_table(qname); const char *sname = qname_schema(qname); sql_schema *s = NULL; sql_func *func = NULL; char is_aggr = (type == F_AGGR); char is_func = (type != F_PROC); char *F = is_aggr?"AGGREGATE":(is_func?"FUNCTION":"PROCEDURE"); char *KF = type==F_FILT?"FILTER ": type==F_UNION?"UNION ": ""; if (sname && !(s = mvc_bind_schema(sql, sname))) return sql_error(sql, 02, SQLSTATE(3F000) "DROP %s%s: no such schema '%s'", KF, F, sname); if (s == NULL) s = cur_schema(sql); func = resolve_func(sql, s, name, typelist, type, "DROP", if_exists); if (!func && !sname) { s = tmp_schema(sql); func = resolve_func(sql, s, name, typelist, type, "DROP", if_exists); } if (func) return rel_drop_function(sql->sa, s->base.name, name, func->base.id, type, drop_action); else if(if_exists && !sql->session->status) return rel_drop_function(sql->sa, s->base.name, name, -2, type, drop_action); return NULL; }
str ALARMctime(str *res) { time_t t = time(0); char *base; #ifdef HAVE_CTIME_R3 char buf[26]; base = ctime_r(&t, buf, sizeof(buf)); #else #ifdef HAVE_CTIME_R char buf[26]; base = ctime_r(&t, buf); #else base = ctime(&t); #endif #endif if (base == NULL) /* very unlikely to happen... */ throw(MAL, "alarm.ctime", "failed to format time"); base[24] = 0; /* squash final newline */ *res = GDKstrdup(base); if (*res == NULL) throw(MAL, "alarm.ctime", SQLSTATE(HY001) MAL_MALLOC_FAIL); return MAL_SUCCEED; }
static list * result_type(mvc *sql, symbol *res) { if (res->token == SQL_TYPE) { sql_subtype *st = &res->data.lval->h->data.typeval; sql_arg *a = sql_create_arg(sql->sa, "result", st, ARG_OUT); return list_append(sa_list(sql->sa), a); } else if (res->token == SQL_TABLE) { sql_arg *a; dnode *n = res->data.lval->h; list *types = sa_list(sql->sa); for(;n; n = n->next->next) { sql_subtype *ct = &n->next->data.typeval; if (list_find(types, n->data.sval, &arg_cmp) != NULL) return sql_error(sql, ERR_AMBIGUOUS, SQLSTATE(42000) "CREATE FUNC: identifier '%s' ambiguous", n->data.sval); a = sql_create_arg(sql->sa, n->data.sval, ct, ARG_OUT); list_append(types, a); } return types; } return NULL; }
static sql_rel * drop_trigger(mvc *sql, dlist *qname, int if_exists) { const char *sname = qname_schema(qname); const char *tname = qname_table(qname); sql_schema *ss = cur_schema(sql); if (!sname) sname = ss->base.name; if (sname && !(ss = mvc_bind_schema(sql, sname))) return sql_error(sql, 02, SQLSTATE(3F000) "DROP TRIGGER: no such schema '%s'", sname); if (!mvc_schema_privs(sql, ss)) return sql_error(sql, 02, SQLSTATE(3F000) "DROP TRIGGER: access denied for %s to schema ;'%s'", stack_get_string(sql, "current_user"), ss->base.name); return rel_drop_trigger(sql, ss->base.name, tname, if_exists); }
str batnil_2_timestamp(bat *res, const bat *bid) { BAT *b, *dst; BUN p, q; if ((b = BATdescriptor(*bid)) == NULL) { throw(SQL, "batcalc.nil_2_timestamp", SQLSTATE(HY005) "Cannot access column descriptor"); } dst = COLnew(b->hseqbase, TYPE_timestamp, BATcount(b), TRANSIENT); if (dst == NULL) { BBPunfix(b->batCacheid); throw(SQL, "sql.2_timestamp", SQLSTATE(HY001) MAL_MALLOC_FAIL); } BATloop(b, p, q) { timestamp r = *timestamp_nil; if (BUNappend(dst, &r, FALSE) != GDK_SUCCEED) { BBPunfix(b->batCacheid); BBPreclaim(dst); throw(SQL, "sql.timestamp", SQLSTATE(HY001) MAL_MALLOC_FAIL); } }
static sql_exp* rel_psm_call(mvc * sql, symbol *se) { sql_subtype *t; sql_exp *res = NULL; exp_kind ek = {type_value, card_none, FALSE}; sql_rel *rel = NULL; res = rel_value_exp(sql, &rel, se, sql_sel, ek); if (!res || rel || ((t=exp_subtype(res)) && t->type)) /* only procedures */ return sql_error(sql, 01, SQLSTATE(42000) "Function calls are ignored"); return res; }
str CMDgetTrace(bat *res, str *ev) { BAT *bn; (void) res; /* fool compiler */ bn = getTrace(*ev); if (bn) { BBPkeepref(*res = bn->batCacheid); return MAL_SUCCEED; } throw(MAL, "getTrace", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING "%s",*ev); }
str GROUPmulticolumngroup(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { bat *grp = getArgReference_bat(stk, pci, 0); bat *ext = getArgReference_bat(stk, pci, 1); bat *hist = getArgReference_bat(stk, pci, 2); int i, j; bat oldgrp, oldext, oldhist; str msg = MAL_SUCCEED; BAT *b; BUN count = 0; AGGRtask *aggr; aggr = GROUPcollect(cntxt, mb, stk, pci); if( aggr == NULL) throw(MAL,"group.multicolumn", SQLSTATE(HY001) MAL_MALLOC_FAIL); GROUPcollectSort(aggr, 0, aggr->last); /* (grp,ext,hist) := group.group(..) */ /* use the old pattern to perform the incremental grouping */ *grp = 0; *ext = 0; *hist = 0; msg = GRPgroup1(grp, ext, hist, &aggr->bid[0]); i = 1; if (msg == MAL_SUCCEED && aggr->last > 1) do { /* early break when there are as many groups as entries */ b = BATdescriptor(*hist); if (b) { j = BATcount(b) == count; BBPunfix(*hist); if (j) break; } /* (grp,ext,hist) := group.subgroup(arg,grp,ext,hist) */ oldgrp = *grp; oldext = *ext; oldhist = *hist; *grp = 0; *ext = 0; *hist = 0; msg = GRPsubgroup5(grp, ext, hist, &aggr->bid[i], NULL, &oldgrp, &oldext, &oldhist); BBPrelease(oldgrp); BBPrelease(oldext); BBPrelease(oldhist); } while (msg == MAL_SUCCEED && ++i < aggr->last); GROUPdelete(aggr); return msg; }
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; }
str yieldFactory(MalBlkPtr mb, InstrPtr p, int pc) { Plant pl; int i; i = yieldResult(mb, p, pc); if (i>=0) { pl = plants+i; pl->pc = pc + 1; pl->client = NULL; pl->caller = NULL; pl->pci = NULL; pl->env = NULL; return MAL_SUCCEED; } throw(MAL, "factory.yield", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING); }
static list * rel_select_into( mvc *sql, symbol *sq, exp_kind ek) { SelectNode *sn = (SelectNode*)sq; dlist *into = sn->into; node *m; dnode *n; sql_rel *r; list *nl = NULL; /* SELECT ... INTO var_list */ sn->into = NULL; r = rel_subquery(sql, NULL, sq, ek, APPLY_JOIN); if (!r) return NULL; nl = sa_list(sql->sa); append(nl, exp_rel(sql, r)); for (m = r->exps->h, n = into->h; m && n; m = m->next, n = n->next) { sql_subtype *tpe = NULL; char *nme = n->data.sval; sql_exp *v = m->data; int level; if (!stack_find_var(sql, nme)) return sql_error(sql, 02, SQLSTATE(42000) "SELECT INTO: variable '%s' unknown", nme); /* dynamic check for single values */ if (v->card > CARD_AGGR) { sql_subaggr *zero_or_one = sql_bind_aggr(sql->sa, sql->session->schema, "zero_or_one", exp_subtype(v)); assert(zero_or_one); v = exp_aggr1(sql->sa, v, zero_or_one, 0, 0, CARD_ATOM, 0); } tpe = stack_find_type(sql, nme); level = stack_find_frame(sql, nme); if (!v || !(v = rel_check_type(sql, tpe, v, type_equal))) return NULL; v = exp_set(sql->sa, nme, v, level); list_append(nl, v); } return nl; }
/** * Returns an exception string for the given type of exception, function * and additional formatting parameters. This function will crash the * system or return bogus when the malexception enum is not aligned with * the exceptionNames array. */ str createException(enum malexception type, const char *fcn, const char *format, ...) { va_list ap; str ret; if (GDKerrbuf && (ret = strstr(format, MAL_MALLOC_FAIL)) != NULL && ret[strlen(MAL_MALLOC_FAIL)] != ':' && (strncmp(GDKerrbuf, "GDKmalloc", 9) == 0 || strncmp(GDKerrbuf, "GDKrealloc", 10) == 0 || strncmp(GDKerrbuf, "GDKzalloc", 9) == 0 || strncmp(GDKerrbuf, "GDKstrdup", 9) == 0 || strncmp(GDKerrbuf, "allocating too much virtual address space", 41) == 0)) { /* override errors when the underlying error is memory * exhaustion, but include whatever it is that the GDK level * reported */ ret = createException(type, fcn, SQLSTATE(HY001) MAL_MALLOC_FAIL ": %s", GDKerrbuf); GDKclrerr(); return ret; } if (strcmp(format, GDK_EXCEPTION) == 0 && GDKerrbuf[0]) { /* for GDK errors, report the underlying error */ char *p = GDKerrbuf; if (strncmp(p, GDKERROR, strlen(GDKERROR)) == 0) p += strlen(GDKERROR); if (strlen(p) > 6 && p[5] == '!') ret = createException(type, fcn, "%s", p); else ret = createException(type, fcn, "GDK reported error: %s", p); GDKclrerr(); return ret; } va_start(ap, format); ret = createExceptionInternal(type, fcn, format, ap); va_end(ap); GDKclrerr(); return ret; }
str QOToptimize(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { str modnme; str fcnnme; Symbol s; (void) stk; if (stk != 0) { modnme = *getArgReference_str(stk, pci, 1); fcnnme = *getArgReference_str(stk, pci, 2); } else { modnme = getArgDefault(mb, pci, 1); fcnnme = getArgDefault(mb, pci, 2); } s = findSymbol(cntxt->usermodule, putName(modnme), fcnnme); if (s == NULL) throw(MAL, "optimizer.optimize", SQLSTATE(HY002) SEMANTIC_OPERATION_MISSING); removeInstruction(mb, pci); addtoMalBlkHistory(s->def); return optimizeMALBlock(cntxt, s->def); }
str str_2_timestamp(timestamp *res, const str *val) { ptr p = NULL; size_t len = 0; ssize_t e; char buf[BUFSIZ]; e = ATOMfromstr(TYPE_timestamp, &p, &len, *val); if (e < 0 || !p || (ATOMcmp(TYPE_timestamp, p, ATOMnilptr(TYPE_timestamp)) == 0 && ATOMcmp(TYPE_str, *val, ATOMnilptr(TYPE_str)) != 0)) { if (p) GDKfree(p); snprintf(buf, BUFSIZ, "Conversion of string '%s' failed", *val? *val:""); throw(SQL, "timestamp", SQLSTATE(42000) "%s", buf); } *res = *(timestamp *) p; if (!ATOMextern(TYPE_timestamp)) { if (p) GDKfree(p); } return MAL_SUCCEED; }
static str monet5_create_user(ptr _mvc, str user, str passwd, char enc, str fullname, sqlid schema_id, sqlid grantorid) { mvc *m = (mvc *) _mvc; oid uid = 0; bat bid = 0; str ret; sqlid user_id; str pwd; sql_schema *s = find_sql_schema(m->session->tr, "sys"); sql_table *db_user_info, *auths; Client c = MCgetClient(m->clientid); if (!enc) { pwd = mcrypt_BackendSum(passwd, strlen(passwd)); if (pwd == NULL) { BBPunfix(bid); throw(MAL, "sql.create_user", SQLSTATE(42000) "Crypt backend hash not found"); } } else { pwd = passwd; } /* add the user to the M5 authorisation administration */ ret = AUTHaddUser(&uid, c, user, pwd); if (!enc) free(pwd); if (ret != MAL_SUCCEED) return ret; user_id = store_next_oid(); db_user_info = find_sql_table(s, "db_user_info"); auths = find_sql_table(s, "auths"); table_funcs.table_insert(m->session->tr, db_user_info, user, fullname, &schema_id); table_funcs.table_insert(m->session->tr, auths, &user_id, user, &grantorid); return NULL; }
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; }
/* * The shortcut operator for factory calls assumes that the user is * not interested in the results produced. */ str callFactory(Client cntxt, MalBlkPtr mb, ValPtr argv[], char flag){ Plant pl; InstrPtr psig = getInstrPtr(mb, 0); int i; ValPtr lhs,rhs; MalStkPtr stk; str ret; i= findPlant(mb); if( i< 0) { /* first call? prepare the factory */ pl = newPlant(mb); if (pl == NULL) throw(MAL, "factory.call", SQLSTATE(HY001) MAL_MALLOC_FAIL); /* remember context, which does not exist. */ pl->client = cntxt; pl->caller = 0; pl->env = 0; pl->pci = 0; pl->inuse = 1; stk = pl->stk; /* initialize the stack */ stk->stktop= mb->vtop; stk->stksize= mb->vsize; stk->blk= mb; stk->up = 0; stk->cmd= flag; /* initialize the stack */ for(i= psig->argc; i< mb->vtop; i++) if( isVarConstant(mb,i) > 0 ){ lhs = &stk->stk[i]; rhs = &getVarConstant(mb,i); if (VALcopy(lhs,rhs) == NULL) throw(MAL, "factory.call", SQLSTATE(HY001) MAL_MALLOC_FAIL); } else { lhs = &stk->stk[i]; lhs->vtype = getVarGDKType(mb,i); } pl->stk= stk; } else { pl= plants+i; /* * When you re-enter the factory the old arguments should be * released to make room for the new ones. */ for (i = psig->retc; i < psig->argc; i++) { lhs = &pl->stk->stk[psig->argv[i]]; if( lhs->vtype == TYPE_bat ) BBPrelease(lhs->val.bval); } } /* copy the calling arguments onto the stack of the factory */ i = psig->retc; for (i = psig->retc; i < psig->argc; i++) { lhs = &pl->stk->stk[psig->argv[i]]; if (VALcopy(lhs, argv[i]) == NULL) throw(MAL, "factory.call", SQLSTATE(HY001) MAL_MALLOC_FAIL); if( lhs->vtype == TYPE_bat ) BBPretain(lhs->val.bval); } ret= reenterMAL(cntxt, mb, pl->pc, -1, pl->stk); /* garbage collect the string arguments, these positions will simply be overwritten the next time. for (i = psig->retc; i < psig->argc; i++) garbageElement(lhs = &pl->stk->stk[psig->argv[i]]); */ return ret; }
str runFactory(Client cntxt, MalBlkPtr mb, MalBlkPtr mbcaller, MalStkPtr stk, InstrPtr pci) { Plant pl=0; int firstcall= TRUE, i, k; InstrPtr psig = getInstrPtr(mb, 0); ValPtr lhs, rhs; char cmd; str msg; #ifdef DEBUG_MAL_FACTORY fprintf(stderr, "#factoryMgr called\n"); #endif /* the lookup can be largely avoided by handing out the index upon factory definition. todo Alternative is to move them to the front */ for(i=0; i< lastPlant; i++) if( plants[i].factory == mb){ if(i > 0 && i< lastPlant ){ PlantRecord prec= plants[i-1]; plants[i-1] = plants[i]; plants[i]= prec; i--; } pl= plants+i; firstcall= FALSE; break; } if (pl == 0) { /* compress the plant table*/ for(k=i=0;i<=lastPlant; i++) if( plants[i].inuse) plants[k++]= plants[i]; lastPlant = k; /* initialize a new plant using the owner policy */ pl = newPlant(mb); if (pl == NULL) throw(MAL, "factory.new", SQLSTATE(HY001) MAL_MALLOC_FAIL); } /* * We have found a factory to process the request. * Let's call it as a synchronous action, without concern on parallelism. */ /* remember context */ pl->client = cntxt; pl->caller = mbcaller; pl->env = stk; pl->pci = pci; pl->inuse = 1; /* inherit debugging */ cmd = stk->cmd; if ( pl->stk == NULL) throw(MAL, "factory.new", "internal error, stack frame missing"); /* copy the calling arguments onto the stack of the factory */ i = psig->retc; for (k = pci->retc; i < pci->argc; i++, k++) { lhs = &pl->stk->stk[psig->argv[k]]; /* variable arguments ? */ if (k == psig->argc - 1) k--; rhs = &pl->env->stk[getArg(pci, i)]; if (VALcopy(lhs, rhs) == NULL) throw(MAL, "factory.call", SQLSTATE(HY001) MAL_MALLOC_FAIL); if( lhs->vtype == TYPE_bat ) BBPretain(lhs->val.bval); } if (mb->errors) throw(MAL, "factory.call", PROGRAM_GENERAL); if (firstcall ){ /* initialize the stack */ for(i= psig->argc; i< mb->vtop; i++) { lhs = &pl->stk->stk[i]; if( isVarConstant(mb,i) > 0 ){ if( !isVarDisabled(mb,i)){ rhs = &getVarConstant(mb,i); if (VALcopy(lhs,rhs) == NULL) throw(MAL, "factory.call", SQLSTATE(HY001) MAL_MALLOC_FAIL); } } else{ lhs->vtype = getVarGDKType(mb,i); lhs->val.pval = 0; lhs->len = 0; } } pl->stk->stkbot= mb->vtop; /* stack already initialized */ msg = runMAL(cntxt, mb, 0, pl->stk); } else { msg = reenterMAL(cntxt, mb, pl->pc, -1, pl->stk); } /* propagate change in debugging status */ if (cmd && pl->stk && pl->stk->cmd != cmd && cmd != 'x') for (; stk; stk = stk->up) stk->cmd = pl->stk->cmd; return msg; }
str OPTevaluateImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci) { InstrPtr p; int i, k, limit, *alias = 0, barrier; MalStkPtr env = NULL; int profiler; int debugstate = cntxt->itrace, actions = 0, constantblock = 0; int *assigned = 0, use; char buf[256]; lng usec = GDKusec(); str msg = MAL_SUCCEED; (void)stk; (void)pci; if ( mb->inlineProp ) return MAL_SUCCEED; cntxt->itrace = 0; #ifdef DEBUG_OPT_EVALUATE fprintf(stderr, "Constant expression optimizer started\n"); #endif assigned = (int*) GDKzalloc(sizeof(int) * mb->vtop); if (assigned == NULL) throw(MAL,"optimzier.evaluate", SQLSTATE(HY001) MAL_MALLOC_FAIL); alias = (int*)GDKzalloc(mb->vsize * sizeof(int) * 2); /* we introduce more */ if (alias == NULL){ GDKfree(assigned); throw(MAL,"optimzier.evaluate", SQLSTATE(HY001) MAL_MALLOC_FAIL); } // arguments are implicitly assigned by context p = getInstrPtr(mb, 0); for ( k =p->retc; k < p->argc; k++) assigned[getArg(p,k)]++; limit = mb->stop; for (i = 1; i < limit; i++) { p = getInstrPtr(mb, i); // The double count emerging from a barrier exit is ignored. if (! blockExit(p) || (blockExit(p) && p->retc != p->argc)) for ( k =0; k < p->retc; k++) if ( p->retc != p->argc || p->token != ASSIGNsymbol ) assigned[getArg(p,k)]++; } for (i = 1; i < limit && cntxt->mode != FINISHCLIENT; i++) { p = getInstrPtr(mb, i); // to avoid management of duplicate assignments over multiple blocks // we limit ourselves to evaluation of the first assignment only. use = assigned[getArg(p,0)] == 1 && !(p->argc == p->retc && blockExit(p)); for (k = p->retc; k < p->argc; k++) if (alias[getArg(p, k)]) getArg(p, k) = alias[getArg(p, k)]; #ifdef DEBUG_OPT_EVALUATE fprintInstruction(stderr , mb, 0, p, LIST_MAL_ALL); #endif /* be aware that you only assign once to a variable */ if (use && p->retc == 1 && OPTallConstant(cntxt, mb, p) && !isUnsafeFunction(p)) { barrier = p->barrier; p->barrier = 0; profiler = malProfileMode; /* we don't trace it */ malProfileMode = 0; if ( env == NULL) { env = prepareMALstack(mb, 2 * mb->vsize); if (!env) { msg = createException(MAL,"optimizer.evaluate", SQLSTATE(HY001) MAL_MALLOC_FAIL); goto wrapup; } env->keepAlive = TRUE; } msg = reenterMAL(cntxt, mb, i, i + 1, env); malProfileMode= profiler; p->barrier = barrier; #ifdef DEBUG_OPT_EVALUATE fprintf(stderr, "#retc var %s\n", getVarName(mb, getArg(p, 0))); fprintf(stderr, "#result:%s\n", msg == MAL_SUCCEED ? "ok" : msg); #endif if (msg == MAL_SUCCEED) { int nvar; ValRecord cst; actions++; cst.vtype = 0; VALcopy(&cst, &env->stk[getArg(p, 0)]); /* You may not overwrite constants. They may be used by * other instructions */ nvar = getArg(p, 1) = defConstant(mb, getArgType(mb, p, 0), &cst); if (nvar >= env->stktop) { VALcopy(&env->stk[getArg(p, 1)], &getVarConstant(mb, getArg(p, 1))); env->stktop = getArg(p, 1) + 1; } alias[getArg(p, 0)] = getArg(p, 1); p->argc = 2; p->token = ASSIGNsymbol; clrFunction(p); p->barrier = barrier; /* freeze the type */ setVarFixed(mb,getArg(p,1)); setVarUDFtype(mb,getArg(p,1)); #ifdef DEBUG_OPT_EVALUATE {str tpename; fprintf(stderr, "Evaluated new constant=%d -> %d:%s\n", getArg(p, 0), getArg(p, 1), tpename = getTypeName(getArgType(mb, p, 1))); GDKfree(tpename); } #endif } else { /* if there is an error, we should postpone message handling, as the actual error (eg. division by zero ) may not happen) */ #ifdef DEBUG_OPT_EVALUATE fprintf(stderr, "Evaluated %s\n", msg); #endif freeException(msg); msg= MAL_SUCCEED; mb->errors = 0; } } constantblock += blockStart(p) && OPTallConstant(cntxt, mb, p); /* default */ } // produces errors in SQL when enabled if ( constantblock) msg = OPTremoveUnusedBlocks(cntxt, mb); cntxt->itrace = debugstate; /* Defense line against incorrect plans */ /* Plan is unaffected */ 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","evaluate",actions,usec); newComment(mb,buf); if( actions >= 0) addtoMalBlkHistory(mb); wrapup: if ( env) freeStack(env); if(assigned) GDKfree(assigned); if(alias) GDKfree(alias); return msg; }
char *FormatCode(char *code, char **args, size_t argcount, size_t tabwidth, PyObject **code_object, char **msg, char **additional_args, size_t additional_argcount) { // Format the python code by fixing the indentation levels // We do two passes, first we get the length of the resulting formatted code // and then we actually create the resulting code size_t i = 0, j = 0, k = 0; size_t length = strlen(code); size_t size = 0; size_t spaces_per_level = 2; size_t code_location = 0; char *newcode = NULL; size_t indentation_count = 0; size_t max_indentation = 100; // This keeps track of the different indentation levels // indentation_levels is a sorted array with how many spaces of indentation // that specific array has // so indentation_levels[0] = 4 means that the first level (level 0) has 4 // spaces in the source code // after this array is constructed we can count the amount of spaces before // a statement and look in this // array to immediately find the indentation level of the statement size_t *indentation_levels; // statements_per_level keeps track of how many statements are at the // specified indentation level // this is needed to compute the size of the resulting formatted code // for every indentation level i, we add statements_per_level[i] * (i + 1) * // spaces_per_level spaces size_t *statements_per_level; size_t initial_spaces = 0; size_t statement_size = 0; bool seen_statement = false; bool multiline_statement = false; int multiline_quotes = 0; char base_start[] = "def pyfun("; char base_end[] = "):\n"; *msg = NULL; #ifndef IS_PY3K if (code[1] == '@') { *code_object = PyCodeObject_ParseString(code, msg); return NULL; } #else (void)code_object; #endif indentation_levels = (size_t *)GDKzalloc(max_indentation * sizeof(size_t)); statements_per_level = (size_t *)GDKzalloc(max_indentation * sizeof(size_t)); if (indentation_levels == NULL || statements_per_level == NULL) { *msg = createException(MAL, "pyapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); goto finally; } // Base function definition size // For every argument, add a comma, and add another entry for the '\0' size += strlen(base_start) + strlen(base_end) + argcount + 1; for (i = 0; i < argcount; i++) { if (args[i] != NULL) { size += strlen(args[i]) + 1; } } // Additional parameters for (i = 0; i < additional_argcount; i++) size += strlen(additional_args[i]) + 1; // First remove the "{" at the start and the "};" at the end of the // function, this is added when we have a function created through SQL and // python doesn't like them // We need to be careful to only remove ones at the start/end, otherwise we // might invalidate some otherwise valid python code containing them for (i = length - 1, j = 0; i > 0; i--) { if (code[i] != '\n' && code[i] != ' ' && code[i] != '\t' && code[i] != ';' && code[i] != '}') break; if (j == 0) { if (code[i] == ';') { code[i] = ' '; j = 1; } } else if (j == 1) { if (code[i] == '}') { code[i] = ' '; break; } } } for (i = 0; i < length; i++) { if (code[i] != '\n' && code[i] != ' ' && code[i] != '\t' && code[i] != '{') break; if (code[i] == '{') { code[i] = ' '; } } // We indent using spaces, four spaces per level // We also erase empty lines for (i = 0; i < length; i++) { // handle multiline strings (strings that start with """) if (code[i] == '\"') { if (!multiline_statement) { multiline_quotes++; multiline_statement = multiline_quotes == 3; } else { multiline_quotes--; multiline_statement = multiline_quotes != 0; } } else { multiline_quotes = multiline_statement ? 3 : 0; } if (!seen_statement) { // We have not seen a statement on this line yet if (code[i] == '\n') { // Empty line, skip to the next one initial_spaces = 0; } else if (code[i] == ' ') { initial_spaces++; } else if (code[i] == '\t') { initial_spaces += tabwidth; } else { // Statement starts here seen_statement = true; } } if (seen_statement) { // We have seen a statement on this line, check the indentation // level statement_size++; if (code[i] == '\n' || i == length - 1) { // Statement ends here bool placed = false; size_t level = 0; if (multiline_statement) { // if we are in a multiline statement, we don't want to mess // with the indentation size += statement_size; initial_spaces = 0; statement_size = 0; continue; } // First put the indentation in the indentation table if (indentation_count >= max_indentation) { // If there is no room in the indentation arrays we will // extend them // This probably will never happen unless in really extreme // code (or if max_indentation is set very low) size_t *new_indentation = GDKzalloc(2 * max_indentation * sizeof(size_t)); size_t *new_statements_per_level; if (new_indentation == NULL) { *msg = createException(MAL, "pyapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); goto finally; } new_statements_per_level = GDKzalloc(2 * max_indentation * sizeof(size_t)); if (new_statements_per_level == NULL) { *msg = createException(MAL, "pyapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); goto finally; } for (i = 0; i < max_indentation; i++) { new_indentation[i] = indentation_levels[i]; new_statements_per_level[i] = statements_per_level[i]; } GDKfree(indentation_levels); GDKfree(statements_per_level); indentation_levels = new_indentation; statements_per_level = new_statements_per_level; max_indentation *= 2; } for (j = 0; j < indentation_count; j++) { if (initial_spaces == indentation_levels[j]) { // The exact space count is already in the array, so we // can stop level = j; placed = true; break; } if (initial_spaces < indentation_levels[j]) { // The indentation level is smaller than this level (but // bigger than the previous level) // So the indentation level belongs here, so we move // every level past this one upward one level // and put the indentation level here for (k = indentation_count; k > j; k--) { indentation_levels[k] = indentation_levels[k - 1]; statements_per_level[k] = statements_per_level[k - 1]; } indentation_count++; statements_per_level[j] = 0; indentation_levels[j] = initial_spaces; level = j; placed = true; break; } } if (!placed) { // The space count is the biggest we have seen, so we add it // to the end of the array level = indentation_count; indentation_levels[indentation_count++] = initial_spaces; } statements_per_level[level]++; size += statement_size; seen_statement = false; initial_spaces = 0; statement_size = 0; } } } // Add the amount of spaces we will add to the size for (i = 0; i < indentation_count; i++) { size += (i + 1) * spaces_per_level * statements_per_level[i]; } // Allocate space for the function newcode = GDKzalloc(size); if (newcode == NULL) { *msg = createException(MAL, "pyapi.eval", SQLSTATE(HY001) MAL_MALLOC_FAIL); goto finally; } initial_spaces = 0; seen_statement = false; // First print in the function definition and arguments for (i = 0; i < strlen(base_start); i++) { newcode[code_location++] = base_start[i]; } // Add user-defined parameters for (i = 0; i < argcount; i++) { if (args[i] != NULL) { for (j = 0; j < strlen(args[i]); j++) { newcode[code_location++] = args[i][j]; } if (i != argcount - 1 || additional_argcount > 0) { newcode[code_location++] = ','; } } } // Add additional parameters for (i = 0; i < additional_argcount; i++) { if (additional_args[i] != NULL) { for (j = 0; j < strlen(additional_args[i]); j++) { newcode[code_location++] = additional_args[i][j]; } if (i != additional_argcount - 1) { newcode[code_location++] = ','; } } } for (i = 0; i < strlen(base_end); i++) { newcode[code_location++] = base_end[i]; } // Now the second pass, actually construct the code for (i = 0; i < length; i++) { // handle multiline statements if (code[i] == '\"') { if (!multiline_statement) { multiline_quotes++; multiline_statement = multiline_quotes == 3; } else { multiline_quotes--; multiline_statement = multiline_quotes != 0; } } else { multiline_quotes = multiline_statement ? 3 : 0; } if (!seen_statement) { if (multiline_statement) seen_statement = true; // if we are in a multiline string, we // simply want to copy everything // (including indentation) // We have not seen a statement on this line yet else if (code[i] == '\n') { // Empty line, skip to the next one initial_spaces = 0; } else if (code[i] == ' ') { initial_spaces++; } else if (code[i] == '\t') { initial_spaces += tabwidth; } else { // Look through the indentation_levels array to find the level // of the statement // from the amount of initial spaces bool placed = false; size_t level = 0; // Statement starts here seen_statement = true; for (j = 0; j < indentation_count; j++) { if (initial_spaces == indentation_levels[j]) { level = j; placed = true; break; } } if (!placed) { // This should never happen, because it means the initial // spaces was not present in the array // When we just did exactly the same loop over the array, we // should have encountered this statement // This means that something happened to either the // indentation_levels array or something happened to the // code *msg = createException(MAL, "pyapi.eval", SQLSTATE(PY000) "If you see this error something " "went wrong in the code. Sorry."); goto finally; } for (j = 0; j < (level + 1) * spaces_per_level; j++) { // Add spaces to the code newcode[code_location++] = ' '; } } } if (seen_statement) { // We have seen a statement on this line, copy it newcode[code_location++] = code[i]; if (code[i] == '\n') { // The statement has ended, move on to the next line seen_statement = false; initial_spaces = 0; statement_size = 0; } } } newcode[code_location] = '\0'; if (code_location >= size) { // Something went wrong with our size computation, this also should // never happen *msg = createException(MAL, "pyapi.eval", SQLSTATE(PY000) "If you see this error something went wrong in " "the code (size computation). Sorry."); goto finally; } finally: GDKfree(indentation_levels); GDKfree(statements_per_level); return newcode; }