← Back to team overview

maria-developers team mailing list archive

Re: merge for MySQL56 FSP data types

 

Hi Sergei,

Thanks for review.

This is a new version of the patch. I tried to address most of your suggestions.

Please see comments inline.

(I wrote some of them in a earlier letter, but will repeat just in case)


Thanks.

On 06/26/2013 11:11 PM, Sergei Golubchik wrote:
Hi, Alexander!

On Jun 19, Alexander Barkov wrote:
Hi Serg,

Please review the patch merging MySQL-5.6 fractional second precision
enabled data types.

Thanks.


Here you are, my comments are below:

=== modified file 'include/my_time.h'
--- include/my_time.h	2013-05-24 15:09:59 +0000
+++ include/my_time.h	2013-06-19 11:13:13 +0000
@@ -107,10 +107,37 @@ ulonglong TIME_to_ulonglong_time(const M
  ulonglong TIME_to_ulonglong(const MYSQL_TIME *);
  double TIME_to_double(const MYSQL_TIME *my_time);

+/** MySQL56 routines and macros **/
+#define MY_PACKED_TIME_GET_INT_PART(x)     ((x) >> 24)
+#define MY_PACKED_TIME_GET_FRAC_PART(x)    ((x) % (1LL << 24))
+#define MY_PACKED_TIME_MAKE(i, f)          ((((longlong) (i)) << 24) + (f))
+#define MY_PACKED_TIME_MAKE_INT(i)         ((((longlong) (i)) << 24))
+
+longlong TIME_to_longlong_datetime_packed(const MYSQL_TIME *);
+longlong TIME_to_longlong_time_packed(const MYSQL_TIME *);
+
+void TIME_from_longlong_datetime_packed(MYSQL_TIME *ltime, longlong nr);
+void TIME_from_longlong_time_packed(MYSQL_TIME *ltime, longlong nr);
+
+void my_datetime_packed_to_binary(longlong nr, uchar *ptr, uint dec);
+longlong my_datetime_packed_from_binary(const uchar *ptr, uint dec);
+uint my_datetime_binary_length(uint dec);
+
+void my_time_packed_to_binary(longlong nr, uchar *ptr, uint dec);
+longlong my_time_packed_from_binary(const uchar *ptr, uint dec);
+uint my_time_binary_length(uint dec);
+
+void my_timestamp_to_binary(const struct timeval *tm, uchar *ptr, uint dec);
+void my_timestamp_from_binary(struct timeval *tm, const uchar *ptr, uint dec);
+uint my_timestamp_binary_length(uint dec);
+/** End of MySQL routines and macros **/

I don't think we need these macros and related functions in include/ and
sql-commont (that is, in the client). These belong in sql/
as they're only used for reading MySQL data files and for RBR.

if you'd like you can even put them in a separate file, sql/compat65.cc

Good idea. Moved to sql/compat56.cc and sql/compat56.h



+
  longlong pack_time(MYSQL_TIME *my_time);
  MYSQL_TIME *unpack_time(longlong packed, MYSQL_TIME *my_time);

  int check_time_range(struct st_mysql_time *my_time, uint dec, int *warning);
+my_bool check_datetime_range(const MYSQL_TIME *ltime);
+

  long calc_daynr(uint year,uint month,uint day);
  uint calc_days_in_year(uint year);
=== modified file 'include/mysql_com.h'
--- include/mysql_com.h	2013-06-06 19:32:29 +0000
+++ include/mysql_com.h	2013-06-18 10:35:35 +0000
@@ -400,6 +400,9 @@ enum enum_field_types { MYSQL_TYPE_DECIM
  			MYSQL_TYPE_DATETIME, MYSQL_TYPE_YEAR,
  			MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR,
  			MYSQL_TYPE_BIT,
+			MYSQL_TYPE_TIMESTAMP2,
+			MYSQL_TYPE_DATETIME2,
+			MYSQL_TYPE_TIME2,

Add a comment about these types please.


Done.


                          MYSQL_TYPE_NEWDECIMAL=246,
  			MYSQL_TYPE_ENUM=247,
  			MYSQL_TYPE_SET=248,
=== added file 'mysql-test/t/type_temporal_mysql56.test'
--- mysql-test/t/type_temporal_mysql56.test	1970-01-01 00:00:00 +0000
+++ mysql-test/t/type_temporal_mysql56.test	2013-06-19 09:01:02 +0000
@@ -0,0 +1,23 @@
+
+let $MYSQLD_DATADIR= `select @@datadir`;
+--copy_file std_data/mysql56time.frm $MYSQLD_DATADIR/test/mysql56time.frm
+--copy_file std_data/mysql56time.MYD $MYSQLD_DATADIR/test/mysql56time.MYD
+--copy_file std_data/mysql56time.MYI $MYSQLD_DATADIR/test/mysql56time.MYI
+SHOW CREATE TABLE mysql56time;
+--query_vertical SELECT * FROM mysql56time;

generally, you either start mysqltest command with -- (dash-dash)
OR you end it with a semicolon.
If you do both - as above - you end up with two semicolons,
see the result file

Fixed.



+DROP TABLE mysql56time;
+
+--copy_file std_data/mysql56datetime.frm $MYSQLD_DATADIR/test/mysql56datetime.frm
+--copy_file std_data/mysql56datetime.MYD $MYSQLD_DATADIR/test/mysql56datetime.MYD
+--copy_file std_data/mysql56datetime.MYI $MYSQLD_DATADIR/test/mysql56datetime.MYI
+SHOW CREATE TABLE mysql56datetime;
+--query_vertical SELECT * FROM mysql56datetime;
+DROP TABLE mysql56datetime;
+
+--copy_file std_data/mysql56timestamp.frm $MYSQLD_DATADIR/test/mysql56timestamp.frm
+--copy_file std_data/mysql56timestamp.MYD $MYSQLD_DATADIR/test/mysql56timestamp.MYD
+--copy_file std_data/mysql56timestamp.MYI $MYSQLD_DATADIR/test/mysql56timestamp.MYI
+SET TIME_ZONE='+00:00';
+SHOW CREATE TABLE mysql56timestamp;
+--query_vertical SELECT * FROM mysql56timestamp;
+DROP TABLE mysql56timestamp;
=== modified file 'sql-common/my_time.c'
--- sql-common/my_time.c	2013-04-07 12:00:16 +0000
+++ sql-common/my_time.c	2013-06-19 11:59:45 +0000
@@ -1346,6 +1396,432 @@ ulonglong TIME_to_ulonglong(const MYSQL_
    return 0;
  }

+
+/*** MySQL56 TIME low-level memory and disk representation routines ***/

As I wrote above, I'd prefer to remove them from the client library
and move to sql/


Done.

+
+/*
+  In-memory format:
+
+   1  bit sign          (Used for sign, when on disk)
+   1  bit unused        (Reserved for wider hour range, e.g. for intervals)
+   10 bit hour          (0-836)
+   6  bit minute        (0-59)
+   6  bit second        (0-59)
+  24  bits microseconds (0-999999)
+
+ Total: 48 bits = 6 bytes
+   Suhhhhhh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
+*/
+
+
+/**
+  Convert time value to MySQL56 numeric packed representation.
+
+  @param    ltime   The value to convert.
+  @return           Numeric packed representation.
+*/
+longlong TIME_to_longlong_time_packed(const MYSQL_TIME *ltime)
+{
+  /* If month is 0, we mix day with hours: "1 00:10:10" -> "24:00:10" */
+  long hms= (((ltime->month ? 0 : ltime->day * 24) + ltime->hour) << 12) |
+            (ltime->minute << 6) | ltime->second;
+  longlong tmp= MY_PACKED_TIME_MAKE(hms, ltime->second_part);
+  return ltime->neg ? -tmp : tmp;
+}
+
+
+
+/**
+  Convert MySQL56 time packed numeric representation to time.
+
+  @param  OUT ltime  The MYSQL_TIME variable to set.
+  @param      tmp    The packed numeric representation.
+*/
+void TIME_from_longlong_time_packed(MYSQL_TIME *ltime, longlong tmp)
+{
+  long hms;
+  if ((ltime->neg= (tmp < 0)))
+    tmp= -tmp;
+  hms= MY_PACKED_TIME_GET_INT_PART(tmp);
+  ltime->year=   (uint) 0;
+  ltime->month=  (uint) 0;
+  ltime->day=    (uint) 0;
+  ltime->hour=   (uint) (hms >> 12) % (1 << 10); /* 10 bits starting at 12th */
+  ltime->minute= (uint) (hms >> 6)  % (1 << 6);  /* 6 bits starting at 6th   */
+  ltime->second= (uint)  hms        % (1 << 6);  /* 6 bits starting at 0th   */
+  ltime->second_part= MY_PACKED_TIME_GET_FRAC_PART(tmp);
+  ltime->time_type= MYSQL_TIMESTAMP_TIME;
+}
+
+
+/**
+  Calculate binary size of MySQL56 packed numeric time representation.
+
+  @param   dec   Precision.
+*/
+uint my_time_binary_length(uint dec)
+{
+  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+  return 3 + (dec + 1) / 2;
+}
+
+
+/*
+  On disk we convert from signed representation to unsigned
+  representation using TIMEF_OFS, so all values become binary comparable.
+*/
+#define TIMEF_OFS 0x800000000000LL
+#define TIMEF_INT_OFS 0x800000LL
+
+
+/**
+  Convert MySQL56 in-memory numeric time representation to on-disk representation
+
+  @param       nr   Value in packed numeric time format.
+  @param   OUT ptr  The buffer to put value at.
+  @param       dec  Precision.
+*/
+void my_time_packed_to_binary(longlong nr, uchar *ptr, uint dec)
+{
+  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+  /* Make sure the stored value was previously properly rounded or truncated */
+  DBUG_ASSERT((MY_PACKED_TIME_GET_FRAC_PART(nr) %
+              (int) log_10_int[TIME_SECOND_PART_DIGITS - dec]) == 0);
+
+  switch (dec)
+  {
+  case 0:
+  default:
+    mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
+    break;
+
+  case 1:
+  case 2:
+    mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
+    ptr[3]= (unsigned char) (char) (MY_PACKED_TIME_GET_FRAC_PART(nr) / 10000);
+    break;
+
+  case 4:
+  case 3:
+    mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
+    mi_int2store(ptr + 3, MY_PACKED_TIME_GET_FRAC_PART(nr) / 100);
+    break;
+
+  case 5:
+  case 6:
+    mi_int6store(ptr, nr + TIMEF_OFS);
+    break;
+  }
+}
+
+
+/**
+  Convert MySQL56 on-disk time representation to in-memory packed numeric
+  representation.
+
+  @param   ptr  The pointer to read the value at.
+  @param   dec  Precision.
+  @return       Packed numeric time representation.
+*/
+longlong my_time_packed_from_binary(const uchar *ptr, uint dec)
+{
+  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+
+  switch (dec)
+  {
+  case 0:
+  default:
+    {
+      longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
+      return MY_PACKED_TIME_MAKE_INT(intpart);
+    }
+  case 1:
+  case 2:
+    {
+      longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
+      int frac= (uint) ptr[3];
+      if (intpart < 0 && frac)
+      {
+        /*
+          Negative values are stored with reverse fractional part order,
+          for binary sort compatibility.
+
+            Disk value  intpart frac   Time value   Memory value
+            800000.00    0      0      00:00:00.00  0000000000.000000
+            7FFFFF.FF   -1      255   -00:00:00.01  FFFFFFFFFF.FFD8F0
+            7FFFFF.9D   -1      99    -00:00:00.99  FFFFFFFFFF.F0E4D0
+            7FFFFF.00   -1      0     -00:00:01.00  FFFFFFFFFF.000000
+            7FFFFE.FF   -1      255   -00:00:01.01  FFFFFFFFFE.FFD8F0
+            7FFFFE.F6   -2      246   -00:00:01.10  FFFFFFFFFE.FE7960
+
+            Formula to convert fractional part from disk format
+            (now stored in "frac" variable) to absolute value: "0x100 - frac".
+            To reconstruct in-memory value, we shift
+            to the next integer value and then substruct fractional part.
+        */
+        intpart++;    /* Shift to the next integer value */
+        frac-= 0x100; /* -(0x100 - frac) */
+      }
+      return MY_PACKED_TIME_MAKE(intpart, frac * 10000);
+    }
+
+  case 3:
+  case 4:
+    {
+      longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
+      int frac= mi_uint2korr(ptr + 3);
+      if (intpart < 0 && frac)
+      {
+        /*
+          Fix reverse fractional part order: "0x10000 - frac".
+          See comments for FSP=1 and FSP=2 above.
+        */
+        intpart++;      /* Shift to the next integer value */
+        frac-= 0x10000; /* -(0x10000-frac) */
+      }
+      return MY_PACKED_TIME_MAKE(intpart, frac * 100);
+    }
+
+  case 5:
+  case 6:
+    return ((longlong) mi_uint6korr(ptr)) - TIMEF_OFS;
+  }
+}
+
+
+/*** MySQL56 DATETIME low-level memory and disk representation routines ***/
+
+/*
+    1 bit  sign            (used when on disk)
+   17 bits year*13+month   (year 0-9999, month 0-12)
+    5 bits day             (0-31)
+    5 bits hour            (0-23)
+    6 bits minute          (0-59)
+    6 bits second          (0-59)
+   24 bits microseconds    (0-999999)
+
+   Total: 64 bits = 8 bytes
+
+   SYYYYYYY.YYYYYYYY.YYdddddh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
+*/
+
+/**
+  Convert datetime to MySQL56 packed numeric datetime representation.
+  @param ltime  The value to convert.
+  @return       Packed numeric representation of ltime.
+*/
+longlong TIME_to_longlong_datetime_packed(const MYSQL_TIME *ltime)
+{
+  longlong ymd= ((ltime->year * 13 + ltime->month) << 5) | ltime->day;
+  longlong hms= (ltime->hour << 12) | (ltime->minute << 6) | ltime->second;
+  longlong tmp= MY_PACKED_TIME_MAKE(((ymd << 17) | hms), ltime->second_part);
+  DBUG_ASSERT(!check_datetime_range(ltime)); /* Make sure no overflow */
+  return ltime->neg ? -tmp : tmp;
+}
+
+
+/**
+  Convert MySQL56 packed numeric datetime representation to MYSQL_TIME.
+  @param OUT  ltime The datetime variable to convert to.
+  @param      tmp   The packed numeric datetime value.
+*/
+void TIME_from_longlong_datetime_packed(MYSQL_TIME *ltime, longlong tmp)
+{
+  longlong ymd, hms;
+  longlong ymdhms, ym;
+  if ((ltime->neg= (tmp < 0)))
+    tmp= -tmp;
+
+  ltime->second_part= MY_PACKED_TIME_GET_FRAC_PART(tmp);
+  ymdhms= MY_PACKED_TIME_GET_INT_PART(tmp);
+
+  ymd= ymdhms >> 17;
+  ym= ymd >> 5;
+  hms= ymdhms % (1 << 17);
+
+  ltime->day= ymd % (1 << 5);
+  ltime->month= ym % 13;
+  ltime->year= ym / 13;
+
+  ltime->second= hms % (1 << 6);
+  ltime->minute= (hms >> 6) % (1 << 6);
+  ltime->hour= (hms >> 12);
+
+  ltime->time_type= MYSQL_TIMESTAMP_DATETIME;
+}
+
+
+/**
+  Calculate binary size of MySQL56 packed datetime representation.
+  @param dec  Precision.
+*/
+uint my_datetime_binary_length(uint dec)
+{
+  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+  return 5 + (dec + 1) / 2;
+}
+
+
+/*
+  On disk we store as unsigned number with DATETIMEF_INT_OFS offset,
+  for HA_KETYPE_BINARY compatibilty purposes.
+*/
+#define DATETIMEF_INT_OFS 0x8000000000LL
+
+
+/**
+  Convert MySQL56 on-disk datetime representation
+  to in-memory packed numeric representation.
+
+  @param ptr   The pointer to read value at.
+  @param dec   Precision.
+  @return      In-memory packed numeric datetime representation.
+*/
+longlong my_datetime_packed_from_binary(const uchar *ptr, uint dec)
+{
+  longlong intpart= mi_uint5korr(ptr) - DATETIMEF_INT_OFS;
+  int frac;
+  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+  switch (dec)
+  {
+  case 0:
+  default:
+    return MY_PACKED_TIME_MAKE_INT(intpart);
+  case 1:
+  case 2:
+    frac= ((int) (signed char) ptr[5]) * 10000;
+    break;
+  case 3:
+  case 4:
+    frac= mi_sint2korr(ptr + 5) * 100;
+    break;
+  case 5:
+  case 6:
+    frac= mi_sint3korr(ptr + 5);
+    break;
+  }
+  return MY_PACKED_TIME_MAKE(intpart, frac);
+}
+
+
+/**
+  Store MySQL56 in-memory numeric packed datetime representation to disk.
+
+  @param      nr  In-memory numeric packed datetime representation.
+  @param OUT  ptr The pointer to store at.
+  @param      dec Precision, 1-6.
+*/
+void my_datetime_packed_to_binary(longlong nr, uchar *ptr, uint dec)
+{
+  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+  /* The value being stored must have been properly rounded or truncated */
+  DBUG_ASSERT((MY_PACKED_TIME_GET_FRAC_PART(nr) %
+              (int) log_10_int[TIME_SECOND_PART_DIGITS - dec]) == 0);
+
+  mi_int5store(ptr, MY_PACKED_TIME_GET_INT_PART(nr) + DATETIMEF_INT_OFS);
+  switch (dec)
+  {
+  case 0:
+  default:
+    break;
+  case 1:
+  case 2:
+    ptr[5]= (unsigned char) (char) (MY_PACKED_TIME_GET_FRAC_PART(nr) / 10000);
+    break;
+  case 3:
+  case 4:
+    mi_int2store(ptr + 5, MY_PACKED_TIME_GET_FRAC_PART(nr) / 100);
+    break;
+  case 5:
+  case 6:
+    mi_int3store(ptr + 5, MY_PACKED_TIME_GET_FRAC_PART(nr));
+  }
+}
+
+
+/*** MySQL56 TIMESTAMP low-level memory and disk representation routines ***/
+
+/**
+  Calculate on-disk size of a timestamp value.
+
+  @param  dec  Precision.
+*/
+uint my_timestamp_binary_length(uint dec)
+{
+  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+  return 4 + (dec + 1) / 2;
+}
+
+
+/**
+  Convert MySQL56 binary timestamp representation to in-memory representation.
+
+  @param  OUT tm  The variable to convert to.
+  @param      ptr The pointer to read the value from.
+  @param      dec Precision.
+*/
+void my_timestamp_from_binary(struct timeval *tm, const uchar *ptr, uint dec)
+{
+  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+  tm->tv_sec= mi_uint4korr(ptr);
+  switch (dec)
+  {
+    case 0:
+    default:
+      tm->tv_usec= 0;
+      break;
+    case 1:
+    case 2:
+      tm->tv_usec= ((int) ptr[4]) * 10000;
+      break;
+    case 3:
+    case 4:
+      tm->tv_usec= mi_sint2korr(ptr + 4) * 100;
+      break;
+    case 5:
+    case 6:
+      tm->tv_usec= mi_sint3korr(ptr + 4);
+  }
+}
+
+
+/**
+  Convert MySQL56 in-memory timestamp representation to on-disk representation.
+
+  @param        tm   The value to convert.
+  @param  OUT   ptr  The pointer to store the value to.
+  @param        dec  Precision.
+*/
+void my_timestamp_to_binary(const struct timeval *tm, uchar *ptr, uint dec)
+{
+  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+  /* Stored value must have been previously properly rounded or truncated */
+  DBUG_ASSERT((tm->tv_usec %
+               (int) log_10_int[TIME_SECOND_PART_DIGITS - dec]) == 0);
+  mi_int4store(ptr, tm->tv_sec);
+  switch (dec)
+  {
+    case 0:
+    default:
+      break;
+    case 1:
+    case 2:
+      ptr[4]= (unsigned char) (char) (tm->tv_usec / 10000);
+      break;
+    case 3:
+    case 4:
+      mi_int2store(ptr + 4, tm->tv_usec / 100);
+      break;
+      /* Impossible second precision. Fall through */
+    case 5:
+    case 6:
+      mi_int3store(ptr + 4, tm->tv_usec);
+  }
+}
+
+/****************************************/
+
+
  double TIME_to_double(const MYSQL_TIME *my_time)
  {
    double d= (double)TIME_to_ulonglong(my_time);
=== modified file 'sql/field.cc'
--- sql/field.cc	2013-06-06 19:32:29 +0000
+++ sql/field.cc	2013-06-19 04:51:38 +0000
@@ -87,6 +87,7 @@ const char field_separator=',';
  #define FIELDTYPE_NUM (FIELDTYPE_TEAR_FROM + (255 - FIELDTYPE_TEAR_TO))
  static inline int field_type2index (enum_field_types field_type)
  {
+  field_type= real_type_to_type(field_type);

I don't like that MYSQL_TYPE_*2 types (which are, really, now only a
migration types, for mysql-5.6 data files) are leaking into the server.
I'd prefer if they'd stay in their corresponding Field objects
and the rest of the server wouldn't need to know they exist.

I tried to reduce the amount of MYSQL_TYPE_*2 appearance.

They now appear in:

field.h
field.cc
log_event.cc
rpl_utility.cc
sql_partition.cc
sql_table.cc
which I think is OK.

Also, I had to add them into two places:

item.cc:          Item::cmp_type()
item_strfunc.cc:  Item_func_dyncol_create::prepare_arguments()

to avoid the "enumeration value is not handled" warning.
I don't think that adding "default" is a good idea.


This should probably be fixed separately.
Perhaps, we need a separate enum consisting of real types,
like this:

enum enum_field_real_types {
 MYSQL_REAL_TYPE_DATE,
 MYSQL_REAL_TYPE_NEWDATE,
 MYSQL_REAL_TYPE_TIME,
 MYSQL_REAL_TYPE_TIME2
 ...
};

Then have enum_field_types defined using enum_field_real_types,
like this:

enum enum_field_types {
  MYSQL_TYPE_DATE= MYSQL_REAL_TYPE_DATE,
  MYSQL_TYPE_TIME= MYSQL_REAL_TYPE_TIME
  ...
  // No codes like MYSQL_REAL_TYPE_NEWDATE or MYSQL_REAL_TYPE_TIME2 here
}


Then, all the functions in should use either enum_field_real_types
or enum_field_types, depending on what they actually need,
For example, the mentioned "value is not handled" should be gone,
because these functions need type (not real type!).



    return (field_type < FIELDTYPE_TEAR_FROM ?
            field_type :
            ((int)FIELDTYPE_TEAR_FROM) + (field_type - FIELDTYPE_TEAR_TO) - 1);
@@ -4690,6 +4767,16 @@ String *Field_timestamp::val_str(String
    *to++= (char) ('0'+(char) (temp));
    *to= 0;
    val_buffer->set_charset(&my_charset_numeric);
+
+  if (uint dec= decimals())

eh? We never declare variables inside if(). A question of a consistent
coding style.

Fixed.


+  {
+    ulong sec_part= (ulong) sec_part_shift(ltime.second_part, dec);
+    char *buf= const_cast<char*>(val_buffer->ptr() + MAX_DATETIME_WIDTH);
+    for (int i= dec; i > 0; i--, sec_part/= 10)
+    buf[i]= (char)(sec_part % 10) + '0';
+    buf[0]= '.';
+    buf[dec + 1]= 0;
+  }
    return val_buffer;
  }

@@ -5092,7 +5198,18 @@ int Field_temporal::store(double nr)
  }


-int Field_temporal::store(longlong nr, bool unsigned_val)
+int Field_temporal::store_decimal(const my_decimal *d)
+{
+  ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+  double val;
+  /* TODO: use decimal2string? */
+  int err= warn_if_overflow(my_decimal2double(E_DEC_FATAL_ERROR &
+                                            ~E_DEC_OVERFLOW, d, &val));

decimal2string is slow, decimal2double is lossy.
Use my_decimal2seconds

Done.



+  return err | store(val);
+}
+
+
+int Field_temporal_with_date::store(longlong nr, bool unsigned_val)
  {
    int error;
    MYSQL_TIME ltime;
@@ -5229,32 +5375,16 @@ longlong Field_time::val_int(void)
    my_charset_bin
  */

-String *Field_time::val_str(String *val_buffer,
-			    String *val_ptr __attribute__((unused)))
+String *Field_time::val_str(String *str,
+			    String *unused __attribute__((unused)))
  {
    ASSERT_COLUMN_MARKED_FOR_READ;
    MYSQL_TIME ltime;
-  long tmp=(long) sint3korr(ptr);
-  ltime.neg= 0;
-  if (tmp < 0)
-  {
-    tmp= -tmp;
-    ltime.neg= 1;
-  }
-  ltime.year= ltime.month= 0;
-  ltime.day= (uint) 0;
-  ltime.hour= (uint) (tmp/10000);
-  ltime.minute= (uint) (tmp/100 % 100);
-  ltime.second= (uint) (tmp % 100);
-  ltime.second_part= 0;
-
-  val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH);
-  uint length= (uint) my_time_to_str(&ltime,
-                                     const_cast<char*>(val_buffer->ptr()), 0);
-  val_buffer->length(length);
-  val_buffer->set_charset(&my_charset_numeric);
-
-  return val_buffer;
+  get_date(&ltime, TIME_TIME_ONLY);
+  str->alloc(field_length + 1);
+  str->length(my_time_to_str(&ltime, const_cast<char*>(str->ptr()), decimals()));
+  str->set_charset(&my_charset_numeric);
+  return str;

Well. I intentionally kept the old "optimized" version.
I'd expect a generic one to be slower.
May be it's ok, though...


It seems we agreed that the old version was not
really that much "optimized" to deserve so much
duplicate code :)

(I wrote about it in a separate letter and you did not argue).


  }


@@ -5431,13 +5541,51 @@ void Field_time_hires::sql_type(String &
                                  "time(%u)", dec));
  }

