Changeset: 7298a3b335b8 for MonetDB URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=7298a3b335b8 Modified Files: sql/backends/monet5/sql_gencode.c sql/backends/monet5/sql_statement.c sql/common/sql_types.c sql/server/rel_optimizer.c sql/server/rel_select.c sql/test/BugDay_2005-11-09_2.9.3/Tests/sql_server_crash.SF-1080024.stable.out sql/test/BugTracker-2012/Tests/case_evaluated_too_early.Bug-3186.stable.out sql/test/BugTracker-2018/Tests/sqlitelogictest-complex-case-nullif-coalesce.Bug-6565.stable.out sql/test/BugTracker-2018/Tests/sqlitelogictest-select-errors.Bug-6600.stable.out sql/test/SQLancer/Tests/sqlancer03.stable.out sql/test/Tests/coalesce.stable.out sql/test/emptydb/Tests/check.stable.out sql/test/emptydb/Tests/check.stable.out.32bit sql/test/emptydb/Tests/check.stable.out.int128 sql/test/sys-schema/Tests/systemfunctions.stable.out sql/test/sys-schema/Tests/systemfunctions.stable.out.int128 Branch: Jun2020 Log Message:
moved coalesce and nullif into 'virtual' functions. They are now rewritten into binary statements (mal) during code generation. Solves crash of sqlancer. diffs (truncated from 907 to 300 lines): diff --git a/sql/backends/monet5/sql_gencode.c b/sql/backends/monet5/sql_gencode.c --- a/sql/backends/monet5/sql_gencode.c +++ b/sql/backends/monet5/sql_gencode.c @@ -1005,9 +1005,16 @@ monet5_resolve_function(ptr M, sql_func int clientID = *(int*) M; str mname = getName(f->mod), fname = getName(f->imp); + /* sql.internal doesn't exist */ + if (!fname && mname == sqlRef && f->imp && strcmp(f->imp, "internal") == 0) + return 1; + if (!mname || !fname) return 0; + /* sql.internal doesn't exist */ + if (strcmp(mname, "sql") == 0 && strcmp(fname,"internal") == 0) + return 1; /* Some SQL functions MAL mapping such as count(*) aggregate, the number of arguments don't match */ if (mname == calcRef && fname == getName("=")) return 1; diff --git a/sql/backends/monet5/sql_statement.c b/sql/backends/monet5/sql_statement.c --- a/sql/backends/monet5/sql_statement.c +++ b/sql/backends/monet5/sql_statement.c @@ -3070,77 +3070,120 @@ stmt_Nop(backend *be, stmt *ops, sql_sub } } - if (backend_create_subfunc(be, f, ops->op4.lval) < 0) - return NULL; - mod = sql_func_mod(f->func); - fimp = sql_func_imp(f->func); - if (o && o->nrcols > 0 && f->func->type != F_LOADER && f->func->type != F_PROC) { - sql_subtype *res = f->res->h->data; - fimp = convertMultiplexFcn(fimp); - q = NULL; - if (strcmp(fimp, "rotate_xor_hash") == 0 && - strcmp(mod, calcRef) == 0 && - (q = newStmt(mb, mkeyRef, bulk_rotate_xor_hashRef)) == NULL) - return NULL; - if (!q) { - if (f->func->type == F_UNION) - q = newStmt(mb, batmalRef, multiplexRef); - else - q = newStmt(mb, malRef, multiplexRef); - if (q == NULL) - return NULL; - setVarType(mb, getArg(q, 0), newBatType(res->type->localtype)); - setVarUDFtype(mb, getArg(q, 0)); - q = pushStr(mb, q, mod); - q = pushStr(mb, q, fimp); - } else { - setVarType(mb, getArg(q, 0), newBatType(res->type->localtype)); - setVarUDFtype(mb, getArg(q, 0)); + /* handle coalesce and nullif */ + if (list_length(ops->op4.lval) == 2 && + f->func->mod && strcmp(f->func->mod, "sql") == 0 && f->func->imp && strcmp(f->func->imp, "internal") == 0) { + stmt *e1 = ops->op4.lval->h->data; + stmt *e2 = ops->op4.lval->h->next->data; + int nrcols = 0; + + nrcols = e1->nrcols>e2->nrcols ? e1->nrcols:e2->nrcols; + /* nrcols */ + //coalesce(e1,e2) -> ifthenelse(not(isnil(e1)),e1,e2) + if (strcmp(f->func->base.name, "coalesce") == 0) { + str mod = (!nrcols)?calcRef:batcalcRef; + q = newStmt(mb, e1->nrcols?mod:calcRef, "isnil"); + q = pushArgument(mb, q, e1->nr); + int nr = getDestVar(q); + + q = newStmt(mb, e1->nrcols?mod:calcRef, "not"); + q = pushArgument(mb, q, nr); + nr = getDestVar(q); + + q = newStmt(mb, mod, "ifthenelse"); + q = pushArgument(mb, q, nr); + q = pushArgument(mb, q, e1->nr); + q = pushArgument(mb, q, e2->nr); } - } else { - fimp = convertOperator(fimp); - q = newStmt(mb, mod, fimp); - - if (f->res && list_length(f->res)) { - sql_subtype *res = f->res->h->data; - - setVarType(mb, getArg(q, 0), res->type->localtype); - setVarUDFtype(mb, getArg(q, 0)); + //nullif(e1,e2) -> ifthenelse(e1==e2),NULL,e1) + if (strcmp(f->func->base.name, "nullif") == 0) { + str mod = (!nrcols)?calcRef:batcalcRef; + sql_subtype *t = tail_type(e1); + int tt = t->type->localtype; + q = newStmt(mb, mod, "=="); + q = pushArgument(mb, q, e1->nr); + q = pushArgument(mb, q, e2->nr); + int nr = getDestVar(q); + + q = newStmt(mb, mod, "ifthenelse"); + q = pushArgument(mb, q, nr); + q = pushNil(mb, q, tt); + q = pushArgument(mb, q, e1->nr); } } - if (LANG_EXT(f->func->lang)) - q = pushPtr(mb, q, f); - if (f->func->lang == FUNC_LANG_C) { - q = pushBit(mb, q, 0); - } else if (f->func->lang == FUNC_LANG_CPP) { - q = pushBit(mb, q, 1); - } - if (f->func->lang == FUNC_LANG_R || f->func->lang >= FUNC_LANG_PY || - f->func->lang == FUNC_LANG_C || f->func->lang == FUNC_LANG_CPP) { - q = pushStr(mb, q, f->func->query); - } - /* first dynamic output of copy* functions */ - if (f->func->type == F_UNION || (f->func->type == F_LOADER && f->res != NULL)) - q = table_func_create_result(mb, q, f->func, f->res); - if (list_length(ops->op4.lval)) - tpe = tail_type(ops->op4.lval->h->data); - if (strcmp(fimp, "round") == 0 && tpe && tpe->type->eclass == EC_DEC) - special = 1; - - for (n = ops->op4.lval->h; n; n = n->next) { - stmt *op = n->data; - - if (!op) - q = pushNil(mb, q, TYPE_bat); - else - q = pushArgument(mb, q, op->nr); - if (op && special) { - q = pushInt(mb, q, tpe->digits); - setVarUDFtype(mb, getArg(q, q->argc-1)); - q = pushInt(mb, q, tpe->scale); - setVarUDFtype(mb, getArg(q, q->argc-1)); + if (!q) { + if (backend_create_subfunc(be, f, ops->op4.lval) < 0) + return NULL; + mod = sql_func_mod(f->func); + fimp = sql_func_imp(f->func); + if (o && o->nrcols > 0 && f->func->type != F_LOADER && f->func->type != F_PROC) { + sql_subtype *res = f->res->h->data; + fimp = convertMultiplexFcn(fimp); + q = NULL; + if (strcmp(fimp, "rotate_xor_hash") == 0 && + strcmp(mod, calcRef) == 0 && + (q = newStmt(mb, mkeyRef, bulk_rotate_xor_hashRef)) == NULL) + return NULL; + if (!q) { + if (f->func->type == F_UNION) + q = newStmt(mb, batmalRef, multiplexRef); + else + q = newStmt(mb, malRef, multiplexRef); + if (q == NULL) + return NULL; + setVarType(mb, getArg(q, 0), newBatType(res->type->localtype)); + setVarUDFtype(mb, getArg(q, 0)); + q = pushStr(mb, q, mod); + q = pushStr(mb, q, fimp); + } else { + setVarType(mb, getArg(q, 0), newBatType(res->type->localtype)); + setVarUDFtype(mb, getArg(q, 0)); + } + } else { + fimp = convertOperator(fimp); + q = newStmt(mb, mod, fimp); + + if (f->res && list_length(f->res)) { + sql_subtype *res = f->res->h->data; + + setVarType(mb, getArg(q, 0), res->type->localtype); + setVarUDFtype(mb, getArg(q, 0)); + } } - special = 0; + if (LANG_EXT(f->func->lang)) + q = pushPtr(mb, q, f); + if (f->func->lang == FUNC_LANG_C) { + q = pushBit(mb, q, 0); + } else if (f->func->lang == FUNC_LANG_CPP) { + q = pushBit(mb, q, 1); + } + if (f->func->lang == FUNC_LANG_R || f->func->lang >= FUNC_LANG_PY || + f->func->lang == FUNC_LANG_C || f->func->lang == FUNC_LANG_CPP) { + q = pushStr(mb, q, f->func->query); + } + /* first dynamic output of copy* functions */ + if (f->func->type == F_UNION || (f->func->type == F_LOADER && f->res != NULL)) + q = table_func_create_result(mb, q, f->func, f->res); + if (list_length(ops->op4.lval)) + tpe = tail_type(ops->op4.lval->h->data); + if (strcmp(fimp, "round") == 0 && tpe && tpe->type->eclass == EC_DEC) + special = 1; + + for (n = ops->op4.lval->h; n; n = n->next) { + stmt *op = n->data; + + if (!op) + q = pushNil(mb, q, TYPE_bat); + else + q = pushArgument(mb, q, op->nr); + if (op && special) { + q = pushInt(mb, q, tpe->digits); + setVarUDFtype(mb, getArg(q, q->argc-1)); + q = pushInt(mb, q, tpe->scale); + setVarUDFtype(mb, getArg(q, q->argc-1)); + } + special = 0; + } } if (q) { diff --git a/sql/common/sql_types.c b/sql/common/sql_types.c --- a/sql/common/sql_types.c +++ b/sql/common/sql_types.c @@ -1314,6 +1314,8 @@ sqltypeinit( sql_allocator *sa) sql_create_func(sa, "least", "calc", "min_no_nil", FALSE, SCALE_FIX, 0, ANY, 2, ANY, ANY); sql_create_func(sa, "greatest", "calc", "max_no_nil", FALSE, SCALE_FIX, 0, ANY, 2, ANY, ANY); sql_create_func(sa, "ifthenelse", "calc", "ifthenelse", FALSE, SCALE_FIX, 0, ANY, 3, BIT, ANY, ANY); + sql_create_func(sa, "nullif", "sql", "internal", FALSE, SCALE_FIX, 0, ANY, 2, ANY, ANY); + sql_create_func(sa, "coalesce", "sql", "internal", FALSE, SCALE_FIX, 0, ANY, 2, ANY, ANY); /* sum for numerical and decimals */ sql_create_aggr(sa, "sum", "aggr", "sum", LargestINT, 1, BTE); 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,6 +3067,11 @@ exp_simplify_math( mvc *sql, sql_exp *e, * min_no_nil or max_no_nil), in which case we * ignore the NULL and return the other * value */ + + /* for both nullif and coalesce don't rewrite the NULL handling */ + if (f && f->func && f->func->imp && strcmp(f->func->imp, "internal") == 0) + return e; + if (exp_is_atom(le) && exp_is_null(sql, le)) { (*changes)++; if (f && f->func && f->func->imp && strstr(f->func->imp, "_no_nil") != NULL) { diff --git a/sql/server/rel_select.c b/sql/server/rel_select.c --- a/sql/server/rel_select.c +++ b/sql/server/rel_select.c @@ -3772,7 +3772,7 @@ rel_aggr(sql_query *query, sql_rel **rel } static sql_exp * -rel_case(sql_query *query, sql_rel **rel, tokens token, symbol *opt_cond, dlist *when_search_list, symbol *opt_else, int f) +rel_case(sql_query *query, sql_rel **rel, symbol *opt_cond, dlist *when_search_list, symbol *opt_else, int f) { mvc *sql = query->sql; sql_subtype *tpe = NULL; @@ -3785,107 +3785,27 @@ rel_case(sql_query *query, sql_rel **rel exp_kind ek = {type_value, card_column, FALSE}; sql_find_subtype(&bt, "boolean", 0, 0); - if (dn) { + for (dn = when_search_list->h; dn; dn = dn->next) { sql_exp *cond = NULL, *result = NULL; - /* NULLIF(e1,e2) == CASE WHEN e1=e2 THEN NULL ELSE e1 END */ - if (token == SQL_NULLIF) { - sql_exp *e1, *e2; - - if (!(e1 = rel_value_exp(query, rel, dn->data.sym, f, ek))) - return NULL; - if (!(e2 = rel_value_exp(query, rel, dn->next->data.sym, f, ek))) - return NULL; - - cond = rel_binop_(sql, rel ? *rel : NULL, e1, e2, NULL, "=", card_value); - result = exp_null(sql->sa, exp_subtype(e1)); - else_exp = exp_ref_save(sql, e1); /* ELSE case */ - /* COALESCE(e1,e2) == CASE WHEN e1 - IS NOT NULL THEN e1 ELSE e2 END */ - } else if (token == SQL_COALESCE) { - cond = rel_value_exp(query, rel, dn->data.sym, f, ek); - - if (cond) { - sql_exp *le; - - result = exp_ref_save(sql, cond); - if (!(le = rel_unop_(sql, rel ? *rel : NULL, cond, NULL, "isnull", card_value))) - return NULL; - set_has_no_nil(le); - if (!(cond = rel_unop_(sql, rel ? *rel : NULL, le, NULL, "not", card_value))) - return NULL; - set_has_no_nil(cond); - } - } else { - dlist *when = dn->data.sym->data.lval; - - if (opt_cond) { - sql_exp *l, *r; - - if (!(l = rel_value_exp(query, rel, opt_cond, f, ek))) - return NULL; - if (!(r = rel_value_exp(query, rel, when->h->data.sym, f, ek))) - return NULL; - if (rel_convert_types(sql, rel ? *rel : NULL, rel ? *rel : NULL, &l, &r, 1, type_equal) < 0) - return NULL; - cond = rel_binop_(sql, rel ? *rel : NULL, l, r, NULL, "=", card_value); - } else { _______________________________________________ checkin-list mailing list checkin-list@monetdb.org https://www.monetdb.org/mailman/listinfo/checkin-list