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:
Alexander Barkov 2018-08-09 06:31:05 +04:00
parent a12e6c5ba4
commit 8524bb6872
15 changed files with 475 additions and 292 deletions

View File

@ -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)) SEC_TO_TIME(CONVERT(900*24*60*60 USING ucs2))
838:59:59.999999 838:59:59.999999
Warnings: 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 # MDEV-13530 VARBINARY doesn't convert to to BLOB for sizes 65533, 65534 and 65535
# #

View File

@ -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') sec_to_time('9001.1') sec_to_time('1234567890123.123')
02:30:01.100000 838:59:59.999999 02:30:01.100000 838:59:59.999999
Warnings: 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, 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) / 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 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) sec_to_time(90011e-1) sec_to_time(1234567890123e30)
02:30:01.100000 838:59:59.999999 02:30:01.100000 838:59:59.999999
Warnings: 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'); select sec_to_time(1234567890123), sec_to_time('99999999999999999999999999999');
sec_to_time(1234567890123) sec_to_time('99999999999999999999999999999') sec_to_time(1234567890123) sec_to_time('99999999999999999999999999999')
838:59:59 838:59:59.999999 838:59:59 838:59:59.999999
Warnings: Warnings:
Warning 1292 Truncated incorrect time value: '1234567890123' Warning 1292 Truncated incorrect seconds value: '1234567890123'
Warning 1292 Truncated incorrect time value: '99999999999999999999999999999' Warning 1292 Truncated incorrect seconds value: '99999999999999999999999999999'
select now()-curdate()*1000000-curtime(); select now()-curdate()*1000000-curtime();
now()-curdate()*1000000-curtime() now()-curdate()*1000000-curtime()
0 0
@ -584,6 +584,8 @@ from_unixtime(2147483647)
select from_unixtime(2147483648); select from_unixtime(2147483648);
from_unixtime(2147483648) from_unixtime(2147483648)
NULL NULL
Warnings:
Warning 1292 Truncated incorrect unixtime value: '2147483648'
select from_unixtime(0); select from_unixtime(0);
from_unixtime(0) from_unixtime(0)
1970-01-01 03:00:00 1970-01-01 03:00:00
@ -593,6 +595,8 @@ unix_timestamp(from_unixtime(2147483647))
select unix_timestamp(from_unixtime(2147483648)); select unix_timestamp(from_unixtime(2147483648));
unix_timestamp(from_unixtime(2147483648)) unix_timestamp(from_unixtime(2147483648))
NULL NULL
Warnings:
Warning 1292 Truncated incorrect unixtime value: '2147483648'
select unix_timestamp('2039-01-20 01:00:00'); select unix_timestamp('2039-01-20 01:00:00');
unix_timestamp('2039-01-20 01:00:00') unix_timestamp('2039-01-20 01:00:00')
NULL NULL
@ -960,17 +964,17 @@ SELECT SEC_TO_TIME(3300000);
SEC_TO_TIME(3300000) SEC_TO_TIME(3300000)
838:59:59 838:59:59
Warnings: Warnings:
Warning 1292 Truncated incorrect time value: '3300000' Warning 1292 Truncated incorrect seconds value: '3300000'
SELECT SEC_TO_TIME(3300000)+0; SELECT SEC_TO_TIME(3300000)+0;
SEC_TO_TIME(3300000)+0 SEC_TO_TIME(3300000)+0
8385959 8385959
Warnings: Warnings:
Warning 1292 Truncated incorrect time value: '3300000' Warning 1292 Truncated incorrect seconds value: '3300000'
SELECT SEC_TO_TIME(3600 * 4294967296); SELECT SEC_TO_TIME(3600 * 4294967296);
SEC_TO_TIME(3600 * 4294967296) SEC_TO_TIME(3600 * 4294967296)
838:59:59 838:59:59
Warnings: Warnings:
Warning 1292 Truncated incorrect time value: '15461882265600' Warning 1292 Truncated incorrect seconds value: '15461882265600'
SELECT TIME_TO_SEC('916:40:00'); SELECT TIME_TO_SEC('916:40:00');
TIME_TO_SEC('916:40:00') TIME_TO_SEC('916:40:00')
3020399 3020399
@ -1019,6 +1023,8 @@ NULL
SELECT MAKETIME(0, 0, 4294967296); SELECT MAKETIME(0, 0, 4294967296);
MAKETIME(0, 0, 4294967296) MAKETIME(0, 0, 4294967296)
NULL NULL
Warnings:
Warning 1292 Truncated incorrect seconds value: '4294967296'
SELECT MAKETIME(CAST(-1 AS UNSIGNED), 0, 0); SELECT MAKETIME(CAST(-1 AS UNSIGNED), 0, 0);
MAKETIME(CAST(-1 AS UNSIGNED), 0, 0) MAKETIME(CAST(-1 AS UNSIGNED), 0, 0)
838:59:59 838:59:59
@ -1044,8 +1050,7 @@ SEC_TO_TIME(CAST(-1 AS UNSIGNED))
838:59:59 838:59:59
Warnings: 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
Note 1105 Cast to unsigned converted negative integer to it's positive complement Warning 1292 Truncated incorrect seconds value: '18446744073709551615'
Warning 1292 Truncated incorrect time value: '18446744073709551615'
SET NAMES latin1; SET NAMES latin1;
SET character_set_results = NULL; SET character_set_results = NULL;
SHOW VARIABLES LIKE 'character_set_results'; SHOW VARIABLES LIKE 'character_set_results';
@ -1058,6 +1063,7 @@ fmtddate field2
Sep-4 12:00AM abcd Sep-4 12:00AM abcd
DROP TABLE testBug8868; DROP TABLE testBug8868;
SET NAMES DEFAULT; SET NAMES DEFAULT;
SET TIMESTAMP=UNIX_TIMESTAMP('2001-01-01 11:22:33');
CREATE TABLE t1 ( CREATE TABLE t1 (
a TIMESTAMP a TIMESTAMP
); );
@ -1066,7 +1072,11 @@ SELECT 1 FROM t1 ORDER BY MAKETIME(1, 1, a);
1 1
1 1
1 1
Warnings:
Warning 1292 Truncated incorrect seconds value: '20010101112233'
Warning 1292 Truncated incorrect seconds value: '20010101112233'
DROP TABLE t1; DROP TABLE t1;
SET TIMESTAMP=DEFAULT;
(select time_format(timediff(now(), DATE_SUB(now(),INTERVAL 5 DAY)),'%H') As H) (select time_format(timediff(now(), DATE_SUB(now(),INTERVAL 5 DAY)),'%H') As H)
union union
(select time_format(timediff(now(), DATE_SUB(now(),INTERVAL 5 DAY)),'%H') As H); (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 c1 c2
9923-03-10 22:47:10.0 NULL 9923-03-10 22:47:10.0 NULL
Warnings: 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) # MDEV-4838 Wrong metadata for DATE_ADD('string', INVERVAL)
# #
@ -2789,13 +2799,12 @@ DO TO_DAYS(SEC_TO_TIME(MAKEDATE('',RAND(~('')))));
Warnings: Warnings:
Warning 1292 Truncated incorrect INTEGER value: '' Warning 1292 Truncated incorrect INTEGER value: ''
Warning 1292 Truncated incorrect INTEGER value: '' Warning 1292 Truncated incorrect INTEGER value: ''
Warning 1292 Truncated incorrect INTEGER value: '' Warning 1292 Truncated incorrect seconds value: '20000101'
Warning 1292 Truncated incorrect time value: '20000101'
SELECT SEC_TO_TIME(MAKEDATE(0,RAND(~0))); SELECT SEC_TO_TIME(MAKEDATE(0,RAND(~0)));
SEC_TO_TIME(MAKEDATE(0,RAND(~0))) SEC_TO_TIME(MAKEDATE(0,RAND(~0)))
838:59:59 838:59:59
Warnings: Warnings:
Warning 1292 Truncated incorrect time value: '20000101' Warning 1292 Truncated incorrect seconds value: '20000101'
# #
# End of 5.5 tests # 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 FUNCTION fn_sleep_before_now;
DROP TRIGGER trg_insert_t_ts; DROP TRIGGER trg_insert_t_ts;
DROP TABLE t_ts, t_trig; 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;