-void Field_time_hires::make_field(Send_field *field)
+void Field_time_with_dec::make_field(Send_field *field)
  {
    Field::make_field(field);
    field->decimals= dec;
  }

  /****************************************************************************
+** time type with fsp (MySQL-5.6 version)
+** In string context: HH:MM:SS.FFFFFF
+** In number context: HHMMSS.FFFFFF
+****************************************************************************/
+
+void Field_timef::sql_type(String &res) const
+{
+  if (dec == 0)
+  {
+    res.set_ascii(STRING_WITH_LEN("time"));
+    return;
+  }
+  const CHARSET_INFO *cs= res.charset();
+  res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
+                               "time(%d)", dec));
+}

Why would your mysql-5.6 types need a separate sql_type() method?
It doesn't seem to be anything that couldn't go into the base class

Moved to the parent classes.



+
+int Field_timef::reset()
+{
+  my_time_packed_to_binary(0, ptr, dec);
+  return 0;
+}
+
+void Field_timef::store_TIME(MYSQL_TIME *ltime)
+{
+  my_time_trunc(ltime, decimals());
+  longlong tmp= TIME_to_longlong_time_packed(ltime);
+  my_time_packed_to_binary(tmp, ptr, dec);
+}
+
+bool Field_timef::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+  longlong tmp= my_time_packed_from_binary(ptr, dec);
+  TIME_from_longlong_time_packed(ltime, tmp);
+  return false;
+}
+
+/****************************************************************************
  ** year type
  ** Save in a byte the year 0, 1901->2155
  ** Can handle 2 byte or 4 byte years!
@@ -9413,17 +9494,6 @@ bool Create_field::init(THD *thd, char *
      DBUG_RETURN(TRUE);
    }

-  switch (fld_type) {
-  case MYSQL_TYPE_DATE:
-  case MYSQL_TYPE_NEWDATE:
-  case MYSQL_TYPE_TIME:
-  case MYSQL_TYPE_DATETIME:
-  case MYSQL_TYPE_TIMESTAMP:
-    charset= &my_charset_numeric;
-    flags|= BINARY_FLAG;
-  default: break;
-  }
-

why?

Because Field_temporal does not need a charset any more,
as it is not a child of Field_str now.



    DBUG_RETURN(FALSE); /* success */
  }

=== modified file 'sql/field.h'
--- sql/field.h	2013-06-06 15:51:28 +0000
+++ sql/field.h	2013-06-19 09:46:34 +0000
@@ -428,6 +464,27 @@ class Field
    virtual uint32 key_length() const { return pack_length(); }
    virtual enum_field_types type() const =0;
    virtual enum_field_types real_type() const { return type(); }
+  virtual enum_field_types binlog_type() const
+  {
+    /*
+      Binlog stores field->type() as type code by default.
+      This puts MYSQL_TYPE_STRING in case of CHAR, VARCHAR, SET and ENUM,
+      with extra data type details put into metadata.
+
+      We cannot store field->type() in case of temporal types with
+      fractional seconds: TIME(n), DATETIME(n) and TIMESTAMP(n),
+      because binlog records with MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME
+      type codes do not have metadata.
+      So for MySQL-5.6 temporal data types with fractional seconds we'll store
+      real_type() type codes instead, i.e.
+      MYSQL_TYPE_TIME2, MYSQL_TYPE_DATETIME2, MYSQL_TYPE_TIMESTAMP2,
+      and put precision into metatada.
+
+      Note: perhaps binlog should eventually be modified to store
+      real_type() instead of type() for all column types.
+    */
+    return type();
+  }

Eh, I don't like it. So, to replicate with RBR between tables with
TIME columns of different precision, one needs to create tables
in MySQL 5.6? That's very lame thing to say to users :(

I'd rather fix it to work for all temporal types, both 5.6 and ours.
Perhaps, indeed, store real_type() as you write above.

But in a separate changeset. And here let's just say that it doesn't
work at all, and we always put MYSQL_TYPE_TIME/etc in the binlog.

Fixed the comment.


    inline  int cmp(const uchar *str) { return cmp(ptr,str); }
    virtual int cmp_max(const uchar *a, const uchar *b, uint max_len)
      { return cmp(a, b); }
@@ -661,6 +718,16 @@ class Field
    { return binary() ? &my_charset_bin : charset(); }
    virtual CHARSET_INFO *sort_charset(void) const { return charset(); }
    virtual bool has_charset(void) const { return FALSE; }
+  /*
+    match_collation_to_optimize_range() is to distinguish in
+    range optimizer (see opt_range.cc) between real string types:
+      CHAR, VARCHAR, TEXT
+    and the other string-alike types with result_type() == STRING_RESULT:
+      DATE, TIME, DATETIME, TIMESTAMP
+    We need it to decide whether to test if collation of the operation
+    matches collation of the field (needed only for real string types).
+  */
+  virtual bool match_collation_to_optimize_range() const { return false; }

Not needed. temporal types are not string-alike,
they have have cmp_type() == TIME_RESULT,
string types have STRING_RESULT.

It seems we agreed to try to do something around this in a separate
changeset.




    virtual void set_charset(CHARSET_INFO *charset_arg) { }
    virtual enum Derivation derivation(void) const
    { return DERIVATION_IMPLICIT; }
@@ -1345,7 +1407,67 @@ class Field_null :public Field_str {
  };


-class Field_timestamp :public Field_str {
+class Field_temporal: public Field {
+public:
+  Field_temporal(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
+                 uchar null_bit_arg, utype unireg_check_arg,
+                 const char *field_name_arg)
+    :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
+               field_name_arg)
+    { flags|= BINARY_FLAG; }
+  Item_result result_type () const { return STRING_RESULT; }
+  uint32 max_display_length() { return field_length; }
+  bool str_needs_quotes() { return TRUE; }
+  enum Derivation derivation(void) const { return DERIVATION_NUMERIC; }
+  uint repertoire(void) const { return MY_REPERTOIRE_NUMERIC; }
+  CHARSET_INFO *charset(void) const { return &my_charset_numeric; }
+  const CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; }
+  bool binary() const { return true; }
+  enum Item_result cmp_type () const { return TIME_RESULT; }
+  uint is_equal(Create_field *new_field);
+  bool eq_def(Field *field)
+  {
+    return (Field::eq_def(field) && decimals() == field->decimals());
+  }
+  int  store_decimal(const my_decimal *);
+  my_decimal *val_decimal(my_decimal*);
+  void set_warnings(MYSQL_ERROR::enum_warning_level trunc_level,
+                    const ErrConv *str, int was_cut, timestamp_type ts_type);
+  double pos_in_interval(Field *min, Field *max)
+  {
+    return pos_in_interval_val_str(min, max, 0);

Really? This would first convert a temporal value to a string,
and then use a quite complicated procedure of placing a string
in the interval. Wouldn't it be much cheaper to use
pos_in_interval_val_real() ?

Changed to pos_in_interval_val_real().

Note, mysql-test/r/statistics.result has changed slightly.
But I think it's Ok.


+  }
+};
+
+
+/**
+  Abstract class for:
+  - DATE
+  - DATETIME
+  - DATETIME(1..6)
+  - DATETIME(0..6) - MySQL56 version
+*/
+class Field_temporal_with_date: public Field_temporal {
+protected:
+  int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str,
+                              int was_cut, int have_smth_to_conv);

Eh? So you'll have *three* different store_TIME_with_warning() methods?
I didn't like my version, because I had *two*,
there should've been only one.

We decided to take a look into this later.


