diff options
-rw-r--r-- | mysql-test/main/type_year.result | 29 | ||||
-rw-r--r-- | mysql-test/main/type_year.test | 17 | ||||
-rw-r--r-- | sql/field.h | 5 | ||||
-rw-r--r-- | sql/item.cc | 20 | ||||
-rw-r--r-- | sql/item.h | 9 | ||||
-rw-r--r-- | sql/item_timefunc.cc | 11 | ||||
-rw-r--r-- | sql/sql_type.cc | 57 | ||||
-rw-r--r-- | sql/sql_type.h | 59 |
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, <ime->year, <ime->month, <ime->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; |