Changeset: 890beb553c41 for MonetDB URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=890beb553c41 Modified Files: sql/backends/monet5/sql.c sql/server/rel_exp.c sql/server/rel_exp.h sql/server/rel_optimizer.c sql/server/rel_propagate.c sql/server/rel_psm.c sql/server/rel_schema.c sql/server/rel_select.c sql/server/rel_select.h sql/server/rel_sequence.c sql/server/rel_unnest.c sql/server/rel_updates.c sql/server/rel_xml.c sql/server/sql_atom.c sql/server/sql_atom.h sql/test/SQLancer/Tests/All sql/test/SQLancer/Tests/sqlancer02.stable.out Branch: Jun2020 Log Message:
Fix for bug 6911, ie never call exp_convert on any expression without knowing if it will be compatible with the output type. Removed the superfluous exp_convert calls and moved rel_check_type to rel_exp.h and renamed it to exp_check_type to make it more widely available. diffs (truncated from 1368 to 300 lines): diff --git a/sql/backends/monet5/sql.c b/sql/backends/monet5/sql.c --- a/sql/backends/monet5/sql.c +++ b/sql/backends/monet5/sql.c @@ -338,7 +338,7 @@ create_table_or_view(mvc *sql, char* sna _DELETE(typestr); r = rel_parse(sql, s, buf, m_deps); if (!r || !is_project(r->op) || !r->exps || list_length(r->exps) != 1 || - rel_check_type(sql, &c->type, r, r->exps->h->data, type_equal) == NULL) { + exp_check_type(sql, &c->type, r, r->exps->h->data, type_equal) == NULL) { if (r) rel_destroy(r); sa_destroy(sql->sa); diff --git a/sql/server/rel_exp.c b/sql/server/rel_exp.c --- a/sql/server/rel_exp.c +++ b/sql/server/rel_exp.c @@ -497,6 +497,13 @@ exp_null(sql_allocator *sa, sql_subtype return exp_atom(sa, a); } +sql_exp * +exp_zero(sql_allocator *sa, sql_subtype *tpe) +{ + atom *a = atom_zero_value(sa, tpe); + return exp_atom(sa, a); +} + atom * exp_value(mvc *sql, sql_exp *e, atom **args, int maxarg) { @@ -2626,6 +2633,205 @@ exps_reset_freevar(list *exps) } } +int +rel_set_type_param(mvc *sql, sql_subtype *type, sql_rel *rel, sql_exp *rel_exp, int upcast) +{ + sql_rel *r = rel; + int is_rel = exp_is_rel(rel_exp); + + if (!type || !rel_exp || (rel_exp->type != e_atom && rel_exp->type != e_column && !is_rel)) + return -1; + + /* use largest numeric types */ + if (upcast && type->type->eclass == EC_NUM) +#ifdef HAVE_HGE + type = sql_bind_localtype(have_hge ? "hge" : "lng"); +#else + type = sql_bind_localtype("lng"); +#endif + if (upcast && type->type->eclass == EC_FLT) + type = sql_bind_localtype("dbl"); + + if (is_rel) + r = (sql_rel*) rel_exp->l; + + if ((rel_exp->type == e_atom && (rel_exp->l || rel_exp->r || rel_exp->f)) || rel_exp->type == e_column || is_rel) { + /* it's not a parameter set possible parameters below */ + const char *relname = exp_relname(rel_exp), *expname = exp_name(rel_exp); + if (rel_set_type_recurse(sql, type, r, &relname, &expname) < 0) + return -1; + } else if (set_type_param(sql, type, rel_exp->flag) != 0) + return -1; + + rel_exp->tpe = *type; + return 0; +} + +/* try to do an in-place conversion + * + * in-place conversion is only possible if the exp is a variable. + * This is only done to be able to map more cached queries onto the same + * interface. + */ + +static void +convert_atom(atom *a, sql_subtype *rt) +{ + if (atom_null(a)) { + if (a->data.vtype != rt->type->localtype) { + const void *p; + + a->data.vtype = rt->type->localtype; + p = ATOMnilptr(a->data.vtype); + VALset(&a->data, a->data.vtype, (ptr) p); + } + } + a->tpe = *rt; +} + +static sql_exp * +exp_convert_inplace(mvc *sql, sql_subtype *t, sql_exp *exp) +{ + atom *a; + + /* exclude named variables and variable lists */ + if (exp->type != e_atom || exp->r /* named */ || exp->f /* list */ || !exp->l /* not direct atom */) + return NULL; + + a = exp->l; + if (t->scale && t->type->eclass != EC_FLT) + return NULL; + + if (a && atom_cast(sql->sa, a, t)) { + convert_atom(a, t); + exp->tpe = *t; + return exp; + } + return NULL; +} + +sql_exp * +exp_numeric_supertype(mvc *sql, sql_exp *e ) +{ + sql_subtype *tp = exp_subtype(e); + + if (tp->type->eclass == EC_DEC) { + sql_subtype *dtp = sql_bind_localtype("dbl"); + + return exp_check_type(sql, dtp, NULL, e, type_cast); + } + if (tp->type->eclass == EC_NUM) { +#ifdef HAVE_HGE + sql_subtype *ltp = sql_bind_localtype(have_hge ? "hge" : "lng"); +#else + sql_subtype *ltp = sql_bind_localtype("lng"); +#endif + + return exp_check_type(sql, ltp, NULL, e, type_cast); + } + return e; +} + +sql_exp * +exp_check_type(mvc *sql, sql_subtype *t, sql_rel *rel, sql_exp *exp, check_type tpe) +{ + int c, err = 0; + sql_exp* nexp = NULL; + sql_subtype *fromtype = exp_subtype(exp); + + if ((!fromtype || !fromtype->type) && rel_set_type_param(sql, t, rel, exp, 0) == 0) + return exp; + + /* first try cheap internal (in-place) conversions ! */ + if ((nexp = exp_convert_inplace(sql, t, exp)) != NULL) + return nexp; + + if (fromtype && subtype_cmp(t, fromtype) != 0) { + if (EC_INTERVAL(fromtype->type->eclass) && (t->type->eclass == EC_NUM || t->type->eclass == EC_POS) && t->digits < fromtype->digits) { + err = 1; /* conversion from interval to num depends on the number of digits */ + } else { + c = sql_type_convert(fromtype->type->eclass, t->type->eclass); + if (!c || (c == 2 && tpe == type_set) || (c == 3 && tpe != type_cast)) { + err = 1; + } else { + exp = exp_convert(sql->sa, exp, fromtype, t); + } + } + } + if (err) { + sql_exp *res = sql_error( sql, 03, SQLSTATE(42000) "types %s(%u,%u) and %s(%u,%u) are not equal%s%s%s", + fromtype->type->sqlname, + fromtype->digits, + fromtype->scale, + t->type->sqlname, + t->digits, + t->scale, + (exp->type == e_column ? " for column '" : ""), + (exp->type == e_column ? exp_name(exp) : ""), + (exp->type == e_column ? "'" : "") + ); + return res; + } + return exp; +} + +sql_exp * +exp_values_set_supertype(mvc *sql, sql_exp *values, sql_subtype *opt_super) +{ + assert(is_values(values)); + list *vals = exp_get_values(values), *nexps; + sql_subtype *tpe = opt_super?opt_super:exp_subtype(vals->h->data); + + if (!opt_super && tpe) + values->tpe = *tpe; + + for (node *m = vals->h; m; m = m->next) { + sql_exp *e = m->data; + sql_subtype super, *ttpe; + + /* if the expression is a parameter set its type */ + if (tpe && e->type == e_atom && !e->l && !e->r && !e->f && !e->tpe.type) { + if (set_type_param(sql, tpe, e->flag) == 0) + e->tpe = *tpe; + else + return NULL; + } + ttpe = exp_subtype(e); + if (tpe && ttpe) { + supertype(&super, ttpe, tpe); + values->tpe = super; + tpe = &values->tpe; + } else { + tpe = ttpe; + } + } + + if (tpe) { + /* if the expression is a parameter set its type */ + for (node *m = vals->h; m; m = m->next) { + sql_exp *e = m->data; + if (e->type == e_atom && !e->l && !e->r && !e->f && !e->tpe.type) { + if (set_type_param(sql, tpe, e->flag) == 0) + e->tpe = *tpe; + else + return NULL; + } + } + values->tpe = *tpe; + nexps = sa_list(sql->sa); + for (node *m = vals->h; m; m = m->next) { + sql_exp *e = m->data; + e = exp_check_type(sql, &values->tpe, NULL, e, type_equal); + if (!e) + return NULL; + exp_label(sql->sa, e, ++sql->label); + append(nexps, e); + } + values->f = nexps; + } + return values; +} + static int exp_set_list_recurse(mvc *sql, sql_subtype *type, sql_exp *e, const char **relname, const char** expname) { diff --git a/sql/server/rel_exp.h b/sql/server/rel_exp.h --- a/sql/server/rel_exp.h +++ b/sql/server/rel_exp.h @@ -12,6 +12,7 @@ #include "sql_relation.h" #include "sql_mvc.h" #include "sql_atom.h" +#include "sql_semantic.h" #define new_exp_list(sa) sa_list(sa) #define exp2list(sa,e) append(sa_list(sa),e) @@ -74,6 +75,7 @@ extern sql_exp * exp_atom_clob(sql_alloc extern sql_exp * exp_atom_ptr(sql_allocator *sa, void *s); extern sql_exp * exp_atom_ref(sql_allocator *sa, int i, sql_subtype *tpe); extern sql_exp * exp_null(sql_allocator *sa, sql_subtype *tpe); +extern sql_exp * exp_zero(sql_allocator *sa, sql_subtype *tpe); /* Apply it to numeric types only obviously */ extern sql_exp * exp_param(sql_allocator *sa, const char *name, sql_subtype *tpe, int frame); extern atom * exp_value(mvc *sql, sql_exp *e, atom **args, int maxarg); extern sql_exp * exp_values(sql_allocator *sa, list *exps); @@ -189,5 +191,10 @@ extern int exp_aggr_is_count(sql_exp *e) extern void exps_reset_freevar(list *exps); +extern sql_exp *exp_check_type(mvc *sql, sql_subtype *t, sql_rel *rel, sql_exp *exp, check_type tpe); +extern int rel_set_type_param(mvc *sql, sql_subtype *type, sql_rel *rel, sql_exp *rel_exp, int upcast); +extern sql_exp *exp_numeric_supertype(mvc *sql, sql_exp *e); +extern sql_exp *exp_values_set_supertype(mvc *sql, sql_exp *values, sql_subtype *opt_super); + extern int rel_set_type_recurse(mvc *sql, sql_subtype *type, sql_rel *rel, const char **relname, const char **expname); #endif /* _REL_EXP_H_ */ diff --git a/sql/server/rel_optimizer.c b/sql/server/rel_optimizer.c --- a/sql/server/rel_optimizer.c +++ b/sql/server/rel_optimizer.c @@ -3067,26 +3067,20 @@ exp_simplify_math( mvc *sql, sql_exp *e, (*changes)++; if (f && f->func && f->func->imp && strstr(f->func->imp, "_no_nil") != NULL) { exp_setname(sql->sa, re, exp_relname(e), exp_name(e)); - if (subtype_cmp(et, exp_subtype(re)) != 0) - re = exp_convert(sql->sa, re, exp_subtype(re), et); return re; } + le = exp_null(sql->sa, et); exp_setname(sql->sa, le, exp_relname(e), exp_name(e)); - if (subtype_cmp(et, exp_subtype(le)) != 0) - le = exp_convert(sql->sa, le, exp_subtype(le), et); return le; } if (exp_is_atom(re) && exp_is_null(sql, re)) { (*changes)++; if (f && f->func && f->func->imp && strstr(f->func->imp, "_no_nil") != NULL) { exp_setname(sql->sa, le, exp_relname(e), exp_name(e)); - if (subtype_cmp(et, exp_subtype(le)) != 0) - le = exp_convert(sql->sa, le, exp_subtype(le), et); return le; } + re = exp_null(sql->sa, et); exp_setname(sql->sa, re, exp_relname(e), exp_name(e)); - if (subtype_cmp(et, exp_subtype(re)) != 0) - re = exp_convert(sql->sa, re, exp_subtype(re), et); return re; } } @@ -3098,17 +3092,15 @@ exp_simplify_math( mvc *sql, sql_exp *e, /* 0*a = 0 */ if (exp_is_atom(le) && exp_is_zero(sql, le) && exp_is_atom(re) && exp_is_not_null(sql, re)) { _______________________________________________ checkin-list mailing list checkin-list@monetdb.org https://www.monetdb.org/mailman/listinfo/checkin-list