Commit 563efece authored by Alexander Barkov's avatar Alexander Barkov

MDEV-17607 DATE(COALESCE(year_column)) returns a wrong result

C++ does not guarantee the order of parameter evaluation.
It was wrong to pass item->val_int() and item->null_value
at the same time to any function or constructor.
Adding a new helper class Longlong_null, and new methods
Item::to_longlong_null() and Item_func_hybrid_field_type::to_longlong_null_op(),
which make sure to properly call val_int()/int_op() and test null_value.
Reorganizing the rest of the code accordingly.
parent 2feac61e
...@@ -560,5 +560,14 @@ SELECT MAKEDATE(18446744073709551615, 1); ...@@ -560,5 +560,14 @@ SELECT MAKEDATE(18446744073709551615, 1);
MAKEDATE(18446744073709551615, 1) MAKEDATE(18446744073709551615, 1)
NULL 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 # End of 10.4 tests
# #
...@@ -299,6 +299,15 @@ DROP TABLE t1; ...@@ -299,6 +299,15 @@ DROP TABLE t1;
--echo # --echo #
SELECT MAKEDATE(18446744073709551615, 1); 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 #
--echo # End of 10.4 tests --echo # End of 10.4 tests
--echo # --echo #
...@@ -1183,6 +1183,16 @@ class Item: public Value_source, ...@@ -1183,6 +1183,16 @@ class Item: public Value_source,
If value is not null null_value flag will be reset to FALSE. If value is not null null_value flag will be reset to FALSE.
*/ */
virtual longlong val_int()=0; 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). Get a value for CAST(x AS SIGNED).
Too large positive unsigned integer values are converted Too large positive unsigned integer values are converted
......
...@@ -784,6 +784,16 @@ class Item_func_hybrid_field_type: public Item_hybrid_func ...@@ -784,6 +784,16 @@ class Item_func_hybrid_field_type: public Item_hybrid_func
@return The result of the operation. @return The result of the operation.
*/ */
virtual longlong int_op()= 0; 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 @brief Performs the operation that this functions implements when the
......
...@@ -326,14 +326,13 @@ uint Year::year_precision(const Item *item) const ...@@ -326,14 +326,13 @@ uint Year::year_precision(const Item *item) const
VYear::VYear(Item *item) VYear::VYear(Item *item)
:Year_null(Year(item->val_int(), item->unsigned_flag, :Year_null(item->to_longlong_null(), item->unsigned_flag, year_precision(item))
year_precision(item)), item->null_value)
{ } { }
VYear_op::VYear_op(Item_func_hybrid_field_type *item) VYear_op::VYear_op(Item_func_hybrid_field_type *item)
:Year_null(Year(item->int_op(), item->unsigned_flag, :Year_null(item->to_longlong_null_op(), item->unsigned_flag,
year_precision(item)), item->null_value) year_precision(item))
{ } { }
......
...@@ -412,16 +412,13 @@ class Year ...@@ -412,16 +412,13 @@ class Year
}; };
class Year_null: public Year class Year_null: public Year, public Null_flag
{ {
protected:
bool m_is_null;
public: public:
Year_null(const Year &other, bool is_null) Year_null(const Longlong_null &nr, bool unsigned_flag, uint length)
:Year(is_null ? Year() : other), :Year(nr.is_null() ? 0 : nr.value(), unsigned_flag, length),
m_is_null(is_null) 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, bool to_mysql_time_with_warn(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate,
const char *field_name) const const char *field_name) const
{ {
......
/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. /* Copyright (c) 2018, MariaDB
Copyright (c) 2011, 2016, MariaDB
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
...@@ -18,17 +17,44 @@ ...@@ -18,17 +17,44 @@
#define SQL_TYPE_INT_INCLUDED #define SQL_TYPE_INT_INCLUDED
// A longlong/ulonglong hybrid. Good to store results of val_int(). class Null_flag
class Longlong_hybrid {
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: protected:
longlong m_value; 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; bool m_unsigned;
public: public:
Longlong_hybrid(longlong nr, bool unsigned_flag) 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 is_unsigned() const { return m_unsigned; }
bool neg() const { return m_value < 0 && !m_unsigned; } bool neg() const { return m_value < 0 && !m_unsigned; }
ulonglong abs() const ulonglong abs() const
......
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