Commit d2ba9edd authored by Alexander Barkov's avatar Alexander Barkov

MDEV-17740 Extend EXTRACT(x AS DAY*) to understand long time intervals

parent f92d223f
......@@ -79,6 +79,16 @@ extern uchar days_in_month[];
/* Useful constants */
#define SECONDS_IN_24H 86400L
/* Limits for the INTERVAL data type */
/* Number of hours between '0001-01-01 00h' and '9999-12-31 23h' */
#define TIME_MAX_INTERVAL_HOUR 87649415
#define TIME_MAX_INTERVAL_HOUR_CHAR_LENGTH 8
/* Number of full days between '0001-01-01' and '9999-12-31'*/
#define TIME_MAX_INTERVAL_DAY 3652058 /*87649415/24*/
#define TIME_MAX_INTERVAL_DAY_CHAR_LENGTH 7
/* Limits for the TIME data type */
#define TIME_MAX_HOUR 838
#define TIME_MAX_MINUTE 59
......@@ -112,7 +122,21 @@ my_bool str_to_DDhhmmssff(const char *str, size_t length, MYSQL_TIME *l_time,
ulong max_hour, MYSQL_TIME_STATUS *status);
my_bool str_to_datetime_or_date_or_time(const char *str, size_t length,
MYSQL_TIME *to, ulonglong flag,
MYSQL_TIME_STATUS *status);
MYSQL_TIME_STATUS *status,
ulong time_max_hour,
ulong time_err_hour);
my_bool
str_to_datetime_or_date_or_interval_hhmmssff(const char *str, size_t length,
MYSQL_TIME *to, ulonglong flag,
MYSQL_TIME_STATUS *status,
ulong time_max_hour,
ulong time_err_hour);
my_bool
str_to_datetime_or_date_or_interval_day(const char *str, size_t length,
MYSQL_TIME *to, ulonglong flag,
MYSQL_TIME_STATUS *status,
ulong time_max_hour,
ulong time_err_hour);
my_bool str_to_datetime_or_date(const char *str, size_t length, MYSQL_TIME *to,
ulonglong flags, MYSQL_TIME_STATUS *status);
......@@ -120,7 +144,7 @@ longlong number_to_datetime_or_date(longlong nr, ulong sec_part,
MYSQL_TIME *time_res,
ulonglong flags, int *was_cut);
int number_to_time_only(my_bool neg, ulonglong nr, ulong sec_part,
MYSQL_TIME *ltime, int *was_cut);
ulong max_hour, MYSQL_TIME *to, int *was_cut);
ulonglong TIME_to_ulonglong_datetime(const MYSQL_TIME *);
ulonglong TIME_to_ulonglong_date(const MYSQL_TIME *);
......
......@@ -3215,7 +3215,8 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value, size_t
{
MYSQL_TIME *tm= (MYSQL_TIME *)buffer;
MYSQL_TIME_STATUS status;
str_to_datetime_or_date_or_time(value, length, tm, 0, &status);
str_to_datetime_or_date_or_time(value, length, tm, 0, &status,
TIME_MAX_HOUR, UINT_MAX32);
err= status.warnings;
*param->error= MY_TEST(err);
break;
......
This diff is collapsed.
--echo #
--echo # MDEV-17385 MICROSECOND() returns confusing results with an out-of-range TIME-alike argument
--echo #
CREATE TABLE t1 (v VARCHAR(64), ll BIGINT, t TIME, dt DATETIME, d DATE);
CREATE TABLE t2 AS SELECT
EXTRACT(DAY FROM t),
EXTRACT(DAY_HOUR FROM t),
EXTRACT(DAY_MINUTE FROM t),
EXTRACT(DAY_SECOND FROM t),
EXTRACT(DAY_MICROSECOND FROM t),
EXTRACT(DAY FROM d),
EXTRACT(DAY_HOUR FROM d),
EXTRACT(DAY_MINUTE FROM d),
EXTRACT(DAY_SECOND FROM d),
EXTRACT(DAY_MICROSECOND FROM d),
EXTRACT(DAY FROM v),
EXTRACT(DAY_HOUR FROM v),
EXTRACT(DAY_MINUTE FROM v),
EXTRACT(DAY_SECOND FROM v),
EXTRACT(DAY_MICROSECOND FROM v),
EXTRACT(DAY FROM ll),
EXTRACT(DAY_HOUR FROM ll),
EXTRACT(DAY_MINUTE FROM ll),
EXTRACT(DAY_SECOND FROM ll),
EXTRACT(DAY_MICROSECOND FROM ll)
FROM t1;
SHOW CREATE TABLE t2;
DROP TABLE t2;
DROP TABLE t1;
CREATE TABLE t1 (a VARCHAR(64), b DECIMAL(32,9));
INSERT INTO t1 VALUES
('9999-12-31 23:59:59.123456', 99991231235959.123456),
('2001-01-01 10:20:30.123456', 20010101102030.123456),
('4294967296:59:59.123456', 42949672965959.123456),
('4294967295:59:59.123456', 42949672955959.123456),
('87649416:59:59.123456', 876494165959.123456),
('87649415:59:59.123456', 876494155959.123456),
('87649414:59:59.123456', 876494145959.123456),
('9999:59:59.123456', 99995959.123456),
('9999:01:01.123456', 99990101.123456),
('9999:01:01', 99990101),
('0.999999', 0.999999),
('0.99999', 0.99999),
('0.9999', 0.9999),
('0.999', 0.999),
('0.99', 0.99),
('0.9', 0.9),
('000000',0);
--echo # Summary:
--echo # Check that FUNC(varchar) and FUNC(decimal) give equal results
--echo # Expect empty sets
--disable_warnings
SELECT a, b, EXTRACT(DAY_HOUR FROM a), EXTRACT(DAY_HOUR FROM b) FROM t1 WHERE NOT (EXTRACT(DAY_HOUR FROM a)<=>EXTRACT(DAY_HOUR FROM b));
SELECT a, b, EXTRACT(DAY FROM a), EXTRACT(DAY FROM b) FROM t1 WHERE NOT (EXTRACT(DAY FROM a)<=>EXTRACT(DAY FROM b));
SELECT a, b, EXTRACT(HOUR FROM a), EXTRACT(HOUR FROM b) FROM t1 WHERE NOT (EXTRACT(HOUR FROM a)<=>EXTRACT(HOUR FROM b));
SELECT a, b, EXTRACT(MINUTE FROM a), EXTRACT(MINUTE FROM b) FROM t1 WHERE NOT (EXTRACT(MINUTE FROM a)<=>EXTRACT(MINUTE FROM b));
SELECT a, b, EXTRACT(SECOND FROM a), EXTRACT(SECOND FROM b) FROM t1 WHERE NOT (EXTRACT(SECOND FROM a)<=>EXTRACT(SECOND FROM b));
SELECT a, b, EXTRACT(MICROSECOND FROM a), EXTRACT(MICROSECOND FROM b) FROM t1 WHERE NOT (EXTRACT(MICROSECOND FROM a)<=>EXTRACT(MICROSECOND FROM b));
--enable_warnings
--echo # Detailed results
SELECT
a,
EXTRACT(DAY FROM a) * 24 + EXTRACT(HOUR FROM a) AS dh,
EXTRACT(DAY_HOUR FROM a),
EXTRACT(DAY FROM a),
EXTRACT(HOUR FROM a),
EXTRACT(MINUTE FROM a),
EXTRACT(SECOND FROM a),
EXTRACT(MICROSECOND FROM a)
FROM t1;
SELECT
b,
EXTRACT(DAY FROM b) * 24 + EXTRACT(HOUR FROM b) AS dh,
EXTRACT(DAY_HOUR FROM b),
EXTRACT(DAY FROM b),
EXTRACT(HOUR FROM b),
EXTRACT(MINUTE FROM b),
EXTRACT(SECOND FROM b),
EXTRACT(MICROSECOND FROM b)
FROM t1;
DROP TABLE t1;
--echo # Special case: DAY + TIME
CREATE TABLE t1 (a VARCHAR(64));
INSERT INTO t1 VALUES ('9999-01-01');
SELECT a,
EXTRACT(DAY_HOUR FROM a),
EXTRACT(DAY_MINUTE FROM a),
EXTRACT(DAY_SECOND FROM a),
EXTRACT(DAY_MICROSECOND FROM a),
EXTRACT(DAY FROM a),
EXTRACT(HOUR FROM a),
EXTRACT(MINUTE FROM a),
EXTRACT(SECOND FROM a),
EXTRACT(MICROSECOND FROM a)
FROM t1;
DROP TABLE t1;
--echo # Bad values
CREATE TABLE t1 (a VARCHAR(64));
INSERT INTO t1 VALUES ('');
SELECT a,
EXTRACT(DAY_HOUR FROM a),
EXTRACT(DAY_MINUTE FROM a),
EXTRACT(DAY_SECOND FROM a),
EXTRACT(DAY_MICROSECOND FROM a),
EXTRACT(DAY FROM a),
EXTRACT(HOUR FROM a),
EXTRACT(MINUTE FROM a),
EXTRACT(SECOND FROM a),
EXTRACT(MICROSECOND FROM a)
FROM t1;
DROP TABLE t1;
--echo # Backward compatibility
--echo # This still parses as DATETIME
SELECT EXTRACT(YEAR FROM '2001/02/03 10:20:30');
SELECT EXTRACT(MONTH FROM '2001/02/03 10:20:30');
SELECT EXTRACT(DAY FROM '2001/02/03 10:20:30');
SELECT EXTRACT(YEAR FROM '01/02/03 10:20:30');
SELECT EXTRACT(MONTH FROM '01/02/03 10:20:30');
SELECT EXTRACT(DAY FROM '01/02/03 10:20:30');
SELECT EXTRACT(YEAR FROM '01:02:03 10:20:30');
SELECT EXTRACT(MONTH FROM '01:02:03 10:20:30');
SELECT EXTRACT(DAY FROM '01:02:03 10:20:30');
--echo # This still parses as DATETIME and returns NULL
SELECT EXTRACT(YEAR FROM "2011-02-32 8:46:06.23434");
SELECT EXTRACT(MONTH FROM "2011-02-32 8:46:06.23434");
SELECT EXTRACT(DAY FROM "2011-02-32 8:46:06.23434");
SELECT EXTRACT(HOUR FROM "2011-02-32 8:46:06.23434");
--echo # This still parses as DATE
SELECT EXTRACT(YEAR FROM '2001/02/03');
SELECT EXTRACT(MONTH FROM '2001/02/03');
SELECT EXTRACT(DAY FROM '2001/02/03');
SELECT EXTRACT(YEAR FROM '01/02/03');
SELECT EXTRACT(MONTH FROM '01/02/03');
SELECT EXTRACT(DAY FROM '01/02/03');
SELECT EXTRACT(YEAR FROM '01-02-03');
SELECT EXTRACT(MONTH FROM '01-02-03');
SELECT EXTRACT(DAY FROM '01-02-03');
SELECT EXTRACT(YEAR FROM '1-2-3');
SELECT EXTRACT(MONTH FROM '1-2-3');
SELECT EXTRACT(DAY FROM '1-2-3');
SELECT EXTRACT(HOUR FROM '1-2-3');
SELECT EXTRACT(DAY FROM '2024-01-03 garbage /////');
SELECT EXTRACT(DAY FROM '24-01-03 garbage /////');
SELECT EXTRACT(DAY FROM '01-02-03');
SELECT EXTRACT(DAY FROM '24:02:03T');
SELECT EXTRACT(DAY FROM '24-02-03');
SELECT EXTRACT(DAY FROM '24/02/03');
SELECT EXTRACT(DAY FROM '11111');
SELECT TIME('2001-01-01T'), TIME('2001-01-01T ');
SELECT TIME('2001/01/01T'), TIME('2001/01/01T ');
SELECT TIME('2001:01:01T'), TIME('2001:01:01T ');
SELECT EXTRACT(DAY FROM '2001-01-01T'), EXTRACT(DAY FROM '2001-01-01T ');
SELECT EXTRACT(DAY FROM '2001/01/01T'), EXTRACT(DAY FROM '2001/01/01T ');
SELECT EXTRACT(DAY FROM '2001:01:01T'), EXTRACT(DAY FROM '2001:01:01T ');
SELECT TIME('2001:01:01T'), TIME('2001:01:01T ');
SELECT EXTRACT(HOUR FROM '2001-01-01T'), EXTRACT(HOUR FROM '2001-01-01T ');
SELECT EXTRACT(HOUR FROM '2001/01/01T'), EXTRACT(HOUR FROM '2001/01/01T ');
SELECT EXTRACT(HOUR FROM '2001:01:01T'), EXTRACT(HOUR FROM '2001:01:01T ');
--echo # This still parses as DATE and returns NULL (without trying TIME)
SELECT EXTRACT(DAY FROM '100000:02:03T');
SELECT EXTRACT(DAY FROM '100000/02/03');
SELECT EXTRACT(DAY FROM '100000-02-03');
SELECT EXTRACT(DAY FROM '1111');
SELECT EXTRACT(DAY FROM '111');
SELECT EXTRACT(DAY FROM '11');
SELECT EXTRACT(DAY FROM '1');
--echo # This still parses as TIME
SELECT EXTRACT(HOUR FROM '11111');
SELECT EXTRACT(HOUR FROM '1111');
SELECT EXTRACT(HOUR FROM '111');
SELECT EXTRACT(HOUR FROM '11');
SELECT EXTRACT(HOUR FROM '1');
SELECT TIME('01:02:03:');
SELECT TIME('01:02:03-');
SELECT TIME('01:02:03;');
SELECT TIME('01:02:03/');
SELECT EXTRACT(HOUR FROM '01:02:03:');
SELECT EXTRACT(HOUR FROM '01:02:03-');
SELECT EXTRACT(HOUR FROM '01:02:03;');
SELECT EXTRACT(HOUR FROM '01:02:03/');
--echo # Backward compatibility preserved for YEAR and MONTH only
--echo # (behavior has changed for DAY, see below)
SELECT EXTRACT(YEAR FROM '01:02:03');
SELECT EXTRACT(MONTH FROM '01:02:03');
SELECT EXTRACT(YEAR FROM '24:01:03 garbage /////');
SELECT EXTRACT(MONTH FROM '24:01:03 garbage /////');
--echo # This still parses as TIME 00:20:01
SELECT TIME('2001/01/01');
SELECT TIME('2001-01-01');
--echo # This still parses as TIME and overflows to '838:59:59'
SELECT TIME('2001:01:01');
--echo # This used to parse as DATE, now parses as TIME interval
CREATE TABLE t1 (a VARCHAR(64));
INSERT INTO t1 VALUES
('2024:01:03 garbage /////'),
('24:01:03 garbage /////'),
('01:01:03 garbage /////'),
('2024:02:03'),
('100000:02:03'),
('24:02:03'),
('01:02:03'),
('01:02:03:'),
('01:02:03-'),
('01:02:03;'),
('01:02:03/'),
('20 10:20:30');
SELECT EXTRACT(DAY FROM a), EXTRACT(DAY_SECOND FROM a), a FROM t1;
DROP TABLE t1;
......@@ -367,9 +367,7 @@ extract(DAY_MINUTE FROM "02 10:11:12")
21011
select extract(DAY_SECOND FROM "225 10:11:12");
extract(DAY_SECOND FROM "225 10:11:12")
34225959
Warnings:
Warning 1292 Truncated incorrect time value: '225 10:11:12'
225101112
select extract(HOUR FROM "1999-01-02 10:11:12");
extract(HOUR FROM "1999-01-02 10:11:12")
10
......@@ -1033,9 +1031,7 @@ Note 1105 Cast to unsigned converted negative integer to it's positive complemen
Warning 1292 Truncated incorrect time value: '18446744073709551615:00:00'
SELECT EXTRACT(HOUR FROM '10000:02:03');
EXTRACT(HOUR FROM '10000:02:03')
22
Warnings:
Warning 1292 Truncated incorrect time value: '10000:02:03'
16
CREATE TABLE t1(f1 TIME);
INSERT IGNORE INTO t1 VALUES('916:00:00 a');
Warnings:
......
......@@ -17,10 +17,7 @@ Warnings:
Warning 1292 Incorrect datetime value: '0'
select extract(hour from '100000:02:03'), extract(hour from '100000:02:03 ');
extract(hour from '100000:02:03') extract(hour from '100000:02:03 ')
NULL NULL
Warnings:
Warning 1292 Incorrect time value: '100000:02:03'
Warning 1292 Incorrect time value: '100000:02:03 '
16 16
#
# backward compatibility craziness
#
......
This diff is collapsed.
......@@ -2092,16 +2092,18 @@ void Item_extract::print(String *str, enum_query_type query_type)
bool Item_extract::fix_length_and_dec()
{
maybe_null=1; // If wrong date
uint32 daylen= args[0]->cmp_type() == TIME_RESULT ? 2 :
TIME_MAX_INTERVAL_DAY_CHAR_LENGTH;
switch (int_type) {
case INTERVAL_YEAR: set_date_length(4); break; // YYYY
case INTERVAL_YEAR_MONTH: set_date_length(6); break; // YYYYMM
case INTERVAL_QUARTER: set_date_length(2); break; // 1..4
case INTERVAL_MONTH: set_date_length(2); break; // MM
case INTERVAL_WEEK: set_date_length(2); break; // 0..52
case INTERVAL_DAY: set_day_length(2); break; // DD
case INTERVAL_DAY_HOUR: set_time_length(4); break; // DDhh
case INTERVAL_DAY_MINUTE: set_time_length(6); break; // DDhhmm
case INTERVAL_DAY_SECOND: set_time_length(8); break; // DDhhmmss
case INTERVAL_DAY: set_day_length(daylen); break; // DD
case INTERVAL_DAY_HOUR: set_day_length(daylen+2); break; // DDhh
case INTERVAL_DAY_MINUTE: set_day_length(daylen+4); break; // DDhhmm
case INTERVAL_DAY_SECOND: set_day_length(daylen+6); break; // DDhhmmss
case INTERVAL_HOUR: set_time_length(2); break; // hh
case INTERVAL_HOUR_MINUTE: set_time_length(4); break; // hhmm
case INTERVAL_HOUR_SECOND: set_time_length(6); break; // hhmmss
......@@ -2109,7 +2111,7 @@ bool Item_extract::fix_length_and_dec()
case INTERVAL_MINUTE_SECOND: set_time_length(4); break; // mmss
case INTERVAL_SECOND: set_time_length(2); break; // ss
case INTERVAL_MICROSECOND: set_time_length(6); break; // ffffff
case INTERVAL_DAY_MICROSECOND: set_time_length(14); break; // DDhhmmssffffff
case INTERVAL_DAY_MICROSECOND: set_time_length(daylen+12); break; // DDhhmmssffffff
case INTERVAL_HOUR_MICROSECOND: set_time_length(12); break; // hhmmssffffff
case INTERVAL_MINUTE_MICROSECOND: set_time_length(10); break; // mmssffffff
case INTERVAL_SECOND_MICROSECOND: set_time_length(8); break; // ssffffff
......
......@@ -993,12 +993,12 @@ class Item_extract :public Item_int_func,
EXTRACT(DAY FROM '-24:00:00') -> -1
*/
set_handler(handler_by_length(max_length= length + 1/*sign*/, 11));
m_date_mode= date_mode_t(0);
m_date_mode= TIME_INTERVAL_DAY;
}
void set_time_length(uint32 length)
{
set_handler(handler_by_length(max_length= length + 1/*sign*/, 11));
m_date_mode= TIME_TIME_ONLY;
m_date_mode= TIME_INTERVAL_hhmmssff;
}
public:
const interval_type int_type; // keep it public
......
......@@ -37,11 +37,13 @@ class date_mode_t
FUZZY_DATES is used for the result will only be used for comparison
purposes. Conversion is as relaxed as possible.
*/
FUZZY_DATES= 1U,
TIME_ONLY= 4U,
NO_ZERO_IN_DATE= (1UL << 23), // MODE_NO_ZERO_IN_DATE
NO_ZERO_DATE= (1UL << 24), // MODE_NO_ZERO_DATE
INVALID_DATES= (1UL << 25) // MODE_INVALID_DATES
FUZZY_DATES= 1U,
TIME_ONLY= 4U,
INTERVAL_hhmmssff= 8U,
INTERVAL_DAY= 16U,
NO_ZERO_IN_DATE= (1UL << 23), // MODE_NO_ZERO_IN_DATE
NO_ZERO_DATE= (1UL << 24), // MODE_NO_ZERO_DATE
INVALID_DATES= (1UL << 25) // MODE_INVALID_DATES
};
private:
......@@ -98,6 +100,8 @@ class date_mode_t
const date_mode_t
TIME_FUZZY_DATES (date_mode_t::value_t::FUZZY_DATES),
TIME_TIME_ONLY (date_mode_t::value_t::TIME_ONLY),
TIME_INTERVAL_hhmmssff (date_mode_t::value_t::INTERVAL_hhmmssff),
TIME_INTERVAL_DAY (date_mode_t::value_t::INTERVAL_DAY),
TIME_NO_ZERO_IN_DATE (date_mode_t::value_t::NO_ZERO_IN_DATE),
TIME_NO_ZERO_DATE (date_mode_t::value_t::NO_ZERO_DATE),
TIME_INVALID_DATES (date_mode_t::value_t::INVALID_DATES);
......
......@@ -371,18 +371,14 @@ class TemporalAsciiBuffer: public LEX_CSTRING
};
/* Character set-aware version of str_to_datetime_or_date_or_time() */
bool Temporal::str_to_datetime_or_date_or_time(MYSQL_TIME_STATUS *status,
/* Character set-aware version of ascii_to_datetime_or_date_or_time() */
bool Temporal::str_to_datetime_or_date_or_time(MYSQL_TIME_STATUS *st,
const char *str, size_t length,
CHARSET_INFO *cs,
date_mode_t fuzzydate)
{
TemporalAsciiBuffer tmp(str, length, cs);
bool rc= ::str_to_datetime_or_date_or_time(tmp.str, tmp.length, this,
ulonglong(fuzzydate & TIME_MODE_FOR_XXX_TO_DATE),
status);
DBUG_ASSERT(status->warnings || !rc);
return rc;
return ascii_to_datetime_or_date_or_time(st, tmp.str, tmp.length, fuzzydate);
}
......@@ -393,11 +389,17 @@ bool Temporal::str_to_datetime_or_date(MYSQL_TIME_STATUS *status,
date_mode_t flags)
{
TemporalAsciiBuffer tmp(str, length, cs);
bool rc= ::str_to_datetime_or_date(tmp.str, tmp.length, this,
ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE),
status);
DBUG_ASSERT(status->warnings || !rc);
return rc;
return ascii_to_datetime_or_date(status, tmp.str, tmp.length, flags);
}
/* Character set-aware version of ascii_to_temporal() */
bool Temporal::str_to_temporal(MYSQL_TIME_STATUS *status,
const char *str, size_t length, CHARSET_INFO *cs,
date_mode_t flags)
{
TemporalAsciiBuffer tmp(str, length, cs);
return ascii_to_temporal(status, tmp.str, tmp.length, flags);
}
......
......@@ -181,9 +181,7 @@ void Temporal::make_from_str(THD *thd, Warn *warn,
push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_YES, ErrConvString(str, length,cs).ptr()););
if (fuzzydate & TIME_TIME_ONLY ?
str_to_datetime_or_date_or_time(warn, str, length, cs, fuzzydate) :
str_to_datetime_or_date(warn, str, length, cs, fuzzydate))
if (str_to_temporal(warn, str, length, cs, fuzzydate))
make_fuzzy_date(&warn->warnings, fuzzydate);
if (warn->warnings)
warn->set_str(str, length, &my_charset_bin);
......@@ -232,8 +230,11 @@ void Sec6::make_truncated_warning(THD *thd, const char *type_str) const
bool Sec6::convert_to_mysql_time(THD *thd, int *warn, MYSQL_TIME *ltime,
date_mode_t fuzzydate) const
{
bool is_time= bool(fuzzydate & TIME_TIME_ONLY);
bool rc= is_time ? to_time(ltime, warn) : to_datetime(ltime, fuzzydate, warn);
bool rc= fuzzydate & (TIME_INTERVAL_hhmmssff | TIME_INTERVAL_DAY) ?
to_datetime_or_to_interval_hhmmssff(ltime, warn) :
fuzzydate & TIME_TIME_ONLY ?
to_datetime_or_time(ltime, warn, fuzzydate) :
to_datetime_or_date(ltime, warn, fuzzydate);
DBUG_ASSERT(*warn || !rc);
if (truncated())
*warn|= MYSQL_TIME_WARN_TRUNCATED;
......
......@@ -264,21 +264,59 @@ class Sec6
MYSQL_TIME *ltime,
date_mode_t fuzzydate) const;
// Convert a number in format hhhmmss.ff to TIME'hhh:mm:ss.ff'
bool to_time(MYSQL_TIME *to, int *warn) const
protected:
bool to_interval_hhmmssff_only(MYSQL_TIME *to, int *warn) const
{
return number_to_time_only(m_neg, m_sec, m_usec,
TIME_MAX_INTERVAL_HOUR, to, warn);
}
bool to_datetime_or_to_interval_hhmmssff(MYSQL_TIME *to, int *warn) const
{
bool rc= (m_sec > 9999999 && m_sec <= 99991231235959ULL && !neg()) ?
number_to_datetime_or_date(m_sec, m_usec, to,
C_TIME_INVALID_DATES, warn) < 0 :
number_to_time_only(m_neg, m_sec, m_usec, to, warn);
/*
Convert a number to a time interval.
The following formats are understood:
- 0 <= x <= 999999995959 - parse as hhhhmmss
- 999999995959 < x <= 99991231235959 - parse as YYYYMMDDhhmmss
(YYMMDDhhmmss) (YYYYMMDDhhmmss)
Note, these formats are NOT understood:
- YYMMDD - overlaps with INTERVAL range
- YYYYMMDD - overlaps with INTERVAL range
- YYMMDDhhmmss - overlaps with INTERVAL range, partially
(see TIME_MAX_INTERVAL_HOUR)
If we ever need wider intervals, this code switching between
full datetime and interval-only should be rewised.
*/
DBUG_ASSERT(TIME_MAX_INTERVAL_HOUR <= 999999995959);
/* (YYMMDDhhmmss) */
if (m_sec > 999999995959ULL &&
m_sec <= 99991231235959ULL && m_neg == 0)
return to_datetime_or_date(to, warn, TIME_INVALID_DATES);
if (m_sec / 10000 > TIME_MAX_INTERVAL_HOUR)
{
*warn= MYSQL_TIME_WARN_OUT_OF_RANGE;
return true;
}
return to_interval_hhmmssff_only(to, warn);
}
public:
// [-][DD]hhhmmss.ff, YYMMDDhhmmss.ff, YYYYMMDDhhmmss.ff
bool to_datetime_or_time(MYSQL_TIME *to, int *warn, date_mode_t mode) const
{
bool rc= m_sec > 9999999 && m_sec <= 99991231235959ULL && !m_neg ?
::number_to_datetime_or_date(m_sec, m_usec, to,
ulonglong(mode & TIME_MODE_FOR_XXX_TO_DATE), warn) < 0 :
::number_to_time_only(m_neg, m_sec, m_usec, TIME_MAX_HOUR, to, warn);
DBUG_ASSERT(*warn || !rc);
return rc;
}
/*
Convert a number in format YYYYMMDDhhmmss.ff to
Convert a number in formats YYYYMMDDhhmmss.ff or YYMMDDhhmmss.ff to
TIMESTAMP'YYYY-MM-DD hh:mm:ss.ff'
*/
bool to_datetime(MYSQL_TIME *to, date_mode_t flags, int *warn) const
bool to_datetime_or_date(MYSQL_TIME *to, int *warn, date_mode_t flags) const
{
if (m_neg)
{
......@@ -458,6 +496,8 @@ class Temporal: protected MYSQL_TIME
timestamp_type tstype, const char *name)
{
const char *typestr= tstype >= 0 ? type_name_by_timestamp_type(tstype) :
mode & (TIME_INTERVAL_hhmmssff | TIME_INTERVAL_DAY) ?
"interval" :
mode & TIME_TIME_ONLY ? "time" : "datetime";
Temporal::push_conversion_warnings(thd, totally_useless_value, warnings, typestr,
name, ptr());
......@@ -579,6 +619,60 @@ class Temporal: protected MYSQL_TIME
if (warn->warnings)
warn->set_decimal(nr);
}
bool ascii_to_temporal(MYSQL_TIME_STATUS *st,
const char *str, size_t length,
date_mode_t mode)
{
if (mode & (TIME_INTERVAL_hhmmssff | TIME_INTERVAL_DAY))
return ascii_to_datetime_or_date_or_interval_DDhhmmssff(st, str, length,
mode);
if (mode & TIME_TIME_ONLY)
return ascii_to_datetime_or_date_or_time(st, str, length, mode);
return ascii_to_datetime_or_date(st, str, length, mode);
}
bool ascii_to_datetime_or_date_or_interval_DDhhmmssff(MYSQL_TIME_STATUS *st,
const char *str,
size_t length,
date_mode_t mode)
{
longlong cflags= ulonglong(mode & TIME_MODE_FOR_XXX_TO_DATE);
bool rc= mode & TIME_INTERVAL_DAY ?
::str_to_datetime_or_date_or_interval_day(str, length, this, cflags, st,
TIME_MAX_INTERVAL_HOUR,
TIME_MAX_INTERVAL_HOUR) :
::str_to_datetime_or_date_or_interval_hhmmssff(str, length, this,
cflags, st,
TIME_MAX_INTERVAL_HOUR,
TIME_MAX_INTERVAL_HOUR);
DBUG_ASSERT(!rc || st->warnings);
return rc;
}
bool ascii_to_datetime_or_date_or_time(MYSQL_TIME_STATUS *status,
const char *str, size_t length,
date_mode_t fuzzydate)
{
ulonglong cflags= ulonglong(fuzzydate & TIME_MODE_FOR_XXX_TO_DATE);
bool rc= ::str_to_datetime_or_date_or_time(str, length, this,
cflags, status,
TIME_MAX_HOUR, UINT_MAX32);
DBUG_ASSERT(!rc || status->warnings);
return rc;
}
bool ascii_to_datetime_or_date(MYSQL_TIME_STATUS *status,
const char *str, size_t length,
date_mode_t fuzzydate)
{
DBUG_ASSERT(bool(fuzzydate & TIME_TIME_ONLY) == false);
bool rc= ::str_to_datetime_or_date(str, length, this,
ulonglong(fuzzydate & TIME_MODE_FOR_XXX_TO_DATE),
status);
DBUG_ASSERT(!rc || status->warnings);
return rc;
}
// Character set aware versions for string conversion routines
bool str_to_temporal(MYSQL_TIME_STATUS *st,
const char *str, size_t length,
CHARSET_INFO *cs, date_mode_t fuzzydate);
bool str_to_datetime_or_date_or_time(MYSQL_TIME_STATUS *st,
const char *str, size_t length,
CHARSET_INFO *cs, date_mode_t fuzzydate);
......@@ -858,7 +952,7 @@ class Interval_DDhhmmssff: public Temporal
*/
static uint max_useful_hour()
{
return 87649415;
return TIME_MAX_INTERVAL_HOUR;
}
public:
Interval_DDhhmmssff(THD *thd, Status *st, bool push_warnings,
......@@ -1120,7 +1214,7 @@ class Time: public Temporal
}
Time(THD *thd, int *warn, const Sec6 &nr, const Options opt)
{
if (nr.to_time(this, warn))
if (nr.to_datetime_or_time(this, warn, TIME_INVALID_DATES))
time_type= MYSQL_TIMESTAMP_NONE;
xxx_to_time_result_to_valid_value(thd, warn, opt);
}
......@@ -1305,7 +1399,7 @@ class Temporal_with_date: public Temporal
Temporal_with_date(int *warn, const Sec6 &nr, date_mode_t flags)
{
DBUG_ASSERT(bool(flags & TIME_TIME_ONLY) == false);
if (nr.to_datetime(this, flags, warn))
if (nr.to_datetime_or_date(this, warn, flags))
time_type= MYSQL_TIMESTAMP_NONE;
}
Temporal_with_date(MYSQL_TIME_STATUS *status,
......
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