← Back to team overview

maria-developers team mailing list archive

Re: merge for MySQL56 temporal literals

 

Hi Sergei,


On 06/28/2013 07:23 PM, Sergei Golubchik wrote:
Hi, Alexander!

On Jun 26, Alexander Barkov wrote:
Hi Serg,

Please review the patch merging MySQL-5.6 temporal literals.

Here is is, below:

Thanks for your review.

I addressed most of your comments.

The latest patch is attached.

Please see the details inline:


=== modified file 'client/mysqlbinlog.cc'
--- client/mysqlbinlog.cc	2013-04-15 13:09:22 +0000
+++ client/mysqlbinlog.cc	2013-06-21 13:31:28 +0000
@@ -1541,13 +1541,14 @@ the mysql command line client.\n\n");

  static my_time_t convert_str_to_timestamp(const char* str)
  {
-  int was_cut;
+  MYSQL_TIME_STATUS status;
    MYSQL_TIME l_time;
    long dummy_my_timezone;
    uint dummy_in_dst_time_gap;
+
    /* We require a total specification (date AND time) */
-  if (str_to_datetime(str, (uint) strlen(str), &l_time, 0, &was_cut) !=
-      MYSQL_TIMESTAMP_DATETIME || was_cut)
+  if (str_to_datetime(str, (uint) strlen(str), &l_time, 0, &status) ||
+      l_time.time_type != MYSQL_TIMESTAMP_DATETIME || status.warnings)

Isn't it better to return l_time.time_type from str_to_datetime?
otherwise you'll always need to write like the above

if (str_to_datetime(...) || l_time.time_type != ...)


Let's see all the calls for str_to_datetime() and str_to_time():


- The cases that do not need timestamp type (only a boolean result is important):

field.cc:    have_smth_to_conv= !str_to_datetime(cs, from, len, &l_time,
field.cc:     bool func_res= !str_to_datetime(cs, from, len, &ltime,
field.cc:    !str_to_time(cs, from, len, &ltime,
sql_time.cc:  bool ret_val= str_to_datetime(cs, str, length, l_time,

(the code has reduced here after switch to boolean result type)


- The cases where timestamp type is needed:
item_create.cc: if (!str_to_datetime(cs, str, length, &ltime, flags, &status) && item_create.cc: if (!str_to_datetime(cs, str, length, &ltime, flags, &status) &&
item_create.cc:    if (!str_to_time(cs, str, length, &ltime, 0, &status) &&

(the new code creating temporal literals)


- The cases where timestamp type in needed only in DBUG_ASSERT:

item_cmpfunc.cc:
     DBUG_ASSERT(l_time->time_type == MYSQL_TIMESTAMP_DATETIME ||
                 l_time->time_type == MYSQL_TIMESTAMP_DATE);
item_cmpfunc.cc:
     DBUG_ASSERT(l_time->time_type != MYSQL_TIMESTAMP_TIME);
(in case of a timestamp_type result, a variable of timestamp_type
would be needed, but only in debug build, which is inconvenient).


Note, also the boolean result is inline with
str_to_datetime_with_warn(). It's easier to remember that way.


I think a boolean result is more convenient.



    {
      error("Incorrect date and time argument: %s", str);
      exit(1);
=== modified file 'include/my_time.h'
--- include/my_time.h	2013-05-24 15:09:59 +0000
+++ include/my_time.h	2013-06-25 06:28:08 +0000
@@ -78,14 +78,27 @@ extern uchar days_in_month[];
  #define TIME_MAX_VALUE_SECONDS (TIME_MAX_HOUR * 3600L + \
                                  TIME_MAX_MINUTE * 60L + TIME_MAX_SECOND)

+/*
+  Structure to return status from
+    str_to_datetime(), str_to_time(), number_to_datetime(), number_to_time()
+*/
+typedef struct st_mysql_time_status
+{
+  int warnings;
+  uint fractional_digits;

could you please rename it to "precision" ?
or at least, add a comment explaining what it contains


Renamed to "precision".

+} MYSQL_TIME_STATUS;
+
+static inline void my_time_status_init(MYSQL_TIME_STATUS *status)
+{
+  status->warnings= status->fractional_digits= 0;
+}
+
  my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date,
                     ulonglong flags, int *was_cut);
-enum enum_mysql_timestamp_type
-str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
-            ulonglong flag, int *warning);
-enum enum_mysql_timestamp_type
-str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
-                ulonglong flags, int *was_cut);
+my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
+                    ulonglong flag, MYSQL_TIME_STATUS *status);
+my_bool str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
+                        ulonglong flags, MYSQL_TIME_STATUS *status);
  longlong number_to_datetime(longlong nr, ulong sec_part, MYSQL_TIME *time_res,
                              ulonglong flags, int *was_cut);

=== added file 'mysql-test/r/temporal_literal.result'
--- mysql-test/r/temporal_literal.result	1970-01-01 00:00:00 +0000
+++ mysql-test/r/temporal_literal.result	2013-06-26 09:12:13 +0000
@@ -0,0 +1,412 @@
+DROP TABLE IF EXISTS t1, t2;
+SET NAMES latin1;
+#
+# Testing DATE literals
+#
+SELECT DATE'xxxx';
+ERROR HY000: Incorrect DATE value: 'xxxx'
+SELECT DATE'01';
+ERROR HY000: Incorrect DATE value: '01'
+SELECT DATE'01-01';
+ERROR HY000: Incorrect DATE value: '01-01'
+SELECT DATE'2001';
+ERROR HY000: Incorrect DATE value: '2001'
+SELECT DATE'2001-01';
+ERROR HY000: Incorrect DATE value: '2001-01'
+SELECT DATE'2001-00-00';
+DATE'2001-00-00'
+2001-00-00
+SELECT DATE'2001-01-00';
+DATE'2001-01-00'
+2001-01-00
+SELECT DATE'0000-00-00';
+DATE'0000-00-00'
+0000-00-00
+SELECT DATE'2001-01-01 00:00:00';
+ERROR HY000: Incorrect DATE value: '2001-01-01 00:00:00'
+SELECT DATE'01:01:01';
+DATE'01:01:01'
+2001-01-01
+SELECT DATE'01-01-01';
+DATE'01-01-01'
+2001-01-01
+SELECT DATE'2010-01-01';
+DATE'2010-01-01'
+2010-01-01
+SELECT DATE '2010-01-01';
+DATE '2010-01-01'
+2010-01-01
+CREATE TABLE t1 AS SELECT DATE'2010-01-01';
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `DATE'2010-01-01'` date NOT NULL DEFAULT '0000-00-00'
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE TABLE t1 AS SELECT
+{d'2001-01-01'},
+{d'2001-01-01 10:10:10'};

This (the second value, I mean) is what, backward compatibility?

These are ODBC escape sequences.

http://msdn.microsoft.com/en-us/library/ms190234%28v=sql.90%29.aspx

IIRC, applications like MSQuery and MSAccess use this format.



+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `{d'2001-01-01'}` date NOT NULL DEFAULT '0000-00-00',
+  `2001-01-01 10:10:10` varchar(19) NOT NULL DEFAULT ''
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+EXPLAIN EXTENDED SELECT {d'2010-01-01'};
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	NULL	No tables used
+Warnings:
+Note	1003	select DATE'2010-01-01' AS `{d'2010-01-01'}`
+EXPLAIN EXTENDED SELECT DATE'2010-01-01';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	NULL	No tables used
+Warnings:
+Note	1003	select DATE'2010-01-01' AS `DATE'2010-01-01'`
...
=== 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-26 09:23:23 +0000
@@ -230,20 +229,43 @@ static uint skip_digits(const char **str

      The second part may have an optional .###### fraction part.

-  RETURN VALUES
+  NOTES
+   This function should work with a format position vector as long as the
+   following things holds:
+   - All date are kept together and all time parts are kept together
+   - Date and time parts must be separated by blank
+   - Second fractions must come after second part and be separated
+     by a '.'.  (The second fractions are optional)
+   - AM/PM must come after second fractions (or after seconds if no fractions)
+   - Year must always been specified.
+   - If time is before date, then we will use datetime format only if
+     the argument consist of two parts, separated by space.
+     Otherwise we will assume the argument is a date.
+   - The hour part must be specified in hour-minute-second order.

I've removed this comment (NOTES) from when I was adding skip_digits()
and other small helpers. Simply because it was mostly wrong.
It's ok to add it back, if you'd like to, but then, please
fix to to reflect the reality (and fix the language too :)

on the other hand, feel free to remove it again, if you want.
the code is pretty much self-documenting now.

Removed the comment.


+
+    status->warnings is set to:
+    0                            Value OK
+    MYSQL_TIME_WARN_TRUNCATED    If value was cut during conversion
+    MYSQL_TIME_WARN_OUT_OF_RANGE check_date(date,flags) considers date invalid
+
+    l_time->time_type is set as follows:
      MYSQL_TIMESTAMP_NONE        String wasn't a timestamp, like
                                  [DD [HH:[MM:[SS]]]].fraction.
+                                l_time is not changed.
      MYSQL_TIMESTAMP_DATE        DATE string (YY MM and DD parts ok)
      MYSQL_TIMESTAMP_DATETIME    Full timestamp
      MYSQL_TIMESTAMP_ERROR       Timestamp with wrong values.
                                  All elements in l_time is set to 0
+  RETURN VALUES
+    0 - Ok
+    1 - Error
  */

  #define MAX_DATE_PARTS 8

-enum enum_mysql_timestamp_type
+my_bool
  str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
-                ulonglong flags, int *was_cut)
+                ulonglong flags, MYSQL_TIME_STATUS *status)
  {
    const char *end=str+length, *pos;
    uint number_of_fields= 0, digits, year_length, not_zero_date;
@@ -513,12 +538,26 @@ str_to_time(const char *str, uint length
        if (field_length-- > 0)
          value= value*10 + (uint) (uchar) (*str - '0');
      }
-    if (field_length > 0)
-      value*= (long) log_10_int[field_length];
-    else if (field_length < 0)
-      *warning|= MYSQL_TIME_WARN_TRUNCATED;
+    if (field_length >= 0)
+    {
+      status->fractional_digits= TIME_SECOND_PART_DIGITS - field_length;
+      if (field_length > 0)
+        value*= (long) log_10_int[field_length];
+    }
+    else
+    {
+      /* Scan digits left after microseconds */
+      status->fractional_digits= 6;
+      for ( ; str != end && my_isdigit(&my_charset_latin1, *str); str++)

1. old code set MYSQL_TIME_WARN_TRUNCATED, you don't do that anymore

Thanks for noticing this.

This did not affect the tests, but anyway,
I slightly modified the logic of str_to_time() to be closer to the
one in str_to_datetime():

A label "err" in the end, which sets time_type to MYSQL_TIMESTAMP_ERROR
and returns TRUE. The warning is now set before all "goto err".


2. there's skip_digits() helper, why not to use it?

Done.

3. actually the whole loop can be probaby replaced by get_digits() call

Which loop do you mean?


+      { }
+    }
      date[4]= (ulong) value;
    }
+  else if ((end - str) == 1 && *str == '.')
+  {
+    str++;
+    date[4]= 0;

is it for, like, "10:10:10." ?

+  }
    else
      date[4]=0;

@@ -553,7 +592,7 @@ str_to_time(const char *str, uint length
    if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
        date[2] > UINT_MAX || date[3] > UINT_MAX ||
        date[4] > UINT_MAX)
-    return MYSQL_TIMESTAMP_ERROR;
+    return TRUE;

you need to set l_time->type= MYSQL_TIMESTAMP_ERROR
(because you no longer return it). Probably better to do it
unconditionally, early in str_to_time().

Done.




    l_time->year=         0;                      /* For protocol::store_time */
    l_time->month=        0;
=== modified file 'sql/item_cmpfunc.cc'
--- sql/item_cmpfunc.cc	2013-06-06 15:51:28 +0000
+++ sql/item_cmpfunc.cc	2013-06-26 08:12:33 +0000
@@ -721,19 +721,18 @@ bool get_mysql_time_from_str(THD *thd, S
                               const char *warn_name, MYSQL_TIME *l_time)
  {
    bool value;
-  int error;
-  enum_mysql_timestamp_type timestamp_type;
+  MYSQL_TIME_STATUS status;
    int flags= TIME_FUZZY_DATE | MODE_INVALID_DATES;
    ErrConvString err(str);

+  DBUG_ASSERT(warn_type != MYSQL_TIMESTAMP_TIME); // Serg: why the below code?

I don't remember. Currently warn_type can never be MYSQL_TIMESTAMP_TIME here.
This function is only called from stored_field_cmp_to_item() which
is a historical hack and should go away completely. Together with
this get_mysql_time_from_str()

Ok. Let's have this DBUG_ASSERT then.
I also added two other DBUG_ASSERTs in this function,
to make sure we never get TIME here.


    if (warn_type == MYSQL_TIMESTAMP_TIME)
      flags|= TIME_TIME_ONLY;

-  timestamp_type=
-    str_to_datetime(str->charset(), str->ptr(), str->length(),
-                    l_time, flags, &error);
-
-  if (timestamp_type > MYSQL_TIMESTAMP_ERROR)
+  if (!str_to_datetime(str->charset(), str->ptr(), str->length(),
+                       l_time, flags, &status) &&
+      (l_time->time_type == MYSQL_TIMESTAMP_DATETIME ||
+       l_time->time_type == MYSQL_TIMESTAMP_DATE))
      /*
        Do not return yet, we may still want to throw a "trailing garbage"
        warning.
=== modified file 'sql/item_timefunc.h'
--- sql/item_timefunc.h	2013-03-13 21:33:52 +0000
+++ sql/item_timefunc.h	2013-06-24 07:26:29 +0000
@@ -526,6 +526,136 @@ class Item_timefunc :public Item_tempora
  };


+/**
+  DATE'2010-01-01'
+*/
+class Item_date_literal :public Item_datefunc
+{
+  MYSQL_TIME cached_time;
+public:
+  /**
+    Constructor for Item_date_literal.
+    @param ltime  DATE value.
+  */
+  Item_date_literal(MYSQL_TIME *ltime) :Item_datefunc()
+  {
+    cached_time= *ltime;
+    fix_length_and_dec();
+    fixed= 1;
+  }
+  const char *func_name() const { return "date_literal"; }
+  void print(String *str, enum_query_type query_type);
+  bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
+  void fix_length_and_dec()
+  {
+    Item_datefunc::fix_length_and_dec();
+    set_persist_maybe_null(false);
+  }
+  bool check_partition_func_processor(uchar *int_arg)
+  {
+    return FALSE;
+  }
+  bool basic_const_item() const { return true; }
+  bool const_item() const { return true; }
+  table_map used_tables() const { return (table_map) 0L; }
+  void cleanup()
+  {
+    // See Item_basic_const::cleanup()
+    if (orig_name)
+      name= orig_name;
+  }
+  bool eq(const Item *item, bool binary_cmp) const;
+};

Hmm. I don't really like this hierarchy. It's not very logical to derive
basic const items from Item_func. And you need to overwrite
lots of virtual methods. For every child too, so there's a lot of
code duplication :(

Would it be better to derive these classes from Item_basic_constant?


Well, that was a choice of "less evil".
I had to overwrite only these simple methods:

  bool check_partition_func_processor(uchar *int_arg)
  {
    return FALSE;
  }
  bool basic_const_item() const { return true; }
  bool const_item() const { return true; }
  table_map used_tables() const { return (table_map) 0L; }
  void cleanup()
  {
    // See Item_basic_const::cleanup()
    if (orig_name)
      name= orig_name;
  }


If we derive from Item_basic_constant, then we'll have to
overwrite more methods:

  enum Item_result result_type () const { return STRING_RESULT; }
CHARSET_INFO *charset_for_protocol(void) const { return &my_charset_bin; }
  enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
  Item_result cmp_type() const { return TIME_RESULT; }
  String *val_str(String *str)
  { return val_string_from_date(str); }
  my_decimal *val_decimal(my_decimal *decimal_value)
  { return  val_decimal_from_date(decimal_value); }
  longlong val_int();
  double val_real();

And further, move the code:
- from Item_temporal_func::val_int() to a new method Item::val_int_from_date() - from Item_temporal_func::val_real() to new method Item::val_real_from_date()

(to reuse the code in both Item_{time|datetime|date}_literal and
Item_temporal_func.

Looks like a much more amount of new code and amount of changes.

What do you think?


In long terms, I'd suggest to consider multiple inheritance here.
The methods related to constant/non-constant processing
could be collected into a separate class inside Item.



+
+
+/**
+  TIME'10:10:10'
+*/
+class Item_time_literal :public Item_timefunc
+{
+  MYSQL_TIME cached_time;
=== modified file 'sql/sql_yacc.yy'
--- sql/sql_yacc.yy	2013-06-06 19:32:29 +0000
+++ sql/sql_yacc.yy	2013-06-20 11:21:37 +0000
@@ -8741,7 +8742,49 @@ simple_expr:
                MYSQL_YYABORT;
            }
          | '{' ident expr '}'
-          { $$= $3; }
+          {
+            Item_string *item;
+            $$= NULL;
+            /*
+              If "expr" is reasonably short pure ASCII string literal,
+              try to parse known ODBC style date, time or timestamp literals,
+              e.g:
+              SELECT {d'2001-01-01'};
+              SELECT {t'10:20:30'};
+              SELECT {ts'2001-01-01 10:20:30'};
+            */
+            if ($3->type() == Item::STRING_ITEM &&
+               (item= (Item_string *) $3) &&
+                item->collation.repertoire == MY_REPERTOIRE_ASCII &&
+                item->str_value.length() < MAX_DATE_STRING_REP_LENGTH * 4)
+            {
+              enum_field_types type= MYSQL_TYPE_STRING;
+              ErrConvString str(&item->str_value);

1. it's a confusing usage of ErrConvString. this class is for error messages
2. why do you convert to system_charset_info at all here?

The token $3 is Item_string, which is created taking into account character_set_connection, which can be "tricky", e.g. ucs2, utf16, utf32.

So, we need to convert the value from a potentially "tricky" charset
to ASCII-compatible representation, so create_temporal_literal can
understand.

Monty also found this confusing when I used ErrConvString()
and passed it to get_charset_by_csname() and get_charset_by_name().
See sys_vars.cc in 10.0.

I agree that it might look confusing.
But this is the fastest and shortest possible way to convert
to an ASCII-compatible presentation.
ErrConvString() perfectly suites this need.

Maybe we should just rename it to ConvString or UTF8String :)


+              LEX_STRING *ls= &$2;
+              if (ls->length == 1)
+              {
+                if (ls->str[0] == 'd')  /* {d'2001-01-01'} */
+                  type= MYSQL_TYPE_DATE;
+                else if (ls->str[0] == 't') /* {t'10:20:30'} */
+                  type= MYSQL_TYPE_TIME;
+              }
+              else if (ls->length == 2) /* {ts'2001-01-01 10:20:30'} */
+              {
+                if (ls->str[0] == 't' && ls->str[1] == 's')
+                  type= MYSQL_TYPE_DATETIME;
+              }
+              if (type != MYSQL_TYPE_STRING)
+              {
+                const char *ascii= str.ptr();
+                $$= create_temporal_literal(YYTHD,
+                                            ascii, strlen(ascii),
+                                            system_charset_info,
+                                            type, false);
+              }
+            }
+            if ($$ == NULL)
+              $$= $3;
+          }
          | MATCH ident_list_arg AGAINST '(' bit_expr fulltext_options ')'
            {
              $2->push_front($5);
Regards,
Sergei

=== modified file 'client/mysqlbinlog.cc'
--- client/mysqlbinlog.cc	2013-04-15 13:09:22 +0000
+++ client/mysqlbinlog.cc	2013-06-21 13:31:28 +0000
@@ -1541,13 +1541,14 @@ the mysql command line client.\n\n");
 
 static my_time_t convert_str_to_timestamp(const char* str)
 {
-  int was_cut;
+  MYSQL_TIME_STATUS status;
   MYSQL_TIME l_time;
   long dummy_my_timezone;
   uint dummy_in_dst_time_gap;
+  
   /* We require a total specification (date AND time) */
-  if (str_to_datetime(str, (uint) strlen(str), &l_time, 0, &was_cut) !=
-      MYSQL_TIMESTAMP_DATETIME || was_cut)
+  if (str_to_datetime(str, (uint) strlen(str), &l_time, 0, &status) ||
+      l_time.time_type != MYSQL_TIMESTAMP_DATETIME || status.warnings)
   {
     error("Incorrect date and time argument: %s", str);
     exit(1);

=== modified file 'include/my_time.h'
--- include/my_time.h	2013-05-24 15:09:59 +0000
+++ include/my_time.h	2013-07-02 06:42:15 +0000
@@ -78,14 +78,27 @@ extern uchar days_in_month[];
 #define TIME_MAX_VALUE_SECONDS (TIME_MAX_HOUR * 3600L + \
                                 TIME_MAX_MINUTE * 60L + TIME_MAX_SECOND)
 
+/*
+  Structure to return status from
+    str_to_datetime(), str_to_time().
+*/
+typedef struct st_mysql_time_status
+{
+  int warnings;
+  uint precision;
+} MYSQL_TIME_STATUS;
+
+static inline void my_time_status_init(MYSQL_TIME_STATUS *status)
+{
+  status->warnings= status->precision= 0;
+}
+
 my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date,
                    ulonglong flags, int *was_cut);
-enum enum_mysql_timestamp_type
-str_to_time(const char *str, uint length, MYSQL_TIME *l_time, 
-            ulonglong flag, int *warning);
-enum enum_mysql_timestamp_type
-str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
-                ulonglong flags, int *was_cut);
+my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time, 
+                    ulonglong flag, MYSQL_TIME_STATUS *status);
+my_bool str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
+                        ulonglong flags, MYSQL_TIME_STATUS *status);
 longlong number_to_datetime(longlong nr, ulong sec_part, MYSQL_TIME *time_res,
                             ulonglong flags, int *was_cut);
 
@@ -107,7 +120,7 @@ ulonglong TIME_to_ulonglong_time(const M
 ulonglong TIME_to_ulonglong(const MYSQL_TIME *);
 double TIME_to_double(const MYSQL_TIME *my_time);
 
-longlong pack_time(MYSQL_TIME *my_time);
+longlong pack_time(const 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);

=== modified file 'mysql-test/r/cast.result'
--- mysql-test/r/cast.result	2013-05-07 11:05:09 +0000
+++ mysql-test/r/cast.result	2013-06-24 07:08:00 +0000
@@ -180,24 +180,24 @@ select 10.0+'10';
 select 10E+0+'10';
 10E+0+'10'
 20
-select CONVERT(DATE "2004-01-22 21:45:33" USING latin1);
-CONVERT(DATE "2004-01-22 21:45:33" USING latin1)
+select CONVERT(TIMESTAMP "2004-01-22 21:45:33" USING latin1);
+CONVERT(TIMESTAMP "2004-01-22 21:45:33" USING latin1)
 2004-01-22 21:45:33
-select CONVERT(DATE "2004-01-22 21:45:33",CHAR);
-CONVERT(DATE "2004-01-22 21:45:33",CHAR)
+select CONVERT(TIMESTAMP "2004-01-22 21:45:33",CHAR);
+CONVERT(TIMESTAMP "2004-01-22 21:45:33",CHAR)
 2004-01-22 21:45:33
-select CONVERT(DATE "2004-01-22 21:45:33",CHAR(4));
-CONVERT(DATE "2004-01-22 21:45:33",CHAR(4))
+select CONVERT(TIMESTAMP "2004-01-22 21:45:33",CHAR(4));
+CONVERT(TIMESTAMP "2004-01-22 21:45:33",CHAR(4))
 2004
 Warnings:
 Warning	1292	Truncated incorrect CHAR(4) value: '2004-01-22 21:45:33'
-select CONVERT(DATE "2004-01-22 21:45:33",BINARY(4));
-CONVERT(DATE "2004-01-22 21:45:33",BINARY(4))
+select CONVERT(TIMESTAMP "2004-01-22 21:45:33",BINARY(4));
+CONVERT(TIMESTAMP "2004-01-22 21:45:33",BINARY(4))
 2004
 Warnings:
 Warning	1292	Truncated incorrect BINARY(4) value: '2004-01-22 21:45:33'
-select CAST(DATE "2004-01-22 21:45:33" AS BINARY(4));
-CAST(DATE "2004-01-22 21:45:33" AS BINARY(4))
+select CAST(TIMESTAMP "2004-01-22 21:45:33" AS BINARY(4));
+CAST(TIMESTAMP "2004-01-22 21:45:33" AS BINARY(4))
 2004
 Warnings:
 Warning	1292	Truncated incorrect BINARY(4) value: '2004-01-22 21:45:33'

=== modified file 'mysql-test/r/partition_innodb.result'
--- mysql-test/r/partition_innodb.result	2013-06-06 19:32:29 +0000
+++ mysql-test/r/partition_innodb.result	2013-06-24 07:06:25 +0000
@@ -51,8 +51,8 @@ p200912	0
 p201103	1
 p201912	0
 SELECT count(*) FROM t1 p where c3 in
-(select c3 from t1 t where t.c3 < date '2011-04-26 19:19:44'
- and t.c3 > date '2011-04-26 19:18:44') ;
+(select c3 from t1 t where t.c3 < timestamp '2011-04-26 19:19:44'
+ and t.c3 > timestamp '2011-04-26 19:18:44') ;
 count(*)
 0
 DROP TABLE t1;

=== added file 'mysql-test/r/temporal_literal.result'
--- mysql-test/r/temporal_literal.result	1970-01-01 00:00:00 +0000
+++ mysql-test/r/temporal_literal.result	2013-06-26 09:12:13 +0000
@@ -0,0 +1,412 @@
+DROP TABLE IF EXISTS t1, t2;
+SET NAMES latin1;
+#
+# Testing DATE literals
+#
+SELECT DATE'xxxx';
+ERROR HY000: Incorrect DATE value: 'xxxx'
+SELECT DATE'01';
+ERROR HY000: Incorrect DATE value: '01'
+SELECT DATE'01-01';
+ERROR HY000: Incorrect DATE value: '01-01'
+SELECT DATE'2001';
+ERROR HY000: Incorrect DATE value: '2001'
+SELECT DATE'2001-01';
+ERROR HY000: Incorrect DATE value: '2001-01'
+SELECT DATE'2001-00-00';
+DATE'2001-00-00'
+2001-00-00
+SELECT DATE'2001-01-00';
+DATE'2001-01-00'
+2001-01-00
+SELECT DATE'0000-00-00';
+DATE'0000-00-00'
+0000-00-00
+SELECT DATE'2001-01-01 00:00:00';
+ERROR HY000: Incorrect DATE value: '2001-01-01 00:00:00'
+SELECT DATE'01:01:01';
+DATE'01:01:01'
+2001-01-01
+SELECT DATE'01-01-01';
+DATE'01-01-01'
+2001-01-01
+SELECT DATE'2010-01-01';
+DATE'2010-01-01'
+2010-01-01
+SELECT DATE '2010-01-01';
+DATE '2010-01-01'
+2010-01-01
+CREATE TABLE t1 AS SELECT DATE'2010-01-01';
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `DATE'2010-01-01'` date NOT NULL DEFAULT '0000-00-00'
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE TABLE t1 AS SELECT
+{d'2001-01-01'},
+{d'2001-01-01 10:10:10'};
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `{d'2001-01-01'}` date NOT NULL DEFAULT '0000-00-00',
+  `2001-01-01 10:10:10` varchar(19) NOT NULL DEFAULT ''
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+EXPLAIN EXTENDED SELECT {d'2010-01-01'};
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	NULL	No tables used
+Warnings:
+Note	1003	select DATE'2010-01-01' AS `{d'2010-01-01'}`
+EXPLAIN EXTENDED SELECT DATE'2010-01-01';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	NULL	No tables used
+Warnings:
+Note	1003	select DATE'2010-01-01' AS `DATE'2010-01-01'`
+#
+# Testing DATE literals in non-default sql_mode
+#
+SET sql_mode=no_zero_in_date;
+SELECT DATE'2001-00-00';
+ERROR HY000: Incorrect DATE value: '2001-00-00'
+SELECT DATE'2001-01-00';
+ERROR HY000: Incorrect DATE value: '2001-01-00'
+SELECT DATE'0000-00-00';
+DATE'0000-00-00'
+0000-00-00
+SET sql_mode=no_zero_date;
+SELECT DATE'0000-00-00';
+ERROR HY000: Incorrect DATE value: '0000-00-00'
+SET sql_mode=default;
+#
+# Testing TIME literals
+#
+SELECT TIME'xxxx';
+ERROR HY000: Incorrect TIME value: 'xxxx'
+SELECT TIME'900:00:00';
+ERROR HY000: Incorrect TIME value: '900:00:00'
+SELECT TIME'-900:00:00';
+ERROR HY000: Incorrect TIME value: '-900:00:00'
+SELECT TIME'1 24:00:00';
+TIME'1 24:00:00'
+48:00:00
+SELECT TIME'30 24:00:00';
+TIME'30 24:00:00'
+744:00:00
+SELECT TIME'0000-00-00 00:00:00';
+ERROR HY000: Incorrect TIME value: '0000-00-00 00:00:00'
+SELECT TIME'40 24:00:00';
+ERROR HY000: Incorrect TIME value: '40 24:00:00'
+SELECT TIME'10';
+TIME'10'
+00:00:10
+SELECT TIME'10:10';
+TIME'10:10'
+10:10:00
+SELECT TIME'10:11.12';
+TIME'10:11.12'
+10:11:00.12
+SELECT TIME'10:10:10';
+TIME'10:10:10'
+10:10:10
+SELECT TIME'10:10:10.';
+TIME'10:10:10.'
+10:10:10
+SELECT TIME'10:10:10.1';
+TIME'10:10:10.1'
+10:10:10.1
+SELECT TIME'10:10:10.12';
+TIME'10:10:10.12'
+10:10:10.12
+SELECT TIME'10:10:10.123';
+TIME'10:10:10.123'
+10:10:10.123
+SELECT TIME'10:10:10.1234';
+TIME'10:10:10.1234'
+10:10:10.1234
+SELECT TIME'10:10:10.12345';
+TIME'10:10:10.12345'
+10:10:10.12345
+SELECT TIME'10:10:10.123456';
+TIME'10:10:10.123456'
+10:10:10.123456
+SELECT TIME'-10:00:00';
+TIME'-10:00:00'
+-10:00:00
+SELECT TIME '10:11:12';
+TIME '10:11:12'
+10:11:12
+CREATE TABLE t1 AS SELECT
+TIME'10:10:10',
+TIME'10:10:10.',
+TIME'10:10:10.1',
+TIME'10:10:10.12',
+TIME'10:10:10.123',
+TIME'10:10:10.1234',
+TIME'10:10:10.12345',
+TIME'10:10:10.123456';
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `TIME'10:10:10'` time NOT NULL DEFAULT '00:00:00',
+  `TIME'10:10:10.'` time NOT NULL DEFAULT '00:00:00',
+  `TIME'10:10:10.1'` time(1) NOT NULL DEFAULT '00:00:00.0',
+  `TIME'10:10:10.12'` time(2) NOT NULL DEFAULT '00:00:00.00',
+  `TIME'10:10:10.123'` time(3) NOT NULL DEFAULT '00:00:00.000',
+  `TIME'10:10:10.1234'` time(4) NOT NULL DEFAULT '00:00:00.0000',
+  `TIME'10:10:10.12345'` time(5) NOT NULL DEFAULT '00:00:00.00000',
+  `TIME'10:10:10.123456'` time(6) NOT NULL DEFAULT '00:00:00.000000'
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE TABLE t1 AS SELECT
+{t'10:10:10'},
+{t'10:10:10.'},
+{t'10:10:10.123456'},
+{t'2001-01-01'};
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `{t'10:10:10'}` time NOT NULL DEFAULT '00:00:00',
+  `{t'10:10:10.'}` time NOT NULL DEFAULT '00:00:00',
+  `{t'10:10:10.123456'}` time(6) NOT NULL DEFAULT '00:00:00.000000',
+  `2001-01-01` varchar(10) NOT NULL DEFAULT ''
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+EXPLAIN EXTENDED SELECT {t'10:01:01'};
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	NULL	No tables used
+Warnings:
+Note	1003	select TIME'10:01:01' AS `{t'10:01:01'}`
+EXPLAIN EXTENDED SELECT TIME'10:01:01';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	NULL	No tables used
+Warnings:
+Note	1003	select TIME'10:01:01' AS `TIME'10:01:01'`
+#
+# Testing TIMESTAMP literals
+#
+SELECT TIMESTAMP'xxxx';
+ERROR HY000: Incorrect DATETIME value: 'xxxx'
+SELECT TIMESTAMP'2010';
+ERROR HY000: Incorrect DATETIME value: '2010'
+SELECT TIMESTAMP'2010-01';
+ERROR HY000: Incorrect DATETIME value: '2010-01'
+SELECT TIMESTAMP'2010-01-01';
+ERROR HY000: Incorrect DATETIME value: '2010-01-01'
+SELECT TIMESTAMP'2010-01-01 00';
+TIMESTAMP'2010-01-01 00'
+2010-01-01 00:00:00
+SELECT TIMESTAMP'2010-01-01 00:01';
+TIMESTAMP'2010-01-01 00:01'
+2010-01-01 00:01:00
+SELECT TIMESTAMP'2010-01-01 10:10:10';
+TIMESTAMP'2010-01-01 10:10:10'
+2010-01-01 10:10:10
+SELECT TIMESTAMP'2010-01-01 10:10:10.';
+TIMESTAMP'2010-01-01 10:10:10.'
+2010-01-01 10:10:10
+SELECT TIMESTAMP'2010-01-01 10:10:10.1';
+TIMESTAMP'2010-01-01 10:10:10.1'
+2010-01-01 10:10:10.1
+SELECT TIMESTAMP'2010-01-01 10:10:10.12';
+TIMESTAMP'2010-01-01 10:10:10.12'
+2010-01-01 10:10:10.12
+SELECT TIMESTAMP'2010-01-01 10:10:10.123';
+TIMESTAMP'2010-01-01 10:10:10.123'
+2010-01-01 10:10:10.123
+SELECT TIMESTAMP'2010-01-01 10:10:10.1234';
+TIMESTAMP'2010-01-01 10:10:10.1234'
+2010-01-01 10:10:10.1234
+SELECT TIMESTAMP'2010-01-01 10:10:10.12345';
+TIMESTAMP'2010-01-01 10:10:10.12345'
+2010-01-01 10:10:10.12345
+SELECT TIMESTAMP'2010-01-01 10:10:10.123456';
+TIMESTAMP'2010-01-01 10:10:10.123456'
+2010-01-01 10:10:10.123456
+SELECT TIMESTAMP '2010-01-01 10:20:30';
+TIMESTAMP '2010-01-01 10:20:30'
+2010-01-01 10:20:30
+CREATE TABLE t1 AS SELECT
+TIMESTAMP'2010-01-01 10:10:10',
+TIMESTAMP'2010-01-01 10:10:10.',
+TIMESTAMP'2010-01-01 10:10:10.1',
+TIMESTAMP'2010-01-01 10:10:10.12',
+TIMESTAMP'2010-01-01 10:10:10.123',
+TIMESTAMP'2010-01-01 10:10:10.1234',
+TIMESTAMP'2010-01-01 10:10:10.12345',
+TIMESTAMP'2010-01-01 10:10:10.123456';
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `TIMESTAMP'2010-01-01 10:10:10'` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+  `TIMESTAMP'2010-01-01 10:10:10.'` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+  `TIMESTAMP'2010-01-01 10:10:10.1'` datetime(1) NOT NULL DEFAULT '0000-00-00 00:00:00.0',
+  `TIMESTAMP'2010-01-01 10:10:10.12'` datetime(2) NOT NULL DEFAULT '0000-00-00 00:00:00.00',
+  `TIMESTAMP'2010-01-01 10:10:10.123'` datetime(3) NOT NULL DEFAULT '0000-00-00 00:00:00.000',
+  `TIMESTAMP'2010-01-01 10:10:10.1234'` datetime(4) NOT NULL DEFAULT '0000-00-00 00:00:00.0000',
+  `TIMESTAMP'2010-01-01 10:10:10.12345'` datetime(5) NOT NULL DEFAULT '0000-00-00 00:00:00.00000',
+  `TIMESTAMP'2010-01-01 10:10:10.123456'` datetime(6) NOT NULL DEFAULT '0000-00-00 00:00:00.000000'
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+CREATE TABLE t1 AS SELECT
+{ts'2001-01-01 10:10:10'},
+{ts'2001-01-01 10:10:10.'},
+{ts'2001-01-01 10:10:10.123456'},
+{ts'2001-01-01'};
+SHOW CREATE TABLE t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `{ts'2001-01-01 10:10:10'}` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+  `{ts'2001-01-01 10:10:10.'}` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+  `{ts'2001-01-01 10:10:10.123456'}` datetime(6) NOT NULL DEFAULT '0000-00-00 00:00:00.000000',
+  `2001-01-01` varchar(10) NOT NULL DEFAULT ''
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+DROP TABLE t1;
+EXPLAIN EXTENDED SELECT {ts'2010-01-01 10:10:10'};
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	NULL	No tables used
+Warnings:
+Note	1003	select TIMESTAMP'2010-01-01 10:10:10' AS `{ts'2010-01-01 10:10:10'}`
+EXPLAIN EXTENDED SELECT TIMESTAMP'2010-01-01 10:10:10';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	filtered	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	NULL	No tables used
+Warnings:
+Note	1003	select TIMESTAMP'2010-01-01 10:10:10' AS `TIMESTAMP'2010-01-01 10:10:10'`
+#
+# Testing nanosecond rounding for TIMESTAMP literals with bad dates
+#
+SELECT TIMESTAMP'2001-00-00 00:00:00.999999';
+TIMESTAMP'2001-00-00 00:00:00.999999'
+2001-00-00 00:00:00.999999
+SELECT TIMESTAMP'2001-00-01 00:00:00.999999';
+TIMESTAMP'2001-00-01 00:00:00.999999'
+2001-00-01 00:00:00.999999
+SELECT TIMESTAMP'2001-01-00 00:00:00.999999';
+TIMESTAMP'2001-01-00 00:00:00.999999'
+2001-01-00 00:00:00.999999
+SELECT TIMESTAMP'2001-00-00 00:00:00.9999999';
+ERROR HY000: Incorrect DATETIME value: '2001-00-00 00:00:00.9999999'
+SELECT TIMESTAMP'2001-00-01 00:00:00.9999999';
+ERROR HY000: Incorrect DATETIME value: '2001-00-01 00:00:00.9999999'
+SELECT TIMESTAMP'2001-01-00 00:00:00.9999999';
+ERROR HY000: Incorrect DATETIME value: '2001-01-00 00:00:00.9999999'
+#
+# String literal with bad dates and nanoseconds to DATETIME(N)
+#
+CREATE TABLE t1 (a DATETIME(6));
+INSERT INTO t1 VALUES ('2001-00-00 00:00:00.9999999');
+Warnings:
+Warning	1265	Data truncated for column 'a' at row 1
+INSERT INTO t1 VALUES ('2001-00-01 00:00:00.9999999');
+Warnings:
+Warning	1265	Data truncated for column 'a' at row 1
+INSERT INTO t1 VALUES ('2001-01-00 00:00:00.9999999');
+Warnings:
+Warning	1265	Data truncated for column 'a' at row 1
+SELECT * FROM t1;
+a
+2001-00-00 00:00:00.999999
+2001-00-01 00:00:00.999999
+2001-01-00 00:00:00.999999
+DROP TABLE t1;
+CREATE TABLE t1 (a DATETIME(5));
+INSERT INTO t1 VALUES ('2001-00-00 00:00:00.9999999');
+Warnings:
+Warning	1265	Data truncated for column 'a' at row 1
+INSERT INTO t1 VALUES ('2001-00-01 00:00:00.9999999');
+Warnings:
+Warning	1265	Data truncated for column 'a' at row 1
+INSERT INTO t1 VALUES ('2001-01-00 00:00:00.9999999');
+Warnings:
+Warning	1265	Data truncated for column 'a' at row 1
+SELECT * FROM t1;
+a
+2001-00-00 00:00:00.99999
+2001-00-01 00:00:00.99999
+2001-01-00 00:00:00.99999
+DROP TABLE t1;
+CREATE TABLE t1 (a DATETIME);
+INSERT INTO t1 VALUES ('2001-00-00 00:00:00.9999999');
+Warnings:
+Warning	1265	Data truncated for column 'a' at row 1
+INSERT INTO t1 VALUES ('2001-00-01 00:00:00.9999999');
+Warnings:
+Warning	1265	Data truncated for column 'a' at row 1
+INSERT INTO t1 VALUES ('2001-01-00 00:00:00.9999999');
+Warnings:
+Warning	1265	Data truncated for column 'a' at row 1
+SELECT * FROM t1;
+a
+2001-00-00 00:00:00
+2001-00-01 00:00:00
+2001-01-00 00:00:00
+DROP TABLE t1;
+#
+# Testing Item_date_literal::eq
+#
+CREATE TABLE t1 (a DATE);
+INSERT INTO t1 VALUES ('2001-01-01'),('2003-01-01');
+SELECT * FROM t1 WHERE a BETWEEN DATE'2001-01-01' AND DATE'2002-01-01';
+a
+2001-01-01
+SELECT DATE'2001-01-01' FROM t1 GROUP BY DATE'2001-01-01';
+DATE'2001-01-01'
+2001-01-01
+DROP TABLE t1;
+#
+# TIME literals in no-zero date context
+#
+SELECT TO_DAYS(TIME'00:00:00');
+TO_DAYS(TIME'00:00:00')
+NULL
+Warnings:
+Warning	1292	Incorrect datetime value: '00:00:00'
+SELECT TO_SECONDS(TIME'00:00:00');
+TO_SECONDS(TIME'00:00:00')
+NULL
+Warnings:
+Warning	1292	Incorrect datetime value: '00:00:00'
+SELECT DAYOFYEAR(TIME'00:00:00');
+DAYOFYEAR(TIME'00:00:00')
+NULL
+Warnings:
+Warning	1292	Incorrect datetime value: '00:00:00'
+SELECT WEEK(TIME'00:00:00');
+WEEK(TIME'00:00:00')
+NULL
+Warnings:
+Warning	1292	Incorrect datetime value: '00:00:00'
+SELECT YEARWEEK(TIME'00:00:00');
+YEARWEEK(TIME'00:00:00')
+NULL
+Warnings:
+Warning	1292	Incorrect datetime value: '00:00:00'
+SELECT WEEKDAY(TIME'00:00:00');
+WEEKDAY(TIME'00:00:00')
+NULL
+Warnings:
+Warning	1292	Incorrect datetime value: '00:00:00'
+SELECT CONVERT_TZ(TIME'00:00:00','+00:00','+01:00');
+CONVERT_TZ(TIME'00:00:00','+00:00','+01:00')
+NULL
+Warnings:
+Warning	1292	Incorrect datetime value: '00:00:00'
+SELECT DATE_ADD(TIME'00:00:00', INTERVAL 1 HOUR);
+DATE_ADD(TIME'00:00:00', INTERVAL 1 HOUR)
+NULL
+Warnings:
+Warning	1292	Incorrect datetime value: '00:00:00'
+SELECT TIMESTAMPDIFF(SECOND,TIME'00:00:00', TIME'00:00:00');
+TIMESTAMPDIFF(SECOND,TIME'00:00:00', TIME'00:00:00')
+NULL
+Warnings:
+Warning	1292	Incorrect datetime value: '00:00:00'
+#
+# Testing Item_func::fix_fields()
+#
+SELECT 'a' LIKE 'a' ESCAPE DATE'2001-01-01';
+ERROR HY000: Incorrect arguments to ESCAPE
+SELECT 'a' LIKE 'a' ESCAPE TIMESTAMP'2001-01-01 00:00:00';
+ERROR HY000: Incorrect arguments to ESCAPE
+SELECT 'a' LIKE 'a' ESCAPE TIME'00:00:00';
+ERROR HY000: Incorrect arguments to ESCAPE

=== modified file 'mysql-test/t/cast.test'
--- mysql-test/t/cast.test	2013-05-07 11:05:09 +0000
+++ mysql-test/t/cast.test	2013-06-24 07:07:17 +0000
@@ -74,11 +74,11 @@ select 10E+0+'10';
 
 # The following cast creates warnings
 
-select CONVERT(DATE "2004-01-22 21:45:33" USING latin1);
-select CONVERT(DATE "2004-01-22 21:45:33",CHAR);
-select CONVERT(DATE "2004-01-22 21:45:33",CHAR(4));
-select CONVERT(DATE "2004-01-22 21:45:33",BINARY(4));
-select CAST(DATE "2004-01-22 21:45:33" AS BINARY(4));
+select CONVERT(TIMESTAMP "2004-01-22 21:45:33" USING latin1);
+select CONVERT(TIMESTAMP "2004-01-22 21:45:33",CHAR);
+select CONVERT(TIMESTAMP "2004-01-22 21:45:33",CHAR(4));
+select CONVERT(TIMESTAMP "2004-01-22 21:45:33",BINARY(4));
+select CAST(TIMESTAMP "2004-01-22 21:45:33" AS BINARY(4));
 select CAST(0xb3 as signed);
 select CAST(0x8fffffffffffffff as signed);
 select CAST(0xffffffffffffffff as unsigned);

=== modified file 'mysql-test/t/partition_innodb.test'
--- mysql-test/t/partition_innodb.test	2013-01-31 08:48:19 +0000
+++ mysql-test/t/partition_innodb.test	2013-06-24 07:05:45 +0000
@@ -60,8 +60,8 @@ insert into t1(c2,c3) values ("Test row"
 
 SELECT PARTITION_NAME, TABLE_ROWS FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 't1' AND TABLE_SCHEMA = 'test';
 SELECT count(*) FROM t1 p where c3 in
-(select c3 from t1 t where t.c3 < date '2011-04-26 19:19:44'
- and t.c3 > date '2011-04-26 19:18:44') ;
+(select c3 from t1 t where t.c3 < timestamp '2011-04-26 19:19:44'
+ and t.c3 > timestamp '2011-04-26 19:18:44') ;
 
 DROP TABLE t1;
 

=== added file 'mysql-test/t/temporal_literal.test'
--- mysql-test/t/temporal_literal.test	1970-01-01 00:00:00 +0000
+++ mysql-test/t/temporal_literal.test	2013-06-26 09:12:06 +0000
@@ -0,0 +1,214 @@
+--disable_warnings
+DROP TABLE IF EXISTS t1, t2;
+--enable_warnings
+
+SET NAMES latin1;
+
+
+--echo #
+--echo # Testing DATE literals
+--echo #
+--error ER_WRONG_VALUE
+SELECT DATE'xxxx';
+--error ER_WRONG_VALUE
+SELECT DATE'01';
+--error ER_WRONG_VALUE
+SELECT DATE'01-01';
+--error ER_WRONG_VALUE
+SELECT DATE'2001';
+--error ER_WRONG_VALUE
+SELECT DATE'2001-01';
+SELECT DATE'2001-00-00';
+SELECT DATE'2001-01-00';
+SELECT DATE'0000-00-00';
+--error ER_WRONG_VALUE
+SELECT DATE'2001-01-01 00:00:00';
+SELECT DATE'01:01:01';
+SELECT DATE'01-01-01';
+SELECT DATE'2010-01-01';
+SELECT DATE '2010-01-01';
+CREATE TABLE t1 AS SELECT DATE'2010-01-01';
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+CREATE TABLE t1 AS SELECT
+  {d'2001-01-01'},
+  {d'2001-01-01 10:10:10'};
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+EXPLAIN EXTENDED SELECT {d'2010-01-01'};
+EXPLAIN EXTENDED SELECT DATE'2010-01-01';
+
+--echo #
+--echo # Testing DATE literals in non-default sql_mode
+--echo #
+SET sql_mode=no_zero_in_date;
+--error ER_WRONG_VALUE
+SELECT DATE'2001-00-00';
+--error ER_WRONG_VALUE
+SELECT DATE'2001-01-00';
+SELECT DATE'0000-00-00';
+
+SET sql_mode=no_zero_date;
+--error ER_WRONG_VALUE
+SELECT DATE'0000-00-00';
+SET sql_mode=default;
+
+--echo #
+--echo # Testing TIME literals
+--echo #
+--error ER_WRONG_VALUE
+SELECT TIME'xxxx';
+--error ER_WRONG_VALUE
+SELECT TIME'900:00:00';
+--error ER_WRONG_VALUE
+SELECT TIME'-900:00:00';
+SELECT TIME'1 24:00:00';
+SELECT TIME'30 24:00:00';
+--error ER_WRONG_VALUE
+SELECT TIME'0000-00-00 00:00:00';
+--error ER_WRONG_VALUE
+SELECT TIME'40 24:00:00';
+SELECT TIME'10';
+SELECT TIME'10:10';
+SELECT TIME'10:11.12';
+SELECT TIME'10:10:10';
+SELECT TIME'10:10:10.';
+SELECT TIME'10:10:10.1';
+SELECT TIME'10:10:10.12';
+SELECT TIME'10:10:10.123';
+SELECT TIME'10:10:10.1234';
+SELECT TIME'10:10:10.12345';
+SELECT TIME'10:10:10.123456';
+SELECT TIME'-10:00:00';
+SELECT TIME '10:11:12';
+CREATE TABLE t1 AS SELECT
+  TIME'10:10:10',
+  TIME'10:10:10.',
+  TIME'10:10:10.1',
+  TIME'10:10:10.12',
+  TIME'10:10:10.123',
+  TIME'10:10:10.1234',
+  TIME'10:10:10.12345',
+  TIME'10:10:10.123456';
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+CREATE TABLE t1 AS SELECT
+  {t'10:10:10'},
+  {t'10:10:10.'},
+  {t'10:10:10.123456'},
+  {t'2001-01-01'};
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+EXPLAIN EXTENDED SELECT {t'10:01:01'};
+EXPLAIN EXTENDED SELECT TIME'10:01:01';
+
+
+--echo #
+--echo # Testing TIMESTAMP literals
+--echo #
+--error ER_WRONG_VALUE
+SELECT TIMESTAMP'xxxx';
+--error ER_WRONG_VALUE
+SELECT TIMESTAMP'2010';
+--error ER_WRONG_VALUE
+SELECT TIMESTAMP'2010-01';
+--error ER_WRONG_VALUE
+SELECT TIMESTAMP'2010-01-01';
+SELECT TIMESTAMP'2010-01-01 00';
+SELECT TIMESTAMP'2010-01-01 00:01';
+SELECT TIMESTAMP'2010-01-01 10:10:10';
+SELECT TIMESTAMP'2010-01-01 10:10:10.';
+SELECT TIMESTAMP'2010-01-01 10:10:10.1';
+SELECT TIMESTAMP'2010-01-01 10:10:10.12';
+SELECT TIMESTAMP'2010-01-01 10:10:10.123';
+SELECT TIMESTAMP'2010-01-01 10:10:10.1234';
+SELECT TIMESTAMP'2010-01-01 10:10:10.12345';
+SELECT TIMESTAMP'2010-01-01 10:10:10.123456';
+SELECT TIMESTAMP '2010-01-01 10:20:30';
+CREATE TABLE t1 AS SELECT
+  TIMESTAMP'2010-01-01 10:10:10',
+  TIMESTAMP'2010-01-01 10:10:10.',
+  TIMESTAMP'2010-01-01 10:10:10.1',
+  TIMESTAMP'2010-01-01 10:10:10.12',
+  TIMESTAMP'2010-01-01 10:10:10.123',
+  TIMESTAMP'2010-01-01 10:10:10.1234',
+  TIMESTAMP'2010-01-01 10:10:10.12345',
+  TIMESTAMP'2010-01-01 10:10:10.123456';
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+CREATE TABLE t1 AS SELECT
+  {ts'2001-01-01 10:10:10'},
+  {ts'2001-01-01 10:10:10.'},
+  {ts'2001-01-01 10:10:10.123456'},
+  {ts'2001-01-01'};
+SHOW CREATE TABLE t1;
+DROP TABLE t1;
+EXPLAIN EXTENDED SELECT {ts'2010-01-01 10:10:10'};
+EXPLAIN EXTENDED SELECT TIMESTAMP'2010-01-01 10:10:10';
+
+--echo #
+--echo # Testing nanosecond rounding for TIMESTAMP literals with bad dates
+--echo #
+SELECT TIMESTAMP'2001-00-00 00:00:00.999999';
+SELECT TIMESTAMP'2001-00-01 00:00:00.999999';
+SELECT TIMESTAMP'2001-01-00 00:00:00.999999';
+--error ER_WRONG_VALUE
+SELECT TIMESTAMP'2001-00-00 00:00:00.9999999';
+--error ER_WRONG_VALUE
+SELECT TIMESTAMP'2001-00-01 00:00:00.9999999';
+--error ER_WRONG_VALUE
+SELECT TIMESTAMP'2001-01-00 00:00:00.9999999';
+
+--echo #
+--echo # String literal with bad dates and nanoseconds to DATETIME(N)
+--echo #
+CREATE TABLE t1 (a DATETIME(6));
+INSERT INTO t1 VALUES ('2001-00-00 00:00:00.9999999');
+INSERT INTO t1 VALUES ('2001-00-01 00:00:00.9999999');
+INSERT INTO t1 VALUES ('2001-01-00 00:00:00.9999999');
+SELECT * FROM t1;
+DROP TABLE t1;
+CREATE TABLE t1 (a DATETIME(5));
+INSERT INTO t1 VALUES ('2001-00-00 00:00:00.9999999');
+INSERT INTO t1 VALUES ('2001-00-01 00:00:00.9999999');
+INSERT INTO t1 VALUES ('2001-01-00 00:00:00.9999999');
+SELECT * FROM t1;
+DROP TABLE t1;
+CREATE TABLE t1 (a DATETIME);
+INSERT INTO t1 VALUES ('2001-00-00 00:00:00.9999999');
+INSERT INTO t1 VALUES ('2001-00-01 00:00:00.9999999');
+INSERT INTO t1 VALUES ('2001-01-00 00:00:00.9999999');
+SELECT * FROM t1;
+DROP TABLE t1;
+
+--echo #
+--echo # Testing Item_date_literal::eq
+--echo #
+CREATE TABLE t1 (a DATE);
+INSERT INTO t1 VALUES ('2001-01-01'),('2003-01-01');
+SELECT * FROM t1 WHERE a BETWEEN DATE'2001-01-01' AND DATE'2002-01-01';
+SELECT DATE'2001-01-01' FROM t1 GROUP BY DATE'2001-01-01';
+DROP TABLE t1;
+
+--echo #
+--echo # TIME literals in no-zero date context
+--echo #
+SELECT TO_DAYS(TIME'00:00:00');
+SELECT TO_SECONDS(TIME'00:00:00');
+SELECT DAYOFYEAR(TIME'00:00:00');
+SELECT WEEK(TIME'00:00:00');
+SELECT YEARWEEK(TIME'00:00:00');
+SELECT WEEKDAY(TIME'00:00:00');
+SELECT CONVERT_TZ(TIME'00:00:00','+00:00','+01:00');
+SELECT DATE_ADD(TIME'00:00:00', INTERVAL 1 HOUR);
+SELECT TIMESTAMPDIFF(SECOND,TIME'00:00:00', TIME'00:00:00');
+
+--echo #
+--echo # Testing Item_func::fix_fields()
+--echo #
+--error ER_WRONG_ARGUMENTS
+SELECT 'a' LIKE 'a' ESCAPE DATE'2001-01-01';
+--error ER_WRONG_ARGUMENTS
+SELECT 'a' LIKE 'a' ESCAPE TIMESTAMP'2001-01-01 00:00:00';
+--error ER_WRONG_ARGUMENTS
+SELECT 'a' LIKE 'a' ESCAPE TIME'00:00:00';

=== 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-03 07:01:55 +0000
@@ -217,9 +217,8 @@ static uint skip_digits(const char **str
                         TIME_NO_ZERO_IN_DATE	Don't allow partial dates
                         TIME_NO_ZERO_DATE	Don't allow 0000-00-00 date
                         TIME_INVALID_DATES	Allow 2000-02-31
-    was_cut             0	Value OK
-			1       If value was cut during conversion
-			2	check_date(date,flags) considers date invalid
+    status              Conversion status
+
 
   DESCRIPTION
     At least the following formats are recogniced (based on number of digits)
@@ -230,20 +229,29 @@ static uint skip_digits(const char **str
 
     The second part may have an optional .###### fraction part.
 
-  RETURN VALUES
+    status->warnings is set to:
+    0                            Value OK
+    MYSQL_TIME_WARN_TRUNCATED    If value was cut during conversion
+    MYSQL_TIME_WARN_OUT_OF_RANGE check_date(date,flags) considers date invalid
+
+    l_time->time_type is set as follows:
     MYSQL_TIMESTAMP_NONE        String wasn't a timestamp, like
                                 [DD [HH:[MM:[SS]]]].fraction.
+                                l_time is not changed.
     MYSQL_TIMESTAMP_DATE        DATE string (YY MM and DD parts ok)
     MYSQL_TIMESTAMP_DATETIME    Full timestamp
     MYSQL_TIMESTAMP_ERROR       Timestamp with wrong values.
                                 All elements in l_time is set to 0
+  RETURN VALUES
+    0 - Ok
+    1 - Error
 */
 
 #define MAX_DATE_PARTS 8
 
-enum enum_mysql_timestamp_type
+my_bool
 str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
-                ulonglong flags, int *was_cut)
+                ulonglong flags, MYSQL_TIME_STATUS *status)
 {
   const char *end=str+length, *pos;
   uint number_of_fields= 0, digits, year_length, not_zero_date;
@@ -252,19 +260,20 @@ str_to_datetime(const char *str, uint le
 
   if (flags & TIME_TIME_ONLY)
   {
-    enum enum_mysql_timestamp_type ret;
-    ret= str_to_time(str, length, l_time, flags, was_cut);
+    my_bool ret= str_to_time(str, length, l_time, flags, status);
     DBUG_RETURN(ret);
   }
-  *was_cut= 0;
+
+  my_time_status_init(status);
 
   /* Skip space at start */
   for (; str != end && my_isspace(&my_charset_latin1, *str) ; str++)
     ;
   if (str == end || ! my_isdigit(&my_charset_latin1, *str))
   {
-    *was_cut= 1;
-    DBUG_RETURN(MYSQL_TIMESTAMP_NONE);
+    status->warnings= MYSQL_TIME_WARN_TRUNCATED;
+    l_time->time_type= MYSQL_TIMESTAMP_NONE;
+    DBUG_RETURN(1);
   }
 
   /*
@@ -293,50 +302,55 @@ str_to_datetime(const char *str, uint le
       (only numbers like [YY]YYMMDD[T][hhmmss[.uuuuuu]])
     */
     year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
-    *was_cut= get_digits(&l_time->year, &number_of_fields, &str, end, year_length) 
-           || get_digits(&l_time->month, &number_of_fields, &str, end, 2)
-           || get_digits(&l_time->day, &number_of_fields, &str, end, 2)
-           || get_maybe_T(&str, end)
-           || get_digits(&l_time->hour, &number_of_fields, &str, end, 2)
-           || get_digits(&l_time->minute, &number_of_fields, &str, end, 2)
-           || get_digits(&l_time->second, &number_of_fields, &str, end, 2);
+    if (get_digits(&l_time->year, &number_of_fields, &str, end, year_length) 
+        || get_digits(&l_time->month, &number_of_fields, &str, end, 2)
+        || get_digits(&l_time->day, &number_of_fields, &str, end, 2)
+        || get_maybe_T(&str, end)
+        || get_digits(&l_time->hour, &number_of_fields, &str, end, 2)
+        || get_digits(&l_time->minute, &number_of_fields, &str, end, 2)
+        || get_digits(&l_time->second, &number_of_fields, &str, end, 2))
+     status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
   }
   else
   {
     const char *start= str;
-    *was_cut = get_number(&l_time->year, &number_of_fields, &str, end);
+    if (get_number(&l_time->year, &number_of_fields, &str, end))
+      status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
     year_length= str - start;
 
-    if (!*was_cut)
-      *was_cut= get_punct(&str, end)
-             || get_number(&l_time->month, &number_of_fields, &str, end)
-             || get_punct(&str, end)
-             || get_number(&l_time->day, &number_of_fields, &str, end)
-             || get_date_time_separator(&number_of_fields, flags, &str, end)
-             || get_number(&l_time->hour, &number_of_fields, &str, end)
-             || get_punct(&str, end)
-             || get_number(&l_time->minute, &number_of_fields, &str, end)
-             || get_punct(&str, end)
-             || get_number(&l_time->second, &number_of_fields, &str, end);
+    if (!status->warnings &&
+        (get_punct(&str, end)
+         || get_number(&l_time->month, &number_of_fields, &str, end)
+         || get_punct(&str, end)
+         || get_number(&l_time->day, &number_of_fields, &str, end)
+         || get_date_time_separator(&number_of_fields, flags, &str, end)
+         || get_number(&l_time->hour, &number_of_fields, &str, end)
+         || get_punct(&str, end)
+         || get_number(&l_time->minute, &number_of_fields, &str, end)
+         || get_punct(&str, end)
+         || get_number(&l_time->second, &number_of_fields, &str, end)))
+      status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
   }
 
-  if (number_of_fields < 3)
-    *was_cut= 1;
-
   /* we're ok if date part is correct. even if the rest is truncated */
-  if (*was_cut && number_of_fields < 3)
-    DBUG_RETURN(MYSQL_TIMESTAMP_NONE);
+  if (number_of_fields < 3)
+  {
+    l_time->time_type= MYSQL_TIMESTAMP_NONE;
+    status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
+    DBUG_RETURN(TRUE);
+  }
 
-  if (!*was_cut && str < end && *str == '.')
+  if (!status->warnings && str < end && *str == '.')
   {
-    uint second_part;
+    uint second_part= 0; // Init to 0 for this case: '2001-01-01 00:00:00.'
     const char *start= ++str;
-    *was_cut= get_digits(&second_part, &number_of_fields, &str, end, 6);
-    if (str - start < 6)
+    if (get_digits(&second_part, &number_of_fields, &str, end, 6))
+      status->warnings= MYSQL_TIME_WARN_TRUNCATED;
+    if ((status->precision= (str - start)) < 6)
       second_part*= log_10_int[6 - (str - start)];
     l_time->second_part= second_part;
     if (skip_digits(&str, end))
-      *was_cut= 1;
+      status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
   }
 
   not_zero_date = l_time->year || l_time->month || l_time->day ||
@@ -349,11 +363,11 @@ str_to_datetime(const char *str, uint le
   if (l_time->year > 9999 || l_time->month > 12 || l_time->day > 31 ||
       l_time->hour > 23 || l_time->minute > 59 || l_time->second > 59)
   {
-    *was_cut= 1;
+    status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
     goto err;
   }
 
-  if (check_date(l_time, not_zero_date, flags, was_cut))
+  if (check_date(l_time, not_zero_date, flags, &status->warnings))
     goto err;
 
   l_time->time_type= (number_of_fields <= 3 ?
@@ -363,16 +377,17 @@ str_to_datetime(const char *str, uint le
   {
     if (!my_isspace(&my_charset_latin1,*str))
     {
-      *was_cut= 1;
+      status->warnings= MYSQL_TIME_WARN_TRUNCATED;
       break;
     }
   }
 
-  DBUG_RETURN(l_time->time_type);
+  DBUG_RETURN(FALSE);
 
 err:
   bzero((char*) l_time, sizeof(*l_time));
-  DBUG_RETURN(MYSQL_TIMESTAMP_ERROR);
+  l_time->time_type= MYSQL_TIMESTAMP_ERROR;
+  DBUG_RETURN(TRUE);
 }
 
 
@@ -387,23 +402,26 @@ str_to_datetime(const char *str, uint le
                         There may be an optional [.second_part] after seconds
    length               Length of str
    l_time               Store result here
-   warning              Set MYSQL_TIME_WARN_TRUNCATED flag if the input string
-                        was cut during conversion, and/or
-                        MYSQL_TIME_WARN_OUT_OF_RANGE flag, if the value is
-                        out of range.
+   status               Conversion status
+
 
    NOTES
+
      Because of the extra days argument, this function can only
      work with times where the time arguments are in the above order.
 
+     status->warnings is set as follows:
+     MYSQL_TIME_WARN_TRUNCATED if the input string was cut during conversion,
+     and/or
+     MYSQL_TIME_WARN_OUT_OF_RANGE flag is set if the value is out of range.
+
    RETURN
-     MYSQL_TIMESTAMP_TIME
-     MYSQL_TIMESTAMP_ERROR
+     FALSE on success
+     TRUE  on error
 */
 
-enum enum_mysql_timestamp_type
-str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
-            ulonglong fuzzydate, int *warning)
+my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
+                    ulonglong fuzzydate, MYSQL_TIME_STATUS *status)
 {
   ulong date[5];
   ulonglong value;
@@ -411,7 +429,7 @@ str_to_time(const char *str, uint length
   my_bool found_days,found_hours, neg= 0;
   uint UNINIT_VAR(state);
 
-  *warning= 0;
+  my_time_status_init(status);
   for (; str != end && my_isspace(&my_charset_latin1,*str) ; str++)
     length--;
   if (str != end && *str == '-')
@@ -421,22 +439,20 @@ str_to_time(const char *str, uint length
     length--;
   }
   if (str == end)
-    return MYSQL_TIMESTAMP_ERROR;
+  {
+    status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
+    goto err;
+  }
 
   /* Check first if this is a full TIMESTAMP */
   if (length >= 12)
   {                                             /* Probably full timestamp */
-    int was_cut;
-    enum enum_mysql_timestamp_type
-      res= str_to_datetime(str, length, l_time,
+    (void) str_to_datetime(str, length, l_time,
                            (fuzzydate & ~TIME_TIME_ONLY) | TIME_DATETIME_ONLY,
-                           &was_cut);
-    if ((int) res >= (int) MYSQL_TIMESTAMP_ERROR)
-    {
-      if (was_cut)
-        *warning|= MYSQL_TIME_WARN_TRUNCATED;
-      return res;
-    }
+                            status);
+    if (l_time->time_type >= MYSQL_TIMESTAMP_ERROR)
+      return l_time->time_type == MYSQL_TIMESTAMP_ERROR;
+    my_time_status_init(status);
   }
 
   l_time->neg= neg;
@@ -513,12 +529,25 @@ str_to_time(const char *str, uint length
       if (field_length-- > 0)
         value= value*10 + (uint) (uchar) (*str - '0');
     }
-    if (field_length > 0)
-      value*= (long) log_10_int[field_length];
-    else if (field_length < 0)
-      *warning|= MYSQL_TIME_WARN_TRUNCATED;
+    if (field_length >= 0)
+    {
+      status->precision= TIME_SECOND_PART_DIGITS - field_length;
+      if (field_length > 0)
+        value*= (long) log_10_int[field_length];
+    }
+    else
+    {
+      /* Scan digits left after microseconds */
+      status->precision= 6;
+      skip_digits(&str, end);
+    }
     date[4]= (ulong) value;
   }
+  else if ((end - str) == 1 && *str == '.') /* '10:10:10.' */
+  {
+    str++;
+    date[4]= 0;
+  }
   else
     date[4]=0;
     
@@ -530,7 +559,10 @@ str_to_time(const char *str, uint length
        ((str[1] == '-' || str[1] == '+') &&
         (end - str) > 2 &&
         my_isdigit(&my_charset_latin1, str[2]))))
-    return MYSQL_TIMESTAMP_ERROR;
+  {
+    status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
+    goto err;
+  }
 
   if (internal_format_positions[7] != 255)
   {
@@ -553,8 +585,11 @@ str_to_time(const char *str, uint length
   if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
       date[2] > UINT_MAX || date[3] > UINT_MAX ||
       date[4] > UINT_MAX)
-    return MYSQL_TIMESTAMP_ERROR;
-  
+  {
+    status->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
+    goto err;
+  }  
+
   l_time->year=         0;                      /* For protocol::store_time */
   l_time->month=        0;
   l_time->day=          date[0];
@@ -565,9 +600,9 @@ str_to_time(const char *str, uint length
   l_time->time_type= MYSQL_TIMESTAMP_TIME;
 
   /* Check if the value is valid and fits into MYSQL_TIME range */
-  if (check_time_range(l_time, 6, warning))
-    return MYSQL_TIMESTAMP_ERROR;
-  
+  if (check_time_range(l_time, 6, &status->warnings))
+    return TRUE;
+
   /* Check if there is garbage at end of the MYSQL_TIME specification */
   if (str != end)
   {
@@ -575,12 +610,17 @@ str_to_time(const char *str, uint length
     {
       if (!my_isspace(&my_charset_latin1,*str))
       {
-        *warning|= MYSQL_TIME_WARN_TRUNCATED;
+        status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
         break;
       }
     } while (++str != end);
   }
-  return MYSQL_TIMESTAMP_TIME;
+  return FALSE;
+
+err:
+  bzero((char*) l_time, sizeof(*l_time));
+  l_time->time_type= MYSQL_TIMESTAMP_ERROR;
+  return TRUE;
 }
 
 
@@ -610,7 +650,10 @@ int check_time_range(struct st_mysql_tim
                                              999000, 999900, 999990, 999999};
 
   if (my_time->minute >= 60 || my_time->second >= 60)
+  {
+    *warning|= MYSQL_TIME_WARN_TRUNCATED;
     return 1;
+  }
 
   hour= my_time->hour + (24*my_time->day);
 
@@ -1357,7 +1400,7 @@ double TIME_to_double(const MYSQL_TIME *
   return my_time->neg ? -d : d;
 }
 
-longlong pack_time(MYSQL_TIME *my_time)
+longlong pack_time(const MYSQL_TIME *my_time)
 {
   return  ((((((my_time->year     * 13ULL +
                my_time->month)    * 32ULL +

=== modified file 'sql/field.cc'
--- sql/field.cc	2013-06-06 19:32:29 +0000
+++ sql/field.cc	2013-06-26 08:10:53 +0000
@@ -1822,7 +1822,7 @@ bool Field::get_date(MYSQL_TIME *ltime,u
   String tmp(buff,sizeof(buff),&my_charset_bin),*res;
   if (!(res=val_str(&tmp)) ||
       str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
-                                ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR)
+                                ltime, fuzzydate))
     return 1;
   return 0;
 }
@@ -4570,18 +4570,18 @@ int Field_timestamp::store_time_dec(MYSQ
 int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs)
 {
   MYSQL_TIME l_time;
-  int error;
-  int have_smth_to_conv;
+  MYSQL_TIME_STATUS status;
+  bool have_smth_to_conv;
   ErrConvString str(from, len, cs);
   THD *thd= get_thd();
 
   /* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */
-  have_smth_to_conv= (str_to_datetime(cs, from, len, &l_time,
+  have_smth_to_conv= !str_to_datetime(cs, from, len, &l_time,
                                       (thd->variables.sql_mode &
                                        MODE_NO_ZERO_DATE) |
-                                       MODE_NO_ZERO_IN_DATE, &error) >
-                      MYSQL_TIMESTAMP_ERROR);
-  return store_TIME_with_warning(thd, &l_time, &str, error, have_smth_to_conv);
+                                       MODE_NO_ZERO_IN_DATE, &status);
+  return store_TIME_with_warning(thd, &l_time, &str,
+                                 status.warnings, have_smth_to_conv);
 }
 
 
@@ -5060,18 +5060,16 @@ int Field_temporal::store_TIME_with_warn
 int Field_temporal::store(const char *from,uint len,CHARSET_INFO *cs)
 {
   MYSQL_TIME ltime;
-  int error;
-  enum enum_mysql_timestamp_type func_res;
+  MYSQL_TIME_STATUS status;
   THD *thd= get_thd();
   ErrConvString str(from, len, cs);
-
-  func_res= str_to_datetime(cs, from, len, &ltime,
-                            (TIME_FUZZY_DATE |
-                             (thd->variables.sql_mode &
-                              (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
-                               MODE_INVALID_DATES))),
-                            &error);
-  return store_TIME_with_warning(&ltime, &str, error, func_res > MYSQL_TIMESTAMP_ERROR);
+  bool func_res= !str_to_datetime(cs, from, len, &ltime,
+                                  (TIME_FUZZY_DATE |
+                                   (thd->variables.sql_mode &
+                                   (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
+                                   MODE_INVALID_DATES))),
+                                  &status);
+  return store_TIME_with_warning(&ltime, &str, status.warnings, func_res);
 }
 
 
@@ -5157,16 +5155,17 @@ void Field_time::store_TIME(MYSQL_TIME *
 int Field_time::store(const char *from,uint len,CHARSET_INFO *cs)
 {
   MYSQL_TIME ltime;
+  MYSQL_TIME_STATUS status;
   ErrConvString str(from, len, cs);
-  int was_cut;
-  int have_smth_to_conv=
-    str_to_time(cs, from, len, &ltime,
+  bool have_smth_to_conv= 
+   !str_to_time(cs, from, len, &ltime,
                 get_thd()->variables.sql_mode &
                 (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE |
                  MODE_INVALID_DATES),
-                &was_cut) > MYSQL_TIMESTAMP_ERROR;
+                &status);
 
-  return store_TIME_with_warning(&ltime, &str, was_cut, have_smth_to_conv);
+  return store_TIME_with_warning(&ltime, &str,
+                                 status.warnings, have_smth_to_conv);
 }
 
 

=== modified file 'sql/item.cc'
--- sql/item.cc	2013-06-06 19:32:29 +0000
+++ sql/item.cc	2013-06-24 07:00:46 +0000
@@ -1269,7 +1269,7 @@ bool Item::get_date(MYSQL_TIME *ltime,ul
     String tmp(buff,sizeof(buff), &my_charset_bin),*res;
     if (!(res=val_str(&tmp)) ||
         str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
-                                  ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR)
+                                  ltime, fuzzydate))
       goto err;
     break;
   }

=== modified file 'sql/item_cmpfunc.cc'
--- sql/item_cmpfunc.cc	2013-06-06 15:51:28 +0000
+++ sql/item_cmpfunc.cc	2013-07-03 06:55:29 +0000
@@ -721,31 +721,31 @@ bool get_mysql_time_from_str(THD *thd, S
                              const char *warn_name, MYSQL_TIME *l_time)
 {
   bool value;
-  int error;
-  enum_mysql_timestamp_type timestamp_type;
+  MYSQL_TIME_STATUS status;
   int flags= TIME_FUZZY_DATE | MODE_INVALID_DATES;
   ErrConvString err(str);
 
-  if (warn_type == MYSQL_TIMESTAMP_TIME)
-    flags|= TIME_TIME_ONLY;
+  DBUG_ASSERT(warn_type != MYSQL_TIMESTAMP_TIME);
 
-  timestamp_type= 
-    str_to_datetime(str->charset(), str->ptr(), str->length(),
-                    l_time, flags, &error);
-
-  if (timestamp_type > MYSQL_TIMESTAMP_ERROR)
+  if (!str_to_datetime(str->charset(), str->ptr(), str->length(),
+                       l_time, flags, &status))
+  {
+     DBUG_ASSERT(l_time->time_type == MYSQL_TIMESTAMP_DATETIME || 
+                 l_time->time_type == MYSQL_TIMESTAMP_DATE);
     /*
       Do not return yet, we may still want to throw a "trailing garbage"
       warning.
     */
     value= FALSE;
+  }
   else
   {
+    DBUG_ASSERT(l_time->time_type != MYSQL_TIMESTAMP_TIME);
+    DBUG_ASSERT(status.warnings != 0); // Must be set by set_to_datetime()
     value= TRUE;
-    error= 1;                                   /* force warning */
   }
 
-  if (error > 0)
+  if (status.warnings > 0)
     make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                                  &err, warn_type, warn_name);
 

=== modified file 'sql/item_create.cc'
--- sql/item_create.cc	2013-04-20 20:30:21 +0000
+++ sql/item_create.cc	2013-07-02 06:44:02 +0000
@@ -32,6 +32,7 @@
 #include "set_var.h"
 #include "sp_head.h"
 #include "sp.h"
+#include "sql_time.h"
 
 /*
 =============================================================================
@@ -5821,6 +5822,70 @@ create_func_cast(THD *thd, Item *a, Cast
 }
 
 
+/**
+  Builder for datetime literals:
+    TIME'00:00:00', DATE'2001-01-01', TIMESTAMP'2001-01-01 00:00:00'.
+  @param thd          The current thread
+  @param str          Character literal
+  @param length       Length of str
+  @param type         Type of literal (TIME, DATE or DATETIME)
+  @param send_error   Whether to generate an error on failure
+*/
+
+Item *create_temporal_literal(THD *thd,
+                              const char *str, uint length,
+                              CHARSET_INFO *cs,
+                              enum_field_types type,
+                              bool send_error)
+{
+  MYSQL_TIME_STATUS status;
+  MYSQL_TIME ltime;
+  Item *item= NULL;
+  ulonglong datetime_flags= thd->variables.sql_mode &
+                            (MODE_NO_ZERO_IN_DATE |
+                             MODE_NO_ZERO_DATE |
+                             MODE_INVALID_DATES);
+  ulonglong flags= TIME_FUZZY_DATE | datetime_flags;
+
+  switch(type)
+  {
+  case MYSQL_TYPE_DATE:
+  case MYSQL_TYPE_NEWDATE:
+    if (!str_to_datetime(cs, str, length, &ltime, flags, &status) &&
+        ltime.time_type == MYSQL_TIMESTAMP_DATE && !status.warnings)
+      item= new (thd->mem_root) Item_date_literal(&ltime);
+    break;
+  case MYSQL_TYPE_DATETIME:
+    if (!str_to_datetime(cs, str, length, &ltime, flags, &status) &&
+        ltime.time_type == MYSQL_TIMESTAMP_DATETIME && !status.warnings)
+      item= new (thd->mem_root) Item_datetime_literal(&ltime,
+                                                      status.precision);
+    break;
+  case MYSQL_TYPE_TIME:
+    if (!str_to_time(cs, str, length, &ltime, 0, &status) &&
+        ltime.time_type == MYSQL_TIMESTAMP_TIME && !status.warnings)
+      item= new (thd->mem_root) Item_time_literal(&ltime,
+                                                  status.precision);
+    break;
+  default:
+    DBUG_ASSERT(0);
+  }
+
+  if (item)
+    return item;
+
+  if (send_error)
+  {
+    const char *typestr=
+      (type == MYSQL_TYPE_DATE) ? "DATE" :
+      (type == MYSQL_TYPE_TIME) ? "TIME" : "DATETIME";
+    ErrConvString err(str, length, thd->variables.character_set_client);
+    my_error(ER_WRONG_VALUE, MYF(0), typestr, err.ptr());
+  }
+  return NULL;
+}
+
+
 static List<Item> *create_func_dyncol_prepare(THD *thd,
                                               DYNCALL_CREATE_DEF **dfs,
                                               List<DYNCALL_CREATE_DEF> &list)

=== modified file 'sql/item_create.h'
--- sql/item_create.h	2012-09-28 11:01:17 +0000
+++ sql/item_create.h	2013-06-25 07:25:26 +0000
@@ -168,6 +168,11 @@ create_func_cast(THD *thd, Item *a, Cast
                  const char *len, const char *dec,
                  CHARSET_INFO *cs);
 
+Item *create_temporal_literal(THD *thd,
+                              const char *str, uint length,
+                              CHARSET_INFO *cs,
+                              enum_field_types type,
+                              bool send_error);
 
 int item_create_init();
 void item_create_cleanup();

=== modified file 'sql/item_func.cc'
--- sql/item_func.cc	2013-06-07 21:16:00 +0000
+++ sql/item_func.cc	2013-06-25 08:15:19 +0000
@@ -174,7 +174,7 @@ Item_func::Item_func(THD *thd, Item_func
 bool
 Item_func::fix_fields(THD *thd, Item **ref)
 {
-  DBUG_ASSERT(fixed == 0);
+  DBUG_ASSERT(fixed == 0 || basic_const_item());
   Item **arg,**arg_end;
   uchar buff[STACK_BUFF_ALLOC];			// Max argument in function
 

=== modified file 'sql/item_strfunc.cc'
--- sql/item_strfunc.cc	2013-06-06 19:32:29 +0000
+++ sql/item_strfunc.cc	2013-06-24 07:01:05 +0000
@@ -4722,7 +4722,7 @@ bool Item_dyncol_get::get_date(MYSQL_TIM
     if (str_to_datetime_with_warn(&my_charset_numeric,
                                   val.x.string.value.str,
                                   val.x.string.value.length,
-                                  ltime, fuzzy_date) <= MYSQL_TIMESTAMP_ERROR)
+                                  ltime, fuzzy_date))
       goto null;
     return 0;
   case DYN_COL_DATETIME:

=== modified file 'sql/item_timefunc.cc'
--- sql/item_timefunc.cc	2013-04-07 12:00:16 +0000
+++ sql/item_timefunc.cc	2013-06-25 07:35:33 +0000
@@ -733,6 +733,95 @@ static bool get_interval_info(const char
 }
 
 
+bool Item_date_literal::eq(const Item *item, bool binary_cmp) const
+{
+  return
+    item->basic_const_item() && type() == item->type() &&
+    func_name() == ((Item_func *) item)->func_name() &&
+    !my_time_compare(&cached_time,
+                     &((Item_date_literal *) item)->cached_time);
+}
+
+
+void Item_date_literal::print(String *str, enum_query_type query_type)
+{
+  str->append("DATE'");
+  char buf[MAX_DATE_STRING_REP_LENGTH];
+  my_date_to_str(&cached_time, buf);
+  str->append(buf);
+  str->append('\'');
+}
+
+
+bool Item_date_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+{
+  DBUG_ASSERT(fixed);
+  *ltime= cached_time;
+  return (null_value= check_date_with_warn(ltime, fuzzy_date,
+                                           MYSQL_TIMESTAMP_ERROR));
+}
+
+
+bool Item_datetime_literal::eq(const Item *item, bool binary_cmp) const
+{
+  return
+    item->basic_const_item() && type() == item->type() &&
+    func_name() == ((Item_func *) item)->func_name() &&
+    !my_time_compare(&cached_time,
+                     &((Item_datetime_literal *) item)->cached_time);
+}
+
+
+void Item_datetime_literal::print(String *str, enum_query_type query_type)
+{
+  str->append("TIMESTAMP'");
+  char buf[MAX_DATE_STRING_REP_LENGTH];
+  my_datetime_to_str(&cached_time, buf, decimals);
+  str->append(buf);
+  str->append('\'');
+}
+
+
+bool Item_datetime_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+{
+  DBUG_ASSERT(fixed);
+  *ltime= cached_time;
+  return (null_value= check_date_with_warn(ltime, fuzzy_date,
+                                           MYSQL_TIMESTAMP_ERROR));
+}
+
+
+bool Item_time_literal::eq(const Item *item, bool binary_cmp) const
+{
+  return
+    item->basic_const_item() && type() == item->type() &&
+    func_name() == ((Item_func *) item)->func_name() &&
+    !my_time_compare(&cached_time,
+                     &((Item_time_literal *) item)->cached_time);
+}
+
+
+void Item_time_literal::print(String *str, enum_query_type query_type)
+{
+  str->append("TIME'");
+  char buf[MAX_DATE_STRING_REP_LENGTH];
+  my_time_to_str(&cached_time, buf, decimals);
+  str->append(buf);
+  str->append('\'');
+}
+
+
+bool Item_time_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+{
+  DBUG_ASSERT(fixed);
+  *ltime= cached_time;
+  if (fuzzy_date & TIME_TIME_ONLY)
+    return (null_value= false);
+  return (null_value= check_date_with_warn(ltime, fuzzy_date,
+                                           MYSQL_TIMESTAMP_ERROR));
+}
+
+
 longlong Item_func_period_add::val_int()
 {
   DBUG_ASSERT(fixed == 1);
@@ -2430,17 +2519,8 @@ bool Item_date_typecast::get_date(MYSQL_
 
   ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0;
   ltime->time_type= MYSQL_TIMESTAMP_DATE;
-
-  int unused;
-  if (check_date(ltime, ltime->year || ltime->month || ltime->day,
-                 fuzzy_date, &unused))
-  {
-    ErrConvTime str(ltime);
-    make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
-                                 &str, MYSQL_TIMESTAMP_DATE, 0);
-    return (null_value= 1);
-  }
-  return (null_value= 0);
+  return (null_value= check_date_with_warn(ltime, fuzzy_date,
+                                           MYSQL_TIMESTAMP_DATE));
 }
 
 

=== modified file 'sql/item_timefunc.h'
--- sql/item_timefunc.h	2013-03-13 21:33:52 +0000
+++ sql/item_timefunc.h	2013-06-24 07:26:29 +0000
@@ -526,6 +526,136 @@ class Item_timefunc :public Item_tempora
 };
 
 
+/**
+  DATE'2010-01-01'
+*/
+class Item_date_literal :public Item_datefunc
+{
+  MYSQL_TIME cached_time;
+public:
+  /**
+    Constructor for Item_date_literal.
+    @param ltime  DATE value.
+  */
+  Item_date_literal(MYSQL_TIME *ltime) :Item_datefunc()
+  {
+    cached_time= *ltime;
+    fix_length_and_dec();
+    fixed= 1;
+  }
+  const char *func_name() const { return "date_literal"; }
+  void print(String *str, enum_query_type query_type);
+  bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
+  void fix_length_and_dec()
+  {
+    Item_datefunc::fix_length_and_dec();
+    set_persist_maybe_null(false);
+  }
+  bool check_partition_func_processor(uchar *int_arg)
+  {
+    return FALSE;
+  }
+  bool basic_const_item() const { return true; }
+  bool const_item() const { return true; }
+  table_map used_tables() const { return (table_map) 0L; }
+  void cleanup()
+  {
+    // See Item_basic_const::cleanup()
+    if (orig_name)
+      name= orig_name;
+  }
+  bool eq(const Item *item, bool binary_cmp) const;
+};
+
+
+/**
+  TIME'10:10:10'
+*/
+class Item_time_literal :public Item_timefunc
+{
+  MYSQL_TIME cached_time;
+public:
+  /**
+    Constructor for Item_time_literal.
+    @param ltime    TIME value.
+    @param dec_arg  number of fractional digits in ltime.
+  */
+  Item_time_literal(MYSQL_TIME *ltime, uint dec_arg) :Item_timefunc()
+  {
+    decimals= MY_MIN(dec_arg, TIME_SECOND_PART_DIGITS);
+    cached_time= *ltime;
+    fix_length_and_dec();
+    fixed= 1;
+  }
+  const char *func_name() const { return "time_literal"; }
+  void print(String *str, enum_query_type query_type);
+  bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
+  void fix_length_and_dec()
+  {
+    Item_timefunc::fix_length_and_dec();
+    set_persist_maybe_null(false);
+  }
+  bool check_partition_func_processor(uchar *int_arg)
+  {
+    return FALSE;
+  }
+  bool basic_const_item() const { return true; }
+  bool const_item() const { return true; }
+  table_map used_tables() const { return (table_map) 0L; }
+  void cleanup()
+  {
+    // See Item_basic_const::cleanup()
+    if (orig_name)
+      name= orig_name;
+  }
+  bool eq(const Item *item, bool binary_cmp) const;
+};
+
+
+/**
+  TIMESTAMP'2001-01-01 10:20:30'
+*/
+class Item_datetime_literal :public Item_temporal_func
+{
+  MYSQL_TIME cached_time;
+public:
+  /**
+    Constructor for Item_datetime_literal.
+    @param ltime    DATETIME value.
+    @param dec_arg  number of fractional digits in ltime.
+  */
+  Item_datetime_literal(MYSQL_TIME *ltime, uint dec_arg) :Item_temporal_func()
+  {
+    decimals= MY_MIN(dec_arg, TIME_SECOND_PART_DIGITS);
+    cached_time= *ltime;
+    fix_length_and_dec();
+    fixed= 1;
+  }
+  const char *func_name() const { return "datetime_literal"; }
+  void print(String *str, enum_query_type query_type);
+  bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
+  void fix_length_and_dec()
+  {
+    Item_temporal_func::fix_length_and_dec();
+    set_persist_maybe_null(false);
+  }
+  bool check_partition_func_processor(uchar *int_arg)
+  {
+    return FALSE;
+  }
+  bool basic_const_item() const { return true; }
+  bool const_item() const { return true; }
+  table_map used_tables() const { return (table_map) 0L; }
+  void cleanup()
+  {
+    // See Item_basic_const::cleanup()
+    if (orig_name)
+      name= orig_name;
+  }
+  bool eq(const Item *item, bool binary_cmp) const;
+};
+
+
 /* Abstract CURTIME function. Children should define what time zone is used */
 
 class Item_func_curtime :public Item_timefunc

=== modified file 'sql/sql_time.cc'
--- sql/sql_time.cc	2013-04-07 12:00:16 +0000
+++ sql/sql_time.cc	2013-06-25 07:11:25 +0000
@@ -214,6 +214,22 @@ ulong convert_month_to_period(ulong mont
 }
 
 
+bool
+check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date,
+                     timestamp_type ts_type)
+{
+  int unused;
+  if (check_date(ltime, fuzzy_date, &unused))
+  {
+    ErrConvTime str(ltime);
+    make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+                                 &str, ts_type, 0);
+    return true;
+  }
+  return false;
+}
+
+
 /*
   Convert a string to 8-bit representation,
   for use in str_to_time/str_to_date/str_to_date.
@@ -249,9 +265,9 @@ to_ascii(CHARSET_INFO *cs,
 
 
 /* Character set-aware version of str_to_time() */
-timestamp_type
+bool
 str_to_time(CHARSET_INFO *cs, const char *str,uint length,
-                 MYSQL_TIME *l_time, ulonglong fuzzydate, int *warning)
+                 MYSQL_TIME *l_time, ulonglong fuzzydate, MYSQL_TIME_STATUS *status)
 {
   char cnv[32];
   if ((cs->state & MY_CS_NONASCII) != 0)
@@ -259,14 +275,14 @@ str_to_time(CHARSET_INFO *cs, const char
     length= to_ascii(cs, str, length, cnv, sizeof(cnv));
     str= cnv;
   }
-  return str_to_time(str, length, l_time, fuzzydate, warning);
+  return str_to_time(str, length, l_time, fuzzydate, status);
 }
 
 
 /* Character set-aware version of str_to_datetime() */
-timestamp_type str_to_datetime(CHARSET_INFO *cs,
-                               const char *str, uint length,
-                               MYSQL_TIME *l_time, ulonglong flags, int *was_cut)
+bool str_to_datetime(CHARSET_INFO *cs, const char *str, uint length,
+                     MYSQL_TIME *l_time, ulonglong flags,
+                     MYSQL_TIME_STATUS *status)
 {
   char cnv[32];
   if ((cs->state & MY_CS_NONASCII) != 0)
@@ -274,7 +290,7 @@ timestamp_type str_to_datetime(CHARSET_I
     length= to_ascii(cs, str, length, cnv, sizeof(cnv));
     str= cnv;
   }
-  return str_to_datetime(str, length, l_time, flags, was_cut);
+  return str_to_datetime(str, length, l_time, flags, status);
 }
 
 
@@ -286,26 +302,24 @@ timestamp_type str_to_datetime(CHARSET_I
     See description of str_to_datetime() for more information.
 */
 
-timestamp_type
+bool
 str_to_datetime_with_warn(CHARSET_INFO *cs,
                           const char *str, uint length, MYSQL_TIME *l_time,
                           ulonglong flags)
 {
-  int was_cut;
+  MYSQL_TIME_STATUS status;
   THD *thd= current_thd;
-  timestamp_type ts_type;
-  
-  ts_type= str_to_datetime(cs, str, length, l_time,
+  bool ret_val= str_to_datetime(cs, str, length, l_time,
                            (flags | (sql_mode_for_dates(thd))),
-                           &was_cut);
-  if (was_cut || ts_type <= MYSQL_TIMESTAMP_ERROR)
+                           &status);
+  if (ret_val || status.warnings)
     make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                                  str, length, flags & TIME_TIME_ONLY ?
-                                 MYSQL_TIMESTAMP_TIME : ts_type, NullS);
+                                 MYSQL_TIMESTAMP_TIME : l_time->time_type, NullS);
   DBUG_EXECUTE_IF("str_to_datetime_warn",
                   push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                                ER_YES, str););
-  return ts_type;
+  return ret_val;
 }
 
 
@@ -1055,7 +1069,7 @@ calc_time_diff(MYSQL_TIME *l_time1, MYSQ
 
 */
 
-int my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b)
+int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b)
 {
   ulonglong a_t= pack_time(a);
   ulonglong b_t= pack_time(b);

=== modified file 'sql/sql_time.h'
--- sql/sql_time.h	2012-08-31 12:15:52 +0000
+++ sql/sql_time.h	2013-06-26 08:53:59 +0000
@@ -35,11 +35,9 @@ ulong convert_period_to_month(ulong peri
 ulong convert_month_to_period(ulong month);
 bool get_date_from_daynr(long daynr,uint *year, uint *month, uint *day);
 my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code);
-bool str_to_time_with_warn(CHARSET_INFO *cs, const char *str, uint length,
-                           MYSQL_TIME *l_time, ulonglong fuzzydate);
-timestamp_type str_to_datetime_with_warn(CHARSET_INFO *cs, const char *str,
-                                         uint length, MYSQL_TIME *l_time,
-                                         ulonglong flags);
+bool str_to_datetime_with_warn(CHARSET_INFO *cs, const char *str,
+                               uint length, MYSQL_TIME *l_time,
+                               ulonglong flags);
 bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime,
                                   ulonglong fuzzydate,
                                   const char *name);
@@ -76,7 +74,7 @@ bool date_add_interval(MYSQL_TIME *ltime
                        INTERVAL interval);
 bool calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign,
                     longlong *seconds_out, long *microseconds_out);
-int my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b);
+int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b);
 void localtime_to_TIME(MYSQL_TIME *to, struct tm *from);
 void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds);
 uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year);
