Commit 28ff7e89 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-16852 Get rid of Item_temporal_hybrid_func::fix_temporal_type()

- Implementing the task according to the MDEV description.
- Adding a helper class Sec6_add to share the code in type-specific
  branches in Item_func_add_time::get_date().
parent aee3d162
......@@ -1230,7 +1230,7 @@ str_to_date("1997-00-04 22:23:00","%Y-%m-%D") + interval 10 minute
NULL
Warnings:
Warning 1292 Truncated incorrect date value: '1997-00-04 22:23:00'
Warning 1292 Incorrect datetime value: '1997-00-04'
Warning 1292 Incorrect datetime value: '1997-00-04 00:00:00'
create table t1 (field DATE);
insert into t1 values ('2006-11-06');
select * from t1 where field < '2006-11-06 04:08:36.0';
......
......@@ -1482,67 +1482,12 @@ String *Item_temporal_func::val_str(String *str)
}
bool Item_temporal_hybrid_func::fix_temporal_type(MYSQL_TIME *ltime)
{
if (ltime->time_type < 0) /* MYSQL_TIMESTAMP_NONE, MYSQL_TIMESTAMP_ERROR */
return false;
if (ltime->time_type != MYSQL_TIMESTAMP_TIME)
goto date_or_datetime_value;
/* Convert TIME to DATE or DATETIME */
switch (field_type())
{
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP:
{
MYSQL_TIME tmp;
if (time_to_datetime_with_warn(current_thd, ltime, &tmp, 0))
return (null_value= true);
*ltime= tmp;
if (field_type() == MYSQL_TYPE_DATE)
datetime_to_date(ltime);
return false;
}
case MYSQL_TYPE_TIME:
case MYSQL_TYPE_STRING: /* DATE_ADD, ADDTIME can return VARCHAR */
return false;
default:
DBUG_ASSERT(0);
return (null_value= true);
}
date_or_datetime_value:
/* Convert DATE or DATETIME to TIME, DATE, or DATETIME */
switch (field_type())
{
case MYSQL_TYPE_TIME:
datetime_to_time(ltime);
return false;
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP:
date_to_datetime(ltime);
return false;
case MYSQL_TYPE_DATE:
datetime_to_date(ltime);
return false;
case MYSQL_TYPE_STRING: /* DATE_ADD, ADDTIME can return VARCHAR */
return false;
default:
DBUG_ASSERT(0);
return (null_value= true);
}
return false;
}
String *Item_temporal_hybrid_func::val_str_ascii(String *str)
{
DBUG_ASSERT(fixed == 1);
MYSQL_TIME ltime;
if (get_date(&ltime, 0) || fix_temporal_type(&ltime) ||
if (get_date(&ltime, 0) ||
(null_value= my_TIME_to_str(&ltime, str, decimals)))
return (String *) 0;
......@@ -2201,11 +2146,45 @@ bool Item_date_add_interval::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
INTERVAL interval;
if (args[0]->get_date(ltime,
field_type() == MYSQL_TYPE_TIME ?
TIME_TIME_ONLY : 0) ||
get_interval_value(args[1], int_type, &interval))
return (null_value=1);
if (field_type() == MYSQL_TYPE_TIME)
{
Time t(args[0]);
if (!t.is_valid_time())
return (null_value= true);
t.copy_to_mysql_time(ltime);
}
else if (field_type() == MYSQL_TYPE_DATETIME)
{
THD *thd= current_thd;
if (args[0]->field_type() == MYSQL_TYPE_TIME)
{
// time_expr + INTERVAL {YEAR|QUARTER|MONTH|WEEK|YEAR_MONTH}
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_DATETIME_FUNCTION_OVERFLOW,
ER_THD(thd, ER_DATETIME_FUNCTION_OVERFLOW),
"time");
return (null_value= true);
}
Datetime dt(thd, args[0], 0);
if (!dt.is_valid_datetime())
return (null_value= true);
dt.copy_to_mysql_time(ltime);
}
else if (field_type() == MYSQL_TYPE_DATE)
{
Date d(current_thd, args[0], 0);
if (!d.is_valid_date())
return (null_value= true);
d.copy_to_mysql_time(ltime);
}
else
{
if (args[0]->get_date(ltime, 0))
return (null_value=true);
}
if (get_interval_value(args[1], int_type, &interval))
return (null_value= true);
if (ltime->time_type != MYSQL_TIMESTAMP_TIME &&
check_date_with_warn(ltime, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE,
......@@ -2756,64 +2735,35 @@ bool Item_func_add_time::fix_length_and_dec()
bool Item_func_add_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
DBUG_ASSERT(fixed == 1);
MYSQL_TIME l_time1, l_time2;
bool is_time= 0;
long days, microseconds;
longlong seconds;
int l_sign= sign;
MYSQL_TIME l_time2;
if (Item_func_add_time::field_type() == MYSQL_TYPE_DATETIME)
{
// TIMESTAMP function OR the first argument is DATE/DATETIME/TIMESTAMP
if (get_arg0_date(&l_time1, 0) ||
args[1]->get_time(&l_time2) ||
l_time1.time_type == MYSQL_TIMESTAMP_TIME ||
l_time2.time_type != MYSQL_TIMESTAMP_TIME)
return (null_value= 1);
}
else
{
// ADDTIME function AND the first argument is TIME
if (args[0]->get_time(&l_time1) ||
args[1]->get_time(&l_time2) ||
l_time2.time_type != MYSQL_TIMESTAMP_TIME)
return (null_value= 1);
is_time= (l_time1.time_type == MYSQL_TIMESTAMP_TIME);
Datetime dt(current_thd, args[0], 0);
return (null_value= (!dt.is_valid_datetime() ||
args[1]->get_time(&l_time2) ||
Sec6_add(dt.get_mysql_time(), &l_time2, sign).
to_datetime(ltime)));
}
if (l_time1.neg != l_time2.neg)
l_sign= -l_sign;
bzero(ltime, sizeof(*ltime));
ltime->neg= calc_time_diff(&l_time1, &l_time2, -l_sign,
&seconds, &microseconds);
/*
If first argument was negative and diff between arguments
is non-zero we need to swap sign to get proper result.
*/
if (l_time1.neg && (seconds || microseconds))
ltime->neg= 1-ltime->neg; // Swap sign of result
if (!is_time && ltime->neg)
return (null_value= 1);
days= (long) (seconds / SECONDS_IN_24H);
calc_time_from_sec(ltime, (long)(seconds % SECONDS_IN_24H), microseconds);
ltime->time_type= is_time ? MYSQL_TIMESTAMP_TIME : MYSQL_TIMESTAMP_DATETIME;
if (!is_time)
if (Item_func_add_time::field_type() == MYSQL_TYPE_TIME)
{
if (get_date_from_daynr(days,&ltime->year,&ltime->month,&ltime->day) ||
!ltime->day)
return (null_value= 1);
return (null_value= 0);
// ADDTIME() and the first argument is TIME
Time t(args[0]);
return (null_value= (!t.is_valid_time() ||
args[1]->get_time(&l_time2) ||
Sec6_add(t.get_mysql_time(), &l_time2, sign).
to_time(ltime, decimals)));
}
ltime->hour+= days*24;
return (null_value= adjust_time_range_with_warn(ltime, decimals));
// Detect a proper timestamp type based on the argument values
MYSQL_TIME l_time1;
if (args[0]->get_time(&l_time1) || args[1]->get_time(&l_time2))
return (null_value= true);
Sec6_add add(&l_time1, &l_time2, sign);
return (null_value= (l_time1.time_type == MYSQL_TIMESTAMP_TIME ?
add.to_time(ltime, decimals) : add.to_datetime(ltime)));
}
......
......@@ -620,11 +620,6 @@ class Item_temporal_hybrid_func: public Item_hybrid_func
my_decimal *val_decimal(my_decimal *decimal_value)
{ return val_decimal_from_date(decimal_value); }
/**
Fix the returned timestamp to match field_type(),
which is important for val_str().
*/
bool fix_temporal_type(MYSQL_TIME *ltime);
/**
Return string value in ASCII character set.
*/
......
......@@ -89,6 +89,65 @@ enum scalar_comparison_op
};
/*
A heler class to perform additive operations between
two MYSQL_TIME structures and return the result as a
combination of seconds, microseconds and sign.
*/
class Sec6_add
{
longlong m_sec; // number of seconds
long m_usec; // number of microseconds
bool m_neg; // false if positive, true if negative
bool m_error; // false if the value is OK, true otherwise
void to_hh24mmssff(MYSQL_TIME *ltime, timestamp_type tstype) const
{
bzero(ltime, sizeof(*ltime));
ltime->neg= m_neg;
calc_time_from_sec(ltime, (long) (m_sec % SECONDS_IN_24H), m_usec);
ltime->time_type= tstype;
}
public:
/*
@param ltime1 - the first value to add (must be a valid DATE,TIME,DATETIME)
@param ltime2 - the second value to add (must be a valid TIME)
@param sign - the sign of the operation
(+1 for addition, -1 for subtraction)
*/
Sec6_add(const MYSQL_TIME *ltime1, const MYSQL_TIME *ltime2, int sign)
{
DBUG_ASSERT(sign == -1 || sign == 1);
DBUG_ASSERT(!ltime1->neg || ltime1->time_type == MYSQL_TIMESTAMP_TIME);
if (!(m_error= (ltime2->time_type != MYSQL_TIMESTAMP_TIME)))
{
if (ltime1->neg != ltime2->neg)
sign= -sign;
m_neg= calc_time_diff(ltime1, ltime2, -sign, &m_sec, &m_usec);
if (ltime1->neg && (m_sec || m_usec))
m_neg= !m_neg; // Swap sign
}
}
bool to_time(MYSQL_TIME *ltime, uint decimals) const
{
if (m_error)
return true;
to_hh24mmssff(ltime, MYSQL_TIMESTAMP_TIME);
ltime->hour+= to_days_abs() * 24;
return adjust_time_range_with_warn(ltime, decimals);
}
bool to_datetime(MYSQL_TIME *ltime) const
{
if (m_error || m_neg)
return true;
to_hh24mmssff(ltime, MYSQL_TIMESTAMP_DATETIME);
return get_date_from_daynr(to_days_abs(),
&ltime->year, &ltime->month, &ltime->day) ||
!ltime->day;
}
long to_days_abs() const { return (long) (m_sec / SECONDS_IN_24H); }
};
/**
Class Time is designed to store valid TIME values.
......@@ -355,6 +414,17 @@ class Date: public Temporal_with_date
DBUG_ASSERT(is_valid_date_slow());
return this;
}
bool copy_to_mysql_time(MYSQL_TIME *ltime) const
{
if (time_type == MYSQL_TIMESTAMP_NONE)
{
ltime->time_type= MYSQL_TIMESTAMP_NONE;
return true;
}
DBUG_ASSERT(is_valid_date_slow());
*ltime= *this;
return false;
}
};
......
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