MDEV-14032 SEC_TO_TIME executes side effect two times
- Adding a helper class Sec6 to store (neg,seconds,microseconds) - Adding a helper class VSec6 (Sec6 with a flag for "IS NULL") - Wrapping related functions as methods of Sec6; * number_to_datetime() * number_to_time() * my_decimal2seconds() * Item::get_seconds() * A big piece of code in Item_func_sec_to_time::get_date() - Using the new classes in places where second-to-temporal conversion takes place: * Field_timestamp::store(double) * Field_timestamp::store(longlong) * Field_timestamp_with_dec::store_decimal(my_decimal) * Field_temporal_with_date::store(double) * Field_temporal_with_date::store(longlong) * Field_time::store(double) * Field_time::store(longlong) * Field_time::store_decimal(my_decimal) * Field_temporal_with_date::store_decimal(my_decimal) * get_interval_value() * Item_func_sec_to_time::get_date() * Item_func_from_unixtime::get_date() * Item_func_maketime::get_date() This change simplifies these methods and functions a lot. - Warnings are now sent at VSec6 initialization time, when the source data is available in its original data type representation. If Sec6::to_time() or Sec6::to_datetime() truncate data again during conversion to MYSQL_TIME, they send warnings, but only if no warnings were sent during VSec6 initialization. This helps prevents double warnings. The call for val_str() in Item_func_sec_to_time::get_date() is not needed any more, so it's removed. This change actually fixes the problem. As a good effect, FROM_UNIXTIME() and MAKETIME() now also send warnings in case if the seconds arguments is out of range. Previously these functions returned NULL silently. - Splitting the code in the global function make_truncated_value_warning() into a number of methods THD::raise_warning_xxxx(). This was needed to reuse the logic that chooses between: * ER_TRUNCATED_WRONG_VALUE * ER_WRONG_VALUE * ER_TRUNCATED_WRONG_VALUE_FOR_FIELD for non-temporal data types (Sec6). - Removing: * Item::get_seconds() * number_to_time_with_warn() as this code now resides inside methods of Sec6. - Cleanup (changes that are not directly related to the fix): * Removing calls for field_name_or_null() and passing NULL instead in Item_func_hybrid_field_type::get_date_from_{int|real}_op, because Item_func_hybrid_field_type::field_name_or_null() always returns NULL * Replacing a number of calls for make_truncated_value_warning() to calls for THD::raise_warning_xxx(). In these places we know that the execution went through a certain branch of make_truncated_value_warning(), (e.g. the exact error code is known, or field name is always NULL, or field name is always not-NULL). So calls for the entire make_truncated_value_warning() after splitting are not necessary.
This commit is contained in:
parent
a12e6c5ba4
commit
8524bb6872
@ -4575,7 +4575,7 @@ SELECT SEC_TO_TIME(CONVERT(900*24*60*60 USING ucs2));
|
||||
SEC_TO_TIME(CONVERT(900*24*60*60 USING ucs2))
|
||||
838:59:59.999999
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect time value: '77760000'
|
||||
Warning 1292 Truncated incorrect seconds value: '77760000'
|
||||
#
|
||||
# MDEV-13530 VARBINARY doesn't convert to to BLOB for sizes 65533, 65534 and 65535
|
||||
#
|
||||
|
@ -26,7 +26,7 @@ select sec_to_time('9001.1'), sec_to_time('1234567890123.123');
|
||||
sec_to_time('9001.1') sec_to_time('1234567890123.123')
|
||||
02:30:01.100000 838:59:59.999999
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect time value: '1234567890123.123'
|
||||
Warning 1292 Truncated incorrect seconds value: '1234567890123.123000'
|
||||
select sec_to_time(-9001.1), sec_to_time(-9001.1) / 1,
|
||||
sec_to_time(-9001.1) / 1e0, sec_to_time(-9001) div 1;
|
||||
sec_to_time(-9001.1) sec_to_time(-9001.1) / 1 sec_to_time(-9001.1) / 1e0 sec_to_time(-9001) div 1
|
||||
@ -35,13 +35,13 @@ select sec_to_time(90011e-1), sec_to_time(1234567890123e30);
|
||||
sec_to_time(90011e-1) sec_to_time(1234567890123e30)
|
||||
02:30:01.100000 838:59:59.999999
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect time value: '1.234567890123e42'
|
||||
Warning 1292 Truncated incorrect seconds value: '1.234567890123e42'
|
||||
select sec_to_time(1234567890123), sec_to_time('99999999999999999999999999999');
|
||||
sec_to_time(1234567890123) sec_to_time('99999999999999999999999999999')
|
||||
838:59:59 838:59:59.999999
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect time value: '1234567890123'
|
||||
Warning 1292 Truncated incorrect time value: '99999999999999999999999999999'
|
||||
Warning 1292 Truncated incorrect seconds value: '1234567890123'
|
||||
Warning 1292 Truncated incorrect seconds value: '99999999999999999999999999999'
|
||||
select now()-curdate()*1000000-curtime();
|
||||
now()-curdate()*1000000-curtime()
|
||||
0
|
||||
@ -584,6 +584,8 @@ from_unixtime(2147483647)
|
||||
select from_unixtime(2147483648);
|
||||
from_unixtime(2147483648)
|
||||
NULL
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect unixtime value: '2147483648'
|
||||
select from_unixtime(0);
|
||||
from_unixtime(0)
|
||||
1970-01-01 03:00:00
|
||||
@ -593,6 +595,8 @@ unix_timestamp(from_unixtime(2147483647))
|
||||
select unix_timestamp(from_unixtime(2147483648));
|
||||
unix_timestamp(from_unixtime(2147483648))
|
||||
NULL
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect unixtime value: '2147483648'
|
||||
select unix_timestamp('2039-01-20 01:00:00');
|
||||
unix_timestamp('2039-01-20 01:00:00')
|
||||
NULL
|
||||
@ -960,17 +964,17 @@ SELECT SEC_TO_TIME(3300000);
|
||||
SEC_TO_TIME(3300000)
|
||||
838:59:59
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect time value: '3300000'
|
||||
Warning 1292 Truncated incorrect seconds value: '3300000'
|
||||
SELECT SEC_TO_TIME(3300000)+0;
|
||||
SEC_TO_TIME(3300000)+0
|
||||
8385959
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect time value: '3300000'
|
||||
Warning 1292 Truncated incorrect seconds value: '3300000'
|
||||
SELECT SEC_TO_TIME(3600 * 4294967296);
|
||||
SEC_TO_TIME(3600 * 4294967296)
|
||||
838:59:59
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect time value: '15461882265600'
|
||||
Warning 1292 Truncated incorrect seconds value: '15461882265600'
|
||||
SELECT TIME_TO_SEC('916:40:00');
|
||||
TIME_TO_SEC('916:40:00')
|
||||
3020399
|
||||
@ -1019,6 +1023,8 @@ NULL
|
||||
SELECT MAKETIME(0, 0, 4294967296);
|
||||
MAKETIME(0, 0, 4294967296)
|
||||
NULL
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect seconds value: '4294967296'
|
||||
SELECT MAKETIME(CAST(-1 AS UNSIGNED), 0, 0);
|
||||
MAKETIME(CAST(-1 AS UNSIGNED), 0, 0)
|
||||
838:59:59
|
||||
@ -1044,8 +1050,7 @@ SEC_TO_TIME(CAST(-1 AS UNSIGNED))
|
||||
838:59:59
|
||||
Warnings:
|
||||
Note 1105 Cast to unsigned converted negative integer to it's positive complement
|
||||
Note 1105 Cast to unsigned converted negative integer to it's positive complement
|
||||
Warning 1292 Truncated incorrect time value: '18446744073709551615'
|
||||
Warning 1292 Truncated incorrect seconds value: '18446744073709551615'
|
||||
SET NAMES latin1;
|
||||
SET character_set_results = NULL;
|
||||
SHOW VARIABLES LIKE 'character_set_results';
|
||||
@ -1058,6 +1063,7 @@ fmtddate field2
|
||||
Sep-4 12:00AM abcd
|
||||
DROP TABLE testBug8868;
|
||||
SET NAMES DEFAULT;
|
||||
SET TIMESTAMP=UNIX_TIMESTAMP('2001-01-01 11:22:33');
|
||||
CREATE TABLE t1 (
|
||||
a TIMESTAMP
|
||||
);
|
||||
@ -1066,7 +1072,11 @@ SELECT 1 FROM t1 ORDER BY MAKETIME(1, 1, a);
|
||||
1
|
||||
1
|
||||
1
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect seconds value: '20010101112233'
|
||||
Warning 1292 Truncated incorrect seconds value: '20010101112233'
|
||||
DROP TABLE t1;
|
||||
SET TIMESTAMP=DEFAULT;
|
||||
(select time_format(timediff(now(), DATE_SUB(now(),INTERVAL 5 DAY)),'%H') As H)
|
||||
union
|
||||
(select time_format(timediff(now(), DATE_SUB(now(),INTERVAL 5 DAY)),'%H') As H);
|
||||
@ -2630,7 +2640,7 @@ SELECT DATE_ADD('2001-01-01 10:20:30',INTERVAL 250000000000.0 SECOND) AS c1, DAT
|
||||
c1 c2
|
||||
9923-03-10 22:47:10.0 NULL
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect DECIMAL value: '2000000000000000000.0'
|
||||
Warning 1292 Truncated incorrect seconds value: '2000000000000000000.0'
|
||||
#
|
||||
# MDEV-4838 Wrong metadata for DATE_ADD('string', INVERVAL)
|
||||
#
|
||||
@ -2789,13 +2799,12 @@ DO TO_DAYS(SEC_TO_TIME(MAKEDATE('',RAND(~('')))));
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect INTEGER value: ''
|
||||
Warning 1292 Truncated incorrect INTEGER value: ''
|
||||
Warning 1292 Truncated incorrect INTEGER value: ''
|
||||
Warning 1292 Truncated incorrect time value: '20000101'
|
||||
Warning 1292 Truncated incorrect seconds value: '20000101'
|
||||
SELECT SEC_TO_TIME(MAKEDATE(0,RAND(~0)));
|
||||
SEC_TO_TIME(MAKEDATE(0,RAND(~0)))
|
||||
838:59:59
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect time value: '20000101'
|
||||
Warning 1292 Truncated incorrect seconds value: '20000101'
|
||||
#
|
||||
# End of 5.5 tests
|
||||
#
|
||||
@ -3544,3 +3553,32 @@ SET @@session.slow_query_log= @sav_slow_query_log;
|
||||
DROP FUNCTION fn_sleep_before_now;
|
||||
DROP TRIGGER trg_insert_t_ts;
|
||||
DROP TABLE t_ts, t_trig;
|
||||
#
|
||||
# MDEV-14032 SEC_TO_TIME executes side effect two times
|
||||
#
|
||||
SET @a=10000000;
|
||||
SELECT SEC_TO_TIME(@a:=@a+1);
|
||||
SEC_TO_TIME(@a:=@a+1)
|
||||
838:59:59
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect seconds value: '10000001'
|
||||
SELECT @a;
|
||||
@a
|
||||
10000001
|
||||
CREATE TABLE t1 (a TEXT);
|
||||
CREATE FUNCTION f1() RETURNS INT
|
||||
BEGIN
|
||||
INSERT INTO t1 VALUES ('f1 was called');
|
||||
RETURN 10000000;
|
||||
END;
|
||||
$$
|
||||
SELECT SEC_TO_TIME(f1());
|
||||
SEC_TO_TIME(f1())
|
||||
838:59:59
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect seconds value: '10000000'
|
||||
SELECT * FROM t1;
|
||||
a
|
||||
f1 was called
|
||||
DROP TABLE t1;
|
||||
DROP FUNCTION f1;
|
||||
|
@ -564,12 +564,15 @@ SET NAMES DEFAULT;
|
||||
# Bug #31160: MAKETIME() crashes server when returning NULL in ORDER BY using
|
||||
# filesort
|
||||
#
|
||||
SET TIMESTAMP=UNIX_TIMESTAMP('2001-01-01 11:22:33');
|
||||
CREATE TABLE t1 (
|
||||
a TIMESTAMP
|
||||
);
|
||||
INSERT INTO t1 VALUES (now()), (now());
|
||||
SELECT 1 FROM t1 ORDER BY MAKETIME(1, 1, a);
|
||||
DROP TABLE t1;
|
||||
SET TIMESTAMP=DEFAULT;
|
||||
|
||||
#
|
||||
# Bug #19844 time_format in Union truncates values
|
||||
#
|
||||
@ -2135,3 +2138,26 @@ DROP TABLE t_ts, t_trig;
|
||||
#
|
||||
# End of MDEV-13727
|
||||
###################
|
||||
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-14032 SEC_TO_TIME executes side effect two times
|
||||
--echo #
|
||||
|
||||
SET @a=10000000;
|
||||
SELECT SEC_TO_TIME(@a:=@a+1);
|
||||
SELECT @a;
|
||||
|
||||
CREATE TABLE t1 (a TEXT);
|
||||
DELIMITER $$;
|
||||
CREATE FUNCTION f1() RETURNS INT
|
||||
BEGIN
|
||||
INSERT INTO t1 VALUES ('f1 was called');
|
||||
RETURN 10000000;
|
||||
END;
|
||||
$$
|
||||
DELIMITER ;$$
|
||||
SELECT SEC_TO_TIME(f1());
|
||||
SELECT * FROM t1;
|
||||
DROP TABLE t1;
|
||||
DROP FUNCTION f1;
|
||||
|
@ -492,8 +492,12 @@ INSERT INTO t1 (f2,f3) VALUES (NOW(), "0000-00-00 00:00:00");
|
||||
INSERT INTO t1 (f2,f3) VALUES (NOW(), NULL);
|
||||
INSERT INTO t1 (f2,f3) VALUES (NOW(), ASCII(NULL));
|
||||
INSERT INTO t1 (f2,f3) VALUES (NOW(), FROM_UNIXTIME('9999999999'));
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect unixtime value: '9999999999'
|
||||
INSERT INTO t1 (f2,f3) VALUES (NOW(), TIME(NULL));
|
||||
UPDATE t1 SET f2=NOW(), f3=FROM_UNIXTIME('9999999999') WHERE f1=1;
|
||||
Warnings:
|
||||
Warning 1292 Truncated incorrect unixtime value: '9999999999'
|
||||
SELECT f1,f2-f3 FROM t1;
|
||||
f1 f2-f3
|
||||
1 0
|
||||
|
87
sql/field.cc
87
sql/field.cc
@ -5116,10 +5116,8 @@ int Field_timestamp::store(double nr)
|
||||
int error;
|
||||
ErrConvDouble str(nr);
|
||||
THD *thd= get_thd();
|
||||
|
||||
longlong tmp= double_to_datetime(nr, &l_time, sql_mode_for_timestamp(thd),
|
||||
&error);
|
||||
return store_TIME_with_warning(thd, &l_time, &str, error, tmp != -1);
|
||||
bool rc= Sec6(nr).to_datetime(&l_time, sql_mode_for_timestamp(thd), &error);
|
||||
return store_TIME_with_warning(thd, &l_time, &str, error, !rc);
|
||||
}
|
||||
|
||||
|
||||
@ -5129,10 +5127,10 @@ int Field_timestamp::store(longlong nr, bool unsigned_val)
|
||||
int error;
|
||||
ErrConvInteger str(nr, unsigned_val);
|
||||
THD *thd= get_thd();
|
||||
|
||||
longlong tmp= number_to_datetime(nr, 0, &l_time, sql_mode_for_timestamp(thd),
|
||||
&error);
|
||||
return store_TIME_with_warning(thd, &l_time, &str, error, tmp != -1);
|
||||
bool rc= Sec6(nr, unsigned_val).to_datetime(&l_time,
|
||||
sql_mode_for_timestamp(thd),
|
||||
&error);
|
||||
return store_TIME_with_warning(thd, &l_time, &str, error, !rc);
|
||||
}
|
||||
|
||||
|
||||
@ -5428,23 +5426,12 @@ my_decimal *Field_timestamp_with_dec::val_decimal(my_decimal *d)
|
||||
|
||||
int Field_timestamp::store_decimal(const my_decimal *d)
|
||||
{
|
||||
ulonglong nr;
|
||||
ulong sec_part;
|
||||
int error;
|
||||
MYSQL_TIME ltime;
|
||||
longlong tmp;
|
||||
THD *thd= get_thd();
|
||||
ErrConvDecimal str(d);
|
||||
|
||||
if (my_decimal2seconds(d, &nr, &sec_part))
|
||||
{
|
||||
tmp= -1;
|
||||
error= 2;
|
||||
}
|
||||
else
|
||||
tmp= number_to_datetime(nr, sec_part, <ime, sql_mode_for_timestamp(thd),
|
||||
&error);
|
||||
return store_TIME_with_warning(thd, <ime, &str, error, tmp != -1);
|
||||
bool rc= Sec6(d).to_datetime(<ime, sql_mode_for_timestamp(thd), &error);
|
||||
return store_TIME_with_warning(thd, <ime, &str, error, !rc);
|
||||
}
|
||||
|
||||
int Field_timestamp_with_dec::set_time()
|
||||
@ -5623,10 +5610,8 @@ int Field_temporal_with_date::store(double nr)
|
||||
MYSQL_TIME ltime;
|
||||
THD *thd= get_thd();
|
||||
ErrConvDouble str(nr);
|
||||
|
||||
longlong tmp= double_to_datetime(nr, <ime,
|
||||
(uint) sql_mode_for_dates(thd), &error);
|
||||
return store_TIME_with_warning(<ime, &str, error, tmp != -1);
|
||||
bool rc= Sec6(nr).to_datetime(<ime, sql_mode_for_dates(thd), &error);
|
||||
return store_TIME_with_warning(<ime, &str, error, !rc);
|
||||
}
|
||||
|
||||
|
||||
@ -5634,13 +5619,11 @@ int Field_temporal_with_date::store(longlong nr, bool unsigned_val)
|
||||
{
|
||||
int error;
|
||||
MYSQL_TIME ltime;
|
||||
longlong tmp;
|
||||
THD *thd= get_thd();
|
||||
ErrConvInteger str(nr, unsigned_val);
|
||||
|
||||
tmp= number_to_datetime(nr, 0, <ime, sql_mode_for_dates(thd), &error);
|
||||
|
||||
return store_TIME_with_warning(<ime, &str, error, tmp != -1);
|
||||
bool rc= Sec6(nr, unsigned_val).to_datetime(<ime, sql_mode_for_dates(thd),
|
||||
&error);
|
||||
return store_TIME_with_warning(<ime, &str, error, !rc);
|
||||
}
|
||||
|
||||
|
||||
@ -5859,14 +5842,8 @@ int Field_time::store(double nr)
|
||||
MYSQL_TIME ltime;
|
||||
ErrConvDouble str(nr);
|
||||
int was_cut;
|
||||
bool neg= nr < 0;
|
||||
if (neg)
|
||||
nr= -nr;
|
||||
int have_smth_to_conv= !number_to_time(neg, (ulonglong) nr,
|
||||
(ulong)((nr - floor(nr)) * TIME_SECOND_PART_FACTOR),
|
||||
<ime, &was_cut);
|
||||
|
||||
return store_TIME_with_warning(<ime, &str, was_cut, have_smth_to_conv);
|
||||
bool rc= Sec6(nr).to_time(<ime, &was_cut);
|
||||
return store_TIME_with_warning(<ime, &str, was_cut, !rc);
|
||||
}
|
||||
|
||||
|
||||
@ -5875,13 +5852,8 @@ int Field_time::store(longlong nr, bool unsigned_val)
|
||||
MYSQL_TIME ltime;
|
||||
ErrConvInteger str(nr, unsigned_val);
|
||||
int was_cut;
|
||||
if (nr < 0 && unsigned_val)
|
||||
nr= 99991231235959LL + 1;
|
||||
int have_smth_to_conv= !number_to_time(nr < 0,
|
||||
(ulonglong) (nr < 0 ? -nr : nr),
|
||||
0, <ime, &was_cut);
|
||||
|
||||
return store_TIME_with_warning(<ime, &str, was_cut, have_smth_to_conv);
|
||||
bool rc= Sec6(nr, unsigned_val).to_time(<ime, &was_cut);
|
||||
return store_TIME_with_warning(<ime, &str, was_cut, !rc);
|
||||
}
|
||||
|
||||
|
||||
@ -6036,16 +6008,11 @@ void Field_time_hires::store_TIME(const MYSQL_TIME *ltime)
|
||||
|
||||
int Field_time::store_decimal(const my_decimal *d)
|
||||
{
|
||||
ulonglong nr;
|
||||
ulong sec_part;
|
||||
ErrConvDecimal str(d);
|
||||
MYSQL_TIME ltime;
|
||||
int was_cut;
|
||||
bool neg= my_decimal2seconds(d, &nr, &sec_part);
|
||||
|
||||
int have_smth_to_conv= !number_to_time(neg, nr, sec_part, <ime, &was_cut);
|
||||
|
||||
return store_TIME_with_warning(<ime, &str, was_cut, have_smth_to_conv);
|
||||
bool rc= Sec6(d).to_time(<ime, &was_cut);
|
||||
return store_TIME_with_warning(<ime, &str, was_cut, !rc);
|
||||
}
|
||||
|
||||
|
||||
@ -6763,24 +6730,12 @@ void Field_datetime_hires::store_TIME(MYSQL_TIME *ltime)
|
||||
|
||||
int Field_temporal_with_date::store_decimal(const my_decimal *d)
|
||||
{
|
||||
ulonglong nr;
|
||||
ulong sec_part;
|
||||
int error;
|
||||
MYSQL_TIME ltime;
|
||||
longlong tmp;
|
||||
THD *thd= get_thd();
|
||||
ErrConvDecimal str(d);
|
||||
|
||||
if (my_decimal2seconds(d, &nr, &sec_part))
|
||||
{
|
||||
tmp= -1;
|
||||
error= 2;
|
||||
}
|
||||
else
|
||||
tmp= number_to_datetime(nr, sec_part, <ime, sql_mode_for_dates(thd),
|
||||
&error);
|
||||
|
||||
return store_TIME_with_warning(<ime, &str, error, tmp != -1);
|
||||
bool rc= Sec6(d).to_datetime(<ime, sql_mode_for_dates(thd), &error);
|
||||
return store_TIME_with_warning(<ime, &str, error, !rc);
|
||||
}
|
||||
|
||||
bool Field_datetime_with_dec::send_binary(Protocol *protocol)
|
||||
|
13
sql/item.cc
13
sql/item.cc
@ -1396,19 +1396,6 @@ bool Item::make_zero_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
|
||||
return !(fuzzydate & TIME_FUZZY_DATES);
|
||||
}
|
||||
|
||||
bool Item::get_seconds(ulonglong *sec, ulong *sec_part)
|
||||
{
|
||||
if (decimals == 0)
|
||||
{ // optimize for an important special case
|
||||
longlong val= val_int();
|
||||
bool neg= val < 0 && !unsigned_flag;
|
||||
*sec= neg ? -val : val;
|
||||
*sec_part= 0;
|
||||
return neg;
|
||||
}
|
||||
VDec tmp(this);
|
||||
return tmp.is_null() ? 0 : my_decimal2seconds(tmp.ptr(), sec, sec_part);
|
||||
}
|
||||
|
||||
const MY_LOCALE *Item::locale_from_val_str()
|
||||
{
|
||||
|
@ -1665,7 +1665,6 @@ public:
|
||||
return get_date_result(<ime, fuzzydate) ? 0 : pack_time(<ime);
|
||||
}
|
||||
|
||||
bool get_seconds(ulonglong *sec, ulong *sec_part);
|
||||
virtual bool get_date_result(MYSQL_TIME *ltime, ulonglong fuzzydate)
|
||||
{ return get_date(ltime,fuzzydate); }
|
||||
/*
|
||||
|
@ -840,8 +840,7 @@ bool Item_func_hybrid_field_type::get_date_from_int_op(MYSQL_TIME *ltime,
|
||||
longlong value= int_op();
|
||||
bool neg= !unsigned_flag && value < 0;
|
||||
if (null_value || int_to_datetime_with_warn(neg, neg ? -value : value,
|
||||
ltime, fuzzydate,
|
||||
field_name_or_null()))
|
||||
ltime, fuzzydate, NULL))
|
||||
return make_zero_mysql_time(ltime, fuzzydate);
|
||||
return (null_value= 0);
|
||||
}
|
||||
@ -875,8 +874,7 @@ bool Item_func_hybrid_field_type::get_date_from_real_op(MYSQL_TIME *ltime,
|
||||
ulonglong fuzzydate)
|
||||
{
|
||||
double value= real_op();
|
||||
if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate,
|
||||
field_name_or_null()))
|
||||
if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate, NULL))
|
||||
return make_zero_mysql_time(ltime, fuzzydate);
|
||||
return (null_value= 0);
|
||||
}
|
||||
|
@ -450,10 +450,10 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
|
||||
{
|
||||
if (!my_isspace(&my_charset_latin1,*val))
|
||||
{
|
||||
ErrConvString err(val_begin, length, &my_charset_bin);
|
||||
make_truncated_value_warning(current_thd,
|
||||
Sql_condition::WARN_LEVEL_WARN,
|
||||
val_begin, length,
|
||||
cached_timestamp_type, NullS);
|
||||
&err, cached_timestamp_type, NullS);
|
||||
break;
|
||||
}
|
||||
} while (++val != val_end);
|
||||
@ -1335,24 +1335,18 @@ bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval)
|
||||
if (int_type == INTERVAL_SECOND && args->decimals)
|
||||
{
|
||||
VDec val(args);
|
||||
ulonglong second;
|
||||
ulong second_part;
|
||||
if (val.is_null())
|
||||
return true;
|
||||
interval->neg= my_decimal2seconds(val.ptr(), &second, &second_part);
|
||||
if (second == LONGLONG_MAX)
|
||||
Sec6 d(val.ptr());
|
||||
interval->neg= d.neg();
|
||||
if (d.sec() >= LONGLONG_MAX)
|
||||
{
|
||||
THD *thd= current_thd;
|
||||
ErrConvDecimal err(val.ptr());
|
||||
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
||||
ER_TRUNCATED_WRONG_VALUE,
|
||||
ER_THD(thd, ER_TRUNCATED_WRONG_VALUE), "DECIMAL",
|
||||
err.ptr());
|
||||
current_thd->push_warning_truncated_wrong_value("seconds", err.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
interval->second= second;
|
||||
interval->second_part= second_part;
|
||||
interval->second= d.sec();
|
||||
interval->second_part= d.usec();
|
||||
return false;
|
||||
}
|
||||
else if ((int) int_type <= INTERVAL_MICROSECOND)
|
||||
@ -1740,52 +1734,12 @@ bool Item_func_sysdate_local::get_date(MYSQL_TIME *res,
|
||||
bool Item_func_sec_to_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
|
||||
{
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
bool sign;
|
||||
ulonglong sec;
|
||||
ulong sec_part;
|
||||
|
||||
bzero((char *)ltime, sizeof(*ltime));
|
||||
ltime->time_type= MYSQL_TIMESTAMP_TIME;
|
||||
|
||||
sign= args[0]->get_seconds(&sec, &sec_part);
|
||||
|
||||
if ((null_value= args[0]->null_value))
|
||||
return 1;
|
||||
|
||||
ltime->neg= sign;
|
||||
if (sec > TIME_MAX_VALUE_SECONDS)
|
||||
goto overflow;
|
||||
|
||||
DBUG_ASSERT(sec_part <= TIME_MAX_SECOND_PART);
|
||||
|
||||
ltime->hour= (uint) (sec/3600);
|
||||
ltime->minute= (uint) (sec % 3600) /60;
|
||||
ltime->second= (uint) sec % 60;
|
||||
ltime->second_part= sec_part;
|
||||
|
||||
return 0;
|
||||
|
||||
overflow:
|
||||
/* use check_time_range() to set ltime to the max value depending on dec */
|
||||
int unused;
|
||||
char buf[100];
|
||||
String tmp(buf, sizeof(buf), &my_charset_bin), *err= args[0]->val_str(&tmp);
|
||||
|
||||
ltime->hour= TIME_MAX_HOUR+1;
|
||||
check_time_range(ltime, decimals, &unused);
|
||||
if (!err)
|
||||
{
|
||||
ErrConvInteger err2(sec, unsigned_flag);
|
||||
make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
|
||||
&err2, MYSQL_TIMESTAMP_TIME, NullS);
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrConvString err2(err);
|
||||
make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
|
||||
&err2, MYSQL_TIMESTAMP_TIME, NullS);
|
||||
}
|
||||
return 0;
|
||||
VSec6 sec(args[0], "seconds", LONGLONG_MAX);
|
||||
if ((null_value= sec.is_null()))
|
||||
return true;
|
||||
if (sec.sec_to_time(ltime, decimals) && !sec.truncated())
|
||||
sec.make_truncated_warning(current_thd, "seconds");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Item_func_date_format::fix_length_and_dec()
|
||||
@ -1995,21 +1949,17 @@ bool Item_func_from_unixtime::fix_length_and_dec()
|
||||
bool Item_func_from_unixtime::get_date(MYSQL_TIME *ltime,
|
||||
ulonglong fuzzy_date __attribute__((unused)))
|
||||
{
|
||||
bool sign;
|
||||
ulonglong sec;
|
||||
ulong sec_part;
|
||||
|
||||
bzero((char *)ltime, sizeof(*ltime));
|
||||
ltime->time_type= MYSQL_TIMESTAMP_TIME;
|
||||
|
||||
sign= args[0]->get_seconds(&sec, &sec_part);
|
||||
VSec6 sec(args[0], "unixtime", TIMESTAMP_MAX_VALUE);
|
||||
DBUG_ASSERT(sec.sec() <= TIMESTAMP_MAX_VALUE);
|
||||
|
||||
if (args[0]->null_value || sign || sec > TIMESTAMP_MAX_VALUE)
|
||||
if (sec.is_null() || sec.truncated() || sec.neg())
|
||||
return (null_value= 1);
|
||||
|
||||
tz->gmt_sec_to_TIME(ltime, (my_time_t)sec);
|
||||
|
||||
ltime->second_part= sec_part;
|
||||
tz->gmt_sec_to_TIME(ltime, (my_time_t) sec.sec());
|
||||
ltime->second_part= sec.usec();
|
||||
|
||||
return (null_value= 0);
|
||||
}
|
||||
@ -2694,12 +2644,11 @@ bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
|
||||
bool overflow= 0;
|
||||
longlong hour= args[0]->val_int();
|
||||
longlong minute= args[1]->val_int();
|
||||
ulonglong second;
|
||||
ulong microsecond;
|
||||
bool neg= args[2]->get_seconds(&second, µsecond);
|
||||
VSec6 sec(args[2], "seconds", 59);
|
||||
|
||||
if (args[0]->null_value || args[1]->null_value || args[2]->null_value ||
|
||||
minute < 0 || minute > 59 || neg || second > 59)
|
||||
DBUG_ASSERT(sec.sec() <= 59);
|
||||
if (args[0]->null_value || args[1]->null_value || sec.is_null() ||
|
||||
minute < 0 || minute > 59 || sec.neg() || sec.truncated())
|
||||
return (null_value= 1);
|
||||
|
||||
bzero(ltime, sizeof(*ltime));
|
||||
@ -2720,8 +2669,8 @@ bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
|
||||
{
|
||||
ltime->hour= (uint) ((hour < 0 ? -hour : hour));
|
||||
ltime->minute= (uint) minute;
|
||||
ltime->second= (uint) second;
|
||||
ltime->second_part= microsecond;
|
||||
ltime->second= (uint) sec.sec();
|
||||
ltime->second_part= sec.usec();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2730,10 +2679,10 @@ bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
|
||||
ltime->second= TIME_MAX_SECOND;
|
||||
char buf[28];
|
||||
char *ptr= longlong10_to_str(hour, buf, args[0]->unsigned_flag ? 10 : -10);
|
||||
int len = (int)(ptr - buf) + sprintf(ptr, ":%02u:%02u", (uint)minute, (uint)second);
|
||||
make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
|
||||
buf, len, MYSQL_TIMESTAMP_TIME,
|
||||
NullS);
|
||||
int len = (int)(ptr - buf) + sprintf(ptr, ":%02u:%02u",
|
||||
(uint) minute, (uint) sec.sec());
|
||||
ErrConvString err(buf, len, &my_charset_bin);
|
||||
current_thd->push_warning_truncated_wrong_value("time", err.ptr());
|
||||
}
|
||||
|
||||
return (null_value= 0);
|
||||
|
@ -4372,6 +4372,69 @@ private:
|
||||
return raised;
|
||||
}
|
||||
|
||||
private:
|
||||
void push_warning_truncated_priv(Sql_condition::enum_warning_level level,
|
||||
uint sql_errno,
|
||||
const char *type_str, const char *val)
|
||||
{
|
||||
DBUG_ASSERT(sql_errno == ER_TRUNCATED_WRONG_VALUE ||
|
||||
sql_errno == ER_WRONG_VALUE);
|
||||
char buff[MYSQL_ERRMSG_SIZE];
|
||||
CHARSET_INFO *cs= &my_charset_latin1;
|
||||
cs->cset->snprintf(cs, buff, sizeof(buff),
|
||||
ER_THD(this, sql_errno), type_str, val);
|
||||
/*
|
||||
Note: the format string can vary between ER_TRUNCATED_WRONG_VALUE
|
||||
and ER_WRONG_VALUE, but the code passed to push_warning() is
|
||||
always ER_TRUNCATED_WRONG_VALUE. This is intentional.
|
||||
*/
|
||||
push_warning(this, level, ER_TRUNCATED_WRONG_VALUE, buff);
|
||||
}
|
||||
public:
|
||||
void push_warning_truncated_wrong_value(Sql_condition::enum_warning_level level,
|
||||
const char *type_str, const char *val)
|
||||
{
|
||||
return push_warning_truncated_priv(level, ER_TRUNCATED_WRONG_VALUE,
|
||||
type_str, val);
|
||||
}
|
||||
void push_warning_wrong_value(Sql_condition::enum_warning_level level,
|
||||
const char *type_str, const char *val)
|
||||
{
|
||||
return push_warning_truncated_priv(level, ER_WRONG_VALUE, type_str, val);
|
||||
}
|
||||
void push_warning_truncated_wrong_value(const char *type_str, const char *val)
|
||||
{
|
||||
return push_warning_truncated_wrong_value(Sql_condition::WARN_LEVEL_WARN,
|
||||
type_str, val);
|
||||
}
|
||||
void push_warning_truncated_value_for_field(Sql_condition::enum_warning_level
|
||||
level, const char *type_str,
|
||||
const char *val, const char *name)
|
||||
{
|
||||
DBUG_ASSERT(name);
|
||||
char buff[MYSQL_ERRMSG_SIZE];
|
||||
CHARSET_INFO *cs= &my_charset_latin1;
|
||||
cs->cset->snprintf(cs, buff, sizeof(buff),
|
||||
ER_THD(this, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
|
||||
type_str, val, name,
|
||||
(ulong) get_stmt_da()->current_row_for_warning());
|
||||
push_warning(this, level, ER_TRUNCATED_WRONG_VALUE, buff);
|
||||
|
||||
}
|
||||
void push_warning_wrong_or_truncated_value(Sql_condition::enum_warning_level level,
|
||||
bool totally_useless_value,
|
||||
const char *type_str,
|
||||
const char *val,
|
||||
const char *field_name)
|
||||
{
|
||||
if (field_name)
|
||||
push_warning_truncated_value_for_field(level, type_str, val, field_name);
|
||||
else if (totally_useless_value)
|
||||
push_warning_wrong_value(level, type_str, val);
|
||||
else
|
||||
push_warning_truncated_wrong_value(level, type_str, val);
|
||||
}
|
||||
|
||||
public:
|
||||
/** Overloaded to guard query/query_length fields */
|
||||
virtual void set_statement(Statement *stmt);
|
||||
|
@ -10063,19 +10063,17 @@ err_new_table_cleanup:
|
||||
if (unlikely(alter_ctx.error_if_not_empty &&
|
||||
thd->get_stmt_da()->current_row_for_warning()))
|
||||
{
|
||||
const char *f_val= 0;
|
||||
enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE;
|
||||
const char *f_val= "0000-00-00";
|
||||
const char *f_type= "date";
|
||||
switch (alter_ctx.datetime_field->real_field_type())
|
||||
{
|
||||
case MYSQL_TYPE_DATE:
|
||||
case MYSQL_TYPE_NEWDATE:
|
||||
f_val= "0000-00-00";
|
||||
t_type= MYSQL_TIMESTAMP_DATE;
|
||||
break;
|
||||
case MYSQL_TYPE_DATETIME:
|
||||
case MYSQL_TYPE_DATETIME2:
|
||||
f_val= "0000-00-00 00:00:00";
|
||||
t_type= MYSQL_TIMESTAMP_DATETIME;
|
||||
f_type= "datetime";
|
||||
break;
|
||||
default:
|
||||
/* Shouldn't get here. */
|
||||
@ -10083,9 +10081,10 @@ err_new_table_cleanup:
|
||||
}
|
||||
bool save_abort_on_warning= thd->abort_on_warning;
|
||||
thd->abort_on_warning= true;
|
||||
make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN,
|
||||
f_val, strlength(f_val), t_type,
|
||||
alter_ctx.datetime_field->field_name.str);
|
||||
thd->push_warning_truncated_value_for_field(Sql_condition::WARN_LEVEL_WARN,
|
||||
f_type, f_val,
|
||||
alter_ctx.datetime_field->
|
||||
field_name.str);
|
||||
thd->abort_on_warning= save_abort_on_warning;
|
||||
}
|
||||
|
||||
|
111
sql/sql_time.cc
111
sql/sql_time.cc
@ -313,8 +313,7 @@ adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec)
|
||||
if (check_time_range(ltime, dec, &warnings))
|
||||
return true;
|
||||
if (warnings)
|
||||
make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
|
||||
&str, MYSQL_TIMESTAMP_TIME, NullS);
|
||||
current_thd->push_warning_truncated_wrong_value("time", str.ptr());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -399,11 +398,14 @@ str_to_datetime_with_warn(CHARSET_INFO *cs,
|
||||
THD *thd= current_thd;
|
||||
bool ret_val= str_to_datetime(cs, str, length, l_time, flags, &status);
|
||||
if (ret_val || status.warnings)
|
||||
{
|
||||
const ErrConvString err(str, length, &my_charset_bin);
|
||||
make_truncated_value_warning(thd,
|
||||
ret_val ? Sql_condition::WARN_LEVEL_WARN :
|
||||
Sql_condition::time_warn_level(status.warnings),
|
||||
str, length, flags & TIME_TIME_ONLY ?
|
||||
&err, flags & TIME_TIME_ONLY ?
|
||||
MYSQL_TIMESTAMP_TIME : l_time->time_type, NullS);
|
||||
}
|
||||
DBUG_EXECUTE_IF("str_to_datetime_warn",
|
||||
push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
|
||||
ER_YES, str););
|
||||
@ -411,77 +413,11 @@ str_to_datetime_with_warn(CHARSET_INFO *cs,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
converts a pair of numbers (integer part, microseconds) to MYSQL_TIME
|
||||
|
||||
@param neg sign of the time value
|
||||
@param nr integer part of the number to convert
|
||||
@param sec_part microsecond part of the number
|
||||
@param ltime converted value will be written here
|
||||
@param fuzzydate conversion flags (TIME_INVALID_DATE, etc)
|
||||
@param str original number, as an ErrConv. For the warning
|
||||
@param field_name field name or NULL if not a field. For the warning
|
||||
|
||||
@returns 0 for success, 1 for a failure
|
||||
*/
|
||||
static bool number_to_time_with_warn(bool neg, ulonglong nr, ulong sec_part,
|
||||
MYSQL_TIME *ltime, ulonglong fuzzydate,
|
||||
const ErrConv *str,
|
||||
const char *field_name)
|
||||
{
|
||||
int was_cut;
|
||||
longlong res;
|
||||
enum_mysql_timestamp_type ts_type;
|
||||
bool have_warnings;
|
||||
|
||||
if (fuzzydate & TIME_TIME_ONLY)
|
||||
{
|
||||
fuzzydate= TIME_TIME_ONLY; // clear other flags
|
||||
ts_type= MYSQL_TIMESTAMP_TIME;
|
||||
res= number_to_time(neg, nr, sec_part, ltime, &was_cut);
|
||||
have_warnings= MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut);
|
||||
}
|
||||
else
|
||||
{
|
||||
ts_type= MYSQL_TIMESTAMP_DATETIME;
|
||||
if (neg)
|
||||
{
|
||||
res= -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
res= number_to_datetime(nr, sec_part, ltime, fuzzydate, &was_cut);
|
||||
have_warnings= was_cut && (fuzzydate & TIME_NO_ZERO_IN_DATE);
|
||||
}
|
||||
}
|
||||
|
||||
if (res < 0 || have_warnings)
|
||||
{
|
||||
make_truncated_value_warning(current_thd,
|
||||
Sql_condition::WARN_LEVEL_WARN, str,
|
||||
res < 0 ? MYSQL_TIMESTAMP_ERROR : ts_type,
|
||||
field_name);
|
||||
}
|
||||
return res < 0;
|
||||
}
|
||||
|
||||
|
||||
bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime,
|
||||
ulonglong fuzzydate, const char *field_name)
|
||||
{
|
||||
const ErrConvDouble str(value);
|
||||
bool neg= value < 0;
|
||||
|
||||
if (neg)
|
||||
value= -value;
|
||||
|
||||
if (value > LONGLONG_MAX)
|
||||
value= static_cast<double>(LONGLONG_MAX);
|
||||
|
||||
longlong nr= static_cast<ulonglong>(floor(value));
|
||||
uint sec_part= static_cast<ulong>((value - floor(value))*TIME_SECOND_PART_FACTOR);
|
||||
return number_to_time_with_warn(neg, nr, sec_part, ltime, fuzzydate, &str,
|
||||
field_name);
|
||||
return Sec6(value).convert_to_mysql_time(ltime, fuzzydate, &str, field_name);
|
||||
}
|
||||
|
||||
|
||||
@ -489,11 +425,7 @@ bool decimal_to_datetime_with_warn(const my_decimal *value, MYSQL_TIME *ltime,
|
||||
ulonglong fuzzydate, const char *field_name)
|
||||
{
|
||||
const ErrConvDecimal str(value);
|
||||
ulonglong nr;
|
||||
ulong sec_part;
|
||||
bool neg= my_decimal2seconds(value, &nr, &sec_part);
|
||||
return number_to_time_with_warn(neg, nr, sec_part, ltime, fuzzydate, &str,
|
||||
field_name);
|
||||
return Sec6(value).convert_to_mysql_time(ltime, fuzzydate, &str, field_name);
|
||||
}
|
||||
|
||||
|
||||
@ -501,8 +433,8 @@ bool int_to_datetime_with_warn(bool neg, ulonglong value, MYSQL_TIME *ltime,
|
||||
ulonglong fuzzydate, const char *field_name)
|
||||
{
|
||||
const ErrConvInteger str(neg ? - (longlong) value : (longlong) value, !neg);
|
||||
return number_to_time_with_warn(neg, value, 0, ltime,
|
||||
fuzzydate, &str, field_name);
|
||||
Sec6 sec(neg, value, 0);
|
||||
return sec.convert_to_mysql_time(ltime, fuzzydate, &str, field_name);
|
||||
}
|
||||
|
||||
|
||||
@ -932,9 +864,7 @@ void make_truncated_value_warning(THD *thd,
|
||||
timestamp_type time_type,
|
||||
const char *field_name)
|
||||
{
|
||||
char warn_buff[MYSQL_ERRMSG_SIZE];
|
||||
const char *type_str;
|
||||
CHARSET_INFO *cs= &my_charset_latin1;
|
||||
|
||||
switch (time_type) {
|
||||
case MYSQL_TIMESTAMP_DATE:
|
||||
@ -948,23 +878,9 @@ void make_truncated_value_warning(THD *thd,
|
||||
type_str= "datetime";
|
||||
break;
|
||||
}
|
||||
if (field_name)
|
||||
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
|
||||
ER_THD(thd, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
|
||||
type_str, sval->ptr(), field_name,
|
||||
(ulong) thd->get_stmt_da()->current_row_for_warning());
|
||||
else
|
||||
{
|
||||
if (time_type > MYSQL_TIMESTAMP_ERROR)
|
||||
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
|
||||
ER_THD(thd, ER_TRUNCATED_WRONG_VALUE),
|
||||
type_str, sval->ptr());
|
||||
else
|
||||
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
|
||||
ER_THD(thd, ER_WRONG_VALUE), type_str, sval->ptr());
|
||||
}
|
||||
push_warning(thd, level,
|
||||
ER_TRUNCATED_WRONG_VALUE, warn_buff);
|
||||
return thd->push_warning_wrong_or_truncated_value(level,
|
||||
time_type <= MYSQL_TIMESTAMP_ERROR,
|
||||
type_str, sval->ptr(), field_name);
|
||||
}
|
||||
|
||||
|
||||
@ -1442,8 +1358,7 @@ time_to_datetime_with_warn(THD *thd,
|
||||
check_date(to, fuzzydate, &warn)))
|
||||
{
|
||||
ErrConvTime str(from);
|
||||
make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN,
|
||||
&str, MYSQL_TIMESTAMP_DATETIME, 0);
|
||||
thd->push_warning_truncated_wrong_value("datetime", str.ptr());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -120,14 +120,6 @@ void make_truncated_value_warning(THD *thd,
|
||||
timestamp_type time_type,
|
||||
const char *field_name);
|
||||
|
||||
static inline void make_truncated_value_warning(THD *thd,
|
||||
Sql_condition::enum_warning_level level, const char *str_val, size_t str_length, timestamp_type time_type,
|
||||
const char *field_name)
|
||||
{
|
||||
const ErrConvString str(str_val, str_length, &my_charset_bin);
|
||||
make_truncated_value_warning(thd, level, &str, time_type, field_name);
|
||||
}
|
||||
|
||||
extern DATE_TIME_FORMAT *date_time_format_make(timestamp_type format_type,
|
||||
const char *format_str,
|
||||
uint format_length);
|
||||
|
138
sql/sql_type.cc
138
sql/sql_type.cc
@ -181,6 +181,144 @@ Temporal_hybrid::Temporal_hybrid(THD *thd, Item *item)
|
||||
}
|
||||
|
||||
|
||||
void Sec6::make_from_decimal(const my_decimal *d)
|
||||
{
|
||||
m_neg= my_decimal2seconds(d, &m_sec, &m_usec);
|
||||
m_truncated= (m_sec >= LONGLONG_MAX);
|
||||
}
|
||||
|
||||
|
||||
void Sec6::make_from_double(double nr)
|
||||
{
|
||||
if ((m_neg= nr < 0))
|
||||
nr= -nr;
|
||||
if ((m_truncated= nr > (double) LONGLONG_MAX))
|
||||
{
|
||||
m_sec= LONGLONG_MAX;
|
||||
m_usec= 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sec= (ulonglong) nr;
|
||||
m_usec= (ulong) ((nr - floor(nr)) * 1000000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Sec6::make_truncated_warning(THD *thd, const char *type_str) const
|
||||
{
|
||||
char buff[1 + MAX_BIGINT_WIDTH + 1 + 6 + 1]; // '-' int '.' frac '\0'
|
||||
to_string(buff, sizeof(buff));
|
||||
current_thd->push_warning_truncated_wrong_value(type_str, buff);
|
||||
}
|
||||
|
||||
|
||||
bool Sec6::to_time_with_warn(MYSQL_TIME *to, const ErrConv *str,
|
||||
const char *field_name) const
|
||||
{
|
||||
int was_cut;
|
||||
bool res= to_time(to, &was_cut);
|
||||
if (res || MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut))
|
||||
current_thd->
|
||||
push_warning_wrong_or_truncated_value(Sql_condition::WARN_LEVEL_WARN,
|
||||
res, "time", str->ptr(),
|
||||
field_name);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
bool Sec6::to_datetime_with_warn(MYSQL_TIME *to, ulonglong fuzzydate,
|
||||
const ErrConv *str,
|
||||
const char *field_name) const
|
||||
{
|
||||
bool res, have_warnings= false;
|
||||
int was_cut;
|
||||
res= to_datetime(to, fuzzydate, &was_cut);
|
||||
have_warnings= was_cut && (fuzzydate & TIME_NO_ZERO_IN_DATE);
|
||||
if (res || have_warnings)
|
||||
current_thd->
|
||||
push_warning_wrong_or_truncated_value(Sql_condition::WARN_LEVEL_WARN,
|
||||
res, "datetime", str->ptr(),
|
||||
field_name);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
bool Sec6::convert_to_mysql_time(MYSQL_TIME *ltime, ulonglong fuzzydate,
|
||||
const ErrConv *str, const char *field_name)
|
||||
const
|
||||
{
|
||||
bool is_time= fuzzydate & TIME_TIME_ONLY;
|
||||
if (truncated())
|
||||
{
|
||||
/*
|
||||
The value was already truncated at the constructor call time,
|
||||
and a truncation warning was issued. Here we convert silently
|
||||
to avoid double warnings.
|
||||
*/
|
||||
current_thd->
|
||||
push_warning_wrong_or_truncated_value(Sql_condition::WARN_LEVEL_WARN,
|
||||
!is_time,
|
||||
is_time ? "time" : "datetime",
|
||||
str->ptr(), field_name);
|
||||
int warn;
|
||||
return is_time ? to_time(ltime, &warn) :
|
||||
to_datetime(ltime, fuzzydate, &warn);
|
||||
}
|
||||
return is_time ? to_time_with_warn(ltime, str, field_name) :
|
||||
to_datetime_with_warn(ltime, fuzzydate, str, field_name);
|
||||
}
|
||||
|
||||
|
||||
VSec6::VSec6(Item *item, const char *type_str, ulonglong limit)
|
||||
{
|
||||
if (item->decimals == 0)
|
||||
{ // optimize for an important special case
|
||||
longlong nr= item->val_int();
|
||||
make_from_int(nr, item->unsigned_flag);
|
||||
m_is_null= item->null_value;
|
||||
if (!m_is_null && m_sec > limit)
|
||||
{
|
||||
m_sec= limit;
|
||||
m_truncated= true;
|
||||
ErrConvInteger err(nr, item->unsigned_flag);
|
||||
current_thd->push_warning_truncated_wrong_value(type_str, err.ptr());
|
||||
}
|
||||
}
|
||||
else if (item->cmp_type() == REAL_RESULT)
|
||||
{
|
||||
double nr= item->val_real();
|
||||
make_from_double(nr);
|
||||
m_is_null= item->null_value;
|
||||
if (!m_is_null && m_sec > limit)
|
||||
{
|
||||
m_sec= limit;
|
||||
m_truncated= true;
|
||||
}
|
||||
if (m_truncated)
|
||||
{
|
||||
ErrConvDouble err(nr);
|
||||
current_thd->push_warning_truncated_wrong_value(type_str, err.ptr());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VDec tmp(item);
|
||||
(m_is_null= tmp.is_null()) ? reset() : make_from_decimal(tmp.ptr());
|
||||
if (!m_is_null && m_sec > limit)
|
||||
{
|
||||
m_sec= limit;
|
||||
m_truncated= true;
|
||||
}
|
||||
if (m_truncated)
|
||||
{
|
||||
ErrConvDecimal err(tmp.ptr());
|
||||
current_thd->push_warning_truncated_wrong_value(type_str, err.ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Time::make_from_item(Item *item, const Options opt)
|
||||
{
|
||||
if (item->get_date(this, opt.get_date_flags()))
|
||||
|
120
sql/sql_type.h
120
sql/sql_type.h
@ -210,6 +210,126 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Class Sec6 represents a fixed point value with 6 fractional digits.
|
||||
Used e.g. to convert double and my_decimal values to TIME/DATETIME.
|
||||
*/
|
||||
|
||||
class Sec6
|
||||
{
|
||||
protected:
|
||||
ulonglong m_sec; // The integer part, between 0 and LONGLONG_MAX
|
||||
ulong m_usec; // The fractional part, between 0 and 999999
|
||||
bool m_neg; // false if positive, true of negative
|
||||
bool m_truncated; // Indicates if the constructor truncated the value
|
||||
void make_from_decimal(const my_decimal *d);
|
||||
void make_from_double(double d);
|
||||
void make_from_int(longlong nr, bool unsigned_val)
|
||||
{
|
||||
m_neg= nr < 0 && !unsigned_val;
|
||||
m_sec= m_neg ? (ulonglong) -nr : (ulonglong) nr;
|
||||
m_usec= 0;
|
||||
m_truncated= false;
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
m_sec= m_usec= m_neg= m_truncated= 0;
|
||||
}
|
||||
Sec6() { }
|
||||
public:
|
||||
Sec6(bool neg, ulonglong nr, ulong frac)
|
||||
:m_sec(nr), m_usec(frac), m_neg(neg), m_truncated(false)
|
||||
{ }
|
||||
Sec6(double nr)
|
||||
{
|
||||
make_from_double(nr);
|
||||
}
|
||||
Sec6(const my_decimal *d)
|
||||
{
|
||||
make_from_decimal(d);
|
||||
}
|
||||
Sec6(longlong nr, bool unsigned_val)
|
||||
{
|
||||
make_from_int(nr, unsigned_val);
|
||||
}
|
||||
bool neg() const { return m_neg; }
|
||||
bool truncated() const { return m_truncated; }
|
||||
ulonglong sec() const { return m_sec; }
|
||||
long usec() const { return m_usec; }
|
||||
/**
|
||||
Converts Sec6 to MYSQL_TIME
|
||||
|
||||
@param ltime converted value will be written here
|
||||
@param fuzzydate conversion flags (TIME_INVALID_DATE, etc)
|
||||
@param str original number, as an ErrConv. For the warning
|
||||
@param field_name field name or NULL if not a field. For the warning
|
||||
@returns false for success, true for a failure
|
||||
*/
|
||||
bool convert_to_mysql_time(MYSQL_TIME *ltime, ulonglong fuzzydate,
|
||||
const ErrConv *str, const char *field_name) const;
|
||||
|
||||
// Convert a number in format hhhmmss.ff to TIME'hhh:mm:ss.ff'
|
||||
bool to_time(MYSQL_TIME *to, int *warn) const
|
||||
{
|
||||
return number_to_time(m_neg, m_sec, m_usec, to, warn);
|
||||
}
|
||||
bool to_time_with_warn(MYSQL_TIME *to, const ErrConv *str,
|
||||
const char *field_name) const;
|
||||
/*
|
||||
Convert a number in format YYYYMMDDhhmmss.ff to
|
||||
TIMESTAMP'YYYY-MM-DD hh:mm:ss.ff'
|
||||
*/
|
||||
bool to_datetime(MYSQL_TIME *to, ulonglong flags, int *warn) const
|
||||
{
|
||||
if (m_neg)
|
||||
{
|
||||
*warn= MYSQL_TIME_WARN_OUT_OF_RANGE;
|
||||
return true;
|
||||
}
|
||||
return number_to_datetime(m_sec, m_usec, to, flags, warn) == -1;
|
||||
}
|
||||
bool to_datetime_with_warn(MYSQL_TIME *to, ulonglong fuzzydate,
|
||||
const ErrConv *str, const char *field_name) const;
|
||||
// Convert elapsed seconds to TIME
|
||||
bool sec_to_time(MYSQL_TIME *ltime, uint dec) const
|
||||
{
|
||||
set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
|
||||
ltime->neg= m_neg;
|
||||
if (m_sec > TIME_MAX_VALUE_SECONDS)
|
||||
{
|
||||
// use check_time_range() to set ltime to the max value depending on dec
|
||||
int unused;
|
||||
ltime->hour= TIME_MAX_HOUR + 1;
|
||||
check_time_range(ltime, dec, &unused);
|
||||
return true;
|
||||
}
|
||||
DBUG_ASSERT(usec() <= TIME_MAX_SECOND_PART);
|
||||
ltime->hour= (uint) (m_sec / 3600);
|
||||
ltime->minute= (uint) (m_sec % 3600) / 60;
|
||||
ltime->second= (uint) m_sec % 60;
|
||||
ltime->second_part= m_usec;
|
||||
return false;
|
||||
}
|
||||
size_t to_string(char *to, size_t nbytes) const
|
||||
{
|
||||
return m_usec ?
|
||||
my_snprintf(to, nbytes, "%s%llu.%06lu",
|
||||
m_neg ? "-" : "", m_sec, (uint) m_usec) :
|
||||
my_snprintf(to, nbytes, "%s%llu", m_neg ? "-" : "", m_sec);
|
||||
}
|
||||
void make_truncated_warning(THD *thd, const char *type_str) const;
|
||||
};
|
||||
|
||||
|
||||
class VSec6: public Sec6
|
||||
{
|
||||
bool m_is_null;
|
||||
public:
|
||||
VSec6(Item *item, const char *type_str, ulonglong limit);
|
||||
bool is_null() const { return m_is_null; }
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
A heler class to perform additive operations between
|
||||
two MYSQL_TIME structures and return the result as a
|
||||
|
Loading…
x
Reference in New Issue
Block a user