@@ -86,12 +84,14 @@ bool parse_date_time_format(timestamp_ty
                             const char *format, uint format_length,
                             DATE_TIME_FORMAT *date_time_format);
 /* Character set-aware version of str_to_time() */
-timestamp_type str_to_time(CHARSET_INFO *cs, const char *str,uint length,
-                 MYSQL_TIME *l_time, ulonglong fuzzydate, int *warning);
+bool str_to_time(CHARSET_INFO *cs, const char *str,uint length,
+                 MYSQL_TIME *l_time, ulonglong fuzzydate,
+                 MYSQL_TIME_STATUS *status);
 /* Character set-aware version of str_to_datetime() */
-timestamp_type str_to_datetime(CHARSET_INFO *cs,
-                               const char *str, uint length,
-                               MYSQL_TIME *l_time, ulonglong flags, int *was_cut);
+bool str_to_datetime(CHARSET_INFO *cs,
+                     const char *str, uint length,
+                     MYSQL_TIME *l_time, ulonglong flags,
+                     MYSQL_TIME_STATUS *status);
 
 /* convenience wrapper */
 inline bool parse_date_time_format(timestamp_type format_type, 
@@ -110,4 +110,18 @@ extern DATE_TIME_FORMAT global_time_form
 extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[];
 extern LEX_STRING interval_type_to_name[];
 
+
+static inline bool
+non_zero_date(const MYSQL_TIME *ltime)
+{
+  return ltime->year || ltime->month || ltime->day;
+}
+static inline bool
+check_date(const MYSQL_TIME *ltime, ulonglong flags, int *was_cut)
+{
+ return check_date(ltime, non_zero_date(ltime), flags, was_cut);
+}
+bool check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date,
+                          timestamp_type ts_type);
+
 #endif /* SQL_TIME_INCLUDED */

