diff --git a/mysql-test/main/type_year.result b/mysql-test/main/type_year.result index 6485a825f5b..3770e9fc316 100644 --- a/mysql-test/main/type_year.result +++ b/mysql-test/main/type_year.result @@ -560,5 +560,14 @@ SELECT MAKEDATE(18446744073709551615, 1); MAKEDATE(18446744073709551615, 1) NULL # +# MDEV-17607 DATE(COALESCE(year_column)) returns a wrong result +# +CREATE TABLE t1 (a YEAR); +INSERT INTO t1 VALUES (NULL); +SELECT COALESCE(a), DATE(COALESCE(a)) FROM t1; +COALESCE(a) DATE(COALESCE(a)) +NULL NULL +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 cc02acd8fbb..a6f6d20d784 100644 --- a/mysql-test/main/type_year.test +++ b/mysql-test/main/type_year.test @@ -299,6 +299,15 @@ DROP TABLE t1; --echo # SELECT MAKEDATE(18446744073709551615, 1); +--echo # +--echo # MDEV-17607 DATE(COALESCE(year_column)) returns a wrong result +--echo # + +CREATE TABLE t1 (a YEAR); +INSERT INTO t1 VALUES (NULL); +SELECT COALESCE(a), DATE(COALESCE(a)) FROM t1; +DROP TABLE t1; + --echo # --echo # End of 10.4 tests --echo # diff --git a/sql/item.h b/sql/item.h index 94fe16bef3e..d15dbade60f 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1183,6 +1183,16 @@ public: If value is not null null_value flag will be reset to FALSE. */ virtual longlong val_int()=0; + Longlong_null to_longlong_null() + { + longlong nr= val_int(); + /* + C++ does not guarantee the order of parameter evaluation, + so to make sure "null_value" is passed to the constructor + after the val_int() call, val_int() is caled on a separate line. + */ + return Longlong_null(nr, null_value); + } /** Get a value for CAST(x AS SIGNED). Too large positive unsigned integer values are converted diff --git a/sql/item_func.h b/sql/item_func.h index 4d73bf8ce47..602b13fad7a 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -784,6 +784,16 @@ public: @return The result of the operation. */ virtual longlong int_op()= 0; + Longlong_null to_longlong_null_op() + { + longlong nr= int_op(); + /* + C++ does not guarantee the order of parameter evaluation, + so to make sure "null_value" is passed to the constructor + after the int_op() call, int_op() is caled on a separate line. + */ + return Longlong_null(nr, null_value); + } /** @brief Performs the operation that this functions implements when the diff --git a/sql/sql_type.cc b/sql/sql_type.cc index c6e22776d27..23fa678e7cf 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -326,14 +326,13 @@ uint Year::year_precision(const Item *item) const VYear::VYear(Item *item) - :Year_null(Year(item->val_int(), item->unsigned_flag, - year_precision(item)), item->null_value) + :Year_null(item->to_longlong_null(), item->unsigned_flag, year_precision(item)) { } 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) + :Year_null(item->to_longlong_null_op(), item->unsigned_flag, + year_precision(item)) { } diff --git a/sql/sql_type.h b/sql/sql_type.h index 8356b08ab67..fd86102c504 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -412,16 +412,13 @@ public: }; -class Year_null: public Year +class Year_null: public Year, public Null_flag { -protected: - bool m_is_null; public: - Year_null(const Year &other, bool is_null) - :Year(is_null ? Year() : other), - m_is_null(is_null) + Year_null(const Longlong_null &nr, bool unsigned_flag, uint length) + :Year(nr.is_null() ? 0 : nr.value(), unsigned_flag, length), + Null_flag(nr.is_null()) { } - bool is_null() const { return m_is_null; } bool to_mysql_time_with_warn(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate, const char *field_name) const { diff --git a/sql/sql_type_int.h b/sql/sql_type_int.h index 1eda5651df5..6c88222fed0 100644 --- a/sql/sql_type_int.h +++ b/sql/sql_type_int.h @@ -1,5 +1,4 @@ -/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. - Copyright (c) 2011, 2016, MariaDB +/* Copyright (c) 2018, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,17 +17,44 @@ #define SQL_TYPE_INT_INCLUDED -// A longlong/ulonglong hybrid. Good to store results of val_int(). -class Longlong_hybrid +class Null_flag +{ +protected: + bool m_is_null; +public: + bool is_null() const { return m_is_null; } + Null_flag(bool is_null) :m_is_null(is_null) { } +}; + + +class Longlong { protected: longlong m_value; +public: + longlong value() const { return m_value; } + Longlong(longlong nr) :m_value(nr) { } +}; + + +class Longlong_null: public Longlong, public Null_flag +{ +public: + Longlong_null(longlong nr, bool is_null) + :Longlong(nr), Null_flag(is_null) + { } +}; + + +// A longlong/ulonglong hybrid. Good to store results of val_int(). +class Longlong_hybrid: public Longlong +{ +protected: bool m_unsigned; public: Longlong_hybrid(longlong nr, bool unsigned_flag) - :m_value(nr), m_unsigned(unsigned_flag) + :Longlong(nr), m_unsigned(unsigned_flag) { } - longlong value() const { return m_value; } bool is_unsigned() const { return m_unsigned; } bool neg() const { return m_value < 0 && !m_unsigned; } ulonglong abs() const