+  virtual void store_TIME(MYSQL_TIME *ltime) = 0;
+public:
+  Field_temporal_with_date(uchar *ptr_arg, uint32 len_arg,
+                           uchar *null_ptr_arg, uchar null_bit_arg,
+                           utype unireg_check_arg,
+                           const char *field_name_arg)
+    :Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+                    unireg_check_arg, field_name_arg)
+    {}
+  int  store(const char *to, uint length, CHARSET_INFO *charset);
+  int  store(double nr);
+  int  store(longlong nr, bool unsigned_val);
+  int  store_time_dec(MYSQL_TIME *ltime, uint dec);
+};
+
+
+class Field_timestamp :public Field_temporal {
  protected:
    int store_TIME_with_warning(THD *, MYSQL_TIME *, const ErrConv *,
                                bool, bool);
=== modified file 'sql/opt_range.cc'
--- sql/opt_range.cc	2013-06-06 19:32:29 +0000
+++ sql/opt_range.cc	2013-06-18 10:35:35 +0000
@@ -8014,10 +8014,10 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND

    */
    if (field->result_type() == STRING_RESULT &&

this and below should probably use cmp_type, not result_type


We agreed to try to do something around this in a separate changeset.

-      ((Field_str*) field)->match_collation_to_optimize_range() &&
+      field->match_collation_to_optimize_range() &&
        value->result_type() == STRING_RESULT &&
        key_part->image_type == Field::itRAW &&
-      ((Field_str*)field)->charset() != conf_func->compare_collation() &&
+      field->charset() != conf_func->compare_collation() &&
        !(conf_func->compare_collation()->state & MY_CS_BINSORT &&
          (type == Item_func::EQUAL_FUNC || type == Item_func::EQ_FUNC)))
      goto end;
=== modified file 'sql/sql_time.h'
--- sql/sql_time.h	2012-08-31 12:15:52 +0000
+++ sql/sql_time.h	2013-06-18 10:35:36 +0000
@@ -110,4 +110,23 @@ extern DATE_TIME_FORMAT global_time_form
  extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[];
  extern LEX_STRING interval_type_to_name[];

+/* Date/time rounding and truncation functions */
+inline long my_time_fraction_remainder(long nr, uint decimals)
+{
+  DBUG_ASSERT(decimals <= TIME_SECOND_PART_DIGITS);
+  return nr % (long) log_10_int[TIME_SECOND_PART_DIGITS - decimals];
+}
+inline void my_time_trunc(MYSQL_TIME *ltime, uint decimals)
+{
+  ltime->second_part-= my_time_fraction_remainder(ltime->second_part, decimals);
+}

good idea, but
1. put it in my_time.h instead of sec_part_truncate()
2. change item_timefunc.cc (and other files too) to use it
(this will remove all uses of sec_part_truncate())

Done.


+inline void my_datetime_trunc(MYSQL_TIME *ltime, uint decimals)
+{
+  return my_time_trunc(ltime, decimals);
+}
+inline void my_timeval_trunc(struct timeval *tv, uint decimals)
+{
+  tv->tv_usec-= my_time_fraction_remainder(tv->tv_usec, decimals);
+}

please move all these four functions to my_time.h
(although, I'd delete my_datetime_trunc() completely)

Done.


+
  #endif /* SQL_TIME_INCLUDED */

Regards,
Sergei

=== modified file 'client/mysqlbinlog.cc'
--- client/mysqlbinlog.cc	2013-04-15 13:09:22 +0000
+++ client/mysqlbinlog.cc	2013-06-27 12:33:05 +0000
@@ -37,6 +37,7 @@
 /* That one is necessary for defines of OPTION_NO_FOREIGN_KEY_CHECKS etc */
 #include "sql_priv.h"
 #include "log_event.h"
+#include "compat56.h"
 #include "sql_common.h"
 #include "my_dir.h"
 #include <welcome_copyright_notice.h> // ORACLE_WELCOME_COPYRIGHT_NOTICE
@@ -2562,3 +2563,4 @@ void *sql_alloc(size_t size)
 #include "sql_string.cc"
 #include "sql_list.cc"
 #include "rpl_filter.cc"
+#include "compat56.cc"

=== modified file 'include/my_time.h'
--- include/my_time.h	2013-05-24 15:09:59 +0000
+++ include/my_time.h	2013-06-27 11:24:23 +0000
@@ -111,6 +111,8 @@ longlong pack_time(MYSQL_TIME *my_time);
 MYSQL_TIME *unpack_time(longlong packed, MYSQL_TIME *my_time);
 
 int check_time_range(struct st_mysql_time *my_time, uint dec, int *warning);
+my_bool check_datetime_range(const MYSQL_TIME *ltime);
+
 
 long calc_daynr(uint year,uint month,uint day);
 uint calc_days_in_year(uint year);
@@ -163,6 +165,8 @@ int my_date_to_str(const MYSQL_TIME *l_t
 int my_datetime_to_str(const MYSQL_TIME *l_time, char *to, uint digits);
 int my_TIME_to_str(const MYSQL_TIME *l_time, char *to, uint digits);
 
+int my_timeval_to_str(const struct timeval *tm, char *to, uint dec);
+
 static inline longlong sec_part_shift(longlong second_part, uint digits)
 {
   return second_part / (longlong)log_10_int[TIME_SECOND_PART_DIGITS - digits];
@@ -177,6 +181,22 @@ static inline ulong sec_part_truncate(ul
   return second_part - second_part % (ulong)log_10_int[TIME_SECOND_PART_DIGITS - digits];
 }
 
+/* Date/time rounding and truncation functions */
+static inline long my_time_fraction_remainder(long nr, uint decimals)
+{
+  DBUG_ASSERT(decimals <= TIME_SECOND_PART_DIGITS);
+  return nr % (long) log_10_int[TIME_SECOND_PART_DIGITS - decimals];
+}
+static inline void my_time_trunc(MYSQL_TIME *ltime, uint decimals)
+{
+  ltime->second_part-= my_time_fraction_remainder(ltime->second_part, decimals);
+}
+static inline void my_timeval_trunc(struct timeval *tv, uint decimals)
+{
+  tv->tv_usec-= my_time_fraction_remainder(tv->tv_usec, decimals);
+}
+
+
 #define hrtime_to_my_time(X) ((my_time_t)hrtime_to_time(X))
 
 /* 

=== modified file 'include/mysql.h.pp'
--- include/mysql.h.pp	2013-05-24 15:09:59 +0000
+++ include/mysql.h.pp	2013-06-18 10:35:35 +0000
@@ -49,6 +49,9 @@ enum enum_field_types { MYSQL_TYPE_DECIM
    MYSQL_TYPE_DATETIME, MYSQL_TYPE_YEAR,
    MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR,
    MYSQL_TYPE_BIT,
+   MYSQL_TYPE_TIMESTAMP2,
+   MYSQL_TYPE_DATETIME2,
+   MYSQL_TYPE_TIME2,
                         MYSQL_TYPE_NEWDECIMAL=246,
    MYSQL_TYPE_ENUM=247,
    MYSQL_TYPE_SET=248,

=== modified file 'include/mysql_com.h'
--- include/mysql_com.h	2013-06-06 19:32:29 +0000
+++ include/mysql_com.h	2013-06-27 10:54:59 +0000
@@ -400,6 +400,9 @@ enum enum_field_types { MYSQL_TYPE_DECIM
 			MYSQL_TYPE_DATETIME, MYSQL_TYPE_YEAR,
 			MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR,
 			MYSQL_TYPE_BIT,
+			MYSQL_TYPE_TIMESTAMP2, /* MySQL-5.6 TIMESTAMP */
+			MYSQL_TYPE_DATETIME2,  /* MySQL-5.6 DATETIME  */
+			MYSQL_TYPE_TIME2,      /* MySQL-5.6 TIME      */
                         MYSQL_TYPE_NEWDECIMAL=246,
 			MYSQL_TYPE_ENUM=247,
 			MYSQL_TYPE_SET=248,

=== modified file 'mysql-test/r/statistics.result'
--- mysql-test/r/statistics.result	2013-04-22 09:18:46 +0000
+++ mysql-test/r/statistics.result	2013-07-02 06:53:52 +0000
@@ -220,7 +220,7 @@ db_name	table_name	column_name	min_value
 test	t1	a	0	49	0.0000	1.0000	4	SINGLE_PREC_HB	2E62A1D0
 test	t1	b	vvvvvvvvvvvvv	zzzzzzzzzzzzzzzzzz	0.2000	6.4000	4	SINGLE_PREC_HB	003FBFFF
 test	t1	c	aaaa	dddddddd	0.1250	7.0000	4	SINGLE_PREC_HB	0055AAFF
-test	t1	d	1989-03-12	1999-07-23	0.1500	8.5000	4	SINGLE_PREC_HB	009393FF
+test	t1	d	1989-03-12	1999-07-23	0.1500	8.5000	4	SINGLE_PREC_HB	001919FF
 test	t1	e	0.01	0.112	0.2250	6.2000	4	SINGLE_PREC_HB	000564E1
 test	t1	f	1	5	0.2000	6.4000	4	SINGLE_PREC_HB	3F7FBFBF
 DELETE FROM mysql.column_stats;
@@ -238,7 +238,7 @@ db_name	table_name	column_name	min_value
 test	t1	a	0	49	0.0000	1.0000	8	DOUBLE_PREC_HB	052F4363F4A1F9D0
 test	t1	b	vvvvvvvvvvvvv	zzzzzzzzzzzzzzzzzz	0.2000	6.4000	8	DOUBLE_PREC_HB	0000FF3FFFBFFFFF
 test	t1	c	aaaa	dddddddd	0.1250	7.0000	8	DOUBLE_PREC_HB	00005555AAAAFFFF
-test	t1	d	1989-03-12	1999-07-23	0.1500	8.5000	8	DOUBLE_PREC_HB	000026942694FFFF
+test	t1	d	1989-03-12	1999-07-23	0.1500	8.5000	8	DOUBLE_PREC_HB	0000031A031AFFFF
 test	t1	e	0.01	0.112	0.2250	6.2000	8	DOUBLE_PREC_HB	000005056464E1E1
 test	t1	f	1	5	0.2000	6.4000	8	DOUBLE_PREC_HB	FF3FFF7FFFBFFFBF
 DELETE FROM mysql.column_stats;

=== modified file 'mysql-test/r/strict.result'
--- mysql-test/r/strict.result	2012-04-07 13:58:46 +0000
+++ mysql-test/r/strict.result	2013-06-28 14:00:13 +0000
@@ -1135,7 +1135,7 @@ create table t1 (col1 date, col2 datetim
 insert into t1 values (0,0,0);
 ERROR 22007: Incorrect date value: '0' for column 'col1' at row 1
 insert into t1 values (0.0,0.0,0.0);
-ERROR 22007: Incorrect date value: '0' for column 'col1' at row 1
+ERROR 22007: Incorrect date value: '0.0' for column 'col1' at row 1
 insert into t1 (col1) values (convert('0000-00-00',date));
 ERROR 22007: Incorrect datetime value: '0000-00-00'
 insert into t1 (col1) values (cast('0000-00-00' as date));

=== added file 'mysql-test/r/type_temporal_mysql56.result'
--- mysql-test/r/type_temporal_mysql56.result	1970-01-01 00:00:00 +0000
+++ mysql-test/r/type_temporal_mysql56.result	2013-06-27 10:56:21 +0000
@@ -0,0 +1,89 @@
+SHOW CREATE TABLE mysql56time;
+Table	Create Table
+mysql56time	CREATE TABLE `mysql56time` (
+  `t0` time DEFAULT NULL,
+  `t1` time(1) DEFAULT NULL,
+  `t2` time(2) DEFAULT NULL,
+  `t3` time(3) DEFAULT NULL,
+  `t4` time(4) DEFAULT NULL,
+  `t5` time(5) DEFAULT NULL,
+  `t6` time(6) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+SELECT * FROM mysql56time;
+t0	838:59:59
+t1	838:59:59.0
+t2	838:59:59.00
+t3	838:59:59.000
+t4	838:59:59.0000
+t5	838:59:59.00000
+t6	838:59:59.000000
+t0	00:00:00
+t1	00:00:00.0
+t2	00:00:00.00
+t3	00:00:00.000
+t4	00:00:00.0000
+t5	00:00:00.00000
+t6	00:00:00.000000
+t0	-838:59:59
+t1	-838:59:59.0
+t2	-838:59:59.00
+t3	-838:59:59.000
+t4	-838:59:59.0000
+t5	-838:59:59.00000
+t6	-838:59:59.000000
+DROP TABLE mysql56time;
+SHOW CREATE TABLE mysql56datetime;
+Table	Create Table
+mysql56datetime	CREATE TABLE `mysql56datetime` (
+  `dt0` datetime DEFAULT NULL,
+  `dt1` datetime(1) DEFAULT NULL,
+  `dt2` datetime(2) DEFAULT NULL,
+  `dt3` datetime(3) DEFAULT NULL,
+  `dt4` datetime(4) DEFAULT NULL,
+  `dt5` datetime(5) DEFAULT NULL,
+  `dt6` datetime(6) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+SELECT * FROM mysql56datetime;
+dt0	0000-00-00 00:00:00
+dt1	0000-00-00 00:00:00.0
+dt2	0000-00-00 00:00:00.00
+dt3	0000-00-00 00:00:00.000
+dt4	0000-00-00 00:00:00.0000
+dt5	0000-00-00 00:00:00.00000
+dt6	0000-00-00 00:00:00.000000
+dt0	9999-12-31 23:59:59
+dt1	9999-12-31 23:59:59.9
+dt2	9999-12-31 23:59:59.99
+dt3	9999-12-31 23:59:59.999
+dt4	9999-12-31 23:59:59.9999
+dt5	9999-12-31 23:59:59.99999
+dt6	9999-12-31 23:59:59.999999
+DROP TABLE mysql56datetime;
+SET TIME_ZONE='+00:00';
+SHOW CREATE TABLE mysql56timestamp;
+Table	Create Table
+mysql56timestamp	CREATE TABLE `mysql56timestamp` (
+  `ts0` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  `ts1` timestamp(1) NOT NULL DEFAULT '0000-00-00 00:00:00.0',
+  `ts2` timestamp(2) NOT NULL DEFAULT '0000-00-00 00:00:00.00',
+  `ts3` timestamp(3) NOT NULL DEFAULT '0000-00-00 00:00:00.000',
+  `ts4` timestamp(4) NOT NULL DEFAULT '0000-00-00 00:00:00.0000',
+  `ts5` timestamp(5) NOT NULL DEFAULT '0000-00-00 00:00:00.00000',
+  `ts6` timestamp(6) NOT NULL DEFAULT '0000-00-00 00:00:00.000000'
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+SELECT * FROM mysql56timestamp;
+ts0	1970-01-01 00:00:01
+ts1	1970-01-01 00:00:01.0
+ts2	1970-01-01 00:00:01.00
+ts3	1970-01-01 00:00:01.000
+ts4	1970-01-01 00:00:01.0000
+ts5	1970-01-01 00:00:01.00000
+ts6	1970-01-01 00:00:01.000000
+ts0	2038-01-19 03:14:07
+ts1	2038-01-19 03:14:07.9
+ts2	2038-01-19 03:14:07.99
+ts3	2038-01-19 03:14:07.999
+ts4	2038-01-19 03:14:07.9999
+ts5	2038-01-19 03:14:07.99999
+ts6	2038-01-19 03:14:07.999999
+DROP TABLE mysql56timestamp;

=== added file 'mysql-test/std_data/mysql56datetime.MYD'
Binary files mysql-test/std_data/mysql56datetime.MYD	1970-01-01 00:00:00 +0000 and mysql-test/std_data/mysql56datetime.MYD	2013-06-19 07:53:28 +0000 differ

=== added file 'mysql-test/std_data/mysql56datetime.MYI'
Binary files mysql-test/std_data/mysql56datetime.MYI	1970-01-01 00:00:00 +0000 and mysql-test/std_data/mysql56datetime.MYI	2013-06-19 07:54:09 +0000 differ

=== added file 'mysql-test/std_data/mysql56datetime.frm'
Binary files mysql-test/std_data/mysql56datetime.frm	1970-01-01 00:00:00 +0000 and mysql-test/std_data/mysql56datetime.frm	2013-06-19 07:53:28 +0000 differ

=== added file 'mysql-test/std_data/mysql56time.MYD'
Binary files mysql-test/std_data/mysql56time.MYD	1970-01-01 00:00:00 +0000 and mysql-test/std_data/mysql56time.MYD	2013-06-19 07:53:28 +0000 differ

=== added file 'mysql-test/std_data/mysql56time.MYI'
Binary files mysql-test/std_data/mysql56time.MYI	1970-01-01 00:00:00 +0000 and mysql-test/std_data/mysql56time.MYI	2013-06-19 07:54:09 +0000 differ

=== added file 'mysql-test/std_data/mysql56time.frm'
Binary files mysql-test/std_data/mysql56time.frm	1970-01-01 00:00:00 +0000 and mysql-test/std_data/mysql56time.frm	2013-06-19 07:53:28 +0000 differ

=== added file 'mysql-test/std_data/mysql56timestamp.MYD'
Binary files mysql-test/std_data/mysql56timestamp.MYD	1970-01-01 00:00:00 +0000 and mysql-test/std_data/mysql56timestamp.MYD	2013-06-19 07:53:28 +0000 differ

=== added file 'mysql-test/std_data/mysql56timestamp.MYI'
Binary files mysql-test/std_data/mysql56timestamp.MYI	1970-01-01 00:00:00 +0000 and mysql-test/std_data/mysql56timestamp.MYI	2013-06-19 07:54:09 +0000 differ

=== added file 'mysql-test/std_data/mysql56timestamp.frm'
Binary files mysql-test/std_data/mysql56timestamp.frm	1970-01-01 00:00:00 +0000 and mysql-test/std_data/mysql56timestamp.frm	2013-06-19 07:53:28 +0000 differ

=== modified file 'mysql-test/suite/binlog/r/binlog_mysqlbinlog_row_innodb.result'
--- mysql-test/suite/binlog/r/binlog_mysqlbinlog_row_innodb.result	2013-04-15 08:55:27 +0000
+++ mysql-test/suite/binlog/r/binlog_mysqlbinlog_row_innodb.result	2013-06-18 10:35:35 +0000
@@ -2404,7 +2404,7 @@ BEGIN
 ###   @28='1000:01:01' /* DATE meta=0 nullable=1 is_null=0 */
 ###   @29=1000-01-01 00:00:00 /* DATETIME meta=0 nullable=1 is_null=0 */
 ###   @30=75601 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
-###   @31='839:12:57' /* TIME meta=0 nullable=1 is_null=0 */
+###   @31='-838:59:59' /* TIME meta=0 nullable=1 is_null=0 */
 ###   @32=1901 /* YEAR meta=0 nullable=1 is_null=0 */
 ###   @33='' /* STRING(1) meta=65025 nullable=1 is_null=0 */
 ###   @34='' /* STRING(0) meta=65024 nullable=1 is_null=0 */
@@ -2766,7 +2766,7 @@ BEGIN
 ###   @28='1000:01:01' /* DATE meta=0 nullable=1 is_null=0 */
 ###   @29=1000-01-01 00:00:00 /* DATETIME meta=0 nullable=1 is_null=0 */
 ###   @30=75601 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
-###   @31='839:12:57' /* TIME meta=0 nullable=1 is_null=0 */
+###   @31='-838:59:59' /* TIME meta=0 nullable=1 is_null=0 */
 ###   @32=1901 /* YEAR meta=0 nullable=1 is_null=0 */
 ###   @33='' /* STRING(1) meta=65025 nullable=1 is_null=0 */
 ###   @34='' /* STRING(0) meta=65024 nullable=1 is_null=0 */
@@ -3019,7 +3019,7 @@ BEGIN
 ###   @28='1000:01:01' /* DATE meta=0 nullable=1 is_null=0 */
 ###   @29=1000-01-01 00:00:00 /* DATETIME meta=0 nullable=1 is_null=0 */
 ###   @30=75601 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
-###   @31='839:12:57' /* TIME meta=0 nullable=1 is_null=0 */
+###   @31='-838:59:59' /* TIME meta=0 nullable=1 is_null=0 */
 ###   @32=1901 /* YEAR meta=0 nullable=1 is_null=0 */
 ###   @33='' /* STRING(1) meta=65025 nullable=1 is_null=0 */
 ###   @34='' /* STRING(0) meta=65024 nullable=1 is_null=0 */
@@ -3551,7 +3551,7 @@ BEGIN
 ###   @28='1000:01:01' /* DATE meta=0 nullable=1 is_null=0 */
 ###   @29=1000-01-01 00:00:00 /* DATETIME meta=0 nullable=1 is_null=0 */
 ###   @30=75601 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
-###   @31='839:12:57' /* TIME meta=0 nullable=1 is_null=0 */
+###   @31='-838:59:59' /* TIME meta=0 nullable=1 is_null=0 */
 ###   @32=1901 /* YEAR meta=0 nullable=1 is_null=0 */
 ###   @33='' /* STRING(1) meta=65025 nullable=1 is_null=0 */
 ###   @34='' /* STRING(0) meta=65024 nullable=1 is_null=0 */

=== modified file 'mysql-test/suite/binlog/r/binlog_mysqlbinlog_row_myisam.result'
--- mysql-test/suite/binlog/r/binlog_mysqlbinlog_row_myisam.result	2013-04-15 08:55:27 +0000
+++ mysql-test/suite/binlog/r/binlog_mysqlbinlog_row_myisam.result	2013-06-18 10:35:35 +0000
@@ -2404,7 +2404,7 @@ BEGIN
 ###   @28='1000:01:01' /* DATE meta=0 nullable=1 is_null=0 */
 ###   @29=1000-01-01 00:00:00 /* DATETIME meta=0 nullable=1 is_null=0 */
 ###   @30=75601 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
-###   @31='839:12:57' /* TIME meta=0 nullable=1 is_null=0 */
+###   @31='-838:59:59' /* TIME meta=0 nullable=1 is_null=0 */
 ###   @32=1901 /* YEAR meta=0 nullable=1 is_null=0 */
 ###   @33='' /* STRING(1) meta=65025 nullable=1 is_null=0 */
 ###   @34='' /* STRING(0) meta=65024 nullable=1 is_null=0 */
@@ -2773,7 +2773,7 @@ BEGIN
 ###   @28='1000:01:01' /* DATE meta=0 nullable=1 is_null=0 */
 ###   @29=1000-01-01 00:00:00 /* DATETIME meta=0 nullable=1 is_null=0 */
 ###   @30=75601 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
-###   @31='839:12:57' /* TIME meta=0 nullable=1 is_null=0 */
+###   @31='-838:59:59' /* TIME meta=0 nullable=1 is_null=0 */
 ###   @32=1901 /* YEAR meta=0 nullable=1 is_null=0 */
 ###   @33='' /* STRING(1) meta=65025 nullable=1 is_null=0 */
 ###   @34='' /* STRING(0) meta=65024 nullable=1 is_null=0 */
@@ -3028,7 +3028,7 @@ BEGIN
 ###   @28='1000:01:01' /* DATE meta=0 nullable=1 is_null=0 */
 ###   @29=1000-01-01 00:00:00 /* DATETIME meta=0 nullable=1 is_null=0 */
 ###   @30=75601 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
-###   @31='839:12:57' /* TIME meta=0 nullable=1 is_null=0 */
+###   @31='-838:59:59' /* TIME meta=0 nullable=1 is_null=0 */
 ###   @32=1901 /* YEAR meta=0 nullable=1 is_null=0 */
 ###   @33='' /* STRING(1) meta=65025 nullable=1 is_null=0 */
 ###   @34='' /* STRING(0) meta=65024 nullable=1 is_null=0 */
@@ -3568,7 +3568,7 @@ BEGIN
 ###   @28='1000:01:01' /* DATE meta=0 nullable=1 is_null=0 */
 ###   @29=1000-01-01 00:00:00 /* DATETIME meta=0 nullable=1 is_null=0 */
 ###   @30=75601 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
-###   @31='839:12:57' /* TIME meta=0 nullable=1 is_null=0 */
+###   @31='-838:59:59' /* TIME meta=0 nullable=1 is_null=0 */
 ###   @32=1901 /* YEAR meta=0 nullable=1 is_null=0 */
 ###   @33='' /* STRING(1) meta=65025 nullable=1 is_null=0 */
 ###   @34='' /* STRING(0) meta=65024 nullable=1 is_null=0 */

=== added file 'mysql-test/suite/rpl/r/rpl_temporal_mysql56.result'
--- mysql-test/suite/rpl/r/rpl_temporal_mysql56.result	1970-01-01 00:00:00 +0000
+++ mysql-test/suite/rpl/r/rpl_temporal_mysql56.result	2013-06-27 10:57:55 +0000
@@ -0,0 +1,84 @@
+include/master-slave.inc
+[connection master]
+SET TIME_ZONE='+00:00';
+SET TIME_ZONE='+00:00';
+INSERT INTO mysql56time VALUES ('01:01:01','01:01:01.1','01:01:01.11','01:01:01.111','01:01:01.1111','01:01:01.11111','01:01:01.111111');
+INSERT INTO mysql56datetime VALUES ('2001-01-01 01:01:01','2001-01-01 01:01:01.1','2001-01-01 01:01:01.11','2001-01-01 01:01:01.111','2001-01-01 01:01:01.1111','2001-01-01 01:01:01.11111','2001-01-01 01:01:01.111111');
+INSERT INTO mysql56timestamp VALUES ('2001-01-01 01:01:01','2001-01-01 01:01:01.1','2001-01-01 01:01:01.11','2001-01-01 01:01:01.111','2001-01-01 01:01:01.1111','2001-01-01 01:01:01.11111','2001-01-01 01:01:01.111111');
+SELECT * FROM mysql56time;
+t0	838:59:59
+t1	838:59:59.0
+t2	838:59:59.00
+t3	838:59:59.000
+t4	838:59:59.0000
+t5	838:59:59.00000
+t6	838:59:59.000000
+t0	00:00:00
+t1	00:00:00.0
+t2	00:00:00.00
+t3	00:00:00.000
+t4	00:00:00.0000
+t5	00:00:00.00000
+t6	00:00:00.000000
+t0	-838:59:59
+t1	-838:59:59.0
+t2	-838:59:59.00
+t3	-838:59:59.000
+t4	-838:59:59.0000
+t5	-838:59:59.00000
+t6	-838:59:59.000000
+t0	01:01:01
+t1	01:01:01.1
+t2	01:01:01.11
+t3	01:01:01.111
+t4	01:01:01.1111
+t5	01:01:01.11111
+t6	01:01:01.111111
+SELECT * FROM mysql56datetime;
+dt0	0000-00-00 00:00:00
+dt1	0000-00-00 00:00:00.0
+dt2	0000-00-00 00:00:00.00
+dt3	0000-00-00 00:00:00.000
+dt4	0000-00-00 00:00:00.0000
+dt5	0000-00-00 00:00:00.00000
+dt6	0000-00-00 00:00:00.000000
+dt0	9999-12-31 23:59:59
+dt1	9999-12-31 23:59:59.9
+dt2	9999-12-31 23:59:59.99
+dt3	9999-12-31 23:59:59.999
+dt4	9999-12-31 23:59:59.9999
+dt5	9999-12-31 23:59:59.99999
+dt6	9999-12-31 23:59:59.999999
+dt0	2001-01-01 01:01:01
+dt1	2001-01-01 01:01:01.1
+dt2	2001-01-01 01:01:01.11
+dt3	2001-01-01 01:01:01.111
+dt4	2001-01-01 01:01:01.1111
+dt5	2001-01-01 01:01:01.11111
+dt6	2001-01-01 01:01:01.111111
+SELECT * FROM mysql56timestamp;
+ts0	1970-01-01 00:00:01
+ts1	1970-01-01 00:00:01.0
+ts2	1970-01-01 00:00:01.00
+ts3	1970-01-01 00:00:01.000
+ts4	1970-01-01 00:00:01.0000
+ts5	1970-01-01 00:00:01.00000
+ts6	1970-01-01 00:00:01.000000
+ts0	2038-01-19 03:14:07
+ts1	2038-01-19 03:14:07.9
+ts2	2038-01-19 03:14:07.99
+ts3	2038-01-19 03:14:07.999
+ts4	2038-01-19 03:14:07.9999
+ts5	2038-01-19 03:14:07.99999
+ts6	2038-01-19 03:14:07.999999
+ts0	2001-01-01 01:01:01
+ts1	2001-01-01 01:01:01.1
+ts2	2001-01-01 01:01:01.11
+ts3	2001-01-01 01:01:01.111
+ts4	2001-01-01 01:01:01.1111
+ts5	2001-01-01 01:01:01.11111
+ts6	2001-01-01 01:01:01.111111
+DROP TABLE mysql56time;
+DROP TABLE mysql56datetime;
+DROP TABLE mysql56timestamp;
+include/rpl_end.inc

=== added file 'mysql-test/suite/rpl/t/rpl_temporal_mysql56.test'
--- mysql-test/suite/rpl/t/rpl_temporal_mysql56.test	1970-01-01 00:00:00 +0000
+++ mysql-test/suite/rpl/t/rpl_temporal_mysql56.test	2013-06-27 10:57:17 +0000
@@ -0,0 +1,48 @@
+--source include/master-slave.inc
+
+connection master;
+SET TIME_ZONE='+00:00';
+let $MYSQLD_MASTER_DATADIR= `select @@datadir`;
+
+connection slave;
+SET TIME_ZONE='+00:00';
+let $MYSQLD_SLAVE_DATADIR= `select @@datadir`;
+
+--copy_file std_data/mysql56time.frm $MYSQLD_MASTER_DATADIR/test/mysql56time.frm
+--copy_file std_data/mysql56time.MYD $MYSQLD_MASTER_DATADIR/test/mysql56time.MYD
+--copy_file std_data/mysql56time.MYI $MYSQLD_MASTER_DATADIR/test/mysql56time.MYI
+--copy_file std_data/mysql56time.frm $MYSQLD_SLAVE_DATADIR/test/mysql56time.frm
+--copy_file std_data/mysql56time.MYD $MYSQLD_SLAVE_DATADIR/test/mysql56time.MYD
+--copy_file std_data/mysql56time.MYI $MYSQLD_SLAVE_DATADIR/test/mysql56time.MYI
+
+--copy_file std_data/mysql56datetime.frm $MYSQLD_MASTER_DATADIR/test/mysql56datetime.frm
+--copy_file std_data/mysql56datetime.MYD $MYSQLD_MASTER_DATADIR/test/mysql56datetime.MYD
+--copy_file std_data/mysql56datetime.MYI $MYSQLD_MASTER_DATADIR/test/mysql56datetime.MYI
+--copy_file std_data/mysql56datetime.frm $MYSQLD_SLAVE_DATADIR/test/mysql56datetime.frm
+--copy_file std_data/mysql56datetime.MYD $MYSQLD_SLAVE_DATADIR/test/mysql56datetime.MYD
+--copy_file std_data/mysql56datetime.MYI $MYSQLD_SLAVE_DATADIR/test/mysql56datetime.MYI
+
+--copy_file std_data/mysql56timestamp.frm $MYSQLD_MASTER_DATADIR/test/mysql56timestamp.frm
+--copy_file std_data/mysql56timestamp.MYD $MYSQLD_MASTER_DATADIR/test/mysql56timestamp.MYD
+--copy_file std_data/mysql56timestamp.MYI $MYSQLD_MASTER_DATADIR/test/mysql56timestamp.MYI
+--copy_file std_data/mysql56timestamp.frm $MYSQLD_SLAVE_DATADIR/test/mysql56timestamp.frm
+--copy_file std_data/mysql56timestamp.MYD $MYSQLD_SLAVE_DATADIR/test/mysql56timestamp.MYD
+--copy_file std_data/mysql56timestamp.MYI $MYSQLD_SLAVE_DATADIR/test/mysql56timestamp.MYI
+
+connection master;
+INSERT INTO mysql56time VALUES ('01:01:01','01:01:01.1','01:01:01.11','01:01:01.111','01:01:01.1111','01:01:01.11111','01:01:01.111111');
+INSERT INTO mysql56datetime VALUES ('2001-01-01 01:01:01','2001-01-01 01:01:01.1','2001-01-01 01:01:01.11','2001-01-01 01:01:01.111','2001-01-01 01:01:01.1111','2001-01-01 01:01:01.11111','2001-01-01 01:01:01.111111');
+INSERT INTO mysql56timestamp VALUES ('2001-01-01 01:01:01','2001-01-01 01:01:01.1','2001-01-01 01:01:01.11','2001-01-01 01:01:01.111','2001-01-01 01:01:01.1111','2001-01-01 01:01:01.11111','2001-01-01 01:01:01.111111');
+sync_slave_with_master;
+
+connection slave;
+--query_vertical SELECT * FROM mysql56time
+--query_vertical SELECT * FROM mysql56datetime
+--query_vertical SELECT * FROM mysql56timestamp
+
+connection master;
+DROP TABLE mysql56time;
+DROP TABLE mysql56datetime;
+DROP TABLE mysql56timestamp;
+
+--source include/rpl_end.inc

=== added file 'mysql-test/t/type_temporal_mysql56.test'
--- mysql-test/t/type_temporal_mysql56.test	1970-01-01 00:00:00 +0000
+++ mysql-test/t/type_temporal_mysql56.test	2013-06-27 10:55:55 +0000
@@ -0,0 +1,23 @@
+
+let $MYSQLD_DATADIR= `select @@datadir`;
+--copy_file std_data/mysql56time.frm $MYSQLD_DATADIR/test/mysql56time.frm
+--copy_file std_data/mysql56time.MYD $MYSQLD_DATADIR/test/mysql56time.MYD
+--copy_file std_data/mysql56time.MYI $MYSQLD_DATADIR/test/mysql56time.MYI
+SHOW CREATE TABLE mysql56time;
+--query_vertical SELECT * FROM mysql56time
+DROP TABLE mysql56time;
+
+--copy_file std_data/mysql56datetime.frm $MYSQLD_DATADIR/test/mysql56datetime.frm
+--copy_file std_data/mysql56datetime.MYD $MYSQLD_DATADIR/test/mysql56datetime.MYD
+--copy_file std_data/mysql56datetime.MYI $MYSQLD_DATADIR/test/mysql56datetime.MYI
+SHOW CREATE TABLE mysql56datetime;
+--query_vertical SELECT * FROM mysql56datetime
+DROP TABLE mysql56datetime;
+
+--copy_file std_data/mysql56timestamp.frm $MYSQLD_DATADIR/test/mysql56timestamp.frm
+--copy_file std_data/mysql56timestamp.MYD $MYSQLD_DATADIR/test/mysql56timestamp.MYD
+--copy_file std_data/mysql56timestamp.MYI $MYSQLD_DATADIR/test/mysql56timestamp.MYI
+SET TIME_ZONE='+00:00';
+SHOW CREATE TABLE mysql56timestamp;
+--query_vertical SELECT * FROM mysql56timestamp
+DROP TABLE mysql56timestamp;

=== modified file 'sql-common/my_time.c'
--- sql-common/my_time.c	2013-04-07 12:00:16 +0000
+++ sql-common/my_time.c	2013-06-27 12:42:14 +0000
@@ -203,6 +203,29 @@ static uint skip_digits(const char **str
   return s - start;
 }
 
+
+/**
+  Check datetime, date, or normalized time (i.e. time without days) range.
+  @param ltime   Datetime value.
+  @returns
+  @retval   FALSE on success
+  @retval   TRUE  on error
+*/
+my_bool check_datetime_range(const MYSQL_TIME *ltime)
+{
+  /*
+    In case of MYSQL_TIMESTAMP_TIME hour value can be up to TIME_MAX_HOUR.
+    In case of MYSQL_TIMESTAMP_DATETIME it cannot be bigger than 23.
+  */
+  return
+    ltime->year > 9999 || ltime->month > 12  || ltime->day > 31 || 
+    ltime->minute > 59 || ltime->second > 59 ||
+    ltime->second_part > TIME_MAX_SECOND_PART ||
+    (ltime->hour >
+     (ltime->time_type == MYSQL_TIMESTAMP_TIME ? TIME_MAX_HOUR : 23));
+}
+
+
 /*
   Convert a timestamp string to a MYSQL_TIME value.
 
@@ -332,9 +355,14 @@ str_to_datetime(const char *str, uint le
     uint second_part;
     const char *start= ++str;
     *was_cut= get_digits(&second_part, &number_of_fields, &str, end, 6);
-    if (str - start < 6)
-      second_part*= log_10_int[6 - (str - start)];
-    l_time->second_part= second_part;
+    if (number_of_fields == 7)
+    {
+      if (str - start < 6)
+        second_part*= log_10_int[6 - (str - start)];
+      l_time->second_part= second_part;
+    }
+    else
+      l_time->second_part= 0;
     if (skip_digits(&str, end))
       *was_cut= 1;
   }
@@ -1101,6 +1129,27 @@ int my_TIME_to_str(const MYSQL_TIME *l_t
 }
 
 
+/**
+  Print a timestamp with an optional fractional part: XXXXX[.YYYYY]
+
+  @param      tm  The timestamp value to print.
+  @param  OUT to  The string pointer to print at. 
+  @param      dec Precision, in the range 0..6.
+  @return         The length of the result string.
+*/
+int my_timeval_to_str(const struct timeval *tm, char *to, uint dec)
+{
+  char *pos= to + sprintf(to, "%d", (int) tm->tv_sec);
+  if (dec)
+  {
+    *pos++= '.';
+    pos= fmt_number((uint) sec_part_shift(tm->tv_usec, dec), pos, dec);
+  }
+  *pos= '\0';
+  return (int) (pos - to);
+}
+
+
 /*
   Convert datetime value specified as number to broken-down TIME
   representation and form value of DATETIME type as side-effect.

=== modified file 'sql/CMakeLists.txt'
--- sql/CMakeLists.txt	2013-06-06 19:32:29 +0000
+++ sql/CMakeLists.txt	2013-06-27 12:13:26 +0000
@@ -37,7 +37,7 @@ IF(SSL_DEFINES)
 ENDIF()
 
 SET (SQL_SOURCE
-              ../sql-common/client.c derror.cc des_key_file.cc
+              ../sql-common/client.c compat56.cc derror.cc des_key_file.cc
                discover.cc ../libmysql/errmsg.c field.cc  field_conv.cc 
                filesort_utils.cc
                filesort.cc gstream.cc sha2.cc

=== added file 'sql/compat56.cc'
--- sql/compat56.cc	1970-01-01 00:00:00 +0000
+++ sql/compat56.cc	2013-06-27 12:42:32 +0000
@@ -0,0 +1,445 @@
+/*
+   Copyright (c) 2004, 2012, Oracle and/or its affiliates.
+   Copyright (c) 2013, MariaDB Foundation.
+
+ 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
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
+
+#include "my_global.h"
+#include "compat56.h"
+#include "myisampack.h"
+#include "my_time.h"
+
+/*** MySQL56 TIME low-level memory and disk representation routines ***/
+
+/*
+  In-memory format:
+
+   1  bit sign          (Used for sign, when on disk)
+   1  bit unused        (Reserved for wider hour range, e.g. for intervals)
+   10 bit hour          (0-836)
+   6  bit minute        (0-59)
+   6  bit second        (0-59)
+  24  bits microseconds (0-999999)
+
+ Total: 48 bits = 6 bytes
+   Suhhhhhh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
+*/
+
+
+/**
+  Convert time value to MySQL56 numeric packed representation.
+  
+  @param    ltime   The value to convert.
+  @return           Numeric packed representation.
+*/
+longlong TIME_to_longlong_time_packed(const MYSQL_TIME *ltime)
+{
+  /* If month is 0, we mix day with hours: "1 00:10:10" -> "24:00:10" */
+  long hms= (((ltime->month ? 0 : ltime->day * 24) + ltime->hour) << 12) |
+            (ltime->minute << 6) | ltime->second;
+  longlong tmp= MY_PACKED_TIME_MAKE(hms, ltime->second_part);
+  return ltime->neg ? -tmp : tmp;
+}
+
+
+
+/**
+  Convert MySQL56 time packed numeric representation to time.
+
+  @param  OUT ltime  The MYSQL_TIME variable to set.
+  @param      tmp    The packed numeric representation.
+*/
+void TIME_from_longlong_time_packed(MYSQL_TIME *ltime, longlong tmp)
+{
+  long hms;
+  if ((ltime->neg= (tmp < 0)))
+    tmp= -tmp;
+  hms= MY_PACKED_TIME_GET_INT_PART(tmp);
+  ltime->year=   (uint) 0;
+  ltime->month=  (uint) 0;
+  ltime->day=    (uint) 0;
+  ltime->hour=   (uint) (hms >> 12) % (1 << 10); /* 10 bits starting at 12th */
+  ltime->minute= (uint) (hms >> 6)  % (1 << 6);  /* 6 bits starting at 6th   */
+  ltime->second= (uint)  hms        % (1 << 6);  /* 6 bits starting at 0th   */
+  ltime->second_part= MY_PACKED_TIME_GET_FRAC_PART(tmp);
+  ltime->time_type= MYSQL_TIMESTAMP_TIME;
+}
+
+
+/**
+  Calculate binary size of MySQL56 packed numeric time representation.
+  
+  @param   dec   Precision.
+*/
+uint my_time_binary_length(uint dec)
+{
+  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+  return 3 + (dec + 1) / 2;
+}
+
+
+/*
+  On disk we convert from signed representation to unsigned
+  representation using TIMEF_OFS, so all values become binary comparable.
+*/
+#define TIMEF_OFS 0x800000000000LL
+#define TIMEF_INT_OFS 0x800000LL
+
+
+/**
+  Convert MySQL56 in-memory numeric time representation to on-disk representation
+  
+  @param       nr   Value in packed numeric time format.
+  @param   OUT ptr  The buffer to put value at.
+  @param       dec  Precision.
+*/
+void my_time_packed_to_binary(longlong nr, uchar *ptr, uint dec)
+{
+  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+  /* Make sure the stored value was previously properly rounded or truncated */
+  DBUG_ASSERT((MY_PACKED_TIME_GET_FRAC_PART(nr) % 
+              (int) log_10_int[TIME_SECOND_PART_DIGITS - dec]) == 0);
+
+  switch (dec)
+  {
+  case 0:
+  default:
+    mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
+    break;
+
+  case 1:
+  case 2:
+    mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
+    ptr[3]= (unsigned char) (char) (MY_PACKED_TIME_GET_FRAC_PART(nr) / 10000);
+    break;
+
+  case 4:
+  case 3:
+    mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
+    mi_int2store(ptr + 3, MY_PACKED_TIME_GET_FRAC_PART(nr) / 100);
+    break;
+
+  case 5:
+  case 6:
+    mi_int6store(ptr, nr + TIMEF_OFS);
+    break;
+  }
+}
+
+
+/**
+  Convert MySQL56 on-disk time representation to in-memory packed numeric 
+  representation.
+  
+  @param   ptr  The pointer to read the value at.
+  @param   dec  Precision.
+  @return       Packed numeric time representation.
+*/
+longlong my_time_packed_from_binary(const uchar *ptr, uint dec)
+{
+  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+
+  switch (dec)
+  {
+  case 0:
+  default:
+    {
+      longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
+      return MY_PACKED_TIME_MAKE_INT(intpart);
+    }
+  case 1:
+  case 2:
+    {
+      longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
+      int frac= (uint) ptr[3];
+      if (intpart < 0 && frac)
+      {
+        /*
+          Negative values are stored with reverse fractional part order,
+          for binary sort compatibility.
+
+            Disk value  intpart frac   Time value   Memory value
+            800000.00    0      0      00:00:00.00  0000000000.000000
+            7FFFFF.FF   -1      255   -00:00:00.01  FFFFFFFFFF.FFD8F0
+            7FFFFF.9D   -1      99    -00:00:00.99  FFFFFFFFFF.F0E4D0
+            7FFFFF.00   -1      0     -00:00:01.00  FFFFFFFFFF.000000
+            7FFFFE.FF   -1      255   -00:00:01.01  FFFFFFFFFE.FFD8F0
+            7FFFFE.F6   -2      246   -00:00:01.10  FFFFFFFFFE.FE7960
+
+            Formula to convert fractional part from disk format
+            (now stored in "frac" variable) to absolute value: "0x100 - frac".
+            To reconstruct in-memory value, we shift
+            to the next integer value and then substruct fractional part.
+        */
+        intpart++;    /* Shift to the next integer value */
+        frac-= 0x100; /* -(0x100 - frac) */
+      }
+      return MY_PACKED_TIME_MAKE(intpart, frac * 10000);
+    }
+
+  case 3:
+  case 4:
+    {
+      longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
+      int frac= mi_uint2korr(ptr + 3);
+      if (intpart < 0 && frac)
+      {
+        /*
+          Fix reverse fractional part order: "0x10000 - frac".
+          See comments for FSP=1 and FSP=2 above.
+        */
+        intpart++;      /* Shift to the next integer value */
+        frac-= 0x10000; /* -(0x10000-frac) */
+      }
+      return MY_PACKED_TIME_MAKE(intpart, frac * 100);
+    }
+
+  case 5:
+  case 6:
+    return ((longlong) mi_uint6korr(ptr)) - TIMEF_OFS;
+  }
+}
+
+
+/*** MySQL56 DATETIME low-level memory and disk representation routines ***/
+
+/*
+    1 bit  sign            (used when on disk)
+   17 bits year*13+month   (year 0-9999, month 0-12)
+    5 bits day             (0-31)
+    5 bits hour            (0-23)
+    6 bits minute          (0-59)
+    6 bits second          (0-59)
+   24 bits microseconds    (0-999999)
+
+   Total: 64 bits = 8 bytes
+
+   SYYYYYYY.YYYYYYYY.YYdddddh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
+*/
+
+/**
+  Convert datetime to MySQL56 packed numeric datetime representation.
+  @param ltime  The value to convert.
+  @return       Packed numeric representation of ltime.
+*/
+longlong TIME_to_longlong_datetime_packed(const MYSQL_TIME *ltime)
+{
+  longlong ymd= ((ltime->year * 13 + ltime->month) << 5) | ltime->day;
+  longlong hms= (ltime->hour << 12) | (ltime->minute << 6) | ltime->second;
+  longlong tmp= MY_PACKED_TIME_MAKE(((ymd << 17) | hms), ltime->second_part);
+  DBUG_ASSERT(!check_datetime_range(ltime)); /* Make sure no overflow */
+  return ltime->neg ? -tmp : tmp;
+}
+
+
+/**
+  Convert MySQL56 packed numeric datetime representation to MYSQL_TIME.
+  @param OUT  ltime The datetime variable to convert to.
+  @param      tmp   The packed numeric datetime value.
+*/
+void TIME_from_longlong_datetime_packed(MYSQL_TIME *ltime, longlong tmp)
+{
+  longlong ymd, hms;
+  longlong ymdhms, ym;
+  if ((ltime->neg= (tmp < 0)))
+    tmp= -tmp;
+
+  ltime->second_part= MY_PACKED_TIME_GET_FRAC_PART(tmp);
+  ymdhms= MY_PACKED_TIME_GET_INT_PART(tmp);
+
+  ymd= ymdhms >> 17;
+  ym= ymd >> 5;
+  hms= ymdhms % (1 << 17);
+
+  ltime->day= ymd % (1 << 5);
+  ltime->month= ym % 13;
+  ltime->year= ym / 13;
+
+  ltime->second= hms % (1 << 6);
+  ltime->minute= (hms >> 6) % (1 << 6);
+  ltime->hour= (hms >> 12);
+  
+  ltime->time_type= MYSQL_TIMESTAMP_DATETIME;
+}
+
+
+/**
+  Calculate binary size of MySQL56 packed datetime representation.
+  @param dec  Precision.
+*/
+uint my_datetime_binary_length(uint dec)
+{
+  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+  return 5 + (dec + 1) / 2;
+}
+
+
+/*
+  On disk we store as unsigned number with DATETIMEF_INT_OFS offset,
+  for HA_KETYPE_BINARY compatibilty purposes.
+*/
+#define DATETIMEF_INT_OFS 0x8000000000LL
+
+
+/**
+  Convert MySQL56 on-disk datetime representation
+  to in-memory packed numeric representation.
+
+  @param ptr   The pointer to read value at.
+  @param dec   Precision.
+  @return      In-memory packed numeric datetime representation.
+*/
+longlong my_datetime_packed_from_binary(const uchar *ptr, uint dec)
+{
+  longlong intpart= mi_uint5korr(ptr) - DATETIMEF_INT_OFS;
+  int frac;
+  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+  switch (dec)
+  {
+  case 0:
+  default:
+    return MY_PACKED_TIME_MAKE_INT(intpart);
+  case 1:
+  case 2:
+    frac= ((int) (signed char) ptr[5]) * 10000;
+    break;
+  case 3:
+  case 4:
+    frac= mi_sint2korr(ptr + 5) * 100;
+    break;
+  case 5:
+  case 6:
+    frac= mi_sint3korr(ptr + 5);
+    break;
+  }
+  return MY_PACKED_TIME_MAKE(intpart, frac);
+}
+
+
+/**
+  Store MySQL56 in-memory numeric packed datetime representation to disk.
+
+  @param      nr  In-memory numeric packed datetime representation.
+  @param OUT  ptr The pointer to store at.
+  @param      dec Precision, 1-6.
+*/
+void my_datetime_packed_to_binary(longlong nr, uchar *ptr, uint dec)
+{
+  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+  /* The value being stored must have been properly rounded or truncated */
+  DBUG_ASSERT((MY_PACKED_TIME_GET_FRAC_PART(nr) %
+              (int) log_10_int[TIME_SECOND_PART_DIGITS - dec]) == 0);
+
+  mi_int5store(ptr, MY_PACKED_TIME_GET_INT_PART(nr) + DATETIMEF_INT_OFS);
+  switch (dec)
+  {
+  case 0:
+  default:
+    break;
+  case 1:
+  case 2:
+    ptr[5]= (unsigned char) (char) (MY_PACKED_TIME_GET_FRAC_PART(nr) / 10000);
+    break;
+  case 3:
+  case 4:
+    mi_int2store(ptr + 5, MY_PACKED_TIME_GET_FRAC_PART(nr) / 100);
+    break;
+  case 5:
+  case 6:
+    mi_int3store(ptr + 5, MY_PACKED_TIME_GET_FRAC_PART(nr));
+  }
+}
+
+
+/*** MySQL56 TIMESTAMP low-level memory and disk representation routines ***/
+
+/**
+  Calculate on-disk size of a timestamp value.
+
+  @param  dec  Precision.
+*/
+uint my_timestamp_binary_length(uint dec)
+{
+  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+  return 4 + (dec + 1) / 2;
+}
+
+
+/**
+  Convert MySQL56 binary timestamp representation to in-memory representation.
+
+  @param  OUT tm  The variable to convert to.
+  @param      ptr The pointer to read the value from.
+  @param      dec Precision.
+*/
+void my_timestamp_from_binary(struct timeval *tm, const uchar *ptr, uint dec)
+{
+  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+  tm->tv_sec= mi_uint4korr(ptr);
+  switch (dec)
+  {
+    case 0:
+    default:
+      tm->tv_usec= 0;
+      break;
+    case 1:
+    case 2:
+      tm->tv_usec= ((int) ptr[4]) * 10000;
+      break;
+    case 3:
+    case 4:
+      tm->tv_usec= mi_sint2korr(ptr + 4) * 100;
+      break;
+    case 5:
+    case 6:
+      tm->tv_usec= mi_sint3korr(ptr + 4);
+  }
+}
+
+
+/**
+  Convert MySQL56 in-memory timestamp representation to on-disk representation.
+
+  @param        tm   The value to convert.
+  @param  OUT   ptr  The pointer to store the value to.
+  @param        dec  Precision.
+*/
+void my_timestamp_to_binary(const struct timeval *tm, uchar *ptr, uint dec)
+{
+  DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+  /* Stored value must have been previously properly rounded or truncated */
+  DBUG_ASSERT((tm->tv_usec %
+               (int) log_10_int[TIME_SECOND_PART_DIGITS - dec]) == 0);
+  mi_int4store(ptr, tm->tv_sec);
+  switch (dec)
+  {
+    case 0:
+    default:
+      break;
+    case 1:
+    case 2:
+      ptr[4]= (unsigned char) (char) (tm->tv_usec / 10000);
+      break;
+    case 3:
+    case 4:
+      mi_int2store(ptr + 4, tm->tv_usec / 100);
+      break;
+      /* Impossible second precision. Fall through */
+    case 5:
+    case 6:
+      mi_int3store(ptr + 4, tm->tv_usec);
+  }
+}
+
+/****************************************/

=== added file 'sql/compat56.h'
--- sql/compat56.h	1970-01-01 00:00:00 +0000
+++ sql/compat56.h	2013-06-27 12:41:04 +0000
@@ -0,0 +1,46 @@
+#ifndef COMPAT56_H_INCLUDED
+#define COMPAT56_H_INCLUDED
+/*
+   Copyright (c) 2004, 2012, Oracle and/or its affiliates.
+   Copyright (c) 2013  MariaDB Foundation.
+
+ 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
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
+
+
+/** MySQL56 routines and macros **/
+#define MY_PACKED_TIME_GET_INT_PART(x)     ((x) >> 24)
+#define MY_PACKED_TIME_GET_FRAC_PART(x)    ((x) % (1LL << 24))
+#define MY_PACKED_TIME_MAKE(i, f)          ((((longlong) (i)) << 24) + (f))
+#define MY_PACKED_TIME_MAKE_INT(i)         ((((longlong) (i)) << 24))
+
+longlong TIME_to_longlong_datetime_packed(const MYSQL_TIME *);
+longlong TIME_to_longlong_time_packed(const MYSQL_TIME *);
+
+void TIME_from_longlong_datetime_packed(MYSQL_TIME *ltime, longlong nr);
+void TIME_from_longlong_time_packed(MYSQL_TIME *ltime, longlong nr);
+
+void my_datetime_packed_to_binary(longlong nr, uchar *ptr, uint dec);
+longlong my_datetime_packed_from_binary(const uchar *ptr, uint dec);
+uint my_datetime_binary_length(uint dec);
+
+void my_time_packed_to_binary(longlong nr, uchar *ptr, uint dec);
+longlong my_time_packed_from_binary(const uchar *ptr, uint dec);
+uint my_time_binary_length(uint dec);
+
+void my_timestamp_to_binary(const struct timeval *tm, uchar *ptr, uint dec);
+void my_timestamp_from_binary(struct timeval *tm, const uchar *ptr, uint dec);
+uint my_timestamp_binary_length(uint dec);
+/** End of MySQL routines and macros **/
+
+#endif /* COMPAT56_H_INCLUDED */

=== modified file 'sql/field.cc'
--- sql/field.cc	2013-06-06 19:32:29 +0000
+++ sql/field.cc	2013-07-01 06:01:51 +0000
@@ -87,6 +87,7 @@ const char field_separator=',';
 #define FIELDTYPE_NUM (FIELDTYPE_TEAR_FROM + (255 - FIELDTYPE_TEAR_TO))
 static inline int field_type2index (enum_field_types field_type)
 {
+  field_type= real_type_to_type(field_type);
   return (field_type < FIELDTYPE_TEAR_FROM ?
           field_type :
           ((int)FIELDTYPE_TEAR_FROM) + (field_type - FIELDTYPE_TEAR_TO) - 1);
@@ -947,8 +948,10 @@ static enum_field_types field_types_merg
 enum_field_types Field::field_type_merge(enum_field_types a,
                                          enum_field_types b)
 {
-  DBUG_ASSERT(a < FIELDTYPE_TEAR_FROM || a > FIELDTYPE_TEAR_TO);
-  DBUG_ASSERT(b < FIELDTYPE_TEAR_FROM || b > FIELDTYPE_TEAR_TO);
+  DBUG_ASSERT(real_type_to_type(a) < FIELDTYPE_TEAR_FROM ||
+              real_type_to_type(a) > FIELDTYPE_TEAR_TO);
+  DBUG_ASSERT(real_type_to_type(b) < FIELDTYPE_TEAR_FROM ||
+              real_type_to_type(b) > FIELDTYPE_TEAR_TO);
   return field_types_merge_rules[field_type2index(a)]
                                 [field_type2index(b)];
 }
@@ -1042,8 +1045,8 @@ CPP_UNNAMED_NS_END
 
 Item_result Field::result_merge_type(enum_field_types field_type)
 {
-  DBUG_ASSERT(field_type < FIELDTYPE_TEAR_FROM || field_type
-              > FIELDTYPE_TEAR_TO);
+  DBUG_ASSERT(real_type_to_type(field_type) < FIELDTYPE_TEAR_FROM ||
+              real_type_to_type(field_type) > FIELDTYPE_TEAR_TO);
   return field_types_result_type[field_type2index(field_type)];
 }
 
@@ -1130,6 +1133,111 @@ void Field::make_sort_key(uchar *buff,ui
 
 
 /**
+  @brief
+  Determine the relative position of the field value in a numeric interval
+
+  @details
+  The function returns a double number between 0.0 and 1.0 as the relative
+  position of the value of the this field in the numeric interval of [min,max].
+  If the value is not in the interval the the function returns 0.0 when
+  the value is less than min, and, 1.0 when the value is greater than max.
+
+  @param  min  value of the left end of the interval
+  @param  max  value of the right end of the interval
+
+  @return
+  relative position of the field value in the numeric interval [min,max] 
+*/
+
+double Field::pos_in_interval_val_real(Field *min, Field *max)
+{
+  double n, d;
+  n= val_real() - min->val_real();
+  if (n < 0)
+    return 0.0;
+  d= max->val_real() - min->val_real();
+  if (d <= 0)
+    return 1.0;
+  return min(n/d, 1.0);
+}
+
+
+static
+inline ulonglong char_prefix_to_ulonglong(uchar *src)
+{
+  uint sz= sizeof(ulonglong);
+  for (uint i= 0; i < sz/2; i++)
+  {
+    uchar tmp= src[i];
+    src[i]= src[sz-1-i];
+    src[sz-1-i]= tmp;
+  }
+  return uint8korr(src); 
+}
+
+
+/**
+  @brief
+  Determine the relative position of the field value in a string interval
+
+  @details
+  The function returns a double number between 0.0 and 1.0 as the relative
+  position of the value of the this field in the string interval of [min,max].
+  If the value is not in the interval the the function returns 0.0 when
+  the value is less than min, and, 1.0 when the value is greater than max.
+
+  @note
+  To calculate the relative position of the string value v in the interval
+  [min, max] the function first converts the beginning of these three
+  strings v, min, max into the strings that are used for byte comparison.
+  For each string not more sizeof(ulonglong) first bytes are taken
+  from the result of conversion. Then these bytes are interpreted as the
+  big-endian representation of an ulonglong integer. The values of these
+  integer numbers obtained for the strings v, min, max are used to calculate
+  the position of v in [min,max] in the same way is it's done for numeric
+  fields (see Field::pos_in_interval_val_real).
+
+  @todo
+  Improve the procedure for the case when min and max have the same
+  beginning
+     
+  @param  min  value of the left end of the interval
+  @param  max  value of the right end of the interval
+
+  @return
+  relative position of the field value in the string interval [min,max] 
+*/
+
+double Field::pos_in_interval_val_str(Field *min, Field *max, uint data_offset)
+{
+  uchar mp_prefix[sizeof(ulonglong)];
+  uchar minp_prefix[sizeof(ulonglong)];
+  uchar maxp_prefix[sizeof(ulonglong)];
+  ulonglong mp, minp, maxp;
+  my_strnxfrm(charset(), mp_prefix, sizeof(mp),
+              ptr + data_offset,
+              data_length());
+  my_strnxfrm(charset(), minp_prefix, sizeof(minp),
+              min->ptr + data_offset,
+              min->data_length());
+  my_strnxfrm(charset(), maxp_prefix, sizeof(maxp),
+              max->ptr + data_offset,
+              max->data_length());
+  mp= char_prefix_to_ulonglong(mp_prefix);
+  minp= char_prefix_to_ulonglong(minp_prefix);
+  maxp= char_prefix_to_ulonglong(maxp_prefix);
+  double n, d;
+  n= mp - minp;
+  if (n < 0)
+    return 0.0;
+  d= maxp - minp;
+  if (d <= 0)
+    return 1.0;
+  return min(n/d, 1.0);
+}
+
+
+/**
   Numeric fields base class constructor.
 */
 Field_num::Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
@@ -1275,36 +1383,6 @@ bool Field_num::get_int(CHARSET_INFO *cs
 
 
 /**
-  @brief
-  Determine the relative position of the field value in a numeric interval
-
-  @details
-  The function returns a double number between 0.0 and 1.0 as the relative
-  position of the value of the this field in the numeric interval of [min,max].
-  If the value is not in the interval the the function returns 0.0 when
-  the value is less than min, and, 1.0 when the value is greater than max.
-
-  @param  min  value of the left end of the interval
-  @param  max  value of the right end of the interval
-
-  @return
-  relative position of the field value in the numeric interval [min,max] 
-*/
-
-double Field_num::pos_in_interval(Field *min, Field *max)
-{
-  double n, d;
-  n= val_real() - min->val_real();
-  if (n < 0)
-    return 0.0;
-  d= max->val_real() - min->val_real();
-  if (d <= 0)
-    return 1.0;
-  return min(n/d, 1.0);
-}
-
-
-/**
   Process decimal library return codes and issue warnings for overflow and
   truncation.
 
@@ -4485,13 +4563,12 @@ Field_timestamp::Field_timestamp(uchar *
                                  uchar *null_ptr_arg, uchar null_bit_arg,
 				 enum utype unireg_check_arg,
 				 const char *field_name_arg,
-				 TABLE_SHARE *share,
-				 CHARSET_INFO *cs)
-  :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
-	     unireg_check_arg, field_name_arg, cs)
+				 TABLE_SHARE *share)
+  :Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+                  unireg_check_arg, field_name_arg)
 {
   /* For 4.0 MYD and 4.0 InnoDB compatibility */
-  flags|= UNSIGNED_FLAG | BINARY_FLAG;
+  flags|= UNSIGNED_FLAG;
   if (unireg_check != NONE)
   {
     /*
@@ -4636,6 +4713,7 @@ String *Field_timestamp::val_str(String
 {
   MYSQL_TIME ltime;
   uint32 temp, temp2;
+  uint dec;
   char *to;
 
   val_buffer->alloc(field_length+1);
@@ -4690,6 +4768,16 @@ String *Field_timestamp::val_str(String
   *to++= (char) ('0'+(char) (temp));
   *to= 0;
   val_buffer->set_charset(&my_charset_numeric);
+
+  if ((dec= decimals()))
+  {
+    ulong sec_part= (ulong) sec_part_shift(ltime.second_part, dec);
+    char *buf= const_cast<char*>(val_buffer->ptr() + MAX_DATETIME_WIDTH);
+    for (int i= dec; i > 0; i--, sec_part/= 10)
+    buf[i]= (char)(sec_part % 10) + '0';
+    buf[0]= '.';
+    buf[dec + 1]= 0;
+  }
   return val_buffer;
 }
 
@@ -4743,7 +4831,14 @@ void Field_timestamp::sort_string(uchar
 
 void Field_timestamp::sql_type(String &res) const
 {
-  res.set_ascii(STRING_WITH_LEN("timestamp"));
+  if (!decimals())
+  {
+    res.set_ascii(STRING_WITH_LEN("timestamp"));
+    return;
+  }
+  CHARSET_INFO *cs=res.charset();
+  res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
+                                "timestamp(%u)", decimals()));
 }
 
 
@@ -4783,13 +4878,6 @@ void Field_timestamp::set_explicit_defau
   set_has_explicit_value();
 }
 
-void Field_timestamp_hires::sql_type(String &res) const
-{
-  CHARSET_INFO *cs=res.charset();
-  res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
-                                "timestamp(%u)", dec));
-}
-
 #ifdef NOT_USED
 static void store_native(ulonglong num, uchar *to, uint bytes)
 {
@@ -4883,7 +4971,7 @@ my_time_t Field_timestamp_hires::get_tim
   return mi_uint4korr(ptr);
 }
 
-double Field_timestamp_hires::val_real(void)
+double Field_timestamp_with_dec::val_real(void)
 {
   MYSQL_TIME ltime;
   if (get_date(&ltime, TIME_NO_ZERO_DATE))
@@ -4894,31 +4982,14 @@ double Field_timestamp_hires::val_real(v
          ltime.minute * 1e2 + ltime.second + ltime.second_part*1e-6;
 }
 
-String *Field_timestamp_hires::val_str(String *val_buffer, String *val_ptr)
-{
-  String *tmp= Field_timestamp::val_str(val_buffer, val_ptr);
-  ulong sec_part= (ulong)read_bigendian(ptr+4, sec_part_bytes[dec]);
-  
-  if (tmp->ptr() == zero_timestamp)
-    return tmp;
-
-  char *buf= const_cast<char*>(tmp->ptr() + MAX_DATETIME_WIDTH);
-  for (int i=dec; i>0; i--, sec_part/=10)
-    buf[i]= (char)(sec_part % 10) + '0';
-  buf[0]= '.';
-  buf[dec+1]= 0;
-  return tmp;
-}
-
-
-my_decimal *Field_timestamp_hires::val_decimal(my_decimal *d)
+my_decimal *Field_timestamp_with_dec::val_decimal(my_decimal *d)
 {
   MYSQL_TIME ltime;
   get_date(&ltime, 0);
   return TIME_to_my_decimal(&ltime, d);
 }
  
-int Field_timestamp_hires::store_decimal(const my_decimal *d)
+int Field_timestamp::store_decimal(const my_decimal *d)
 {
   ulonglong nr;
   ulong sec_part;
@@ -4941,7 +5012,7 @@ int Field_timestamp_hires::store_decimal
   return store_TIME_with_warning(thd, &ltime, &str, error, tmp != -1);
 }
 
-int Field_timestamp_hires::set_time()
+int Field_timestamp_with_dec::set_time()
 {
   THD *thd= get_thd();
   set_notnull();
@@ -4949,7 +5020,7 @@ int Field_timestamp_hires::set_time()
   return 0;
 }
 
-bool Field_timestamp_hires::send_binary(Protocol *protocol)
+bool Field_timestamp_with_dec::send_binary(Protocol *protocol)
 {
   MYSQL_TIME ltime;
   Field_timestamp::get_date(&ltime, 0);
@@ -4970,23 +5041,72 @@ int Field_timestamp_hires::cmp(const uch
 }
 
 
-void Field_timestamp_hires::sort_string(uchar *to,uint length)
-{
-  DBUG_ASSERT(length == Field_timestamp_hires::pack_length());
-  memcpy(to, ptr, length);
-}
-
 uint32 Field_timestamp_hires::pack_length() const
 {
   return 4 + sec_part_bytes[dec];
 }
 
-void Field_timestamp_hires::make_field(Send_field *field)
+void Field_timestamp_with_dec::make_field(Send_field *field)
 {
   Field::make_field(field);
   field->decimals= dec;
 }
 
+
+/*************************************************************
+** MySQL-5.6 compatible TIMESTAMP(N)
+**************************************************************/
+
+void Field_timestampf::store_TIME(my_time_t timestamp, ulong sec_part)
+{
+  struct timeval tm;
+  tm.tv_sec= timestamp;
+  tm.tv_usec= sec_part;
+  my_timeval_trunc(&tm, dec);
+  my_timestamp_to_binary(&tm, ptr, dec);
+}
+
+
+my_time_t Field_timestampf::get_timestamp(ulong *sec_part) const
+{
+  struct timeval tm;
+  my_timestamp_from_binary(&tm, ptr, dec);
+  *sec_part= tm.tv_usec;
+  return tm.tv_sec;
+}
+
+
+/*************************************************************/
+uint Field_temporal::is_equal(Create_field *new_field)
+{
+  return new_field->sql_type == real_type() &&
+         new_field->length == max_display_length();
+}
+
+
+void Field_temporal::set_warnings(MYSQL_ERROR::enum_warning_level trunc_level,
+                                  const ErrConv *str, int was_cut,
+                                  timestamp_type ts_type)
+{
+  /*
+    error code logic:
+    MYSQL_TIME_WARN_TRUNCATED means that the value was not a date/time at all.
+      it will be stored as zero date/time.
+    MYSQL_TIME_WARN_OUT_OF_RANGE means that the value was a date/time,
+      that is, it was parsed as such, but the value was invalid.
+
+    Also, MYSQL_TIME_WARN_TRUNCATED is used when storing a DATETIME in
+    a DATE field and non-zero time part is thrown away.
+  */
+  if (was_cut & MYSQL_TIME_WARN_TRUNCATED)
+    set_datetime_warning(trunc_level, WARN_DATA_TRUNCATED,
+                         str, mysql_type_to_time_type(type()), 1);
+  if (was_cut & MYSQL_TIME_WARN_OUT_OF_RANGE)
+    set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE,
+                         str, mysql_type_to_time_type(type()), 1);
+}
+
+
 /*
   Store string into a date/time field
 
@@ -4997,21 +5117,21 @@ void Field_timestamp_hires::make_field(S
     3  Datetime value that was cut (warning level NOTE)
        This is used by opt_range.cc:get_mm_leaf().
 */
-int Field_temporal::store_TIME_with_warning(MYSQL_TIME *ltime,
-                                            const ErrConv *str,
-                                            int was_cut, int have_smth_to_conv)
+int Field_temporal_with_date::store_TIME_with_warning(MYSQL_TIME *ltime,
+                                                      const ErrConv *str,
+                                                      int was_cut,
+                                                      int have_smth_to_conv)
 {
   MYSQL_ERROR::enum_warning_level trunc_level= MYSQL_ERROR::WARN_LEVEL_WARN;
   int ret= 2;
   
   ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
 
-  if (was_cut == 0 &&
-      have_smth_to_conv == 0 &&
-      mysql_type_to_time_type(type()) != MYSQL_TIMESTAMP_TIME) // special case: zero date
+  if (was_cut == 0 && have_smth_to_conv == 0) // special case: zero date
+  {
     was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE;
-  else
-  if (!have_smth_to_conv)
+  }
+  else if (!have_smth_to_conv)
   {
     bzero(ltime, sizeof(*ltime));
     was_cut=  MYSQL_TIME_WARN_TRUNCATED;
@@ -5025,39 +5145,13 @@ int Field_temporal::store_TIME_with_warn
     was_cut|=  MYSQL_TIME_WARN_TRUNCATED;
     ret= 3;
   }
-  else if (!(was_cut & MYSQL_TIME_WARN_TRUNCATED) &&
-           mysql_type_to_time_type(type()) == MYSQL_TIMESTAMP_TIME &&
-           (ltime->year || ltime->month))
-  {
-    ltime->year= ltime->month= ltime->day= 0;
-    trunc_level= MYSQL_ERROR::WARN_LEVEL_NOTE;
-    was_cut|=  MYSQL_TIME_WARN_TRUNCATED;
-    ret= 3;
-  }
-
-  /*
-    error code logic:
-    MYSQL_TIME_WARN_TRUNCATED means that the value was not a date/time at all.
-      it will be stored as zero date/time.
-    MYSQL_TIME_WARN_OUT_OF_RANGE means that the value was a date/time,
-      that is, it was parsed as such, but the value was invalid.
-
-    Also, MYSQL_TIME_WARN_TRUNCATED is used when storing a DATETIME in
-    a DATE field and non-zero time part is thrown away.
-  */
-  if (was_cut & MYSQL_TIME_WARN_TRUNCATED)
-    set_datetime_warning(trunc_level, WARN_DATA_TRUNCATED,
-                         str, mysql_type_to_time_type(type()), 1);
-  if (was_cut & MYSQL_TIME_WARN_OUT_OF_RANGE)
-    set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE,
-                         str, mysql_type_to_time_type(type()), 1);
-
+  set_warnings(trunc_level, str, was_cut, mysql_type_to_time_type(type()));
   store_TIME(ltime);
   return was_cut ? ret : 0;
 }
 
 
-int Field_temporal::store(const char *from,uint len,CHARSET_INFO *cs)
+int Field_temporal_with_date::store(const char *from, uint len, CHARSET_INFO *cs)
 {
   MYSQL_TIME ltime;
   int error;
@@ -5075,7 +5169,7 @@ int Field_temporal::store(const char *fr
 }
 
 
-int Field_temporal::store(double nr)
+int Field_temporal_with_date::store(double nr)
 {
   int error= 0;
   MYSQL_TIME ltime;
@@ -5092,7 +5186,7 @@ int Field_temporal::store(double nr)
 }
 
 
-int Field_temporal::store(longlong nr, bool unsigned_val)
+int Field_temporal_with_date::store(longlong nr, bool unsigned_val)
 {
   int error;
   MYSQL_TIME ltime;
@@ -5110,7 +5204,7 @@ int Field_temporal::store(longlong nr, b
 }
 
 
-int Field_temporal::store_time_dec(MYSQL_TIME *ltime, uint dec)
+int Field_temporal_with_date::store_time_dec(MYSQL_TIME *ltime, uint dec)
 {
   int error = 0, have_smth_to_conv= 1;
   MYSQL_TIME l_time= *ltime;
@@ -5144,6 +5238,35 @@ my_decimal *Field_temporal::val_decimal(
 ** In number context: HHMMSS
 ** Stored as a 3 byte unsigned int
 ****************************************************************************/
+int Field_time::store_TIME_with_warning(MYSQL_TIME *ltime,
+                                        const ErrConv *str,
+                                        int was_cut,
+                                        int have_smth_to_conv)
+{
+  MYSQL_ERROR::enum_warning_level trunc_level= MYSQL_ERROR::WARN_LEVEL_WARN;
+  int ret= 2;
+  
+  ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+
+  if (!have_smth_to_conv)
+  {
+    bzero(ltime, sizeof(*ltime));
+    was_cut= MYSQL_TIME_WARN_TRUNCATED;
+    ret= 1;
+  }
+  else if (!(was_cut & MYSQL_TIME_WARN_TRUNCATED) &&
+           (ltime->year || ltime->month))
+  {
+    ltime->year= ltime->month= ltime->day= 0;
+    trunc_level= MYSQL_ERROR::WARN_LEVEL_NOTE;
+    was_cut|=  MYSQL_TIME_WARN_TRUNCATED;
+    ret= 3;
+  }
+  set_warnings(trunc_level, str, was_cut, MYSQL_TIMESTAMP_TIME);
+  store_TIME(ltime);
+  return was_cut ? ret : 0;
+}
+
 
 void Field_time::store_TIME(MYSQL_TIME *ltime)
 {
@@ -5229,32 +5352,16 @@ longlong Field_time::val_int(void)
   my_charset_bin
 */
 
-String *Field_time::val_str(String *val_buffer,
-			    String *val_ptr __attribute__((unused)))
+String *Field_time::val_str(String *str,
+			    String *unused __attribute__((unused)))
 {
   ASSERT_COLUMN_MARKED_FOR_READ;
   MYSQL_TIME ltime;
-  long tmp=(long) sint3korr(ptr);
-  ltime.neg= 0;
-  if (tmp < 0)
-  {
-    tmp= -tmp;
-    ltime.neg= 1;
-  }
-  ltime.year= ltime.month= 0;
-  ltime.day= (uint) 0;
-  ltime.hour= (uint) (tmp/10000);
-  ltime.minute= (uint) (tmp/100 % 100);
-  ltime.second= (uint) (tmp % 100);
-  ltime.second_part= 0;
-
-  val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH);
-  uint length= (uint) my_time_to_str(&ltime,
-                                     const_cast<char*>(val_buffer->ptr()), 0);
-  val_buffer->length(length);
-  val_buffer->set_charset(&my_charset_numeric);
-
-  return val_buffer;
+  get_date(&ltime, TIME_TIME_ONLY);
+  str->alloc(field_length + 1);
+  str->length(my_time_to_str(&ltime, const_cast<char*>(str->ptr()), decimals()));
+  str->set_charset(&my_charset_numeric);
+  return str;
 }
 
 
@@ -5297,8 +5404,8 @@ bool Field_time::get_date(MYSQL_TIME *lt
 bool Field_time::send_binary(Protocol *protocol)
 {
   MYSQL_TIME ltime;
-  Field_time::get_date(&ltime, TIME_TIME_ONLY);
-  return protocol->store_time(&ltime, 0);
+  get_date(&ltime, TIME_TIME_ONLY);
+  return protocol->store_time(&ltime, decimals());
 }
 
 
@@ -5319,7 +5426,14 @@ void Field_time::sort_string(uchar *to,u
 
 void Field_time::sql_type(String &res) const
 {
-  res.set_ascii(STRING_WITH_LEN("time"));
+  if (decimals() == 0)
+  {
+    res.set_ascii(STRING_WITH_LEN("time"));
+    return;
+  }
+  const CHARSET_INFO *cs= res.charset();
+  res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
+                               "time(%d)", decimals()));
 }
 
 int Field_time_hires::reset()
@@ -5335,7 +5449,7 @@ void Field_time_hires::store_TIME(MYSQL_
   store_bigendian(packed, ptr, Field_time_hires::pack_length());
 }
 
-int Field_time_hires::store_decimal(const my_decimal *d)
+int Field_time::store_decimal(const my_decimal *d)
 {
   ulonglong nr;
   ulong sec_part;
@@ -5354,35 +5468,23 @@ uint32 Field_time_hires::pack_length() c
   return time_hires_bytes[dec];
 }
 
-longlong Field_time_hires::val_int(void)
+longlong Field_time_with_dec::val_int(void)
 {
   ASSERT_COLUMN_MARKED_FOR_READ;
   MYSQL_TIME ltime;
-  Field_time_hires::get_date(&ltime, TIME_TIME_ONLY);
+  get_date(&ltime, TIME_TIME_ONLY);
   longlong val= TIME_to_ulonglong_time(&ltime);
   return ltime.neg ? -val : val;
 }
 
-double Field_time_hires::val_real(void)
+double Field_time_with_dec::val_real(void)
 {
   ASSERT_COLUMN_MARKED_FOR_READ;
   MYSQL_TIME ltime;
-  Field_time_hires::get_date(&ltime, TIME_TIME_ONLY);
+  get_date(&ltime, TIME_TIME_ONLY);
   return TIME_to_double(&ltime);
 }
 
-String *Field_time_hires::val_str(String *str,
-                                  String *unused __attribute__((unused)))
-{
-  ASSERT_COLUMN_MARKED_FOR_READ;
-  MYSQL_TIME ltime;
-  Field_time_hires::get_date(&ltime, TIME_TIME_ONLY);
-  str->alloc(field_length+1);
-  str->length(my_time_to_str(&ltime, (char*) str->ptr(), dec));
-  str->set_charset(&my_charset_bin);
-  return str;
-}
-
 bool Field_time_hires::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
 {
   uint32 len= pack_length();
@@ -5402,14 +5504,6 @@ bool Field_time_hires::get_date(MYSQL_TI
 }
 
 
-bool Field_time_hires::send_binary(Protocol *protocol)
-{
-  MYSQL_TIME ltime;
-  Field_time_hires::get_date(&ltime, TIME_TIME_ONLY);
-  return protocol->store_time(&ltime, dec);
-}
-
-
 int Field_time_hires::cmp(const uchar *a_ptr, const uchar *b_ptr)
 {
   ulonglong a=read_bigendian(a_ptr, Field_time_hires::pack_length());
@@ -5424,17 +5518,36 @@ void Field_time_hires::sort_string(uchar
   to[0]^= 128;
 }
 
-void Field_time_hires::sql_type(String &res) const
+void Field_time_with_dec::make_field(Send_field *field)
 {
-  CHARSET_INFO *cs=res.charset();
-  res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
-                                "time(%u)", dec));
+  Field::make_field(field);
+  field->decimals= dec;
 }
 
-void Field_time_hires::make_field(Send_field *field)
+/****************************************************************************
+** time type with fsp (MySQL-5.6 version)
+** In string context: HH:MM:SS.FFFFFF
+** In number context: HHMMSS.FFFFFF
+****************************************************************************/
+
+int Field_timef::reset()
 {
-  Field::make_field(field);
-  field->decimals= dec;
+  my_time_packed_to_binary(0, ptr, dec);
+  return 0;
+}
+
+void Field_timef::store_TIME(MYSQL_TIME *ltime)
+{
+  my_time_trunc(ltime, decimals());
+  longlong tmp= TIME_to_longlong_time_packed(ltime);
+  my_time_packed_to_binary(tmp, ptr, dec);
+}
+
+bool Field_timef::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+  longlong tmp= my_time_packed_from_binary(ptr, dec);
+  TIME_from_longlong_time_packed(ltime, tmp);
+  return false;
 }
 
 /****************************************************************************
@@ -5792,8 +5905,8 @@ bool Field_datetime::send_binary(Protoco
   Field_datetime::get_date(&tm, TIME_FUZZY_DATE);
   return protocol->store(&tm, 0);
 }
-  
-  
+
+
 double Field_datetime::val_real(void)
 {
   return (double) Field_datetime::val_int();
@@ -5901,7 +6014,14 @@ void Field_datetime::sort_string(uchar *
 
 void Field_datetime::sql_type(String &res) const
 {
-  res.set_ascii(STRING_WITH_LEN("datetime"));
+  if (decimals() == 0)
+  {
+    res.set_ascii(STRING_WITH_LEN("datetime"));
+    return;
+  }
+  CHARSET_INFO *cs= res.charset();
+  res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
+                                "datetime(%u)", decimals()));
 }
 
 
@@ -5924,7 +6044,7 @@ void Field_datetime_hires::store_TIME(MY
   store_bigendian(packed, ptr, Field_datetime_hires::pack_length());
 }
 
-int Field_datetime_hires::store_decimal(const my_decimal *d)
+int Field_temporal_with_date::store_decimal(const my_decimal *d)
 {
   ulonglong nr;
   ulong sec_part;
@@ -5949,38 +6069,38 @@ int Field_datetime_hires::store_decimal(
   return store_TIME_with_warning(&ltime, &str, error, tmp != -1);
 }
 
-bool Field_datetime_hires::send_binary(Protocol *protocol)
+bool Field_datetime_with_dec::send_binary(Protocol *protocol)
 {
   MYSQL_TIME ltime;
-  Field_datetime_hires::get_date(&ltime, TIME_FUZZY_DATE);
+  get_date(&ltime, TIME_FUZZY_DATE);
   return protocol->store(&ltime, dec);
 }
 
 
-double Field_datetime_hires::val_real(void)
+double Field_datetime_with_dec::val_real(void)
 {
   MYSQL_TIME ltime;
-  Field_datetime_hires::get_date(&ltime, TIME_FUZZY_DATE);
+  get_date(&ltime, TIME_FUZZY_DATE);
   return TIME_to_double(&ltime);
 }
 
-longlong Field_datetime_hires::val_int(void)
+longlong Field_datetime_with_dec::val_int(void)
 {
   MYSQL_TIME ltime;
-  Field_datetime_hires::get_date(&ltime, TIME_FUZZY_DATE);
+  get_date(&ltime, TIME_FUZZY_DATE);
   return TIME_to_ulonglong_datetime(&ltime);
 }
 
 
-String *Field_datetime_hires::val_str(String *str,
-                                      String *unused __attribute__((unused)))
+String *Field_datetime_with_dec::val_str(String *str,
+                                         String *unused __attribute__((unused)))
 {
   MYSQL_TIME ltime;
-  Field_datetime_hires::get_date(&ltime, TIME_FUZZY_DATE);
+  get_date(&ltime, TIME_FUZZY_DATE);
   str->alloc(field_length+1);
   str->length(field_length);
   my_datetime_to_str(&ltime, (char*) str->ptr(), dec);
-  str->set_charset(&my_charset_bin);
+  str->set_charset(&my_charset_numeric);
   return str;
 }
 
@@ -6007,27 +6127,42 @@ int Field_datetime_hires::cmp(const ucha
   return a < b ? -1 : a > b ? 1 : 0;
 }
 
-void Field_datetime_hires::sort_string(uchar *to,
-                                       uint length __attribute__((unused)))
+void Field_datetime_with_dec::make_field(Send_field *field)
 {
-  DBUG_ASSERT(length == Field_datetime_hires::pack_length());
-  memcpy(to, ptr, length);
+  Field::make_field(field);
+  field->decimals= dec;
 }
 
 
-void Field_datetime_hires::sql_type(String &res) const
+/****************************************************************************
+** MySQL-5.6 compatible DATETIME(N)
+**
+****************************************************************************/
+int Field_datetimef::reset()
 {
-  CHARSET_INFO *cs=res.charset();
-  res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
-                                "datetime(%u)", dec));
+  my_datetime_packed_to_binary(0, ptr, dec);
+  return 0;
 }
 
-void Field_datetime_hires::make_field(Send_field *field)
+void Field_datetimef::store_TIME(MYSQL_TIME *ltime)
 {
-  Field::make_field(field);
-  field->decimals= dec;
+  my_time_trunc(ltime, decimals());
+  longlong tmp= TIME_to_longlong_datetime_packed(ltime);
+  my_datetime_packed_to_binary(tmp, ptr, dec);
+}
+
+bool Field_datetimef::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+  longlong tmp= my_datetime_packed_from_binary(ptr, dec);
+  TIME_from_longlong_datetime_packed(ltime, tmp);
+  if (!tmp)
+    return fuzzydate & TIME_NO_ZERO_DATE;
+  if (!ltime->month || !ltime->day)
+    return !(fuzzydate & TIME_FUZZY_DATE);
+  return false;
 }
 
+
 /****************************************************************************
 ** string type
 ** A string may be varchar or binary
@@ -6198,80 +6333,6 @@ int Field_str::store(double nr)
   return store(buff, length, &my_charset_numeric);
 }
 
-static
-inline ulonglong char_prefix_to_ulonglong(uchar *src)
-{
-  uint sz= sizeof(ulonglong);
-  for (uint i= 0; i < sz/2; i++)
-  {
-    uchar tmp= src[i];
-    src[i]= src[sz-1-i];
-    src[sz-1-i]= tmp;
-  }
-  return uint8korr(src); 
-}
-
-/**
-  @brief
-  Determine the relative position of the field value in a string interval
-
-  @details
-  The function returns a double number between 0.0 and 1.0 as the relative
-  position of the value of the this field in the string interval of [min,max].
-  If the value is not in the interval the the function returns 0.0 when
-  the value is less than min, and, 1.0 when the value is greater than max.
-
-  @note
-  To calculate the relative position of the string value v in the interval
-  [min, max] the function first converts the beginning of these three
-  strings v, min, max into the strings that are used for byte comparison.
-  For each string not more sizeof(ulonglong) first bytes are taken
-  from the result of conversion. Then these bytes are interpreted as the
-  big-endian representation of an ulonglong integer. The values of these
-  integer numbers obtained for the strings v, min, max are used to calculate
-  the position of v in [min,max] in the same way is it's done for numeric
-  fields (see Field_num::pos_in_interval).
-
-  @todo
-  Improve the procedure for the case when min and max have the same
-  beginning
-     
-  @param  min  value of the left end of the interval
-  @param  max  value of the right end of the interval
-
-  @return
-  relative position of the field value in the string interval [min,max] 
-*/
-
-double Field_str::pos_in_interval(Field *min, Field *max)
-{
-  uchar mp_prefix[sizeof(ulonglong)];
-  uchar minp_prefix[sizeof(ulonglong)];
-  uchar maxp_prefix[sizeof(ulonglong)];
-  ulonglong mp, minp, maxp;
-  my_strnxfrm(charset(), mp_prefix, sizeof(mp),
-              ptr + length_size(),
-              data_length());
-  my_strnxfrm(charset(), minp_prefix, sizeof(minp),
-              min->ptr + length_size(),
-              min->data_length());
-  my_strnxfrm(charset(), maxp_prefix, sizeof(maxp),
-              max->ptr + length_size(),
-              max->data_length());
-  mp= char_prefix_to_ulonglong(mp_prefix);
-  minp= char_prefix_to_ulonglong(minp_prefix);
-  maxp= char_prefix_to_ulonglong(maxp_prefix);
-  double n, d;
-  n= mp - minp;
-  if (n < 0)
-    return 0.0;
-  d= maxp - minp;
-  if (d <= 0)
-    return 1.0;
-  return min(n/d, 1.0);
-}
-
-
 uint Field::is_equal(Create_field *new_field)
 {
   return (new_field->sql_type == real_type());
@@ -8482,36 +8543,6 @@ my_decimal *Field_bit::val_decimal(my_de
 }
 
 
-/**
-  @brief
-  Determine the relative position of the field value in a bit interval
-
-  @details
-  The function returns a double number between 0.0 and 1.0 as the relative
-  position of the value of the this field in the bit interval of [min,max].
-  If the value is not in the interval the the function returns 0.0 when
-  the value is less than min, and, 1.0 when the value is greater than max.
-
-  @param  min  value of the left end of the interval
-  @param  max  value of the right end of the interval
-
-  @return
-  relative position of the field value in the bit interval [min,max] 
-*/
-
-double Field_bit::pos_in_interval(Field *min, Field *max)
-{
-  double n, d;
-  n= val_real() - min->val_real();
-  if (n < 0)
-    return 0.0;
-  d= max->val_real() - min->val_real();
-  if (d <= 0)
-    return 1.0;
-  return min(n/d, 1.0);
-}
-
-
 /*
   Compare two bit fields using pointers within the record.
   SYNOPSIS
@@ -9144,7 +9175,7 @@ bool Create_field::init(THD *thd, char *
     it is NOT NULL, not an AUTO_INCREMENT field and not a TIMESTAMP.
   */
   if (!fld_default_value && !(fld_type_modifier & AUTO_INCREMENT_FLAG) &&
-      (fld_type_modifier & NOT_NULL_FLAG) && fld_type != MYSQL_TYPE_TIMESTAMP)
+      (fld_type_modifier & NOT_NULL_FLAG) && !is_timestamp_type(fld_type))
     flags|= NO_DEFAULT_VALUE_FLAG;
 
   if (fld_length != NULL)
@@ -9306,6 +9337,7 @@ bool Create_field::init(THD *thd, char *
     }
     break;
   case MYSQL_TYPE_TIMESTAMP:
+  case MYSQL_TYPE_TIMESTAMP2:
     if (length > MAX_DATETIME_PRECISION)
     {
       my_error(ER_TOO_BIG_PRECISION, MYF(0), length, fld_name,
@@ -9323,6 +9355,7 @@ bool Create_field::init(THD *thd, char *
     length= MAX_DATE_WIDTH;
     break;
   case MYSQL_TYPE_TIME:
+  case MYSQL_TYPE_TIME2:
     if (length > MAX_DATETIME_PRECISION)
     {
       my_error(ER_TOO_BIG_PRECISION, MYF(0), length, fld_name,
@@ -9332,6 +9365,7 @@ bool Create_field::init(THD *thd, char *
     length+= MIN_TIME_WIDTH + (length ? 1 : 0);
     break;
   case MYSQL_TYPE_DATETIME:
+  case MYSQL_TYPE_DATETIME2:
     if (length > MAX_DATETIME_PRECISION)
     {
       my_error(ER_TOO_BIG_PRECISION, MYF(0), length, fld_name,
@@ -9413,17 +9447,6 @@ bool Create_field::init(THD *thd, char *
     DBUG_RETURN(TRUE);
   }
 
-  switch (fld_type) {
-  case MYSQL_TYPE_DATE:
-  case MYSQL_TYPE_NEWDATE:
-  case MYSQL_TYPE_TIME:
-  case MYSQL_TYPE_DATETIME:
-  case MYSQL_TYPE_TIMESTAMP:
-    charset= &my_charset_numeric;
-    flags|= BINARY_FLAG;
-  default: break;
-  }
-
   DBUG_RETURN(FALSE); /* success */
 }
 
@@ -9462,10 +9485,16 @@ uint32 calc_pack_length(enum_field_types
   case MYSQL_TYPE_TIME:   return length > MIN_TIME_WIDTH
                             ? time_hires_bytes[length - 1 - MIN_TIME_WIDTH]
                             : 3;
+  case MYSQL_TYPE_TIME2:
+    return length > MIN_TIME_WIDTH ?
+           my_time_binary_length(length - MIN_TIME_WIDTH - 1) : 3;
   case MYSQL_TYPE_TIMESTAMP:
                           return length > MAX_DATETIME_WIDTH
                             ? 4 + sec_part_bytes[length - 1 - MAX_DATETIME_WIDTH]
                             : 4;
+  case MYSQL_TYPE_TIMESTAMP2:
+    return length > MAX_DATETIME_WIDTH ?
+           my_timestamp_binary_length(length - MAX_DATETIME_WIDTH - 1) : 4;
   case MYSQL_TYPE_DATE:
   case MYSQL_TYPE_LONG	: return 4;
   case MYSQL_TYPE_FLOAT : return sizeof(float);
@@ -9474,6 +9503,9 @@ uint32 calc_pack_length(enum_field_types
                           return length > MAX_DATETIME_WIDTH
                             ? datetime_hires_bytes[length - 1 - MAX_DATETIME_WIDTH]
                             : 8;
+  case MYSQL_TYPE_DATETIME2:
+    return length > MAX_DATETIME_WIDTH ?
+           my_datetime_binary_length(length - MAX_DATETIME_WIDTH - 1) : 5;
   case MYSQL_TYPE_LONGLONG: return 8;	/* Don't crash if no longlong */
   case MYSQL_TYPE_NULL	: return 0;
   case MYSQL_TYPE_TINY_BLOB:	return 1+portable_sizeof_char_ptr;
@@ -9538,16 +9570,6 @@ Field *make_field(TABLE_SHARE *share, uc
     null_bit= ((uchar) 1) << null_bit;
   }
 
-  switch (field_type) {
-  case MYSQL_TYPE_DATE:
-  case MYSQL_TYPE_NEWDATE:
-  case MYSQL_TYPE_TIME:
-  case MYSQL_TYPE_DATETIME:
-  case MYSQL_TYPE_TIMESTAMP:
-    field_charset= &my_charset_numeric;
-  default: break;
-  }
-
   DBUG_PRINT("debug", ("field_type: %d, field_length: %u, interval: %p, pack_flag: %s%s%s%s%s",
                        field_type, field_length, interval,
                        FLAGSTR(pack_flag, FIELDFLAG_BINARY),
@@ -9661,30 +9683,51 @@ Field *make_field(TABLE_SHARE *share, uc
     uint dec= field_length > MAX_DATETIME_WIDTH ?
                        field_length - MAX_DATETIME_WIDTH - 1: 0;
     return new_Field_timestamp(ptr, null_pos, null_bit, unireg_check,
-                               field_name, share, dec, field_charset);
+                               field_name, share, dec);
+  }
+  case MYSQL_TYPE_TIMESTAMP2:
+  {
+    uint dec= field_length > MAX_DATETIME_WIDTH ?
+                       field_length - MAX_DATETIME_WIDTH - 1: 0;
+    return new Field_timestampf(ptr, null_pos, null_bit, unireg_check,
+                                field_name, share, dec);
   }
   case MYSQL_TYPE_YEAR:
     return new Field_year(ptr,field_length,null_pos,null_bit,
 			  unireg_check, field_name);
   case MYSQL_TYPE_DATE:
     return new Field_date(ptr,null_pos,null_bit,
-			  unireg_check, field_name, field_charset);
+                          unireg_check, field_name);
   case MYSQL_TYPE_NEWDATE:
     return new Field_newdate(ptr,null_pos,null_bit,
-			     unireg_check, field_name, field_charset);
+                             unireg_check, field_name);
   case MYSQL_TYPE_TIME:
   {
     uint dec= field_length > MIN_TIME_WIDTH ?
                        field_length - MIN_TIME_WIDTH - 1: 0;
     return new_Field_time(ptr, null_pos, null_bit, unireg_check,
-                              field_name, dec, field_charset);
+                          field_name, dec);
+  }
+  case MYSQL_TYPE_TIME2:
+  {
+    uint dec= field_length > MIN_TIME_WIDTH ?
+                       field_length - MIN_TIME_WIDTH - 1: 0;
+    return new Field_timef(ptr, null_pos, null_bit, unireg_check,
+                           field_name, dec);
   }
   case MYSQL_TYPE_DATETIME:
   {
     uint dec= field_length > MAX_DATETIME_WIDTH ?
                        field_length - MAX_DATETIME_WIDTH - 1: 0;
     return new_Field_datetime(ptr, null_pos, null_bit, unireg_check,
-                              field_name, dec, field_charset);
+                              field_name, dec);
+  }
+  case MYSQL_TYPE_DATETIME2:
+  {
+    uint dec= field_length > MAX_DATETIME_WIDTH ?
+                       field_length - MAX_DATETIME_WIDTH - 1: 0;
+    return new Field_datetimef(ptr, null_pos, null_bit, unireg_check,
+                              field_name, dec);
   }
   case MYSQL_TYPE_NULL:
     return new Field_null(ptr, field_length, unireg_check, field_name,

=== modified file 'sql/field.h'
--- sql/field.h	2013-06-06 15:51:28 +0000
+++ sql/field.h	2013-07-01 06:50:40 +0000
@@ -30,6 +30,7 @@
 #include "sql_string.h"                         /* String */
 #include "my_decimal.h"                         /* my_decimal */
 #include "sql_error.h"                          /* MYSQL_ERROR */
+#include "compat56.h"
 
 class Send_field;
 class Protocol;
@@ -89,6 +90,42 @@ inline uint get_set_pack_length(int elem
   return len > 4 ? 8 : len;
 }
 
+
+/**
+   Recognizer for concrete data type (called real_type for some reason),
+   returning true if it is one of the TIMESTAMP types.
+*/
+inline bool is_timestamp_type(enum_field_types type)
+{
+  return type == MYSQL_TYPE_TIMESTAMP || type == MYSQL_TYPE_TIMESTAMP2;
+}
+
+
+/**
+  Convert temporal real types as retuned by field->real_type()
+  to field type as returned by field->type().
+  
+  @param real_type  Real type.
+  @retval           Field type.
+*/
+inline enum_field_types real_type_to_type(enum_field_types real_type)
+{
+  switch (real_type)
+  {
+  case MYSQL_TYPE_TIME2:
+    return MYSQL_TYPE_TIME;
+  case MYSQL_TYPE_DATETIME2:
+    return MYSQL_TYPE_DATETIME;
+  case MYSQL_TYPE_TIMESTAMP2:
+    return MYSQL_TYPE_TIMESTAMP;
+  case MYSQL_TYPE_NEWDATE:
+    return MYSQL_TYPE_DATE;
+  /* Note: NEWDECIMAL is a type, not only a real_type */
+  default: return real_type;
+  }
+}
+
+
 /*
   Virtual_column_info is the class to contain additional
   characteristics that is specific for a virtual/computed
@@ -428,6 +465,54 @@ class Field
   virtual uint32 key_length() const { return pack_length(); }
   virtual enum_field_types type() const =0;
   virtual enum_field_types real_type() const { return type(); }
+  virtual enum_field_types binlog_type() const
+  {
+    /*
+      Binlog stores field->type() as type code by default. For example,
+      it puts MYSQL_TYPE_STRING in case of CHAR, VARCHAR, SET and ENUM,
+      with extra data type details put into metadata.
+
+      Binlog behaviour slightly differs between various MySQL and MariaDB
+      versions for the temporal data types TIME, DATETIME and TIMESTAMP.
+
+      MySQL prior to 5.6 uses MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME 
+      and MYSQL_TYPE_TIMESTAMP type codes in binlog and stores no 
+      additional metadata.
+
+      MariaDB-5.3 implements new versions for TIME, DATATIME, TIMESTAMP
+      with fractional second precision, but uses the old format for the
+      types TIME(0), DATETIME(0), TIMESTAMP(0), and it still stores
+      MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME and MYSQL_TYPE_TIMESTAMP in binlog,
+      with no additional metadata.
+      So row-based replication between temporal data types of
+      different precision is not possible in MariaDB.
+
+      MySQL-5.6 also implements a new version of TIME, DATETIME, TIMESTAMP
+      which support fractional second precision 0..6, and use the new
+      format even for the types TIME(0), DATETIME(0), TIMESTAMP(0).
+      For these new data types, MySQL-5.6 stores new type codes 
+      MYSQL_TYPE_TIME2, MYSQL_TYPE_DATETIME2, MYSQL_TYPE_TIMESTAMP2 in binlog,
+      with fractional precision 0..6 put into metadata.
+      This makes it in theory possible to do row-based replication between
+      columns of different fractional precision (e.g. from TIME(1) on master
+      to TIME(6) on slave). However, it's not currently fully implemented yet.
+      MySQL-5.6 can only do row-based replication from the old types
+      TIME, DATETIME, TIMESTAMP (represented by MYSQL_TYPE_TIME,
+      MYSQL_TYPE_DATETIME and MYSQL_TYPE_TIMESTAMP type codes in binlog)
+      to the new corresponding types TIME(0), DATETIME(0), TIMESTAMP(0).
+
+      Note: MariaDB starting from the version 10.0 understands the new
+      MySQL-5.6 type codes MYSQL_TYPE_TIME2, MYSQL_TYPE_DATETIME2,
+      MYSQL_TYPE_TIMESTAMP2. When started over MySQL-5.6 tables both on
+      master and on slave, MariaDB-10.0 can also do row-based replication
+      from the old types TIME, DATETIME, TIMESTAMP to the new MySQL-5.6
+      types TIME(0), DATETIME(0), TIMESTAMP(0).
+
+      Note: perhaps binlog should eventually be modified to store
+      real_type() instead of type() for all column types.
+    */
+    return type();
+  }
   inline  int cmp(const uchar *str) { return cmp(ptr,str); }
   virtual int cmp_max(const uchar *a, const uchar *b, uint max_len)
     { return cmp(a, b); }
@@ -661,6 +746,16 @@ class Field
   { return binary() ? &my_charset_bin : charset(); }
   virtual CHARSET_INFO *sort_charset(void) const { return charset(); }
   virtual bool has_charset(void) const { return FALSE; }
+  /*
+    match_collation_to_optimize_range() is to distinguish in
+    range optimizer (see opt_range.cc) between real string types:
+      CHAR, VARCHAR, TEXT
+    and the other string-alike types with result_type() == STRING_RESULT:
+      DATE, TIME, DATETIME, TIMESTAMP
+    We need it to decide whether to test if collation of the operation
+    matches collation of the field (needed only for real string types).
+  */
+  virtual bool match_collation_to_optimize_range() const { return false; }
   virtual void set_charset(CHARSET_INFO *charset_arg) { }
   virtual enum Derivation derivation(void) const
   { return DERIVATION_IMPLICIT; }
@@ -808,7 +903,8 @@ class Field
   {
     return (flags & (BINCMP_FLAG | BINARY_FLAG)) != 0;
   }
-
+  double pos_in_interval_val_real(Field *min, Field *max);
+  double pos_in_interval_val_str(Field *min, Field *max, uint data_offset);
 };
 
 
@@ -847,7 +943,10 @@ class Field_num :public Field {
   bool get_int(CHARSET_INFO *cs, const char *from, uint len, 
                longlong *rnd, ulonglong unsigned_max, 
                longlong signed_min, longlong signed_max);
-  double pos_in_interval(Field *min, Field *max);
+  double pos_in_interval(Field *min, Field *max)
+  {
+    return pos_in_interval_val_real(min, max);
+  }
 };
 
 
@@ -860,23 +959,11 @@ class Field_str :public Field {
 	    uchar null_bit_arg, utype unireg_check_arg,
 	    const char *field_name_arg, CHARSET_INFO *charset);
   Item_result result_type () const { return STRING_RESULT; }
-  /*
-    match_collation_to_optimize_range() is to distinguish in
-    range optimizer (see opt_range.cc) between real string types:
-      CHAR, VARCHAR, TEXT
-    and the other string-alike types with result_type() == STRING_RESULT:
-      DATE, TIME, DATETIME, TIMESTAMP
-    We need it to decide whether to test if collation of the operation
-    matches collation of the field (needed only for real string types).
-    QQ: shouldn't DATE/TIME types have their own XXX_RESULT types eventually?
-  */
-  virtual bool match_collation_to_optimize_range() const=0;
   uint decimals() const { return NOT_FIXED_DEC; }
   int  store(double nr);
   int  store(longlong nr, bool unsigned_val)=0;
   int  store_decimal(const my_decimal *);
   int  store(const char *to,uint length,CHARSET_INFO *cs)=0;
-  uint size_of() const { return sizeof(*this); }
   uint repertoire(void) const
   {
     return my_charset_repertoire(field_charset);
@@ -894,7 +981,10 @@ class Field_str :public Field {
   uint is_equal(Create_field *new_field);
   bool eq_cmp_as_binary() { return test(flags & BINARY_FLAG); }
   virtual uint length_size() { return 0; }
-  double pos_in_interval(Field *min, Field *max);
+  double pos_in_interval(Field *min, Field *max)
+  {
+    return pos_in_interval_val_str(min, max, length_size());
+  }
 };
 
 /* base class for Field_string, Field_varstring and Field_blob */
@@ -914,6 +1004,7 @@ class Field_longstr :public Field_str
 
   int store_decimal(const my_decimal *d);
   uint32 max_data_length() const;
+  bool match_collation_to_optimize_range() const { return true; }
 };
 
 /* base class for float and double and decimal (old one) */
@@ -1323,7 +1414,6 @@ class Field_null :public Field_str {
 	       unireg_check_arg, field_name_arg, cs)
     {}
   enum_field_types type() const { return MYSQL_TYPE_NULL;}
-  bool match_collation_to_optimize_range() const { return FALSE; }
   int  store(const char *to, uint length, CHARSET_INFO *cs)
   { null[0]=1; return 0; }
   int store(double nr)   { null[0]=1; return 0; }
@@ -1345,7 +1435,67 @@ class Field_null :public Field_str {
 };
 
 
-class Field_timestamp :public Field_str {
+class Field_temporal: public Field {
+public:
+  Field_temporal(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
+                 uchar null_bit_arg, utype unireg_check_arg,
+                 const char *field_name_arg)
+    :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
+               field_name_arg)
+    { flags|= BINARY_FLAG; }
+  Item_result result_type () const { return STRING_RESULT; }   
+  uint32 max_display_length() { return field_length; }
+  bool str_needs_quotes() { return TRUE; }
+  enum Derivation derivation(void) const { return DERIVATION_NUMERIC; }
+  uint repertoire(void) const { return MY_REPERTOIRE_NUMERIC; }
+  CHARSET_INFO *charset(void) const { return &my_charset_numeric; }
+  const CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; }
+  bool binary() const { return true; }
+  enum Item_result cmp_type () const { return TIME_RESULT; }
+  uint is_equal(Create_field *new_field);
+  bool eq_def(Field *field)
+  {
+    return (Field::eq_def(field) && decimals() == field->decimals());
+  }
+  my_decimal *val_decimal(my_decimal*);
+  void set_warnings(MYSQL_ERROR::enum_warning_level trunc_level,
+                    const ErrConv *str, int was_cut, timestamp_type ts_type);
+  double pos_in_interval(Field *min, Field *max)
+  {
+    return pos_in_interval_val_real(min, max);
+  }
+};
+
+
+/**
+  Abstract class for:
+  - DATE
+  - DATETIME
+  - DATETIME(1..6)
+  - DATETIME(0..6) - MySQL56 version
+*/
+class Field_temporal_with_date: public Field_temporal {
+protected:
+  int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str,
+                              int was_cut, int have_smth_to_conv);
+  virtual void store_TIME(MYSQL_TIME *ltime) = 0;
+public:
+  Field_temporal_with_date(uchar *ptr_arg, uint32 len_arg,
+                           uchar *null_ptr_arg, uchar null_bit_arg,
+                           utype unireg_check_arg,
+                           const char *field_name_arg)
+    :Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+                    unireg_check_arg, field_name_arg)
+    {}
+  int  store(const char *to, uint length, CHARSET_INFO *charset);
+  int  store(double nr);
+  int  store(longlong nr, bool unsigned_val);
+  int  store_time_dec(MYSQL_TIME *ltime, uint dec);
+  int  store_decimal(const my_decimal *);
+};
+
+
+class Field_timestamp :public Field_temporal {
 protected:
   int store_TIME_with_warning(THD *, MYSQL_TIME *, const ErrConv *,
                               bool, bool);
@@ -1353,21 +1503,14 @@ class Field_timestamp :public Field_str
   Field_timestamp(uchar *ptr_arg, uint32 len_arg,
                   uchar *null_ptr_arg, uchar null_bit_arg,
 		  enum utype unireg_check_arg, const char *field_name_arg,
-		  TABLE_SHARE *share, CHARSET_INFO *cs);
-  Field_timestamp(bool maybe_null_arg, const char *field_name_arg,
-		  CHARSET_INFO *cs);
+		  TABLE_SHARE *share);
   enum_field_types type() const { return MYSQL_TYPE_TIMESTAMP;}
-  bool match_collation_to_optimize_range() const { return FALSE; }
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; }
-  enum Item_result cmp_type () const { return TIME_RESULT; }
-  enum Derivation derivation(void) const { return DERIVATION_NUMERIC; }
-  uint repertoire(void) const { return MY_REPERTOIRE_NUMERIC; }
-  CHARSET_INFO *charset(void) const { return &my_charset_numeric; }
-  bool binary() const { return 1; }
   int  store(const char *to,uint length,CHARSET_INFO *charset);
   int  store(double nr);
   int  store(longlong nr, bool unsigned_val);
   int  store_time_dec(MYSQL_TIME *ltime, uint dec);
+  int  store_decimal(const my_decimal *);
   double val_real(void);
   longlong val_int(void);
   String *val_str(String*,String *);
@@ -1377,7 +1520,6 @@ class Field_timestamp :public Field_str
   uint32 pack_length() const { return 4; }
   void sql_type(String &str) const;
   bool zero_pack() const { return 0; }
-  uint decimals() const { return 0; }
   virtual int set_time();
   virtual void set_default()
   {
@@ -1418,46 +1560,109 @@ class Field_timestamp :public Field_str
   {
     return unpack_int32(to, from, from_end);
   }
+  uint size_of() const { return sizeof(*this); }
 };
 
 
-class Field_timestamp_hires :public Field_timestamp {
+/**
+  Abstract class for:
+  - TIMESTAMP(1..6)
+  - TIMESTAMP(0..6) - MySQL56 version
+*/
+class Field_timestamp_with_dec :public Field_timestamp {
+protected:
   uint dec;
 public:
-  Field_timestamp_hires(uchar *ptr_arg,
-                  uchar *null_ptr_arg, uchar null_bit_arg,
-                  enum utype unireg_check_arg, const char *field_name_arg,
-                  TABLE_SHARE *share, uint dec_arg, CHARSET_INFO *cs) :
-  Field_timestamp(ptr_arg, MAX_DATETIME_WIDTH + dec_arg + 1, null_ptr_arg,
-                  null_bit_arg, unireg_check_arg, field_name_arg, share, cs),
+  Field_timestamp_with_dec(uchar *ptr_arg,
+                           uchar *null_ptr_arg, uchar null_bit_arg,
+                           enum utype unireg_check_arg,
+                           const char *field_name_arg,
+                           TABLE_SHARE *share, uint dec_arg) :
+  Field_timestamp(ptr_arg,
+                  MAX_DATETIME_WIDTH + dec_arg + test(dec_arg), null_ptr_arg,
+                  null_bit_arg, unireg_check_arg, field_name_arg, share),
   dec(dec_arg)
   {
-    DBUG_ASSERT(dec);
     DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
   }
-  void sql_type(String &str) const;
-  my_time_t get_timestamp(ulong *sec_part) const;
-  void store_TIME(my_time_t timestamp, ulong sec_part);
-  int store_decimal(const my_decimal *d);
-  double val_real(void);
-  String *val_str(String*,String *);
-  my_decimal* val_decimal(my_decimal*);
-  bool send_binary(Protocol *protocol);
-  int cmp(const uchar *,const uchar *);
-  void sort_string(uchar *buff,uint length);
   uint decimals() const { return dec; }
-  int set_time();
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
-  void make_field(Send_field *field);
-  uint32 pack_length() const;
   uchar *pack(uchar *to, const uchar *from, uint max_length)
   { return Field::pack(to, from, max_length); }
   const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end,
                       uint param_data)
   { return Field::unpack(to, from, from_end, param_data); }
+  void make_field(Send_field *field);
+  void sort_string(uchar *to, uint length)
+  {
+    DBUG_ASSERT(length == pack_length());
+    memcpy(to, ptr, length);
+  }
+  bool send_binary(Protocol *protocol);
+  double val_real(void);
+  my_decimal* val_decimal(my_decimal*);
+  int set_time();
+};
+
+
+class Field_timestamp_hires :public Field_timestamp_with_dec {
+public:
+  Field_timestamp_hires(uchar *ptr_arg,
+                        uchar *null_ptr_arg, uchar null_bit_arg,
+                        enum utype unireg_check_arg,
+                        const char *field_name_arg,
+                        TABLE_SHARE *share, uint dec_arg) :
+  Field_timestamp_with_dec(ptr_arg, null_ptr_arg, null_bit_arg,
+                           unireg_check_arg, field_name_arg, share, dec_arg)
+  {
+    DBUG_ASSERT(dec);
+  }
+  my_time_t get_timestamp(ulong *sec_part) const;
+  void store_TIME(my_time_t timestamp, ulong sec_part);
+  int cmp(const uchar *,const uchar *);
+  uint32 pack_length() const;
+  uint size_of() const { return sizeof(*this); }
+};
+
+
+/**
+  TIMESTAMP(0..6) - MySQL56 version
+*/
+class Field_timestampf :public Field_timestamp_with_dec {
+  int do_save_field_metadata(uchar *metadata_ptr)
+  {
+    *metadata_ptr= decimals();
+    return 1;
+  }
+public:
+  Field_timestampf(uchar *ptr_arg,
+                   uchar *null_ptr_arg, uchar null_bit_arg,
+                   enum utype unireg_check_arg,
+                   const char *field_name_arg,
+                   TABLE_SHARE *share, uint dec_arg) :
+    Field_timestamp_with_dec(ptr_arg, null_ptr_arg, null_bit_arg,
+                             unireg_check_arg, field_name_arg, share, dec_arg)
+    {}
+  enum_field_types real_type() const { return MYSQL_TYPE_TIMESTAMP2; }
+  enum_field_types binlog_type() const { return MYSQL_TYPE_TIMESTAMP2; }
+  uint32 pack_length() const
+  {
+    return my_timestamp_binary_length(dec);
+  }
+  uint row_pack_length() { return pack_length(); }
+  uint pack_length_from_metadata(uint field_metadata)
+  {
+    DBUG_ENTER("Field_timestampf::pack_length_from_metadata");
+    uint tmp= my_timestamp_binary_length(field_metadata);
+    DBUG_RETURN(tmp);
+  }
+  int cmp(const uchar *a_ptr,const uchar *b_ptr)
+  {
+    return memcmp(a_ptr, b_ptr, pack_length());
+  }
+  void store_TIME(my_time_t timestamp, ulong sec_part);
+  my_time_t get_timestamp(ulong *sec_part) const;
   uint size_of() const { return sizeof(*this); }
-  bool eq_def(Field *field)
-  { return Field_str::eq_def(field) && dec == field->decimals(); }
 };
 
 
@@ -1484,56 +1689,24 @@ class Field_year :public Field_tiny {
 };
 
 
-class Field_temporal: public Field_str {
-protected:
-  int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str,
-                              int was_cut, int have_smth_to_conv);
-  virtual void store_TIME(MYSQL_TIME *ltime) = 0;
-public:
-  Field_temporal(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
-                 uchar null_bit_arg, utype unireg_check_arg,
-                 const char *field_name_arg, CHARSET_INFO *charset_arg)
-    :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
-               field_name_arg, charset_arg)
-    { flags|= BINARY_FLAG; }
-  enum Derivation derivation(void) const { return DERIVATION_NUMERIC; }
-  uint repertoire(void) const { return MY_REPERTOIRE_NUMERIC; }
-  CHARSET_INFO *charset(void) const { return &my_charset_numeric; }
-  bool binary() const { return 1; }
-  bool match_collation_to_optimize_range() const { return FALSE; }
-  enum Item_result cmp_type () const { return TIME_RESULT; }
-  int  store(const char *to,uint length,CHARSET_INFO *charset);
-  int  store(double nr);
-  int  store(longlong nr, bool unsigned_val);
-  int  store_time_dec(MYSQL_TIME *ltime, uint dec);
-  my_decimal *val_decimal(my_decimal*);
-  bool eq_def(Field *field)
-  {
-    return (Field_str::eq_def(field) && decimals() == field->decimals());
-  }
-};
-
-class Field_date :public Field_temporal {
+class Field_date :public Field_temporal_with_date {
   void store_TIME(MYSQL_TIME *ltime);
 public:
   Field_date(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
-	     enum utype unireg_check_arg, const char *field_name_arg,
-	     CHARSET_INFO *cs)
-    :Field_temporal(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg,
-                    unireg_check_arg, field_name_arg, cs) {}
+	     enum utype unireg_check_arg, const char *field_name_arg)
+    :Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg,
+                              unireg_check_arg, field_name_arg) {}
   enum_field_types type() const { return MYSQL_TYPE_DATE;}
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; }
   int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; }
   double val_real(void);
   longlong val_int(void);
   String *val_str(String*,String *);
-  uint decimals() const { return 0; }
   bool send_binary(Protocol *protocol);
   int cmp(const uchar *,const uchar *);
   void sort_string(uchar *buff,uint length);
   uint32 pack_length() const { return 4; }
   void sql_type(String &str) const;
-  bool zero_pack() const { return 1; }
   uchar *pack(uchar* to, const uchar *from,
               uint max_length __attribute__((unused)))
   {
@@ -1544,23 +1717,22 @@ class Field_date :public Field_temporal
   {
     return unpack_int32(to, from, from_end);
   }
+  uint size_of() const { return sizeof(*this); }
 };
 
 
-class Field_newdate :public Field_temporal {
+class Field_newdate :public Field_temporal_with_date {
   void store_TIME(MYSQL_TIME *ltime);
 public:
   Field_newdate(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
-		enum utype unireg_check_arg, const char *field_name_arg,
-		CHARSET_INFO *cs)
-    :Field_temporal(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg,
-                    unireg_check_arg, field_name_arg, cs)
+		enum utype unireg_check_arg, const char *field_name_arg)
+    :Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg,
+                              unireg_check_arg, field_name_arg)
     {}
   enum_field_types type() const { return MYSQL_TYPE_DATE;}
   enum_field_types real_type() const { return MYSQL_TYPE_NEWDATE; }
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_UINT24; }
   int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; }
-  uint decimals() const { return 0; }
   double val_real(void);
   longlong val_int(void);
   String *val_str(String*,String *);
@@ -1569,19 +1741,22 @@ class Field_newdate :public Field_tempor
   void sort_string(uchar *buff,uint length);
   uint32 pack_length() const { return 3; }
   void sql_type(String &str) const;
-  bool zero_pack() const { return 1; }
   bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+  uint size_of() const { return sizeof(*this); }
 };
 
 
 class Field_time :public Field_temporal {
-  void store_TIME(MYSQL_TIME *ltime);
+protected:
+  virtual void store_TIME(MYSQL_TIME *ltime);
+  int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str,
+                              int was_cut, int have_smth_to_conv);
 public:
   Field_time(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg,
              uchar null_bit_arg, enum utype unireg_check_arg,
-             const char *field_name_arg, CHARSET_INFO *cs)
+             const char *field_name_arg)
     :Field_temporal(ptr_arg, length_arg, null_ptr_arg, null_bit_arg,
-                    unireg_check_arg, field_name_arg, cs)
+                    unireg_check_arg, field_name_arg)
     {}
   enum_field_types type() const { return MYSQL_TYPE_TIME;}
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_INT24; }
@@ -1589,7 +1764,7 @@ class Field_time :public Field_temporal
   int store(const char *to,uint length,CHARSET_INFO *charset);
   int store(double nr);
   int store(longlong nr, bool unsigned_val);
-  uint decimals() const { return 0; }
+  int  store_decimal(const my_decimal *);
   double val_real(void);
   longlong val_int(void);
   String *val_str(String*,String *);
@@ -1599,55 +1774,122 @@ class Field_time :public Field_temporal
   void sort_string(uchar *buff,uint length);
   uint32 pack_length() const { return 3; }
   void sql_type(String &str) const;
-  bool zero_pack() const { return 1; }
+  uint size_of() const { return sizeof(*this); }
 };
 
