mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
MDEV-16926 CAST(COALESCE(year_field)) returns wrong value
This commit is contained in:
@ -525,5 +525,34 @@ Warnings:
|
|||||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 93
|
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 93
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
#
|
#
|
||||||
|
# MDEV-16926 CAST(COALESCE(year_field)) returns wrong value
|
||||||
|
#
|
||||||
|
CREATE OR REPLACE TABLE t1 (a YEAR);
|
||||||
|
INSERT INTO t1 VALUES (1970),(1978),(2000),(2069);
|
||||||
|
SELECT a, CAST(a AS DATE), CAST(COALESCE(a) AS DATE) FROM t1;
|
||||||
|
a CAST(a AS DATE) CAST(COALESCE(a) AS DATE)
|
||||||
|
1970 1970-00-00 1970-00-00
|
||||||
|
1978 1978-00-00 1978-00-00
|
||||||
|
2000 2000-00-00 2000-00-00
|
||||||
|
2069 2069-00-00 2069-00-00
|
||||||
|
SELECT MIN(a), MAX(a) FROM t1;
|
||||||
|
MIN(a) MAX(a)
|
||||||
|
1970 2069
|
||||||
|
DROP TABLE t1;
|
||||||
|
CREATE OR REPLACE TABLE t1 (a YEAR(2));
|
||||||
|
Warnings:
|
||||||
|
Note 1287 'YEAR(2)' is deprecated and will be removed in a future release. Please use YEAR(4) instead
|
||||||
|
INSERT INTO t1 VALUES (1970),(1978),(2000),(2069);
|
||||||
|
SELECT a, CAST(a AS DATE), CAST(COALESCE(a) AS DATE) FROM t1;
|
||||||
|
a CAST(a AS DATE) CAST(COALESCE(a) AS DATE)
|
||||||
|
70 1970-00-00 1970-00-00
|
||||||
|
78 1978-00-00 1978-00-00
|
||||||
|
00 2000-00-00 2000-00-00
|
||||||
|
69 2069-00-00 2069-00-00
|
||||||
|
SELECT MIN(a), MAX(a) FROM t1;
|
||||||
|
MIN(a) MAX(a)
|
||||||
|
70 69
|
||||||
|
DROP TABLE t1;
|
||||||
|
#
|
||||||
# End of 10.4 tests
|
# End of 10.4 tests
|
||||||
#
|
#
|
||||||
|
@ -278,6 +278,23 @@ SELECT * FROM t1 WHERE a=1993 and a=93;
|
|||||||
EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=1993 and a=93;
|
EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=1993 and a=93;
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-16926 CAST(COALESCE(year_field)) returns wrong value
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
CREATE OR REPLACE TABLE t1 (a YEAR);
|
||||||
|
INSERT INTO t1 VALUES (1970),(1978),(2000),(2069);
|
||||||
|
SELECT a, CAST(a AS DATE), CAST(COALESCE(a) AS DATE) FROM t1;
|
||||||
|
SELECT MIN(a), MAX(a) FROM t1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
CREATE OR REPLACE TABLE t1 (a YEAR(2));
|
||||||
|
INSERT INTO t1 VALUES (1970),(1978),(2000),(2069);
|
||||||
|
SELECT a, CAST(a AS DATE), CAST(COALESCE(a) AS DATE) FROM t1;
|
||||||
|
SELECT MIN(a), MAX(a) FROM t1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
|
||||||
--echo #
|
--echo #
|
||||||
--echo # End of 10.4 tests
|
--echo # End of 10.4 tests
|
||||||
--echo #
|
--echo #
|
||||||
|
@ -2881,7 +2881,10 @@ public:
|
|||||||
:Field_tiny(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
|
:Field_tiny(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
|
||||||
unireg_check_arg, field_name_arg, 1, 1)
|
unireg_check_arg, field_name_arg, 1, 1)
|
||||||
{}
|
{}
|
||||||
const Type_handler *type_handler() const { return &type_handler_year; }
|
const Type_handler *type_handler() const
|
||||||
|
{
|
||||||
|
return field_length == 2 ? &type_handler_year2 : &type_handler_year;
|
||||||
|
}
|
||||||
Copy_func *get_copy_func(const Field *from) const
|
Copy_func *get_copy_func(const Field *from) const
|
||||||
{
|
{
|
||||||
if (eq_def(from))
|
if (eq_def(from))
|
||||||
|
20
sql/item.cc
20
sql/item.cc
@ -1328,26 +1328,6 @@ bool Item::get_date_from_int(MYSQL_TIME *ltime, ulonglong fuzzydate)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Item::get_date_from_year(MYSQL_TIME *ltime, ulonglong fuzzydate)
|
|
||||||
{
|
|
||||||
longlong value= val_int();
|
|
||||||
DBUG_ASSERT(unsigned_flag || value >= 0);
|
|
||||||
if (max_length == 2)
|
|
||||||
{
|
|
||||||
if (value < 70)
|
|
||||||
value+= 2000;
|
|
||||||
else if (value <= 1900)
|
|
||||||
value+= 1900;
|
|
||||||
}
|
|
||||||
value*= 10000; /* make it YYYYMMHH */
|
|
||||||
if (null_value || int_to_datetime_with_warn(false, value,
|
|
||||||
ltime, fuzzydate,
|
|
||||||
field_name_or_null()))
|
|
||||||
return null_value|= make_zero_date(ltime, fuzzydate);
|
|
||||||
return null_value= false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool Item::get_date_from_real(MYSQL_TIME *ltime, ulonglong fuzzydate)
|
bool Item::get_date_from_real(MYSQL_TIME *ltime, ulonglong fuzzydate)
|
||||||
{
|
{
|
||||||
double value= val_real();
|
double value= val_real();
|
||||||
|
@ -1607,7 +1607,6 @@ public:
|
|||||||
Item **ref, uint flags);
|
Item **ref, uint flags);
|
||||||
virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)= 0;
|
virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)= 0;
|
||||||
bool get_date_from_int(MYSQL_TIME *ltime, ulonglong fuzzydate);
|
bool get_date_from_int(MYSQL_TIME *ltime, ulonglong fuzzydate);
|
||||||
bool get_date_from_year(MYSQL_TIME *ltime, ulonglong fuzzydate);
|
|
||||||
bool get_date_from_real(MYSQL_TIME *ltime, ulonglong fuzzydate);
|
bool get_date_from_real(MYSQL_TIME *ltime, ulonglong fuzzydate);
|
||||||
bool get_date_from_string(MYSQL_TIME *ltime, ulonglong fuzzydate);
|
bool get_date_from_string(MYSQL_TIME *ltime, ulonglong fuzzydate);
|
||||||
bool get_time(MYSQL_TIME *ltime)
|
bool get_time(MYSQL_TIME *ltime)
|
||||||
@ -6476,9 +6475,13 @@ public:
|
|||||||
class Item_cache_year: public Item_cache_int
|
class Item_cache_year: public Item_cache_int
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Item_cache_year(THD *thd): Item_cache_int(thd, &type_handler_year) { }
|
Item_cache_year(THD *thd, const Type_handler *handler)
|
||||||
|
:Item_cache_int(thd, handler) { }
|
||||||
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
|
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
|
||||||
{ return get_date_from_year(ltime, fuzzydate); }
|
{
|
||||||
|
return null_value=
|
||||||
|
VYear(this).to_mysql_time_with_warn(ltime, fuzzydate, NULL);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -2528,17 +2528,14 @@ bool Item_datetime_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
|
|||||||
bool Item_func_makedate::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
|
bool Item_func_makedate::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
|
||||||
{
|
{
|
||||||
DBUG_ASSERT(fixed == 1);
|
DBUG_ASSERT(fixed == 1);
|
||||||
long daynr= (long) args[1]->val_int();
|
long year, days, daynr= (long) args[1]->val_int();
|
||||||
long year= (long) args[0]->val_int();
|
|
||||||
long days;
|
|
||||||
|
|
||||||
if (args[0]->null_value || args[1]->null_value ||
|
VYear vyear(args[0]);
|
||||||
year < 0 || year > 9999 || daynr <= 0)
|
if (vyear.is_null() || args[1]->null_value || vyear.truncated() || daynr <= 0)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if (year < 100)
|
if ((year= (long) vyear.year()) < 100)
|
||||||
year= year_2000_handling(year);
|
year= year_2000_handling(year);
|
||||||
|
|
||||||
days= calc_daynr(year,1,1) + daynr - 1;
|
days= calc_daynr(year,1,1) + daynr - 1;
|
||||||
if (get_date_from_daynr(days, <ime->year, <ime->month, <ime->day))
|
if (get_date_from_daynr(days, <ime->year, <ime->month, <ime->day))
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -42,6 +42,7 @@ Type_handler_olddecimal type_handler_olddecimal;
|
|||||||
Type_handler_newdecimal type_handler_newdecimal;
|
Type_handler_newdecimal type_handler_newdecimal;
|
||||||
|
|
||||||
Type_handler_year type_handler_year;
|
Type_handler_year type_handler_year;
|
||||||
|
Type_handler_year type_handler_year2;
|
||||||
Type_handler_time type_handler_time;
|
Type_handler_time type_handler_time;
|
||||||
Type_handler_date type_handler_date;
|
Type_handler_date type_handler_date;
|
||||||
Type_handler_timestamp type_handler_timestamp;
|
Type_handler_timestamp type_handler_timestamp;
|
||||||
@ -319,6 +320,45 @@ VSec6::VSec6(Item *item, const char *type_str, ulonglong limit)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Year::Year(longlong value, bool unsigned_flag, uint length)
|
||||||
|
{
|
||||||
|
if ((m_truncated= (value < 0 && !unsigned_flag)))
|
||||||
|
m_year= 0;
|
||||||
|
else if (value > 9999)
|
||||||
|
{
|
||||||
|
m_truncated= true;
|
||||||
|
m_year= 9999;
|
||||||
|
}
|
||||||
|
else if (length == 2)
|
||||||
|
{
|
||||||
|
m_year= value < 70 ? (uint) value + 2000 :
|
||||||
|
value <= 1900 ? (uint) value + 1900 :
|
||||||
|
(uint) value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_year= (uint) value;
|
||||||
|
DBUG_ASSERT(m_year <= 9999);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint Year::year_precision(const Item *item) const
|
||||||
|
{
|
||||||
|
return item->type_handler() == &type_handler_year2 ? 2 : 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VYear::VYear(Item *item)
|
||||||
|
:Year_null(Year(item->val_int(), item->unsigned_flag,
|
||||||
|
year_precision(item)), item->null_value)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
VYear_op::VYear_op(Item_func_hybrid_field_type *item)
|
||||||
|
:Year_null(Year(item->int_op(), item->unsigned_flag,
|
||||||
|
year_precision(item)), item->null_value)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
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()))
|
||||||
@ -3061,7 +3101,7 @@ Type_handler_int_result::Item_get_cache(THD *thd, const Item *item) const
|
|||||||
Item_cache *
|
Item_cache *
|
||||||
Type_handler_year::Item_get_cache(THD *thd, const Item *item) const
|
Type_handler_year::Item_get_cache(THD *thd, const Item *item) const
|
||||||
{
|
{
|
||||||
return new (thd->mem_root) Item_cache_year(thd);
|
return new (thd->mem_root) Item_cache_year(thd, item->type_handler());
|
||||||
}
|
}
|
||||||
|
|
||||||
Item_cache *
|
Item_cache *
|
||||||
@ -3611,7 +3651,9 @@ bool Type_handler_int_result::Item_get_date(Item *item, MYSQL_TIME *ltime,
|
|||||||
bool Type_handler_year::Item_get_date(Item *item, MYSQL_TIME *ltime,
|
bool Type_handler_year::Item_get_date(Item *item, MYSQL_TIME *ltime,
|
||||||
ulonglong fuzzydate) const
|
ulonglong fuzzydate) const
|
||||||
{
|
{
|
||||||
return item->get_date_from_year(ltime, fuzzydate);
|
return item->null_value=
|
||||||
|
VYear(item).to_mysql_time_with_warn(ltime, fuzzydate,
|
||||||
|
item->field_name_or_null());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3786,6 +3828,17 @@ Type_handler_decimal_result::Item_func_hybrid_field_type_get_date(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Type_handler_year::Item_func_hybrid_field_type_get_date(
|
||||||
|
Item_func_hybrid_field_type *item,
|
||||||
|
MYSQL_TIME *ltime,
|
||||||
|
ulonglong fuzzydate) const
|
||||||
|
{
|
||||||
|
return item->null_value=
|
||||||
|
VYear_op(item).to_mysql_time_with_warn(ltime, fuzzydate, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************/
|
/***************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
@ -389,6 +389,61 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Year
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
uint m_year;
|
||||||
|
bool m_truncated;
|
||||||
|
bool to_mysql_time_with_warn(MYSQL_TIME *to, ulonglong fuzzydate,
|
||||||
|
const char *field_name) const
|
||||||
|
{
|
||||||
|
longlong value= m_year * 10000; // Make it YYYYMMDD
|
||||||
|
const ErrConvInteger str(value, true);
|
||||||
|
Sec6 sec(false, value, 0);
|
||||||
|
return sec.convert_to_mysql_time(to, fuzzydate, &str, field_name);
|
||||||
|
}
|
||||||
|
uint year_precision(const Item *item) const;
|
||||||
|
public:
|
||||||
|
Year(): m_year(0), m_truncated(false) { }
|
||||||
|
Year(longlong value, bool unsigned_flag, uint length);
|
||||||
|
uint year() const { return m_year; }
|
||||||
|
bool truncated() const { return m_truncated; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Year_null: public Year
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
bool m_is_null;
|
||||||
|
public:
|
||||||
|
Year_null(const Year &other, bool is_null)
|
||||||
|
:Year(is_null ? Year() : other),
|
||||||
|
m_is_null(is_null)
|
||||||
|
{ }
|
||||||
|
bool is_null() const { return m_is_null; }
|
||||||
|
bool to_mysql_time_with_warn(MYSQL_TIME *to, ulonglong fuzzydate,
|
||||||
|
const char *field_name) const
|
||||||
|
{
|
||||||
|
return m_is_null ? true :
|
||||||
|
Year::to_mysql_time_with_warn(to, fuzzydate, field_name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class VYear: public Year_null
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VYear(Item *item);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class VYear_op: public Year_null
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VYear_op(Item_func_hybrid_field_type *item);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class Temporal: protected MYSQL_TIME
|
class Temporal: protected MYSQL_TIME
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
@ -3323,6 +3378,9 @@ public:
|
|||||||
uint32 flags) const;
|
uint32 flags) const;
|
||||||
Item_cache *Item_get_cache(THD *thd, const Item *item) const;
|
Item_cache *Item_get_cache(THD *thd, const Item *item) const;
|
||||||
bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const;
|
bool Item_get_date(Item *item, MYSQL_TIME *ltime, ulonglong fuzzydate) const;
|
||||||
|
bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *item,
|
||||||
|
MYSQL_TIME *to,
|
||||||
|
ulonglong fuzzydate) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -4562,6 +4620,7 @@ extern MYSQL_PLUGIN_IMPORT Type_handler_newdecimal type_handler_newdecimal;
|
|||||||
extern MYSQL_PLUGIN_IMPORT Type_handler_olddecimal type_handler_olddecimal;
|
extern MYSQL_PLUGIN_IMPORT Type_handler_olddecimal type_handler_olddecimal;
|
||||||
|
|
||||||
extern MYSQL_PLUGIN_IMPORT Type_handler_year type_handler_year;
|
extern MYSQL_PLUGIN_IMPORT Type_handler_year type_handler_year;
|
||||||
|
extern MYSQL_PLUGIN_IMPORT Type_handler_year type_handler_year2;
|
||||||
extern MYSQL_PLUGIN_IMPORT Type_handler_newdate type_handler_newdate;
|
extern MYSQL_PLUGIN_IMPORT Type_handler_newdate type_handler_newdate;
|
||||||
extern MYSQL_PLUGIN_IMPORT Type_handler_date type_handler_date;
|
extern MYSQL_PLUGIN_IMPORT Type_handler_date type_handler_date;
|
||||||
extern MYSQL_PLUGIN_IMPORT Type_handler_time type_handler_time;
|
extern MYSQL_PLUGIN_IMPORT Type_handler_time type_handler_time;
|
||||||
|
Reference in New Issue
Block a user