← Back to team overview

maria-developers team mailing list archive

Re: merge for MySQL56 FSP data types

 

Hi Sergei,

The latest version is attached.

See comments inline:

On 07/04/2013 09:49 PM, Sergei Golubchik wrote:
Hi, Alexander!

On Jul 02, Alexander Barkov wrote:
On 06/26/2013 11:11 PM, Sergei Golubchik wrote:
=== 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.

I thought you'll make real_type() methods to return MYSQL_TYPE_xxx
values, not MYSQL_TYPE_xxx2 values. Why wouldn't that work?

I did not even consider it.

This is my understanding of the things:


Field::type() is responsible for behaviour (interaction with Items).
It's OK to return MYSQL_TYPE_xxx here for the MySQL56 date types.

Field::real_type() is responsible for low level format, and
is used to check binary compatibility between fields.


I think using MYSQL_TYPE_xxx for the MySQL56 types will not work
in many cases. For example:

INSERT INTO t1 (mysql56_column) SELECT maria_column FROM t2;
INSERT INTO t1 (maria_column) SELECT mysql56_column FROM t2;

Or, in row-based-replication.


It will not be possible to detect that mysql56_column and maria_column
are in fact binary incompatible. real_type() is the only thing that
makes it possible to distinguish between them.


See the difference between Field_date and Field_newdate.
The former returns MYSQL_TYPE_DATE in real_type(),
the latter MYSQL_TYPE_NEWDATE, because they are binary
incompatible.



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.

Agree with that.

-String *Field_time::val_str(String *val_buffer,
-			    String *val_ptr __attribute__((unused)))
+String *Field_time::val_str(String *str,
+			    String *unused __attribute__((unused)))
   {
...
+  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).

Right.

=== 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
@@ -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];
  }

Why you didn't remove sec_part_truncate()?

Oops. Forgot to remove.

Done.



+/* 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_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      */

better to have a more verbose one multi-line comment for all three types,
something like

  /*
    mysql-5.6 compatibility types. they're only used internally
    for reading RBR mysql-5.6 events and mysql-5.6 frm files
    and they're are never sent to the client
  */

Done.


                          MYSQL_TYPE_NEWDECIMAL=246,
  			MYSQL_TYPE_ENUM=247,
  			MYSQL_TYPE_SET=248,

=== 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
@@ -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);

why sprintf?

changed to longlong10_to_str.


+  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.


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-07-05 05:46:34 +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];
@@ -171,12 +175,23 @@ static inline longlong sec_part_unshift(
 {
   return second_part * (longlong)log_10_int[TIME_SECOND_PART_DIGITS - digits];
 }
-static inline ulong sec_part_truncate(ulong second_part, uint 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)
 {
-  /* the cast here should be unnecessary! */
-  return second_part - second_part % (ulong)log_10_int[TIME_SECOND_PART_DIGITS - digits];
+  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-07-05 05:50:37 +0000
@@ -400,6 +400,16 @@ enum enum_field_types { MYSQL_TYPE_DECIM
 			MYSQL_TYPE_DATETIME, MYSQL_TYPE_YEAR,
 			MYSQL_TYPE_NEWDATE, MYSQL_TYPE_VARCHAR,
 			MYSQL_TYPE_BIT,
+                        /*
+                          mysql-5.6 compatibility temporal types.
+                          They're only used internally for reading RBR
+                          mysql-5.6 binary log events and mysql-5.6 frm files.
+                          They're never sent to the client.
+                        */
+                        MYSQL_TYPE_TIMESTAMP2,
+                        MYSQL_TYPE_DATETIME2,
+                        MYSQL_TYPE_TIME2,
+                        
                         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-07-05 06:02:50 +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= longlong10_to_str((longlong) tm->tv_sec, to, 10);
+  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