Commit 6d3186e3 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-23323 Rounding functions return a wrong data type for a BIT, ENUM, SET argument

Implementing dedicated fixing methods:
- Type_handler_bit::Item_func_round_fix_length_and_dec()
- Type_handler_bit::Item_func_int_val_fix_length_and_dec()
- Type_handler_typelib::Item_func_round_fix_length_and_dec()

because the inherited methods did not work well.

Fixing:
- Type_handler_typelib::Item_func_int_val_fix_length_and_dec
  It did not work well, because it used args[0]->max_length to
  calculate the result data type. In case of ENUM and SET it was
  not correct, because in FLOOR() and CEILING() context
  ENUM and SET return not more than 5 digits (65535 is the biggest
  possible value).

Misc:
- Changing the API of
    Type_handler_bit::Bit_decimal_notation_int_digits(const Item *item)
  to a more generic form:
    Type_handler_bit::Bit_decimal_notation_int_digits_by_nbits(uint nbits)

- Fixing Type_handler_bit::Bit_decimal_notation_int_digits_by_nbits() to
  return the exact number of decimal digits for all nbits 1..64.
  The old implementation was approximate.
  This change gives better (more precise) data types.
parent 92499ae9
This diff is collapsed.
This diff is collapsed.
...@@ -507,6 +507,35 @@ EXPLAIN SELECT * FROM t1 WHERE a=200; ...@@ -507,6 +507,35 @@ EXPLAIN SELECT * FROM t1 WHERE a=200;
EXPLAIN SELECT * FROM t1 WHERE a<=>200; EXPLAIN SELECT * FROM t1 WHERE a<=>200;
DROP TABLE t1; DROP TABLE t1;
--echo #
--echo # MDEV-23323 Rounding functions return a wrong data type for a BIT, ENUM, SET argument
--echo #
--vertical_results
DELIMITER $$;
BEGIN NOT ATOMIC
FOR i IN 1..64
DO
SELECT '-----', CONCAT('BIT(',i,')') AS Type;
EXECUTE IMMEDIATE REPLACE('CREATE TABLE t1 (a BIT(64))','64', i);
INSERT IGNORE INTO t1 VALUES (0xFFFFFFFFFFFFFFFF);
CREATE TABLE t2 AS SELECT
a,
FLOOR(a) AS cf,
CEILING(a) AS cc,
ROUND(a) AS cr,
TRUNCATE(a,0) AS ct
FROM t1;
SHOW CREATE TABLE t2;
SELECT CAST(a AS UNSIGNED) AS a, cf, cc, cr, ct FROM t2;
DROP TABLE t2;
DROP TABLE t1;
END FOR;
END;
$$
DELIMITER ;$$
--horizontal_results
--echo # --echo #
--echo # End of 10.4 tests --echo # End of 10.4 tests
......
...@@ -2311,3 +2311,24 @@ t2 CREATE TABLE `t2` ( ...@@ -2311,3 +2311,24 @@ t2 CREATE TABLE `t2` (
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
DROP PROCEDURE p1; DROP PROCEDURE p1;
DROP TABLE t1; DROP TABLE t1;
#
# MDEV-23323 Rounding functions return a wrong data type for a BIT, ENUM, SET argument
#
CREATE TABLE t1 (a ENUM('999999999999999999999999999999999999999999999999999999999999'));
INSERT INTO t1 VALUES (1);
CREATE TABLE t2 AS
SELECT a, FLOOR(a), CEILING(a), TRUNCATE(a,0), ROUND(a) FROM t1;
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`a` enum('999999999999999999999999999999999999999999999999999999999999') DEFAULT NULL,
`FLOOR(a)` int(5) unsigned DEFAULT NULL,
`CEILING(a)` int(5) unsigned DEFAULT NULL,
`TRUNCATE(a,0)` int(5) unsigned DEFAULT NULL,
`ROUND(a)` int(5) unsigned DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
SELECT * FROM t2;
a FLOOR(a) CEILING(a) TRUNCATE(a,0) ROUND(a)
999999999999999999999999999999999999999999999999999999999999 1 1 1 1
DROP TABLE t2;
DROP TABLE t1;
......
...@@ -518,3 +518,16 @@ DELIMITER ;$$ ...@@ -518,3 +518,16 @@ DELIMITER ;$$
CALL p1(); CALL p1();
DROP PROCEDURE p1; DROP PROCEDURE p1;
DROP TABLE t1; DROP TABLE t1;
--echo #
--echo # MDEV-23323 Rounding functions return a wrong data type for a BIT, ENUM, SET argument
--echo #
CREATE TABLE t1 (a ENUM('999999999999999999999999999999999999999999999999999999999999'));
INSERT INTO t1 VALUES (1);
CREATE TABLE t2 AS
SELECT a, FLOOR(a), CEILING(a), TRUNCATE(a,0), ROUND(a) FROM t1;
SHOW CREATE TABLE t2;
SELECT * FROM t2;
DROP TABLE t2;
DROP TABLE t1;
......
...@@ -358,3 +358,24 @@ DROP TABLE t1; ...@@ -358,3 +358,24 @@ DROP TABLE t1;
SET NAMES utf8; SET NAMES utf8;
CREATE TABLE t1 (a SET('a,bü')); CREATE TABLE t1 (a SET('a,bü'));
ERROR 22007: Illegal set 'a,bü' value found during parsing ERROR 22007: Illegal set 'a,bü' value found during parsing
#
# MDEV-23323 Rounding functions return a wrong data type for a BIT, ENUM, SET argument
#
CREATE TABLE t1 (a SET('999999999999999999999999999999999999999999999999999999999999'));
INSERT INTO t1 VALUES (1);
CREATE TABLE t2 AS
SELECT a, FLOOR(a), CEILING(a), TRUNCATE(a,0), ROUND(a) FROM t1;
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`a` set('999999999999999999999999999999999999999999999999999999999999') DEFAULT NULL,
`FLOOR(a)` int(5) unsigned DEFAULT NULL,
`CEILING(a)` int(5) unsigned DEFAULT NULL,
`TRUNCATE(a,0)` int(5) unsigned DEFAULT NULL,
`ROUND(a)` int(5) unsigned DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
SELECT * FROM t2;
a FLOOR(a) CEILING(a) TRUNCATE(a,0) ROUND(a)
999999999999999999999999999999999999999999999999999999999999 1 1 1 1
DROP TABLE t2;
DROP TABLE t1;
...@@ -248,3 +248,16 @@ DROP TABLE t1; ...@@ -248,3 +248,16 @@ DROP TABLE t1;
SET NAMES utf8; SET NAMES utf8;
--error ER_ILLEGAL_VALUE_FOR_TYPE --error ER_ILLEGAL_VALUE_FOR_TYPE
CREATE TABLE t1 (a SET('a,bü')); CREATE TABLE t1 (a SET('a,bü'));
--echo #
--echo # MDEV-23323 Rounding functions return a wrong data type for a BIT, ENUM, SET argument
--echo #
CREATE TABLE t1 (a SET('999999999999999999999999999999999999999999999999999999999999'));
INSERT INTO t1 VALUES (1);
CREATE TABLE t2 AS
SELECT a, FLOOR(a), CEILING(a), TRUNCATE(a,0), ROUND(a) FROM t1;
SHOW CREATE TABLE t2;
SELECT * FROM t2;
DROP TABLE t2;
DROP TABLE t1;
...@@ -2218,9 +2218,13 @@ bool Item_func_int_val::fix_length_and_dec() ...@@ -2218,9 +2218,13 @@ bool Item_func_int_val::fix_length_and_dec()
{ {
DBUG_ENTER("Item_func_int_val::fix_length_and_dec"); DBUG_ENTER("Item_func_int_val::fix_length_and_dec");
DBUG_PRINT("info", ("name %s", func_name())); DBUG_PRINT("info", ("name %s", func_name()));
if (args[0]->type_handler()->Item_func_int_val_fix_length_and_dec(this)) /*
We don't want to translate ENUM/SET to CHAR here.
So let's call real_type_handler(), not type_handler().
*/
if (args[0]->real_type_handler()->Item_func_int_val_fix_length_and_dec(this))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr())); DBUG_PRINT("info", ("Type: %s", real_type_handler()->name().ptr()));
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
} }
......
...@@ -458,6 +458,29 @@ class Item_hybrid_func: public Item_func, ...@@ -458,6 +458,29 @@ class Item_hybrid_func: public Item_func,
{ {
Type_geometry_attributes::set_geometry_type(type); Type_geometry_attributes::set_geometry_type(type);
} }
void fix_length_and_dec_long_or_longlong(uint char_length, bool unsigned_arg)
{
collation.set_numeric();
unsigned_flag= unsigned_arg;
max_length= char_length;
#if MARIADB_VERSION_ID < 100500
set_handler(Type_handler::type_handler_long_or_longlong(char_length));
#else
set_handler(Type_handler::type_handler_long_or_longlong(char_length,
unsigned_arg));
#endif
}
void fix_length_and_dec_ulong_or_ulonglong_by_nbits(uint nbits)
{
uint digits= Type_handler_bit::Bit_decimal_notation_int_digits_by_nbits(nbits);
collation.set_numeric();
unsigned_flag= true;
max_length= digits;
if (nbits > 32)
set_handler(&type_handler_longlong);
else
set_handler(&type_handler_long);
}
}; };
...@@ -1758,7 +1781,11 @@ class Item_func_round :public Item_func_hybrid_field_type ...@@ -1758,7 +1781,11 @@ class Item_func_round :public Item_func_hybrid_field_type
void fix_arg_temporal(const Type_handler *h, uint int_part_length); void fix_arg_temporal(const Type_handler *h, uint int_part_length);
bool fix_length_and_dec() bool fix_length_and_dec()
{ {
return args[0]->type_handler()->Item_func_round_fix_length_and_dec(this); /*
We don't want to translate ENUM/SET to CHAR here.
So let's real_type_handler(), not type_handler().
*/
return args[0]->real_type_handler()->Item_func_round_fix_length_and_dec(this);
} }
Item *get_copy(THD *thd) Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_round>(thd, this); } { return get_item_copy<Item_func_round>(thd, this); }
......
...@@ -3536,7 +3536,7 @@ uint32 ...@@ -3536,7 +3536,7 @@ uint32
Type_handler_bit::Item_decimal_notation_int_digits(const Item *item) Type_handler_bit::Item_decimal_notation_int_digits(const Item *item)
const const
{ {
return Bit_decimal_notation_int_digits(item); return Bit_decimal_notation_int_digits_by_nbits(item->max_length);
} }
...@@ -3554,9 +3554,23 @@ Type_handler_general_purpose_int::Item_decimal_notation_int_digits( ...@@ -3554,9 +3554,23 @@ Type_handler_general_purpose_int::Item_decimal_notation_int_digits(
a divisor. a divisor.
*/ */
uint32 uint32
Type_handler_bit::Bit_decimal_notation_int_digits(const Item *item) Type_handler_bit::Bit_decimal_notation_int_digits_by_nbits(uint nbits)
{ {
return item->max_length/3+1; DBUG_ASSERT(nbits > 0);
DBUG_ASSERT(nbits <= 64);
set_if_smaller(nbits, 64); // Safety
static uint ndigits[65]=
{0,
1,1,1,2,2,2,3,3, // 1..8 bits
3,4,4,4,4,5,5,5, // 9..16 bits
6,6,6,7,7,7,7,8, // 17..24 bits
8,8,9,9,9,10,10,10, // 25..32 bits
10,11,11,11,12,12,12,13, // 33..40 bits
13,13,13,14,14,14,15,15, // 41..48 bits
15,16,16,16,16,17,17,17, // 49..56 bits
18,18,18,19,19,19,19,20 // 57..64 bits
};
return ndigits[nbits];
} }
/*************************************************************************/ /*************************************************************************/
...@@ -5658,6 +5672,23 @@ bool Type_handler_hex_hybrid:: ...@@ -5658,6 +5672,23 @@ bool Type_handler_hex_hybrid::
} }
bool Type_handler_bit::
Item_func_round_fix_length_and_dec(Item_func_round *item) const
{
uint nbits= item->arguments()[0]->max_length;
item->fix_length_and_dec_ulong_or_ulonglong_by_nbits(nbits);
return false;
}
bool Type_handler_typelib::
Item_func_round_fix_length_and_dec(Item_func_round *item) const
{
item->fix_length_and_dec_long_or_longlong(5, true);
return false;
}
bool Type_handler_real_result:: bool Type_handler_real_result::
Item_func_round_fix_length_and_dec(Item_func_round *item) const Item_func_round_fix_length_and_dec(Item_func_round *item) const
{ {
...@@ -5740,10 +5771,19 @@ bool Type_handler_int_result:: ...@@ -5740,10 +5771,19 @@ bool Type_handler_int_result::
} }
bool Type_handler_bit::
Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
{
uint nbits= item->arguments()[0]->max_length;
item->fix_length_and_dec_ulong_or_ulonglong_by_nbits(nbits);
return false;
}
bool Type_handler_typelib:: bool Type_handler_typelib::
Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
{ {
item->fix_length_and_dec_int_or_decimal(); item->fix_length_and_dec_long_or_longlong(5, true);
return false; return false;
} }
...@@ -5751,14 +5791,8 @@ bool Type_handler_typelib:: ...@@ -5751,14 +5791,8 @@ bool Type_handler_typelib::
bool Type_handler_hex_hybrid:: bool Type_handler_hex_hybrid::
Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const
{ {
item->collation.set_numeric(); uint nchars= item->arguments()[0]->decimal_precision();
item->unsigned_flag= true; item->fix_length_and_dec_long_or_longlong(nchars, true);
item->max_length= item->arguments()[0]->decimal_precision();
#if MARIADB_VERSION_ID < 100500
item->set_handler(type_handler_long_or_longlong(item->max_length));
#else
item->set_handler(type_handler_long_or_longlong(item->max_length, true));
#endif
return false; return false;
} }
......
...@@ -5147,7 +5147,7 @@ class Type_handler_bit: public Type_handler_int_result ...@@ -5147,7 +5147,7 @@ class Type_handler_bit: public Type_handler_int_result
} }
uint32 max_display_length(const Item *item) const; uint32 max_display_length(const Item *item) const;
uint32 Item_decimal_notation_int_digits(const Item *item) const; uint32 Item_decimal_notation_int_digits(const Item *item) const;
static uint32 Bit_decimal_notation_int_digits(const Item *item); static uint32 Bit_decimal_notation_int_digits_by_nbits(uint nbits);
uint32 calc_pack_length(uint32 length) const { return length / 8; } uint32 calc_pack_length(uint32 length) const { return length / 8; }
bool Item_send(Item *item, Protocol *protocol, st_value *buf) const bool Item_send(Item *item, Protocol *protocol, st_value *buf) const
{ {
...@@ -5157,6 +5157,8 @@ class Type_handler_bit: public Type_handler_int_result ...@@ -5157,6 +5157,8 @@ class Type_handler_bit: public Type_handler_int_result
{ {
return print_item_value_csstr(thd, item, str); return print_item_value_csstr(thd, item, str);
} }
bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
Field *make_conversion_table_field(TABLE *, uint metadata, Field *make_conversion_table_field(TABLE *, uint metadata,
const Field *target) const; const Field *target) const;
bool Column_definition_fix_attributes(Column_definition *c) const; bool Column_definition_fix_attributes(Column_definition *c) const;
...@@ -6271,6 +6273,7 @@ class Type_handler_typelib: public Type_handler_general_purpose_string ...@@ -6271,6 +6273,7 @@ class Type_handler_typelib: public Type_handler_general_purpose_string
enum_field_types field_type() const { return MYSQL_TYPE_STRING; } enum_field_types field_type() const { return MYSQL_TYPE_STRING; }
const Type_handler *type_handler_for_item_field() const; const Type_handler *type_handler_for_item_field() const;
const Type_handler *cast_to_int_type_handler() const; const Type_handler *cast_to_int_type_handler() const;
bool Item_func_round_fix_length_and_dec(Item_func_round *) const;
bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const; bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const;
bool Item_hybrid_func_fix_attributes(THD *thd, bool Item_hybrid_func_fix_attributes(THD *thd,
const char *name, const char *name,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment