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 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) { 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, "Variable '%s' already declared", name); } /* variables are put on stack, * TODO make sure on plan/explain etc they only * exist during plan phase */ stack_push_var(sql, name, ctype); 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_partition(mvc *sql, sql_rel *rel) { list *tables = sa_list(sql->sa); /* find basetable relations */ /* mark one (largest) with REL_PARTITION */ find_basetables(rel, tables); if (list_length(tables)) { sql_rel *r; node *n; int i, mi = 0; lng *sizes = SA_NEW_ARRAY(sql->sa, lng, list_length(tables)), m = 0; for(i=0, n = tables->h; n; i++, n = n->next) { r = n->data; sizes[i] = rel_getcount(sql, r); if (sizes[i] > m) { m = sizes[i]; mi = i; } } for(i=0, n = tables->h; i<mi; i++, n = n->next) ; r = n->data; /* TODO, we now pick first (okay?)! In case of self joins we need to pick the correct table */ r->flag = REL_PARTITION; } return rel; }
static list * create_type_list(mvc *sql, dlist *params, int param) { sql_subtype *par_subtype; list * type_list = sa_list(sql->sa); dnode * n = NULL; if (params) { for (n = params->h; n; n = n->next) { dnode *an = n; if (param) { an = n->data.lval->h; par_subtype = &an->next->data.typeval; if (par_subtype && !par_subtype->type) /* var arg */ return type_list; list_append(type_list, par_subtype); } else { par_subtype = &an->data.typeval; list_prepend(type_list, par_subtype); } } } return type_list; }
static sql_exp * rel_psm_if_then_else( mvc *sql, sql_subtype *res, list *restypelist, dnode *elseif, int is_func) { if (!elseif) return NULL; if (elseif->next && elseif->type == type_symbol) { /* if or elseif */ sql_exp *cond; list *ifstmts, *elsestmts; dnode *n = elseif; sql_rel *rel = NULL; cond = rel_logical_value_exp(sql, &rel, n->data.sym, sql_sel); n = n->next; ifstmts = sequential_block(sql, res, restypelist, n->data.lval, NULL, is_func); n = n->next; elsestmts = psm_if_then_else( sql, res, restypelist, n, is_func); if (sql->session->status || !cond || !ifstmts) return NULL; if (rel) { sql_exp *er = exp_rel(sql, rel); list *b = sa_list(sql->sa); append(b, er); append(b, exp_if(sql->sa, cond, ifstmts, elsestmts)); return exp_rel(sql, rel_psm_block(sql->sa, b)); } return exp_if( sql->sa, cond, ifstmts, elsestmts); } return NULL; }
/* [ label: ] while (cond) do statement_list end [ label ] currently we only parse the labels, they cannot be used as there is no support for LEAVE and ITERATE (sql multi-level break and continue) */ static sql_exp * rel_psm_while_do( mvc *sql, sql_subtype *res, list *restypelist, dnode *w, int is_func ) { if (!w) return NULL; if (w->type == type_symbol) { sql_exp *cond; list *whilestmts; dnode *n = w; sql_rel *rel = NULL; cond = rel_logical_value_exp(sql, &rel, n->data.sym, sql_sel); n = n->next; whilestmts = sequential_block(sql, res, restypelist, n->data.lval, n->next->data.sval, is_func); if (sql->session->status || !cond || !whilestmts) return NULL; if (rel) { sql_exp *er = exp_rel(sql, rel); list *b = sa_list(sql->sa); append(b, er); append(b, exp_while( sql->sa, cond, whilestmts )); return exp_rel(sql, rel_psm_block(sql->sa, b)); } return exp_while( sql->sa, cond, whilestmts ); } return NULL; }
/* if (cond) then statement_list [ elseif (cond) then statement_list ]* [ else statement_list ] end if */ static list * psm_if_then_else( mvc *sql, sql_subtype *res, dnode *elseif, int is_func) { if (!elseif) return NULL; if (elseif->next && elseif->type == type_symbol) { /* if or elseif */ sql_exp *cond; list *ifstmts, *elsestmts; dnode *n = elseif; sql_rel *rel = NULL; cond = rel_logical_value_exp(sql, &rel, n->data.sym, sql_sel); n = n->next; ifstmts = sequential_block(sql, res, NULL, n->data.lval, NULL, is_func); n = n->next; elsestmts = psm_if_then_else( sql, res, n, is_func); if (sql->session->status || !cond || !ifstmts || rel) { if (rel) return sql_error(sql, 02, "IF THEN: No SELECT statements allowed within the IF condition"); return NULL; } return append(sa_list(sql->sa), exp_if( sql->sa, cond, ifstmts, elsestmts)); } else { /* else */ symbol *e = elseif->data.sym; if (e==NULL || (e->token != SQL_ELSE)) return NULL; return sequential_block( sql, res, NULL, e->data.lval, NULL, is_func); } }
sql_exp * exp_filter2(sql_allocator *sa, sql_exp *l, sql_exp *r1, sql_exp *r2, sql_subfunc *f, int anti) { list *r = sa_list(sa); append(r, r1); if (r2) append(r, r2); return exp_filter(sa, l, r, f, anti); }
static void monet5_create_privileges(ptr _mvc, sql_schema *s) { sql_table *t, *uinfo; mvc *m = (mvc *) _mvc; char *err = NULL; int schema_id = 0; str monetdbuser = "******"; list *res, *ops; /* create the authorisation related tables */ t = mvc_create_table(m, s, "db_user_info", tt_table, 1, SQL_PERSIST, 0, -1); mvc_create_column_(m, t, "name", "varchar", 1024); mvc_create_column_(m, t, "fullname", "varchar", 2048); mvc_create_column_(m, t, "default_schema", "int", 9); uinfo = t; (void) err; res = sa_list(m->sa); list_append(res, sql_create_arg(m->sa, "name", sql_bind_subtype(m->sa, "varchar", 2048, 0), ARG_OUT)); /* add function */ ops = sa_list(m->sa); /* following funcion returns a table (single column) of user names with the approriate scenario (sql) */ mvc_create_func(m, NULL, s, "db_users", ops, res, F_UNION, FUNC_LANG_SQL, "sql", "db_users", "CREATE FUNCTION db_users () RETURNS TABLE( name varchar(2048)) EXTERNAL NAME sql.db_users;", FALSE, FALSE); t = mvc_create_view(m, s, "users", SQL_PERSIST, "SELECT u.\"name\" AS \"name\", " "ui.\"fullname\", ui.\"default_schema\" " "FROM db_users() AS u LEFT JOIN " "\"sys\".\"db_user_info\" AS ui " "ON u.\"name\" = ui.\"name\" " ";", 1); mvc_create_column_(m, t, "name", "varchar", 1024); mvc_create_column_(m, t, "fullname", "varchar", 2024); mvc_create_column_(m, t, "default_schema", "int", 9); schema_id = sql_find_schema(m, "sys"); assert(schema_id >= 0); table_funcs.table_insert(m->session->tr, uinfo, monetdbuser, "MonetDB Admin", &schema_id); }
list * exp_types(sql_allocator *sa, list *exps) { list *l = sa_list(sa); node *n; for ( n = exps->h; n; n = n->next) append(l, exp_subtype(n->data)); return l; }
static sql_rel * rel_psm_stmt(sql_allocator *sa, sql_exp *e) { if (e) { list *l = sa_list(sa); list_append(l, e); return rel_psm_block(sa, l); } return NULL; }
int sql_create_env(mvc *m, sql_schema *s) { list *res, *ops; res = sa_list(m->sa); list_append(res, sql_create_arg(m->sa, "name", sql_bind_subtype(m->sa, "varchar", 1024, 0), ARG_OUT)); list_append(res, sql_create_arg(m->sa, "value", sql_bind_subtype(m->sa, "varchar", 2048, 0), ARG_OUT)); /* add function */ ops = sa_list(m->sa); mvc_create_func(m, NULL, s, "env", ops, res, F_UNION, FUNC_LANG_SQL, "sql", "sql_environment", "CREATE FUNCTION env () RETURNS TABLE( name varchar(1024), value varchar(2048)) EXTERNAL NAME sql.sql_environment;", FALSE, FALSE); res = sa_list(m->sa); list_append(res, sql_create_arg(m->sa, "name", sql_bind_subtype(m->sa, "varchar", 1024, 0), ARG_OUT)); /* add function */ ops = sa_list(m->sa); mvc_create_func(m, NULL, s, "var", ops, res, F_UNION, FUNC_LANG_SQL, "sql", "sql_variables", "CREATE FUNCTION var() RETURNS TABLE( name varchar(1024)) EXTERNAL NAME sql.sql_variables;", FALSE, FALSE); return 0; }
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; a = sql_create_arg(sql->sa, n->data.sval, ct, ARG_OUT); list_append(types, a); } return types; } return NULL; }
/* if (cond) then statement_list [ elseif (cond) then statement_list ]* [ else statement_list ] end if */ static list * psm_if_then_else( mvc *sql, sql_subtype *res, list *restypelist, dnode *elseif, int is_func) { if (!elseif) return NULL; assert(elseif->type == type_symbol); if (elseif->data.sym && elseif->data.sym->token == SQL_IF) { sql_exp *cond; list *ifstmts, *elsestmts; dnode *n = elseif->data.sym->data.lval->h; sql_rel *rel = NULL; cond = rel_logical_value_exp(sql, &rel, n->data.sym, sql_sel); n = n->next; ifstmts = sequential_block(sql, res, restypelist, n->data.lval, NULL, is_func); n = n->next; elsestmts = psm_if_then_else( sql, res, restypelist, n, is_func); if (sql->session->status || !cond || !ifstmts) return NULL; if (rel) { sql_exp *er = exp_rel(sql, rel); list *b = sa_list(sql->sa); append(b, er); append(b, exp_if(sql->sa, cond, ifstmts, elsestmts)); return b; } return append(sa_list(sql->sa), exp_if( sql->sa, cond, ifstmts, elsestmts)); } else { /* else */ symbol *e = elseif->data.sym; if (e==NULL || (e->token != SQL_ELSE)) return NULL; return sequential_block( sql, res, restypelist, e->data.lval, NULL, is_func); } }
sql_exp * exp_convert(sql_allocator *sa, sql_exp *exp, sql_subtype *fromtype, sql_subtype *totype ) { sql_exp *e = exp_create(sa, e_convert); e->card = exp->card; e->l = exp; totype = dup_subtype(sa, totype); e->r = append(append(sa_list(sa), dup_subtype(sa, fromtype)),totype); e->tpe = *totype; if (exp->name) e->name = sa_strdup(sa, exp->name); if (exp->rname) e->rname = sa_strdup(sa, exp->rname); return e; }
/* SET variable = value */ static sql_exp * psm_set_exp(mvc *sql, dnode *n) { exp_kind ek = {type_value, card_value, FALSE}; char *name = n->data.sval; symbol *val = n->next->data.sym; sql_exp *e = NULL; int level = 0, is_last = 0; sql_subtype *tpe = NULL; sql_rel *rel = NULL; sql_exp *res = NULL; /* name can be 'parameter of the function' (ie in the param list) or a local or global variable, declared earlier */ /* check if variable is known from the stack */ if (!stack_find_var(sql, name)) { sql_arg *a = sql_bind_param(sql, name); if (!a) /* not parameter, ie local var ? */ return sql_error(sql, 01, "Variable %s unknown", name); tpe = &a->type; } else { tpe = stack_find_type(sql, name); } e = rel_value_exp2(sql, &rel, val, sql_sel, ek, &is_last); if (!e || (rel && e->card > CARD_AGGR)) return NULL; level = stack_find_frame(sql, name); e = rel_check_type(sql, tpe, e, type_cast); if (!e) return NULL; if (rel) { sql_exp *er = exp_rel(sql, rel); list *b = sa_list(sql->sa); append(b, er); append(b, exp_set(sql->sa, name, e, level)); res = exp_rel(sql, rel_psm_block(sql->sa, b)); } else { res = exp_set(sql->sa, name, e, level); } return res; }
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, "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; }
static sql_rel * rel_create_func(mvc *sql, dlist *qname, dlist *params, symbol *res, dlist *ext_name, dlist *body, int type, int lang) { char *fname = qname_table(qname); char *sname = qname_schema(qname); sql_schema *s = NULL; sql_func *f = NULL; sql_subfunc *sf; dnode *n; list *type_list = NULL, *restype = NULL; int instantiate = (sql->emode == m_instantiate); int deps = (sql->emode == m_deps); int create = (!instantiate && !deps); bit vararg = FALSE; char is_table = (res && res->token == SQL_TABLE); 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 ": ""; assert(res || type == F_PROC || type == F_FILT); if (is_table) type = F_UNION; if (STORE_READONLY && create) return sql_error(sql, 06, "schema statements cannot be executed on a readonly database."); if (sname && !(s = mvc_bind_schema(sql, sname))) return sql_error(sql, 02, "3F000!CREATE %s%s: no such schema '%s'", KF, F, sname); if (s == NULL) s = cur_schema(sql); type_list = create_type_list(sql, params, 1); if ((sf = sql_bind_func_(sql->sa, s, fname, type_list, type)) != NULL && create) { if (params) { char *arg_list = NULL; node *n; for (n = type_list->h; n; n = n->next) { char *tpe = subtype2string((sql_subtype *) n->data); if (arg_list) { arg_list = sql_message("%s, %s", arg_list, tpe); _DELETE(tpe); } else { arg_list = tpe; } } (void)sql_error(sql, 02, "CREATE %s%s: name '%s' (%s) already in use", KF, F, fname, arg_list); _DELETE(arg_list); list_destroy(type_list); return NULL; } else { list_destroy(type_list); return sql_error(sql, 02, "CREATE %s%s: name '%s' already in use", KF, F, fname); } } else { list_destroy(type_list); if (create && !schema_privs(sql->role_id, s)) { return sql_error(sql, 02, "CREATE %s%s: insufficient privileges " "for user '%s' in schema '%s'", KF, F, stack_get_string(sql, "current_user"), s->base.name); } else { char *q = QUERY(sql->scanner); list *l = NULL; if (params) { for (n = params->h; n; n = n->next) { dnode *an = n->data.lval->h; sql_add_param(sql, an->data.sval, &an->next->data.typeval); } l = sql->params; if (l && list_length(l) == 1) { sql_arg *a = l->h->data; if (strcmp(a->name, "*") == 0) { l = NULL; vararg = TRUE; } } } if (!l) l = sa_list(sql->sa); if (res) { restype = result_type(sql, res); if (!restype) return sql_error(sql, 01, "CREATE %s%s: failed to get restype", KF, F); } if (body && lang > FUNC_LANG_SQL) { char *lang_body = body->h->data.sval; char *mod = (lang == FUNC_LANG_R)?"rapi": (lang == FUNC_LANG_C)?"capi": (lang == FUNC_LANG_J)?"japi":"unknown"; sql->params = NULL; if (create) { f = mvc_create_func(sql, sql->sa, s, fname, l, restype, type, lang, mod, fname, lang_body, FALSE, vararg); } else if (!sf) { return sql_error(sql, 01, "CREATE %s%s: R function %s.%s not bound", KF, F, s->base.name, fname ); } else { sql_func *f = sf->func; f->mod = _STRDUP("rapi"); f->imp = _STRDUP("eval"); if (res && restype) f->res = restype; f->sql = 0; /* native */ f->lang = FUNC_LANG_INT; } } else if (body) { sql_arg *ra = (restype && !is_table)?restype->h->data:NULL; list *b = NULL; sql_schema *old_schema = cur_schema(sql); sql->session->schema = s; b = sequential_block(sql, (ra)?&ra->type:NULL, ra?NULL:restype, body, NULL, is_func); sql->session->schema = old_schema; sql->params = NULL; if (!b) return NULL; /* check if we have a return statement */ if (is_func && restype && !has_return(b)) { return sql_error(sql, 01, "CREATE %s%s: missing return statement", KF, F); } if (!is_func && !restype && has_return(b)) { return sql_error(sql, 01, "CREATE %s%s: procedures " "cannot have return statements", KF, F); } /* in execute mode we instantiate the function */ if (instantiate || deps) { return rel_psm_block(sql->sa, b); } else if (create) { f = mvc_create_func(sql, sql->sa, s, fname, l, restype, type, lang, "user", q, q, FALSE, vararg); } } else { char *fmod = qname_module(ext_name); char *fnme = qname_fname(ext_name); if (!fmod || !fnme) return NULL; sql->params = NULL; if (create) { f = mvc_create_func(sql, sql->sa, s, fname, l, restype, type, lang, fmod, fnme, q, FALSE, vararg); } else if (!sf) { return sql_error(sql, 01, "CREATE %s%s: external name %s.%s not bound (%s,%s)", KF, F, fmod, fnme, s->base.name, fname ); } else { sql_func *f = sf->func; f->mod = _STRDUP(fmod); f->imp = _STRDUP(fnme); f->sql = 0; /* native */ f->lang = FUNC_LANG_INT; } } } } return rel_create_function(sql->sa, s->base.name, f); }
static list * sequential_block (mvc *sql, sql_subtype *restype, list *restypelist, dlist *blk, char *opt_label, int is_func) { list *l=0; dnode *n; assert(!restype || !restypelist); if (THRhighwater()) return sql_error(sql, 10, "SELECT: too many nested operators"); if (blk->h) l = sa_list(sql->sa); stack_push_frame(sql, opt_label); for (n = blk->h; n; n = n->next ) { sql_exp *res = NULL; list *reslist = NULL; symbol *s = n->data.sym; switch (s->token) { case SQL_SET: res = psm_set_exp(sql, s->data.lval->h); break; case SQL_DECLARE: reslist = rel_psm_declare(sql, s->data.lval->h); break; case SQL_CREATE_TABLE: res = rel_psm_declare_table(sql, s->data.lval->h); break; case SQL_WHILE: res = rel_psm_while_do(sql, restype, s->data.lval->h, is_func); break; case SQL_IF: res = rel_psm_if_then_else(sql, restype, s->data.lval->h, is_func); break; case SQL_CASE: reslist = rel_psm_case(sql, restype, s->data.lval->h, is_func); break; case SQL_CALL: res = rel_psm_call(sql, s->data.sym); break; case SQL_RETURN: /*If it is not a function it cannot have a return statement*/ if (!is_func) res = sql_error(sql, 01, "Return statement in the procedure body"); else { /* should be last statement of a sequential_block */ if (n->next) { res = sql_error(sql, 01, "Statement after return"); } else { reslist = rel_psm_return(sql, restype, restypelist, s->data.sym); } } break; case SQL_SELECT: { /* row selections (into variables) */ exp_kind ek = {type_value, card_row, TRUE}; reslist = rel_select_into(sql, s, ek); } break; case SQL_COPYFROM: case SQL_BINCOPYFROM: case SQL_INSERT: case SQL_UPDATE: case SQL_DELETE: { sql_rel *r = rel_updates(sql, s); if (!r) return NULL; res = exp_rel(sql, r); } break; default: res = sql_error(sql, 01, "Statement '%s' is not a valid flow control statement", token2string(s->token)); } if (!res && !reslist) { l = NULL; break; } if (res) list_append(l, res); else list_merge(l, reslist, NULL); } stack_pop_frame(sql); return l; }
/* return val; */ static list * rel_psm_return( mvc *sql, sql_subtype *restype, list *restypelist, symbol *return_sym ) { exp_kind ek = {type_value, card_value, FALSE}; sql_exp *res; sql_rel *rel = NULL; int is_last = 0; list *l = sa_list(sql->sa); if (restypelist) ek.card = card_relation; res = rel_value_exp2(sql, &rel, return_sym, sql_sel, ek, &is_last); if (!res) return NULL; if (ek.card != card_relation && (!res || (res = rel_check_type(sql, restype, res, type_equal)) == NULL)) return NULL; else if (ek.card == card_relation && !rel) return NULL; if (rel && ek.card != card_relation) append(l, exp_rel(sql, rel)); else if (rel && !is_ddl(rel->op)) { list *exps = sa_list(sql->sa); node *n, *m; int isproject = (rel->op == op_project); list *oexps = rel->exps; sql_rel *l = rel->l; if (is_topn(rel->op)) oexps = l->exps; for (n = oexps->h, m = restypelist->h; n && m; n = n->next, m = m->next) { sql_exp *e = n->data; sql_arg *ce = m->data; char *cname = exp_name(e); char name[16]; if (!cname) cname = sa_strdup(sql->sa, number2name(name, 16, ++sql->label)); if (!isproject) e = exp_column(sql->sa, exp_relname(e), cname, exp_subtype(e), exp_card(e), has_nil(e), is_intern(e)); e = rel_check_type(sql, &ce->type, e, type_equal); if (!e) return NULL; append(exps, e); } if (isproject) rel -> exps = exps; else rel = rel_project(sql->sa, rel, exps); res = exp_rel(sql, rel); } else if (rel && restypelist){ /* handle return table-var */ list *exps = sa_list(sql->sa); sql_table *t = rel_ddl_table_get(rel); node *n, *m; char *tname = t->base.name; if (cs_size(&t->columns) != list_length(restypelist)) return sql_error(sql, 02, "RETURN: number of columns do not match"); for (n = t->columns.set->h, m = restypelist->h; n && m; n = n->next, m = m->next) { sql_column *c = n->data; sql_arg *ce = m->data; sql_exp *e = exp_alias(sql->sa, tname, c->base.name, tname, c->base.name, &c->type, CARD_MULTI, c->null, 0); e = rel_check_type(sql, &ce->type, e, type_equal); if (!e) return NULL; append(exps, e); } rel = rel_project(sql->sa, rel, exps); res = exp_rel(sql, rel); } append(l, exp_return(sql->sa, res, stack_nr_of_declared_tables(sql))); return l; }
/* 1 CASE WHEN search_condition THEN statements [ WHEN search_condition THEN statements ] [ ELSE statements ] END CASE 2 CASE case_value WHEN when_value THEN statements [ WHEN when_value THEN statements ] [ ELSE statements ] END CASE */ static list * rel_psm_case( mvc *sql, sql_subtype *res, dnode *case_when, int is_func ) { list *case_stmts = sa_list(sql->sa); if (!case_when) return NULL; /* case 1 */ if (case_when->type == type_symbol) { dnode *n = case_when; symbol *case_value = n->data.sym; dlist *when_statements = n->next->data.lval; dlist *else_statements = n->next->next->data.lval; list *else_stmt = NULL; sql_rel *rel = NULL; exp_kind ek = {type_value, card_value, FALSE}; sql_exp *v = rel_value_exp(sql, &rel, case_value, sql_sel, ek); if (!v) return NULL; if (rel) return sql_error(sql, 02, "CASE: No SELECT statements allowed within the CASE condition"); if (else_statements) { else_stmt = sequential_block( sql, res, NULL, else_statements, NULL, is_func); if (!else_stmt) return NULL; } n = when_statements->h; while(n) { dnode *m = n->data.sym->data.lval->h; sql_exp *cond=0, *when_value = rel_value_exp(sql, &rel, m->data.sym, sql_sel, ek); list *if_stmts = NULL; sql_exp *case_stmt = NULL; if (!when_value || rel || (cond = rel_binop_(sql, v, when_value, NULL, "=", card_value)) == NULL || (if_stmts = sequential_block( sql, res, NULL, m->next->data.lval, NULL, is_func)) == NULL ) { if (rel) return sql_error(sql, 02, "CASE: No SELECT statements allowed within the CASE condition"); return NULL; } case_stmt = exp_if(sql->sa, cond, if_stmts, NULL); list_append(case_stmts, case_stmt); n = n->next; } if (else_stmt) list_merge(case_stmts, else_stmt, NULL); return case_stmts; } else { /* case 2 */ dnode *n = case_when; dlist *whenlist = n->data.lval; dlist *else_statements = n->next->data.lval; list *else_stmt = NULL; if (else_statements) { else_stmt = sequential_block( sql, res, NULL, else_statements, NULL, is_func); if (!else_stmt) return NULL; } n = whenlist->h; while(n) { dnode *m = n->data.sym->data.lval->h; sql_rel *rel = NULL; sql_exp *cond = rel_logical_value_exp(sql, &rel, m->data.sym, sql_sel); list *if_stmts = NULL; sql_exp *case_stmt = NULL; if (!cond || rel || (if_stmts = sequential_block( sql, res, NULL, m->next->data.lval, NULL, is_func)) == NULL ) { if (rel) return sql_error(sql, 02, "CASE: No SELECT statements allowed within the CASE condition"); return NULL; } case_stmt = exp_if(sql->sa, cond, if_stmts, NULL); list_append(case_stmts, case_stmt); n = n->next; } if (else_stmt) list_merge(case_stmts, else_stmt, NULL); return case_stmts; } }
static sql_rel * psm_analyze(mvc *sql, dlist *qname, dlist *columns, symbol *sample ) { exp_kind ek = {type_value, card_value, FALSE}; sql_exp *sample_exp = NULL, *call; char *sname = NULL, *tname = NULL; list *tl = sa_list(sql->sa); list *exps = sa_list(sql->sa), *analyze_calls = sa_list(sql->sa); sql_subfunc *f = NULL; if (sample) { sql_subtype *tpe = sql_bind_localtype("lng"); sample_exp = rel_value_exp( sql, NULL, sample, 0, ek); if (sample_exp) sample_exp = rel_check_type(sql, tpe, sample_exp, type_cast); } if (qname) { if (qname->h->next) sname = qname_schema(qname); else sname = qname_table(qname); if (!sname) sname = cur_schema(sql)->base.name; if (qname->h->next) tname = qname_table(qname); } /* call analyze( [schema, [ table ]], opt_sample_size ) */ if (sname) { sql_exp *sname_exp = exp_atom_clob(sql->sa, sname); append(exps, sname_exp); append(tl, exp_subtype(sname_exp)); } if (tname) { sql_exp *tname_exp = exp_atom_clob(sql->sa, tname); append(exps, tname_exp); append(tl, exp_subtype(tname_exp)); if (columns) append(tl, exp_subtype(tname_exp)); } if (!columns) { if (sample_exp) { append(exps, sample_exp); append(tl, exp_subtype(sample_exp)); } f = sql_bind_func_(sql->sa, mvc_bind_schema(sql, "sys"), "analyze", tl, F_PROC); if (!f) return sql_error(sql, 01, "Analyze procedure missing"); call = exp_op(sql->sa, exps, f); append(analyze_calls, call); } else { dnode *n; if (sample_exp) append(tl, exp_subtype(sample_exp)); f = sql_bind_func_(sql->sa, mvc_bind_schema(sql, "sys"), "analyze", tl, F_PROC); if (!f) return sql_error(sql, 01, "Analyze procedure missing"); for( n = columns->h; n; n = n->next) { char *cname = n->data.sval; list *nexps = list_dup(exps, NULL); sql_exp *cname_exp = exp_atom_clob(sql->sa, cname); append(nexps, cname_exp); if (sample_exp) append(nexps, sample_exp); /* call analyze( sname, tname, cname, opt_sample_size ) */ call = exp_op(sql->sa, nexps, f); append(analyze_calls, call); } } return rel_psm_block(sql->sa, analyze_calls); }
static sql_rel * rel_create_func(mvc *sql, dlist *qname, dlist *params, symbol *res, dlist *ext_name, dlist *body, int type, int lang, int replace) { const char *fname = qname_table(qname); const char *sname = qname_schema(qname); sql_schema *s = NULL; sql_func *f = NULL; sql_subfunc *sf; dnode *n; list *type_list = NULL, *restype = NULL; int instantiate = (sql->emode == m_instantiate); int deps = (sql->emode == m_deps); int create = (!instantiate && !deps); bit vararg = FALSE; char is_table = (res && res->token == SQL_TABLE); char is_aggr = (type == F_AGGR); char is_func = (type != F_PROC); char is_loader = (type == F_LOADER); char *F = is_loader?"LOADER":(is_aggr?"AGGREGATE":(is_func?"FUNCTION":"PROCEDURE")); char *fn = is_loader?"loader":(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 " : ""; assert(res || type == F_PROC || type == F_FILT || type == F_LOADER); if (is_table) type = F_UNION; if (STORE_READONLY && create) return sql_error(sql, 06, SQLSTATE(42000) "Schema statements cannot be executed on a readonly database."); if (sname && !(s = mvc_bind_schema(sql, sname))) return sql_error(sql, 02, SQLSTATE(3F000) "CREATE %s%s: no such schema '%s'", KF, F, sname); if (s == NULL) s = cur_schema(sql); type_list = create_type_list(sql, params, 1); if ((sf = sql_bind_func_(sql->sa, s, fname, type_list, type)) != NULL && create) { if (replace) { sql_func *func = sf->func; int action = 0; if (!mvc_schema_privs(sql, s)) { return sql_error(sql, 02, SQLSTATE(42000) "CREATE OR REPLACE %s%s: access denied for %s to schema ;'%s'", KF, F, stack_get_string(sql, "current_user"), s->base.name); } if (mvc_check_dependency(sql, func->base.id, !IS_PROC(func) ? FUNC_DEPENDENCY : PROC_DEPENDENCY, NULL)) return sql_error(sql, 02, SQLSTATE(42000) "CREATE OR REPLACE %s%s: there are database objects dependent on %s%s %s;", KF, F, kf, fn, func->base.name); if (!func->s) { return sql_error(sql, 02, SQLSTATE(42000) "CREATE OR REPLACE %s%s: not allowed to replace system %s%s %s;", KF, F, kf, fn, func->base.name); } if(mvc_drop_func(sql, s, func, action)) return sql_error(sql, 02, SQLSTATE(HY001) MAL_MALLOC_FAIL); sf = NULL; } else { if (params) { char *arg_list = NULL; node *n; for (n = type_list->h; n; n = n->next) { char *tpe = subtype2string((sql_subtype *) n->data); if (arg_list) { char *t = arg_list; arg_list = sql_message("%s, %s", arg_list, tpe); _DELETE(t); _DELETE(tpe); } else { arg_list = tpe; } } (void)sql_error(sql, 02, SQLSTATE(42000) "CREATE %s%s: name '%s' (%s) already in use", KF, F, fname, arg_list); _DELETE(arg_list); list_destroy(type_list); return NULL; } else { list_destroy(type_list); return sql_error(sql, 02, SQLSTATE(42000) "CREATE %s%s: name '%s' already in use", KF, F, fname); } } } list_destroy(type_list); if (create && !mvc_schema_privs(sql, s)) { return sql_error(sql, 02, SQLSTATE(42000) "CREATE %s%s: insufficient privileges " "for user '%s' in schema '%s'", KF, F, stack_get_string(sql, "current_user"), s->base.name); } else { char *q = QUERY(sql->scanner); list *l = NULL; if (params) { for (n = params->h; n; n = n->next) { dnode *an = n->data.lval->h; sql_add_param(sql, an->data.sval, &an->next->data.typeval); } l = sql->params; if (l && list_length(l) == 1) { sql_arg *a = l->h->data; if (strcmp(a->name, "*") == 0) { l = NULL; vararg = TRUE; } } } if (!l) l = sa_list(sql->sa); if (res) { restype = result_type(sql, res); if (!restype) return sql_error(sql, 01, SQLSTATE(42000) "CREATE %s%s: failed to get restype", KF, F); } if (body && lang > FUNC_LANG_SQL) { char *lang_body = body->h->data.sval; char *mod = (lang == FUNC_LANG_R)?"rapi": (lang == FUNC_LANG_C || lang == FUNC_LANG_CPP)?"capi": (lang == FUNC_LANG_J)?"japi": (lang == FUNC_LANG_PY)?"pyapi": (lang == FUNC_LANG_MAP_PY)?"pyapimap":"unknown"; sql->params = NULL; if (create) { f = mvc_create_func(sql, sql->sa, s, fname, l, restype, type, lang, mod, fname, lang_body, (type == F_LOADER)?TRUE:FALSE, vararg, FALSE); } else if (!sf) { return sql_error(sql, 01, SQLSTATE(42000) "CREATE %s%s: R function %s.%s not bound", KF, F, s->base.name, fname ); } /*else { sql_func *f = sf->func; f->mod = _STRDUP("rapi"); f->imp = _STRDUP("eval"); if (res && restype) f->res = restype; f->sql = 0; f->lang = FUNC_LANG_INT; }*/ } else if (body) { sql_arg *ra = (restype && !is_table)?restype->h->data:NULL; list *b = NULL; sql_schema *old_schema = cur_schema(sql); if (create) { /* needed for recursive functions */ q = query_cleaned(q); sql->forward = f = mvc_create_func(sql, sql->sa, s, fname, l, restype, type, lang, "user", q, q, FALSE, vararg, FALSE); GDKfree(q); } sql->session->schema = s; b = sequential_block(sql, (ra)?&ra->type:NULL, ra?NULL:restype, body, NULL, is_func); sql->forward = NULL; sql->session->schema = old_schema; sql->params = NULL; if (!b) return NULL; /* check if we have a return statement */ if (is_func && restype && !has_return(b)) { return sql_error(sql, 01, SQLSTATE(42000) "CREATE %s%s: missing return statement", KF, F); } if (!is_func && !restype && has_return(b)) { return sql_error(sql, 01, SQLSTATE(42000) "CREATE %s%s: procedures " "cannot have return statements", KF, F); } /* in execute mode we instantiate the function */ if (instantiate || deps) { return rel_psm_block(sql->sa, b); } } else { char *fmod = qname_module(ext_name); char *fnme = qname_fname(ext_name); if (!fmod || !fnme) return NULL; sql->params = NULL; if (create) { q = query_cleaned(q); f = mvc_create_func(sql, sql->sa, s, fname, l, restype, type, lang, fmod, fnme, q, FALSE, vararg, FALSE); GDKfree(q); } else if (!sf) { return sql_error(sql, 01, SQLSTATE(42000) "CREATE %s%s: external name %s.%s not bound (%s.%s)", KF, F, fmod, fnme, s->base.name, fname ); } else { sql_func *f = sf->func; if (!f->mod || strcmp(f->mod, fmod)) f->mod = _STRDUP(fmod); if (!f->imp || strcmp(f->imp, fnme)) f->imp = (f->sa)?sa_strdup(f->sa, fnme):_STRDUP(fnme); if(!f->mod || !f->imp) { _DELETE(f->mod); _DELETE(f->imp); return sql_error(sql, 02, SQLSTATE(HY001) "CREATE %s%s: could not allocate space", KF, F); } f->sql = 0; /* native */ f->lang = FUNC_LANG_INT; } } } return rel_create_function(sql->sa, s->base.name, f); }
/* SET variable = value and set (variable1, .., variableN) = (query) */ static sql_exp * psm_set_exp(mvc *sql, dnode *n) { symbol *val = n->next->data.sym; sql_exp *e = NULL; int level = 0, is_last = 0; sql_subtype *tpe = NULL; sql_rel *rel = NULL; sql_exp *res = NULL; int single = (n->type == type_string); if (single) { exp_kind ek = {type_value, card_value, FALSE}; const char *name = n->data.sval; /* name can be 'parameter of the function' (ie in the param list) or a local or global variable, declared earlier */ /* check if variable is known from the stack */ if (!stack_find_var(sql, name)) { sql_arg *a = sql_bind_param(sql, name); if (!a) /* not parameter, ie local var ? */ return sql_error(sql, 01, SQLSTATE(42000) "Variable %s unknown", name); tpe = &a->type; } else { tpe = stack_find_type(sql, name); } e = rel_value_exp2(sql, &rel, val, sql_sel, ek, &is_last); if (!e || (rel && e->card > CARD_AGGR)) return NULL; level = stack_find_frame(sql, name); e = rel_check_type(sql, tpe, e, type_cast); if (!e) return NULL; if (rel) { sql_exp *er = exp_rel(sql, rel); list *b = sa_list(sql->sa); append(b, er); append(b, exp_set(sql->sa, name, e, level)); res = exp_rel(sql, rel_psm_block(sql->sa, b)); } else { res = exp_set(sql->sa, name, e, level); } } else { /* multi assignment */ exp_kind ek = {type_value, (single)?card_column:card_relation, FALSE}; sql_rel *rel_val = rel_subquery(sql, NULL, val, ek, APPLY_JOIN); dlist *vars = n->data.lval; dnode *m; node *n; list *b; if (!rel_val || !is_project(rel_val->op) || dlist_length(vars) != list_length(rel_val->exps)) { return sql_error(sql, 02, SQLSTATE(42000) "SET: Number of variables not equal to number of supplied values"); } b = sa_list(sql->sa); if (rel_val) { sql_exp *er = exp_rel(sql, rel_val); append(b, er); } for(m = vars->h, n = rel_val->exps->h; n && m; n = n->next, m = m->next) { char *vname = m->data.sval; sql_exp *v = n->data; if (!stack_find_var(sql, vname)) { sql_arg *a = sql_bind_param(sql, vname); if (!a) /* not parameter, ie local var ? */ return sql_error(sql, 01, SQLSTATE(42000) "Variable %s unknown", vname); tpe = &a->type; } else { tpe = stack_find_type(sql, vname); } if (!exp_name(v)) exp_label(sql->sa, v, ++sql->label); v = exp_column(sql->sa, exp_relname(v), exp_name(v), exp_subtype(v), v->card, has_nil(v), is_intern(v)); level = stack_find_frame(sql, vname); v = rel_check_type(sql, tpe, v, type_cast); if (!v) return NULL; 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); } append(b, exp_set(sql->sa, vname, v, level)); } res = exp_rel(sql, rel_psm_block(sql->sa, b)); } return res; }