=== modified file 'sql/sql_yacc.yy'
--- sql/sql_yacc.yy	2013-06-06 19:32:29 +0000
+++ sql/sql_yacc.yy	2013-06-20 11:21:37 +0000
@@ -32,6 +32,7 @@
 #define YYTHD ((THD *)yythd)
 #define YYLIP (& YYTHD->m_parser_state->m_lip)
 #define YYPS (& YYTHD->m_parser_state->m_yacc)
+#define YYCSCL  YYTHD->variables.character_set_client
 
 #define MYSQL_YACC
 #define YYINITDEPTH 100
@@ -899,10 +900,10 @@ bool my_yyoverflow(short **a, YYSTYPE **
 
 %pure_parser                                    /* We have threads */
 /*
-  Currently there are 170 shift/reduce conflicts.
+  Currently there are 167 shift/reduce conflicts.
   We should not introduce new conflicts any more.
 */
-%expect 170
+%expect 167
 
 /*
    Comments for TOKENS.
@@ -1628,7 +1629,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
         replace_lock_option opt_low_priority insert_lock_option load_data_lock
 
 %type <item>
-        literal text_literal insert_ident order_ident
+        literal text_literal insert_ident order_ident temporal_literal
         simple_ident expr opt_expr opt_else sum_expr in_sum_expr
         variable variable_aux bool_pri
         predicate bit_expr
@@ -8741,7 +8742,49 @@ simple_expr:
               MYSQL_YYABORT;
           }
         | '{' ident expr '}'
-          { $$= $3; }
+          {
+            Item_string *item;
+            $$= NULL;
+            /*
+              If "expr" is reasonably short pure ASCII string literal,
+              try to parse known ODBC style date, time or timestamp literals,
+              e.g:
+              SELECT {d'2001-01-01'};
+              SELECT {t'10:20:30'};
+              SELECT {ts'2001-01-01 10:20:30'};
+            */
+            if ($3->type() == Item::STRING_ITEM &&
+               (item= (Item_string *) $3) &&
+                item->collation.repertoire == MY_REPERTOIRE_ASCII &&
+                item->str_value.length() < MAX_DATE_STRING_REP_LENGTH * 4)
+            {
+              enum_field_types type= MYSQL_TYPE_STRING;
+              ErrConvString str(&item->str_value);
+              LEX_STRING *ls= &$2;
+              if (ls->length == 1)
+              {
+                if (ls->str[0] == 'd')  /* {d'2001-01-01'} */
+                  type= MYSQL_TYPE_DATE;
+                else if (ls->str[0] == 't') /* {t'10:20:30'} */
+                  type= MYSQL_TYPE_TIME;
+              }
+              else if (ls->length == 2) /* {ts'2001-01-01 10:20:30'} */
+              {
+                if (ls->str[0] == 't' && ls->str[1] == 's')
+                  type= MYSQL_TYPE_DATETIME;
+              }
+              if (type != MYSQL_TYPE_STRING)
+              {
+                const char *ascii= str.ptr();
+                $$= create_temporal_literal(YYTHD,
+                                            ascii, strlen(ascii),
+                                            system_charset_info,
+                                            type, false);
+              }
+            }
+            if ($$ == NULL)
+              $$= $3;
+          }
         | MATCH ident_list_arg AGAINST '(' bit_expr fulltext_options ')'
           {
             $2->push_front($5);
@@ -12730,6 +12773,7 @@ signed_literal:
 literal:
           text_literal { $$ = $1; }
         | NUM_literal { $$ = $1; }
+        | temporal_literal { $$= $1; }
         | NULL_SYM
           {
             $$ = new (YYTHD->mem_root) Item_null();
@@ -12824,9 +12868,6 @@ literal:
 
             $$= item_str;
           }
-        | DATE_SYM text_literal { $$ = $2; }
-        | TIME_SYM text_literal { $$ = $2; }
-        | TIMESTAMP text_literal { $$ = $2; }
         ;
 
 NUM_literal:
@@ -12875,6 +12916,31 @@ NUM_literal:
           }
         ;
 
+
+temporal_literal:
+        DATE_SYM TEXT_STRING
+          {
+            if (!($$= create_temporal_literal(YYTHD, $2.str, $2.length, YYCSCL,
+                                              MYSQL_TYPE_DATE, true)))
+              MYSQL_YYABORT;
+          }
+        | TIME_SYM TEXT_STRING
+          {
+            if (!($$= create_temporal_literal(YYTHD, $2.str, $2.length, YYCSCL,
+                                              MYSQL_TYPE_TIME, true)))
+              MYSQL_YYABORT;
+          }
+        | TIMESTAMP TEXT_STRING
+          {
+            if (!($$= create_temporal_literal(YYTHD, $2.str, $2.length, YYCSCL,
+                                              MYSQL_TYPE_DATETIME, true)))
+              MYSQL_YYABORT;
+          }
+        ;
+
+
+
+
 /**********************************************************************
 ** Creating different items.
 **********************************************************************/


Follow ups

References