diff options
Diffstat (limited to 'sql/item_func.cc')
-rw-r--r-- | sql/item_func.cc | 2233 |
1 files changed, 1857 insertions, 376 deletions
diff --git a/sql/item_func.cc b/sql/item_func.cc index 4e0b6d8813f..296083b256e 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -28,6 +28,9 @@ #include <time.h> #include <ft_global.h> +#include "sp_head.h" +#include "sp_rcontext.h" +#include "sp.h" bool check_reserved_words(LEX_STRING *name) { @@ -42,10 +45,10 @@ bool check_reserved_words(LEX_STRING *name) static void my_coll_agg_error(DTCollation &c1, DTCollation &c2, const char *fname) { - my_error(ER_CANT_AGGREGATE_2COLLATIONS,MYF(0), - c1.collation->name,c1.derivation_name(), - c2.collation->name,c2.derivation_name(), - fname); + my_error(ER_CANT_AGGREGATE_2COLLATIONS, MYF(0), + c1.collation->name, c1.derivation_name(), + c2.collation->name, c2.derivation_name(), + fname); } static void my_coll_agg_error(DTCollation &c1, @@ -53,11 +56,11 @@ static void my_coll_agg_error(DTCollation &c1, DTCollation &c3, const char *fname) { - my_error(ER_CANT_AGGREGATE_3COLLATIONS,MYF(0), - c1.collation->name,c1.derivation_name(), - c2.collation->name,c2.derivation_name(), - c3.collation->name,c3.derivation_name(), - fname); + my_error(ER_CANT_AGGREGATE_3COLLATIONS, MYF(0), + c1.collation->name, c1.derivation_name(), + c2.collation->name, c2.derivation_name(), + c3.collation->name, c3.derivation_name(), + fname); } @@ -71,7 +74,7 @@ static void my_coll_agg_error(Item** args, uint count, const char *fname) args[2]->collation, fname); else - my_error(ER_CANT_AGGREGATE_NCOLLATIONS,MYF(0),fname); + my_error(ER_CANT_AGGREGATE_NCOLLATIONS, MYF(0), fname); } @@ -158,7 +161,7 @@ bool Item_func::agg_arg_charsets(DTCollation &coll, } THD *thd= current_thd; - Item_arena *arena, backup; + Query_arena *arena, backup; bool res= FALSE; /* In case we're in statement prepare, create conversion item @@ -187,7 +190,8 @@ bool Item_func::agg_arg_charsets(DTCollation &coll, res= TRUE; break; // we cannot return here, we need to restore "arena". } - conv->fix_fields(thd, 0, &conv); + if ((*arg)->type() == FIELD_ITEM) + ((Item_field *)(*arg))->no_const_subst= 1; /* If in statement prepare, then we create a converter for two constant items, do it once and then reuse it. @@ -202,6 +206,11 @@ bool Item_func::agg_arg_charsets(DTCollation &coll, *arg= conv; else thd->change_item_tree(arg, conv); + /* + We do not check conv->fixed, because Item_func_conv_charset which can + be return by safe_charset_converter can't be fixed at creation + */ + conv->fix_fields(thd, arg); } if (arena) thd->restore_backup_item_arena(arena, &backup); @@ -214,15 +223,16 @@ void Item_func::set_arguments(List<Item> &list) { allowed_arg_cols= 1; arg_count=list.elements; - if ((args=(Item**) sql_alloc(sizeof(Item*)*arg_count))) + args= tmp_arg; // If 2 arguments + if (arg_count <= 2 || (args=(Item**) sql_alloc(sizeof(Item*)*arg_count))) { - uint i=0; List_iterator_fast<Item> li(list); Item *item; + Item **save_args= args; while ((item=li++)) { - args[i++]= item; + *(save_args++)= item; with_sum_func|=item->with_sum_func; } } @@ -263,7 +273,6 @@ Item_func::Item_func(THD *thd, Item_func *item) SYNOPSIS: fix_fields() thd Thread object - tables List of all open tables involved in the query ref Pointer to where this object is used. This reference is used if we want to replace this object with another one (for example in the summary functions). @@ -287,12 +296,12 @@ Item_func::Item_func(THD *thd, Item_func *item) item. RETURN VALUES - 0 ok - 1 Got error. Stored with my_error(). + FALSE ok + TRUE Got error. Stored with my_error(). */ bool -Item_func::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) +Item_func::fix_fields(THD *thd, Item **ref) { DBUG_ASSERT(fixed == 0); Item **arg,**arg_end; @@ -303,8 +312,8 @@ Item_func::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) used_tables_cache= not_null_tables_cache= 0; const_item_cache=1; - if (check_stack_overrun(thd, buff)) - return 1; // Fatal error if flag is set! + if (check_stack_overrun(thd, STACK_MIN_SIZE, buff)) + return TRUE; // Fatal error if flag is set! if (arg_count) { // Print purify happy for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++) @@ -314,9 +323,8 @@ Item_func::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) We can't yet set item to *arg as fix_fields may change *arg We shouldn't call fix_fields() twice, so check 'fixed' field first */ - if ((!(*arg)->fixed && (*arg)->fix_fields(thd, tables, arg))) - return 1; /* purecov: inspected */ - + if ((!(*arg)->fixed && (*arg)->fix_fields(thd, arg))) + return TRUE; /* purecov: inspected */ item= *arg; if (allowed_arg_cols) @@ -342,10 +350,10 @@ Item_func::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) } } fix_length_and_dec(); - if (thd->net.last_errno) // An error inside fix_length_and_dec occured - return 1; + if (thd->net.report_error) // An error inside fix_length_and_dec occured + return TRUE; fixed= 1; - return 0; + return FALSE; } bool Item_func::walk (Item_processor processor, byte *argument) @@ -362,6 +370,71 @@ bool Item_func::walk (Item_processor processor, byte *argument) return (this->*processor)(argument); } +void Item_func::traverse_cond(Cond_traverser traverser, + void *argument, traverse_order order) +{ + if (arg_count) + { + Item **arg,**arg_end; + + switch (order) { + case(PREFIX): + (*traverser)(this, argument); + for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++) + { + (*arg)->traverse_cond(traverser, argument, order); + } + break; + case (POSTFIX): + for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++) + { + (*arg)->traverse_cond(traverser, argument, order); + } + (*traverser)(this, argument); + } + } +} + + + +/* + Transform an Item_func object with a transformer callback function + + SYNOPSIS + transform() + transformer the transformer callback function to be applied to the nodes + of the tree of the object + argument parameter to be passed to the transformer + + DESCRIPTION + The function recursively applies the transform method with the + same transformer to each argument the function. + If the call of the method for a member item returns a new item + the old item is substituted for a new one. + After this the transform method is applied to the root node + of the Item_func object. + + RETURN VALUES + Item returned as the result of transformation of the root node +*/ + +Item *Item_func::transform(Item_transformer transformer, byte *argument) +{ + if (arg_count) + { + Item **arg,**arg_end; + for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++) + { + Item *new_item= (*arg)->transform(transformer, argument); + if (!new_item) + return 0; + if (*arg != new_item) + current_thd->change_item_tree(arg, new_item); + } + } + return (this->*transformer)(argument); +} + /* See comments in Item_cmp_func::split_sum_func() */ @@ -451,6 +524,7 @@ bool Item_func::eq(const Item *item, bool binary_cmp) const return 1; } + Field *Item_func::tmp_table_field(TABLE *t_arg) { Field *res; @@ -469,25 +543,35 @@ Field *Item_func::tmp_table_field(TABLE *t_arg) res= new Field_double(max_length, maybe_null, name, t_arg, decimals); break; case STRING_RESULT: - if (max_length > 255) - res= new Field_blob(max_length, maybe_null, name, t_arg, collation.collation); - else - res= new Field_string(max_length, maybe_null, name, t_arg, collation.collation); + res= make_string_field(t_arg); + break; + case DECIMAL_RESULT: + res= new Field_new_decimal(my_decimal_precision_to_length(decimal_precision(), + decimals, + unsigned_flag), + maybe_null, name, t_arg, decimals, unsigned_flag); break; case ROW_RESULT: default: - // This case should never be choosen + // This case should never be chosen DBUG_ASSERT(0); break; } return res; } +my_decimal *Item_func::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed); + int2my_decimal(E_DEC_FATAL_ERROR, val_int(), unsigned_flag, decimal_value); + return decimal_value; +} + String *Item_real_func::val_str(String *str) { DBUG_ASSERT(fixed == 1); - double nr=val(); + double nr= val_real(); if (null_value) return 0; /* purecov: inspected */ str->set(nr,decimals, &my_charset_bin); @@ -495,38 +579,121 @@ String *Item_real_func::val_str(String *str) } -String *Item_num_func::val_str(String *str) +my_decimal *Item_real_func::val_decimal(my_decimal *decimal_value) { - DBUG_ASSERT(fixed == 1); - if (hybrid_type == INT_RESULT) + DBUG_ASSERT(fixed); + double nr= val_real(); + if (null_value) + return 0; /* purecov: inspected */ + double2my_decimal(E_DEC_FATAL_ERROR, nr, decimal_value); + return decimal_value; +} + + +void Item_func::fix_num_length_and_dec() +{ + decimals= 0; + for (uint i=0 ; i < arg_count ; i++) { - longlong nr=val_int(); - if (null_value) - return 0; /* purecov: inspected */ - if (!unsigned_flag) - str->set(nr,&my_charset_bin); - else - str->set((ulonglong) nr,&my_charset_bin); + set_if_bigger(decimals, args[i]->decimals); } - else + max_length= float_length(decimals); +} + + +void Item_func_numhybrid::fix_num_length_and_dec() +{} + + +/* + Set max_length/decimals of function if function is fixed point and + result length/precision depends on argument ones + + SYNOPSIS + Item_func::count_decimal_length() +*/ + +void Item_func::count_decimal_length() +{ + int max_int_part= 0; + decimals= 0; + unsigned_flag= 1; + for (uint i=0 ; i < arg_count ; i++) { - double nr=val(); - if (null_value) - return 0; /* purecov: inspected */ - str->set(nr,decimals,&my_charset_bin); + set_if_bigger(decimals, args[i]->decimals); + set_if_bigger(max_int_part, args[i]->decimal_int_part()); + set_if_smaller(unsigned_flag, args[i]->unsigned_flag); } - return str; + int precision= min(max_int_part + decimals, DECIMAL_MAX_PRECISION); + max_length= my_decimal_precision_to_length(precision, decimals, + unsigned_flag); } -void Item_func::fix_num_length_and_dec() +/* + Set max_length of if it is maximum length of its arguments + + SYNOPSIS + Item_func::count_only_length() +*/ + +void Item_func::count_only_length() { - decimals=0; + max_length= 0; + unsigned_flag= 0; + for (uint i=0 ; i < arg_count ; i++) + { + set_if_bigger(max_length, args[i]->max_length); + set_if_bigger(unsigned_flag, args[i]->unsigned_flag); + } +} + + +/* + Set max_length/decimals of function if function is floating point and + result length/precision depends on argument ones + + SYNOPSIS + Item_func::count_real_length() +*/ + +void Item_func::count_real_length() +{ + uint32 length= 0; + decimals= 0; + max_length= 0; for (uint i=0 ; i < arg_count ; i++) - set_if_bigger(decimals,args[i]->decimals); - max_length=float_length(decimals); + { + if (decimals != NOT_FIXED_DEC) + { + set_if_bigger(decimals, args[i]->decimals); + set_if_bigger(length, (args[i]->max_length - args[i]->decimals)); + } + set_if_bigger(max_length, args[i]->max_length); + } + if (decimals != NOT_FIXED_DEC) + { + max_length= length; + length+= decimals; + if (length < max_length) // If previous operation gave overflow + max_length= UINT_MAX32; + else + max_length= length; + } +} + + + +void Item_func::signal_divide_by_null() +{ + THD *thd= current_thd; + if (thd->variables.sql_mode & MODE_ERROR_FOR_DIVISION_BY_ZERO) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_DIVISION_BY_ZERO, + ER(ER_DIVISION_BY_ZERO)); + null_value= 1; } + Item *Item_func::get_tmp_table_item(THD *thd) { if (!with_sum_func && !const_item()) @@ -547,45 +714,242 @@ String *Item_int_func::val_str(String *str) return str; } + /* - Change from REAL_RESULT (default) to INT_RESULT if both arguments are - integers + Check arguments here to determine result's type for a numeric + function of two arguments. + + SYNOPSIS + Item_num_op::find_num_type() */ void Item_num_op::find_num_type(void) { - if (args[0]->result_type() == INT_RESULT && - args[1]->result_type() == INT_RESULT) + DBUG_ENTER("Item_num_op::find_num_type"); + DBUG_PRINT("info", ("name %s", func_name())); + DBUG_ASSERT(arg_count == 2); + Item_result r0= args[0]->result_type(); + Item_result r1= args[1]->result_type(); + + if (r0 == REAL_RESULT || r1 == REAL_RESULT || + r0 == STRING_RESULT || r1 ==STRING_RESULT) + { + count_real_length(); + max_length= float_length(decimals); + hybrid_type= REAL_RESULT; + } + else if (r0 == DECIMAL_RESULT || r1 == DECIMAL_RESULT) + { + hybrid_type= DECIMAL_RESULT; + result_precision(); + } + else { + DBUG_ASSERT(r0 == INT_RESULT && r1 == INT_RESULT); + decimals= 0; hybrid_type=INT_RESULT; - unsigned_flag=args[0]->unsigned_flag | args[1]->unsigned_flag; + result_precision(); + } + DBUG_PRINT("info", ("Type: %s", + (hybrid_type == REAL_RESULT ? "REAL_RESULT" : + hybrid_type == DECIMAL_RESULT ? "DECIMAL_RESULT" : + hybrid_type == INT_RESULT ? "INT_RESULT" : + "--ILLEGAL!!!--"))); + DBUG_VOID_RETURN; +} + + +/* + Set result type for a numeric function of one argument + (can be also used by a numeric function of many arguments, if the result + type depends only on the first argument) + + SYNOPSIS + Item_func_num1::find_num_type() +*/ + +void Item_func_num1::find_num_type() +{ + DBUG_ENTER("Item_func_num1::find_num_type"); + DBUG_PRINT("info", ("name %s", func_name())); + switch (hybrid_type= args[0]->result_type()) { + case INT_RESULT: + unsigned_flag= args[0]->unsigned_flag; + break; + case STRING_RESULT: + case REAL_RESULT: + hybrid_type= REAL_RESULT; + max_length= float_length(decimals); + break; + case DECIMAL_RESULT: + break; + default: + DBUG_ASSERT(0); } + DBUG_PRINT("info", ("Type: %s", + (hybrid_type == REAL_RESULT ? "REAL_RESULT" : + hybrid_type == DECIMAL_RESULT ? "DECIMAL_RESULT" : + hybrid_type == INT_RESULT ? "INT_RESULT" : + "--ILLEGAL!!!--"))); + DBUG_VOID_RETURN; } -String *Item_num_op::val_str(String *str) + +void Item_func_num1::fix_num_length_and_dec() +{ + decimals= args[0]->decimals; + max_length= args[0]->max_length; +} + + +void Item_func_numhybrid::fix_length_and_dec() +{ + fix_num_length_and_dec(); + find_num_type(); +} + + +String *Item_func_numhybrid::val_str(String *str) { DBUG_ASSERT(fixed == 1); - if (hybrid_type == INT_RESULT) + switch (hybrid_type) { + case DECIMAL_RESULT: { - longlong nr=val_int(); + my_decimal decimal_value, *val; + if (!(val= decimal_op(&decimal_value))) + return 0; // null is set + my_decimal_round(E_DEC_FATAL_ERROR, val, decimals, FALSE, val); + my_decimal2string(E_DEC_FATAL_ERROR, val, 0, 0, 0, str); + break; + } + case INT_RESULT: + { + longlong nr= int_op(); if (null_value) return 0; /* purecov: inspected */ if (!unsigned_flag) str->set(nr,&my_charset_bin); else str->set((ulonglong) nr,&my_charset_bin); + break; } - else + case REAL_RESULT: { - double nr=val(); + double nr= real_op(); if (null_value) return 0; /* purecov: inspected */ str->set(nr,decimals,&my_charset_bin); + break; + } + case STRING_RESULT: + return str_op(&str_value); + default: + DBUG_ASSERT(0); } return str; } +double Item_func_numhybrid::val_real() +{ + DBUG_ASSERT(fixed == 1); + switch (hybrid_type) { + case DECIMAL_RESULT: + { + my_decimal decimal_value, *val; + double result; + if (!(val= decimal_op(&decimal_value))) + return 0.0; // null is set + my_decimal2double(E_DEC_FATAL_ERROR, val, &result); + return result; + } + case INT_RESULT: + return (double)int_op(); + case REAL_RESULT: + return real_op(); + case STRING_RESULT: + { + char *end_not_used; + int err_not_used; + String *res= str_op(&str_value); + return (res ? my_strntod(res->charset(), (char*) res->ptr(), res->length(), + &end_not_used, &err_not_used) : 0.0); + } + default: + DBUG_ASSERT(0); + } + return 0.0; +} + + +longlong Item_func_numhybrid::val_int() +{ + DBUG_ASSERT(fixed == 1); + switch (hybrid_type) { + case DECIMAL_RESULT: + { + my_decimal decimal_value, *val; + if (!(val= decimal_op(&decimal_value))) + return 0; // null is set + longlong result; + my_decimal2int(E_DEC_FATAL_ERROR, val, unsigned_flag, &result); + return result; + } + case INT_RESULT: + return int_op(); + case REAL_RESULT: + return (longlong)real_op(); + case STRING_RESULT: + { + int err_not_used; + String *res= str_op(&str_value); + char *end= (char*) res->ptr() + res->length(); + CHARSET_INFO *cs= str_value.charset(); + return (res ? (*(cs->cset->strtoll10))(cs, res->ptr(), &end, + &err_not_used) : 0); + } + default: + DBUG_ASSERT(0); + } + return 0; +} + + +my_decimal *Item_func_numhybrid::val_decimal(my_decimal *decimal_value) +{ + my_decimal *val= decimal_value; + DBUG_ASSERT(fixed == 1); + switch (hybrid_type) { + case DECIMAL_RESULT: + val= decimal_op(decimal_value); + break; + case INT_RESULT: + { + longlong result= int_op(); + int2my_decimal(E_DEC_FATAL_ERROR, result, unsigned_flag, decimal_value); + break; + } + case REAL_RESULT: + { + double result= (double)int_op(); + double2my_decimal(E_DEC_FATAL_ERROR, result, decimal_value); + break; + } + case STRING_RESULT: + { + String *res= str_op(&str_value); + str2my_decimal(E_DEC_FATAL_ERROR, (char*) res->ptr(), + res->length(), res->charset(), decimal_value); + break; + } + case ROW_RESULT: + default: + DBUG_ASSERT(0); + } + return val; +} + + void Item_func_signed::print(String *str) { str->append("cast(", 5); @@ -677,26 +1041,121 @@ longlong Item_func_unsigned::val_int() } -double Item_func_plus::val() +String *Item_decimal_typecast::val_str(String *str) { - DBUG_ASSERT(fixed == 1); - double value=args[0]->val()+args[1]->val(); + my_decimal tmp_buf, *tmp= val_decimal(&tmp_buf); + if (null_value) + return NULL; + my_decimal2string(E_DEC_FATAL_ERROR, &tmp_buf, 0, 0, 0, str); + return str; +} + + +double Item_decimal_typecast::val_real() +{ + my_decimal tmp_buf, *tmp= val_decimal(&tmp_buf); + double res; + if (null_value) + return 0.0; + my_decimal2double(E_DEC_FATAL_ERROR, tmp, &res); + return res; +} + + +longlong Item_decimal_typecast::val_int() +{ + my_decimal tmp_buf, *tmp= val_decimal(&tmp_buf); + longlong res; + if (null_value) + return 0; + my_decimal2int(E_DEC_FATAL_ERROR, tmp, unsigned_flag, &res); + return res; +} + + +my_decimal *Item_decimal_typecast::val_decimal(my_decimal *dec) +{ + my_decimal tmp_buf, *tmp= args[0]->val_decimal(&tmp_buf); + if ((null_value= args[0]->null_value)) + return NULL; + my_decimal_round(E_DEC_FATAL_ERROR, tmp, decimals, FALSE, dec); + return dec; +} + + +void Item_decimal_typecast::print(String *str) +{ + str->append("cast(", 5); + args[0]->print(str); + str->append(" as decimal)", 12); +} + + +double Item_func_plus::real_op() +{ + double value= args[0]->val_real() + args[1]->val_real(); if ((null_value=args[0]->null_value || args[1]->null_value)) return 0.0; return value; } -longlong Item_func_plus::val_int() + +longlong Item_func_plus::int_op() { - DBUG_ASSERT(fixed == 1); - if (hybrid_type == INT_RESULT) - { - longlong value=args[0]->val_int()+args[1]->val_int(); - if ((null_value=args[0]->null_value || args[1]->null_value)) - return 0; - return value; - } - return (longlong) Item_func_plus::val(); + longlong value=args[0]->val_int()+args[1]->val_int(); + if ((null_value=args[0]->null_value || args[1]->null_value)) + return 0; + return value; +} + + +/* + Calculate plus of two decimail's + + SYNOPSIS + decimal_op() + decimal_value Buffer that can be used to store result + + RETURN + 0 Value was NULL; In this case null_value is set + # Value of operation as a decimal +*/ + +my_decimal *Item_func_plus::decimal_op(my_decimal *decimal_value) +{ + my_decimal value1, *val1; + my_decimal value2, *val2; + val1= args[0]->val_decimal(&value1); + if ((null_value= args[0]->null_value)) + return 0; + val2= args[1]->val_decimal(&value2); + if (!(null_value= (args[1]->null_value || + my_decimal_add(E_DEC_FATAL_ERROR, decimal_value, val1, + val2) > 1))) + return decimal_value; + return 0; +} + +/* + Set precision of results for additive operations (+ and -) + + SYNOPSIS + Item_func_additive_op::result_precision() +*/ +void Item_func_additive_op::result_precision() +{ + decimals= max(args[0]->decimals, args[1]->decimals); + int max_int_part= max(args[0]->decimal_precision() - args[0]->decimals, + args[1]->decimal_precision() - args[1]->decimals); + int precision= min(max_int_part + 1 + decimals, DECIMAL_MAX_PRECISION); + + /* Integer operations keep unsigned_flag if one of arguments is unsigned */ + if (result_type() == INT_RESULT) + unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag; + else + unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag; + max_length= my_decimal_precision_to_length(precision, decimals, + unsigned_flag); } @@ -714,84 +1173,179 @@ void Item_func_minus::fix_length_and_dec() } -double Item_func_minus::val() +double Item_func_minus::real_op() { - DBUG_ASSERT(fixed == 1); - double value=args[0]->val() - args[1]->val(); + double value= args[0]->val_real() - args[1]->val_real(); if ((null_value=args[0]->null_value || args[1]->null_value)) return 0.0; return value; } -longlong Item_func_minus::val_int() + +longlong Item_func_minus::int_op() { - DBUG_ASSERT(fixed == 1); - if (hybrid_type == INT_RESULT) - { - longlong value=args[0]->val_int() - args[1]->val_int(); - if ((null_value=args[0]->null_value || args[1]->null_value)) - return 0; - return value; - } - return (longlong) Item_func_minus::val(); + longlong value=args[0]->val_int() - args[1]->val_int(); + if ((null_value=args[0]->null_value || args[1]->null_value)) + return 0; + return value; } -double Item_func_mul::val() +/* See Item_func_plus::decimal_op for comments */ + +my_decimal *Item_func_minus::decimal_op(my_decimal *decimal_value) +{ + my_decimal value1, *val1; + my_decimal value2, *val2= + + val1= args[0]->val_decimal(&value1); + if ((null_value= args[0]->null_value)) + return 0; + val2= args[1]->val_decimal(&value2); + if (!(null_value= (args[1]->null_value || + my_decimal_sub(E_DEC_FATAL_ERROR, decimal_value, val1, + val2) > 1))) + return decimal_value; + return 0; +} + + +double Item_func_mul::real_op() { DBUG_ASSERT(fixed == 1); - double value=args[0]->val()*args[1]->val(); + double value= args[0]->val_real() * args[1]->val_real(); if ((null_value=args[0]->null_value || args[1]->null_value)) - return 0.0; /* purecov: inspected */ + return 0.0; return value; } -longlong Item_func_mul::val_int() + +longlong Item_func_mul::int_op() { DBUG_ASSERT(fixed == 1); - if (hybrid_type == INT_RESULT) - { - longlong value=args[0]->val_int()*args[1]->val_int(); - if ((null_value=args[0]->null_value || args[1]->null_value)) - return 0; /* purecov: inspected */ - return value; - } - return (longlong) Item_func_mul::val(); + longlong value=args[0]->val_int()*args[1]->val_int(); + if ((null_value=args[0]->null_value || args[1]->null_value)) + return 0; + return value; +} + + +/* See Item_func_plus::decimal_op for comments */ + +my_decimal *Item_func_mul::decimal_op(my_decimal *decimal_value) +{ + my_decimal value1, *val1; + my_decimal value2, *val2; + val1= args[0]->val_decimal(&value1); + if ((null_value= args[0]->null_value)) + return 0; + val2= args[1]->val_decimal(&value2); + if (!(null_value= (args[1]->null_value || + my_decimal_mul(E_DEC_FATAL_ERROR, decimal_value, val1, + val2) > 1))) + return decimal_value; + return 0; +} + + +void Item_func_mul::result_precision() +{ + /* Integer operations keep unsigned_flag if one of arguments is unsigned */ + if (result_type() == INT_RESULT) + unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag; + else + unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag; + decimals= min(args[0]->decimals + args[1]->decimals, DECIMAL_MAX_SCALE); + int precision= min(args[0]->decimal_precision() + args[1]->decimal_precision(), + DECIMAL_MAX_PRECISION); + max_length= my_decimal_precision_to_length(precision, decimals,unsigned_flag); } -double Item_func_div::val() +double Item_func_div::real_op() { DBUG_ASSERT(fixed == 1); - double value=args[0]->val(); - double val2=args[1]->val(); - if ((null_value= val2 == 0.0 || args[0]->null_value || args[1]->null_value)) + double value= args[0]->val_real(); + double val2= args[1]->val_real(); + if ((null_value= args[0]->null_value || args[1]->null_value)) + return 0.0; + if (val2 == 0.0) + { + signal_divide_by_null(); return 0.0; + } return value/val2; } -longlong Item_func_div::val_int() + +my_decimal *Item_func_div::decimal_op(my_decimal *decimal_value) { - DBUG_ASSERT(fixed == 1); - if (hybrid_type == INT_RESULT) - { - longlong value=args[0]->val_int(); - longlong val2=args[1]->val_int(); - if ((null_value= val2 == 0 || args[0]->null_value || args[1]->null_value)) - return 0; - return value/val2; + my_decimal value1, *val1; + my_decimal value2, *val2; + + val1= args[0]->val_decimal(&value1); + if ((null_value= args[0]->null_value)) + return 0; + val2= args[1]->val_decimal(&value2); + if ((null_value= args[1]->null_value)) + return 0; + switch (my_decimal_div(E_DEC_FATAL_ERROR & ~E_DEC_DIV_ZERO, decimal_value, + val1, val2, prec_increment)) { + case E_DEC_TRUNCATED: + case E_DEC_OK: + return decimal_value; + case E_DEC_DIV_ZERO: + signal_divide_by_null(); + default: + null_value= 1; // Safety + return 0; } - return (longlong) Item_func_div::val(); } + +void Item_func_div::result_precision() +{ + uint precision=min(args[0]->decimal_precision() + prec_increment, + DECIMAL_MAX_PRECISION); + /* Integer operations keep unsigned_flag if one of arguments is unsigned */ + if (result_type() == INT_RESULT) + unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag; + else + unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag; + decimals= min(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE); + max_length= my_decimal_precision_to_length(precision, decimals, + unsigned_flag); +} + + void Item_func_div::fix_length_and_dec() { - decimals=max(args[0]->decimals,args[1]->decimals)+2; - set_if_smaller(decimals, NOT_FIXED_DEC); - max_length=args[0]->max_length - args[0]->decimals + decimals; - uint tmp=float_length(decimals); - set_if_smaller(max_length,tmp); - maybe_null=1; + DBUG_ENTER("Item_func_div::fix_length_and_dec"); + prec_increment= current_thd->variables.div_precincrement; + Item_num_op::fix_length_and_dec(); + switch(hybrid_type) { + case REAL_RESULT: + { + decimals=max(args[0]->decimals,args[1]->decimals)+prec_increment; + set_if_smaller(decimals, NOT_FIXED_DEC); + max_length=args[0]->max_length - args[0]->decimals + decimals; + uint tmp=float_length(decimals); + set_if_smaller(max_length,tmp); + break; + } + case INT_RESULT: + hybrid_type= DECIMAL_RESULT; + DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT")); + result_precision(); + break; + case DECIMAL_RESULT: + result_precision(); + break; + default: + DBUG_ASSERT(0); + } + maybe_null= 1; // devision by zero + DBUG_VOID_RETURN; } @@ -801,8 +1355,13 @@ longlong Item_func_int_div::val_int() DBUG_ASSERT(fixed == 1); longlong value=args[0]->val_int(); longlong val2=args[1]->val_int(); - if ((null_value= val2 == 0 || args[0]->null_value || args[1]->null_value)) + if ((null_value= (args[0]->null_value || args[1]->null_value))) return 0; + if (val2 == 0) + { + signal_divide_by_null(); + return 0; + } return (unsigned_flag ? (ulonglong) value / (ulonglong) val2 : value / val2); @@ -811,134 +1370,182 @@ longlong Item_func_int_div::val_int() void Item_func_int_div::fix_length_and_dec() { - find_num_type(); max_length=args[0]->max_length - args[0]->decimals; maybe_null=1; + unsigned_flag=args[0]->unsigned_flag | args[1]->unsigned_flag; } -double Item_func_mod::val() -{ - DBUG_ASSERT(fixed == 1); - double x= args[0]->val(); - double y= args[1]->val(); - if ((null_value= (y == 0.0) || args[0]->null_value || args[1]->null_value)) - return 0.0; /* purecov: inspected */ - return fmod(x, y); -} - -longlong Item_func_mod::val_int() +longlong Item_func_mod::int_op() { DBUG_ASSERT(fixed == 1); longlong value= args[0]->val_int(); longlong val2= args[1]->val_int(); - if ((null_value=val2 == 0 || args[0]->null_value || args[1]->null_value)) + if ((null_value= args[0]->null_value || args[1]->null_value)) return 0; /* purecov: inspected */ + if (val2 == 0) + { + signal_divide_by_null(); + return 0; + } return value % val2; } -void Item_func_mod::fix_length_and_dec() +double Item_func_mod::real_op() { - Item_num_op::fix_length_and_dec(); + DBUG_ASSERT(fixed == 1); + double value= args[0]->val_real(); + double val2= args[1]->val_real(); + if ((null_value= args[0]->null_value || args[1]->null_value)) + return 0.0; /* purecov: inspected */ + if (val2 == 0.0) + { + signal_divide_by_null(); + return 0.0; + } + return fmod(value,val2); } -double Item_func_neg::val() +my_decimal *Item_func_mod::decimal_op(my_decimal *decimal_value) { - DBUG_ASSERT(fixed == 1); - double value=args[0]->val(); - null_value=args[0]->null_value; + my_decimal value1, *val1; + my_decimal value2, *val2; + + val1= args[0]->val_decimal(&value1); + if ((null_value= args[0]->null_value)) + return 0; + val2= args[1]->val_decimal(&value2); + if ((null_value= args[1]->null_value)) + return 0; + switch (my_decimal_mod(E_DEC_FATAL_ERROR & ~E_DEC_DIV_ZERO, decimal_value, + val1, val2)) { + case E_DEC_TRUNCATED: + case E_DEC_OK: + return decimal_value; + case E_DEC_DIV_ZERO: + signal_divide_by_null(); + default: + null_value= 1; + return 0; + } +} + + +void Item_func_mod::result_precision() +{ + decimals= max(args[0]->decimals, args[1]->decimals); + max_length= max(args[0]->max_length, args[1]->max_length); +} + + +double Item_func_neg::real_op() +{ + double value= args[0]->val_real(); + null_value= args[0]->null_value; return -value; } -longlong Item_func_neg::val_int() +longlong Item_func_neg::int_op() { - DBUG_ASSERT(fixed == 1); - longlong value=args[0]->val_int(); - null_value=args[0]->null_value; + longlong value= args[0]->val_int(); + null_value= args[0]->null_value; return -value; } +my_decimal *Item_func_neg::decimal_op(my_decimal *decimal_value) +{ + my_decimal val, *value= args[0]->val_decimal(&val); + if (!(null_value= args[0]->null_value)) + { + my_decimal2decimal(value, decimal_value); + my_decimal_neg(decimal_value); + return decimal_value; + } + return 0; +} + + +void Item_func_neg::fix_num_length_and_dec() +{ + decimals= args[0]->decimals; + /* 1 add because sign can appear */ + max_length= args[0]->max_length + 1; +} + + void Item_func_neg::fix_length_and_dec() { - enum Item_result arg_result= args[0]->result_type(); - enum Item::Type arg_type= args[0]->type(); - decimals=args[0]->decimals; - max_length=args[0]->max_length; - hybrid_type= REAL_RESULT; - + DBUG_ENTER("Item_func_neg::fix_length_and_dec"); + Item_func_num1::fix_length_and_dec(); + /* - We need to account for added '-' in the following cases: - A) argument is a real or integer positive constant - in this case - argument's max_length is set to actual number of bytes occupied, and not - maximum number of bytes real or integer may require. Note that all - constants are non negative so we don't need to account for removed '-'. - B) argument returns a string. + If this is in integer context keep the context as integer if possible + (This is how multiplication and other integer functions works) Use val() to get value as arg_type doesn't mean that item is Item_int or Item_real due to existence of Item_param. */ - if (arg_result == STRING_RESULT || - (arg_type == REAL_ITEM && args[0]->val() >= 0) || - (arg_type == INT_ITEM && args[0]->val_int() > 0)) - max_length++; - - if (args[0]->result_type() == INT_RESULT) + if (hybrid_type == INT_RESULT && + args[0]->type() == INT_ITEM && + ((ulonglong) args[0]->val_int() >= (ulonglong) LONGLONG_MIN)) { /* - If this is in integer context keep the context as integer - (This is how multiplication and other integer functions works) - - We must however do a special case in the case where the argument - is a unsigned bigint constant as in this case the only safe - number to convert in integer context is 9223372036854775808. - (This is needed because the lex parser doesn't anymore handle - signed integers) + Ensure that result is converted to DECIMAL, as longlong can't hold + the negated number */ - if (args[0]->type() != INT_ITEM || - (((ulonglong) args[0]->val_int()) <= (ulonglong) LONGLONG_MIN)) - hybrid_type= INT_RESULT; + hybrid_type= DECIMAL_RESULT; + DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT")); } + unsigned_flag= 0; + DBUG_VOID_RETURN; } -double Item_func_abs::val() +double Item_func_abs::real_op() { - DBUG_ASSERT(fixed == 1); - double value=args[0]->val(); - null_value=args[0]->null_value; + double value= args[0]->val_real(); + null_value= args[0]->null_value; return fabs(value); } -longlong Item_func_abs::val_int() +longlong Item_func_abs::int_op() { - DBUG_ASSERT(fixed == 1); - longlong value=args[0]->val_int(); - null_value=args[0]->null_value; + longlong value= args[0]->val_int(); + null_value= args[0]->null_value; return value >= 0 ? value : -value; } -void Item_func_abs::fix_length_and_dec() +my_decimal *Item_func_abs::decimal_op(my_decimal *decimal_value) { - decimals=args[0]->decimals; - max_length=args[0]->max_length; - hybrid_type= REAL_RESULT; - if (args[0]->result_type() == INT_RESULT) + my_decimal val, *value= args[0]->val_decimal(&val); + if (!(null_value= args[0]->null_value)) { - hybrid_type= INT_RESULT; - unsigned_flag= 1; + my_decimal2decimal(value, decimal_value); + if (decimal_value->sign()) + my_decimal_neg(decimal_value); + return decimal_value; } + return 0; +} + + +void Item_func_abs::fix_length_and_dec() +{ + Item_func_num1::fix_length_and_dec(); + if (hybrid_type == INT_RESULT) + unsigned_flag= 1; } /* Gateway to natural LOG function */ -double Item_func_ln::val() +double Item_func_ln::val_real() { DBUG_ASSERT(fixed == 1); - double value=args[0]->val(); + double value= args[0]->val_real(); if ((null_value=(args[0]->null_value || value <= 0.0))) return 0.0; return log(value); @@ -949,15 +1556,15 @@ double Item_func_ln::val() We have to check if all values are > zero and first one is not one as these are the cases then result is not a number. */ -double Item_func_log::val() +double Item_func_log::val_real() { DBUG_ASSERT(fixed == 1); - double value=args[0]->val(); + double value= args[0]->val_real(); if ((null_value=(args[0]->null_value || value <= 0.0))) return 0.0; if (arg_count == 2) { - double value2= args[1]->val(); + double value2= args[1]->val_real(); if ((null_value=(args[1]->null_value || value2 <= 0.0 || value == 1.0))) return 0.0; return log(value2) / log(value); @@ -965,47 +1572,47 @@ double Item_func_log::val() return log(value); } -double Item_func_log2::val() +double Item_func_log2::val_real() { DBUG_ASSERT(fixed == 1); - double value=args[0]->val(); + double value= args[0]->val_real(); if ((null_value=(args[0]->null_value || value <= 0.0))) return 0.0; - return log(value) / log(2.0); + return log(value) / M_LN2; } -double Item_func_log10::val() +double Item_func_log10::val_real() { DBUG_ASSERT(fixed == 1); - double value=args[0]->val(); + double value= args[0]->val_real(); if ((null_value=(args[0]->null_value || value <= 0.0))) return 0.0; /* purecov: inspected */ return log10(value); } -double Item_func_exp::val() +double Item_func_exp::val_real() { DBUG_ASSERT(fixed == 1); - double value=args[0]->val(); + double value= args[0]->val_real(); if ((null_value=args[0]->null_value)) return 0.0; /* purecov: inspected */ return exp(value); } -double Item_func_sqrt::val() +double Item_func_sqrt::val_real() { DBUG_ASSERT(fixed == 1); - double value=args[0]->val(); + double value= args[0]->val_real(); if ((null_value=(args[0]->null_value || value < 0))) return 0.0; /* purecov: inspected */ return sqrt(value); } -double Item_func_pow::val() +double Item_func_pow::val_real() { DBUG_ASSERT(fixed == 1); - double value=args[0]->val(); - double val2=args[1]->val(); + double value= args[0]->val_real(); + double val2= args[1]->val_real(); if ((null_value=(args[0]->null_value || args[1]->null_value))) return 0.0; /* purecov: inspected */ return pow(value,val2); @@ -1013,35 +1620,35 @@ double Item_func_pow::val() // Trigonometric functions -double Item_func_acos::val() +double Item_func_acos::val_real() { DBUG_ASSERT(fixed == 1); // the volatile's for BUG #2338 to calm optimizer down (because of gcc's bug) - volatile double value=args[0]->val(); + volatile double value= args[0]->val_real(); if ((null_value=(args[0]->null_value || (value < -1.0 || value > 1.0)))) return 0.0; return fix_result(acos(value)); } -double Item_func_asin::val() +double Item_func_asin::val_real() { DBUG_ASSERT(fixed == 1); // the volatile's for BUG #2338 to calm optimizer down (because of gcc's bug) - volatile double value=args[0]->val(); + volatile double value= args[0]->val_real(); if ((null_value=(args[0]->null_value || (value < -1.0 || value > 1.0)))) return 0.0; return fix_result(asin(value)); } -double Item_func_atan::val() +double Item_func_atan::val_real() { DBUG_ASSERT(fixed == 1); - double value=args[0]->val(); + double value= args[0]->val_real(); if ((null_value=args[0]->null_value)) return 0.0; if (arg_count == 2) { - double val2= args[1]->val(); + double val2= args[1]->val_real(); if ((null_value=args[1]->null_value)) return 0.0; return fix_result(atan2(value,val2)); @@ -1049,28 +1656,28 @@ double Item_func_atan::val() return fix_result(atan(value)); } -double Item_func_cos::val() +double Item_func_cos::val_real() { DBUG_ASSERT(fixed == 1); - double value=args[0]->val(); + double value= args[0]->val_real(); if ((null_value=args[0]->null_value)) return 0.0; return fix_result(cos(value)); } -double Item_func_sin::val() +double Item_func_sin::val_real() { DBUG_ASSERT(fixed == 1); - double value=args[0]->val(); + double value= args[0]->val_real(); if ((null_value=args[0]->null_value)) return 0.0; return fix_result(sin(value)); } -double Item_func_tan::val() +double Item_func_tan::val_real() { DBUG_ASSERT(fixed == 1); - double value=args[0]->val(); + double value= args[0]->val_real(); if ((null_value=args[0]->null_value)) return 0.0; return fix_result(tan(value)); @@ -1131,54 +1738,219 @@ void Item_func_integer::fix_length_and_dec() decimals=0; } -longlong Item_func_ceiling::val_int() +void Item_func_int_val::fix_num_length_and_dec() { - DBUG_ASSERT(fixed == 1); - double value=args[0]->val(); - null_value=args[0]->null_value; - return (longlong) ceil(value); + max_length= args[0]->max_length - (args[0]->decimals ? + args[0]->decimals + 1 : + 0) + 2; + uint tmp= float_length(decimals); + set_if_smaller(max_length,tmp); + decimals= 0; } -longlong Item_func_floor::val_int() + +void Item_func_int_val::find_num_type() { - DBUG_ASSERT(fixed == 1); - // the volatile's for BUG #3051 to calm optimizer down (because of gcc's bug) - volatile double value=args[0]->val(); - null_value=args[0]->null_value; - return (longlong) floor(value); + DBUG_ENTER("Item_func_int_val::find_num_type"); + DBUG_PRINT("info", ("name %s", func_name())); + switch(hybrid_type= args[0]->result_type()) + { + case STRING_RESULT: + case REAL_RESULT: + hybrid_type= REAL_RESULT; + max_length= float_length(decimals); + break; + case INT_RESULT: + case DECIMAL_RESULT: + /* + -2 because in most high position can't be used any digit for longlong + and one position for increasing value during operation + */ + if ((args[0]->max_length - args[0]->decimals) >= + (DECIMAL_LONGLONG_DIGITS - 2)) + { + hybrid_type= DECIMAL_RESULT; + } + else + { + unsigned_flag= args[0]->unsigned_flag; + hybrid_type= INT_RESULT; + } + break; + default: + DBUG_ASSERT(0); + } + DBUG_PRINT("info", ("Type: %s", + (hybrid_type == REAL_RESULT ? "REAL_RESULT" : + hybrid_type == DECIMAL_RESULT ? "DECIMAL_RESULT" : + hybrid_type == INT_RESULT ? "INT_RESULT" : + "--ILLEGAL!!!--"))); + + DBUG_VOID_RETURN; } -void Item_func_round::fix_length_and_dec() + +longlong Item_func_ceiling::int_op() { - max_length=args[0]->max_length; - decimals=args[0]->decimals; - if (args[1]->const_item()) + longlong result; + switch (args[0]->result_type()) { + case INT_RESULT: + result= args[0]->val_int(); + null_value= args[0]->null_value; + break; + case DECIMAL_RESULT: { - int tmp=(int) args[1]->val_int(); - if (tmp < 0) - decimals=0; + my_decimal dec_buf, *dec; + if ((dec= Item_func_ceiling::decimal_op(&dec_buf))) + my_decimal2int(E_DEC_FATAL_ERROR, dec, unsigned_flag, &result); + else + result= 0; + break; + } + default: + result= (longlong)Item_func_ceiling::real_op(); + }; + return result; +} + + +double Item_func_ceiling::real_op() +{ + /* + the volatile's for BUG #3051 to calm optimizer down (because of gcc's + bug) + */ + volatile double value= args[0]->val_real(); + null_value= args[0]->null_value; + return ceil(value); +} + + +my_decimal *Item_func_ceiling::decimal_op(my_decimal *decimal_value) +{ + my_decimal val, *value= args[0]->val_decimal(&val); + if (!(null_value= (args[0]->null_value || + my_decimal_ceiling(E_DEC_FATAL_ERROR, value, + decimal_value) > 1))) + return decimal_value; + return 0; +} + + +longlong Item_func_floor::int_op() +{ + longlong result; + switch (args[0]->result_type()) { + case INT_RESULT: + result= args[0]->val_int(); + null_value= args[0]->null_value; + break; + case DECIMAL_RESULT: + { + my_decimal dec_buf, *dec; + if ((dec= Item_func_floor::decimal_op(&dec_buf))) + my_decimal2int(E_DEC_FATAL_ERROR, dec, unsigned_flag, &result); else - decimals=min(tmp,NOT_FIXED_DEC); - if ((tmp= decimals - args[0]->decimals) > 0) - max_length+= tmp; + result= 0; + break; } + default: + result= (longlong)Item_func_floor::real_op(); + }; + return result; } -double Item_func_round::val() + +double Item_func_floor::real_op() +{ + /* + the volatile's for BUG #3051 to calm optimizer down (because of gcc's + bug) + */ + volatile double value= args[0]->val_real(); + null_value= args[0]->null_value; + return floor(value); +} + + +my_decimal *Item_func_floor::decimal_op(my_decimal *decimal_value) +{ + my_decimal val, *value= args[0]->val_decimal(&val); + if (!(null_value= (args[0]->null_value || + my_decimal_floor(E_DEC_FATAL_ERROR, value, + decimal_value) > 1))) + return decimal_value; + return 0; +} + + +void Item_func_round::fix_length_and_dec() +{ + unsigned_flag= args[0]->unsigned_flag; + if (!args[1]->const_item()) + { + max_length= args[0]->max_length; + decimals= args[0]->decimals; + hybrid_type= REAL_RESULT; + return; + } + + int decimals_to_set= max((int)args[1]->val_int(), 0); + if (args[0]->decimals == NOT_FIXED_DEC) + { + max_length= args[0]->max_length; + decimals= min(decimals_to_set, NOT_FIXED_DEC); + hybrid_type= REAL_RESULT; + return; + } + + switch (args[0]->result_type()) { + case REAL_RESULT: + case STRING_RESULT: + hybrid_type= REAL_RESULT; + decimals= min(decimals_to_set, NOT_FIXED_DEC); + max_length= float_length(decimals); + break; + case INT_RESULT: + if (!decimals_to_set && + (truncate || (args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS))) + { + int length_can_increase= test(!truncate && (args[1]->val_int() < 0)); + max_length= args[0]->max_length + length_can_increase; + /* Here we can keep INT_RESULT */ + hybrid_type= INT_RESULT; + decimals= 0; + break; + } + /* fall through */ + case DECIMAL_RESULT: + { + hybrid_type= DECIMAL_RESULT; + int decimals_delta= args[0]->decimals - decimals_to_set; + int precision= args[0]->decimal_precision(); + int length_increase= ((decimals_delta <= 0) || truncate) ? 0:1; + + precision-= decimals_delta - length_increase; + decimals= decimals_to_set; + max_length= my_decimal_precision_to_length(precision, decimals, + unsigned_flag); + break; + } + default: + DBUG_ASSERT(0); /* This result type isn't handled */ + } +} + +double my_double_round(double value, int dec, bool truncate) { - DBUG_ASSERT(fixed == 1); - double value=args[0]->val(); - int dec=(int) args[1]->val_int(); - uint abs_dec=abs(dec); double tmp; + uint abs_dec= abs(dec); /* tmp2 is here to avoid return the value with 80 bit precision This will fix that the test round(0.1,1) = round(0.1,1) is true */ volatile double tmp2; - if ((null_value=args[0]->null_value || args[1]->null_value)) - return 0.0; tmp=(abs_dec < array_elements(log_10) ? log_10[abs_dec] : pow(10.0,(double) abs_dec)); @@ -1195,10 +1967,74 @@ double Item_func_round::val() } -bool Item_func_rand::fix_fields(THD *thd, struct st_table_list *tables, - Item **ref) +double Item_func_round::real_op() +{ + double value= args[0]->val_real(); + int dec= (int) args[1]->val_int(); + + if (!(null_value= args[0]->null_value || args[1]->null_value)) + return my_double_round(value, dec, truncate); + + return 0.0; +} + + +longlong Item_func_round::int_op() +{ + longlong value= args[0]->val_int(); + int dec=(int) args[1]->val_int(); + decimals= 0; + uint abs_dec; + if ((null_value= args[0]->null_value || args[1]->null_value)) + return 0; + if (dec >= 0) + return value; // integer have not digits after point + + abs_dec= -dec; + double tmp; + /* + tmp2 is here to avoid return the value with 80 bit precision + This will fix that the test round(0.1,1) = round(0.1,1) is true + */ + volatile double tmp2; + + tmp= (abs_dec < array_elements(log_10) ? + log_10[abs_dec] : pow(10.0, (double) abs_dec)); + + if (truncate) + { + if (unsigned_flag) + tmp2= floor(ulonglong2double(value)/tmp)*tmp; + else if (value >= 0) + tmp2= floor(((double)value)/tmp)*tmp; + else + tmp2= ceil(((double)value)/tmp)*tmp; + } + else + tmp2= rint(((double)value)/tmp)*tmp; + return (longlong)tmp2; +} + + +my_decimal *Item_func_round::decimal_op(my_decimal *decimal_value) { - if (Item_real_func::fix_fields(thd, tables, ref)) + my_decimal val, *value= args[0]->val_decimal(&val); + int dec=(int) args[1]->val_int(); + if (dec > 0) + { + decimals= min(dec, DECIMAL_MAX_SCALE); // to get correct output + } + if (!(null_value= (args[0]->null_value || args[1]->null_value || + my_decimal_round(E_DEC_FATAL_ERROR, value, dec, truncate, + decimal_value) > 1))) + return decimal_value; + return 0; +} + + +bool Item_func_rand::fix_fields(THD *thd,Item **ref) +{ + if (Item_real_func::fix_fields(thd, ref)) return TRUE; used_tables_cache|= RAND_TABLE_BIT; if (arg_count) @@ -1251,7 +2087,7 @@ void Item_func_rand::update_used_tables() } -double Item_func_rand::val() +double Item_func_rand::val_real() { DBUG_ASSERT(fixed == 1); return my_rnd(rand); @@ -1260,16 +2096,16 @@ double Item_func_rand::val() longlong Item_func_sign::val_int() { DBUG_ASSERT(fixed == 1); - double value=args[0]->val(); + double value= args[0]->val_real(); null_value=args[0]->null_value; return value < 0.0 ? -1 : (value > 0 ? 1 : 0); } -double Item_func_units::val() +double Item_func_units::val_real() { DBUG_ASSERT(fixed == 1); - double value=args[0]->val(); + double value= args[0]->val_real(); if ((null_value=args[0]->null_value)) return 0; return value*mul+add; @@ -1278,6 +2114,7 @@ double Item_func_units::val() void Item_func_min_max::fix_length_and_dec() { + int max_int_part=0; decimals=0; max_length=0; maybe_null=1; @@ -1285,16 +2122,18 @@ void Item_func_min_max::fix_length_and_dec() for (uint i=0 ; i < arg_count ; i++) { - if (max_length < args[i]->max_length) - max_length=args[i]->max_length; - if (decimals < args[i]->decimals) - decimals=args[i]->decimals; + set_if_bigger(max_length, args[i]->max_length); + set_if_bigger(decimals, args[i]->decimals); + set_if_bigger(max_int_part, args[i]->decimal_int_part()); if (!args[i]->maybe_null) maybe_null=0; cmp_type=item_cmp_type(cmp_type,args[i]->result_type()); } if (cmp_type == STRING_RESULT) agg_arg_charsets(collation, args, arg_count, MY_COLL_CMP_CONV); + else if ((cmp_type == DECIMAL_RESULT) || (cmp_type == INT_RESULT)) + max_length= my_decimal_precision_to_length(max_int_part+decimals, decimals, + unsigned_flag); } @@ -1313,9 +2152,17 @@ String *Item_func_min_max::val_str(String *str) str->set((ulonglong) nr,&my_charset_bin); return str; } + case DECIMAL_RESULT: + { + my_decimal dec_buf, *dec_val= val_decimal(&dec_buf); + if (null_value) + return 0; + my_decimal2string(E_DEC_FATAL_ERROR, dec_val, 0, 0, 0, str); + return str; + } case REAL_RESULT: { - double nr=val(); + double nr= val_real(); if (null_value) return 0; /* purecov: inspected */ str->set(nr,decimals,&my_charset_bin); @@ -1351,7 +2198,7 @@ String *Item_func_min_max::val_str(String *str) } case ROW_RESULT: default: - // This case should never be choosen + // This case should never be chosen DBUG_ASSERT(0); return 0; } @@ -1359,7 +2206,7 @@ String *Item_func_min_max::val_str(String *str) } -double Item_func_min_max::val() +double Item_func_min_max::val_real() { DBUG_ASSERT(fixed == 1); double value=0.0; @@ -1368,12 +2215,12 @@ double Item_func_min_max::val() { if (null_value) { - value=args[i]->val(); + value= args[i]->val_real(); null_value=args[i]->null_value; } else { - double tmp=args[i]->val(); + double tmp= args[i]->val_real(); if (!args[i]->null_value && (tmp < value ? cmp_sign : -cmp_sign) > 0) value=tmp; } @@ -1404,6 +2251,40 @@ longlong Item_func_min_max::val_int() return value; } + +my_decimal *Item_func_min_max::val_decimal(my_decimal *dec) +{ + DBUG_ASSERT(fixed == 1); + my_decimal tmp_buf, *tmp, *res= NULL; + null_value=1; + for (uint i=0; i < arg_count ; i++) + { + if (null_value) + { + res= args[i]->val_decimal(dec); + null_value= args[i]->null_value; + } + else + { + tmp= args[i]->val_decimal(&tmp_buf); + if (args[i]->null_value) + continue; + if ((my_decimal_cmp(tmp, res) * cmp_sign) < 0) + { + if (tmp == &tmp_buf) + { + my_decimal2decimal(tmp, dec); + res= dec; + } + else + res= tmp; + } + } + } + return res; +} + + longlong Item_func_length::val_int() { DBUG_ASSERT(fixed == 1); @@ -1505,8 +2386,8 @@ longlong Item_func_field::val_int() if (cmp_type == STRING_RESULT) { String *field; - if (!(field=args[0]->val_str(&value))) - return 0; // -1 if null ? + if (!(field= args[0]->val_str(&value))) + return 0; for (uint i=1 ; i < arg_count ; i++) { String *tmp_value=args[i]->val_str(&tmp); @@ -1525,14 +2406,27 @@ longlong Item_func_field::val_int() return (longlong) (i); } } + else if (cmp_type == DECIMAL_RESULT) + { + my_decimal dec_arg_buf, *dec_arg, + dec_buf, *dec= args[0]->val_decimal(&dec_buf); + if (args[0]->null_value) + return 0; + for (uint i=1; i < arg_count; i++) + { + dec_arg= args[i]->val_decimal(&dec_arg_buf); + if (!args[i]->null_value && !my_decimal_cmp(dec_arg, dec)) + return (longlong) (i); + } + } else { - double val= args[0]->val(); + double val= args[0]->val_real(); if (args[0]->null_value) return 0; for (uint i=1; i < arg_count ; i++) { - if (val == args[i]->val() && !args[i]->null_value) + if (val == args[i]->val_real() && !args[i]->null_value) return (longlong) (i); } } @@ -1676,12 +2570,12 @@ longlong Item_func_find_in_set::val_int() } str_end= substr_end; } - else if (str_end - str_begin == 0 && - find_str_len == 0 && + else if (str_end - str_begin == 0 && + find_str_len == 0 && wc == (my_wc_t) separator) return (longlong) ++position; else - return (longlong) 0; + return LL(0); } } return 0; @@ -1708,13 +2602,6 @@ longlong Item_func_bit_count::val_int() #ifdef HAVE_DLOPEN -udf_handler::~udf_handler() -{ - /* Everything should be properly cleaned up by this moment. */ - DBUG_ASSERT(not_original || !(initialized || buffers)); -} - - void udf_handler::cleanup() { if (!not_original) @@ -1738,7 +2625,7 @@ void udf_handler::cleanup() bool -udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func, +udf_handler::fix_fields(THD *thd, Item_result_field *func, uint arg_count, Item **arguments) { #ifndef EMBEDDED_LIBRARY // Avoid compiler warning @@ -1746,16 +2633,15 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func, #endif DBUG_ENTER("Item_udf_func::fix_fields"); - if (check_stack_overrun(thd, buff)) - DBUG_RETURN(1); // Fatal error flag is set! + if (check_stack_overrun(thd, STACK_MIN_SIZE, buff)) + DBUG_RETURN(TRUE); // Fatal error flag is set! udf_func *tmp_udf=find_udf(u_d->name.str,(uint) u_d->name.length,1); if (!tmp_udf) { - my_printf_error(ER_CANT_FIND_UDF,ER(ER_CANT_FIND_UDF),MYF(0),u_d->name.str, - errno); - DBUG_RETURN(1); + my_error(ER_CANT_FIND_UDF, MYF(0), u_d->name.str, errno); + DBUG_RETURN(TRUE); } u_d=tmp_udf; args=arguments; @@ -1772,7 +2658,7 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func, { free_udf(u_d); - DBUG_RETURN(1); + DBUG_RETURN(TRUE); } uint i; Item **arg,**arg_end; @@ -1780,13 +2666,13 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func, arg != arg_end ; arg++,i++) { - if (!(*arg)->fixed && - (*arg)->fix_fields(thd, tables, arg)) + if (!(*arg)->fixed && + (*arg)->fix_fields(thd, arg)) DBUG_RETURN(1); // we can't assign 'item' before, because fix_fields() can change arg Item *item= *arg; if (item->check_cols(1)) - DBUG_RETURN(1); + DBUG_RETURN(TRUE); /* TODO: We should think about this. It is not always right way just to set an UDF result to return my_charset_bin @@ -1796,7 +2682,7 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func, Moreover, some arguments can represent a numeric input which doesn't effect the result character set and collation. There is no a general rule for UDF. Everything depends on - the particular user definted function. + the particular user defined function. */ if (item->collation.collation->state & MY_CS_BINSORT) func->collation.set(&my_charset_bin); @@ -1807,14 +2693,19 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func, const_item_cache&=item->const_item(); f_args.arg_type[i]=item->result_type(); } + //TODO: why all following memory is not allocated with 1 call of sql_alloc? if (!(buffers=new String[arg_count]) || !(f_args.args= (char**) sql_alloc(arg_count * sizeof(char *))) || - !(f_args.lengths=(ulong*) sql_alloc(arg_count * sizeof(long))) || - !(f_args.maybe_null=(char*) sql_alloc(arg_count * sizeof(char))) || - !(num_buffer= (char*) sql_alloc(ALIGN_SIZE(sizeof(double))*arg_count))) + !(f_args.lengths= (ulong*) sql_alloc(arg_count * sizeof(long))) || + !(f_args.maybe_null= (char*) sql_alloc(arg_count * sizeof(char))) || + !(num_buffer= (char*) sql_alloc(arg_count * + ALIGN_SIZE(sizeof(double)))) || + !(f_args.attributes= (char**) sql_alloc(arg_count * sizeof(char *))) || + !(f_args.attribute_lengths= (ulong*) sql_alloc(arg_count * + sizeof(long)))) { free_udf(u_d); - DBUG_RETURN(1); + DBUG_RETURN(TRUE); } } func->fix_length_and_dec(); @@ -1830,8 +2721,10 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func, for (uint i=0; i < arg_count; i++) { f_args.args[i]=0; - f_args.lengths[i]=arguments[i]->max_length; - f_args.maybe_null[i]=(char) arguments[i]->maybe_null; + f_args.lengths[i]= arguments[i]->max_length; + f_args.maybe_null[i]= (char) arguments[i]->maybe_null; + f_args.attributes[i]= arguments[i]->name; + f_args.attribute_lengths[i]= arguments[i]->name_length; switch(arguments[i]->type()) { case Item::STRING_ITEM: // Constant string ! @@ -1851,7 +2744,7 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func, } break; case Item::REAL_ITEM: - *((double*) to) = arguments[i]->val(); + *((double*) to)= arguments[i]->val_real(); if (!arguments[i]->null_value) { f_args.args[i]=to; @@ -1868,10 +2761,10 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func, u_d->func_init; if ((error=(uchar) init(&initid, &f_args, thd->net.last_error))) { - my_printf_error(ER_CANT_INITIALIZE_UDF,ER(ER_CANT_INITIALIZE_UDF),MYF(0), - u_d->name.str, thd->net.last_error); + my_error(ER_CANT_INITIALIZE_UDF, MYF(0), + u_d->name.str, thd->net.last_error); free_udf(u_d); - DBUG_RETURN(1); + DBUG_RETURN(TRUE); } func->max_length=min(initid.max_length,MAX_BLOB_WIDTH); func->maybe_null=initid.maybe_null; @@ -1881,11 +2774,11 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func, initialized=1; if (error) { - my_printf_error(ER_CANT_INITIALIZE_UDF,ER(ER_CANT_INITIALIZE_UDF),MYF(0), - u_d->name.str, ER(ER_UNKNOWN_ERROR)); - DBUG_RETURN(1); + my_error(ER_CANT_INITIALIZE_UDF, MYF(0), + u_d->name.str, ER(ER_UNKNOWN_ERROR)); + DBUG_RETURN(TRUE); } - DBUG_RETURN(0); + DBUG_RETURN(FALSE); } @@ -1900,6 +2793,7 @@ bool udf_handler::get_arguments() f_args.args[i]=0; switch (f_args.arg_type[i]) { case STRING_RESULT: + case DECIMAL_RESULT: { String *res=args[i]->val_str(&buffers[str_count++]); if (!(args[i]->null_value)) @@ -1918,7 +2812,7 @@ bool udf_handler::get_arguments() } break; case REAL_RESULT: - *((double*) to) = args[i]->val(); + *((double*) to)= args[i]->val_real(); if (!args[i]->null_value) { f_args.args[i]=to; @@ -1927,7 +2821,7 @@ bool udf_handler::get_arguments() break; case ROW_RESULT: default: - // This case should never be choosen + // This case should never be chosen DBUG_ASSERT(0); break; } @@ -1972,6 +2866,36 @@ String *udf_handler::val_str(String *str,String *save_str) } +/* + For the moment, UDF functions are returning DECIMAL values as strings +*/ + +my_decimal *udf_handler::val_decimal(my_bool *null_value, my_decimal *dec_buf) +{ + char buf[DECIMAL_MAX_STR_LENGTH+1], *end; + ulong res_length= DECIMAL_MAX_STR_LENGTH; + + if (get_arguments()) + { + *null_value=1; + return 0; + } + char *(*func)(UDF_INIT *, UDF_ARGS *, char *, ulong *, uchar *, uchar *)= + (char* (*)(UDF_INIT *, UDF_ARGS *, char *, ulong *, uchar *, uchar *)) + u_d->func; + + char *res= func(&initid, &f_args, buf, &res_length, &is_null, &error); + if (is_null || error) + { + *null_value= 1; + return 0; + } + end= res+ res_length; + str2my_decimal(E_DEC_FATAL_ERROR, res, dec_buf, &end); + return dec_buf; +} + + void Item_udf_func::cleanup() { udf.cleanup(); @@ -1979,7 +2903,7 @@ void Item_udf_func::cleanup() } -double Item_func_udf_float::val() +double Item_func_udf_float::val_real() { DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_func_udf_float::val"); @@ -1992,7 +2916,7 @@ double Item_func_udf_float::val() String *Item_func_udf_float::val_str(String *str) { DBUG_ASSERT(fixed == 1); - double nr=val(); + double nr= val_real(); if (null_value) return 0; /* purecov: inspected */ str->set(nr,decimals,&my_charset_bin); @@ -2024,6 +2948,59 @@ String *Item_func_udf_int::val_str(String *str) return str; } + +longlong Item_func_udf_decimal::val_int() +{ + my_decimal dec_buf, *dec= udf.val_decimal(&null_value, &dec_buf); + longlong result; + if (null_value) + return 0; + my_decimal2int(E_DEC_FATAL_ERROR, dec, unsigned_flag, &result); + return result; +} + + +double Item_func_udf_decimal::val_real() +{ + my_decimal dec_buf, *dec= udf.val_decimal(&null_value, &dec_buf); + double result; + if (null_value) + return 0.0; + my_decimal2double(E_DEC_FATAL_ERROR, dec, &result); + return result; +} + + +my_decimal *Item_func_udf_decimal::val_decimal(my_decimal *dec_buf) +{ + DBUG_ASSERT(fixed == 1); + DBUG_ENTER("Item_func_udf_decimal::val_decimal"); + DBUG_PRINT("info",("result_type: %d arg_count: %d", + args[0]->result_type(), arg_count)); + + DBUG_RETURN(udf.val_decimal(&null_value, dec_buf)); +} + + +String *Item_func_udf_decimal::val_str(String *str) +{ + my_decimal dec_buf, *dec= udf.val_decimal(&null_value, &dec_buf); + if (null_value) + return 0; + if (str->length() < DECIMAL_MAX_STR_LENGTH) + str->length(DECIMAL_MAX_STR_LENGTH); + my_decimal_round(E_DEC_FATAL_ERROR, dec, decimals, FALSE, &dec_buf); + my_decimal2string(E_DEC_FATAL_ERROR, &dec_buf, 0, 0, '0', str); + return str; +} + + +void Item_func_udf_decimal::fix_length_and_dec() +{ + fix_num_length_and_dec(); +} + + /* Default max_length is max argument length */ void Item_func_udf_str::fix_length_and_dec() @@ -2043,6 +3020,18 @@ String *Item_func_udf_str::val_str(String *str) return res; } + +/* + This has to come last in the udf_handler methods, or C for AIX + version 6.0.0.0 fails to compile with debugging enabled. (Yes, really.) + */ + +udf_handler::~udf_handler() +{ + /* Everything should be properly cleaned up by this moment. */ + DBUG_ASSERT(not_original || !(initialized || buffers)); +} + #else bool udf_handler::get_arguments() { return 0; } #endif /* HAVE_DLOPEN */ @@ -2126,18 +3115,6 @@ void item_user_lock_free(void) void item_user_lock_release(User_level_lock *ull) { ull->locked=0; - if (mysql_bin_log.is_open()) - { - char buf[256]; - const char *command="DO RELEASE_LOCK(\""; - String tmp(buf,sizeof(buf), system_charset_info); - tmp.copy(command, strlen(command), tmp.charset()); - tmp.append(ull->key,ull->key_length); - tmp.append("\")", 2); - Query_log_event qev(current_thd, tmp.ptr(), tmp.length(),1, FALSE); - qev.error_code=0; // this query is always safe to run on slave - mysql_bin_log.write(&qev); - } if (--ull->count) pthread_cond_signal(&ull->cond); else @@ -2261,6 +3238,16 @@ longlong Item_func_get_lock::val_int() User_level_lock *ull; int error=0; + /* + In slave thread no need to get locks, everything is serialized. Anyway + there is no way to make GET_LOCK() work on slave like it did on master + (i.e. make it return exactly the same value) because we don't have the + same other concurrent threads environment. No matter what we return here, + it's not guaranteed to be same as on master. + */ + if (thd->slave_thread) + return 1; + pthread_mutex_lock(&LOCK_user_locks); if (!res || !res->length()) @@ -2384,16 +3371,17 @@ longlong Item_func_release_lock::val_int() longlong Item_func_last_insert_id::val_int() { + THD *thd= current_thd; DBUG_ASSERT(fixed == 1); if (arg_count) { - longlong value=args[0]->val_int(); - current_thd->insert_id(value); - null_value=args[0]->null_value; + longlong value= args[0]->val_int(); + thd->insert_id(value); + null_value= args[0]->null_value; + return value; // Avoid side effect of insert_id() } - else - current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - return current_thd->insert_id(); + thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + return thd->insert_id(); } /* This function is just used to test speed of different functions */ @@ -2409,7 +3397,7 @@ longlong Item_func_benchmark::val_int() { switch (args[0]->result_type()) { case REAL_RESULT: - (void) args[0]->val(); + (void) args[0]->val_real(); break; case INT_RESULT: (void) args[0]->val_int(); @@ -2419,7 +3407,7 @@ longlong Item_func_benchmark::val_int() break; case ROW_RESULT: default: - // This case should never be choosen + // This case should never be chosen DBUG_ASSERT(0); return 0; } @@ -2491,14 +3479,13 @@ static user_var_entry *get_variable(HASH *hash, LEX_STRING &name, SELECT @a:= ). */ -bool Item_func_set_user_var::fix_fields(THD *thd, TABLE_LIST *tables, - Item **ref) +bool Item_func_set_user_var::fix_fields(THD *thd, Item **ref) { DBUG_ASSERT(fixed == 0); /* fix_fields will call Item_func_set_user_var::fix_length_and_dec */ - if (Item_func::fix_fields(thd, tables, ref) || + if (Item_func::fix_fields(thd, ref) || !(entry= get_variable(&thd->user_vars, name, 1))) - return 1; + return TRUE; /* Remember the last query which updated it, this way a query can later know if this variable is a constant item in the query (it is if update_query_id @@ -2520,11 +3507,12 @@ bool Item_func_set_user_var::fix_fields(THD *thd, TABLE_LIST *tables, from the argument if the argument is NULL and the variable has previously been initialized. */ - if (!entry->collation.collation || !args[0]->null_value) + null_item= (args[0]->type() == NULL_ITEM); + if (!entry->collation.collation || !null_item) entry->collation.set(args[0]->collation.collation, DERIVATION_IMPLICIT); collation.set(entry->collation.collation, DERIVATION_IMPLICIT); cached_result_type= args[0]->result_type(); - return 0; + return FALSE; } @@ -2538,18 +3526,34 @@ Item_func_set_user_var::fix_length_and_dec() } -bool Item_func_set_user_var::update_hash(void *ptr, uint length, - Item_result type, - CHARSET_INFO *cs, - Derivation dv) +/* + Set value to user variable. + + SYNOPSYS + update_hash() + entry - pointer to structure representing variable + set_null - should we set NULL value ? + ptr - pointer to buffer with new value + length - length of new value + type - type of new value + cs - charset info for new value + dv - derivation for new value + + RETURN VALUE + False - success, True - failure +*/ + +static bool +update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length, + Item_result type, CHARSET_INFO *cs, Derivation dv) { - if ((null_value=args[0]->null_value)) + if (set_null) { char *pos= (char*) entry+ ALIGN_SIZE(sizeof(user_var_entry)); if (entry->value && entry->value != pos) my_free(entry->value,MYF(0)); - entry->value=0; - entry->length=0; + entry->value= 0; + entry->length= 0; } else { @@ -2576,7 +3580,7 @@ bool Item_func_set_user_var::update_hash(void *ptr, uint length, entry->value=0; if (!(entry->value=(char*) my_realloc(entry->value, length, MYF(MY_ALLOW_ZERO_PTR)))) - goto err; + return 1; } } if (type == STRING_RESULT) @@ -2585,22 +3589,40 @@ bool Item_func_set_user_var::update_hash(void *ptr, uint length, entry->value[length]= 0; // Store end \0 } memcpy(entry->value,ptr,length); + if (type == DECIMAL_RESULT) + ((my_decimal*)entry->value)->fix_buffer_pointer(); entry->length= length; - entry->type=type; entry->collation.set(cs, dv); } + entry->type=type; return 0; +} - err: - current_thd->fatal_error(); // Probably end of memory - null_value= 1; - return 1; + +bool +Item_func_set_user_var::update_hash(void *ptr, uint length, Item_result type, + CHARSET_INFO *cs, Derivation dv) +{ + /* + If we set a variable explicitely to NULL then keep the old + result type of the variable + */ + if ((null_value= args[0]->null_value) && null_item) + type= entry->type; // Don't change type of item + if (::update_hash(entry, (null_value= args[0]->null_value), + ptr, length, type, cs, dv)) + { + current_thd->fatal_error(); // Probably end of memory + null_value= 1; + return 1; + } + return 0; } /* Get the value of a variable as a double */ -double user_var_entry::val(my_bool *null_value) +double user_var_entry::val_real(my_bool *null_value) { if ((*null_value= (value == 0))) return 0.0; @@ -2610,6 +3632,12 @@ double user_var_entry::val(my_bool *null_value) return *(double*) value; case INT_RESULT: return (double) *(longlong*) value; + case DECIMAL_RESULT: + { + double result; + my_decimal2double(E_DEC_FATAL_ERROR, (my_decimal *)value, &result); + return result; + } case STRING_RESULT: return my_atof(value); // This is null terminated case ROW_RESULT: @@ -2632,6 +3660,12 @@ longlong user_var_entry::val_int(my_bool *null_value) return (longlong) *(double*) value; case INT_RESULT: return *(longlong*) value; + case DECIMAL_RESULT: + { + longlong result; + my_decimal2int(E_DEC_FATAL_ERROR, (my_decimal *)value, 0, &result); + return result; + } case STRING_RESULT: { int error; @@ -2660,6 +3694,9 @@ String *user_var_entry::val_str(my_bool *null_value, String *str, case INT_RESULT: str->set(*(longlong*) value, &my_charset_bin); break; + case DECIMAL_RESULT: + my_decimal2string(E_DEC_FATAL_ERROR, (my_decimal *)value, 0, 0, 0, str); + break; case STRING_RESULT: if (str->copy(value, length, collation.collation)) str= 0; // EOM error @@ -2670,19 +3707,46 @@ String *user_var_entry::val_str(my_bool *null_value, String *str, return(str); } +/* Get the value of a variable as a decimal */ + +my_decimal *user_var_entry::val_decimal(my_bool *null_value, my_decimal *val) +{ + if ((*null_value= (value == 0))) + return 0; + + switch (type) { + case REAL_RESULT: + double2my_decimal(E_DEC_FATAL_ERROR, *(double*) value, val); + break; + case INT_RESULT: + int2my_decimal(E_DEC_FATAL_ERROR, *(longlong*) value, 0, val); + break; + case DECIMAL_RESULT: + val= (my_decimal *)value; + break; + case STRING_RESULT: + str2my_decimal(E_DEC_FATAL_ERROR, value, length, collation.collation, val); + break; + case ROW_RESULT: + DBUG_ASSERT(1); // Impossible + break; + } + return(val); +} + /* This functions is invoked on SET @variable or @variable:= expression. - Evaluete (and check expression), store results. + Evaluate (and check expression), store results. SYNOPSYS Item_func_set_user_var::check() NOTES - For now it always return OK. All problem with value evalueting - will be catched by thd->net.report_error check in sql_set_variables(). + For now it always return OK. All problem with value evaluating + will be caught by thd->net.report_error check in sql_set_variables(). RETURN - 0 - OK. + FALSE OK. */ bool @@ -2693,7 +3757,7 @@ Item_func_set_user_var::check() switch (cached_result_type) { case REAL_RESULT: { - save_result.vreal= args[0]->val(); + save_result.vreal= args[0]->val_real(); break; } case INT_RESULT: @@ -2706,13 +3770,18 @@ Item_func_set_user_var::check() save_result.vstr= args[0]->val_str(&value); break; } + case DECIMAL_RESULT: + { + save_result.vdec= args[0]->val_decimal(&decimal_buff); + break; + } case ROW_RESULT: default: - // This case should never be choosen + // This case should never be chosen DBUG_ASSERT(0); break; } - DBUG_RETURN(0); + DBUG_RETURN(FALSE); } @@ -2727,7 +3796,7 @@ Item_func_set_user_var::check() the value method used by the user RETURN - 0 Ok + 0 OK 1 EOM Error */ @@ -2764,9 +3833,20 @@ Item_func_set_user_var::update() DERIVATION_IMPLICIT); break; } + case DECIMAL_RESULT: + { + if (!save_result.vdec) // Null value + res= update_hash((void*) 0, 0, DECIMAL_RESULT, &my_charset_bin, + DERIVATION_IMPLICIT); + else + res= update_hash((void*) save_result.vdec, + sizeof(my_decimal), DECIMAL_RESULT, + &my_charset_bin, DERIVATION_IMPLICIT); + break; + } case ROW_RESULT: default: - // This case should never be choosen + // This case should never be chosen DBUG_ASSERT(0); break; } @@ -2774,12 +3854,12 @@ Item_func_set_user_var::update() } -double Item_func_set_user_var::val() +double Item_func_set_user_var::val_real() { DBUG_ASSERT(fixed == 1); check(); update(); // Store expression - return entry->val(&null_value); + return entry->val_real(&null_value); } longlong Item_func_set_user_var::val_int() @@ -2799,6 +3879,15 @@ String *Item_func_set_user_var::val_str(String *str) } +my_decimal *Item_func_set_user_var::val_decimal(my_decimal *val) +{ + DBUG_ASSERT(fixed == 1); + check(); + update(); // Store expression + return entry->val_decimal(&null_value, val); +} + + void Item_func_set_user_var::print(String *str) { str->append("(@", 2); @@ -2809,6 +3898,16 @@ void Item_func_set_user_var::print(String *str) } +void Item_func_set_user_var::print_as_stmt(String *str) +{ + str->append("set @", 5); + str->append(name.str, name.length); + str->append(":=", 2); + args[0]->print(str); + str->append(')'); +} + + String * Item_func_get_user_var::val_str(String *str) { @@ -2820,12 +3919,21 @@ Item_func_get_user_var::val_str(String *str) } -double Item_func_get_user_var::val() +double Item_func_get_user_var::val_real() { DBUG_ASSERT(fixed == 1); if (!var_entry) return 0.0; // No such variable - return (var_entry->val(&null_value)); + return (var_entry->val_real(&null_value)); +} + + +my_decimal *Item_func_get_user_var::val_decimal(my_decimal *dec) +{ + DBUG_ASSERT(fixed == 1); + if (!var_entry) + return 0; + return var_entry->val_decimal(&null_value, dec); } @@ -2856,7 +3964,7 @@ longlong Item_func_get_user_var::val_int() RETURN 0 OK - 1 Failed to put appropiate record into binary log + 1 Failed to put appropriate record into binary log */ @@ -2880,8 +3988,8 @@ int get_var_with_binlog(THD *thd, LEX_STRING &name, that it gets into the binlog (if it didn't, the slave could be influenced by a variable of the same name previously set by another thread). - We create it like if it had been explicitely set with SET before. - The 'new' mimicks what sql_yacc.yy does when 'SET @a=10;'. + We create it like if it had been explicitly set with SET before. + The 'new' mimics what sql_yacc.yy does when 'SET @a=10;'. sql_set_variables() is what is called from 'case SQLCOM_SET_OPTION' in dispatch_command()). Instead of building a one-element list to pass to sql_set_variables(), we could instead manually call check() and update(); @@ -2912,7 +4020,7 @@ int get_var_with_binlog(THD *thd, LEX_STRING &name, uint size; /* First we need to store value of var_entry, when the next situation - appers: + appears: > set @a:=1; > insert into t1 values (@a), (@a:=@a+1), (@a:=@a+1); We have to write to binlog value @a= 1; @@ -2968,13 +4076,21 @@ void Item_func_get_user_var::fix_length_and_dec() switch (var_entry->type) { case REAL_RESULT: max_length= DBL_DIG + 8; + break; case INT_RESULT: max_length= MAX_BIGINT_WIDTH; + decimals=0; break; case STRING_RESULT: max_length= MAX_BLOB_WIDTH; break; + case DECIMAL_RESULT: + max_length= DECIMAL_MAX_STR_LENGTH; + decimals= DECIMAL_MAX_SCALE; + break; case ROW_RESULT: // Keep compiler happy + default: + DBUG_ASSERT(0); break; } } @@ -3023,7 +4139,7 @@ bool Item_func_get_user_var::eq(const Item *item, bool binary_cmp) const return 1; // Same item is same. /* Check if other type is also a get_user_var() object */ if (item->type() != FUNC_ITEM || - ((Item_func*) item)->func_name() != func_name()) + ((Item_func*) item)->functype() != functype()) return 0; Item_func_get_user_var *other=(Item_func_get_user_var*) item; return (name.length == other->name.length && @@ -3031,6 +4147,76 @@ bool Item_func_get_user_var::eq(const Item *item, bool binary_cmp) const } +bool Item_user_var_as_out_param::fix_fields(THD *thd, Item **ref) +{ + DBUG_ASSERT(fixed == 0); + if (Item::fix_fields(thd, ref) || + !(entry= get_variable(&thd->user_vars, name, 1))) + return TRUE; + entry->type= STRING_RESULT; + /* + Let us set the same collation which is used for loading + of fields in LOAD DATA INFILE. + (Since Item_user_var_as_out_param is used only there). + */ + entry->collation.set(thd->variables.collation_database); + entry->update_query_id= thd->query_id; + return FALSE; +} + + +void Item_user_var_as_out_param::set_null_value(CHARSET_INFO* cs) +{ + if (::update_hash(entry, TRUE, 0, 0, STRING_RESULT, cs, + DERIVATION_IMPLICIT)) + current_thd->fatal_error(); // Probably end of memory +} + + +void Item_user_var_as_out_param::set_value(const char *str, uint length, + CHARSET_INFO* cs) +{ + if (::update_hash(entry, FALSE, (void*)str, length, STRING_RESULT, cs, + DERIVATION_IMPLICIT)) + current_thd->fatal_error(); // Probably end of memory +} + + +double Item_user_var_as_out_param::val_real() +{ + DBUG_ASSERT(0); + return 0.0; +} + + +longlong Item_user_var_as_out_param::val_int() +{ + DBUG_ASSERT(0); + return 0; +} + + +String* Item_user_var_as_out_param::val_str(String *str) +{ + DBUG_ASSERT(0); + return 0; +} + + +my_decimal* Item_user_var_as_out_param::val_decimal(my_decimal *decimal_buffer) +{ + DBUG_ASSERT(0); + return 0; +} + + +void Item_user_var_as_out_param::print(String *str) +{ + str->append('@'); + str->append(name.str,name.length); +} + + longlong Item_func_inet_aton::val_int() { DBUG_ASSERT(fixed == 1); @@ -3147,7 +4333,7 @@ void Item_func_match::init_search(bool no_order) } -bool Item_func_match::fix_fields(THD *thd, TABLE_LIST *tlist, Item **ref) +bool Item_func_match::fix_fields(THD *thd, Item **ref) { DBUG_ASSERT(fixed == 0); Item *item; @@ -3162,11 +4348,11 @@ bool Item_func_match::fix_fields(THD *thd, TABLE_LIST *tlist, Item **ref) modifications to find_best and auto_close as complement to auto_init code above. */ - if (Item_func::fix_fields(thd, tlist, ref) || + if (Item_func::fix_fields(thd, ref) || !args[0]->const_during_execution()) { my_error(ER_WRONG_ARGUMENTS,MYF(0),"AGAINST"); - return 1; + return TRUE; } const_item_cache=0; @@ -3189,7 +4375,7 @@ bool Item_func_match::fix_fields(THD *thd, TABLE_LIST *tlist, Item **ref) if (key == NO_SUCH_KEY && !(flags & FT_BOOL)) { my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH"); - return 1; + return TRUE; } table=((Item_field *)item)->field->table; if (!(table->file->table_flags() & HA_CAN_FULLTEXT)) @@ -3210,7 +4396,7 @@ bool Item_func_match::fix_index() if (key == NO_SUCH_KEY) return 0; - for (keynr=0 ; keynr < table->keys ; keynr++) + for (keynr=0 ; keynr < table->s->keys ; keynr++) { if ((table->key_info[keynr].flags & HA_FULLTEXT) && (table->keys_in_use_for_query.is_set(keynr))) @@ -3276,14 +4462,16 @@ err: key=NO_SUCH_KEY; return 0; } - my_error(ER_FT_MATCHING_KEY_NOT_FOUND,MYF(0)); + my_message(ER_FT_MATCHING_KEY_NOT_FOUND, + ER(ER_FT_MATCHING_KEY_NOT_FOUND), MYF(0)); return 1; } bool Item_func_match::eq(const Item *item, bool binary_cmp) const { - if (item->type() != FUNC_ITEM || ((Item_func*)item)->functype() != FT_FUNC || + if (item->type() != FUNC_ITEM || + ((Item_func*)item)->functype() != FT_FUNC || flags != ((Item_func_match*)item)->flags) return 0; @@ -3297,7 +4485,7 @@ bool Item_func_match::eq(const Item *item, bool binary_cmp) const } -double Item_func_match::val() +double Item_func_match::val_real() { DBUG_ASSERT(fixed == 1); DBUG_ENTER("Item_func_match::val"); @@ -3405,7 +4593,7 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, { if (!var->is_struct()) { - net_printf(thd, ER_VARIABLE_IS_NOT_STRUCT, base_name->str); + my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), base_name->str); return 0; } } @@ -3452,7 +4640,7 @@ Item *get_system_var(THD *thd, enum_var_type var_type, const char *var_name, if (!(item=var->item(thd, var_type, &null_lex_string))) return 0; // Impossible thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); - item->set_name(item_name, 0, system_charset_info); // Will use original name + item->set_name(item_name, 0, system_charset_info); // Will use original name return item; } @@ -3513,10 +4701,303 @@ longlong Item_func_is_used_lock::val_int() } -longlong Item_func_found_rows::val_int() +longlong Item_func_row_count::val_int() { DBUG_ASSERT(fixed == 1); THD *thd= current_thd; - return thd->found_rows(); + return thd->row_count_func; +} + + +Item_func_sp::Item_func_sp(Name_resolution_context *context_arg, sp_name *name) + :Item_func(), context(context_arg), m_name(name), m_sp(NULL), + result_field(NULL) +{ + maybe_null= 1; + m_name->init_qname(current_thd); + dummy_table= (TABLE*) sql_calloc(sizeof(TABLE)); +} + + +Item_func_sp::Item_func_sp(Name_resolution_context *context_arg, + sp_name *name, List<Item> &list) + :Item_func(list), context(context_arg), m_name(name), m_sp(NULL), + result_field(NULL) +{ + maybe_null= 1; + m_name->init_qname(current_thd); + dummy_table= (TABLE*) sql_calloc(sizeof(TABLE)); +} + +void +Item_func_sp::cleanup() +{ + if (result_field) + { + delete result_field; + result_field= NULL; + } + m_sp= NULL; + Item_func::cleanup(); +} + +const char * +Item_func_sp::func_name() const +{ + THD *thd= current_thd; + /* Calculate length to avoid reallocation of string for sure */ + uint len= ((m_name->m_db.length + + m_name->m_name.length)*2 + //characters*quoting + 2 + // ` and ` + 1 + // . + 1 + // end of string + ALIGN_SIZE(1)); // to avoid String reallocation + String qname((char *)alloc_root(thd->mem_root, len), len, + system_charset_info); + + qname.length(0); + append_identifier(thd, &qname, m_name->m_db.str, m_name->m_db.length); + qname.append('.'); + append_identifier(thd, &qname, m_name->m_name.str, m_name->m_name.length); + return qname.ptr(); +} + + +Field * +Item_func_sp::sp_result_field(void) const +{ + Field *field; + DBUG_ENTER("Item_func_sp::sp_result_field"); + + if (!m_sp) + { + if (!(m_sp= sp_find_function(current_thd, m_name, TRUE))) + { + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str); + DBUG_RETURN(0); + } + } + if (!dummy_table->s) + { + char *empty_name= (char *) ""; + TABLE_SHARE *share; + dummy_table->s= share= &dummy_table->share_not_to_be_used; + dummy_table->alias = empty_name; + dummy_table->maybe_null = maybe_null; + dummy_table->in_use= current_thd; + share->table_cache_key = empty_name; + share->table_name = empty_name; + } + field= m_sp->make_field(max_length, name, dummy_table); + DBUG_RETURN(field); +} + + +/* + Execute function & store value in field + + RETURN + 0 value <> NULL + 1 value = NULL or error +*/ + +int +Item_func_sp::execute(Field **flp) +{ + Item *it; + Field *f; + if (execute(&it)) + { + null_value= 1; + return 1; + } + if (!(f= *flp)) + { + *flp= f= sp_result_field(); + f->move_field((f->pack_length() > sizeof(result_buf)) ? + sql_alloc(f->pack_length()) : result_buf); + f->null_ptr= (uchar *)&null_value; + f->null_bit= 1; + } + it->save_in_field(f, 1); + return null_value= f->is_null(); +} + + +int +Item_func_sp::execute(Item **itp) +{ + DBUG_ENTER("Item_func_sp::execute"); + THD *thd= current_thd; + ulong old_client_capabilites; + int res= -1; + bool save_in_sub_stmt= thd->transaction.in_sub_stmt; + my_bool save_no_send_ok; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + st_sp_security_context save_ctx; +#endif + + if (! m_sp && ! (m_sp= sp_find_function(thd, m_name, TRUE))) + { + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str); + goto error; + } + + old_client_capabilites= thd->client_capabilities; + thd->client_capabilities &= ~CLIENT_MULTI_RESULTS; + +#ifndef EMBEDDED_LIBRARY + save_no_send_ok= thd->net.no_send_ok; + thd->net.no_send_ok= TRUE; +#endif + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_routine_access(thd, EXECUTE_ACL, + m_sp->m_db.str, m_sp->m_name.str, 0, 0)) + goto error_check; + sp_change_security_context(thd, m_sp, &save_ctx); + if (save_ctx.changed && + check_routine_access(thd, EXECUTE_ACL, + m_sp->m_db.str, m_sp->m_name.str, 0, 0)) + goto error_check_ctx; +#endif + /* + Like for SPs, we don't binlog the substatements. If the statement which + called this function is an update statement, it will be binlogged; but if + it's not (e.g. SELECT myfunc()) it won't be binlogged (documented known + problem). + */ + + tmp_disable_binlog(thd); /* don't binlog the substatements */ + thd->transaction.in_sub_stmt= TRUE; + + res= m_sp->execute_function(thd, args, arg_count, itp); + + thd->transaction.in_sub_stmt= save_in_sub_stmt; + reenable_binlog(thd); + if (res && mysql_bin_log.is_open() && + (m_sp->m_chistics->daccess == SP_CONTAINS_SQL || + m_sp->m_chistics->daccess == SP_MODIFIES_SQL_DATA)) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_FAILED_ROUTINE_BREAK_BINLOG, + ER(ER_FAILED_ROUTINE_BREAK_BINLOG)); + +#ifndef NO_EMBEDDED_ACCESS_CHECKS +error_check_ctx: + sp_restore_security_context(thd, m_sp, &save_ctx); +#endif + + thd->client_capabilities|= old_client_capabilites & CLIENT_MULTI_RESULTS; + +error_check: +#ifndef EMBEDDED_LIBRARY + thd->net.no_send_ok= save_no_send_ok; +#endif + + thd->client_capabilities|= old_client_capabilites & CLIENT_MULTI_RESULTS; + +error: + DBUG_RETURN(res); +} + + +void +Item_func_sp::make_field(Send_field *tmp_field) +{ + Field *field; + DBUG_ENTER("Item_func_sp::make_field"); + if ((field= sp_result_field())) + { + field->make_field(tmp_field); + delete field; + DBUG_VOID_RETURN; + } + init_make_field(tmp_field, MYSQL_TYPE_VARCHAR); + DBUG_VOID_RETURN; +} + + +enum enum_field_types +Item_func_sp::field_type() const +{ + Field *field; + DBUG_ENTER("Item_func_sp::field_type"); + + if (result_field) + DBUG_RETURN(result_field->type()); + if ((field= sp_result_field())) + { + enum_field_types result= field->type(); + delete field; + DBUG_RETURN(result); + } + DBUG_RETURN(MYSQL_TYPE_VARCHAR); +} + + +Item_result +Item_func_sp::result_type() const +{ + Field *field; + DBUG_ENTER("Item_func_sp::result_type"); + DBUG_PRINT("info", ("m_sp = %p", m_sp)); + + if (result_field) + DBUG_RETURN(result_field->result_type()); + if ((field= sp_result_field())) + { + Item_result result= field->result_type(); + delete field; + DBUG_RETURN(result); + } + DBUG_RETURN(STRING_RESULT); +} + +void +Item_func_sp::fix_length_and_dec() +{ + Field *field; + DBUG_ENTER("Item_func_sp::fix_length_and_dec"); + + if (result_field) + { + decimals= result_field->decimals(); + max_length= result_field->field_length; + DBUG_VOID_RETURN; + } + + if (!(field= sp_result_field())) + { + context->process_error(current_thd); + DBUG_VOID_RETURN; + } + decimals= field->decimals(); + max_length= field->field_length; + maybe_null= 1; + delete field; + DBUG_VOID_RETURN; +} + + +longlong Item_func_found_rows::val_int() +{ + DBUG_ASSERT(fixed == 1); + return current_thd->found_rows(); +} + + +Field * +Item_func_sp::tmp_table_field(TABLE *t_arg) +{ + Field *res= 0; + DBUG_ENTER("Item_func_sp::tmp_table_field"); + + if (m_sp) + res= m_sp->make_field(max_length, (const char *)name, t_arg); + + if (!res) + res= Item_func::tmp_table_field(t_arg); + + DBUG_RETURN(res); } |