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);
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
#
......@@ -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 #
......@@ -1183,6 +1183,16 @@ class Item: public Value_source,
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
......
......@@ -784,6 +784,16 @@ class Item_func_hybrid_field_type: public Item_hybrid_func
@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
......
......@@ -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))
{ }
......
......@@ -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:
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
{
......
/* 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
......
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