summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.com>2018-08-11 06:47:48 +0400
committerAlexander Barkov <bar@mariadb.com>2018-08-11 06:47:48 +0400
commit2085f14a8dddf175765d97dcb5306bc2a0578b00 (patch)
tree24979de5af9a1fef91752d548be6a296e68338c1
parent2966c1e422866f48b68e109ac9e27d52be77fb60 (diff)
downloadmariadb-git-2085f14a8dddf175765d97dcb5306bc2a0578b00.tar.gz
MDEV-16938 Move Item::get_time_with_conversion() to Time
The affected code is well covered by tests for MDEV-8766. Adding only the missing part: the old mode OLD_MODE_ZERO_DATE_TIME_CAST in combination with 0000-MM-00 and YYYY-00-00. The old mode in combination with 0000-00-DD was already covered, so was the new mode with all types of DATETIME values.
-rw-r--r--mysql-test/main/type_time.result12
-rw-r--r--mysql-test/main/type_time.test5
-rw-r--r--sql/field.cc32
-rw-r--r--sql/item.cc44
-rw-r--r--sql/item.h34
-rw-r--r--sql/sql_time.cc20
-rw-r--r--sql/sql_time.h44
-rw-r--r--sql/sql_type.h43
8 files changed, 77 insertions, 157 deletions
diff --git a/mysql-test/main/type_time.result b/mysql-test/main/type_time.result
index 51b2aecaa6b..8f50d49d4b9 100644
--- a/mysql-test/main/type_time.result
+++ b/mysql-test/main/type_time.result
@@ -841,6 +841,18 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 8 100.00 Using where
Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'0000-00-01 10:20:30' and octet_length(`test`.`t1`.`a`) = 30 + rand()
+EXPLAIN EXTENDED
+SELECT * FROM t1 WHERE a=TIMESTAMP'0000-01-00 10:20:30' AND LENGTH(a)=8;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 8 100.00 Using where
+Warnings:
+Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'0000-01-00 10:20:30' and octet_length(`test`.`t1`.`a`) = 8
+EXPLAIN EXTENDED
+SELECT * FROM t1 WHERE a=TIMESTAMP'0001-00-00 10:20:30' AND LENGTH(a)=8;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 8 100.00 Using where
+Warnings:
+Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = TIMESTAMP'0001-00-00 10:20:30' and octet_length(`test`.`t1`.`a`) = 8
# Old mode, TIMESTAMP-alike string literal, zero YYYYMMDD, Ok to propagate
SELECT * FROM t1 WHERE a='0000-00-00 10:20:30';
a
diff --git a/mysql-test/main/type_time.test b/mysql-test/main/type_time.test
index 17494cec04f..f4a1a474046 100644
--- a/mysql-test/main/type_time.test
+++ b/mysql-test/main/type_time.test
@@ -514,6 +514,11 @@ SELECT * FROM t1 WHERE a=TIMESTAMP'0000-00-01 10:20:30' AND LENGTH(a)=8;
EXPLAIN EXTENDED
SELECT * FROM t1 WHERE a=TIMESTAMP'0000-00-01 10:20:30' AND LENGTH(a)=30+RAND();
+EXPLAIN EXTENDED
+SELECT * FROM t1 WHERE a=TIMESTAMP'0000-01-00 10:20:30' AND LENGTH(a)=8;
+EXPLAIN EXTENDED
+SELECT * FROM t1 WHERE a=TIMESTAMP'0001-00-00 10:20:30' AND LENGTH(a)=8;
+
--echo # Old mode, TIMESTAMP-alike string literal, zero YYYYMMDD, Ok to propagate
SELECT * FROM t1 WHERE a='0000-00-00 10:20:30';
SELECT * FROM t1 WHERE a='0000-00-00 10:20:30' AND LENGTH(a)=8;
diff --git a/sql/field.cc b/sql/field.cc
index 50644bf4f05..217fd2bb6e8 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -5952,14 +5952,28 @@ bool Field_time::can_be_substituted_to_equal_item(const Context &ctx,
Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx,
Item *const_item)
{
+ /*
+ Old mode conversion from DATETIME with non-zero YYYYMMDD part
+ to TIME works very inconsistently. Possible variants:
+ - truncate the YYYYMMDD part
+ - add (MM*33+DD)*24 to hours
+ - add (MM*31+DD)*24 to hours
+ Let's disallow propagation of DATETIME with non-zero YYYYMMDD
+ as an equal constant for a TIME field.
+ */
+ Time::datetime_to_time_mode_t mode=
+ (thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) ?
+ Time::DATETIME_TO_TIME_YYYYMMDD_00000000_ONLY :
+ Time::DATETIME_TO_TIME_MINUS_CURRENT_DATE;
+
switch (ctx.subst_constraint()) {
case ANY_SUBST:
if (const_item->field_type() != MYSQL_TYPE_TIME)
{
- MYSQL_TIME ltime;
// Get the value of const_item with conversion from DATETIME to TIME
- ulonglong fuzzydate= Time::comparison_flags_for_get_date();
- if (const_item->get_time_with_conversion(thd, &ltime, fuzzydate))
+ Time tm(const_item,
+ Time::Options(Time::comparison_flags_for_get_date(), mode));
+ if (!tm.is_valid_time())
return NULL;
/*
Replace a DATE/DATETIME constant to a TIME constant:
@@ -5971,8 +5985,9 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx,
(assuming CURRENT_DATE is '2015-08-30'
*/
- return new (thd->mem_root) Item_time_literal(thd, &ltime,
- ltime.second_part ?
+ return new (thd->mem_root) Item_time_literal(thd, tm.get_mysql_time(),
+ tm.get_mysql_time()->
+ second_part ?
TIME_SECOND_PART_DIGITS :
0);
}
@@ -5981,8 +5996,8 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx,
if (const_item->field_type() != MYSQL_TYPE_TIME ||
const_item->decimals != decimals())
{
- MYSQL_TIME ltime;
- if (const_item->get_time_with_conversion(thd, &ltime, TIME_TIME_ONLY))
+ Time tm(const_item, Time::Options(TIME_TIME_ONLY, mode));
+ if (!tm.is_valid_time())
return NULL;
/*
Note, the value returned in "ltime" can have more fractional
@@ -5998,7 +6013,8 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx,
The optimized WHERE will return with "Impossible WHERE", without
having to do the full table scan.
*/
- return new (thd->mem_root) Item_time_literal(thd, &ltime, decimals());
+ return new (thd->mem_root) Item_time_literal(thd, tm.get_mysql_time(),
+ decimals());
}
break;
}
diff --git a/sql/item.cc b/sql/item.cc
index 8b962d1706d..4422d73787b 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -128,50 +128,6 @@ longlong Item::val_datetime_packed_result()
}
-/**
- Get date/time/datetime.
- If DATETIME or DATE result is returned, it's converted to TIME.
-*/
-bool Item::get_time_with_conversion(THD *thd, MYSQL_TIME *ltime,
- ulonglong fuzzydate)
-{
- if (get_date(ltime, fuzzydate))
- return true;
- if (ltime->time_type != MYSQL_TIMESTAMP_TIME)
- {
- MYSQL_TIME ltime2;
- if ((thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) &&
- (ltime->year || ltime->day || ltime->month))
- {
- /*
- Old mode conversion from DATETIME with non-zero YYYYMMDD part
- to TIME works very inconsistently. Possible variants:
- - truncate the YYYYMMDD part
- - add (MM*33+DD)*24 to hours
- - add (MM*31+DD)*24 to hours
- Let's return TRUE here, to disallow equal field propagation.
- Note, If we start to use this method in more pieces of the code other
- than equal field propagation, we should probably return
- TRUE only if some flag in fuzzydate is set.
- */
- return true;
- }
- if (datetime_to_time_with_warn(thd, ltime, &ltime2, TIME_SECOND_PART_DIGITS))
- {
- /*
- If the time difference between CURRENT_DATE and ltime
- did not fit into the supported TIME range, then we set the
- difference to the maximum possible value in the supported TIME range
- */
- DBUG_ASSERT(0);
- return (null_value= true);
- }
- *ltime= ltime2;
- }
- return false;
-}
-
-
/*
For the items which don't have its own fast val_str_ascii()
implementation we provide a generic slower version,
diff --git a/sql/item.h b/sql/item.h
index a96406fa0cd..73584fd3293 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -1611,40 +1611,6 @@ public:
bool get_date_from_string(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool get_time(MYSQL_TIME *ltime)
{ return get_date(ltime, Time::flags_for_get_date()); }
- /*
- Get time with automatic DATE/DATETIME to TIME conversion,
- by subtracting CURRENT_DATE.
-
- Performce a reverse operation to CAST(time AS DATETIME)
- Suppose:
- - we have a set of items (typically with the native MYSQL_TYPE_TIME type)
- whose item->get_date() return TIME1 value, and
- - CAST(AS DATETIME) for the same Items return DATETIME1,
- after applying time-to-datetime conversion to TIME1.
-
- then all items (typically of the native MYSQL_TYPE_{DATE|DATETIME} types)
- whose get_date() return DATETIME1 must also return TIME1 from
- get_time_with_conversion()
-
- @param thd - the thread, its variables.old_mode is checked
- to decide if use simple YYYYMMDD truncation (old mode),
- or perform full DATETIME-to-TIME conversion with
- CURRENT_DATE subtraction.
- @param[out] ltime - store the result here
- @param fuzzydate - flags to be used for the get_date() call.
- Normally, should include TIME_TIME_ONLY, to let
- the called low-level routines, e.g. str_to_date(),
- know that we prefer TIME rather that DATE/DATETIME
- and do less conversion outside of the low-level
- routines.
-
- @returns true - on error, e.g. get_date() returned NULL value,
- or get_date() returned DATETIME/DATE with non-zero
- YYYYMMDD part.
- @returns false - on success
- */
- bool get_time_with_conversion(THD *thd, MYSQL_TIME *ltime,
- ulonglong fuzzydate);
// Get a DATE or DATETIME value in numeric packed format for comparison
virtual longlong val_datetime_packed()
{
diff --git a/sql/sql_time.cc b/sql/sql_time.cc
index c75b8b19e1a..430eb192052 100644
--- a/sql/sql_time.cc
+++ b/sql/sql_time.cc
@@ -1365,26 +1365,6 @@ time_to_datetime_with_warn(THD *thd,
}
-bool datetime_to_time_with_warn(THD *thd, const MYSQL_TIME *dt,
- MYSQL_TIME *tm, uint dec)
-{
- if (thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)
- {
- *tm= *dt;
- datetime_to_time(tm);
- return false;
- }
- else /* new mode */
- {
- MYSQL_TIME current_date;
- set_current_date(thd, &current_date);
- calc_time_diff(dt, &current_date, 1, tm, 0);
- }
- int warnings= 0;
- return check_time_range(tm, dec, &warnings);
-}
-
-
longlong pack_time(const MYSQL_TIME *my_time)
{
return ((((((my_time->year * 13ULL +
diff --git a/sql/sql_time.h b/sql/sql_time.h
index cfa1468841b..8c6e58856e6 100644
--- a/sql/sql_time.h
+++ b/sql/sql_time.h
@@ -54,50 +54,6 @@ bool time_to_datetime(THD *thd, const MYSQL_TIME *tm, MYSQL_TIME *dt);
bool time_to_datetime_with_warn(THD *thd,
const MYSQL_TIME *tm, MYSQL_TIME *dt,
ulonglong fuzzydate);
-/*
- Simply truncate the YYYY-MM-DD part to 0000-00-00
- and change time_type to MYSQL_TIMESTAMP_TIME
-*/
-inline void datetime_to_time(MYSQL_TIME *ltime)
-{
- DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE ||
- ltime->time_type == MYSQL_TIMESTAMP_DATETIME);
- DBUG_ASSERT(ltime->neg == 0);
- ltime->year= ltime->month= ltime->day= 0;
- ltime->time_type= MYSQL_TIMESTAMP_TIME;
-}
-
-
-/**
- Convert DATE/DATETIME to TIME(dec)
- using CURRENT_DATE in a non-old mode,
- or using simple truncation in old mode (OLD_MODE_ZERO_DATE_TIME_CAST).
-
- @param thd - the thread to get the variables.old_behaviour value from
- @param dt - the DATE of DATETIME value to convert
- @param[out] tm - store result here
- @param dec - the desired scale. The fractional part of the result
- is checked according to this parameter before returning
- the conversion result. "dec" is important in the corner
- cases near the max/min limits.
- If the result is '838:59:59.999999' and the desired scale
- is less than 6, an error is returned.
- Note, dec is not important in the
- OLD_MODE_ZERO_DATE_TIME_CAST old mode.
-
- - in case of OLD_MODE_ZERO_DATE_TIME_CAST
- the TIME part is simply truncated and "false" is returned.
- - otherwise, the result is calculated effectively similar to:
- TIMEDIFF(dt, CAST(CURRENT_DATE AS DATETIME))
- If the difference fits into the supported TIME range, "false" is returned,
- otherwise a warning is issued and "true" is returned.
-
- @return false - on success
- @return true - on error
-*/
-bool datetime_to_time_with_warn(THD *, const MYSQL_TIME *dt,
- MYSQL_TIME *tm, uint dec);
-
inline void datetime_to_date(MYSQL_TIME *ltime)
{
diff --git a/sql/sql_type.h b/sql/sql_type.h
index 3465708f768..40433fdaabd 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -534,7 +534,9 @@ public:
enum datetime_to_time_mode_t
{
DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS,
- DATETIME_TO_TIME_YYYYMMDD_TRUNCATE
+ DATETIME_TO_TIME_YYYYMMDD_TRUNCATE,
+ DATETIME_TO_TIME_YYYYMMDD_00000000_ONLY,
+ DATETIME_TO_TIME_MINUS_CURRENT_DATE
};
class Options
{
@@ -601,6 +603,21 @@ private:
hour+= from_day * 24;
}
/*
+ The result is calculated effectively similar to:
+ TIMEDIFF(dt, CAST(CURRENT_DATE AS DATETIME))
+ If the difference does not fit to the supported TIME range, it's truncated.
+ */
+ void datetime_to_time_minus_current_date(THD *thd)
+ {
+ MYSQL_TIME current_date, tmp;
+ set_current_date(thd, &current_date);
+ calc_time_diff(this, &current_date, 1, &tmp, 0);
+ static_cast<MYSQL_TIME*>(this)[0]= tmp;
+ int warnings= 0;
+ (void) check_time_range(this, TIME_SECOND_PART_DIGITS, &warnings);
+ DBUG_ASSERT(is_valid_time());
+ }
+ /*
Convert a valid DATE or DATETIME to TIME.
Before this call, "this" must be a valid DATE or DATETIME value,
e.g. returned from Item::get_date(), str_to_time(), number_to_time().
@@ -621,11 +638,18 @@ private:
*/
DBUG_ASSERT(day < 32);
DBUG_ASSERT(hour < 24);
- if (opt.datetime_to_time_mode() ==
- DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS)
- datetime_to_time_YYYYMMDD_000000DD_mix_to_hours(warn, year, month, day);
- year= month= day= 0;
- time_type= MYSQL_TIMESTAMP_TIME;
+ if (opt.datetime_to_time_mode() == DATETIME_TO_TIME_MINUS_CURRENT_DATE)
+ {
+ datetime_to_time_minus_current_date(current_thd);
+ }
+ else
+ {
+ if (opt.datetime_to_time_mode() ==
+ DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS)
+ datetime_to_time_YYYYMMDD_000000DD_mix_to_hours(warn, year, month, day);
+ year= month= day= 0;
+ time_type= MYSQL_TIMESTAMP_TIME;
+ }
DBUG_ASSERT(is_valid_time_slow());
}
/**
@@ -646,7 +670,12 @@ private:
switch (time_type) {
case MYSQL_TIMESTAMP_DATE:
case MYSQL_TIMESTAMP_DATETIME:
- valid_datetime_to_valid_time(warn, opt);
+ if (opt.datetime_to_time_mode() ==
+ DATETIME_TO_TIME_YYYYMMDD_00000000_ONLY &&
+ (year || month || day))
+ make_from_out_of_range(warn);
+ else
+ valid_datetime_to_valid_time(warn, opt);
break;
case MYSQL_TIMESTAMP_NONE:
break;