-class Field_time_hires :public Field_time {
+
+/**
+  Abstract class for:
+  - TIME(1..6)
+  - TIME(0..6) - MySQL56 version
+*/
+class Field_time_with_dec :public Field_time {
+protected:
   uint dec;
+public:
+  Field_time_with_dec(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
+                      enum utype unireg_check_arg, const char *field_name_arg,
+                      uint dec_arg)
+    :Field_time(ptr_arg, MIN_TIME_WIDTH + dec_arg + test(dec_arg), null_ptr_arg,
+                null_bit_arg, unireg_check_arg, field_name_arg),
+     dec(dec_arg)
+  {
+    DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+  }
+  uint decimals() const { return dec; }
+  enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
+  longlong val_int(void);
+  double val_real(void);
+  void make_field(Send_field *);
+};
+
+
+/**
+  TIME(1..6)
+*/
+class Field_time_hires :public Field_time_with_dec {
   longlong zero_point;
   void store_TIME(MYSQL_TIME *ltime);
 public:
   Field_time_hires(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
              enum utype unireg_check_arg, const char *field_name_arg,
-             uint dec_arg, CHARSET_INFO *cs)
-    :Field_time(ptr_arg, MIN_TIME_WIDTH + dec_arg + 1, null_ptr_arg,
-                null_bit_arg, unireg_check_arg, field_name_arg, cs),
-     dec(dec_arg)
+             uint dec_arg)
+    :Field_time_with_dec(ptr_arg, null_ptr_arg,
+                         null_bit_arg, unireg_check_arg, field_name_arg,
+                         dec_arg)
   {
     DBUG_ASSERT(dec);
-    DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
     zero_point= sec_part_shift(
                    ((TIME_MAX_VALUE_SECONDS+1LL)*TIME_SECOND_PART_FACTOR), dec);
   }
-  enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
-  uint decimals() const { return dec; }
-  int store_decimal(const my_decimal *d);
-  longlong val_int(void);
-  double val_real(void);
-  String *val_str(String*,String *);
   int reset(void);
   bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
-  bool send_binary(Protocol *protocol);
   int cmp(const uchar *,const uchar *);
   void sort_string(uchar *buff,uint length);
   uint32 pack_length() const;
-  void sql_type(String &str) const;
-  void make_field(Send_field *);
   uint size_of() const { return sizeof(*this); }
 };
 
