Commit 5b452ae0 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-4511 Assertion `scale <= precision' fails on GROUP BY TIMEDIFF with incorrect types

MDEV-6302 Wrong result set when using GROUP BY FROM_UNIXTIME(...)+0
Fixed.
parent 4e6e7201
...@@ -1914,6 +1914,136 @@ SELECT 1 FROM DUAL WHERE MINUTE(TIMEDIFF(NULL, '12:12:12')); ...@@ -1914,6 +1914,136 @@ SELECT 1 FROM DUAL WHERE MINUTE(TIMEDIFF(NULL, '12:12:12'));
SELECT 1 FROM DUAL WHERE SECOND(TIMEDIFF(NULL, '12:12:12')); SELECT 1 FROM DUAL WHERE SECOND(TIMEDIFF(NULL, '12:12:12'));
1 1
# #
# MDEV-4511 Assertion `scale <= precision' fails on GROUP BY TIMEDIFF with incorrect types
#
CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
SELECT a FROM t1 GROUP BY TIMEDIFF('2004-06-12',a) * 1;
a
2005-05-04
Warnings:
Warning 1292 Truncated incorrect time value: '2004-06-12'
Warning 1292 Truncated incorrect time value: '2004-06-12'
DROP TABLE t1;
CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
SELECT a FROM t1 GROUP BY ADDTIME(a,'10')*1;
a
2000-02-23
2005-05-04
DROP TABLE t1;
CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
SELECT * FROM t1 GROUP BY SEC_TO_TIME(concat(a,'10'))*1;
a
2000-02-23
2005-05-04
DROP TABLE t1;
CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
SELECT * FROM t1 GROUP BY ADDTIME(timestamp('2001-01-01 00:00:00'),CAST(a AS SIGNED)&0xF)*1;
a
2005-05-04
2000-02-23
DROP TABLE t1;
CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
SELECT * FROM t1 GROUP BY STR_TO_DATE(a,concat('%Y-%m-%d.%f',if(rand(),'','')))*1;
a
2000-02-23
2005-05-04
DROP TABLE t1;
CREATE TABLE t1 AS SELECT
STR_TO_DATE('2001-01-01', '%Y-%m-%d') AS date_only,
STR_TO_DATE('10:10:10', '%H:%i:%s') AS time_only,
STR_TO_DATE('10:10:10.123', '%H:%i:%s.%f') AS time_microsecond,
STR_TO_DATE('2001-01-01 10:10:10', '%Y-%m-%d %H:%i:%s') AS date_time,
STR_TO_DATE('2001-01-01 10:10:10.123', '%Y-%m-%d %H:%i:%s.%f') AS date_time_microsecond;
SHOW COLUMNS FROM t1;
Field Type Null Key Default Extra
date_only date YES NULL
time_only time YES NULL
time_microsecond time(6) YES NULL
date_time datetime YES NULL
date_time_microsecond datetime(6) YES NULL
DROP TABLE t1;
CREATE TABLE t1 AS SELECT
SEC_TO_TIME(1)+0.1,
SEC_TO_TIME(1.1)+0.1,
SEC_TO_TIME(1.12)+0.1,
SEC_TO_TIME(1.123456)+0.1,
SEC_TO_TIME(1.1234567)+0.1;
SHOW COLUMNS FROM t1;
Field Type Null Key Default Extra
SEC_TO_TIME(1)+0.1 decimal(12,1) YES NULL
SEC_TO_TIME(1.1)+0.1 decimal(13,1) YES NULL
SEC_TO_TIME(1.12)+0.1 decimal(14,2) YES NULL
SEC_TO_TIME(1.123456)+0.1 decimal(18,6) YES NULL
SEC_TO_TIME(1.1234567)+0.1 decimal(18,6) YES NULL
DROP TABLE t1;
CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
SELECT * FROM t1 GROUP BY FROM_UNIXTIME(concat(a,'10'))*1;
a
2000-02-23
2005-05-04
SELECT * FROM t1 GROUP BY (-FROM_UNIXTIME(concat(a,'10')))*1;
a
2005-05-04
2000-02-23
SELECT * FROM t1 GROUP BY (-FROM_UNIXTIME(concat(a,'10')));
a
2005-05-04
2000-02-23
SELECT * FROM t1 GROUP BY ABS(FROM_UNIXTIME(concat(a,'10')));
a
2000-02-23
2005-05-04
SELECT * FROM t1 GROUP BY @a:=(FROM_UNIXTIME(concat(a,'10'))*1);
a
2000-02-23
2005-05-04
DROP TABLE t1;
#
# MDEV-6302 Wrong result set when using GROUP BY FROM_UNIXTIME(...)+0
#
CREATE TABLE t1 (a DATE);
INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
SELECT a, FROM_UNIXTIME(CONCAT(a,'10')) AS f1, FROM_UNIXTIME(CONCAT(a,'10'))+0 AS f2 FROM t1;
a f1 f2
2005-05-04 1970-01-01 03:33:25 19700101033325.000000
2000-02-23 1970-01-01 03:33:20 19700101033320.000000
SELECT * FROM t1 GROUP BY FROM_UNIXTIME(CONCAT(a,'10'))+0;
a
2000-02-23
2005-05-04
DROP TABLE t1;
CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
SELECT * FROM t1 GROUP BY FROM_UNIXTIME(concat(a,'10'))/1;
a
2000-02-23
2005-05-04
DROP TABLE t1;
CREATE TABLE t1 (a DATE);
INSERT INTO t1 VALUES ('2005-05-04');
SELECT CONCAT(FROM_UNIXTIME(CONCAT(a,'10')) MOD FROM_UNIXTIME(CONCAT(a,'10'))) AS f2 FROM t1;
f2
0.000000
SELECT CHAR_LENGTH(CONCAT(FROM_UNIXTIME(CONCAT(a,'10')) MOD FROM_UNIXTIME(CONCAT(a,'10')))) AS f2 FROM t1;
f2
8
CREATE TABLE t2 AS SELECT CONCAT(FROM_UNIXTIME(CONCAT(a,'10')) MOD FROM_UNIXTIME(CONCAT(a,'10'))) AS f2 FROM t1;
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`f2` varbinary(26) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
SELECT * FROM t2;
f2
0.000000
DROP TABLE t1,t2;
#
# MDEV-4635 Crash in UNIX_TIMESTAMP(STR_TO_DATE('2020','%Y')) # MDEV-4635 Crash in UNIX_TIMESTAMP(STR_TO_DATE('2020','%Y'))
# #
SET TIME_ZONE='+02:00'; SET TIME_ZONE='+02:00';
......
...@@ -798,10 +798,10 @@ ROUND(qty,3) dps ROUND(qty,dps) ...@@ -798,10 +798,10 @@ ROUND(qty,3) dps ROUND(qty,dps)
DROP TABLE t1; DROP TABLE t1;
SELECT 1 % .123456789123456789123456789123456789123456789123456789123456789123456789123456789 AS '%'; SELECT 1 % .123456789123456789123456789123456789123456789123456789123456789123456789123456789 AS '%';
% %
0.012345687012345687012345687012345687012345687012345687012345687012345687000000000 0.012345687012345687012345687012
SELECT MOD(1, .123456789123456789123456789123456789123456789123456789123456789123456789123456789) AS 'MOD()'; SELECT MOD(1, .123456789123456789123456789123456789123456789123456789123456789123456789123456789) AS 'MOD()';
MOD() MOD()
0.012345687012345687012345687012345687012345687012345687012345687012345687000000000 0.012345687012345687012345687012
create table t1 (f1 decimal(6,6),f2 decimal(6,6) zerofill); create table t1 (f1 decimal(6,6),f2 decimal(6,6) zerofill);
insert into t1 values (-0.123456,0.123456); insert into t1 values (-0.123456,0.123456);
select group_concat(f1),group_concat(f2) from t1; select group_concat(f1),group_concat(f2) from t1;
......
...@@ -703,7 +703,7 @@ select .7777777777777777777777777777777777777 * ...@@ -703,7 +703,7 @@ select .7777777777777777777777777777777777777 *
777777777777777777.777777777777777777700000000000 777777777777777777.777777777777777777700000000000
select .7777777777777777777777777777777777777 - 0.1; select .7777777777777777777777777777777777777 - 0.1;
.7777777777777777777777777777777777777 - 0.1 .7777777777777777777777777777777777777 - 0.1
0.6777777777777777777777777777777777777 0.677777777777777777777777777778
select .343434343434343434 + .343434343434343434; select .343434343434343434 + .343434343434343434;
.343434343434343434 + .343434343434343434 .343434343434343434 + .343434343434343434
0.686868686868686868 0.686868686868686868
...@@ -1845,7 +1845,7 @@ Warnings: ...@@ -1845,7 +1845,7 @@ Warnings:
Note 1265 Data truncated for column 'c1' at row 3 Note 1265 Data truncated for column 'c1' at row 3
DESC t2; DESC t2;
Field Type Null Key Default Extra Field Type Null Key Default Extra
c1 decimal(32,30) YES NULL c1 decimal(33,30) YES NULL
DROP TABLE t1,t2; DROP TABLE t1,t2;
CREATE TABLE t1 (a DECIMAL(30,30)); CREATE TABLE t1 (a DECIMAL(30,30));
INSERT INTO t1 VALUES (0.1),(0.2),(0.3); INSERT INTO t1 VALUES (0.1),(0.2),(0.3);
...@@ -1856,7 +1856,7 @@ Note 1265 Data truncated for column 'c1' at row 2 ...@@ -1856,7 +1856,7 @@ Note 1265 Data truncated for column 'c1' at row 2
Note 1265 Data truncated for column 'c1' at row 3 Note 1265 Data truncated for column 'c1' at row 3
DESC t2; DESC t2;
Field Type Null Key Default Extra Field Type Null Key Default Extra
c1 decimal(34,0) YES NULL c1 decimal(33,30) YES NULL
DROP TABLE t1,t2; DROP TABLE t1,t2;
CREATE TABLE t1 (a DECIMAL(30,30)); CREATE TABLE t1 (a DECIMAL(30,30));
INSERT INTO t1 VALUES (0.1),(0.2),(0.3); INSERT INTO t1 VALUES (0.1),(0.2),(0.3);
......
...@@ -1156,6 +1156,86 @@ SELECT 1 FROM DUAL WHERE MINUTE(TIMEDIFF(NULL, '12:12:12')); ...@@ -1156,6 +1156,86 @@ SELECT 1 FROM DUAL WHERE MINUTE(TIMEDIFF(NULL, '12:12:12'));
SELECT 1 FROM DUAL WHERE SECOND(TIMEDIFF(NULL, '12:12:12')); SELECT 1 FROM DUAL WHERE SECOND(TIMEDIFF(NULL, '12:12:12'));
--echo #
--echo # MDEV-4511 Assertion `scale <= precision' fails on GROUP BY TIMEDIFF with incorrect types
--echo #
CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
SELECT a FROM t1 GROUP BY TIMEDIFF('2004-06-12',a) * 1;
DROP TABLE t1;
CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
SELECT a FROM t1 GROUP BY ADDTIME(a,'10')*1;
DROP TABLE t1;
CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
SELECT * FROM t1 GROUP BY SEC_TO_TIME(concat(a,'10'))*1;
DROP TABLE t1;
CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
SELECT * FROM t1 GROUP BY ADDTIME(timestamp('2001-01-01 00:00:00'),CAST(a AS SIGNED)&0xF)*1;
DROP TABLE t1;
CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
SELECT * FROM t1 GROUP BY STR_TO_DATE(a,concat('%Y-%m-%d.%f',if(rand(),'','')))*1;
DROP TABLE t1;
CREATE TABLE t1 AS SELECT
STR_TO_DATE('2001-01-01', '%Y-%m-%d') AS date_only,
STR_TO_DATE('10:10:10', '%H:%i:%s') AS time_only,
STR_TO_DATE('10:10:10.123', '%H:%i:%s.%f') AS time_microsecond,
STR_TO_DATE('2001-01-01 10:10:10', '%Y-%m-%d %H:%i:%s') AS date_time,
STR_TO_DATE('2001-01-01 10:10:10.123', '%Y-%m-%d %H:%i:%s.%f') AS date_time_microsecond;
SHOW COLUMNS FROM t1;
DROP TABLE t1;
CREATE TABLE t1 AS SELECT
SEC_TO_TIME(1)+0.1,
SEC_TO_TIME(1.1)+0.1,
SEC_TO_TIME(1.12)+0.1,
SEC_TO_TIME(1.123456)+0.1,
SEC_TO_TIME(1.1234567)+0.1;
SHOW COLUMNS FROM t1;
DROP TABLE t1;
CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
SELECT * FROM t1 GROUP BY FROM_UNIXTIME(concat(a,'10'))*1;
SELECT * FROM t1 GROUP BY (-FROM_UNIXTIME(concat(a,'10')))*1;
SELECT * FROM t1 GROUP BY (-FROM_UNIXTIME(concat(a,'10')));
SELECT * FROM t1 GROUP BY ABS(FROM_UNIXTIME(concat(a,'10')));
SELECT * FROM t1 GROUP BY @a:=(FROM_UNIXTIME(concat(a,'10'))*1);
DROP TABLE t1;
--echo #
--echo # MDEV-6302 Wrong result set when using GROUP BY FROM_UNIXTIME(...)+0
--echo #
CREATE TABLE t1 (a DATE);
INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
SELECT a, FROM_UNIXTIME(CONCAT(a,'10')) AS f1, FROM_UNIXTIME(CONCAT(a,'10'))+0 AS f2 FROM t1;
SELECT * FROM t1 GROUP BY FROM_UNIXTIME(CONCAT(a,'10'))+0;
DROP TABLE t1;
CREATE TABLE t1 (a DATE) ENGINE=MyISAM;
INSERT INTO t1 VALUES ('2005-05-04'),('2000-02-23');
SELECT * FROM t1 GROUP BY FROM_UNIXTIME(concat(a,'10'))/1;
DROP TABLE t1;
CREATE TABLE t1 (a DATE);
INSERT INTO t1 VALUES ('2005-05-04');
SELECT CONCAT(FROM_UNIXTIME(CONCAT(a,'10')) MOD FROM_UNIXTIME(CONCAT(a,'10'))) AS f2 FROM t1;
SELECT CHAR_LENGTH(CONCAT(FROM_UNIXTIME(CONCAT(a,'10')) MOD FROM_UNIXTIME(CONCAT(a,'10')))) AS f2 FROM t1;
CREATE TABLE t2 AS SELECT CONCAT(FROM_UNIXTIME(CONCAT(a,'10')) MOD FROM_UNIXTIME(CONCAT(a,'10'))) AS f2 FROM t1;
SHOW CREATE TABLE t2;
SELECT * FROM t2;
DROP TABLE t1,t2;
--echo # --echo #
--echo # MDEV-4635 Crash in UNIX_TIMESTAMP(STR_TO_DATE('2020','%Y')) --echo # MDEV-4635 Crash in UNIX_TIMESTAMP(STR_TO_DATE('2020','%Y'))
--echo # --echo #
......
...@@ -65,6 +65,29 @@ inline bool is_temporal_type(enum_field_types type) ...@@ -65,6 +65,29 @@ inline bool is_temporal_type(enum_field_types type)
return mysql_type_to_time_type(type) != MYSQL_TIMESTAMP_ERROR; return mysql_type_to_time_type(type) != MYSQL_TIMESTAMP_ERROR;
} }
/**
Tests if field type is temporal and has time part,
i.e. represents TIME, DATETIME or TIMESTAMP types in SQL.
@param type Field type, as returned by field->type().
@retval true If field type is temporal type with time part.
@retval false If field type is not temporal type with time part.
*/
inline bool is_temporal_type_with_time(enum_field_types type)
{
switch (type)
{
case MYSQL_TYPE_TIME:
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP:
return true;
default:
return false;
}
}
/* /*
Virtual_column_info is the class to contain additional Virtual_column_info is the class to contain additional
characteristics that is specific for a virtual/computed characteristics that is specific for a virtual/computed
......
...@@ -909,9 +909,47 @@ public: ...@@ -909,9 +909,47 @@ public:
virtual cond_result eq_cmp_result() const { return COND_OK; } virtual cond_result eq_cmp_result() const { return COND_OK; }
inline uint float_length(uint decimals_par) const inline uint float_length(uint decimals_par) const
{ return decimals != NOT_FIXED_DEC ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;} { return decimals != NOT_FIXED_DEC ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;}
/* Returns total number of decimal digits */
virtual uint decimal_precision() const; virtual uint decimal_precision() const;
/* Returns the number of integer part digits only */
inline int decimal_int_part() const inline int decimal_int_part() const
{ return my_decimal_int_part(decimal_precision(), decimals); } { return my_decimal_int_part(decimal_precision(), decimals); }
/*
Returns the number of fractional digits only.
NOT_FIXED_DEC is replaced to the maximum possible number
of fractional digits, taking into account the data type.
*/
uint decimal_scale() const
{
return decimals < NOT_FIXED_DEC ? decimals :
is_temporal_type_with_time(field_type()) ?
TIME_SECOND_PART_DIGITS :
min(max_length, DECIMAL_MAX_SCALE);
}
/*
Returns how many digits a divisor adds into a division result.
This is important when the integer part of the divisor can be 0.
In this example:
SELECT 1 / 0.000001; -> 1000000.0000
the divisor adds 5 digits into the result precision.
Currently this method only replaces NOT_FIXED_DEC to
TIME_SECOND_PART_DIGITS for temporal data types.
This method can be made virtual, to create more efficient (smaller)
data types for division results.
For example, in
SELECT 1/1.000001;
the divisor could provide no additional precision into the result,
so could any other items that are know to return a result
with non-zero integer part.
*/
uint divisor_precision_increment() const
{
return decimals < NOT_FIXED_DEC ? decimals :
is_temporal_type_with_time(field_type()) ?
TIME_SECOND_PART_DIGITS :
decimals;
}
/** /**
TIME or DATETIME precision of the item: 0..6 TIME or DATETIME precision of the item: 0..6
*/ */
......
...@@ -569,7 +569,7 @@ my_decimal *Item_real_func::val_decimal(my_decimal *decimal_value) ...@@ -569,7 +569,7 @@ my_decimal *Item_real_func::val_decimal(my_decimal *decimal_value)
} }
void Item_func::fix_num_length_and_dec() void Item_udf_func::fix_num_length_and_dec()
{ {
uint fl_length= 0; uint fl_length= 0;
decimals=0; decimals=0;
...@@ -587,11 +587,6 @@ void Item_func::fix_num_length_and_dec() ...@@ -587,11 +587,6 @@ void Item_func::fix_num_length_and_dec()
} }
void Item_func_numhybrid::fix_num_length_and_dec()
{}
/** /**
Count max_length and decimals for temporal functions. Count max_length and decimals for temporal functions.
...@@ -777,9 +772,9 @@ bool Item_func_connection_id::fix_fields(THD *thd, Item **ref) ...@@ -777,9 +772,9 @@ bool Item_func_connection_id::fix_fields(THD *thd, Item **ref)
function of two arguments. function of two arguments.
*/ */
void Item_num_op::find_num_type(void) void Item_num_op::fix_length_and_dec(void)
{ {
DBUG_ENTER("Item_num_op::find_num_type"); DBUG_ENTER("Item_num_op::fix_length_and_dec");
DBUG_PRINT("info", ("name %s", func_name())); DBUG_PRINT("info", ("name %s", func_name()));
DBUG_ASSERT(arg_count == 2); DBUG_ASSERT(arg_count == 2);
Item_result r0= args[0]->cast_to_int_type(); Item_result r0= args[0]->cast_to_int_type();
...@@ -823,22 +818,26 @@ void Item_num_op::find_num_type(void) ...@@ -823,22 +818,26 @@ void Item_num_op::find_num_type(void)
type depends only on the first argument) type depends only on the first argument)
*/ */
void Item_func_num1::find_num_type() void Item_func_num1::fix_length_and_dec()
{ {
DBUG_ENTER("Item_func_num1::find_num_type"); DBUG_ENTER("Item_func_num1::fix_length_and_dec");
DBUG_PRINT("info", ("name %s", func_name())); DBUG_PRINT("info", ("name %s", func_name()));
switch (cached_result_type= args[0]->cast_to_int_type()) { switch (cached_result_type= args[0]->cast_to_int_type()) {
case INT_RESULT: case INT_RESULT:
max_length= args[0]->max_length;
unsigned_flag= args[0]->unsigned_flag; unsigned_flag= args[0]->unsigned_flag;
break; break;
case STRING_RESULT: case STRING_RESULT:
case REAL_RESULT: case REAL_RESULT:
cached_result_type= REAL_RESULT; cached_result_type= REAL_RESULT;
decimals= args[0]->decimals; // Preserve NOT_FIXED_DEC
max_length= float_length(decimals); max_length= float_length(decimals);
break; break;
case TIME_RESULT: case TIME_RESULT:
cached_result_type= DECIMAL_RESULT; cached_result_type= DECIMAL_RESULT;
case DECIMAL_RESULT: case DECIMAL_RESULT:
decimals= args[0]->decimal_scale(); // Do not preserve NOT_FIXED_DEC
max_length= args[0]->max_length;
break; break;
case ROW_RESULT: case ROW_RESULT:
case IMPOSSIBLE_RESULT: case IMPOSSIBLE_RESULT:
...@@ -853,20 +852,6 @@ void Item_func_num1::find_num_type() ...@@ -853,20 +852,6 @@ void Item_func_num1::find_num_type()
} }
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_hybrid_result_type::val_str(String *str) String *Item_func_hybrid_result_type::val_str(String *str)
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
...@@ -1455,11 +1440,14 @@ my_decimal *Item_func_plus::decimal_op(my_decimal *decimal_value) ...@@ -1455,11 +1440,14 @@ my_decimal *Item_func_plus::decimal_op(my_decimal *decimal_value)
*/ */
void Item_func_additive_op::result_precision() void Item_func_additive_op::result_precision()
{ {
decimals= max(args[0]->decimals, args[1]->decimals); decimals= max(args[0]->decimal_scale(), args[1]->decimal_scale());
int arg1_int= args[0]->decimal_precision() - args[0]->decimals; int arg1_int= args[0]->decimal_precision() - args[0]->decimal_scale();
int arg2_int= args[1]->decimal_precision() - args[1]->decimals; int arg2_int= args[1]->decimal_precision() - args[1]->decimal_scale();
int precision= max(arg1_int, arg2_int) + 1 + decimals; int precision= max(arg1_int, arg2_int) + 1 + decimals;
DBUG_ASSERT(arg1_int >= 0);
DBUG_ASSERT(arg2_int >= 0);
/* Integer operations keep unsigned_flag if one of arguments is unsigned */ /* Integer operations keep unsigned_flag if one of arguments is unsigned */
if (result_type() == INT_RESULT) if (result_type() == INT_RESULT)
unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag; unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag;
...@@ -1568,7 +1556,8 @@ void Item_func_mul::result_precision() ...@@ -1568,7 +1556,8 @@ void Item_func_mul::result_precision()
unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag; unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag;
else else
unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag; unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag;
decimals= min(args[0]->decimals + args[1]->decimals, DECIMAL_MAX_SCALE); decimals= min(args[0]->decimal_scale() + args[1]->decimal_scale(),
DECIMAL_MAX_SCALE);
uint est_prec = args[0]->decimal_precision() + args[1]->decimal_precision(); uint est_prec = args[0]->decimal_precision() + args[1]->decimal_precision();
uint precision= min(est_prec, DECIMAL_MAX_PRECISION); uint precision= min(est_prec, DECIMAL_MAX_PRECISION);
max_length= my_decimal_precision_to_length_no_truncation(precision, decimals, max_length= my_decimal_precision_to_length_no_truncation(precision, decimals,
...@@ -1618,8 +1607,20 @@ my_decimal *Item_func_div::decimal_op(my_decimal *decimal_value) ...@@ -1618,8 +1607,20 @@ my_decimal *Item_func_div::decimal_op(my_decimal *decimal_value)
void Item_func_div::result_precision() void Item_func_div::result_precision()
{ {
/*
We need to add args[1]->divisor_precision_increment(),
to properly handle the cases like this:
SELECT 5.05 / 0.014; -> 360.714286
i.e. when the divisor has a zero integer part
and non-zero digits appear only after the decimal point.
Precision in this example is calculated as
args[0]->decimal_precision() + // 3
args[1]->divisor_precision_increment() + // 3
prec_increment // 4
which gives 10 decimals digits.
*/
uint precision=min(args[0]->decimal_precision() + uint precision=min(args[0]->decimal_precision() +
args[1]->decimals + prec_increment, args[1]->divisor_precision_increment() + prec_increment,
DECIMAL_MAX_PRECISION); DECIMAL_MAX_PRECISION);
/* Integer operations keep unsigned_flag if one of arguments is unsigned */ /* Integer operations keep unsigned_flag if one of arguments is unsigned */
...@@ -1627,7 +1628,7 @@ void Item_func_div::result_precision() ...@@ -1627,7 +1628,7 @@ void Item_func_div::result_precision()
unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag; unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag;
else else
unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag; unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag;
decimals= min(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE); decimals= min(args[0]->decimal_scale() + prec_increment, DECIMAL_MAX_SCALE);
max_length= my_decimal_precision_to_length_no_truncation(precision, decimals, max_length= my_decimal_precision_to_length_no_truncation(precision, decimals,
unsigned_flag); unsigned_flag);
} }
...@@ -1776,7 +1777,7 @@ my_decimal *Item_func_mod::decimal_op(my_decimal *decimal_value) ...@@ -1776,7 +1777,7 @@ my_decimal *Item_func_mod::decimal_op(my_decimal *decimal_value)
void Item_func_mod::result_precision() void Item_func_mod::result_precision()
{ {
decimals= max(args[0]->decimals, args[1]->decimals); decimals= max(args[0]->decimal_scale(), args[1]->decimal_scale());
max_length= max(args[0]->max_length, args[1]->max_length); max_length= max(args[0]->max_length, args[1]->max_length);
} }
...@@ -1818,18 +1819,12 @@ my_decimal *Item_func_neg::decimal_op(my_decimal *decimal_value) ...@@ -1818,18 +1819,12 @@ my_decimal *Item_func_neg::decimal_op(my_decimal *decimal_value)
} }
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() void Item_func_neg::fix_length_and_dec()
{ {
DBUG_ENTER("Item_func_neg::fix_length_and_dec"); DBUG_ENTER("Item_func_neg::fix_length_and_dec");
Item_func_num1::fix_length_and_dec(); Item_func_num1::fix_length_and_dec();
/* 1 add because sign can appear */
max_length= args[0]->max_length + 1;
/* /*
If this is in integer context keep the context as integer if possible If this is in integer context keep the context as integer if possible
...@@ -2119,8 +2114,12 @@ void Item_func_integer::fix_length_and_dec() ...@@ -2119,8 +2114,12 @@ void Item_func_integer::fix_length_and_dec()
decimals=0; decimals=0;
} }
void Item_func_int_val::fix_num_length_and_dec()
void 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()));
ulonglong tmp_max_length= (ulonglong ) args[0]->max_length - ulonglong tmp_max_length= (ulonglong ) args[0]->max_length -
(args[0]->decimals ? args[0]->decimals + 1 : 0) + 2; (args[0]->decimals ? args[0]->decimals + 1 : 0) + 2;
max_length= tmp_max_length > (ulonglong) max_field_size ? max_length= tmp_max_length > (ulonglong) max_field_size ?
...@@ -2128,13 +2127,7 @@ void Item_func_int_val::fix_num_length_and_dec() ...@@ -2128,13 +2127,7 @@ void Item_func_int_val::fix_num_length_and_dec()
uint tmp= float_length(decimals); uint tmp= float_length(decimals);
set_if_smaller(max_length,tmp); set_if_smaller(max_length,tmp);
decimals= 0; decimals= 0;
}
void Item_func_int_val::find_num_type()
{
DBUG_ENTER("Item_func_int_val::find_num_type");
DBUG_PRINT("info", ("name %s", func_name()));
switch (cached_result_type= args[0]->cast_to_int_type()) switch (cached_result_type= args[0]->cast_to_int_type())
{ {
case STRING_RESULT: case STRING_RESULT:
...@@ -3569,12 +3562,6 @@ String *Item_func_udf_decimal::val_str(String *str) ...@@ -3569,12 +3562,6 @@ String *Item_func_udf_decimal::val_str(String *str)
} }
void Item_func_udf_decimal::fix_length_and_dec()
{
fix_num_length_and_dec();
}
/* Default max_length is max argument length */ /* Default max_length is max argument length */
void Item_func_udf_str::fix_length_and_dec() void Item_func_udf_str::fix_length_and_dec()
......
...@@ -145,7 +145,6 @@ public: ...@@ -145,7 +145,6 @@ public:
virtual void print(String *str, enum_query_type query_type); virtual void print(String *str, enum_query_type query_type);
void print_op(String *str, enum_query_type query_type); void print_op(String *str, enum_query_type query_type);
void print_args(String *str, uint from, enum_query_type query_type); void print_args(String *str, uint from, enum_query_type query_type);
virtual void fix_num_length_and_dec();
void count_only_length(Item **item, uint nitems); void count_only_length(Item **item, uint nitems);
void count_real_length(); void count_real_length();
void count_decimal_length(); void count_decimal_length();
...@@ -450,9 +449,6 @@ public: ...@@ -450,9 +449,6 @@ public:
Item_func_numhybrid(List<Item> &list) Item_func_numhybrid(List<Item> &list)
:Item_func_hybrid_result_type(list) :Item_func_hybrid_result_type(list)
{} {}
void fix_length_and_dec();
void fix_num_length_and_dec();
virtual void find_num_type()= 0; /* To be called from fix_length_and_dec */
String *str_op(String *str) { DBUG_ASSERT(0); return 0; } String *str_op(String *str) { DBUG_ASSERT(0); return 0; }
bool date_op(MYSQL_TIME *ltime, uint fuzzydate) { DBUG_ASSERT(0); return true; } bool date_op(MYSQL_TIME *ltime, uint fuzzydate) { DBUG_ASSERT(0); return true; }
}; };
...@@ -464,9 +460,7 @@ class Item_func_num1: public Item_func_numhybrid ...@@ -464,9 +460,7 @@ class Item_func_num1: public Item_func_numhybrid
public: public:
Item_func_num1(Item *a) :Item_func_numhybrid(a) {} Item_func_num1(Item *a) :Item_func_numhybrid(a) {}
Item_func_num1(Item *a, Item *b) :Item_func_numhybrid(a, b) {} Item_func_num1(Item *a, Item *b) :Item_func_numhybrid(a, b) {}
void fix_length_and_dec();
void fix_num_length_and_dec();
void find_num_type();
}; };
...@@ -482,7 +476,7 @@ class Item_num_op :public Item_func_numhybrid ...@@ -482,7 +476,7 @@ class Item_num_op :public Item_func_numhybrid
print_op(str, query_type); print_op(str, query_type);
} }
void find_num_type(); void fix_length_and_dec();
}; };
...@@ -698,7 +692,6 @@ public: ...@@ -698,7 +692,6 @@ public:
const char *func_name() const { return "-"; } const char *func_name() const { return "-"; }
enum Functype functype() const { return NEG_FUNC; } enum Functype functype() const { return NEG_FUNC; }
void fix_length_and_dec(); void fix_length_and_dec();
void fix_num_length_and_dec();
uint decimal_precision() const { return args[0]->decimal_precision(); } uint decimal_precision() const { return args[0]->decimal_precision(); }
bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
...@@ -857,8 +850,7 @@ class Item_func_int_val :public Item_func_num1 ...@@ -857,8 +850,7 @@ class Item_func_int_val :public Item_func_num1
{ {
public: public:
Item_func_int_val(Item *a) :Item_func_num1(a) {} Item_func_int_val(Item *a) :Item_func_num1(a) {}
void fix_num_length_and_dec(); void fix_length_and_dec();
void find_num_type();
}; };
...@@ -1267,6 +1259,7 @@ public: ...@@ -1267,6 +1259,7 @@ public:
fixed= 1; fixed= 1;
return res; return res;
} }
void fix_num_length_and_dec();
void update_used_tables() void update_used_tables()
{ {
/* /*
...@@ -1380,7 +1373,7 @@ public: ...@@ -1380,7 +1373,7 @@ public:
my_decimal *val_decimal(my_decimal *); my_decimal *val_decimal(my_decimal *);
String *val_str(String *str); String *val_str(String *str);
enum Item_result result_type () const { return DECIMAL_RESULT; } enum Item_result result_type () const { return DECIMAL_RESULT; }
void fix_length_and_dec(); void fix_length_and_dec() { fix_num_length_and_dec(); }
}; };
......
...@@ -365,16 +365,15 @@ protected: ...@@ -365,16 +365,15 @@ protected:
public: public:
Item_func_seconds_hybrid() :Item_func_numhybrid() {} Item_func_seconds_hybrid() :Item_func_numhybrid() {}
Item_func_seconds_hybrid(Item *a) :Item_func_numhybrid(a) {} Item_func_seconds_hybrid(Item *a) :Item_func_numhybrid(a) {}
void fix_num_length_and_dec() void fix_length_and_dec()
{ {
if (arg_count) if (arg_count)
decimals= args[0]->temporal_precision(arg0_expected_type()); decimals= args[0]->temporal_precision(arg0_expected_type());
set_if_smaller(decimals, TIME_SECOND_PART_DIGITS); set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
max_length=17 + (decimals ? decimals + 1 : 0); max_length=17 + (decimals ? decimals + 1 : 0);
maybe_null= true; maybe_null= true;
cached_result_type= decimals ? DECIMAL_RESULT : INT_RESULT;
} }
void find_num_type()
{ cached_result_type= decimals ? DECIMAL_RESULT : INT_RESULT; }
double real_op() { DBUG_ASSERT(0); return 0; } double real_op() { DBUG_ASSERT(0); return 0; }
String *str_op(String *str) { DBUG_ASSERT(0); return 0; } String *str_op(String *str) { DBUG_ASSERT(0); return 0; }
bool date_op(MYSQL_TIME *ltime, uint fuzzydate) { DBUG_ASSERT(0); return true; } bool date_op(MYSQL_TIME *ltime, uint fuzzydate) { DBUG_ASSERT(0); return true; }
...@@ -421,11 +420,6 @@ protected: ...@@ -421,11 +420,6 @@ protected:
public: public:
Item_func_time_to_sec(Item *item) :Item_func_seconds_hybrid(item) {} Item_func_time_to_sec(Item *item) :Item_func_seconds_hybrid(item) {}
const char *func_name() const { return "time_to_sec"; } const char *func_name() const { return "time_to_sec"; }
void fix_num_length_and_dec()
{
maybe_null= true;
Item_func_seconds_hybrid::fix_num_length_and_dec();
}
bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} bool check_vcol_func_processor(uchar *int_arg) { return FALSE;}
bool check_valid_arguments_processor(uchar *int_arg) bool check_valid_arguments_processor(uchar *int_arg)
......
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