summaryrefslogtreecommitdiff
path: root/sql/item_func.h
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.com>2020-06-05 12:57:42 +0400
committerAlexander Barkov <bar@mariadb.com>2020-06-06 11:33:11 +0400
commit79cdd7e76bbebf62629066166f43279095a0fcc9 (patch)
treef18fc41438f97b84427815e6b82727706c86b4c0 /sql/item_func.h
parenta793ae5bc1270b18ab9359363dedab951f9a912f (diff)
downloadmariadb-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.h153
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)