-class Field_datetime :public Field_temporal {
+
+/**
+  TIME(0..6) - MySQL56 version
+*/
+class Field_timef :public Field_time_with_dec {
+  void store_TIME(MYSQL_TIME *ltime);
+  int do_save_field_metadata(uchar *metadata_ptr)
+  {
+    *metadata_ptr= decimals();
+    return 1;
+  }
+public:
+  Field_timef(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
+             enum utype unireg_check_arg, const char *field_name_arg,
+             uint dec_arg)
+    :Field_time_with_dec(ptr_arg, null_ptr_arg,
+                         null_bit_arg, unireg_check_arg, field_name_arg,
+                         dec_arg)
+  {
+    DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+  }
+  enum_field_types real_type() const { return MYSQL_TYPE_TIME2; }
+  enum_field_types binlog_type() const { return MYSQL_TYPE_TIME2; }
+  uint32 pack_length() const
+  {
+    return my_time_binary_length(dec);
+  }
+  uint row_pack_length() { return pack_length(); }
+  uint pack_length_from_metadata(uint field_metadata)
+  {
+    DBUG_ENTER("Field_timef::pack_length_from_metadata");
+    uint tmp= my_time_binary_length(field_metadata);
+    DBUG_RETURN(tmp);
+  }
+  void sort_string(uchar *to, uint length)
+  {
+    DBUG_ASSERT(length == Field_timef::pack_length());
+    memcpy(to, ptr, length);
+  }
+  int cmp(const uchar *a_ptr, const uchar *b_ptr)
+  {
+    return memcmp(a_ptr, b_ptr, pack_length());
+  }
+  int reset();
+  bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+  uint size_of() const { return sizeof(*this); }
+};
+
+
+class Field_datetime :public Field_temporal_with_date {
   void store_TIME(MYSQL_TIME *ltime);
 public:
   Field_datetime(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg,
                  uchar null_bit_arg, enum utype unireg_check_arg,
-                 const char *field_name_arg, CHARSET_INFO *cs)
-    :Field_temporal(ptr_arg, length_arg, null_ptr_arg, null_bit_arg,
-                    unireg_check_arg, field_name_arg, cs)
+                 const char *field_name_arg)
+    :Field_temporal_with_date(ptr_arg, length_arg, null_ptr_arg, null_bit_arg,
+                              unireg_check_arg, field_name_arg)
     {}
   enum_field_types type() const { return MYSQL_TYPE_DATETIME;}
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; }
-  uint decimals() const { return 0; }
   double val_real(void);
   longlong val_int(void);
   String *val_str(String*,String *);