View File

@ -564,12 +564,15 @@ SET NAMES DEFAULT;
# Bug #31160: MAKETIME() crashes server when returning NULL in ORDER BY using # Bug #31160: MAKETIME() crashes server when returning NULL in ORDER BY using
# filesort # filesort
# #
SET TIMESTAMP=UNIX_TIMESTAMP('2001-01-01 11:22:33');
CREATE TABLE t1 ( CREATE TABLE t1 (
a TIMESTAMP a TIMESTAMP
); );
INSERT INTO t1 VALUES (now()), (now()); INSERT INTO t1 VALUES (now()), (now());
SELECT 1 FROM t1 ORDER BY MAKETIME(1, 1, a); SELECT 1 FROM t1 ORDER BY MAKETIME(1, 1, a);
DROP TABLE t1; DROP TABLE t1;
SET TIMESTAMP=DEFAULT;
# #
# Bug #19844 time_format in Union truncates values # Bug #19844 time_format in Union truncates values
# #
@ -2135,3 +2138,26 @@ DROP TABLE t_ts, t_trig;
# #
# End of MDEV-13727 # 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;

View File

@ -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(), NULL);
INSERT INTO t1 (f2,f3) VALUES (NOW(), ASCII(NULL)); INSERT INTO t1 (f2,f3) VALUES (NOW(), ASCII(NULL));
INSERT INTO t1 (f2,f3) VALUES (NOW(), FROM_UNIXTIME('9999999999')); 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)); INSERT INTO t1 (f2,f3) VALUES (NOW(), TIME(NULL));
UPDATE t1 SET f2=NOW(), f3=FROM_UNIXTIME('9999999999') WHERE f1=1; 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; SELECT f1,f2-f3 FROM t1;
f1 f2-f3 f1 f2-f3
1 0 1 0

