mirror of
https://github.com/MariaDB/server.git
synced 2025-07-27 18:02:13 +03:00
MDEV-13995 MAX(timestamp) returns a wrong result near DST change
This commit is contained in:
332
sql/sql_type.cc
332
sql/sql_type.cc
@ -209,6 +209,104 @@ Temporal_hybrid::Temporal_hybrid(THD *thd, Item *item, date_mode_t fuzzydate)
|
||||
}
|
||||
|
||||
|
||||
uint Timestamp::binary_length_to_precision(uint length)
|
||||
{
|
||||
switch (length) {
|
||||
case 4: return 0;
|
||||
case 5: return 2;
|
||||
case 6: return 4;
|
||||
case 7: return 6;
|
||||
}
|
||||
DBUG_ASSERT(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Timestamp::Timestamp(const Native &native)
|
||||
{
|
||||
DBUG_ASSERT(native.length() >= 4 && native.length() <= 7);
|
||||
uint dec= binary_length_to_precision(native.length());
|
||||
my_timestamp_from_binary(this, (const uchar *) native.ptr(), dec);
|
||||
}
|
||||
|
||||
|
||||
bool Timestamp::to_native(Native *to, uint decimals) const
|
||||
{
|
||||
uint len= my_timestamp_binary_length(decimals);
|
||||
if (to->reserve(len))
|
||||
return true;
|
||||
my_timestamp_to_binary(this, (uchar *) to->ptr(), decimals);
|
||||
to->length(len);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Timestamp::to_TIME(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate) const
|
||||
{
|
||||
return thd->timestamp_to_TIME(to, tv_sec, tv_usec, fuzzydate);
|
||||
}
|
||||
|
||||
|
||||
Timestamp::Timestamp(THD *thd, const MYSQL_TIME *ltime, uint *error_code)
|
||||
:Timeval(TIME_to_timestamp(thd, ltime, error_code), ltime->second_part)
|
||||
{ }
|
||||
|
||||
|
||||
Timestamp_or_zero_datetime::Timestamp_or_zero_datetime(THD *thd,
|
||||
const MYSQL_TIME *ltime,
|
||||
uint *error_code)
|
||||
:Timestamp(thd, ltime, error_code),
|
||||
m_is_zero_datetime(*error_code == ER_WARN_DATA_OUT_OF_RANGE)
|
||||
{
|
||||
if (m_is_zero_datetime)
|
||||
{
|
||||
if (!non_zero_date(ltime))
|
||||
*error_code= 0; // ltime was '0000-00-00 00:00:00'
|
||||
}
|
||||
else if (*error_code == ER_WARN_INVALID_TIMESTAMP)
|
||||
*error_code= 0; // ltime fell into spring time gap, adjusted.
|
||||
}
|
||||
|
||||
|
||||
bool Timestamp_or_zero_datetime::to_TIME(THD *thd, MYSQL_TIME *to,
|
||||
date_mode_t fuzzydate) const
|
||||
{
|
||||
if (m_is_zero_datetime)
|
||||
{
|
||||
set_zero_time(to, MYSQL_TIMESTAMP_DATETIME);
|
||||
return false;
|
||||
}
|
||||
return Timestamp::to_TIME(thd, to, fuzzydate);
|
||||
}
|
||||
|
||||
|
||||
bool Timestamp_or_zero_datetime::to_native(Native *to, uint decimals) const
|
||||
{
|
||||
if (m_is_zero_datetime)
|
||||
{
|
||||
to->length(0);
|
||||
return false;
|
||||
}
|
||||
return Timestamp::to_native(to, decimals);
|
||||
}
|
||||
|
||||
|
||||
int Timestamp_or_zero_datetime_native::save_in_field(Field *field,
|
||||
uint decimals) const
|
||||
{
|
||||
field->set_notnull();
|
||||
if (field->type_handler()->type_handler_for_native_format() ==
|
||||
&type_handler_timestamp2)
|
||||
return field->store_native(*this);
|
||||
if (is_zero_datetime())
|
||||
{
|
||||
static Datetime zero(Datetime::zero());
|
||||
return field->store_time_dec(zero.get_mysql_time(), decimals);
|
||||
}
|
||||
return field->store_timestamp_dec(Timestamp(*this).tv(), decimals);
|
||||
}
|
||||
|
||||
|
||||
void Sec6::make_from_decimal(const my_decimal *d, ulong *nanoseconds)
|
||||
{
|
||||
m_neg= my_decimal2seconds(d, &m_sec, &m_usec, nanoseconds);
|
||||
@ -1272,7 +1370,7 @@ const Type_handler *Type_handler_datetime_common::type_handler_for_comparison()
|
||||
|
||||
const Type_handler *Type_handler_timestamp_common::type_handler_for_comparison() const
|
||||
{
|
||||
return &type_handler_datetime;
|
||||
return &type_handler_timestamp;
|
||||
}
|
||||
|
||||
|
||||
@ -1281,6 +1379,15 @@ const Type_handler *Type_handler_row::type_handler_for_comparison() const
|
||||
return &type_handler_row;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
const Type_handler *
|
||||
Type_handler_timestamp_common::type_handler_for_native_format() const
|
||||
{
|
||||
return &type_handler_timestamp2;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
const Type_handler *Type_handler_typelib::type_handler_for_item_field() const
|
||||
@ -1457,6 +1564,16 @@ Type_handler_hybrid_field_type::aggregate_for_comparison(const Type_handler *h)
|
||||
*/
|
||||
if (b == TIME_RESULT)
|
||||
m_type_handler= h; // Temporal types bit non-temporal types
|
||||
/*
|
||||
Compare TIMESTAMP to a non-temporal type as DATETIME.
|
||||
This is needed to make queries with fuzzy dates work:
|
||||
SELECT * FROM t1
|
||||
WHERE
|
||||
ts BETWEEN '0000-00-00' AND '2010-00-01 00:00:00';
|
||||
*/
|
||||
if (m_type_handler->type_handler_for_native_format() ==
|
||||
&type_handler_timestamp2)
|
||||
m_type_handler= &type_handler_datetime;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1540,7 +1657,19 @@ Type_handler_hybrid_field_type::aggregate_for_min_max(const Type_handler *h)
|
||||
}
|
||||
else if (a == TIME_RESULT || b == TIME_RESULT)
|
||||
{
|
||||
if ((a == TIME_RESULT) + (b == TIME_RESULT) == 1)
|
||||
if ((m_type_handler->type_handler_for_native_format() ==
|
||||
&type_handler_timestamp2) +
|
||||
(h->type_handler_for_native_format() ==
|
||||
&type_handler_timestamp2) == 1)
|
||||
{
|
||||
/*
|
||||
Handle LEAST(TIMESTAMP, non-TIMESTAMP) as DATETIME,
|
||||
to make sure fuzzy dates work in this context:
|
||||
LEAST('2001-00-00', timestamp_field)
|
||||
*/
|
||||
m_type_handler= &type_handler_datetime2;
|
||||
}
|
||||
else if ((a == TIME_RESULT) + (b == TIME_RESULT) == 1)
|
||||
{
|
||||
/*
|
||||
We're here if there's only one temporal data type:
|
||||
@ -3395,6 +3524,18 @@ int Type_handler_temporal_with_date::Item_save_in_field(Item *item,
|
||||
}
|
||||
|
||||
|
||||
int Type_handler_timestamp_common::Item_save_in_field(Item *item,
|
||||
Field *field,
|
||||
bool no_conversions)
|
||||
const
|
||||
{
|
||||
Timestamp_or_zero_datetime_native_null tmp(field->table->in_use, item, true);
|
||||
if (tmp.is_null())
|
||||
return set_field_to_null_with_conversions(field, no_conversions);
|
||||
return tmp.save_in_field(field, item->decimals);
|
||||
}
|
||||
|
||||
|
||||
int Type_handler_string_result::Item_save_in_field(Item *item, Field *field,
|
||||
bool no_conversions) const
|
||||
{
|
||||
@ -3461,6 +3602,12 @@ Type_handler_temporal_with_date::set_comparator_func(Arg_comparator *cmp) const
|
||||
return cmp->set_cmp_func_datetime();
|
||||
}
|
||||
|
||||
bool
|
||||
Type_handler_timestamp_common::set_comparator_func(Arg_comparator *cmp) const
|
||||
{
|
||||
return cmp->set_cmp_func_native();
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
@ -3596,7 +3743,7 @@ Type_handler_string_result::Item_get_cache(THD *thd, const Item *item) const
|
||||
Item_cache *
|
||||
Type_handler_timestamp_common::Item_get_cache(THD *thd, const Item *item) const
|
||||
{
|
||||
return new (thd->mem_root) Item_cache_datetime(thd);
|
||||
return new (thd->mem_root) Item_cache_timestamp(thd);
|
||||
}
|
||||
|
||||
Item_cache *
|
||||
@ -4763,6 +4910,12 @@ longlong Type_handler_time_common::
|
||||
return func->val_int_cmp_time();
|
||||
}
|
||||
|
||||
longlong Type_handler_timestamp_common::
|
||||
Item_func_between_val_int(Item_func_between *func) const
|
||||
{
|
||||
return func->val_int_cmp_native();
|
||||
}
|
||||
|
||||
longlong Type_handler_int_result::
|
||||
Item_func_between_val_int(Item_func_between *func) const
|
||||
{
|
||||
@ -4826,6 +4979,12 @@ cmp_item *Type_handler_temporal_with_date::make_cmp_item(THD *thd,
|
||||
return new (thd->mem_root) cmp_item_datetime;
|
||||
}
|
||||
|
||||
cmp_item *Type_handler_timestamp_common::make_cmp_item(THD *thd,
|
||||
CHARSET_INFO *cs) const
|
||||
{
|
||||
return new (thd->mem_root) cmp_item_timestamp;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
static int srtcmp_in(CHARSET_INFO *cs, const String *x,const String *y)
|
||||
@ -4886,6 +5045,15 @@ Type_handler_temporal_with_date::make_in_vector(THD *thd,
|
||||
}
|
||||
|
||||
|
||||
in_vector *
|
||||
Type_handler_timestamp_common::make_in_vector(THD *thd,
|
||||
const Item_func_in *func,
|
||||
uint nargs) const
|
||||
{
|
||||
return new (thd->mem_root) in_timestamp(thd, nargs);
|
||||
}
|
||||
|
||||
|
||||
in_vector *Type_handler_row::make_in_vector(THD *thd,
|
||||
const Item_func_in *func,
|
||||
uint nargs) const
|
||||
@ -5004,7 +5172,9 @@ String *Type_handler_datetime_common::
|
||||
String *Type_handler_timestamp_common::
|
||||
Item_func_min_max_val_str(Item_func_min_max *func, String *str) const
|
||||
{
|
||||
return Datetime(func).to_string(str, func->decimals);
|
||||
THD *thd= current_thd;
|
||||
return Timestamp_or_zero_datetime_native_null(thd, func).
|
||||
to_datetime(thd).to_string(str, func->decimals);
|
||||
}
|
||||
|
||||
|
||||
@ -5056,10 +5226,13 @@ double Type_handler_datetime_common::
|
||||
return Datetime(current_thd, func).to_double();
|
||||
}
|
||||
|
||||
|
||||
double Type_handler_timestamp_common::
|
||||
Item_func_min_max_val_real(Item_func_min_max *func) const
|
||||
{
|
||||
return Datetime(current_thd, func).to_double();
|
||||
THD *thd= current_thd;
|
||||
return Timestamp_or_zero_datetime_native_null(thd, func).
|
||||
to_datetime(thd).to_double();
|
||||
}
|
||||
|
||||
|
||||
@ -5101,7 +5274,9 @@ longlong Type_handler_datetime_common::
|
||||
longlong Type_handler_timestamp_common::
|
||||
Item_func_min_max_val_int(Item_func_min_max *func) const
|
||||
{
|
||||
return Datetime(current_thd, func).to_longlong();
|
||||
THD *thd= current_thd;
|
||||
return Timestamp_or_zero_datetime_native_null(thd, func).
|
||||
to_datetime(thd).to_longlong();
|
||||
}
|
||||
|
||||
|
||||
@ -5156,7 +5331,9 @@ my_decimal *Type_handler_timestamp_common::
|
||||
Item_func_min_max_val_decimal(Item_func_min_max *func,
|
||||
my_decimal *dec) const
|
||||
{
|
||||
return Datetime(current_thd, func).to_decimal(dec);
|
||||
THD *thd= current_thd;
|
||||
return Timestamp_or_zero_datetime_native_null(thd, func).
|
||||
to_datetime(thd).to_decimal(dec);
|
||||
}
|
||||
|
||||
|
||||
@ -5208,6 +5385,15 @@ bool Type_handler_time_common::
|
||||
return func->get_time_native(thd, ltime);
|
||||
}
|
||||
|
||||
|
||||
bool Type_handler_timestamp_common::
|
||||
Item_func_min_max_get_date(THD *thd, Item_func_min_max *func,
|
||||
MYSQL_TIME *ltime, date_mode_t fuzzydate) const
|
||||
{
|
||||
return Timestamp_or_zero_datetime_native_null(thd, func).
|
||||
to_datetime(thd).copy_to_mysql_time(ltime);
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
/**
|
||||
@ -6480,6 +6666,18 @@ bool Type_handler::
|
||||
}
|
||||
|
||||
|
||||
bool Type_handler::Item_send_timestamp(Item *item,
|
||||
Protocol *protocol,
|
||||
st_value *buf) const
|
||||
{
|
||||
Timestamp_or_zero_datetime_native_null native(protocol->thd, item);
|
||||
if (native.is_null())
|
||||
return protocol->store_null();
|
||||
native.to_TIME(protocol->thd, &buf->value.m_time);
|
||||
return protocol->store(&buf->value.m_time, item->decimals);
|
||||
}
|
||||
|
||||
|
||||
bool Type_handler::
|
||||
Item_send_datetime(Item *item, Protocol *protocol, st_value *buf) const
|
||||
{
|
||||
@ -7628,6 +7826,16 @@ bool Type_handler_temporal_with_date::Item_eq_value(THD *thd,
|
||||
}
|
||||
|
||||
|
||||
bool Type_handler_timestamp_common::Item_eq_value(THD *thd,
|
||||
const Type_cmp_attributes *attr,
|
||||
Item *a, Item *b) const
|
||||
{
|
||||
Timestamp_or_zero_datetime_native_null na(thd, a, true);
|
||||
Timestamp_or_zero_datetime_native_null nb(thd, b, true);
|
||||
return !na.is_null() && !nb.is_null() && !cmp_native(na, nb);
|
||||
}
|
||||
|
||||
|
||||
bool Type_handler_string_result::Item_eq_value(THD *thd,
|
||||
const Type_cmp_attributes *attr,
|
||||
Item *a, Item *b) const
|
||||
@ -7845,3 +8053,113 @@ Type_handler_time_common::create_literal_item(THD *thd,
|
||||
literal_warn(thd, item, str, length, cs, &st, "TIME", send_error);
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
bool Type_handler_timestamp_common::TIME_to_native(THD *thd,
|
||||
const MYSQL_TIME *ltime,
|
||||
Native *to,
|
||||
uint decimals) const
|
||||
{
|
||||
uint error_code;
|
||||
Timestamp_or_zero_datetime tm(thd, ltime, &error_code);
|
||||
if (error_code)
|
||||
return true;
|
||||
tm.trunc(decimals);
|
||||
return tm.to_native(to, decimals);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Type_handler_timestamp_common::Item_val_native_with_conversion(THD *thd,
|
||||
Item *item,
|
||||
Native *to) const
|
||||
{
|
||||
MYSQL_TIME ltime;
|
||||
if (item->type_handler()->type_handler_for_native_format() ==
|
||||
&type_handler_timestamp2)
|
||||
return item->val_native(thd, to);
|
||||
return
|
||||
item->get_date(thd, <ime, Datetime::Options(TIME_NO_ZERO_IN_DATE, thd)) ||
|
||||
TIME_to_native(thd, <ime, to, item->datetime_precision(thd));
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Type_handler_timestamp_common::Item_val_native_with_conversion_result(THD *thd,
|
||||
Item *item,
|
||||
Native *to)
|
||||
const
|
||||
{
|
||||
MYSQL_TIME ltime;
|
||||
if (item->type_handler()->type_handler_for_native_format() ==
|
||||
&type_handler_timestamp2)
|
||||
return item->val_native_result(thd, to);
|
||||
return
|
||||
item->get_date_result(thd, <ime,
|
||||
Datetime::Options(TIME_NO_ZERO_IN_DATE, thd)) ||
|
||||
TIME_to_native(thd, <ime, to, item->datetime_precision(thd));
|
||||
}
|
||||
|
||||
|
||||
int Type_handler_timestamp_common::cmp_native(const Native &a,
|
||||
const Native &b) const
|
||||
{
|
||||
/*
|
||||
Optimize a simple case:
|
||||
Either both timeatamp values have the same fractional precision,
|
||||
or both values are zero datetime '0000-00-00 00:00:00.000000',
|
||||
*/
|
||||
if (a.length() == b.length())
|
||||
return memcmp(a.ptr(), b.ptr(), a.length());
|
||||
return Timestamp_or_zero_datetime(a).cmp(Timestamp_or_zero_datetime(b));
|
||||
}
|
||||
|
||||
|
||||
Timestamp_or_zero_datetime_native_null::
|
||||
Timestamp_or_zero_datetime_native_null(THD *thd, Item *item, bool conv)
|
||||
:Null_flag(false)
|
||||
{
|
||||
DBUG_ASSERT(item->type_handler()->type_handler_for_native_format() ==
|
||||
&type_handler_timestamp2 || conv);
|
||||
if (conv ?
|
||||
type_handler_timestamp2.Item_val_native_with_conversion(thd, item, this) :
|
||||
item->val_native(thd, this))
|
||||
Null_flag::operator=(true);
|
||||
// If no conversion, then is_null() should be equal to item->null_value
|
||||
DBUG_ASSERT(is_null() == item->null_value || conv);
|
||||
/*
|
||||
is_null() can be true together with item->null_value==false, which means
|
||||
a non-NULL item was evaluated, but then the conversion to TIMESTAMP failed.
|
||||
But is_null() can never be false if item->null_value==true.
|
||||
*/
|
||||
DBUG_ASSERT(is_null() >= item->null_value);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Type_handler::Item_param_val_native(THD *thd,
|
||||
Item_param *item,
|
||||
Native *to) const
|
||||
{
|
||||
DBUG_ASSERT(0); // TODO-TYPE: MDEV-14271
|
||||
return item->null_value= true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Type_handler_timestamp_common::Item_param_val_native(THD *thd,
|
||||
Item_param *item,
|
||||
Native *to) const
|
||||
{
|
||||
/*
|
||||
The below code may not run well in corner cases.
|
||||
This will be fixed under terms of MDEV-14271.
|
||||
Item_param should:
|
||||
- either remember @@time_zone at bind time
|
||||
- or store TIMESTAMP in my_time_t format, rather than in MYSQL_TIME format.
|
||||
*/
|
||||
MYSQL_TIME ltime;
|
||||
return
|
||||
item->get_date(thd, <ime, Datetime::Options(TIME_NO_ZERO_IN_DATE, thd)) ||
|
||||
TIME_to_native(thd, <ime, to, item->datetime_precision(thd));
|
||||
}
|
||||
|
Reference in New Issue
Block a user