@@ -1656,7 +1898,6 @@ class Field_datetime :public Field_tempo
   void sort_string(uchar *buff,uint length);
   uint32 pack_length() const { return 8; }
   void sql_type(String &str) const;
-  bool zero_pack() const { return 1; }
   bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
   virtual int set_time();
   virtual void set_default()
@@ -1690,85 +1931,149 @@ class Field_datetime :public Field_tempo
   {
     return unpack_int64(to, from, from_end);
   }
+  uint size_of() const { return sizeof(*this); }
 };
 
 
-class Field_datetime_hires :public Field_datetime {
-  void store_TIME(MYSQL_TIME *ltime);
+/**
+  Abstract class for:
+  - DATETIME(1..6)
+  - DATETIME(0..6) - MySQL56 version
+*/
+class Field_datetime_with_dec :public Field_datetime {
+protected:
   uint dec;
 public:
-  Field_datetime_hires(uchar *ptr_arg, uchar *null_ptr_arg,
-                       uchar null_bit_arg, enum utype unireg_check_arg,
-                       const char *field_name_arg, uint dec_arg,
-                       CHARSET_INFO *cs)
-    :Field_datetime(ptr_arg, MAX_DATETIME_WIDTH + dec_arg + 1,
+  Field_datetime_with_dec(uchar *ptr_arg, uchar *null_ptr_arg,
+                          uchar null_bit_arg, enum utype unireg_check_arg,
+                          const char *field_name_arg, uint dec_arg)
+    :Field_datetime(ptr_arg, MAX_DATETIME_WIDTH + dec_arg + test(dec_arg),
                     null_ptr_arg, null_bit_arg, unireg_check_arg,
-                    field_name_arg, cs), dec(dec_arg)
+                    field_name_arg), dec(dec_arg)
   {
-    DBUG_ASSERT(dec);
     DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
   }
-  enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
   uint decimals() const { return dec; }
+  enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
   void make_field(Send_field *field);
-  int store_decimal(const my_decimal *d);
-  double val_real(void);
-  longlong val_int(void);
-  String *val_str(String*,String *);
   bool send_binary(Protocol *protocol);
-  int cmp(const uchar *,const uchar *);
-  void sort_string(uchar *buff,uint length);
-  uint32 pack_length() const;
-  void sql_type(String &str) const;
-  bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
   uchar *pack(uchar *to, const uchar *from, uint max_length)
   { return Field::pack(to, from, max_length); }
   const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end,
                       uint param_data)
   { return Field::unpack(to, from, from_end, param_data); }
+  void sort_string(uchar *to, uint length)
+  {
+    DBUG_ASSERT(length == pack_length());
+    memcpy(to, ptr, length);
+  }
+  double val_real(void);
+  longlong val_int(void);
+  String *val_str(String*,String *);
+};
+
+
+/**
+  DATETIME(1..6)
+*/
+class Field_datetime_hires :public Field_datetime_with_dec {
+  void store_TIME(MYSQL_TIME *ltime);
+public:
+  Field_datetime_hires(uchar *ptr_arg, uchar *null_ptr_arg,
+                       uchar null_bit_arg, enum utype unireg_check_arg,
+                       const char *field_name_arg, uint dec_arg)
+    :Field_datetime_with_dec(ptr_arg, null_ptr_arg, null_bit_arg,
+                             unireg_check_arg, field_name_arg, dec_arg)
+  {
+    DBUG_ASSERT(dec);
+  }
+  int cmp(const uchar *,const uchar *);
+  uint32 pack_length() const;
+  bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+  uint size_of() const { return sizeof(*this); }
+};
+
+
+/**
+  DATETIME(0..6) - MySQL56 version
+*/
+class Field_datetimef :public Field_datetime_with_dec {
+  void store_TIME(MYSQL_TIME *ltime);
+  int do_save_field_metadata(uchar *metadata_ptr)
+  {
+    *metadata_ptr= decimals();
+    return 1;
+  }
+public:
+  Field_datetimef(uchar *ptr_arg, uchar *null_ptr_arg,
+                  uchar null_bit_arg, enum utype unireg_check_arg,
+                  const char *field_name_arg, uint dec_arg)
+    :Field_datetime_with_dec(ptr_arg, null_ptr_arg, null_bit_arg,
+                             unireg_check_arg, field_name_arg, dec_arg)
+  {}
+  enum_field_types real_type() const { return MYSQL_TYPE_DATETIME2; }
+  enum_field_types binlog_type() const { return MYSQL_TYPE_DATETIME2; }
+  uint32 pack_length() const
+  {
+    return my_datetime_binary_length(dec);
+  }
+  uint row_pack_length() { return pack_length(); }
+  uint pack_length_from_metadata(uint field_metadata)
+  {
+    DBUG_ENTER("Field_datetimef::pack_length_from_metadata");
+    uint tmp= my_datetime_binary_length(field_metadata);
+    DBUG_RETURN(tmp);
+  }
+  int cmp(const uchar *a_ptr, const uchar *b_ptr)
+  {
+    return memcmp(a_ptr, b_ptr, pack_length());
+  }
+  int reset();
+  bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
   uint size_of() const { return sizeof(*this); }
 };
 