View File

@ -5116,10 +5116,8 @@ int Field_timestamp::store(double nr)
int error; int error;
ErrConvDouble str(nr); ErrConvDouble str(nr);
THD *thd= get_thd(); THD *thd= get_thd();
bool rc= Sec6(nr).to_datetime(&l_time, sql_mode_for_timestamp(thd), &error);
longlong tmp= double_to_datetime(nr, &l_time, sql_mode_for_timestamp(thd), return store_TIME_with_warning(thd, &l_time, &str, error, !rc);
&error);
return store_TIME_with_warning(thd, &l_time, &str, error, tmp != -1);
} }
@ -5129,10 +5127,10 @@ int Field_timestamp::store(longlong nr, bool unsigned_val)
int error; int error;
ErrConvInteger str(nr, unsigned_val); ErrConvInteger str(nr, unsigned_val);
THD *thd= get_thd(); THD *thd= get_thd();
bool rc= Sec6(nr, unsigned_val).to_datetime(&l_time,
longlong tmp= number_to_datetime(nr, 0, &l_time, sql_mode_for_timestamp(thd), sql_mode_for_timestamp(thd),
&error); &error);
return store_TIME_with_warning(thd, &l_time, &str, error, tmp != -1); 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) int Field_timestamp::store_decimal(const my_decimal *d)
{ {
ulonglong nr;
ulong sec_part;
int error; int error;
MYSQL_TIME ltime; MYSQL_TIME ltime;
longlong tmp;
THD *thd= get_thd(); THD *thd= get_thd();
ErrConvDecimal str(d); ErrConvDecimal str(d);
bool rc= Sec6(d).to_datetime(&ltime, sql_mode_for_timestamp(thd), &error);
if (my_decimal2seconds(d, &nr, &sec_part)) return store_TIME_with_warning(thd, &ltime, &str, error, !rc);
{
tmp= -1;
error= 2;
}
else
tmp= number_to_datetime(nr, sec_part, &ltime, sql_mode_for_timestamp(thd),
&error);
return store_TIME_with_warning(thd, &ltime, &str, error, tmp != -1);
} }
int Field_timestamp_with_dec::set_time() int Field_timestamp_with_dec::set_time()
@ -5623,10 +5610,8 @@ int Field_temporal_with_date::store(double nr)
MYSQL_TIME ltime; MYSQL_TIME ltime;
THD *thd= get_thd(); THD *thd= get_thd();
ErrConvDouble str(nr); ErrConvDouble str(nr);
bool rc= Sec6(nr).to_datetime(&ltime, sql_mode_for_dates(thd), &error);
longlong tmp= double_to_datetime(nr, &ltime, return store_TIME_with_warning(&ltime, &str, error, !rc);
(uint) sql_mode_for_dates(thd), &error);
return store_TIME_with_warning(&ltime, &str, error, tmp != -1);
} }
@ -5634,13 +5619,11 @@ int Field_temporal_with_date::store(longlong nr, bool unsigned_val)
{ {
int error; int error;
MYSQL_TIME ltime; MYSQL_TIME ltime;
longlong tmp;
THD *thd= get_thd(); THD *thd= get_thd();
ErrConvInteger str(nr, unsigned_val); ErrConvInteger str(nr, unsigned_val);
bool rc= Sec6(nr, unsigned_val).to_datetime(&ltime, sql_mode_for_dates(thd),
tmp= number_to_datetime(nr, 0, &ltime, sql_mode_for_dates(thd), &error); &error);
return store_TIME_with_warning(&ltime, &str, error, !rc);
return store_TIME_with_warning(&ltime, &str, error, tmp != -1);
} }
@ -5859,14 +5842,8 @@ int Field_time::store(double nr)
MYSQL_TIME ltime; MYSQL_TIME ltime;
ErrConvDouble str(nr); ErrConvDouble str(nr);
int was_cut; int was_cut;
bool neg= nr < 0; bool rc= Sec6(nr).to_time(&ltime, &was_cut);
if (neg) return store_TIME_with_warning(&ltime, &str, was_cut, !rc);
nr= -nr;
int have_smth_to_conv= !number_to_time(neg, (ulonglong) nr,
(ulong)((nr - floor(nr)) * TIME_SECOND_PART_FACTOR),
&ltime, &was_cut);
return store_TIME_with_warning(&ltime, &str, was_cut, have_smth_to_conv);
} }
@ -5875,13 +5852,8 @@ int Field_time::store(longlong nr, bool unsigned_val)
MYSQL_TIME ltime; MYSQL_TIME ltime;
ErrConvInteger str(nr, unsigned_val); ErrConvInteger str(nr, unsigned_val);
int was_cut; int was_cut;
if (nr < 0 && unsigned_val) bool rc= Sec6(nr, unsigned_val).to_time(&ltime, &was_cut);
nr= 99991231235959LL + 1; return store_TIME_with_warning(&ltime, &str, was_cut, !rc);
int have_smth_to_conv= !number_to_time(nr < 0,
(ulonglong) (nr < 0 ? -nr : nr),
0, &ltime, &was_cut);
return store_TIME_with_warning(&ltime, &str, was_cut, have_smth_to_conv);
} }
@ -6036,16 +6008,11 @@ void Field_time_hires::store_TIME(const MYSQL_TIME *ltime)
int Field_time::store_decimal(const my_decimal *d) int Field_time::store_decimal(const my_decimal *d)
{ {
ulonglong nr;
ulong sec_part;
ErrConvDecimal str(d); ErrConvDecimal str(d);
MYSQL_TIME ltime; MYSQL_TIME ltime;
int was_cut; int was_cut;
bool neg= my_decimal2seconds(d, &nr, &sec_part); bool rc= Sec6(d).to_time(&ltime, &was_cut);
return store_TIME_with_warning(&ltime, &str, was_cut, !rc);
int have_smth_to_conv= !number_to_time(neg, nr, sec_part, &ltime, &was_cut);
return store_TIME_with_warning(&ltime, &str, was_cut, have_smth_to_conv);
} }
@ -6763,24 +6730,12 @@ void Field_datetime_hires::store_TIME(MYSQL_TIME *ltime)
int Field_temporal_with_date::store_decimal(const my_decimal *d) int Field_temporal_with_date::store_decimal(const my_decimal *d)
{ {
ulonglong nr;
ulong sec_part;
int error; int error;
MYSQL_TIME ltime; MYSQL_TIME ltime;
longlong tmp;
THD *thd= get_thd(); THD *thd= get_thd();
ErrConvDecimal str(d); ErrConvDecimal str(d);
bool rc= Sec6(d).to_datetime(&ltime, sql_mode_for_dates(thd), &error);
if (my_decimal2seconds(d, &nr, &sec_part)) return store_TIME_with_warning(&ltime, &str, error, !rc);
{
tmp= -1;
error= 2;
}
else
tmp= number_to_datetime(nr, sec_part, &ltime, sql_mode_for_dates(thd),
&error);
return store_TIME_with_warning(&ltime, &str, error, tmp != -1);
} }
bool Field_datetime_with_dec::send_binary(Protocol *protocol) bool Field_datetime_with_dec::send_binary(Protocol *protocol)

