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; }
/* 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; }
/* 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); } }
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; }
/* 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; }
sql_rel * rel_psm(mvc *sql, symbol *s) { sql_rel *ret = NULL; switch (s->token) { case SQL_CREATE_FUNC: { dlist *l = s->data.lval; int type = l->h->next->next->next->next->next->data.i_val; int lang = l->h->next->next->next->next->next->next->data.i_val; int repl = l->h->next->next->next->next->next->next->next->data.i_val; ret = rel_create_func(sql, l->h->data.lval, l->h->next->data.lval, l->h->next->next->data.sym, l->h->next->next->next->data.lval, l->h->next->next->next->next->data.lval, type, lang, repl); sql->type = Q_SCHEMA; } break; case SQL_DROP_FUNC: { dlist *l = s->data.lval; dlist *qname = l->h->data.lval; dlist *typelist = l->h->next->data.lval; int type = l->h->next->next->data.i_val; int if_exists = l->h->next->next->next->data.i_val; int all = l->h->next->next->next->next->data.i_val; int drop_action = l->h->next->next->next->next->next->data.i_val; if (STORE_READONLY) return sql_error(sql, 06, SQLSTATE(42000) "Schema statements cannot be executed on a readonly database."); if (all) ret = rel_drop_all_func(sql, qname, drop_action, type); else { ret = rel_drop_func(sql, qname, typelist, drop_action, type, if_exists); } sql->type = Q_SCHEMA; } break; case SQL_SET: ret = rel_psm_stmt(sql->sa, psm_set_exp(sql, s->data.lval->h)); sql->type = Q_SCHEMA; break; case SQL_DECLARE: ret = rel_psm_block(sql->sa, rel_psm_declare(sql, s->data.lval->h)); sql->type = Q_SCHEMA; break; case SQL_CALL: ret = rel_psm_stmt(sql->sa, rel_psm_call(sql, s->data.sym)); sql->type = Q_UPDATE; break; case SQL_CREATE_TABLE_LOADER: { dlist *l = s->data.lval; dlist *qname = l->h->data.lval; symbol *sym = l->h->next->data.sym; ret = create_table_from_loader(sql, qname, sym); if (ret == NULL) return NULL; ret = rel_psm_stmt(sql->sa, exp_rel(sql, ret)); sql->type = Q_SCHEMA; } break; case SQL_CREATE_TRIGGER: { dlist *l = s->data.lval; assert(l->h->next->type == type_int); ret = create_trigger(sql, l->h->data.lval, l->h->next->data.i_val, l->h->next->next->data.sym, l->h->next->next->next->data.lval, l->h->next->next->next->next->data.lval, l->h->next->next->next->next->next->data.lval, l->h->next->next->next->next->next->next->data.i_val); sql->type = Q_SCHEMA; } break; case SQL_DROP_TRIGGER: { dlist *l = s->data.lval; dlist *qname = l->h->data.lval; int if_exists = l->h->next->data.i_val; ret = drop_trigger(sql, qname, if_exists); sql->type = Q_SCHEMA; } break; case SQL_ANALYZE: { dlist *l = s->data.lval; ret = psm_analyze(sql, "analyze", l->h->data.lval /* qualified table name */, l->h->next->data.lval /* opt list of column */, l->h->next->next->data.sym /* opt_sample_size */, l->h->next->next->next->data.i_val); sql->type = Q_UPDATE; } break; default: return sql_error(sql, 01, SQLSTATE(42000) "Schema statement unknown symbol(%p)->token = %s", s, token2string(s->token)); } return ret; }