Commit ffdae1a9 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-16926 CAST(COALESCE(year_field)) returns wrong value

parent 3f01c4fb
......@@ -525,5 +525,34 @@ Warnings:
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 93
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
#
......@@ -278,6 +278,23 @@ SELECT * FROM t1 WHERE a=1993 and a=93;
EXPLAIN EXTENDED SELECT * FROM t1 WHERE a=1993 and a=93;
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 # End of 10.4 tests
--echo #
......@@ -2881,7 +2881,10 @@ class Field_year :public Field_tiny {
:Field_tiny(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
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
{
if (eq_def(from))
......
......@@ -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)
{
double value= val_real();
......
......@@ -1607,7 +1607,6 @@ class Item: public Value_source,
Item **ref, uint flags);
virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)= 0;
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_string(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool get_time(MYSQL_TIME *ltime)
......@@ -6476,9 +6475,13 @@ class Item_cache_int: public Item_cache
class Item_cache_year: public Item_cache_int
{
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)
{ 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)
{
DBUG_ASSERT(fixed == 1);
long daynr= (long) args[1]->val_int();
long year= (long) args[0]->val_int();
long days;
long year, days, daynr= (long) args[1]->val_int();
if (args[0]->null_value || args[1]->null_value ||
year < 0 || year > 9999 || daynr <= 0)
VYear vyear(args[0]);
if (vyear.is_null() || args[1]->null_value || vyear.truncated() || daynr <= 0)
goto err;
if (year < 100)
if ((year= (long) vyear.year()) < 100)
year= year_2000_handling(year);
days= calc_daynr(year,1,1) + daynr - 1;
if (get_date_from_daynr(days, &ltime->year, &ltime->month, &ltime->day))
goto err;
......
......@@ -42,6 +42,7 @@ Type_handler_olddecimal type_handler_olddecimal;
Type_handler_newdecimal type_handler_newdecimal;
Type_handler_year type_handler_year;
Type_handler_year type_handler_year2;
Type_handler_time type_handler_time;
Type_handler_date type_handler_date;
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)
{
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 *
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 *
......@@ -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,
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 @@ class Sec6_add
};
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
{
protected:
......@@ -3323,6 +3378,9 @@ class Type_handler_year: public Type_handler_int_result
uint32 flags) 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_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_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_date type_handler_date;
extern MYSQL_PLUGIN_IMPORT Type_handler_time type_handler_time;
......
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