diff options
author | Alexander Barkov <bar@mariadb.com> | 2020-06-05 12:57:42 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.com> | 2020-06-06 11:33:11 +0400 |
commit | 79cdd7e76bbebf62629066166f43279095a0fcc9 (patch) | |
tree | f18fc41438f97b84427815e6b82727706c86b4c0 /sql/item_func.h | |
parent | a793ae5bc1270b18ab9359363dedab951f9a912f (diff) | |
download | mariadb-git-79cdd7e76bbebf62629066166f43279095a0fcc9.tar.gz |
MDEV-20305 Data loss on DOUBLE and DECIMAL conversion to INT
Bit operators (~ ^ | & << >>) and the function BIT_COUNT()
always called val_int() for their arguments.
It worked correctly only for INT type arguments.
In case of DECIMAL and DOUBLE arguments it did not work well:
the argument values were truncated to the maximum SIGNED BIGINT value
of 9223372036854775807.
Fixing the code as follows:
- If the argument if of an integer data type,
it works using val_int() as before.
- If the argument if of some other data type, it gets the argument value
using val_decimal(), to avoid truncation, and then converts the result
to ulonglong.
Using Item_handled_func to switch between the two approaches easier.
As an additional advantage, with Item_handled_func it will be easier
to implement overloading in the future, so data type plugings will be able
to define their own behavioir of bit operators and BIT_COUNT().
Moving the code from the former val_int() implementations
as methods to Longlong_null, to avoid code duplication in the
INT and DECIMAL branches.
Diffstat (limited to 'sql/item_func.h')
-rw-r--r-- | sql/item_func.h | 153 |
1 files changed, 125 insertions, 28 deletions
diff --git a/sql/item_func.h b/sql/item_func.h index 3771992d617..ca72563581b 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -641,6 +641,87 @@ public: }; + class Handler_int: public Handler + { + public: + String *val_str(Item_handled_func *item, String *to) const + { + longlong nr= val_int(item); + if (item->null_value) + return 0; + to->set_int(nr, item->unsigned_flag, item->collation.collation); + return to; + } + String *val_str_ascii(Item_handled_func *item, String *to) const + { + return item->Item::val_str_ascii(to); + } + double val_real(Item_handled_func *item) const + { + return item->unsigned_flag ? (double) ((ulonglong) val_int(item)) : + (double) val_int(item); + } + my_decimal *val_decimal(Item_handled_func *item, my_decimal *to) const + { + return item->val_decimal_from_int(to); + } + bool get_date(THD *thd, Item_handled_func *item, + MYSQL_TIME *to, date_mode_t fuzzydate) const + { + return item->get_date_from_int(thd, to, fuzzydate); + } + longlong val_int(Item_handled_func *item) const + { + Longlong_null tmp= to_longlong_null(item); + item->null_value= tmp.is_null(); + return tmp.value(); + } + virtual Longlong_null to_longlong_null(Item_handled_func *item) const= 0; + }; + + class Handler_slong: public Handler_int + { + public: + const Type_handler *return_type_handler(const Item_handled_func *item) const + { + return &type_handler_slong; + } + bool fix_length_and_dec(Item_handled_func *item) const + { + item->unsigned_flag= false; + item->collation= DTCollation_numeric(); + item->fix_char_length(11); + return false; + } + }; + + class Handler_slong2: public Handler_slong + { + public: + bool fix_length_and_dec(Item_handled_func *func) const + { + bool rc= Handler_slong::fix_length_and_dec(func); + func->max_length= 2; + return rc; + } + }; + + class Handler_ulonglong: public Handler_int + { + public: + const Type_handler *return_type_handler(const Item_handled_func *item) const + { + return &type_handler_ulonglong; + } + bool fix_length_and_dec(Item_handled_func *item) const + { + item->unsigned_flag= true; + item->collation= DTCollation_numeric(); + item->fix_char_length(21); + return false; + } + }; + protected: const Handler *m_func_handler; public: @@ -2188,84 +2269,99 @@ public: /* Base class for all bit functions: '~', '|', '^', '&', '>>', '<<' */ -class Item_func_bit: public Item_longlong_func +class Item_func_bit_operator: public Item_handled_func { bool check_arguments() const { return check_argument_types_can_return_int(0, arg_count); } +protected: + bool fix_length_and_dec_op1_std(const Handler *ha_int, const Handler *ha_dec) + { + set_func_handler(args[0]->cmp_type() == INT_RESULT ? ha_int : ha_dec); + return m_func_handler->fix_length_and_dec(this); + } + bool fix_length_and_dec_op2_std(const Handler *ha_int, const Handler *ha_dec) + { + set_func_handler(args[0]->cmp_type() == INT_RESULT && + args[1]->cmp_type() == INT_RESULT ? ha_int : ha_dec); + return m_func_handler->fix_length_and_dec(this); + } public: - Item_func_bit(THD *thd, Item *a, Item *b): Item_longlong_func(thd, a, b) {} - Item_func_bit(THD *thd, Item *a): Item_longlong_func(thd, a) {} - bool fix_length_and_dec() { unsigned_flag= 1; return FALSE; } - - virtual inline void print(String *str, enum_query_type query_type) + Item_func_bit_operator(THD *thd, Item *a) + :Item_handled_func(thd, a) {} + Item_func_bit_operator(THD *thd, Item *a, Item *b) + :Item_handled_func(thd, a, b) {} + void print(String *str, enum_query_type query_type) { print_op(str, query_type); } bool need_parentheses_in_default() { return true; } }; -class Item_func_bit_or :public Item_func_bit +class Item_func_bit_or :public Item_func_bit_operator { public: - Item_func_bit_or(THD *thd, Item *a, Item *b): Item_func_bit(thd, a, b) {} - longlong val_int(); + Item_func_bit_or(THD *thd, Item *a, Item *b) + :Item_func_bit_operator(thd, a, b) {} + bool fix_length_and_dec(); const char *func_name() const { return "|"; } enum precedence precedence() const { return BITOR_PRECEDENCE; } Item *get_copy(THD *thd) { return get_item_copy<Item_func_bit_or>(thd, this); } }; -class Item_func_bit_and :public Item_func_bit +class Item_func_bit_and :public Item_func_bit_operator { public: - Item_func_bit_and(THD *thd, Item *a, Item *b): Item_func_bit(thd, a, b) {} - longlong val_int(); + Item_func_bit_and(THD *thd, Item *a, Item *b) + :Item_func_bit_operator(thd, a, b) {} + bool fix_length_and_dec(); const char *func_name() const { return "&"; } enum precedence precedence() const { return BITAND_PRECEDENCE; } Item *get_copy(THD *thd) { return get_item_copy<Item_func_bit_and>(thd, this); } }; -class Item_func_bit_count :public Item_long_func +class Item_func_bit_count :public Item_handled_func { bool check_arguments() const { return args[0]->check_type_can_return_int(func_name()); } public: - Item_func_bit_count(THD *thd, Item *a): Item_long_func(thd, a) {} - longlong val_int(); + Item_func_bit_count(THD *thd, Item *a): Item_handled_func(thd, a) {} const char *func_name() const { return "bit_count"; } - bool fix_length_and_dec() { max_length=2; return FALSE; } + bool fix_length_and_dec(); Item *get_copy(THD *thd) { return get_item_copy<Item_func_bit_count>(thd, this); } }; -class Item_func_shift_left :public Item_func_bit +class Item_func_shift_left :public Item_func_bit_operator { public: - Item_func_shift_left(THD *thd, Item *a, Item *b): Item_func_bit(thd, a, b) {} - longlong val_int(); + Item_func_shift_left(THD *thd, Item *a, Item *b) + :Item_func_bit_operator(thd, a, b) {} + bool fix_length_and_dec(); const char *func_name() const { return "<<"; } enum precedence precedence() const { return SHIFT_PRECEDENCE; } Item *get_copy(THD *thd) { return get_item_copy<Item_func_shift_left>(thd, this); } }; -class Item_func_shift_right :public Item_func_bit +class Item_func_shift_right :public Item_func_bit_operator { public: - Item_func_shift_right(THD *thd, Item *a, Item *b): Item_func_bit(thd, a, b) {} - longlong val_int(); + Item_func_shift_right(THD *thd, Item *a, Item *b) + :Item_func_bit_operator(thd, a, b) {} + bool fix_length_and_dec(); const char *func_name() const { return ">>"; } enum precedence precedence() const { return SHIFT_PRECEDENCE; } Item *get_copy(THD *thd) { return get_item_copy<Item_func_shift_right>(thd, this); } }; -class Item_func_bit_neg :public Item_func_bit +class Item_func_bit_neg :public Item_func_bit_operator { public: - Item_func_bit_neg(THD *thd, Item *a): Item_func_bit(thd, a) {} - longlong val_int(); + Item_func_bit_neg(THD *thd, Item *a): Item_func_bit_operator(thd, a) {} + bool fix_length_and_dec(); const char *func_name() const { return "~"; } enum precedence precedence() const { return NEG_PRECEDENCE; } void print(String *str, enum_query_type query_type) @@ -3156,11 +3252,12 @@ private: }; -class Item_func_bit_xor : public Item_func_bit +class Item_func_bit_xor : public Item_func_bit_operator { public: - Item_func_bit_xor(THD *thd, Item *a, Item *b): Item_func_bit(thd, a, b) {} - longlong val_int(); + Item_func_bit_xor(THD *thd, Item *a, Item *b) + :Item_func_bit_operator(thd, a, b) {} + bool fix_length_and_dec(); const char *func_name() const { return "^"; } enum precedence precedence() const { return BITXOR_PRECEDENCE; } Item *get_copy(THD *thd) |