View File

@ -1396,19 +1396,6 @@ bool Item::make_zero_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
return !(fuzzydate & TIME_FUZZY_DATES); 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() const MY_LOCALE *Item::locale_from_val_str()
{ {

View File

@ -1665,7 +1665,6 @@ public:
return get_date_result(&ltime, fuzzydate) ? 0 : pack_time(&ltime); return get_date_result(&ltime, fuzzydate) ? 0 : pack_time(&ltime);
} }
bool get_seconds(ulonglong *sec, ulong *sec_part);
virtual bool get_date_result(MYSQL_TIME *ltime, ulonglong fuzzydate) virtual bool get_date_result(MYSQL_TIME *ltime, ulonglong fuzzydate)
{ return get_date(ltime,fuzzydate); } { return get_date(ltime,fuzzydate); }
/* /*

View File

@ -840,8 +840,7 @@ bool Item_func_hybrid_field_type::get_date_from_int_op(MYSQL_TIME *ltime,
longlong value= int_op(); longlong value= int_op();
bool neg= !unsigned_flag && value < 0; bool neg= !unsigned_flag && value < 0;
if (null_value || int_to_datetime_with_warn(neg, neg ? -value : value, if (null_value || int_to_datetime_with_warn(neg, neg ? -value : value,
ltime, fuzzydate, ltime, fuzzydate, NULL))
field_name_or_null()))
return make_zero_mysql_time(ltime, fuzzydate); return make_zero_mysql_time(ltime, fuzzydate);
return (null_value= 0); return (null_value= 0);
} }
@ -875,8 +874,7 @@ bool Item_func_hybrid_field_type::get_date_from_real_op(MYSQL_TIME *ltime,
ulonglong fuzzydate) ulonglong fuzzydate)
{ {
double value= real_op(); double value= real_op();
if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate, if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate, NULL))
field_name_or_null()))
return make_zero_mysql_time(ltime, fuzzydate); return make_zero_mysql_time(ltime, fuzzydate);
return (null_value= 0); return (null_value= 0);
} }

View File

@ -450,10 +450,10 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
{ {
if (!my_isspace(&my_charset_latin1,*val)) if (!my_isspace(&my_charset_latin1,*val))
{ {
ErrConvString err(val_begin, length, &my_charset_bin);
make_truncated_value_warning(current_thd, make_truncated_value_warning(current_thd,
Sql_condition::WARN_LEVEL_WARN, Sql_condition::WARN_LEVEL_WARN,
val_begin, length, &err, cached_timestamp_type, NullS);
cached_timestamp_type, NullS);
break; break;
} }
} while (++val != val_end); } 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) if (int_type == INTERVAL_SECOND && args->decimals)
{ {
VDec val(args); VDec val(args);
ulonglong second;
ulong second_part;
if (val.is_null()) if (val.is_null())
return true; return true;
interval->neg= my_decimal2seconds(val.ptr(), &second, &second_part); Sec6 d(val.ptr());
if (second == LONGLONG_MAX) interval->neg= d.neg();
if (d.sec() >= LONGLONG_MAX)
{ {
THD *thd= current_thd;
ErrConvDecimal err(val.ptr()); ErrConvDecimal err(val.ptr());
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, current_thd->push_warning_truncated_wrong_value("seconds", err.ptr());
ER_TRUNCATED_WRONG_VALUE,
ER_THD(thd, ER_TRUNCATED_WRONG_VALUE), "DECIMAL",
err.ptr());
return true; return true;
} }
interval->second= d.sec();
interval->second= second; interval->second_part= d.usec();
interval->second_part= second_part;
return false; return false;
} }
else if ((int) int_type <= INTERVAL_MICROSECOND) 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) bool Item_func_sec_to_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
bool sign; VSec6 sec(args[0], "seconds", LONGLONG_MAX);
ulonglong sec; if ((null_value= sec.is_null()))
ulong sec_part; return true;
if (sec.sec_to_time(ltime, decimals) && !sec.truncated())
bzero((char *)ltime, sizeof(*ltime)); sec.make_truncated_warning(current_thd, "seconds");
ltime->time_type= MYSQL_TIMESTAMP_TIME; return false;
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;
} }
bool Item_func_date_format::fix_length_and_dec() 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, bool Item_func_from_unixtime::get_date(MYSQL_TIME *ltime,
ulonglong fuzzy_date __attribute__((unused))) ulonglong fuzzy_date __attribute__((unused)))
{ {
bool sign;
ulonglong sec;
ulong sec_part;
bzero((char *)ltime, sizeof(*ltime)); bzero((char *)ltime, sizeof(*ltime));
ltime->time_type= MYSQL_TIMESTAMP_TIME; 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); return (null_value= 1);
tz->gmt_sec_to_TIME(ltime, (my_time_t)sec); tz->gmt_sec_to_TIME(ltime, (my_time_t) sec.sec());
ltime->second_part= sec.usec();
ltime->second_part= sec_part;
return (null_value= 0); return (null_value= 0);
} }
@ -2694,12 +2644,11 @@ bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
bool overflow= 0; bool overflow= 0;
longlong hour= args[0]->val_int(); longlong hour= args[0]->val_int();
longlong minute= args[1]->val_int(); longlong minute= args[1]->val_int();
ulonglong second; VSec6 sec(args[2], "seconds", 59);
ulong microsecond;
bool neg= args[2]->get_seconds(&second, &microsecond);
if (args[0]->null_value || args[1]->null_value || args[2]->null_value || DBUG_ASSERT(sec.sec() <= 59);
minute < 0 || minute > 59 || neg || second > 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); return (null_value= 1);
bzero(ltime, sizeof(*ltime)); 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->hour= (uint) ((hour < 0 ? -hour : hour));
ltime->minute= (uint) minute; ltime->minute= (uint) minute;
ltime->second= (uint) second; ltime->second= (uint) sec.sec();
ltime->second_part= microsecond; ltime->second_part= sec.usec();
} }
else else
{ {
@ -2730,10 +2679,10 @@ bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
ltime->second= TIME_MAX_SECOND; ltime->second= TIME_MAX_SECOND;
char buf[28]; char buf[28];
char *ptr= longlong10_to_str(hour, buf, args[0]->unsigned_flag ? 10 : -10); 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); int len = (int)(ptr - buf) + sprintf(ptr, ":%02u:%02u",
make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, (uint) minute, (uint) sec.sec());
buf, len, MYSQL_TIMESTAMP_TIME, ErrConvString err(buf, len, &my_charset_bin);
NullS); current_thd->push_warning_truncated_wrong_value("time", err.ptr());
} }
return (null_value= 0); return (null_value= 0);

View File

@ -4372,6 +4372,69 @@ private:
return raised; 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: public:
/** Overloaded to guard query/query_length fields */ /** Overloaded to guard query/query_length fields */
virtual void set_statement(Statement *stmt); virtual void set_statement(Statement *stmt);

