commit 2e5d86f49e7ee538806fba68dc8c960d6acdd483
Author: Alexander Barkov <bar@mariadb.com>
Date:   Sat Aug 22 15:22:20 2020 +0400

    MDEV-23537 Comparison with temporal columns is slow in MariaDB
    
    Implementing methods:
    - Field::val_time_packed()
    - Field::val_datetime_packed()
    - Item_field::val_datetime_packed(THD *thd);
    - Item_field::val_time_packed(THD *thd);
    to give a faster access to temporal packed longlong representation of a Field,
    which is used in temporal Arg_comparator's to DATE, TIME, DATETIME data types.
    
    The same idea is used in MySQL-5.6+.
    
    This improves performance.

diff --git a/sql/field.cc b/sql/field.cc
index 65dc7f768a3..bac5dd95b5a 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -2328,6 +2328,33 @@ bool Field::get_date(MYSQL_TIME *to, date_mode_t mode)
   return !t->is_valid_temporal();
 }
 
+
+longlong Field::val_datetime_packed(THD *thd)
+{
+  MYSQL_TIME ltime, tmp;
+  if (get_date(&ltime, Datetime::Options_cmp(thd)))
+    return 0;
+  if (ltime.time_type != MYSQL_TIMESTAMP_TIME)
+    return pack_time(&ltime);
+  if (time_to_datetime_with_warn(thd, &ltime, &tmp, TIME_CONV_NONE))
+    return 0;
+  return pack_time(&tmp);
+}
+
+
+longlong Field::val_time_packed(THD *thd)
+{
+  MYSQL_TIME ltime;
+  Time::Options_cmp opt(thd);
+  if (get_date(&ltime, opt))
+    return 0;
+  if (ltime.time_type == MYSQL_TIMESTAMP_TIME)
+    return pack_time(&ltime);
+  // Conversion from DATETIME or DATE to TIME is needed
+  return Time(thd, &ltime, opt).to_packed();
+}
+
+
 /**
   This is called when storing a date in a string.
 
@@ -6272,6 +6299,17 @@ bool Field_timef::get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
   return false;
 }
 
+
+longlong Field_timef::val_time_packed(THD *thd)
+{
+  DBUG_ASSERT(marked_for_read());
+  longlong tmp= my_time_packed_from_binary(ptr, dec);
+  MYSQL_TIME ltime;
+  TIME_from_longlong_time_packed(&ltime, tmp);
+  return pack_time(&ltime);
+}
+
+
 int Field_timef::store_native(const Native &value)
 {
   DBUG_ASSERT(value.length() == my_time_binary_length(dec));
@@ -6673,6 +6711,14 @@ bool Field_newdate::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
 }
 
 
+longlong Field_newdate::val_datetime_packed(THD *thd)
+{
+  MYSQL_TIME ltime;
+  Field_newdate::get_date(&ltime, date_mode_t(0));
+  return pack_time(&ltime);
+}
+
+
 int Field_newdate::cmp(const uchar *a_ptr, const uchar *b_ptr)
 {
   uint32 a,b;
@@ -7008,6 +7054,16 @@ bool Field_datetimef::get_TIME(MYSQL_TIME *ltime, const uchar *pos,
   return validate_MMDD(tmp, ltime->month, ltime->day, fuzzydate);
 }
 
+longlong Field_datetimef::val_datetime_packed(THD *thd)
+{
+  DBUG_ASSERT(marked_for_read());
+  longlong tmp= my_datetime_packed_from_binary(ptr, dec);
+  MYSQL_TIME ltime;
+  TIME_from_longlong_datetime_packed(&ltime, tmp);
+  return pack_time(&ltime);
+}
+
+
 /****************************************************************************
 ** string type
 ** A string may be varchar or binary
diff --git a/sql/field.h b/sql/field.h
index 0ee4aca5f69..b3bc2d4dbea 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -1401,6 +1401,8 @@ class Field: public Value_source
   void copy_from_tmp(int offset);
   uint fill_cache_field(struct st_cache_field *copy);
   virtual bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
+  virtual longlong val_datetime_packed(THD *thd);
+  virtual longlong val_time_packed(THD *thd);
   virtual TYPELIB *get_typelib() const { return NULL; }
   virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; }
   virtual CHARSET_INFO *charset_for_protocol(void) const
@@ -3167,6 +3169,7 @@ class Field_newdate :public Field_date_common
   void sql_type(String &str) const;
   bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
   { return Field_newdate::get_TIME(ltime, ptr, fuzzydate); }
+  longlong val_datetime_packed(THD *thd);
   uint size_of() const { return sizeof(*this); }
   Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item);
 };
@@ -3336,6 +3339,7 @@ class Field_timef :public Field_time_with_dec {
   }
   int reset();
   bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate);
+  longlong val_time_packed(THD *thd);
   int store_native(const Native &value);
   bool val_native(Native *to);
   uint size_of() const { return sizeof(*this); }
@@ -3495,6 +3499,7 @@ class Field_datetimef :public Field_datetime_with_dec {
   int reset();
   bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate)
   { return Field_datetimef::get_TIME(ltime, ptr, fuzzydate); }
+  longlong val_datetime_packed(THD *thd);
   uint size_of() const { return sizeof(*this); }
 };
 
diff --git a/sql/item.cc b/sql/item.cc
index 312effeb6c1..22a8cb169b3 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -3246,6 +3246,24 @@ bool Item_field::val_native_result(THD *thd, Native *to)
 }
 
 
+longlong Item_field::val_datetime_packed(THD *thd)
+{
+  DBUG_ASSERT(fixed == 1);
+  if ((null_value= field->is_null()))
+    return 0;
+  return field->val_datetime_packed(thd);
+}
+
+
+longlong Item_field::val_time_packed(THD *thd)
+{
+  DBUG_ASSERT(fixed == 1);
+  if ((null_value= field->is_null()))
+    return 0;
+  return field->val_time_packed(thd);
+}
+
+
 void Item_field::save_result(Field *to)
 {
   save_field_in_field(result_field, &null_value, to, TRUE);
diff --git a/sql/item.h b/sql/item.h
index aced2ec5f0d..95ca06ac211 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -3418,6 +3418,8 @@ class Item_field :public Item_ident,
   longlong val_int_endpoint(bool left_endp, bool *incl_endp);
   bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate);
   bool get_date_result(THD *thd, MYSQL_TIME *ltime,date_mode_t fuzzydate);
+  longlong val_datetime_packed(THD *thd);
+  longlong val_time_packed(THD *thd);
   bool is_null() { return field->is_null(); }
   void update_null_value();
   void update_table_bitmaps()
diff --git a/sql/sql_type.h b/sql/sql_type.h
index 553d204d806..8726208b788 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -1542,6 +1542,13 @@ class Time: public Temporal
   Time(int *warn, bool neg, ulonglong hour, uint minute, const Sec6 &second);
   Time() { time_type= MYSQL_TIMESTAMP_NONE; }
   Time(const Native &native);
+  Time(THD *thd, const MYSQL_TIME *ltime, const Options opt)
+  {
+    *(static_cast<MYSQL_TIME*>(this))= *ltime;
+    DBUG_ASSERT(is_valid_temporal());
+    int warn= 0;
+    valid_MYSQL_TIME_to_valid_value(thd, &warn, opt);
+  }
   Time(Item *item)
    :Time(current_thd, item)
   { }