+
 static inline Field_timestamp *
 new_Field_timestamp(uchar *ptr, uchar *null_ptr, uchar null_bit,
                     enum Field::utype unireg_check, const char *field_name,
-                    TABLE_SHARE *share, uint dec, CHARSET_INFO *cs)
+                    TABLE_SHARE *share, uint dec)
 {
   if (dec==0)
     return new Field_timestamp(ptr, MAX_DATETIME_WIDTH, null_ptr, null_bit,
-                                unireg_check, field_name, share, cs);
+                                unireg_check, field_name, share);
   if (dec == NOT_FIXED_DEC)
     dec= MAX_DATETIME_PRECISION;
   return new Field_timestamp_hires(ptr, null_ptr, null_bit, unireg_check,
-                                   field_name, share, dec, cs);
+                                   field_name, share, dec);
 }
 
 static inline Field_time *
 new_Field_time(uchar *ptr, uchar *null_ptr, uchar null_bit,
                enum Field::utype unireg_check, const char *field_name,
-               uint dec, CHARSET_INFO *cs)
+               uint dec)
 {
   if (dec == 0)
     return new Field_time(ptr, MIN_TIME_WIDTH, null_ptr, null_bit,
-                          unireg_check, field_name, cs);
+                          unireg_check, field_name);
   if (dec == NOT_FIXED_DEC)
     dec= MAX_DATETIME_PRECISION;
   return new Field_time_hires(ptr, null_ptr, null_bit,
-                                  unireg_check, field_name, dec, cs);
+                                  unireg_check, field_name, dec);
 }
 
 static inline Field_datetime *
 new_Field_datetime(uchar *ptr, uchar *null_ptr, uchar null_bit,
                    enum Field::utype unireg_check,
-                   const char *field_name, uint dec, CHARSET_INFO *cs)
+                   const char *field_name, uint dec)
 {
   if (dec == 0)
     return new Field_datetime(ptr, MAX_DATETIME_WIDTH, null_ptr, null_bit,
-                              unireg_check, field_name, cs);
+                              unireg_check, field_name);
   if (dec == NOT_FIXED_DEC)
     dec= MAX_DATETIME_PRECISION;
   return new Field_datetime_hires(ptr, null_ptr, null_bit,
-                                  unireg_check, field_name, dec, cs);
+                                  unireg_check, field_name, dec);
 }
 
 class Field_string :public Field_longstr {
@@ -1795,7 +2100,6 @@ class Field_string :public Field_longstr
             orig_table->s->frm_version < FRM_VER_TRUE_VARCHAR ?
 	    MYSQL_TYPE_VAR_STRING : MYSQL_TYPE_STRING);
   }
-  bool match_collation_to_optimize_range() const { return TRUE; }
   enum ha_base_keytype key_type() const
     { return binary() ? HA_KEYTYPE_BINARY : HA_KEYTYPE_TEXT; }
   bool zero_pack() const { return 0; }
@@ -1876,7 +2180,6 @@ class Field_varstring :public Field_long
   }
 
   enum_field_types type() const { return MYSQL_TYPE_VARCHAR; }
-  bool match_collation_to_optimize_range() const { return TRUE; }
   enum ha_base_keytype key_type() const;
   uint row_pack_length() { return field_length; }
   bool zero_pack() const { return 0; }