View File

@ -10063,19 +10063,17 @@ err_new_table_cleanup:
if (unlikely(alter_ctx.error_if_not_empty && if (unlikely(alter_ctx.error_if_not_empty &&
thd->get_stmt_da()->current_row_for_warning())) thd->get_stmt_da()->current_row_for_warning()))
{ {
const char *f_val= 0; const char *f_val= "0000-00-00";
enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE; const char *f_type= "date";
switch (alter_ctx.datetime_field->real_field_type()) switch (alter_ctx.datetime_field->real_field_type())
{ {
case MYSQL_TYPE_DATE: case MYSQL_TYPE_DATE:
case MYSQL_TYPE_NEWDATE: case MYSQL_TYPE_NEWDATE:
f_val= "0000-00-00";
t_type= MYSQL_TIMESTAMP_DATE;
break; break;
case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_DATETIME2: case MYSQL_TYPE_DATETIME2:
f_val= "0000-00-00 00:00:00"; f_val= "0000-00-00 00:00:00";
t_type= MYSQL_TIMESTAMP_DATETIME; f_type= "datetime";
break; break;
default: default:
/* Shouldn't get here. */ /* Shouldn't get here. */
@ -10083,9 +10081,10 @@ err_new_table_cleanup:
} }
bool save_abort_on_warning= thd->abort_on_warning; bool save_abort_on_warning= thd->abort_on_warning;
thd->abort_on_warning= true; thd->abort_on_warning= true;
make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN, thd->push_warning_truncated_value_for_field(Sql_condition::WARN_LEVEL_WARN,
f_val, strlength(f_val), t_type, f_type, f_val,
alter_ctx.datetime_field->field_name.str); alter_ctx.datetime_field->
field_name.str);
thd->abort_on_warning= save_abort_on_warning; thd->abort_on_warning= save_abort_on_warning;
} }

