1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00

MDEV-17216 Assertion `!dt->fraction_remainder(decimals())' failed in Field_temporal_with_date::store_TIME_with_warning

The problem happened because {{Field_xxx::store(longlong nr, bool unsigned_val)}} erroneously passed {{unsigned_flag}} to the {{usec}} parameter of this constructor:
{code:cpp}
Datetime(int *warn, longlong sec, ulong usec, date_conv_mode_t flags)
{code}

1. Changing Time and Datetime constructors to accept data as Sec6 rather than as
longlong/double/my_decimal, so it's not possible to do such mistakes
in the future. Additional good effect of these changes:
- This reduced some amount of similar code (minus ~35 lines).
- The code now does not rely on the fact that "unsigned_flag" is
   not important inside Datetime().
  The constructor always gets all three parts: sign, integer part,
  fractional part. The simple the better.

2. Fixing Field_xxx::store() to use the new Datetime constructor format.
   This change actually fixes the problem.

3. Adding "explicit" keyword to all Sec6 constructors,
to avoid automatic hidden conversion from double/my_decimal to Sec6,
as well as from longlong/ulonglong through double to Sec6.

4. Change#1 caused (as a dependency) changes in a few places
   with code like this:

  bool neg= nr < 0 && !unsigned_val;
  ulonglong value= m_neg ? (ulonglong) -nr : (ulonglong) nr;

These fragments relied on a non-standard behavior with
the operator "minus" applied to the lowest possible negative
signed long long value. This can lead to different results
depending on the platform and compilation flags.
We have fixed such bugs a few times already.
So instead of modifying the old wrong code to a new wrong code,
replacing all such fragments to use Longlong_hybrid,
which correctly handles this special case with -LONGLONG_MIN
in its method abs().
This also reduced the amount of similar code
(1 or 2 new lines instead 3 old lines in all 6 such fragments).

5. Removing ErrConvInteger(longlong nr, bool unsigned_flag= false)
   and adding ErrConvInteger(Longlong_hybrid) instead, to encourage
   use of safe Longlong_hybrid instead of unsafe pairs nr+neg.

6. Removing unused ErrConvInteger from Item_cache_temporal::get_date()
This commit is contained in:
Alexander Barkov
2018-10-09 12:02:35 +04:00
parent f4cdf90d73
commit 5646c43159
15 changed files with 146 additions and 106 deletions

View File

@ -225,10 +225,10 @@ protected:
bool m_truncated; // Indicates if the constructor truncated the value
void make_from_decimal(const my_decimal *d);
void make_from_double(double d);
void make_from_int(longlong nr, bool unsigned_val)
void make_from_int(const Longlong_hybrid &nr)
{
m_neg= nr < 0 && !unsigned_val;
m_sec= m_neg ? (ulonglong) -nr : (ulonglong) nr;
m_neg= nr.neg();
m_sec= nr.abs();
m_usec= 0;
m_truncated= false;
}
@ -238,20 +238,21 @@ protected:
}
Sec6() { }
public:
Sec6(bool neg, ulonglong nr, ulong frac)
:m_sec(nr), m_usec(frac), m_neg(neg), m_truncated(false)
{ }
Sec6(double nr)
explicit Sec6(double nr)
{
make_from_double(nr);
}
Sec6(const my_decimal *d)
explicit Sec6(const my_decimal *d)
{
make_from_decimal(d);
}
Sec6(longlong nr, bool unsigned_val)
explicit Sec6(const Longlong_hybrid &nr)
{
make_from_int(nr, unsigned_val);
make_from_int(nr);
}
explicit Sec6(longlong nr, bool unsigned_val)
{
make_from_int(Longlong_hybrid(nr, unsigned_val));
}
bool neg() const { return m_neg; }
bool truncated() const { return m_truncated; }
@ -396,9 +397,10 @@ protected:
bool to_mysql_time_with_warn(THD *thd, MYSQL_TIME *to, date_mode_t fuzzydate,
const char *field_name) const
{
longlong value= m_year * 10000; // Make it YYYYMMDD
const ErrConvInteger str(value, true);
Sec6 sec(false, value, 0);
// Make it YYYYMMDD
Longlong_hybrid value(static_cast<ulonglong>(m_year) * 10000, true);
const ErrConvInteger str(value);
Sec6 sec(value);
return sec.convert_to_mysql_time(thd, to, fuzzydate, &str, field_name);
}
uint year_precision(const Item *item) const;
@ -846,14 +848,8 @@ public:
time_type= MYSQL_TIMESTAMP_NONE;
xxx_to_time_result_to_valid_value(thd, warn, opt);
}
Time(THD *thd, int *warn, double nr)
:Time(thd, warn, Sec6(nr), Options())
{ }
Time(THD *thd, int *warn, longlong nr, bool unsigned_val)
:Time(thd, warn, Sec6(nr, unsigned_val), Options())
{ }
Time(THD *thd, int *warn, const my_decimal *d)
:Time(thd, warn, Sec6(d), Options())
Time(THD *thd, int *warn, const Sec6 &nr)
:Time(thd, warn, nr, Options())
{ }
Time(THD *thd, Item *item, const Options opt, uint dec)
@ -873,25 +869,11 @@ public:
{
trunc(dec);
}
Time(THD *thd, int *warn, double nr, uint dec)
Time(THD *thd, int *warn, const Sec6 &nr, uint dec)
:Time(thd, warn, nr)
{
trunc(dec);
}
Time(THD *thd, int *warn, longlong nr, bool unsigned_val, uint dec)
:Time(thd, warn, nr, unsigned_val)
{
/*
Decimal digit truncation is needed here in case if nr was out
of the supported TIME range, so "this" was set to '838:59:59.999999'.
*/
trunc(dec);
}
Time(THD *thd, int *warn, const my_decimal *d, uint dec)
:Time(thd, warn, d)
{
trunc(dec);
}
static date_mode_t flags_for_get_date()
{ return TIME_TIME_ONLY | TIME_INVALID_DATES; }
@ -1242,56 +1224,33 @@ public:
date_to_datetime_if_needed();
DBUG_ASSERT(is_valid_value_slow());
}
Datetime(int *warn, double nr, date_mode_t flags)
:Temporal_with_date(warn, Sec6(nr), flags)
{
date_to_datetime_if_needed();
DBUG_ASSERT(is_valid_value_slow());
}
Datetime(int *warn, const my_decimal *d, date_mode_t flags)
:Temporal_with_date(warn, Sec6(d), flags)
{
date_to_datetime_if_needed();
DBUG_ASSERT(is_valid_value_slow());
}
/*
Create a Datime object from a longlong number.
Note, unlike in Time(), we don't need an "unsigned_val" here,
as it's not important if overflow happened because
of a negative number, or because of a huge positive number.
*/
Datetime(int *warn, longlong sec, ulong usec, date_mode_t flags)
:Temporal_with_date(warn, Sec6(false, (ulonglong) sec, usec), flags)
Datetime(int *warn, const Sec6 &nr, date_mode_t flags)
:Temporal_with_date(warn, nr, flags)
{
date_to_datetime_if_needed();
DBUG_ASSERT(is_valid_value_slow());
}
Datetime(THD *thd, Item *item, date_mode_t flags, uint dec)
:Temporal_with_date(Datetime(thd, item, flags))
:Datetime(thd, item, flags)
{
trunc(dec);
}
Datetime(MYSQL_TIME_STATUS *status,
const char *str, size_t len, CHARSET_INFO *cs,
date_mode_t fuzzydate, uint dec)
:Temporal_with_date(Datetime(status, str, len, cs, fuzzydate))
:Datetime(status, str, len, cs, fuzzydate)
{
trunc(dec);
}
Datetime(int *warn, double nr, date_mode_t fuzzydate, uint dec)
:Temporal_with_date(Datetime(warn, nr, fuzzydate))
{
trunc(dec);
}
Datetime(int *warn, const my_decimal *d, date_mode_t fuzzydate, uint dec)
:Temporal_with_date(Datetime(warn, d, fuzzydate))
Datetime(int *warn, const Sec6 &nr, date_mode_t fuzzydate, uint dec)
:Datetime(warn, nr, fuzzydate)
{
trunc(dec);
}
Datetime(THD *thd, int *warn, const MYSQL_TIME *from,
date_mode_t fuzzydate, uint dec)
:Temporal_with_date(Datetime(thd, warn, from, fuzzydate))
:Datetime(thd, warn, from, fuzzydate)
{
trunc(dec);
}