Changeset: 38bf565fc89a for MonetDB URL: https://dev.monetdb.org/hg/MonetDB/rev/38bf565fc89a Modified Files: sql/common/sql_types.c sql/server/rel_dump.c sql/server/rel_exp.c sql/server/rel_exp.h sql/server/rel_select.c sql/test/SQLancer/Tests/sqlancer19.SQL.py sql/test/SQLancer/Tests/sqlancer20.SQL.py Branch: default Log Message:
Two fixes for remote plans. For functions with more than 2 inputs, compute output digits and scale if needed. When binding an inout function make sure the result number of digits is equal to the first argument diffs (240 lines): 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 @@ -493,7 +493,7 @@ sql_dup_subfunc(sql_allocator *sa, sql_f /* same scale as the input */ if (a && a->scale > mscale) mscale = a->scale; - if (a && f->fix_scale == INOUT) + if (a && f->fix_scale == INOUT && tn == ops->h) mdigits = a->digits; } } diff --git a/sql/server/rel_dump.c b/sql/server/rel_dump.c --- a/sql/server/rel_dump.c +++ b/sql/server/rel_dump.c @@ -1235,7 +1235,7 @@ exp_read(mvc *sql, sql_rel *lrel, sql_re if (f && !execute_priv(sql, f->func)) return sql_error(sql, -1, SQLSTATE(42000) "Function: no privilege to call function '%s%s%s %d'\n", tname ? tname : "", tname ? "." : "", cname, nops); /* apply scale fixes if needed */ - if (f && f->func->fix_scale != SCALE_NONE) { + if (f && f->func->type != F_ANALYTIC) { if (list_length(exps) == 1) { if (f->func->fix_scale == INOUT) { sql_subtype *t = exp_subtype(exps->h->data); @@ -1290,6 +1290,9 @@ exp_read(mvc *sql, sql_rel *lrel, sql_re res->digits = 0; } } + } else if (list_length(exps) > 2) { + if (!f->func->vararg && !(exps = check_arguments_and_find_largest_any_type(sql, lrel, exps, f, 0))) + return NULL; } } } @@ -1859,6 +1862,8 @@ rel_read(mvc *sql, char *r, int *pos, li if (list_length(outputs) != list_length(sf->func->res)) return sql_error(sql, -1, SQLSTATE(42000) "Table returning function: the number of output parameters don't match the table ones relation outputs: %d != function outputs: %d\n", list_length(outputs), list_length(sf->func->res)); + if (!list_empty(outputs) && !(outputs = check_arguments_and_find_largest_any_type(sql, lrel, outputs, sf, 0))) + return NULL; rel = rel_table_func(sql->sa, lrel, tudf, outputs, TABLE_FROM_RELATION); } else { if (r[*pos] != ')') 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 @@ -2956,6 +2956,73 @@ exp_sum_scales(sql_subfunc *f, sql_exp * } } +list * +check_arguments_and_find_largest_any_type(mvc *sql, sql_rel *rel, list *exps, sql_subfunc *sf, int maybe_zero_or_one) +{ + list *nexps = new_exp_list(sql->sa); + sql_subtype *atp = NULL, super, *res = !list_empty(sf->res) ? sf->res->h->data: NULL; + unsigned int rdigits = 0; /* used for res of type char and varchar */ + + /* find largest any type argument */ + for (node *n = exps->h, *m = sf->func->ops->h; n && m; n = n->next, m = m->next) { + sql_arg *a = m->data; + sql_exp *e = n->data; + sql_subtype *t = exp_subtype(e); + + if (a->type.type->eclass == EC_ANY) { + if (t && atp) { + result_datatype(&super, t, atp); + atp = &super; + } else if (t) { + atp = t; + } + } + } + if (atp && atp->type->localtype == TYPE_void) /* NULL */ + atp = sql_bind_localtype("str"); + for (node *n = exps->h, *m = sf->func->ops->h; n && m; n = n->next, m = m->next) { + sql_arg *a = m->data; + sql_exp *e = n->data; + sql_subtype *ntp = &a->type, *t = exp_subtype(e); + + if (a->type.type->eclass == EC_ANY && atp) + ntp = sql_create_subtype(sql->sa, atp->type, atp->digits, atp->scale); + else if (t && ntp->digits == 0 && (!strcmp(a->type.type->base.name, "char") || !strcmp(a->type.type->base.name, "varchar"))) + ntp = sql_create_subtype(sql->sa, a->type.type, type_digits_to_char_digits(t), 0); + if (!(e = exp_check_type(sql, ntp, rel, e, type_equal))) + return NULL; + if (maybe_zero_or_one && e->card > CARD_ATOM) { + sql_subfunc *zero_or_one = sql_bind_func(sql, "sys", "zero_or_one", exp_subtype(e), NULL, F_AGGR); + e = exp_aggr1(sql->sa, e, zero_or_one, 0, 0, CARD_ATOM, has_nil(e)); + } + append(nexps, e); + + /* for (var)char returning functions the output type will be the biggest string found except for fix_scale cases */ + if (res && res->digits == 0 && (t = exp_subtype(e)) && (!strcmp(res->type->base.name, "char") || !strcmp(res->type->base.name, "varchar"))) { + unsigned int tdigits = type_digits_to_char_digits(t); + if (sf->func->fix_scale == DIGITS_ADD) { + unsigned int nvalue = rdigits + tdigits; + if (nvalue < rdigits || nvalue >= (unsigned int) INT32_MAX) + return sql_error(sql, 02, SQLSTATE(42000) "SELECT: output number of digits for %s is too large", sf->func->base.name); + rdigits = nvalue; + } else if (sf->func->fix_scale == INOUT) { + if (n == exps->h) + rdigits = tdigits; + } else { + rdigits = sql_max(rdigits, tdigits); + } + } + } + /* dirty hack */ + if (sf->func->type != F_PROC && sf->func->type != F_UNION && sf->func->type != F_LOADER && res) { + if (res->type->eclass == EC_ANY && atp) + sf->res->h->data = sql_create_subtype(sql->sa, atp->type, atp->digits, atp->scale); + else if (res->digits == 0 && (!strcmp(res->type->base.name, "char") || !strcmp(res->type->base.name, "varchar"))) + res->digits = rdigits; + } + return nexps; +} + int exp_aggr_is_count(sql_exp *e) { 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 @@ -196,6 +196,7 @@ extern atom *exp_flatten(mvc *sql, sql_e extern sql_exp *exp_scale_algebra(mvc *sql, sql_subfunc *f, sql_rel *rel, sql_exp *l, sql_exp *r); extern void exp_sum_scales(sql_subfunc *f, sql_exp *l, sql_exp *r); +extern list *check_arguments_and_find_largest_any_type(mvc *sql, sql_rel *rel, list *exps, sql_subfunc *sf, int maybe_zero_or_one); extern int exp_aggr_is_count(sql_exp *e); extern list *check_distinct_exp_names(mvc *sql, list *exps); 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 @@ -452,73 +452,6 @@ score_func( sql_subfunc *sf, list *tl) return score; } -static list * -check_arguments_and_find_largest_any_type(mvc *sql, sql_rel *rel, list *exps, sql_subfunc *sf, int maybe_zero_or_one) -{ - list *nexps = new_exp_list(sql->sa); - sql_subtype *atp = NULL, super, *res = !list_empty(sf->res) ? sf->res->h->data: NULL; - unsigned int rdigits = 0; /* used for res of type char and varchar */ - - /* find largest any type argument */ - for (node *n = exps->h, *m = sf->func->ops->h; n && m; n = n->next, m = m->next) { - sql_arg *a = m->data; - sql_exp *e = n->data; - sql_subtype *t = exp_subtype(e); - - if (a->type.type->eclass == EC_ANY) { - if (t && atp) { - result_datatype(&super, t, atp); - atp = &super; - } else if (t) { - atp = t; - } - } - } - if (atp && atp->type->localtype == TYPE_void) /* NULL */ - atp = sql_bind_localtype("str"); - for (node *n = exps->h, *m = sf->func->ops->h; n && m; n = n->next, m = m->next) { - sql_arg *a = m->data; - sql_exp *e = n->data; - sql_subtype *ntp = &a->type, *t = exp_subtype(e); - - if (a->type.type->eclass == EC_ANY && atp) - ntp = sql_create_subtype(sql->sa, atp->type, atp->digits, atp->scale); - else if (t && ntp->digits == 0 && (!strcmp(a->type.type->base.name, "char") || !strcmp(a->type.type->base.name, "varchar"))) - ntp = sql_create_subtype(sql->sa, a->type.type, type_digits_to_char_digits(t), 0); - if (!(e = exp_check_type(sql, ntp, rel, e, type_equal))) - return NULL; - if (maybe_zero_or_one && e->card > CARD_ATOM) { - sql_subfunc *zero_or_one = sql_bind_func(sql, "sys", "zero_or_one", exp_subtype(e), NULL, F_AGGR); - e = exp_aggr1(sql->sa, e, zero_or_one, 0, 0, CARD_ATOM, has_nil(e)); - } - append(nexps, e); - - /* for (var)char returning functions the output type will be the biggest string found except for fix_scale cases */ - if (res && res->digits == 0 && (t = exp_subtype(e)) && (!strcmp(res->type->base.name, "char") || !strcmp(res->type->base.name, "varchar"))) { - unsigned int tdigits = type_digits_to_char_digits(t); - if (sf->func->fix_scale == DIGITS_ADD) { - unsigned int nvalue = rdigits + tdigits; - if (nvalue < rdigits || nvalue >= (unsigned int) INT32_MAX) - return sql_error(sql, 02, SQLSTATE(42000) "SELECT: output number of digits for %s is too large", sf->func->base.name); - rdigits = nvalue; - } else if (sf->func->fix_scale == INOUT) { - if (n == exps->h) - rdigits = tdigits; - } else { - rdigits = sql_max(rdigits, tdigits); - } - } - } - /* dirty hack */ - if (sf->func->type != F_PROC && sf->func->type != F_UNION && sf->func->type != F_LOADER && res) { - if (res->type->eclass == EC_ANY && atp) - sf->res->h->data = sql_create_subtype(sql->sa, atp->type, atp->digits, atp->scale); - else if (res->digits == 0 && (!strcmp(res->type->base.name, "char") || !strcmp(res->type->base.name, "varchar"))) - res->digits = rdigits; - } - return nexps; -} - static char * nary_function_arg_types_2str(mvc *sql, list* types, int N) { diff --git a/sql/test/SQLancer/Tests/sqlancer19.SQL.py b/sql/test/SQLancer/Tests/sqlancer19.SQL.py --- a/sql/test/SQLancer/Tests/sqlancer19.SQL.py +++ b/sql/test/SQLancer/Tests/sqlancer19.SQL.py @@ -95,6 +95,14 @@ with SQLTestCase() as cli: .assertSucceeded().assertDataResultMatch([(Decimal('0.02000'),)]) cli.execute("SELECT CAST(2 AS DECIMAL) * 0.010 FROM rt3 where rt3.c0 = 1;") \ .assertSucceeded().assertDataResultMatch([(Decimal('0.02000'),)]) + cli.execute("SELECT \"insert\"('99', 5, 8, '10S') FROM t3 where t3.c0 = 1;") \ + .assertSucceeded().assertDataResultMatch([("9910S",)]) + cli.execute("SELECT \"insert\"('99', 5, 8, '10S') FROM rt3 where rt3.c0 = 1;") \ + .assertSucceeded().assertDataResultMatch([("9910S",)]) + cli.execute("SELECT greatest('69', splitpart('', '191', 2)) FROM t3 where t3.c0 = 1;") \ + .assertSucceeded().assertDataResultMatch([('69',)]) + cli.execute("SELECT greatest('69', splitpart('', '191', 2)) FROM rt3 where rt3.c0 = 1;") \ + .assertSucceeded().assertDataResultMatch([('69',)]) cli.execute("SELECT t3.c0 FROM t3 where (t3.c0) NOT IN (0.07564294, 211.0, 1, 2) ORDER BY t3.c0;") \ .assertSucceeded().assertDataResultMatch([(5,),(5,),(7,)]) cli.execute("SELECT rt3.c0 FROM rt3 where (rt3.c0) NOT IN (0.07564294, 211.0, 1, 2) ORDER BY rt3.c0;") \ diff --git a/sql/test/SQLancer/Tests/sqlancer20.SQL.py b/sql/test/SQLancer/Tests/sqlancer20.SQL.py --- a/sql/test/SQLancer/Tests/sqlancer20.SQL.py +++ b/sql/test/SQLancer/Tests/sqlancer20.SQL.py @@ -23,10 +23,6 @@ with SQLTestCase() as cli: .assertSucceeded().assertDataResultMatch([(Decimal('0.002'),)]) cli.execute("SELECT CAST(2 AS DECIMAL) & CAST(3 AS DOUBLE) FROM rt1 where rt1.c0 = 1;") \ .assertSucceeded().assertDataResultMatch([(Decimal('0.002'),)]) - cli.execute("SELECT greatest('69', splitpart('', '191', 2)) FROM t1 where t1.c0 = 1;") \ - .assertSucceeded().assertDataResultMatch([('69',)]) - cli.execute("SELECT greatest('69', splitpart('', '191', 2)) FROM rt1 where rt1.c0 = 1;") \ - .assertSucceeded().assertDataResultMatch([('69',)]) # Issues related to comparisons not being correctly delimited on plans, which causes ambiguity cli.execute("SELECT TRUE BETWEEN (TRUE BETWEEN FALSE AND FALSE) AND TRUE FROM t1 where t1.c0 = 1;") \ _______________________________________________ checkin-list mailing list checkin-list@monetdb.org https://www.monetdb.org/mailman/listinfo/checkin-list