Commit 79cdd7e7 authored by Alexander Barkov's avatar Alexander Barkov

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.
parent a793ae5b
#
# Start of 10.5 tests
#
#
# MDEV-20305 Data loss on DOUBLE and DECIMAL conversion to INT
#
CREATE PROCEDURE p1(type VARCHAR(64), val VARCHAR(64))
BEGIN
EXECUTE IMMEDIATE CONCAT('CREATE TABLE t1 (a ', type, ')');
SHOW CREATE TABLE t1;
EXECUTE IMMEDIATE CONCAT('INSERT INTO t1 VALUES (', val, ')');
SELECT
a,
~a,
a & 18446744073709551615,
18446744073709551615 & a,
0 | a,
a | 0,
a << 0,
a >> 0,
a ^ 1,
1 ^ a,
BIT_COUNT(a)
FROM t1;
SHOW WARNINGS;
DROP TABLE t1;
END;
$$
CALL p1('BIGINT UNSIGNED', 18446744073709551615);
Table t1
Create Table CREATE TABLE `t1` (
`a` bigint(20) unsigned DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
a 18446744073709551615
~a 0
a & 18446744073709551615 18446744073709551615
18446744073709551615 & a 18446744073709551615
0 | a 18446744073709551615
a | 0 18446744073709551615
a << 0 18446744073709551615
a >> 0 18446744073709551615
a ^ 1 18446744073709551614
1 ^ a 18446744073709551614
BIT_COUNT(a) 64
CALL p1('DOUBLE', 18446744073709551615);
Table t1
Create Table CREATE TABLE `t1` (
`a` double DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
a 1.8446744073709552e19
~a 0
a & 18446744073709551615 18446744073709551615
18446744073709551615 & a 18446744073709551615
0 | a 18446744073709551615
a | 0 18446744073709551615
a << 0 18446744073709551615
a >> 0 18446744073709551615
a ^ 1 18446744073709551614
1 ^ a 18446744073709551614
BIT_COUNT(a) 64
Level Warning
Code 1916
Message Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
Level Warning
Code 1916
Message Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
Level Warning
Code 1916
Message Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
Level Warning
Code 1916
Message Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
Level Warning
Code 1916
Message Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
Level Warning
Code 1916
Message Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
Level Warning
Code 1916
Message Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
Level Warning
Code 1916
Message Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
Level Warning
Code 1916
Message Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
Level Warning
Code 1916
Message Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
CALL p1('DECIMAL(30,0)', 18446744073709551615);
Table t1
Create Table CREATE TABLE `t1` (
`a` decimal(30,0) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
a 18446744073709551615
~a 0
a & 18446744073709551615 18446744073709551615
18446744073709551615 & a 18446744073709551615
0 | a 18446744073709551615
a | 0 18446744073709551615
a << 0 18446744073709551615
a >> 0 18446744073709551615
a ^ 1 18446744073709551614
1 ^ a 18446744073709551614
BIT_COUNT(a) 64
CALL p1('BIGINT', -1);
Table t1
Create Table CREATE TABLE `t1` (
`a` bigint(20) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
a -1
~a 0
a & 18446744073709551615 18446744073709551615
18446744073709551615 & a 18446744073709551615
0 | a 18446744073709551615
a | 0 18446744073709551615
a << 0 18446744073709551615
a >> 0 18446744073709551615
a ^ 1 18446744073709551614
1 ^ a 18446744073709551614
BIT_COUNT(a) 64
CALL p1('DOUBLE', -1);
Table t1
Create Table CREATE TABLE `t1` (
`a` double DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
a -1
~a 0
a & 18446744073709551615 18446744073709551615
18446744073709551615 & a 18446744073709551615
0 | a 18446744073709551615
a | 0 18446744073709551615
a << 0 18446744073709551615
a >> 0 18446744073709551615
a ^ 1 18446744073709551614
1 ^ a 18446744073709551614
BIT_COUNT(a) 64
CALL p1('DECIMAL(30,0)', -1);
Table t1
Create Table CREATE TABLE `t1` (
`a` decimal(30,0) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
a -1
~a 0
a & 18446744073709551615 18446744073709551615
18446744073709551615 & a 18446744073709551615
0 | a 18446744073709551615
a | 0 18446744073709551615
a << 0 18446744073709551615
a >> 0 18446744073709551615
a ^ 1 18446744073709551614
1 ^ a 18446744073709551614
BIT_COUNT(a) 64
CALL p1('BIGINT', -9223372036854775808);
Table t1
Create Table CREATE TABLE `t1` (
`a` bigint(20) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
a -9223372036854775808
~a 9223372036854775807
a & 18446744073709551615 9223372036854775808
18446744073709551615 & a 9223372036854775808
0 | a 9223372036854775808
a | 0 9223372036854775808
a << 0 9223372036854775808
a >> 0 9223372036854775808
a ^ 1 9223372036854775809
1 ^ a 9223372036854775809
BIT_COUNT(a) 1
CALL p1('DOUBLE', -9223372036854775808);
Table t1
Create Table CREATE TABLE `t1` (
`a` double DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
a -9.223372036854776e18
~a 9223372036854775807
a & 18446744073709551615 9223372036854775808
18446744073709551615 & a 9223372036854775808
0 | a 9223372036854775808
a | 0 9223372036854775808
a << 0 9223372036854775808
a >> 0 9223372036854775808
a ^ 1 9223372036854775809
1 ^ a 9223372036854775809
BIT_COUNT(a) 1
Level Warning
Code 1916
Message Got overflow when converting '-9223372036854776000' to INT. Value truncated
Level Warning
Code 1916
Message Got overflow when converting '-9223372036854776000' to INT. Value truncated
Level Warning
Code 1916
Message Got overflow when converting '-9223372036854776000' to INT. Value truncated
Level Warning
Code 1916
Message Got overflow when converting '-9223372036854776000' to INT. Value truncated
Level Warning
Code 1916
Message Got overflow when converting '-9223372036854776000' to INT. Value truncated
Level Warning
Code 1916
Message Got overflow when converting '-9223372036854776000' to INT. Value truncated
Level Warning
Code 1916
Message Got overflow when converting '-9223372036854776000' to INT. Value truncated
Level Warning
Code 1916
Message Got overflow when converting '-9223372036854776000' to INT. Value truncated
Level Warning
Code 1916
Message Got overflow when converting '-9223372036854776000' to INT. Value truncated
Level Warning
Code 1916
Message Got overflow when converting '-9223372036854776000' to INT. Value truncated
CALL p1('DECIMAL(30,0)', -9223372036854775808);
Table t1
Create Table CREATE TABLE `t1` (
`a` decimal(30,0) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
a -9223372036854775808
~a 9223372036854775807
a & 18446744073709551615 9223372036854775808
18446744073709551615 & a 9223372036854775808
0 | a 9223372036854775808
a | 0 9223372036854775808
a << 0 9223372036854775808
a >> 0 9223372036854775808
a ^ 1 9223372036854775809
1 ^ a 9223372036854775809
BIT_COUNT(a) 1
DROP PROCEDURE p1;
SELECT CAST(CAST(18446744073709551615 AS UNSIGNED) AS DECIMAL(32))<<0 AS c1;
c1
18446744073709551615
SELECT CAST(CAST(18446744073709551615 AS UNSIGNED) AS DOUBLE)<<0 AS c1;
c1
18446744073709551615
Warnings:
Warning 1916 Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
SELECT COALESCE(CAST(CAST(0xFFFFFFFFFFFFFFFF AS UNSIGNED) AS DECIMAL(32))) << 0 AS c1;
c1
18446744073709551615
SELECT COALESCE(CAST(CAST(0xFFFFFFFFFFFFFFFF AS UNSIGNED) AS DOUBLE)) << 0 AS c1;
c1
18446744073709551615
Warnings:
Warning 1916 Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
SELECT 18446744073709551615 ^ 1 AS c1;
c1
18446744073709551614
SELECT 18446744073709551615.0 ^ 1 AS c1;
c1
18446744073709551614
SELECT 18446744073709551615e0 ^ 1 AS c1;
c1
18446744073709551614
Warnings:
Warning 1916 Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
SELECT LAST_VALUE(18446744073709551615) ^ 1 AS c1;
c1
18446744073709551614
SELECT LAST_VALUE(18446744073709551615.0) ^ 1 AS c1;
c1
18446744073709551614
SELECT LAST_VALUE(18446744073709551615e0) ^ 1 AS c1;
c1
18446744073709551614
Warnings:
Warning 1916 Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
SELECT 18446744073709551615 & 18446744073709551615 AS c1;
c1
18446744073709551615
SELECT 18446744073709551615 & 18446744073709551615.0 AS c1;
c1
18446744073709551615
SELECT 18446744073709551615 & 18446744073709551615e0 AS c1;
c1
18446744073709551615
Warnings:
Warning 1916 Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
SELECT 18446744073709551615.0 & 18446744073709551615 AS c1;
c1
18446744073709551615
SELECT 18446744073709551615.0 & 18446744073709551615.0 AS c1;
c1
18446744073709551615
SELECT 18446744073709551615.0 & 18446744073709551615e0 AS c1;
c1
18446744073709551615
Warnings:
Warning 1916 Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
SELECT 18446744073709551615e0 & 18446744073709551615 AS c1;
c1
18446744073709551615
Warnings:
Warning 1916 Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
SELECT 18446744073709551615e0 & 18446744073709551615.0 AS c1;
c1
18446744073709551615
Warnings:
Warning 1916 Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
SELECT 18446744073709551615e0 & 18446744073709551615e0 AS c1;
c1
18446744073709551615
Warnings:
Warning 1916 Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
Warning 1916 Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
SELECT 0 | 18446744073709551615 AS c1;
c1
18446744073709551615
SELECT 0 | 18446744073709551615.0 AS c1;
c1
18446744073709551615
SELECT 0 | 18446744073709551615e0 AS c1;
c1
18446744073709551615
Warnings:
Warning 1916 Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
SELECT 18446744073709551615 | 0 AS c1;
c1
18446744073709551615
SELECT 18446744073709551615.0 | 0 AS c1;
c1
18446744073709551615
SELECT 18446744073709551615e0 | 0 AS c1;
c1
18446744073709551615
Warnings:
Warning 1916 Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
SELECT ~18446744073709551615 AS c1;
c1
0
SELECT ~18446744073709551615.0 AS c1;
c1
0
SELECT ~18446744073709551615e0 AS c1;
c1
0
Warnings:
Warning 1916 Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
SELECT BIT_COUNT(18446744073709551615) AS c1;
c1
64
SELECT BIT_COUNT(18446744073709551615.0) AS c1;
c1
64
SELECT BIT_COUNT(18446744073709551615e0) AS c1;
c1
64
Warnings:
Warning 1916 Got overflow when converting '18446744073709552000' to UNSIGNED INT. Value truncated
SELECT BIT_COUNT(-9223372036854775808) AS c1;
c1
1
SELECT BIT_COUNT(-9223372036854775808.0) AS c1;
c1
1
SELECT BIT_COUNT(-9223372036854775808e0) AS c1;
c1
1
Warnings:
Warning 1916 Got overflow when converting '-9223372036854776000' to INT. Value truncated
#
# End of 10.5 tests
#
--echo #
--echo # Start of 10.5 tests
--echo #
--echo #
--echo # MDEV-20305 Data loss on DOUBLE and DECIMAL conversion to INT
--echo #
DELIMITER $$;
CREATE PROCEDURE p1(type VARCHAR(64), val VARCHAR(64))
BEGIN
EXECUTE IMMEDIATE CONCAT('CREATE TABLE t1 (a ', type, ')');
SHOW CREATE TABLE t1;
EXECUTE IMMEDIATE CONCAT('INSERT INTO t1 VALUES (', val, ')');
SELECT
a,
~a,
a & 18446744073709551615,
18446744073709551615 & a,
0 | a,
a | 0,
a << 0,
a >> 0,
a ^ 1,
1 ^ a,
BIT_COUNT(a)
FROM t1;
SHOW WARNINGS;
DROP TABLE t1;
END;
$$
DELIMITER ;$$
--vertical_results
CALL p1('BIGINT UNSIGNED', 18446744073709551615);
CALL p1('DOUBLE', 18446744073709551615);
CALL p1('DECIMAL(30,0)', 18446744073709551615);
CALL p1('BIGINT', -1);
CALL p1('DOUBLE', -1);
CALL p1('DECIMAL(30,0)', -1);
CALL p1('BIGINT', -9223372036854775808);
CALL p1('DOUBLE', -9223372036854775808);
CALL p1('DECIMAL(30,0)', -9223372036854775808);
--horizontal_results
DROP PROCEDURE p1;
SELECT CAST(CAST(18446744073709551615 AS UNSIGNED) AS DECIMAL(32))<<0 AS c1;
SELECT CAST(CAST(18446744073709551615 AS UNSIGNED) AS DOUBLE)<<0 AS c1;
SELECT COALESCE(CAST(CAST(0xFFFFFFFFFFFFFFFF AS UNSIGNED) AS DECIMAL(32))) << 0 AS c1;
SELECT COALESCE(CAST(CAST(0xFFFFFFFFFFFFFFFF AS UNSIGNED) AS DOUBLE)) << 0 AS c1;
SELECT 18446744073709551615 ^ 1 AS c1;
SELECT 18446744073709551615.0 ^ 1 AS c1;
SELECT 18446744073709551615e0 ^ 1 AS c1;
SELECT LAST_VALUE(18446744073709551615) ^ 1 AS c1;
SELECT LAST_VALUE(18446744073709551615.0) ^ 1 AS c1;
SELECT LAST_VALUE(18446744073709551615e0) ^ 1 AS c1;
SELECT 18446744073709551615 & 18446744073709551615 AS c1;
SELECT 18446744073709551615 & 18446744073709551615.0 AS c1;
SELECT 18446744073709551615 & 18446744073709551615e0 AS c1;
SELECT 18446744073709551615.0 & 18446744073709551615 AS c1;
SELECT 18446744073709551615.0 & 18446744073709551615.0 AS c1;
SELECT 18446744073709551615.0 & 18446744073709551615e0 AS c1;
SELECT 18446744073709551615e0 & 18446744073709551615 AS c1;
SELECT 18446744073709551615e0 & 18446744073709551615.0 AS c1;
SELECT 18446744073709551615e0 & 18446744073709551615e0 AS c1;
SELECT 0 | 18446744073709551615 AS c1;
SELECT 0 | 18446744073709551615.0 AS c1;
SELECT 0 | 18446744073709551615e0 AS c1;
SELECT 18446744073709551615 | 0 AS c1;
SELECT 18446744073709551615.0 | 0 AS c1;
SELECT 18446744073709551615e0 | 0 AS c1;
SELECT ~18446744073709551615 AS c1;
SELECT ~18446744073709551615.0 AS c1;
SELECT ~18446744073709551615e0 AS c1;
SELECT BIT_COUNT(18446744073709551615) AS c1;
SELECT BIT_COUNT(18446744073709551615.0) AS c1;
SELECT BIT_COUNT(18446744073709551615e0) AS c1;
SELECT BIT_COUNT(-9223372036854775808) AS c1;
SELECT BIT_COUNT(-9223372036854775808.0) AS c1;
SELECT BIT_COUNT(-9223372036854775808e0) AS c1;
--echo #
--echo # End of 10.5 tests
--echo #
......@@ -2326,7 +2326,7 @@ SELECT MAX('x') << 1, CAST(MAX('x') AS DOUBLE), CAST(MAX('x') AS DECIMAL);
MAX('x') << 1 CAST(MAX('x') AS DOUBLE) CAST(MAX('x') AS DECIMAL)
0 0 0
Warnings:
Warning 1292 Truncated incorrect INTEGER value: 'x'
Warning 1292 Truncated incorrect DECIMAL value: 'x'
Warning 1292 Truncated incorrect DOUBLE value: 'x'
Warning 1292 Truncated incorrect DECIMAL value: 'x'
#
......
......@@ -3071,7 +3071,7 @@ SELECT ((rpad(1.0,2048,1)) = ('4(') ^ (0.1));
((rpad(1.0,2048,1)) = ('4(') ^ (0.1))
0
Warnings:
Warning 1292 Truncated incorrect INTEGER value: '4('
Warning 1292 Truncated incorrect DECIMAL value: '4('
SELECT
pow((rpad(10.0,2048,1)),(b'1111111111111111111111111111111111111111111'));
ERROR 22003: DOUBLE value is out of range in 'pow(rpad(10.0,2048,1),0x07ffffffffff)'
......@@ -3079,7 +3079,7 @@ SELECT ((rpad(1.0,2048,1)) + (0) ^ ('../'));
((rpad(1.0,2048,1)) + (0) ^ ('../'))
1.011111111111111
Warnings:
Warning 1292 Truncated incorrect INTEGER value: '../'
Warning 1292 Truncated incorrect DECIMAL value: '../'
SELECT stddev_samp(rpad(1.0,2048,1));
stddev_samp(rpad(1.0,2048,1))
NULL
......
......@@ -2795,7 +2795,7 @@ SET sql_mode=DEFAULT;
DO TO_DAYS(SEC_TO_TIME(TIME(CEILING(UUID()))));
DO TO_DAYS(SEC_TO_TIME(MAKEDATE('',RAND(~('')))));
Warnings:
Warning 1292 Truncated incorrect INTEGER value: ''
Warning 1292 Truncated incorrect DECIMAL value: ''
Warning 1292 Truncated incorrect INTEGER value: ''
Warning 1292 Truncated incorrect seconds value: '20000101'
SELECT SEC_TO_TIME(MAKEDATE(0,RAND(~0)));
......
......@@ -4733,43 +4733,73 @@ void Item_func_in::mark_as_condition_AND_part(TABLE_LIST *embedding)
}
longlong Item_func_bit_or::val_int()
class Func_handler_bit_or_int_to_ulonglong:
public Item_handled_func::Handler_ulonglong
{
DBUG_ASSERT(fixed == 1);
ulonglong arg1= (ulonglong) args[0]->val_int();
if (args[0]->null_value)
public:
Longlong_null to_longlong_null(Item_handled_func *item) const
{
null_value=1; /* purecov: inspected */
return 0; /* purecov: inspected */
DBUG_ASSERT(item->is_fixed());
Longlong_null a= item->arguments()[0]->to_longlong_null();
return a.is_null() ? a : a | item->arguments()[1]->to_longlong_null();
}
ulonglong arg2= (ulonglong) args[1]->val_int();
if (args[1]->null_value)
};
class Func_handler_bit_or_dec_to_ulonglong:
public Item_handled_func::Handler_ulonglong
{
public:
Longlong_null to_longlong_null(Item_handled_func *item) const
{
null_value=1;
return 0;
DBUG_ASSERT(item->is_fixed());
VDec a(item->arguments()[0]);
return a.is_null() ? Longlong_null() :
a.to_xlonglong_null() | VDec(item->arguments()[1]).to_xlonglong_null();
}
null_value=0;
return (longlong) (arg1 | arg2);
};
bool Item_func_bit_or::fix_length_and_dec()
{
static Func_handler_bit_or_int_to_ulonglong ha_int_to_ull;
static Func_handler_bit_or_dec_to_ulonglong ha_dec_to_ull;
return fix_length_and_dec_op2_std(&ha_int_to_ull, &ha_dec_to_ull);
}
longlong Item_func_bit_and::val_int()
class Func_handler_bit_and_int_to_ulonglong:
public Item_handled_func::Handler_ulonglong
{
DBUG_ASSERT(fixed == 1);
ulonglong arg1= (ulonglong) args[0]->val_int();
if (args[0]->null_value)
public:
Longlong_null to_longlong_null(Item_handled_func *item) const
{
null_value=1; /* purecov: inspected */
return 0; /* purecov: inspected */
DBUG_ASSERT(item->is_fixed());
Longlong_null a= item->arguments()[0]->to_longlong_null();
return a.is_null() ? a : a & item->arguments()[1]->to_longlong_null();
}
ulonglong arg2= (ulonglong) args[1]->val_int();
if (args[1]->null_value)
};
class Func_handler_bit_and_dec_to_ulonglong:
public Item_handled_func::Handler_ulonglong
{
public:
Longlong_null to_longlong_null(Item_handled_func *item) const
{
null_value=1; /* purecov: inspected */
return 0; /* purecov: inspected */
DBUG_ASSERT(item->is_fixed());
VDec a(item->arguments()[0]);
return a.is_null() ? Longlong_null() :
a.to_xlonglong_null() & VDec(item->arguments()[1]).to_xlonglong_null();
}
null_value=0;
return (longlong) (arg1 & arg2);
};
bool Item_func_bit_and::fix_length_and_dec()
{
static Func_handler_bit_and_int_to_ulonglong ha_int_to_ull;
static Func_handler_bit_and_dec_to_ulonglong ha_dec_to_ull;
return fix_length_and_dec_op2_std(&ha_int_to_ull, &ha_dec_to_ull);
}
Item_cond::Item_cond(THD *thd, Item_cond *item)
......
......@@ -2139,44 +2139,103 @@ double Item_func_cot::val_real()
// Shift-functions, same as << and >> in C/C++
longlong Item_func_shift_left::val_int()
class Func_handler_shift_left_int_to_ulonglong:
public Item_handled_func::Handler_ulonglong
{
DBUG_ASSERT(fixed == 1);
uint shift;
ulonglong res= ((ulonglong) args[0]->val_int() <<
(shift=(uint) args[1]->val_int()));
if (args[0]->null_value || args[1]->null_value)
public:
Longlong_null to_longlong_null(Item_handled_func *item) const
{
null_value=1;
return 0;
DBUG_ASSERT(item->is_fixed());
return item->arguments()[0]->to_longlong_null() <<
item->arguments()[1]->to_longlong_null();
}
null_value=0;
return (shift < sizeof(longlong)*8 ? (longlong) res : 0);
};
class Func_handler_shift_left_decimal_to_ulonglong:
public Item_handled_func::Handler_ulonglong
{
public:
Longlong_null to_longlong_null(Item_handled_func *item) const
{
DBUG_ASSERT(item->is_fixed());
return VDec(item->arguments()[0]).to_xlonglong_null() <<
item->arguments()[1]->to_longlong_null();
}
};
bool Item_func_shift_left::fix_length_and_dec()
{
static Func_handler_shift_left_int_to_ulonglong ha_int_to_ull;
static Func_handler_shift_left_decimal_to_ulonglong ha_dec_to_ull;
return fix_length_and_dec_op1_std(&ha_int_to_ull, &ha_dec_to_ull);
}
longlong Item_func_shift_right::val_int()
class Func_handler_shift_right_int_to_ulonglong:
public Item_handled_func::Handler_ulonglong
{
DBUG_ASSERT(fixed == 1);
uint shift;
ulonglong res= (ulonglong) args[0]->val_int() >>
(shift=(uint) args[1]->val_int());
if (args[0]->null_value || args[1]->null_value)
public:
Longlong_null to_longlong_null(Item_handled_func *item) const
{
null_value=1;
return 0;
DBUG_ASSERT(item->fixed == 1);
return item->arguments()[0]->to_longlong_null() >>
item->arguments()[1]->to_longlong_null();
}
null_value=0;
return (shift < sizeof(longlong)*8 ? (longlong) res : 0);
};
class Func_handler_shift_right_decimal_to_ulonglong:
public Item_handled_func::Handler_ulonglong
{
public:
Longlong_null to_longlong_null(Item_handled_func *item) const
{
DBUG_ASSERT(item->is_fixed());
return VDec(item->arguments()[0]).to_xlonglong_null() >>
item->arguments()[1]->to_longlong_null();
}
};
bool Item_func_shift_right::fix_length_and_dec()
{
static Func_handler_shift_right_int_to_ulonglong ha_int_to_ull;
static Func_handler_shift_right_decimal_to_ulonglong ha_dec_to_ull;
return fix_length_and_dec_op1_std(&ha_int_to_ull, &ha_dec_to_ull);
}
longlong Item_func_bit_neg::val_int()
class Func_handler_bit_neg_int_to_ulonglong:
public Item_handled_func::Handler_ulonglong
{
DBUG_ASSERT(fixed == 1);
ulonglong res= (ulonglong) args[0]->val_int();
if ((null_value=args[0]->null_value))
return 0;
return ~res;
public:
Longlong_null to_longlong_null(Item_handled_func *item) const
{
DBUG_ASSERT(item->is_fixed());
return ~ item->arguments()[0]->to_longlong_null();
}
};
class Func_handler_bit_neg_decimal_to_ulonglong:
public Item_handled_func::Handler_ulonglong
{
public:
Longlong_null to_longlong_null(Item_handled_func *item) const
{
DBUG_ASSERT(item->is_fixed());
return ~ VDec(item->arguments()[0]).to_xlonglong_null();
}
};
bool Item_func_bit_neg::fix_length_and_dec()
{
static Func_handler_bit_neg_int_to_ulonglong ha_int_to_ull;
static Func_handler_bit_neg_decimal_to_ulonglong ha_dec_to_ull;
return fix_length_and_dec_op1_std(&ha_int_to_ull, &ha_dec_to_ull);
}
......@@ -3199,13 +3258,39 @@ longlong Item_func_find_in_set::val_int()
return 0;
}
longlong Item_func_bit_count::val_int()
class Func_handler_bit_count_int_to_slong:
public Item_handled_func::Handler_slong2
{
DBUG_ASSERT(fixed == 1);
ulonglong value= (ulonglong) args[0]->val_int();
if ((null_value= args[0]->null_value))
return 0; /* purecov: inspected */
return (longlong) my_count_bits(value);
public:
Longlong_null to_longlong_null(Item_handled_func *item) const
{
DBUG_ASSERT(item->is_fixed());
return item->arguments()[0]->to_longlong_null().bit_count();
}
};
class Func_handler_bit_count_decimal_to_slong:
public Item_handled_func::Handler_slong2
{
public:
Longlong_null to_longlong_null(Item_handled_func *item) const
{
DBUG_ASSERT(item->is_fixed());
return VDec(item->arguments()[0]).to_xlonglong_null().bit_count();
}
};
bool Item_func_bit_count::fix_length_and_dec()
{
static Func_handler_bit_count_int_to_slong ha_int_to_slong;
static Func_handler_bit_count_decimal_to_slong ha_dec_to_slong;
set_func_handler(args[0]->cmp_type() == INT_RESULT ?
(const Handler *) &ha_int_to_slong :
(const Handler *) &ha_dec_to_slong);
return m_func_handler->fix_length_and_dec(this);
}
......@@ -6221,14 +6306,38 @@ void Item_func_match::print(String *str, enum_query_type query_type)
str->append(STRING_WITH_LEN("))"));
}
longlong Item_func_bit_xor::val_int()
class Func_handler_bit_xor_int_to_ulonglong:
public Item_handled_func::Handler_ulonglong
{
DBUG_ASSERT(fixed == 1);
ulonglong arg1= (ulonglong) args[0]->val_int();
ulonglong arg2= (ulonglong) args[1]->val_int();
if ((null_value= (args[0]->null_value || args[1]->null_value)))
return 0;
return (longlong) (arg1 ^ arg2);
public:
Longlong_null to_longlong_null(Item_handled_func *item) const
{
DBUG_ASSERT(item->is_fixed());
return item->arguments()[0]->to_longlong_null() ^
item->arguments()[1]->to_longlong_null();
}
};
class Func_handler_bit_xor_dec_to_ulonglong:
public Item_handled_func::Handler_ulonglong
{
public:
Longlong_null to_longlong_null(Item_handled_func *item) const
{
DBUG_ASSERT(item->is_fixed());
return VDec(item->arguments()[0]).to_xlonglong_null() ^
VDec(item->arguments()[1]).to_xlonglong_null();
}
};
bool Item_func_bit_xor::fix_length_and_dec()
{
static const Func_handler_bit_xor_int_to_ulonglong ha_int_to_ull;
static const Func_handler_bit_xor_dec_to_ulonglong ha_dec_to_ull;
return fix_length_and_dec_op2_std(&ha_int_to_ull, &ha_dec_to_ull);
}
......
......@@ -641,6 +641,87 @@ class Item_handled_func: public Item_func
};
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 @@ class Item_func_find_in_set :public Item_long_func
/* 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 @@ class Item_func_match :public Item_real_func
};
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)
......
......@@ -195,6 +195,16 @@ class my_decimal :public decimal_t
return res;
}
longlong to_longlong(bool unsigned_flag) const;
/*
Return the value as a signed or unsigned longlong, depending on the sign.
- Positive values are returned as unsigned.
- Negative values are returned as signed.
This is used by bit SQL operators: | & ^ ~
as well as by the SQL function BIT_COUNT().
*/
longlong to_xlonglong() const
{ return to_longlong(!sign()); }
// Convert to string returning decimal2string() error code
int to_string_native(String *to, uint prec, uint dec, char filler,
uint mask= E_DEC_FATAL_ERROR) const;
......
......@@ -338,6 +338,10 @@ class Dec_ptr
double to_double() const { return m_ptr ? m_ptr->to_double() : 0.0; }
longlong to_longlong(bool unsigned_flag)
{ return m_ptr ? m_ptr->to_longlong(unsigned_flag) : 0; }
Longlong_null to_xlonglong_null()
{
return m_ptr ? Longlong_null(m_ptr->to_xlonglong()) : Longlong_null();
}
bool to_bool() const { return m_ptr ? m_ptr->to_bool() : false; }
String *to_string(String *to) const
{
......
......@@ -16,6 +16,8 @@
#ifndef SQL_TYPE_INT_INCLUDED
#define SQL_TYPE_INT_INCLUDED
#include "my_bit.h" // my_count_bits()
class Null_flag
{
......@@ -43,6 +45,58 @@ class Longlong_null: public Longlong, public Null_flag
Longlong_null(longlong nr, bool is_null)
:Longlong(nr), Null_flag(is_null)
{ }
explicit Longlong_null()
:Longlong(0), Null_flag(true)
{ }
explicit Longlong_null(longlong nr)
:Longlong(nr), Null_flag(false)
{ }
Longlong_null operator|(const Longlong_null &other) const
{
if (is_null() || other.is_null())
return Longlong_null();
return Longlong_null(value() | other.value());
}
Longlong_null operator&(const Longlong_null &other) const
{
if (is_null() || other.is_null())
return Longlong_null();
return Longlong_null(value() & other.value());
}
Longlong_null operator^(const Longlong_null &other) const
{
if (is_null() || other.is_null())
return Longlong_null();
return Longlong_null((longlong) (value() ^ other.value()));
}
Longlong_null operator~() const
{
if (is_null())
return *this;
return Longlong_null((longlong) ~ (ulonglong) value());
}
Longlong_null operator<<(const Longlong_null &llshift) const
{
if (is_null() || llshift.is_null())
return Longlong_null();
uint shift= (uint) llshift.value();
ulonglong res= ((ulonglong) value()) << shift;
return Longlong_null(shift < sizeof(longlong) * 8 ? (longlong) res : 0);
}
Longlong_null operator>>(const Longlong_null &llshift) const
{
if (is_null() || llshift.is_null())
return Longlong_null();
uint shift= (uint) llshift.value();
ulonglong res= ((ulonglong) value()) >> shift;
return Longlong_null(shift < sizeof(longlong) * 8 ? (longlong) res : 0);
}
Longlong_null bit_count() const
{
if (is_null())
return *this;
return Longlong_null((longlong) my_count_bits((ulonglong) value()));
}
};
......
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