@@ -1972,7 +2275,6 @@ class Field_blob :public Field_longstr {
     :Field_longstr((uchar*) 0, 0, (uchar*) "", 0, NONE, "temp", system_charset_info),
     packlength(packlength_arg) {}
   enum_field_types type() const { return MYSQL_TYPE_BLOB;}
-  bool match_collation_to_optimize_range() const { return TRUE; }
   enum ha_base_keytype key_type() const
     { return binary() ? HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2; }
   int  store(const char *to,uint length,CHARSET_INFO *charset);
@@ -2118,7 +2420,7 @@ class Field_geom :public Field_blob {
   { geom_type= geom_type_arg; }
   enum ha_base_keytype key_type() const { return HA_KEYTYPE_VARBINARY2; }
   enum_field_types type() const { return MYSQL_TYPE_GEOMETRY; }
-  bool match_collation_to_optimize_range() const { return FALSE; }
+  bool match_collation_to_optimize_range() const { return false; }
   void sql_type(String &str) const;
   int  store(const char *to, uint length, CHARSET_INFO *charset);
   int  store(double nr);
@@ -2156,7 +2458,6 @@ class Field_enum :public Field_str {
   }
   Field *new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type);
   enum_field_types type() const { return MYSQL_TYPE_STRING; }
-  bool match_collation_to_optimize_range() const { return FALSE; }
   enum Item_result cmp_type () const { return INT_RESULT; }
   enum ha_base_keytype key_type() const;
   int  store(const char *to,uint length,CHARSET_INFO *charset);
@@ -2309,7 +2610,10 @@ class Field_bit :public Field {
   {
     store(*((longlong *)val), TRUE);
   }
-  double pos_in_interval(Field *min, Field *max);
+  double pos_in_interval(Field *min, Field *max)
+  {
+    return pos_in_interval_val_real(min, max);
+  }
   void get_image(uchar *buff, uint length, CHARSET_INFO *cs)
   { get_key_image(buff, length, itRAW); }   
   void set_image(const uchar *buff,uint length, CHARSET_INFO *cs)

=== modified file 'sql/item.cc'
--- sql/item.cc	2013-06-06 19:32:29 +0000
+++ sql/item.cc	2013-07-02 06:59:27 +0000
@@ -651,9 +651,12 @@ Item_result Item::cmp_type() const
   case MYSQL_TYPE_GEOMETRY:
                            return STRING_RESULT;
   case MYSQL_TYPE_TIMESTAMP:
+  case MYSQL_TYPE_TIMESTAMP2:
   case MYSQL_TYPE_DATE:
   case MYSQL_TYPE_TIME:
+  case MYSQL_TYPE_TIME2:
   case MYSQL_TYPE_DATETIME:
+  case MYSQL_TYPE_DATETIME2:
   case MYSQL_TYPE_NEWDATE:
                            return TIME_RESULT;
   };
@@ -3218,11 +3221,7 @@ void Item_param::set_time(MYSQL_TIME *tm
   value.time= *tm;
   value.time.time_type= time_type;
 
-  if (value.time.year > 9999 || value.time.month > 12 ||
-      value.time.day > 31 ||
-      (time_type != MYSQL_TIMESTAMP_TIME && value.time.hour > 23) ||
-      value.time.minute > 59 || value.time.second > 59 ||
-      value.time.second_part > TIME_MAX_SECOND_PART)
+  if (check_datetime_range(&value.time))
   {
     ErrConvTime str(&value.time);
     make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
@@ -5678,19 +5677,17 @@ Field *Item::tmp_table_field_from_field_
     break;
   case MYSQL_TYPE_NEWDATE:
   case MYSQL_TYPE_DATE:
-    field= new Field_newdate(0, null_ptr, 0, Field::NONE, name, &my_charset_bin);
+    field= new Field_newdate(0, null_ptr, 0, Field::NONE, name);
     break;
   case MYSQL_TYPE_TIME:
-    field= new_Field_time(0, null_ptr, 0, Field::NONE, name,
-                              decimals, &my_charset_bin);
+    field= new_Field_time(0, null_ptr, 0, Field::NONE, name, decimals);
     break;
   case MYSQL_TYPE_TIMESTAMP:
     field= new_Field_timestamp(0, null_ptr, 0,
-                               Field::NONE, name, 0, decimals, &my_charset_bin);
+                               Field::NONE, name, 0, decimals);
     break;
   case MYSQL_TYPE_DATETIME:
-    field= new_Field_datetime(0, null_ptr, 0, Field::NONE, name,
-                              decimals, &my_charset_bin);
+    field= new_Field_datetime(0, null_ptr, 0, Field::NONE, name, decimals);
     break;
   case MYSQL_TYPE_YEAR:
     field= new Field_year((uchar*) 0, max_length, null_ptr, 0, Field::NONE,

=== modified file 'sql/item_strfunc.cc'
--- sql/item_strfunc.cc	2013-06-06 19:32:29 +0000
+++ sql/item_strfunc.cc	2013-07-02 05:52:21 +0000
@@ -3972,7 +3972,9 @@ bool Item_func_dyncol_create::prepare_ar
         type= DYN_COL_NULL;
         break;
       case MYSQL_TYPE_TIMESTAMP:
+      case MYSQL_TYPE_TIMESTAMP2:
       case MYSQL_TYPE_DATETIME:
+      case MYSQL_TYPE_DATETIME2:
         type= DYN_COL_DATETIME;
 	break;
       case MYSQL_TYPE_DATE:
@@ -3980,6 +3982,7 @@ bool Item_func_dyncol_create::prepare_ar
         type= DYN_COL_DATE;
         break;
       case MYSQL_TYPE_TIME:
+      case MYSQL_TYPE_TIME2:
         type= DYN_COL_TIME;
         break;
       case MYSQL_TYPE_VARCHAR:

=== modified file 'sql/item_sum.cc'
--- sql/item_sum.cc	2013-06-06 15:51:28 +0000
+++ sql/item_sum.cc	2013-06-18 10:35:35 +0000
@@ -1307,16 +1307,16 @@ Field *Item_sum_hybrid::create_tmp_field
   switch (args[0]->field_type()) {
   case MYSQL_TYPE_DATE:
     field= new Field_newdate(0, maybe_null ? (uchar*)"" : 0, 0, Field::NONE,
-                             name, collation.collation);
+                             name);
     break;
   case MYSQL_TYPE_TIME:
     field= new_Field_time(0, maybe_null ? (uchar*)"" : 0, 0, Field::NONE,
-                          name, decimals, collation.collation);
+                          name, decimals);
     break;
   case MYSQL_TYPE_TIMESTAMP:
   case MYSQL_TYPE_DATETIME:
     field= new_Field_datetime(0, maybe_null ? (uchar*)"" : 0, 0, Field::NONE,
-                              name, decimals, collation.collation);
+                              name, decimals);
     break;
   default:
     return Item_sum::create_tmp_field(group, table, convert_blob_length);

=== modified file 'sql/item_timefunc.cc'
--- sql/item_timefunc.cc	2013-04-07 12:00:16 +0000
+++ sql/item_timefunc.cc	2013-06-27 11:11:57 +0000
@@ -1574,7 +1574,7 @@ static void set_sec_part(ulong sec_part,
   {
     ltime->second_part= sec_part;
     if (item->decimals < TIME_SECOND_PART_DIGITS)
-      ltime->second_part= sec_part_truncate(ltime->second_part, item->decimals);
+      my_time_trunc(ltime, item->decimals);
   }
 }
 
@@ -2411,7 +2411,7 @@ bool Item_time_typecast::get_date(MYSQL_
   if (get_arg0_time(ltime))
     return 1;
   if (decimals < TIME_SECOND_PART_DIGITS)
-    ltime->second_part= sec_part_truncate(ltime->second_part, decimals);
+    my_time_trunc(ltime, decimals);
   /*
     MYSQL_TIMESTAMP_TIME value can have non-zero day part,
     which we should not lose.
@@ -2450,8 +2450,7 @@ bool Item_datetime_typecast::get_date(MY
     return 1;
 
   if (decimals < TIME_SECOND_PART_DIGITS)
-    ltime->second_part= sec_part_truncate(ltime->second_part, decimals);
-
+    my_time_trunc(ltime, decimals);
 
   /*
     ltime is valid MYSQL_TYPE_TIME (according to fuzzy_date).

=== modified file 'sql/log_event.cc'
--- sql/log_event.cc	2013-06-06 19:32:29 +0000
+++ sql/log_event.cc	2013-06-27 12:26:59 +0000
@@ -48,6 +48,7 @@
 #include <my_dir.h>
 #include "sql_show.h"    // append_identifier
 #include <strfunc.h>
+#include "compat56.h"
 
 #endif /* MYSQL_CLIENT */
 
@@ -2128,6 +2129,17 @@ log_event_print_value(IO_CACHE *file, co
       return 4;
     }
 
+  case MYSQL_TYPE_TIMESTAMP2:
+    {
+      char buf[MAX_DATE_STRING_REP_LENGTH];
+      struct timeval tm;
+      my_timestamp_from_binary(&tm, ptr, meta);
+      int buflen= my_timeval_to_str(&tm, buf, meta);
+      my_b_write(file, buf, buflen);
+      my_snprintf(typestr, typestr_length, "TIMESTAMP(%d)", meta);
+      return my_timestamp_binary_length(meta);
+    }
+
   case MYSQL_TYPE_DATETIME:
     {
       ulong d, t;
@@ -2142,15 +2154,41 @@ log_event_print_value(IO_CACHE *file, co
       return 8;
     }
 
+  case MYSQL_TYPE_DATETIME2:
+    {
+      char buf[MAX_DATE_STRING_REP_LENGTH];
+      MYSQL_TIME ltime;
+      longlong packed= my_datetime_packed_from_binary(ptr, meta);
+      TIME_from_longlong_datetime_packed(&ltime, packed);
+      int buflen= my_datetime_to_str(&ltime, buf, meta);
+      my_b_write_quoted(file, (uchar *) buf, buflen);
+      my_snprintf(typestr, typestr_length, "DATETIME(%d)", meta);
+      return my_datetime_binary_length(meta);
+    }
+
   case MYSQL_TYPE_TIME:
     {
-      uint32 i32= uint3korr(ptr);
-      my_b_printf(file, "'%02d:%02d:%02d'",
-                  i32 / 10000, (i32 % 10000) / 100, i32 % 100);
+      int32 tmp= sint3korr(ptr);
+      int32 i32= tmp >= 0 ? tmp : - tmp;
+      const char *sign= tmp < 0 ? "-" : "";
+      my_b_printf(file, "'%s%02d:%02d:%02d'",
+                  sign, i32 / 10000, (i32 % 10000) / 100, i32 % 100, i32);
       my_snprintf(typestr,  typestr_length, "TIME");
       return 3;
     }
-    
+
+  case MYSQL_TYPE_TIME2:
+    {
+      char buf[MAX_DATE_STRING_REP_LENGTH];
+      MYSQL_TIME ltime;
+      longlong packed= my_time_packed_from_binary(ptr, meta);
+      TIME_from_longlong_time_packed(&ltime, packed);
+      int buflen= my_time_to_str(&ltime, buf, meta);
+      my_b_write_quoted(file, (uchar *) buf, buflen);
+      my_snprintf(typestr, typestr_length, "TIME(%d)", meta);
+      return my_time_binary_length(meta);
+    }
+
   case MYSQL_TYPE_NEWDATE:
     {
       uint32 tmp= uint3korr(ptr);
@@ -9860,7 +9898,7 @@ Table_map_log_event::Table_map_log_event
   {
     m_coltype= reinterpret_cast<uchar*>(m_memory);
     for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
-      m_coltype[i]= m_table->field[i]->type();
+      m_coltype[i]= m_table->field[i]->binlog_type();
   }
 
   /*

=== modified file 'sql/opt_range.cc'
--- sql/opt_range.cc	2013-06-06 19:32:29 +0000
+++ sql/opt_range.cc	2013-06-18 10:35:35 +0000
@@ -8014,10 +8014,10 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND
 
   */
   if (field->result_type() == STRING_RESULT &&
-      ((Field_str*) field)->match_collation_to_optimize_range() &&
+      field->match_collation_to_optimize_range() &&
       value->result_type() == STRING_RESULT &&
       key_part->image_type == Field::itRAW &&
-      ((Field_str*)field)->charset() != conf_func->compare_collation() &&
+      field->charset() != conf_func->compare_collation() &&
       !(conf_func->compare_collation()->state & MY_CS_BINSORT &&
         (type == Item_func::EQUAL_FUNC || type == Item_func::EQ_FUNC)))
     goto end;
@@ -12885,7 +12885,7 @@ check_group_min_max_predicates(Item *con
             */
             ((args[1]->result_type() == STRING_RESULT &&
               image_type == Field::itRAW &&
-              ((Field_str*) min_max_arg_item->field)->charset() !=
+              min_max_arg_item->field->charset() !=
               pred->compare_collation())
              ||
              /*

=== modified file 'sql/opt_table_elimination.cc'
--- sql/opt_table_elimination.cc	2012-02-17 11:19:38 +0000
+++ sql/opt_table_elimination.cc	2013-06-18 10:35:35 +0000
@@ -1482,7 +1482,7 @@ void check_equality(Dep_analysis_context
           collation of the operation differ from the field collation.
         */
         if (field->cmp_type() == STRING_RESULT &&
-            ((Field_str*)field)->charset() != cond->compare_collation())
+            field->charset() != cond->compare_collation())
           return;
       }
     }

=== modified file 'sql/protocol.cc'
--- sql/protocol.cc	2013-03-27 22:41:02 +0000
+++ sql/protocol.cc	2013-06-27 11:13:00 +0000
@@ -1427,7 +1427,7 @@ bool Protocol_binary::store(MYSQL_TIME *
   DBUG_ASSERT(decimals == AUTO_SEC_PART_DIGITS ||
               (decimals >= 0 && decimals <= TIME_SECOND_PART_DIGITS));
   if (decimals != AUTO_SEC_PART_DIGITS)
-    tm->second_part= sec_part_truncate(tm->second_part, decimals);
+    my_time_trunc(tm, decimals);
   int4store(pos+7, tm->second_part);
   if (tm->second_part)
     length=11;
@@ -1469,7 +1469,7 @@ bool Protocol_binary::store_time(MYSQL_T
   DBUG_ASSERT(decimals == AUTO_SEC_PART_DIGITS ||
               (decimals >= 0 && decimals <= TIME_SECOND_PART_DIGITS));
   if (decimals != AUTO_SEC_PART_DIGITS)
-    tm->second_part= sec_part_truncate(tm->second_part, decimals);
+    my_time_trunc(tm, decimals);
   int4store(pos+8, tm->second_part);
   if (tm->second_part)
     length=12;

=== modified file 'sql/rpl_utility.cc'
--- sql/rpl_utility.cc	2013-06-06 15:51:28 +0000
+++ sql/rpl_utility.cc	2013-06-19 05:07:00 +0000
@@ -109,12 +109,15 @@ max_display_length_for_field(enum_field_
 
   case MYSQL_TYPE_DATE:
   case MYSQL_TYPE_TIME:
+  case MYSQL_TYPE_TIME2:
     return 3;
 
   case MYSQL_TYPE_TIMESTAMP:
+  case MYSQL_TYPE_TIMESTAMP2:
     return 4;
 
   case MYSQL_TYPE_DATETIME:
+  case MYSQL_TYPE_DATETIME2:
     return 8;
 
   case MYSQL_TYPE_BIT:
@@ -262,12 +265,21 @@ uint32 table_def::calc_field_size(uint c
   case MYSQL_TYPE_TIME:
     length= 3;
     break;
+  case MYSQL_TYPE_TIME2:
+    length= my_time_binary_length(m_field_metadata[col]);
+    break;
   case MYSQL_TYPE_TIMESTAMP:
     length= 4;
     break;
+  case MYSQL_TYPE_TIMESTAMP2:
+    length= my_timestamp_binary_length(m_field_metadata[col]);
+    break;
   case MYSQL_TYPE_DATETIME:
     length= 8;
     break;
+  case MYSQL_TYPE_DATETIME2:
+    length= my_datetime_binary_length(m_field_metadata[col]);
+    break;
   case MYSQL_TYPE_BIT:
   {
     /*
@@ -376,6 +388,7 @@ void show_sql_type(enum_field_types type
     break;
 
   case MYSQL_TYPE_TIMESTAMP:
+  case MYSQL_TYPE_TIMESTAMP2:
     str->set_ascii(STRING_WITH_LEN("timestamp"));
     break;
 
@@ -393,10 +406,12 @@ void show_sql_type(enum_field_types type
     break;
 
   case MYSQL_TYPE_TIME:
+  case MYSQL_TYPE_TIME2:
     str->set_ascii(STRING_WITH_LEN("time"));
     break;
 
   case MYSQL_TYPE_DATETIME:
+  case MYSQL_TYPE_DATETIME2:
     str->set_ascii(STRING_WITH_LEN("datetime"));
     break;
 
@@ -615,6 +630,23 @@ can_convert_field_to(Field *field,
     else
       DBUG_RETURN(false);
   }
+  else if (metadata == 0 &&
+           ((field->real_type() == MYSQL_TYPE_TIMESTAMP2 &&
+             source_type == MYSQL_TYPE_TIMESTAMP) ||
+            (field->real_type() == MYSQL_TYPE_TIME2 &&
+             source_type == MYSQL_TYPE_TIME) ||
+            (field->real_type() == MYSQL_TYPE_DATETIME2 &&
+             source_type == MYSQL_TYPE_DATETIME)))
+  {
+    /*
+      TS-TODO: conversion from FSP1>FSP2.
+      Can do non-lossy conversion
+      from old TIME, TIMESTAMP, DATETIME
+      to MySQL56 TIME(0), TIMESTAMP(0), DATETIME(0).
+    */
+    *order_var= -1;
+    DBUG_RETURN(true);
+  }
   else if (!slave_type_conversions_options)
     DBUG_RETURN(false);
 
@@ -739,6 +771,9 @@ can_convert_field_to(Field *field,
   case MYSQL_TYPE_NULL:
   case MYSQL_TYPE_ENUM:
   case MYSQL_TYPE_SET:
+  case MYSQL_TYPE_TIMESTAMP2:
+  case MYSQL_TYPE_DATETIME2:
+  case MYSQL_TYPE_TIME2:
     DBUG_RETURN(false);
   }
   DBUG_RETURN(false);                                 // To keep GCC happy
@@ -939,7 +974,7 @@ TABLE *table_def::create_conversion_tabl
 
     DBUG_PRINT("debug", ("sql_type: %d, target_field: '%s', max_length: %d, decimals: %d,"
                          " maybe_null: %d, unsigned_flag: %d, pack_length: %u",
-                         type(col), target_table->field[col]->field_name,
+                         binlog_type(col), target_table->field[col]->field_name,
                          max_length, decimals, TRUE, FALSE, pack_length));
     field_def->init_for_tmp_table(type(col),
                                   max_length,
@@ -993,7 +1028,7 @@ table_def::table_def(unsigned char *type
     int index= 0;
     for (unsigned int i= 0; i < m_size; i++)
     {
-      switch (m_type[i]) {
+      switch (binlog_type(i)) {
       case MYSQL_TYPE_TINY_BLOB:
       case MYSQL_TYPE_BLOB:
       case MYSQL_TYPE_MEDIUM_BLOB:
@@ -1042,6 +1077,11 @@ table_def::table_def(unsigned char *type
         m_field_metadata[i]= x;
         break;
       }
+      case MYSQL_TYPE_TIME2:
+      case MYSQL_TYPE_DATETIME2:
+      case MYSQL_TYPE_TIMESTAMP2:
+        m_field_metadata[i]= field_metadata[index++];
+        break;
       default:
         m_field_metadata[i]= 0;
         break;

=== modified file 'sql/rpl_utility.h'
--- sql/rpl_utility.h	2013-05-07 11:05:09 +0000
+++ sql/rpl_utility.h	2013-06-18 10:35:35 +0000
@@ -65,6 +65,14 @@ class table_def
   ulong size() const { return m_size; }
 
 
+  /**
+    Returns internal binlog type code for one field,
+    without translation to real types.
+  */
+  enum_field_types binlog_type(ulong index) const
+  {
+    return static_cast<enum_field_types>(m_type[index]);
+  }
   /*
     Return a representation of the type data for one field.
 
@@ -82,7 +90,7 @@ class table_def
       either MYSQL_TYPE_STRING, MYSQL_TYPE_ENUM, or MYSQL_TYPE_SET, so
       we might need to modify the type to get the real type.
     */
-    enum_field_types source_type= static_cast<enum_field_types>(m_type[index]);
+    enum_field_types source_type= binlog_type(index);
     uint16 source_metadata= m_field_metadata[index];
     switch (source_type)
     {

=== modified file 'sql/sql_partition.cc'
--- sql/sql_partition.cc	2013-06-06 15:51:28 +0000
+++ sql/sql_partition.cc	2013-07-02 06:19:23 +0000
@@ -1563,7 +1563,7 @@ bool field_is_partition_charset(Field *f
       !(field->type() == MYSQL_TYPE_VARCHAR))
     return FALSE;
   {
-    CHARSET_INFO *cs= ((Field_str*)field)->charset();
+    CHARSET_INFO *cs= field->charset();
     if (!(field->type() == MYSQL_TYPE_STRING) ||
         !(cs->state & MY_CS_BINSORT))
       return TRUE;
@@ -1606,7 +1606,7 @@ bool check_part_func_fields(Field **ptr,
     */
     if (field_is_partition_charset(field))
     {
-      CHARSET_INFO *cs= ((Field_str*)field)->charset();
+      CHARSET_INFO *cs= field->charset();
       if (!ok_with_charsets ||
           cs->mbmaxlen > 1 ||
           cs->strxfrm_multiply > 1)
@@ -2090,6 +2090,8 @@ static int check_part_field(enum_field_t
     case MYSQL_TYPE_DATE:
     case MYSQL_TYPE_TIME:
     case MYSQL_TYPE_DATETIME:
+    case MYSQL_TYPE_TIME2:
+    case MYSQL_TYPE_DATETIME2:
       *result_type= STRING_RESULT;
       *need_cs_check= TRUE;
       return FALSE;
@@ -2102,6 +2104,7 @@ static int check_part_field(enum_field_t
     case MYSQL_TYPE_NEWDECIMAL:
     case MYSQL_TYPE_DECIMAL:
     case MYSQL_TYPE_TIMESTAMP:
+    case MYSQL_TYPE_TIMESTAMP2:
     case MYSQL_TYPE_NULL:
     case MYSQL_TYPE_FLOAT:
     case MYSQL_TYPE_DOUBLE:
@@ -2974,7 +2977,7 @@ static void copy_to_part_field_buffers(F
     restore_ptr++;
     if (!field->maybe_null() || !field->is_null())
     {
-      CHARSET_INFO *cs= ((Field_str*)field)->charset();
+      CHARSET_INFO *cs= field->charset();
       uint max_len= field->pack_length();
       uint data_len= field->data_length();
       uchar *field_buf= *field_bufs;

=== modified file 'sql/sql_prepare.cc'
--- sql/sql_prepare.cc	2013-06-06 19:32:29 +0000
+++ sql/sql_prepare.cc	2013-06-27 11:16:36 +0000
@@ -4409,7 +4409,7 @@ bool Protocol_local::store(const char *s
 bool Protocol_local::store(MYSQL_TIME *time, int decimals)
 {
   if (decimals != AUTO_SEC_PART_DIGITS)
-    time->second_part= sec_part_truncate(time->second_part, decimals);
+    my_time_trunc(time, decimals);
   return store_column(time, sizeof(MYSQL_TIME));
 }
 
@@ -4427,7 +4427,7 @@ bool Protocol_local::store_date(MYSQL_TI
 bool Protocol_local::store_time(MYSQL_TIME *time, int decimals)
 {
   if (decimals != AUTO_SEC_PART_DIGITS)
-    time->second_part= sec_part_truncate(time->second_part, decimals);
+    my_time_trunc(time, decimals);
   return store_column(time, sizeof(MYSQL_TIME));
 }
 

=== modified file 'sql/sql_select.cc'
--- sql/sql_select.cc	2013-06-06 19:32:29 +0000
+++ sql/sql_select.cc	2013-06-18 10:35:35 +0000
@@ -4307,7 +4307,7 @@ add_key_field(JOIN *join,
       {
         if ((*value)->cmp_type() != STRING_RESULT)
             return;
-        if (((Field_str*)field)->charset() != cond->compare_collation())
+        if (field->charset() != cond->compare_collation())
           return;
       }
     }
@@ -11857,7 +11857,7 @@ static bool check_simple_equality(Item *
 
       if (field_item->cmp_type() == STRING_RESULT)
       {
-        CHARSET_INFO *cs= ((Field_str*) field_item->field)->charset();
+        CHARSET_INFO *cs= field_item->field->charset();
         if (!item)
         {
           Item_func_eq *eq_item;

=== modified file 'sql/sql_table.cc'
--- sql/sql_table.cc	2013-06-06 19:32:29 +0000
+++ sql/sql_table.cc	2013-06-18 10:35:36 +0000
@@ -2790,6 +2790,8 @@ int prepare_create_field(Create_field *s
   case MYSQL_TYPE_NEWDATE:
   case MYSQL_TYPE_TIME:
   case MYSQL_TYPE_DATETIME:
+  case MYSQL_TYPE_TIME2:
+  case MYSQL_TYPE_DATETIME2:
   case MYSQL_TYPE_NULL:
     sql_field->pack_flag=f_settype((uint) sql_field->sql_type);
     break;
@@ -2808,6 +2810,7 @@ int prepare_create_field(Create_field *s
                           (sql_field->decimals << FIELDFLAG_DEC_SHIFT));
     break;
   case MYSQL_TYPE_TIMESTAMP:
+  case MYSQL_TYPE_TIMESTAMP2:
     /* fall-through */
   default:
     sql_field->pack_flag=(FIELDFLAG_NUMBER |
@@ -2893,7 +2896,7 @@ void promote_first_timestamp_column(List
 
   while ((column_definition= it++) != NULL)
   {
-    if (column_definition->sql_type == MYSQL_TYPE_TIMESTAMP ||      // TIMESTAMP
+    if (is_timestamp_type(column_definition->sql_type) ||              // TIMESTAMP
         column_definition->unireg_check == Field::TIMESTAMP_OLD_FIELD) // Legacy
     {
       if ((column_definition->flags & NOT_NULL_FLAG) != 0 && // NOT NULL,
@@ -3854,7 +3857,7 @@ mysql_prepare_create_table(THD *thd, HA_
 
     if (thd->variables.sql_mode & MODE_NO_ZERO_DATE &&
         !sql_field->def &&
-        sql_field->sql_type == MYSQL_TYPE_TIMESTAMP &&
+        is_timestamp_type(sql_field->sql_type) &&
         (sql_field->flags & NOT_NULL_FLAG) &&
         (type == Field::NONE || type == Field::TIMESTAMP_UN_FIELD))
     {
@@ -5950,7 +5953,8 @@ mysql_prepare_alter_table(THD *thd, TABL
     */
     if ((def->sql_type == MYSQL_TYPE_DATE ||
          def->sql_type == MYSQL_TYPE_NEWDATE ||
-         def->sql_type == MYSQL_TYPE_DATETIME) &&
+         def->sql_type == MYSQL_TYPE_DATETIME ||
+         def->sql_type == MYSQL_TYPE_DATETIME2) &&
          !alter_info->datetime_field &&
          !(~def->flags & (NO_DEFAULT_VALUE_FLAG | NOT_NULL_FLAG)) &&
          thd->variables.sql_mode & MODE_NO_ZERO_DATE)
@@ -7582,6 +7586,7 @@ bool mysql_alter_table(THD *thd,char *ne
         t_type= MYSQL_TIMESTAMP_DATE;
         break;
       case MYSQL_TYPE_DATETIME:
+      case MYSQL_TYPE_DATETIME2:
         f_val= "0000-00-00 00:00:00";
         t_type= MYSQL_TIMESTAMP_DATETIME;
         break;

=== modified file 'sql/table.cc'
--- sql/table.cc	2013-06-06 19:32:29 +0000
+++ sql/table.cc	2013-06-18 10:35:36 +0000
@@ -3281,7 +3281,7 @@ bool get_field(MEM_ROOT *mem, Field *fie
   }
   if (!(to= strmake_root(mem, str.ptr(), length)))
     length= 0;                                  // Safety fix
-  res->set(to, length, ((Field_str*)field)->charset());
+  res->set(to, length, field->charset());
   return 0;
 }
 

=== modified file 'storage/perfschema/pfs_engine_table.cc'
--- storage/perfschema/pfs_engine_table.cc	2013-06-06 19:32:29 +0000
+++ storage/perfschema/pfs_engine_table.cc	2013-06-18 10:35:36 +0000
@@ -514,7 +514,7 @@ void PFS_engine_table::set_field_enum(Fi
 
 void PFS_engine_table::set_field_timestamp(Field *f, ulonglong value)
 {
-  DBUG_ASSERT(f->real_type() == MYSQL_TYPE_TIMESTAMP);
+  DBUG_ASSERT(is_timestamp_type(f->real_type()));
   Field_timestamp *f2= (Field_timestamp*) f;
   f2->store_TIME((long)(value / 1000000), (value % 1000000));
 }


Follow ups

References