View File

@ -313,8 +313,7 @@ adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec)
if (check_time_range(ltime, dec, &warnings)) if (check_time_range(ltime, dec, &warnings))
return true; return true;
if (warnings) if (warnings)
make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, current_thd->push_warning_truncated_wrong_value("time", str.ptr());
&str, MYSQL_TIMESTAMP_TIME, NullS);
return false; return false;
} }
@ -399,11 +398,14 @@ str_to_datetime_with_warn(CHARSET_INFO *cs,
THD *thd= current_thd; THD *thd= current_thd;
bool ret_val= str_to_datetime(cs, str, length, l_time, flags, &status); bool ret_val= str_to_datetime(cs, str, length, l_time, flags, &status);
if (ret_val || status.warnings) if (ret_val || status.warnings)
{
const ErrConvString err(str, length, &my_charset_bin);
make_truncated_value_warning(thd, make_truncated_value_warning(thd,
ret_val ? Sql_condition::WARN_LEVEL_WARN : ret_val ? Sql_condition::WARN_LEVEL_WARN :
Sql_condition::time_warn_level(status.warnings), 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); MYSQL_TIMESTAMP_TIME : l_time->time_type, NullS);
}
DBUG_EXECUTE_IF("str_to_datetime_warn", DBUG_EXECUTE_IF("str_to_datetime_warn",
push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_YES, str);); 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, bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime,
ulonglong fuzzydate, const char *field_name) ulonglong fuzzydate, const char *field_name)
{ {
const ErrConvDouble str(value); const ErrConvDouble str(value);
bool neg= value < 0; return Sec6(value).convert_to_mysql_time(ltime, fuzzydate, &str, field_name);
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);
} }
@ -489,11 +425,7 @@ bool decimal_to_datetime_with_warn(const my_decimal *value, MYSQL_TIME *ltime,
ulonglong fuzzydate, const char *field_name) ulonglong fuzzydate, const char *field_name)
{ {
const ErrConvDecimal str(value); const ErrConvDecimal str(value);
ulonglong nr; return Sec6(value).convert_to_mysql_time(ltime, fuzzydate, &str, field_name);
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);
} }
@ -501,8 +433,8 @@ bool int_to_datetime_with_warn(bool neg, ulonglong value, MYSQL_TIME *ltime,
ulonglong fuzzydate, const char *field_name) ulonglong fuzzydate, const char *field_name)
{ {
const ErrConvInteger str(neg ? - (longlong) value : (longlong) value, !neg); const ErrConvInteger str(neg ? - (longlong) value : (longlong) value, !neg);
return number_to_time_with_warn(neg, value, 0, ltime, Sec6 sec(neg, value, 0);
fuzzydate, &str, field_name); 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, timestamp_type time_type,
const char *field_name) const char *field_name)
{ {
char warn_buff[MYSQL_ERRMSG_SIZE];
const char *type_str; const char *type_str;
CHARSET_INFO *cs= &my_charset_latin1;
switch (time_type) { switch (time_type) {
case MYSQL_TIMESTAMP_DATE: case MYSQL_TIMESTAMP_DATE:
@ -948,23 +878,9 @@ void make_truncated_value_warning(THD *thd,
type_str= "datetime"; type_str= "datetime";
break; break;
} }
if (field_name) return thd->push_warning_wrong_or_truncated_value(level,
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff), time_type <= MYSQL_TIMESTAMP_ERROR,
ER_THD(thd, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), type_str, sval->ptr(), field_name);
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);
} }
@ -1442,8 +1358,7 @@ time_to_datetime_with_warn(THD *thd,
check_date(to, fuzzydate, &warn))) check_date(to, fuzzydate, &warn)))
{ {
ErrConvTime str(from); ErrConvTime str(from);
make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN, thd->push_warning_truncated_wrong_value("datetime", str.ptr());
&str, MYSQL_TIMESTAMP_DATETIME, 0);
return true; return true;
} }
return false; return false;

View File

@ -120,14 +120,6 @@ void make_truncated_value_warning(THD *thd,
timestamp_type time_type, timestamp_type time_type,
const char *field_name); 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, extern DATE_TIME_FORMAT *date_time_format_make(timestamp_type format_type,
const char *format_str, const char *format_str,
uint format_length); uint format_length);

View File

@ -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) void Time::make_from_item(Item *item, const Options opt)
{ {
if (item->get_date(this, opt.get_date_flags())) if (item->get_date(this, opt.get_date_flags()))

View File

@ -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 A heler class to perform additive operations between
two MYSQL_TIME structures and return the result as a two MYSQL_TIME structures and return the result as a