summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/main/type_year.result29
-rw-r--r--mysql-test/main/type_year.test17
-rw-r--r--sql/field.h5
-rw-r--r--sql/item.cc20
-rw-r--r--sql/item.h9
-rw-r--r--sql/item_timefunc.cc11
-rw-r--r--sql/sql_type.cc57
-rw-r--r--sql/sql_type.h59
8 files changed, 174 insertions, 33 deletions
diff --git a/mysql-test/main/type_year.result b/mysql-test/main/type_year.result
index 2d04853a0b2..3fdca099a94 100644
--- a/mysql-test/main/type_year.result
+++ b/mysql-test/main/type_year.result
@@ -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
#
diff --git a/mysql-test/main/type_year.test b/mysql-test/main/type_year.test
index f7354ebf7c6..657940a62a8 100644
--- a/mysql-test/main/type_year.test
+++ b/mysql-test/main/type_year.test
@@ -279,5 +279,22 @@ 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 #
diff --git a/sql/field.h b/sql/field.h
index 909ee218f71..2b2dd88ab17 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -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))
diff --git a/sql/item.cc b/sql/item.cc
index 3381d6daaed..8b962d1706d 100644
--- a/sql/item.cc
+++ b/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();
diff --git a/sql/item.h b/sql/item.h
index 06fd7df2549..a96406fa0cd 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -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);
+ }
};
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index abb143add8d..84db154566a 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -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;
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index cdcd37f1006..cb2a9308d82 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -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);
+}
+
+
/***************************************************************************/
diff --git a/sql/sql_type.h b/sql/sql_type.h
index c8bc64f72d2..a842983a137 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -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;