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
|
||||
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 @@ public:
|
||||
: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))
|
||||
|
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)
|
||||
{
|
||||
double value= val_real();
|
||||
|
@ -1607,7 +1607,6 @@ public:
|
||||
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 @@ public:
|
||||
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, <ime->year, <ime->month, <ime->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 @@ 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
|
||||
{
|
||||
protected:
|
||||
@ -3323,6 +3378,9 @@ public:
|
||||
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;
|
||||
|
Reference in New Issue
Block a user