diff options
-rw-r--r-- | mysql-test/r/date_formats.result | 157 | ||||
-rw-r--r-- | mysql-test/t/date_formats-master.opt | 1 | ||||
-rw-r--r-- | mysql-test/t/date_formats.test | 82 | ||||
-rw-r--r-- | sql/field.cc | 73 | ||||
-rw-r--r-- | sql/item.cc | 34 | ||||
-rw-r--r-- | sql/item.h | 10 | ||||
-rw-r--r-- | sql/item_create.cc | 5 | ||||
-rw-r--r-- | sql/item_create.h | 1 | ||||
-rw-r--r-- | sql/item_timefunc.cc | 1121 | ||||
-rw-r--r-- | sql/item_timefunc.h | 43 | ||||
-rw-r--r-- | sql/lex.h | 2 | ||||
-rw-r--r-- | sql/mysql_priv.h | 28 | ||||
-rw-r--r-- | sql/mysqld.cc | 67 | ||||
-rw-r--r-- | sql/protocol.cc | 45 | ||||
-rw-r--r-- | sql/set_var.cc | 72 | ||||
-rw-r--r-- | sql/set_var.h | 50 | ||||
-rw-r--r-- | sql/sql_class.cc | 3 | ||||
-rw-r--r-- | sql/sql_class.h | 1 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 11 | ||||
-rw-r--r-- | sql/structs.h | 9 | ||||
-rw-r--r-- | sql/time.cc | 328 |
21 files changed, 1650 insertions, 493 deletions
diff --git a/mysql-test/r/date_formats.result b/mysql-test/r/date_formats.result new file mode 100644 index 00000000000..590a1d6904b --- /dev/null +++ b/mysql-test/r/date_formats.result @@ -0,0 +1,157 @@ +SHOW GLOBAL VARIABLES LIKE "%_format%"; +Variable_name Value +date_format %d.%m.%Y +datetime_format %Y/%d/%m-%H:%i:%s +default_week_format 0 +time_format %H.%i.%s +SHOW SESSION VARIABLES LIKE "%_format%"; +Variable_name Value +date_format %d.%m.%Y +datetime_format %Y/%d/%m-%H:%i:%s +default_week_format 0 +time_format %H.%i.%s +SET date_format="%d.%m.%Y"; +select CAST("01.01.2001" as DATE) as a; +a +01.01.2001 +SET datetime_format="%d.%m.%Y %H.%i.%s"; +select CAST("01.01.2001 05.12.06" as DATETIME) as a; +a +01.01.2001 05.12.06 +SET time_format="%H.%i.%s"; +select CAST("05.12.06" as TIME) as a; +a +05.12.06 +SET datetime_format="%d.%m.%Y %h:%i:%s %p"; +select CAST("01.01.2001 05:12:06AM" as DATETIME) as a; +a +01.01.2001 05:12:06 AM +select CAST("01.01.2001 05:12:06 PM" as DATETIME) as a; +a +01.01.2001 05:12:06 PM +SET time_format="%h:%i:%s %p"; +select CAST("05:12:06 AM" as TIME) as a; +a +05:12:06 AM +select CAST("05:12:06.1234PM" as TIME) as a; +a +05:12:06.001234 PM +SET time_format="%h.%i.%s %p"; +SET date_format='%d.%m.%y'; +SET datetime_format="%d.%m.%y %h.%i.%s %p"; +select CAST("12-12-06" as DATE) as a; +a +12.12.06 +select adddate("01.01.97 11.59.59.000001 PM", 10); +adddate("01.01.97 11.59.59.000001 PM", 10) +11.01.97 11.59.59.000001 PM +select datediff("31.12.97 11.59:59.000001 PM","01.01.98"); +datediff("31.12.97 11.59:59.000001 PM","01.01.98") +-1 +select weekofyear("31.11.97 11:59:59.000001 PM"); +weekofyear("31.11.97 11:59:59.000001 PM") +49 +select makedate(1997,1); +makedate(1997,1) +01.01.97 +select addtime("31.12.97 11.59.59.999999 PM", "1 1.1.1.000002"); +addtime("31.12.97 11.59.59.999999 PM", "1 1.1.1.000002") +02.01.98 01.01.01.000001 AM +select maketime(23,11,12); +maketime(23,11,12) +11.11.12 PM +select timediff("01.01.97 11:59:59.000001 PM","31.12.95 11:59:59.000002 PM"); +timediff("01.01.97 11:59:59.000001 PM","31.12.95 11:59:59.000002 PM") +8795.59.59.999999 PM +SET time_format="%H%i%s"; +SET time_format="%h%i%s"; +ERROR HY000: Unknown error +SET date_format='%d.%m.%d'; +ERROR HY000: Unknown error +SET datetime_format="%d.%m.%y %h.%i.%s"; +ERROR HY000: Unknown error +SET GLOBAL date_format=default; +SHOW GLOBAL VARIABLES LIKE "date_format%"; +Variable_name Value +date_format %d.%m.%Y +SET GLOBAL time_format=default; +SHOW GLOBAL VARIABLES LIKE "time_format%"; +Variable_name Value +time_format %H.%i.%s +SET GLOBAL datetime_format=default; +SHOW GLOBAL VARIABLES LIKE "datetime_format%"; +Variable_name Value +datetime_format %Y/%d/%m-%H:%i:%s +SET date_format=default; +SHOW SESSION VARIABLES LIKE "date_format%"; +Variable_name Value +date_format %d.%m.%Y +SET time_format=default; +SHOW SESSION VARIABLES LIKE "time_format%"; +Variable_name Value +time_format %H.%i.%s +SET datetime_format=default; +SHOW SESSION VARIABLES LIKE "datetime_format%"; +Variable_name Value +datetime_format %Y/%d/%m-%H:%i:%s +SET time_format='%i:%s:%H'; +select cast(str_to_date("15-01-2001 12:59:59", "%d-%m-%Y %H:%i:%S") as TIME); +cast(str_to_date("15-01-2001 12:59:59", "%d-%m-%Y %H:%i:%S") as TIME) +59:59:12 +SET GLOBAL date_format='%Y-%m-%d'; +SET GLOBAL time_format='%H:%i:%s'; +SET GLOBAL datetime_format='%Y-%m-%d %H:%i:%s'; +SET date_format='%Y-%m-%d'; +SET time_format='%H:%i:%s'; +SET datetime_format='%Y-%m-%d %H:%i:%s'; +select str_to_date("15-01-2001 12:59:59", "%d-%m-%Y %H:%i:%S"); +str_to_date("15-01-2001 12:59:59", "%d-%m-%Y %H:%i:%S") +2001-01-15 12:59:59 +select str_to_date("15 September 2001", "%d %M %Y"); +str_to_date("15 September 2001", "%d %M %Y") +2001-09-15 00:00:00 +select str_to_date("15 Septembeb 2001", "%d %M %Y"); +str_to_date("15 Septembeb 2001", "%d %M %Y") +NULL +select str_to_date("15 MAY 2001", "%d %b %Y"); +str_to_date("15 MAY 2001", "%d %b %Y") +2001-05-15 00:00:00 +select str_to_date("Sunday 15 MAY 2001", "%W %d %b %Y"); +str_to_date("Sunday 15 MAY 2001", "%W %d %b %Y") +2001-05-15 00:00:00 +select str_to_date("Sundai 15 MAY 2001", "%W %d %b %Y"); +str_to_date("Sundai 15 MAY 2001", "%W %d %b %Y") +NULL +select str_to_date("Sundai 15 MA", "%W %d %b %Y"); +str_to_date("Sundai 15 MA", "%W %d %b %Y") +NULL +select str_to_date("Tuesday 52 2001", "%W %V %X"); +str_to_date("Tuesday 52 2001", "%W %V %X") +NULL +select str_to_date("Sunday 01 2001", "%W %V %X"); +str_to_date("Sunday 01 2001", "%W %V %X") +NULL +select str_to_date("Tuesday 00 2002", "%W %U %Y"); +str_to_date("Tuesday 00 2002", "%W %U %Y") +2002-01-01 00:00:00 +select str_to_date("Thursday 53 1998", "%W %u %Y"); +str_to_date("Thursday 53 1998", "%W %u %Y") +1998-12-31 00:00:00 +select str_to_date("15-01-2001", "%d-%m-%Y %H:%i:%S"); +str_to_date("15-01-2001", "%d-%m-%Y %H:%i:%S") +2001-01-15 00:00:00 +select str_to_date("15-01-20", "%d-%m-%Y"); +str_to_date("15-01-20", "%d-%m-%Y") +NULL +select str_to_date("15-2001-1", "%d-%Y-%c"); +str_to_date("15-2001-1", "%d-%Y-%c") +2001-01-15 00:00:00 +select get_format(DATE, 'USA') as a; +a +%m.%d.%Y +select get_format(TIME, 'internal') as a; +a +%H%i%s +select get_format(DATETIME, 'eur') as a; +a +%Y-%m-%d-%H.%i.%s diff --git a/mysql-test/t/date_formats-master.opt b/mysql-test/t/date_formats-master.opt new file mode 100644 index 00000000000..7977a601dd7 --- /dev/null +++ b/mysql-test/t/date_formats-master.opt @@ -0,0 +1 @@ +--date_format=%d.%m.%Y --time_format=%H.%i.%s --datetime_format=%Y/%d/%m-%H:%i:%s diff --git a/mysql-test/t/date_formats.test b/mysql-test/t/date_formats.test new file mode 100644 index 00000000000..9551efaa648 --- /dev/null +++ b/mysql-test/t/date_formats.test @@ -0,0 +1,82 @@ +SHOW GLOBAL VARIABLES LIKE "%_format%"; +SHOW SESSION VARIABLES LIKE "%_format%"; + +SET date_format="%d.%m.%Y"; +select CAST("01.01.2001" as DATE) as a; +SET datetime_format="%d.%m.%Y %H.%i.%s"; +select CAST("01.01.2001 05.12.06" as DATETIME) as a; +SET time_format="%H.%i.%s"; +select CAST("05.12.06" as TIME) as a; + +SET datetime_format="%d.%m.%Y %h:%i:%s %p"; +select CAST("01.01.2001 05:12:06AM" as DATETIME) as a; +select CAST("01.01.2001 05:12:06 PM" as DATETIME) as a; + +SET time_format="%h:%i:%s %p"; +select CAST("05:12:06 AM" as TIME) as a; +select CAST("05:12:06.1234PM" as TIME) as a; + +SET time_format="%h.%i.%s %p"; +SET date_format='%d.%m.%y'; +SET datetime_format="%d.%m.%y %h.%i.%s %p"; +select CAST("12-12-06" as DATE) as a; + +select adddate("01.01.97 11.59.59.000001 PM", 10); +select datediff("31.12.97 11.59:59.000001 PM","01.01.98"); +select weekofyear("31.11.97 11:59:59.000001 PM"); +select makedate(1997,1); +select addtime("31.12.97 11.59.59.999999 PM", "1 1.1.1.000002"); +select maketime(23,11,12); +select timediff("01.01.97 11:59:59.000001 PM","31.12.95 11:59:59.000002 PM"); + +SET time_format="%H%i%s"; +--error 1105 +SET time_format="%h%i%s"; +--error 1105 +SET date_format='%d.%m.%d'; +--error 1105 +SET datetime_format="%d.%m.%y %h.%i.%s"; + +SET GLOBAL date_format=default; +SHOW GLOBAL VARIABLES LIKE "date_format%"; +SET GLOBAL time_format=default; +SHOW GLOBAL VARIABLES LIKE "time_format%"; +SET GLOBAL datetime_format=default; +SHOW GLOBAL VARIABLES LIKE "datetime_format%"; + +SET date_format=default; +SHOW SESSION VARIABLES LIKE "date_format%"; +SET time_format=default; +SHOW SESSION VARIABLES LIKE "time_format%"; +SET datetime_format=default; +SHOW SESSION VARIABLES LIKE "datetime_format%"; + +SET time_format='%i:%s:%H'; +select cast(str_to_date("15-01-2001 12:59:59", "%d-%m-%Y %H:%i:%S") as TIME); + +SET GLOBAL date_format='%Y-%m-%d'; +SET GLOBAL time_format='%H:%i:%s'; +SET GLOBAL datetime_format='%Y-%m-%d %H:%i:%s'; +SET date_format='%Y-%m-%d'; +SET time_format='%H:%i:%s'; +SET datetime_format='%Y-%m-%d %H:%i:%s'; + +select str_to_date("15-01-2001 12:59:59", "%d-%m-%Y %H:%i:%S"); +select str_to_date("15 September 2001", "%d %M %Y"); +select str_to_date("15 Septembeb 2001", "%d %M %Y"); +select str_to_date("15 MAY 2001", "%d %b %Y"); +select str_to_date("Sunday 15 MAY 2001", "%W %d %b %Y"); +select str_to_date("Sundai 15 MAY 2001", "%W %d %b %Y"); +select str_to_date("Sundai 15 MA", "%W %d %b %Y"); +select str_to_date("Tuesday 52 2001", "%W %V %X"); +select str_to_date("Sunday 01 2001", "%W %V %X"); +select str_to_date("Tuesday 00 2002", "%W %U %Y"); +select str_to_date("Thursday 53 1998", "%W %u %Y"); +select str_to_date("15-01-2001", "%d-%m-%Y %H:%i:%S"); +select str_to_date("15-01-20", "%d-%m-%Y"); +select str_to_date("15-2001-1", "%d-%Y-%c"); + +select get_format(DATE, 'USA') as a; +select get_format(TIME, 'internal') as a; +select get_format(DATETIME, 'eur') as a; + diff --git a/sql/field.cc b/sql/field.cc index 68c9922e887..b025f65a798 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -279,7 +279,8 @@ bool Field::get_date(TIME *ltime,bool fuzzydate) char buff[40]; String tmp(buff,sizeof(buff),&my_charset_bin),tmp2,*res; if (!(res=val_str(&tmp,&tmp2)) || - str_to_TIME(res->ptr(),res->length(),ltime,fuzzydate) == TIMESTAMP_NONE) + str_to_TIME(res->ptr(),res->length(),ltime,fuzzydate,current_thd)<= + WRONG_TIMESTAMP_FULL) return 1; return 0; } @@ -289,7 +290,7 @@ bool Field::get_time(TIME *ltime) char buff[40]; String tmp(buff,sizeof(buff),&my_charset_bin),tmp2,*res; if (!(res=val_str(&tmp,&tmp2)) || - str_to_time(res->ptr(),res->length(),ltime)) + str_to_time(res->ptr(),res->length(),ltime,current_thd)) return 1; return 0; } @@ -299,28 +300,29 @@ bool Field::get_time(TIME *ltime) void Field::store_time(TIME *ltime,timestamp_type type) { char buff[25]; + String tmp((char*) buff,sizeof(buff),&my_charset_bin); + DATETIME_FORMAT *tmp_format= 0; + bool is_time_only= 0; + switch (type) { case TIMESTAMP_NONE: + case WRONG_TIMESTAMP_FULL: store("",0,&my_charset_bin); // Probably an error - break; + return; case TIMESTAMP_DATE: - sprintf(buff,"%04d-%02d-%02d", ltime->year,ltime->month,ltime->day); - store(buff,10,&my_charset_bin); + tmp_format= &t_datetime_frm(current_thd, DATE_FORMAT_TYPE).datetime_format; break; case TIMESTAMP_FULL: - sprintf(buff,"%04d-%02d-%02d %02d:%02d:%02d", - ltime->year,ltime->month,ltime->day, - ltime->hour,ltime->minute,ltime->second); - store(buff,19,&my_charset_bin); + tmp_format=&t_datetime_frm(current_thd,DATETIME_FORMAT_TYPE).datetime_format; break; case TIMESTAMP_TIME: - { - ulong length= my_sprintf(buff, (buff, "%02d:%02d:%02d", - ltime->hour,ltime->minute,ltime->second)); - store(buff,(uint) length, &my_charset_bin); + tmp_format= &t_datetime_frm(current_thd, TIME_FORMAT_TYPE).datetime_format; + is_time_only= 1; break; } - } + make_datetime(&tmp, ltime, is_time_only, 0, + tmp_format->format, tmp_format->format_length, 1); + store(tmp.ptr(),tmp.length(),&my_charset_bin); } @@ -2691,7 +2693,7 @@ Field_timestamp::Field_timestamp(char *ptr_arg, uint32 len_arg, int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs) { - long tmp=(long) str_to_timestamp(from,len); + long tmp=(long) str_to_timestamp(from,len,current_thd); #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -3025,7 +3027,7 @@ int Field_time::store(const char *from,uint len,CHARSET_INFO *cs) TIME ltime; long tmp; int error= 0; - if (str_to_time(from,len,<ime)) + if (str_to_time(from,len,<ime,current_thd)) { tmp=0L; error= 1; @@ -3134,19 +3136,25 @@ longlong Field_time::val_int(void) String *Field_time::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { + TIME ltime; val_buffer->alloc(16); long tmp=(long) sint3korr(ptr); const char *sign=""; + ltime.neg= 0; if (tmp < 0) { tmp= -tmp; - sign= "-"; - } - long length= my_sprintf((char*) val_buffer->ptr(), - ((char*) val_buffer->ptr(),"%s%02d:%02d:%02d", - sign,(int) (tmp/10000), (int) (tmp/100 % 100), - (int) (tmp % 100))); - val_buffer->length(length); + ltime.neg= 1; + } + DATETIME_FORMAT *tmp_format= (&t_datetime_frm + (current_thd, TIME_FORMAT_TYPE).datetime_format); + ltime.day= (uint) 0; + ltime.hour= (uint) (tmp/10000); + ltime.minute= (uint) (tmp/100 % 100); + ltime.second= (uint) (tmp % 100); + make_datetime(val_buffer, <ime, 0, 0, + tmp_format->format, + tmp_format->format_length, 1); return val_buffer; } @@ -3312,7 +3320,7 @@ int Field_date::store(const char *from, uint len,CHARSET_INFO *cs) TIME l_time; uint32 tmp; int error= 0; - if (str_to_TIME(from,len,&l_time,1) == TIMESTAMP_NONE) + if (str_to_TIME(from,len,&l_time,1,current_thd) <= WRONG_TIMESTAMP_FULL) { tmp=0; error= 1; @@ -3422,6 +3430,7 @@ longlong Field_date::val_int(void) String *Field_date::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { + TIME ltime; val_buffer->alloc(field_length); val_buffer->length(field_length); int32 tmp; @@ -3431,9 +3440,15 @@ String *Field_date::val_str(String *val_buffer, else #endif longget(tmp,ptr); - sprintf((char*) val_buffer->ptr(),"%04d-%02d-%02d", - (int) ((uint32) tmp/10000L % 10000), (int) ((uint32) tmp/100 % 100), - (int) ((uint32) tmp % 100)); + DATETIME_FORMAT *tmp_format= (&t_datetime_frm + (current_thd, DATE_FORMAT_TYPE).datetime_format); + ltime.neg= 0; + ltime.year= (int) ((uint32) tmp/10000L % 10000); + ltime.month= (int) ((uint32) tmp/100 % 100); + ltime.day= (int) ((uint32) tmp % 100); + make_datetime(val_buffer, <ime, 0, 0, + tmp_format->format, + tmp_format->format_length, 1); return val_buffer; } @@ -3492,7 +3507,7 @@ int Field_newdate::store(const char *from,uint len,CHARSET_INFO *cs) TIME l_time; long tmp; int error= 0; - if (str_to_TIME(from,len,&l_time,1) == TIMESTAMP_NONE) + if (str_to_TIME(from,len,&l_time,1,current_thd) <= WRONG_TIMESTAMP_FULL) { tmp=0L; error= 1; @@ -3661,7 +3676,7 @@ void Field_newdate::sql_type(String &res) const int Field_datetime::store(const char *from,uint len,CHARSET_INFO *cs) { - longlong tmp=str_to_datetime(from,len,1); + longlong tmp=str_to_datetime(from,len,1,current_thd); #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { diff --git a/sql/item.cc b/sql/item.cc index 4de4951cb51..32fcba1bf2d 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -46,7 +46,7 @@ Item::Item(): collation.set(default_charset(), DERIVATION_COERCIBLE); name= 0; decimals= 0; max_length= 0; - THD *thd= current_thd; + thd= current_thd; next= thd->free_list; // Put in free list thd->free_list= this; /* @@ -69,7 +69,7 @@ Item::Item(): Used for duplicating lists in processing queries with temporary tables */ -Item::Item(THD *thd, Item &item): +Item::Item(THD *c_thd, Item &item): str_value(item.str_value), name(item.name), max_length(item.max_length), @@ -82,7 +82,8 @@ Item::Item(THD *thd, Item &item): fixed(item.fixed), collation(item.collation) { - next=thd->free_list; // Put in free list + next=c_thd->free_list; // Put in free list + thd= c_thd; thd->free_list= this; } @@ -174,7 +175,8 @@ bool Item::get_date(TIME *ltime,bool fuzzydate) char buff[40]; String tmp(buff,sizeof(buff), &my_charset_bin),*res; if (!(res=val_str(&tmp)) || - str_to_TIME(res->ptr(),res->length(),ltime,fuzzydate) == TIMESTAMP_NONE) + str_to_TIME(res->ptr(),res->length(),ltime,fuzzydate, thd) <= + WRONG_TIMESTAMP_FULL) { bzero((char*) ltime,sizeof(*ltime)); return 1; @@ -192,7 +194,7 @@ bool Item::get_time(TIME *ltime) char buff[40]; String tmp(buff,sizeof(buff),&my_charset_bin),*res; if (!(res=val_str(&tmp)) || - str_to_time(res->ptr(),res->length(),ltime)) + str_to_time(res->ptr(),res->length(),ltime, thd)) { bzero((char*) ltime,sizeof(*ltime)); return 1; @@ -673,30 +675,28 @@ String *Item_param::query_val_str(String* str) } else { - char buff[25]; + DATETIME_FORMAT *tmp_format= 0; + bool is_time_only= 0; switch (ltime.time_type) { case TIMESTAMP_NONE: + case WRONG_TIMESTAMP_FULL: break; case TIMESTAMP_DATE: - sprintf(buff, "%04d-%02d-%02d", - ltime.year,ltime.month,ltime.day); - str->append(buff, 10); + tmp_format= &t_datetime_frm(thd, DATE_FORMAT_TYPE).datetime_format; break; case TIMESTAMP_FULL: - sprintf(buff, "%04d-%02d-%02d %02d:%02d:%02d", - ltime.year,ltime.month,ltime.day, - ltime.hour,ltime.minute,ltime.second); - str->append(buff, 19); + tmp_format= &t_datetime_frm(thd, DATETIME_FORMAT_TYPE).datetime_format; break; case TIMESTAMP_TIME: { - sprintf(buff, "%02d:%02d:%02d", - ltime.hour,ltime.minute,ltime.second); - str->append(buff, 8); + tmp_format= &t_datetime_frm(thd, TIME_FORMAT_TYPE).datetime_format; + is_time_only= 1; break; - } + } } + make_datetime(str, <ime, is_time_only, 0, + tmp_format->format, tmp_format->format_length, 0); } str->append("'"); } diff --git a/sql/item.h b/sql/item.h index 517d3f311e5..c738f92124f 100644 --- a/sql/item.h +++ b/sql/item.h @@ -113,6 +113,14 @@ public: my_bool with_sum_func; my_bool fixed; /* If item fixed with fix_fields */ DTCollation collation; + + + /* + thd is current_thd value. Like some other Item's fields it + will be a problem for using one Item in different threads + (as stored procedures may want to do in the future) + */ + THD *thd; // alloc & destruct is done as start of select using sql_alloc Item(); @@ -124,7 +132,7 @@ public: top AND/OR ctructure of WHERE clause to protect it of optimisation changes in prepared statements */ - Item(THD *thd, Item &item); + Item(THD *c_thd, Item &item); virtual ~Item() { name=0; } /*lint -e1509 */ void set_name(const char *str,uint length, CHARSET_INFO *cs); void init_make_field(Send_field *tmp_field,enum enum_field_types type); diff --git a/sql/item_create.cc b/sql/item_create.cc index b1173b9c7b8..fce59d68c1f 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -701,3 +701,8 @@ Item *create_func_maketime(Item* a,Item* b,Item* c) { return new Item_func_maketime(a, b, c); } + +Item *create_func_str_to_date(Item* a,Item* b) +{ + return new Item_func_str_to_date(a, b); +} diff --git a/sql/item_create.h b/sql/item_create.h index c75f4404bad..5d6cbe1d58f 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -149,3 +149,4 @@ Item *create_func_addtime(Item* a,Item* b); Item *create_func_subtime(Item* a,Item* b); Item *create_func_timediff(Item* a,Item* b); Item *create_func_maketime(Item* a,Item* b,Item* c); +Item *create_func_str_to_date(Item* a,Item* b); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 31ce2ad9cdc..4009256ee17 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -57,82 +57,544 @@ static String day_names[] = String("Sunday", &my_charset_latin1) }; -enum date_time_format_types -{ - TIME_ONLY= 0, TIME_MICROSECOND, - DATE_ONLY, DATE_TIME, DATE_TIME_MICROSECOND -}; - -typedef struct date_time_format +uint check_names(String *arr,int item_count,const char *val_ptr, + const char *val_end, uint *val, bool check_part) { - const char* format_str; - uint length; -}; + for (int i= 0; i < item_count; i++) + { + String *tmp=&arr[i]; + if (!my_strnncoll(&my_charset_latin1, + (const uchar *) val_ptr, 3, + (const uchar *) tmp->ptr(), 3)) + { + if (check_part) + { + *val= i+1; + return 3; + } -static struct date_time_format date_time_formats[]= + int part_len= tmp->length() - 3; + int val_len= val_end - val_ptr - 3; + if (val_len < part_len) + return 0; + val_ptr+=3; + if (!my_strnncoll(&my_charset_latin1, + (const uchar *) val_ptr, part_len, + (const uchar *) tmp->ptr() + 3, part_len)) + { + *val= i+1; + return tmp->length(); + } + return 0; + } + } + return 0; +} + +uint check_val_is_digit(const char *ptr, uint val_len, uint digit_count) { - {"%s%02d:%02d:%02d", 10}, - {"%s%02d:%02d:%02d.%06d", 17}, - {"%04d-%02d-%02d", 10}, - {"%04d-%02d-%02d %02d:%02d:%02d", 19}, - {"%04d-%02d-%02d %02d:%02d:%02d.%06d", 26} -}; + uint i; + uint verify_count= (val_len < digit_count ? val_len : digit_count); + uint digit_found= 0; + for (i= 0; i < verify_count; i++) + { + if (!my_isdigit(&my_charset_latin1, *(ptr+i))) + break; + digit_found++; + } + return digit_found; +} /* - OPTIMIZATION TODO: - - Replace the switch with a function that should be called for each - date type. - - Remove sprintf and opencode the conversion, like we do in - Field_datetime. + Extract datetime value to TIME struct from string value + according to format string. */ - -String *make_datetime(String *str, TIME *ltime, - enum date_time_format_types format) +bool extract_datetime(const char *str_val, uint str_val_len, + const char *str_format, uint str_format_len, + TIME *l_time) { - char *buff; + char intbuff[15]; + int weekday= 0, yearday= 0, daypart= 0, len; + int val_len= 0; + int week_number= -1; + ulong length; CHARSET_INFO *cs= &my_charset_bin; - uint length= date_time_formats[format].length + 32; - const char* format_str= date_time_formats[format].format_str; + int err= 0; + bool usa_time= 0; + bool sunday_first= 0; + const char *rT_format= "%H:%i:%s"; + uint part_len= 0; + const char *val_ptr=str_val; + const char *val_end= str_val + str_val_len; + const char *ptr=str_format; + const char *end=ptr+ str_format_len; + + DBUG_ENTER("extract_datetime"); + for (; ptr != end && val_ptr != val_end; ptr++) + { + if (*ptr == '%' && ptr+1 != end) + { + val_len= val_end - val_ptr; + char *val_end1= (char *) val_end; + switch (*++ptr) { + case 'h': + case 'I': + case 'H': + l_time->hour= my_strntoll(cs, val_ptr, + 2, 10, &val_end1, &err); + if (err || (val_end1 - val_ptr != 2)) + return 1; + usa_time= (*ptr == 'I' || *ptr == 'h'); + val_ptr+=2; + break; + case 'k': + case 'l': + l_time->hour= my_strntoll(cs, val_ptr, + 2, 10, &val_end1, &err); + if (err) + return 1; + usa_time= (*ptr == 'l'); + val_ptr= val_end1; + break; + case 'e': + l_time->day= my_strntoll(cs, val_ptr, + 2, 10, &val_end1, &err); + if (err) + return 1; + val_ptr= val_end1; + break; + case 'c': + l_time->month= my_strntoll(cs, val_ptr, + 2, 10, &val_end1, &err); + if (err) + return 1; + val_ptr= val_end1; + break; + case 'Y': + l_time->year= my_strntoll(cs, val_ptr, + 4, 10, &val_end1, &err); + if (err || (val_end1 - val_ptr != 4)) + return 1; + val_ptr+=4; + break; + case 'y': + l_time->year= my_strntoll(cs, val_ptr, + 2, 10, &val_end1, &err); + if (err || (val_end1 - val_ptr != 2)) + return 1; + l_time->year+= (l_time->year < YY_PART_YEAR ? 2000 : 1900); + val_ptr+=2; + break; + case 'm': + l_time->month= my_strntoll(cs, val_ptr, + 2, 10, &val_end1, &err); + if (err || (val_end1 - val_ptr != 2)) + return 1; + val_ptr+=2; + break; + case 'd': + l_time->day= my_strntoll(cs, val_ptr, + 2, 10, &val_end1, &err); + if (err || (val_end1 - val_ptr != 2)) + return 1; + val_ptr+=2; + break; + case 'D': + l_time->day= my_strntoll(cs, val_ptr, + 2, 10, &val_end1, &err); + if (err || (val_len < val_end1 - val_ptr + 2)) + return 1; + val_ptr= val_end1 + 2; + break; + case 'i': + l_time->minute=my_strntoll(cs, val_ptr, + 2, 10, &val_end1, &err); + if (err || (val_end1 - val_ptr != 2)) + return 1; + val_ptr+=2; + break; + case 's': + case 'S': + l_time->second= my_strntoll(cs, val_ptr, + 2, 10, &val_end1, &err); + if (err || (val_end1 - val_ptr != 2)) + return 1; + val_ptr+=2; + break; + case 'M': + if (val_len < 3 || + !(part_len= check_names(month_names, 12 , val_ptr, + val_end, &l_time->month, 0))) + return 1; + val_ptr+= part_len; + break; + case 'b': + if (val_len < 3 || + !(part_len= check_names(month_names, 12 , val_ptr, + val_end,(uint *) &l_time->month, 1))) + return 1; + val_ptr+= part_len; + break; + case 'W': + if (val_len < 3 || + !(part_len= check_names(day_names, 7 , val_ptr, + val_end,(uint *) &weekday, 0))) + return 1; + val_ptr+= part_len; + break; + case 'a': + if (val_len < 3 || + !(part_len= check_names(day_names, 7 , val_ptr, + val_end,(uint *) &weekday, 1))) + return 1; + val_ptr+= part_len; + break; + case 'w': + weekday= my_strntoll(cs, val_ptr, 1, 10, &val_end1, &err); + if (err) + return 1; + val_ptr++; + break; + case 'j': + yearday= my_strntoll(cs, val_ptr, 3, 10, &val_end1, &err); + if (err || (val_end1 - val_ptr != 3)) + return 1; + val_ptr+=3; + break; + case 'f': + l_time->second_part= my_strntoll(cs, val_ptr, 3, 10, &val_end1, &err); + if (err) + return 1; + val_ptr= val_end1; + break; + case 'p': + if (val_len < 2) + return 1; + if (!my_strnncoll(&my_charset_latin1, + (const uchar *) val_ptr, 2, + (const uchar *) "PM", 2)) + { + daypart= 12; + val_ptr+= 2; + } + break; + case 'U': + week_number= my_strntoll(cs, val_ptr, 2, 10, &val_end1, &err); + if (err || (val_end1 - val_ptr != 2)) + return 1; + sunday_first= 1; + val_ptr+=2; + break; + case 'u': + week_number= my_strntoll(cs, val_ptr, 2, 10, &val_end1, &err); + if (err || (val_end1 - val_ptr != 2)) + return 1; + sunday_first=0; + val_ptr+=2; + break; + case 'r': + case 'T': + usa_time= (*ptr == 'r'); + if (extract_datetime(val_ptr, val_end-val_ptr, + rT_format, strlen(rT_format), + l_time)) + return 1; + val_ptr+=8; + break; + default: + if (*val_ptr != *ptr) + return 1; + val_ptr++; + } + } + else + { + if (*val_ptr != *ptr) + return 1; + val_ptr++; + } + } + if (usa_time) + { + if (l_time->hour > 12 || l_time->hour < 1) + return 1; + l_time->hour= l_time->hour%12+daypart; + } - if (str->alloc(length)) - return 0; + if (yearday > 0) + { + uint days= calc_daynr(l_time->year,1,1) + yearday - 1; + if (days > 0 || days < MAX_DAY_NUMBER) + { + get_date_from_daynr(days,&l_time->year,&l_time->month,&l_time->day); + } + } - buff= (char*) str->ptr(); - switch (format) { - case TIME_ONLY: - length= cs->cset->snprintf(cs, buff, length, format_str, ltime->neg ? "-" : "", - ltime->hour, ltime->minute, ltime->second); - break; - case TIME_MICROSECOND: - length= cs->cset->snprintf(cs, buff, length, format_str, ltime->neg ? "-" : "", - ltime->hour, ltime->minute, ltime->second, - ltime->second_part); - break; - case DATE_ONLY: - length= cs->cset->snprintf(cs, buff, length, format_str, - ltime->year, ltime->month, ltime->day); - break; - case DATE_TIME: - length= cs->cset->snprintf(cs, buff, length, format_str, - ltime->year, ltime->month, ltime->day, - ltime->hour, ltime->minute, ltime->second); - break; - case DATE_TIME_MICROSECOND: - length= cs->cset->snprintf(cs, buff, length, format_str, - ltime->year, ltime->month, ltime->day, - ltime->hour, ltime->minute, ltime->second, - ltime->second_part); - break; - default: - return 0; + if (week_number >= 0 && weekday) + { + int days= calc_daynr(l_time->year,1,1); + uint weekday_b; + + if (weekday > 7 || weekday < 0) + return 1; + if (sunday_first) + weekday = weekday%7; + + if (week_number == 53) + { + days+= (week_number - 1)*7; + weekday_b= calc_weekday(days, sunday_first); + weekday = weekday - weekday_b - !sunday_first; + days+= weekday; + } + else if (week_number == 0) + { + weekday_b= calc_weekday(days, sunday_first); + weekday = weekday - weekday_b - !sunday_first; + days+= weekday; + } + else + { + days+= (week_number - !sunday_first)*7; + weekday_b= calc_weekday(days, sunday_first); + weekday =weekday - weekday_b - !sunday_first; + days+= weekday; + } + if (days > 0 || days < MAX_DAY_NUMBER) + { + get_date_from_daynr(days,&l_time->year,&l_time->month,&l_time->day); + } } - str->length(length); - str->set_charset(cs); + if (l_time->month > 12 || l_time->day > 31 || l_time->hour > 23 || + l_time->minute > 59 || l_time->second > 59) + return 1; + + DBUG_RETURN(0); +} + + + +/* + Print datetime string from TIME struct + according to format string. +*/ + + +String *make_datetime(String *str, TIME *l_time, + const bool is_time_only, + const bool add_second_frac, + const char *ptr, uint format_length, + bool set_len_to_zero) +{ + char intbuff[15]; + uint days_i; + uint hours_i; + uint weekday; + ulong length; + if (set_len_to_zero) + str->length(0); + if (l_time->neg) + str->append("-", 1); + const char *end=ptr+format_length; + for (; ptr != end ; ptr++) + { + if (*ptr != '%' || ptr+1 == end) + str->append(*ptr); + else + { + switch (*++ptr) { + case 'M': + if (!l_time->month) + return 0; + str->append(month_names[l_time->month-1]); + break; + case 'b': + if (!l_time->month) + return 0; + str->append(month_names[l_time->month-1].ptr(),3); + break; + case 'W': + if (is_time_only) + return 0; + weekday=calc_weekday(calc_daynr(l_time->year,l_time->month,l_time->day),0); + str->append(day_names[weekday]); + break; + case 'a': + if (is_time_only) + return 0; + weekday=calc_weekday(calc_daynr(l_time->year,l_time->month,l_time->day),0); + str->append(day_names[weekday].ptr(),3); + break; + case 'D': + if (is_time_only) + return 0; + length= int10_to_str(l_time->day, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 1, '0'); + if (l_time->day >= 10 && l_time->day <= 19) + str->append("th"); + else + { + switch (l_time->day %10) { + case 1: + str->append("st",2); + break; + case 2: + str->append("nd",2); + break; + case 3: + str->append("rd",2); + break; + default: + str->append("th",2); + break; + } + } + break; + case 'Y': + length= int10_to_str(l_time->year, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 4, '0'); + break; + case 'y': + length= int10_to_str(l_time->year%100, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 2, '0'); + break; + case 'm': + length= int10_to_str(l_time->month, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 2, '0'); + break; + case 'c': + length= int10_to_str(l_time->month, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 1, '0'); + break; + case 'd': + length= int10_to_str(l_time->day, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 2, '0'); + break; + case 'e': + length= int10_to_str(l_time->day, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 1, '0'); + break; + case 'f': + length= int10_to_str(l_time->second_part, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 6, '0'); + break; + case 'H': + length= int10_to_str(l_time->hour, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 2, '0'); + break; + case 'h': + case 'I': + days_i= l_time->hour/24; + hours_i= (l_time->hour%24 + 11)%12+1 + 24*days_i; + length= int10_to_str(hours_i, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 2, '0'); + break; + case 'i': /* minutes */ + length= int10_to_str(l_time->minute, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 2, '0'); + break; + case 'j': + if (is_time_only) + return 0; + length= int10_to_str(calc_daynr(l_time->year,l_time->month,l_time->day) - + calc_daynr(l_time->year,1,1) + 1, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 3, '0'); + break; + case 'k': + length= int10_to_str(l_time->hour, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 1, '0'); + break; + case 'l': + days_i= l_time->hour/24; + hours_i= (l_time->hour%24 + 11)%12+1 + 24*days_i; + length= int10_to_str(hours_i, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 1, '0'); + break; + case 'p': + hours_i= l_time->hour%24; + str->append(hours_i < 12 ? "AM" : "PM",2); + break; + case 'r': + length= my_sprintf(intbuff, + (intbuff, + (l_time->hour < 12) ? "%02d:%02d:%02d AM" : "%02d:%02d:%02d PM", + (l_time->hour+11)%12+1, + l_time->minute, + l_time->second)); + str->append(intbuff, length); + break; + case 'S': + case 's': + length= int10_to_str(l_time->second, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 2, '0'); + if (add_second_frac) + { + str->append(".", 1); + length= int10_to_str(l_time->second_part, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 6, '0'); + } + break; + case 'T': + length= my_sprintf(intbuff, + (intbuff, + "%02d:%02d:%02d", + l_time->hour, + l_time->minute, + l_time->second)); + str->append(intbuff, length); + break; + case 'U': + case 'u': + { + uint year; + if (is_time_only) + return 0; + length= int10_to_str(calc_week(l_time, 0, (*ptr) == 'U', &year), + intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 2, '0'); + } + break; + case 'v': + case 'V': + { + uint year; + if (is_time_only) + return 0; + length= int10_to_str(calc_week(l_time, 1, (*ptr) == 'V', &year), + intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 2, '0'); + } + break; + case 'x': + case 'X': + { + uint year; + if (is_time_only) + return 0; + (void) calc_week(l_time, 1, (*ptr) == 'X', &year); + length= int10_to_str(year, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 4, '0'); + } + break; + case 'w': + if (is_time_only) + return 0; + weekday=calc_weekday(calc_daynr(l_time->year,l_time->month,l_time->day),1); + length= int10_to_str(weekday, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 1, '0'); + break; + default: + str->append(*ptr); + break; + } + } + } return str; } + /* ** Get a array of positive numbers from a string object. ** Each number is separated by 1 non digit character @@ -346,7 +808,7 @@ longlong Item_func_year::val_int() longlong Item_func_unix_timestamp::val_int() { if (arg_count == 0) - return (longlong) current_thd->query_start(); + return (longlong) thd->query_start(); if (args[0]->type() == FIELD_ITEM) { // Optimize timestamp field Field *field=((Item_field*) args[0])->field; @@ -358,7 +820,7 @@ longlong Item_func_unix_timestamp::val_int() { return 0; /* purecov: inspected */ } - return (longlong) str_to_timestamp(str->ptr(),str->length()); + return (longlong) str_to_timestamp(str->ptr(),str->length(), thd); } @@ -522,22 +984,26 @@ static bool get_interval_value(Item *args,interval_type int_type, String *Item_date::val_str(String *str) { + DATETIME_FORMAT *tmp_format; + TIME ltime; ulong value=(ulong) val_int(); if (null_value) - return (String*) 0; - if (!value) // zero daynr - { - str->copy("0000-00-00",10,&my_charset_latin1,default_charset()); + goto null_date; + + ltime.year= (value/10000L) % 10000; + ltime.month= (value/100)%100; + ltime.day= (value%100); + ltime.neg=0; + ltime.time_type=TIMESTAMP_DATE; + + tmp_format= &t_datetime_frm(thd, DATE_FORMAT_TYPE).datetime_format; + if (make_datetime(str, <ime, 0, 0, + tmp_format->format, tmp_format->format_length, 1)) return str; - } - - char tmpbuff[11]; - sprintf(tmpbuff,"%04d-%02d-%02d", - (int) (value/10000L) % 10000, - (int) (value/100)%100, - (int) (value%100)); - str->copy(tmpbuff,10,&my_charset_latin1,default_charset()); - return str; + + null_value= 1; +null_date: + return 0; } @@ -577,7 +1043,7 @@ void Item_func_curdate::fix_length_and_dec() decimals=0; max_length=10*default_charset()->mbmaxlen; - store_now_in_tm(current_thd->query_start(),&start); + store_now_in_tm(thd->query_start(),&start); value=(longlong) ((ulong) ((uint) start.tm_year+1900)*10000L+ ((uint) start.tm_mon+1)*100+ @@ -632,22 +1098,27 @@ String *Item_func_curtime::val_str(String *str) void Item_func_curtime::fix_length_and_dec() { struct tm start; - CHARSET_INFO *cs= default_charset(); + DATETIME_FORMAT *tmp_format; + String tmp((char*) buff,sizeof(buff),default_charset()); + TIME ltime; decimals=0; - max_length=8*cs->mbmaxlen; - collation.set(cs); - - store_now_in_tm(current_thd->query_start(),&start); - + store_now_in_tm(thd->query_start(),&start); value=(longlong) ((ulong) ((uint) start.tm_hour)*10000L+ (ulong) (((uint) start.tm_min)*100L+ (uint) start.tm_sec)); - - buff_length=cs->cset->snprintf(cs,buff,sizeof(buff),"%02d:%02d:%02d", - (int) start.tm_hour, - (int) start.tm_min, - (int) start.tm_sec); + ltime.day= 0; + ltime.hour= start.tm_hour; + ltime.minute= start.tm_min; + ltime.second= start.tm_sec; + ltime.second_part= 0; + ltime.neg= 0; + ltime.time_type= TIMESTAMP_TIME; + tmp_format= &t_datetime_frm(thd, TIME_FORMAT_TYPE).datetime_format; + make_datetime(&tmp, <ime, 0, 0, + tmp_format->format, tmp_format->format_length, 1); + buff_length= tmp.length(); + max_length= buff_length; } @@ -681,14 +1152,11 @@ String *Item_func_now::val_str(String *str) void Item_func_now::fix_length_and_dec() { struct tm start; - CHARSET_INFO *cs= &my_charset_bin; + DATETIME_FORMAT *tmp_format; + String tmp((char*) buff,sizeof(buff),&my_charset_bin); decimals=0; - max_length=19*cs->mbmaxlen; - collation.set(cs); - - store_now_in_tm(current_thd->query_start(),&start); - + store_now_in_tm(thd->query_start(),&start); value=((longlong) ((ulong) ((uint) start.tm_year+1900)*10000L+ (((uint) start.tm_mon+1)*100+ (uint) start.tm_mday))*(longlong) 1000000L+ @@ -696,14 +1164,6 @@ void Item_func_now::fix_length_and_dec() (ulong) (((uint) start.tm_min)*100L+ (uint) start.tm_sec))); - buff_length= (uint) cs->cset->snprintf(cs,buff, sizeof(buff), - "%04d-%02d-%02d %02d:%02d:%02d", - ((int) (start.tm_year+1900)) % 10000, - (int) start.tm_mon+1, - (int) start.tm_mday, - (int) start.tm_hour, - (int) start.tm_min, - (int) start.tm_sec); /* For getdate */ ltime.year= start.tm_year+1900; ltime.month= start.tm_mon+1; @@ -711,9 +1171,15 @@ void Item_func_now::fix_length_and_dec() ltime.hour= start.tm_hour; ltime.minute= start.tm_min; ltime.second= start.tm_sec; - ltime.second_part=0; - ltime.neg=0; - ltime.time_type=TIMESTAMP_FULL; + ltime.second_part= 0; + ltime.neg= 0; + ltime.time_type= TIMESTAMP_FULL; + + tmp_format= &t_datetime_frm(thd, DATETIME_FORMAT_TYPE).datetime_format; + make_datetime(&tmp, <ime, 0, 0, + tmp_format->format, tmp_format->format_length, 1); + buff_length= tmp.length(); + max_length= buff_length; } bool Item_func_now::get_date(TIME *res, @@ -754,22 +1220,36 @@ void Item_func_now_utc::store_now_in_tm(time_t now, struct tm *now_tm) String *Item_func_sec_to_time::val_str(String *str) { - char buff[23*2]; - const char *sign=""; longlong seconds=(longlong) args[0]->val_int(); - ulong length; + uint sec; + + DATETIME_FORMAT *tmp_format; + TIME ltime; + if ((null_value=args[0]->null_value)) - return (String*) 0; + goto null_date; + + ltime.neg= 0; if (seconds < 0) { seconds= -seconds; - sign= "-"; + ltime.neg= 1; } - uint sec= (uint) ((ulonglong) seconds % 3600); - length= my_sprintf(buff,(buff,"%s%02lu:%02u:%02u",sign,(long) (seconds/3600), - sec/60, sec % 60)); - str->copy(buff, length, &my_charset_latin1, default_charset()); - return str; + + sec= (uint) ((ulonglong) seconds % 3600); + ltime.day= 0; + ltime.hour= seconds/3600; + ltime.minute= sec/60; + ltime.second= sec % 60; + + tmp_format= &t_datetime_frm(thd, TIME_FORMAT_TYPE).datetime_format; + if (make_datetime(str, <ime, 0, 0, + tmp_format->format, tmp_format->format_length, 1)) + return str; + + null_value= 1; +null_date: + return (String*) 0; } @@ -879,9 +1359,7 @@ String *Item_func_date_format::val_str(String *str) { String *format; TIME l_time; - char intbuff[15]; uint size,weekday; - ulong length; if (!date_or_time) { @@ -892,24 +1370,17 @@ String *Item_func_date_format::val_str(String *str) { String *res; if (!(res=args[0]->val_str(str))) - { - null_value=1; - return 0; - } - if (str_to_time(res->ptr(),res->length(),&l_time)) - { - null_value=1; - return 0; - } + goto null_date; + + if (str_to_time(res->ptr(),res->length(),&l_time, thd)) + goto null_date; + l_time.year=l_time.month=l_time.day=0; null_value=0; } if (!(format = args[1]->val_str(str)) || !format->length()) - { - null_value=1; - return 0; - } + goto null_date; if (fixed_length) size=max_length; @@ -918,237 +1389,53 @@ String *Item_func_date_format::val_str(String *str) if (format == str) str= &value; // Save result here if (str->alloc(size)) - { - null_value=1; - return 0; - } - str->length(0); + goto null_date; + /* Create the result string */ - const char *ptr=format->ptr(); - const char *end=ptr+format->length(); - for (; ptr != end ; ptr++) - { - if (*ptr != '%' || ptr+1 == end) - str->append(*ptr); - else - { - switch (*++ptr) { - case 'M': - if (!l_time.month) - { - null_value=1; - return 0; - } - str->append(month_names[l_time.month-1]); - break; - case 'b': - if (!l_time.month) - { - null_value=1; - return 0; - } - str->append(month_names[l_time.month-1].ptr(),3); - break; - case 'W': - if (date_or_time) - { - null_value=1; - return 0; - } - weekday=calc_weekday(calc_daynr(l_time.year,l_time.month,l_time.day),0); - str->append(day_names[weekday]); - break; - case 'a': - if (date_or_time) - { - null_value=1; - return 0; - } - weekday=calc_weekday(calc_daynr(l_time.year,l_time.month,l_time.day),0); - str->append(day_names[weekday].ptr(),3); - break; - case 'D': - if (date_or_time) - { - null_value=1; - return 0; - } - length= int10_to_str(l_time.day, intbuff, 10) - intbuff; - str->append_with_prefill(intbuff, length, 1, '0'); - if (l_time.day >= 10 && l_time.day <= 19) - str->append("th"); - else - { - switch (l_time.day %10) { - case 1: - str->append("st",2); - break; - case 2: - str->append("nd",2); - break; - case 3: - str->append("rd",2); - break; - default: - str->append("th",2); - break; - } - } - break; - case 'Y': - length= int10_to_str(l_time.year, intbuff, 10) - intbuff; - str->append_with_prefill(intbuff, length, 4, '0'); - break; - case 'y': - length= int10_to_str(l_time.year%100, intbuff, 10) - intbuff; - str->append_with_prefill(intbuff, length, 2, '0'); - break; - case 'm': - length= int10_to_str(l_time.month, intbuff, 10) - intbuff; - str->append_with_prefill(intbuff, length, 2, '0'); - break; - case 'c': - length= int10_to_str(l_time.month, intbuff, 10) - intbuff; - str->append_with_prefill(intbuff, length, 1, '0'); - break; - case 'd': - length= int10_to_str(l_time.day, intbuff, 10) - intbuff; - str->append_with_prefill(intbuff, length, 2, '0'); - break; - case 'e': - length= int10_to_str(l_time.day, intbuff, 10) - intbuff; - str->append_with_prefill(intbuff, length, 1, '0'); - break; - case 'f': - length= int10_to_str(l_time.second_part, intbuff, 10) - intbuff; - str->append_with_prefill(intbuff, length, 6, '0'); - break; - case 'H': - length= int10_to_str(l_time.hour, intbuff, 10) - intbuff; - str->append_with_prefill(intbuff, length, 2, '0'); - break; - case 'h': - case 'I': - length= int10_to_str((l_time.hour+11)%12+1, intbuff, 10) - intbuff; - str->append_with_prefill(intbuff, length, 2, '0'); - break; - case 'i': /* minutes */ - length= int10_to_str(l_time.minute, intbuff, 10) - intbuff; - str->append_with_prefill(intbuff, length, 2, '0'); - break; - case 'j': - if (date_or_time) - { - null_value=1; - return 0; - } - length= int10_to_str(calc_daynr(l_time.year,l_time.month,l_time.day) - - calc_daynr(l_time.year,1,1) + 1, intbuff, 10) - intbuff; - str->append_with_prefill(intbuff, length, 3, '0'); - break; - case 'k': - length= int10_to_str(l_time.hour, intbuff, 10) - intbuff; - str->append_with_prefill(intbuff, length, 1, '0'); - break; - case 'l': - length= int10_to_str((l_time.hour+11)%12+1, intbuff, 10) - intbuff; - str->append_with_prefill(intbuff, length, 1, '0'); - break; - case 'p': - str->append(l_time.hour < 12 ? "AM" : "PM",2); - break; - case 'r': - length= my_sprintf(intbuff, - (intbuff, - (l_time.hour < 12) ? "%02d:%02d:%02d AM" : "%02d:%02d:%02d PM", - (l_time.hour+11)%12+1, - l_time.minute, - l_time.second)); - str->append(intbuff, length); - break; - case 'S': - case 's': - length= int10_to_str(l_time.second, intbuff, 10) - intbuff; - str->append_with_prefill(intbuff, length, 2, '0'); - break; - case 'T': - length= my_sprintf(intbuff, - (intbuff, - "%02d:%02d:%02d", - l_time.hour, - l_time.minute, - l_time.second)); - str->append(intbuff, length); - break; - case 'U': - case 'u': - { - uint year; - length= int10_to_str(calc_week(&l_time, 0, (*ptr) == 'U', &year), - intbuff, 10) - intbuff; - str->append_with_prefill(intbuff, length, 2, '0'); - } - break; - case 'v': - case 'V': - { - uint year; - length= int10_to_str(calc_week(&l_time, 1, (*ptr) == 'V', &year), - intbuff, 10) - intbuff; - str->append_with_prefill(intbuff, length, 2, '0'); - } - break; - case 'x': - case 'X': - { - uint year; - (void) calc_week(&l_time, 1, (*ptr) == 'X', &year); - length= int10_to_str(year, intbuff, 10) - intbuff; - str->append_with_prefill(intbuff, length, 4, '0'); - } - break; - case 'w': - weekday=calc_weekday(calc_daynr(l_time.year,l_time.month,l_time.day),1); - length= int10_to_str(weekday, intbuff, 10) - intbuff; - str->append_with_prefill(intbuff, length, 1, '0'); + if (make_datetime(str, &l_time, 0, 0, + format->ptr(), format->length(), 1)) + return str; - break; - default: - str->append(*ptr); - break; - } - } - } - return str; +null_date: + null_value=1; + return 0; } String *Item_func_from_unixtime::val_str(String *str) { struct tm tm_tmp,*start; + DATETIME_FORMAT *tmp_format; time_t tmp=(time_t) args[0]->val_int(); uint32 l; CHARSET_INFO *cs=default_charset(); + TIME ltime; if ((null_value=args[0]->null_value)) - return 0; + goto null_date; + localtime_r(&tmp,&tm_tmp); start=&tm_tmp; - + + ltime.year= start->tm_year+1900; + ltime.month= start->tm_mon+1; + ltime.day= start->tm_mday; + ltime.hour= start->tm_hour; + ltime.minute= start->tm_min; + ltime.second= start->tm_sec; + ltime.second_part= 0; + ltime.neg=0; + l=20*cs->mbmaxlen+32; - if (str->alloc(l)) - return str; /* purecov: inspected */ - l=cs->cset->snprintf(cs,(char*) str->ptr(),l,"%04d-%02d-%02d %02d:%02d:%02d", - (int) start->tm_year+1900, - (int) start->tm_mon+1, - (int) start->tm_mday, - (int) start->tm_hour, - (int) start->tm_min, - (int) start->tm_sec); - str->length(l); - str->set_charset(cs); - return str; + tmp_format= &t_datetime_frm(thd, DATETIME_FORMAT_TYPE).datetime_format; + if (str->alloc(l) && make_datetime(str, <ime, 1, 0, + tmp_format->format, + tmp_format->format_length, 1)) + return str; + null_value= 1; +null_date: + return 0; } @@ -1229,7 +1516,7 @@ bool Item_date_add_interval::get_date(TIME *ltime, bool fuzzy_date) { long period,sign; INTERVAL interval; - + ltime->neg= 0; if (args[0]->get_date(ltime,0) || get_interval_value(args[1],int_type,&value,&interval)) goto null_date; @@ -1329,19 +1616,17 @@ bool Item_date_add_interval::get_date(TIME *ltime, bool fuzzy_date) String *Item_date_add_interval::val_str(String *str) { TIME ltime; - enum date_time_format_types format; + DATETIME_FORMAT *tmp_format; if (Item_date_add_interval::get_date(<ime,0)) return 0; if (ltime.time_type == TIMESTAMP_DATE) - format= DATE_ONLY; - else if (ltime.second_part) - format= DATE_TIME_MICROSECOND; - else - format= DATE_TIME; - - if (make_datetime(str, <ime, format)) + tmp_format= &t_datetime_frm(thd, DATE_FORMAT_TYPE).datetime_format; + else + tmp_format= &t_datetime_frm(thd, DATETIME_FORMAT_TYPE).datetime_format; + if (make_datetime(str, <ime, 1, ltime.second_part, + tmp_format->format, tmp_format->format_length, 1)) return str; null_value=1; @@ -1400,7 +1685,7 @@ longlong Item_extract::val_int() else { String *res= args[0]->val_str(&value); - if (!res || str_to_time(res->ptr(),res->length(),<ime)) + if (!res || str_to_time(res->ptr(),res->length(),<ime, thd)) { null_value=1; return 0; @@ -1408,7 +1693,6 @@ longlong Item_extract::val_int() neg= ltime.neg ? -1 : 1; null_value=0; } - switch (int_type) { case INTERVAL_YEAR: return ltime.year; case INTERVAL_YEAR_MONTH: return ltime.year*100L+ltime.month; @@ -1530,10 +1814,12 @@ void Item_char_typecast::fix_length_and_dec() String *Item_datetime_typecast::val_str(String *str) { TIME ltime; + DATETIME_FORMAT *tmp_format= (&t_datetime_frm + (thd, DATETIME_FORMAT_TYPE).datetime_format); if (!get_arg0_date(<ime,1) && - make_datetime(str, <ime, ltime.second_part ? - DATE_TIME_MICROSECOND : DATE_TIME)) + make_datetime(str, <ime, 1, ltime.second_part, + tmp_format->format, tmp_format->format_length, 1)) return str; null_date: @@ -1553,9 +1839,12 @@ bool Item_time_typecast::get_time(TIME *ltime) String *Item_time_typecast::val_str(String *str) { TIME ltime; + DATETIME_FORMAT *tmp_format= (&t_datetime_frm + (thd, TIME_FORMAT_TYPE).datetime_format); if (!get_arg0_time(<ime) && - make_datetime(str, <ime, ltime.second_part ? TIME_MICROSECOND : TIME_ONLY)) + make_datetime(str, <ime, 0, ltime.second_part, + tmp_format->format, tmp_format->format_length, 1)) return str; null_value=1; @@ -1574,9 +1863,12 @@ bool Item_date_typecast::get_date(TIME *ltime, bool fuzzy_date) String *Item_date_typecast::val_str(String *str) { TIME ltime; + DATETIME_FORMAT *tmp_format= (&t_datetime_frm + (thd, DATE_FORMAT_TYPE).datetime_format); if (!get_arg0_date(<ime,1) && - make_datetime(str, <ime, DATE_ONLY)) + make_datetime(str, <ime, 1, 0, + tmp_format->format, tmp_format->format_length, 1)) return str; null_date: @@ -1605,7 +1897,11 @@ String *Item_func_makedate::val_str(String *str) { null_value=0; get_date_from_daynr(days,&l_time.year,&l_time.month,&l_time.day); - if (make_datetime(str, &l_time, DATE_ONLY)) + + DATETIME_FORMAT *tmp_format= (&t_datetime_frm + (thd, DATE_FORMAT_TYPE).datetime_format); + if (make_datetime(str, &l_time, 1, 0, + tmp_format->format, tmp_format->format_length, 1)) return str; } @@ -1656,6 +1952,7 @@ String *Item_func_add_time::val_str(String *str) bool is_time= 0; long microseconds, seconds, days= 0; int l_sign= sign; + DATETIME_FORMAT *tmp_format; null_value=0; l_time3.neg= 0; @@ -1720,19 +2017,21 @@ String *Item_func_add_time::val_str(String *str) calc_time_from_sec(&l_time3, seconds, microseconds); if (!is_time) { + tmp_format= &t_datetime_frm(thd, DATETIME_FORMAT_TYPE).datetime_format; get_date_from_daynr(days,&l_time3.year,&l_time3.month,&l_time3.day); if (l_time3.day && - make_datetime(str, &l_time3, - l_time1.second_part || l_time2.second_part ? - DATE_TIME_MICROSECOND : DATE_TIME)) + make_datetime(str, &l_time3, 1, + l_time1.second_part || l_time2.second_part, + tmp_format->format, tmp_format->format_length, 1)) return str; goto null_date; } - + + tmp_format= &t_datetime_frm(thd, TIME_FORMAT_TYPE).datetime_format; l_time3.hour+= days*24; - if (make_datetime(str, &l_time3, - l_time1.second_part || l_time2.second_part ? - TIME_MICROSECOND : TIME_ONLY)) + if (make_datetime(str, &l_time3, 0, + l_time1.second_part || l_time2.second_part, + tmp_format->format, tmp_format->format_length, 1)) return str; null_date: @@ -1755,6 +2054,7 @@ String *Item_func_timediff::val_str(String *str) long days; int l_sign= 1; TIME l_time1 ,l_time2, l_time3; + DATETIME_FORMAT *tmp_format; null_value= 0; if (args[0]->get_time(&l_time1) || @@ -1800,9 +2100,11 @@ String *Item_func_timediff::val_str(String *str) l_time3.neg= l_time3.neg ? 0 : 1; calc_time_from_sec(&l_time3, seconds, microseconds); - if (make_datetime(str, &l_time3, - l_time1.second_part || l_time2.second_part ? - TIME_MICROSECOND : TIME_ONLY)) + + tmp_format= &t_datetime_frm(thd, TIME_FORMAT_TYPE).datetime_format; + if (make_datetime(str, &l_time3, 0, + l_time1.second_part || l_time2.second_part, + tmp_format->format, tmp_format->format_length, 1)) return str; null_date: @@ -1819,6 +2121,7 @@ null_date: String *Item_func_maketime::val_str(String *str) { TIME ltime; + DATETIME_FORMAT *tmp_format; long hour= args[0]->val_int(); long minute= args[1]->val_int(); @@ -1840,7 +2143,9 @@ String *Item_func_maketime::val_str(String *str) ltime.hour= (ulong)hour; ltime.minute= (ulong)minute; ltime.second= (ulong)second; - if (make_datetime(str, <ime, TIME_ONLY)) + tmp_format= &t_datetime_frm(thd, TIME_FORMAT_TYPE).datetime_format; + if (make_datetime(str, <ime, 0, 0, + tmp_format->format, tmp_format->format_length, 1)) return str; null_date: @@ -1860,3 +2165,79 @@ longlong Item_func_microsecond::val_int() return ltime.second_part; return 0; } + +/* + Array of MySQL date/time/datetime formats + Firts element is date format + Second element is time format + Third element is datetime format + Fourth is format name. +*/ + +const char *datetime_formats[4][5]= +{ + {"%m.%d.%Y", "%Y-%m-%d", "%Y-%m-%d", "%d.%m.%Y", "%Y%m%d"}, + {"%h:%i:%s %p", "%H:%i:%s", "%H:%i:%s", "%H.%i.%S", "%H%i%s"}, + {"%Y-%m-%d-%H.%i.%s", "%Y-%m-%d %H:%i:%s", "%Y-%m-%d %H:%i:%s", "%Y-%m-%d-%H.%i.%s", "%Y%m%d%H%i%s"}, + {"USA", "JIS", "ISO", "EUR", "INTERNAL"} +}; + + +/* + Return format string according format name. + If name is unknown, result is ISO format string +*/ + +String *Item_func_get_format::val_str(String *str) +{ + String *val=args[0]->val_str(str); + const char *format_str= datetime_formats[tm_format][ISO_FORMAT]; + + if (!args[0]->null_value) + { + const char *val_ptr= val->ptr(); + uint val_len= val->length(); + for (int i= 0; i < 5; i++) + { + const char *name_format_str= datetime_formats[3][i]; + uint format_str_len= strlen(name_format_str); + if ( val_len == format_str_len && + !my_strnncoll(&my_charset_latin1, + (const uchar *) val_ptr, val_len, + (const uchar *) name_format_str, format_str_len)) + { + format_str= datetime_formats[tm_format][i]; + break; + } + } + } + + null_value= 0; + str->length(0); + str->append(format_str); + return str; +} + + +String *Item_func_str_to_date::val_str(String *str) +{ + TIME ltime; + bzero((char*) <ime, sizeof(ltime)); + DATETIME_FORMAT *tmp_format; + String *val=args[0]->val_str(str); + String *format=args[1]->val_str(str); + if (args[0]->null_value || args[1]->null_value || + extract_datetime(val->ptr(), val->length(), + format->ptr(), val->length(), + <ime)) + goto null_date; + + tmp_format= &t_datetime_frm(thd, DATETIME_FORMAT_TYPE).datetime_format; + if (make_datetime(str, <ime, 0, 0, tmp_format->format, + tmp_format->format_length, 1)) + return str; + +null_date: + null_value=1; + return 0; +} diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 6dcf7d00ce1..b7bf294b83d 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -763,3 +763,46 @@ public: maybe_null=1; } }; + + +enum datetime_format +{ + USA_FORMAT, JIS_FORMAT, ISO_FORMAT, EUR_FORMAT, INTERNAL_FORMAT +}; + + +enum datetime_format_types +{ + DATE_FORMAT_TYPE= 0, TIME_FORMAT_TYPE, DATETIME_FORMAT_TYPE +}; + + +class Item_func_get_format :public Item_str_func +{ + const datetime_format_types tm_format; +public: + Item_func_get_format(datetime_format_types type_arg1, Item *a) + :Item_str_func(a), tm_format(type_arg1) {} + String *val_str(String *str); + const char *func_name() const { return "get_format"; } + void fix_length_and_dec() + { + decimals=0; + max_length=17*MY_CHARSET_BIN_MB_MAXLEN; + } +}; + + +class Item_func_str_to_date :public Item_str_func +{ +public: + Item_func_str_to_date(Item *a, Item *b) + :Item_str_func(a, b) {} + String *val_str(String *str); + const char *func_name() const { return "str_to_date"; } + void fix_length_and_dec() + { + decimals=0; + max_length=29*MY_CHARSET_BIN_MB_MAXLEN; + } +}; diff --git a/sql/lex.h b/sql/lex.h index f8bb34c5c5b..5d79e378d4f 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -177,6 +177,7 @@ static SYMBOL symbols[] = { { "FUNCTION", SYM(UDF_SYM),0,0}, { "GEOMETRY", SYM(GEOMETRY_SYM),0,0}, { "GEOMETRYCOLLECTION",SYM(GEOMETRYCOLLECTION),0,0}, + { "GET_FORMAT", SYM(GET_FORMAT),0,0}, { "GLOBAL", SYM(GLOBAL_SYM),0,0}, { "GRANT", SYM(GRANT),0,0}, { "GRANTS", SYM(GRANTS),0,0}, @@ -640,6 +641,7 @@ static SYMBOL sql_functions[] = { { "STARTPOINT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_startpoint)}, { "STD", SYM(STD_SYM),0,0}, { "STDDEV", SYM(STD_SYM),0,0}, + { "STR_TO_DATE", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_str_to_date)}, { "STRCMP", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_strcmp)}, { "SUBSTR", SYM(SUBSTRING),0,0}, { "SUBSTRING", SYM(SUBSTRING),0,0}, diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 6c1f7bfa472..c8d793ede29 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -822,7 +822,7 @@ extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open, LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone, LOCK_slave_list, LOCK_active_mi, LOCK_manager, LOCK_global_system_variables, LOCK_user_conn; -extern rw_lock_t LOCK_grant; +extern rw_lock_t LOCK_grant; extern pthread_cond_t COND_refresh, COND_thread_count, COND_manager; extern pthread_attr_t connection_attrib; extern I_List<THD> threads; @@ -839,6 +839,14 @@ extern SHOW_COMP_OPTION have_berkeley_db; extern struct system_variables global_system_variables; extern struct system_variables max_system_variables; extern struct rand_struct sql_rand; + +#define g_datetime_frm(a) (global_system_variables.datetime_formats[(a)]) +#define t_datetime_frm(a, b) ((a)->variables.datetime_formats[(b)]) + +extern const char *datetime_formats[4][5]; +extern const char *opt_datetime_format_names[3]; +extern const char *opt_datetime_formats[3]; + extern String null_string; extern HASH open_cache; extern TABLE *unused_tables; @@ -909,14 +917,24 @@ void get_date_from_daynr(long daynr,uint *year, uint *month, uint *day); void init_time(void); long my_gmt_sec(TIME *, long *current_timezone); -time_t str_to_timestamp(const char *str,uint length); -bool str_to_time(const char *str,uint length,TIME *l_time); -longlong str_to_datetime(const char *str,uint length,bool fuzzy_date); +time_t str_to_timestamp(const char *str,uint length, THD *thd); +bool str_to_time(const char *str,uint length,TIME *l_time, THD *thd); +longlong str_to_datetime(const char *str,uint length,bool fuzzy_date, THD *thd); timestamp_type str_to_TIME(const char *str, uint length, TIME *l_time, - bool fuzzy_date); + bool fuzzy_date, THD *thd); void localtime_to_TIME(TIME *to, struct tm *from); void calc_time_from_sec(TIME *to, long seconds, long microseconds); +extern DATETIME_FORMAT *make_format(DATETIME_FORMAT *datetime_format, + datetime_format_types format_type, + const char *format_str, + uint format_length, bool is_alloc); +extern String *make_datetime(String *str, TIME *l_time, + const bool is_time_only, + const bool add_second_frac, + const char *ptr, uint format_length, + bool set_len_to_zero); + int test_if_number(char *str,int *res,bool allow_wildcards); void change_byte(byte *,uint,char,char); extern "C" void unireg_abort(int exit_code); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 812a5b28ff7..8a8c3a6c32d 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -319,6 +319,12 @@ char* log_error_file_ptr= log_error_file; char mysql_real_data_home[FN_REFLEN], language[LIBLEN],reg_ext[FN_EXTLEN], mysql_charsets_dir[FN_REFLEN], max_sort_char,*mysqld_user,*mysqld_chroot, *opt_init_file; + +const char *opt_datetime_formats[3]; +const char *opt_datetime_format_names[3]= {"date_format", + "time_format", + "datetime_format"}; + char *language_ptr, *default_collation_name, *default_character_set_name; char mysql_data_home_buff[2], *mysql_data_home=mysql_real_data_home; char server_version[SERVER_VERSION_LENGTH]=MYSQL_SERVER_VERSION; @@ -906,6 +912,9 @@ void clean_up(bool print_message) #ifdef USE_RAID end_raid(); #endif + g_datetime_frm(DATE_FORMAT_TYPE).clean(); + g_datetime_frm(TIME_FORMAT_TYPE).clean(); + g_datetime_frm(DATETIME_FORMAT_TYPE).clean(); if (defaults_argv) free_defaults(defaults_argv); free_tmpdir(&mysql_tmpdir_list); @@ -1998,6 +2007,36 @@ bool open_log(MYSQL_LOG *log, const char *hostname, } +int init_global_datetime_format(datetime_format_types format_type, bool is_alloc) +{ + const char *format_str= opt_datetime_formats[format_type]; + uint format_length= 0; + DATETIME_FORMAT *tmp_format= &g_datetime_frm(format_type).datetime_format; + + if (format_str) + { + format_str= opt_datetime_formats[format_type]; + format_length= strlen(format_str); + } + else + { + format_str= datetime_formats[format_type][ISO_FORMAT]; + format_length= strlen(datetime_formats[format_type][ISO_FORMAT]); + opt_datetime_formats[format_type]= format_str; + } + if (make_format(tmp_format, format_type, format_str, + format_length, is_alloc)) + { + g_datetime_frm(format_type).name= opt_datetime_format_names[format_type]; + g_datetime_frm(format_type).name_length= + strlen(opt_datetime_format_names[format_type]); + g_datetime_frm(format_type).format_type= format_type; + return 0; + } + return 1; +} + + static int init_common_variables(const char *conf_file_name, int argc, char **argv, const char **groups) { @@ -2113,6 +2152,12 @@ static int init_common_variables(const char *conf_file_name, int argc, global_system_variables.collation_connection= default_charset_info; global_system_variables.character_set_results= default_charset_info; global_system_variables.character_set_client= default_charset_info; + global_system_variables.collation_connection= default_charset_info; + + if (init_global_datetime_format(DATE_FORMAT_TYPE, 1) || + init_global_datetime_format(TIME_FORMAT_TYPE, 1) || + init_global_datetime_format(DATETIME_FORMAT_TYPE, 1)) + return 1; if (use_temp_pool && bitmap_init(&temp_pool,1024,1)) return 1; @@ -3578,6 +3623,9 @@ enum options_mysqld OPT_GROUP_CONCAT_MAX_LEN, OPT_DEFAULT_COLLATION, OPT_SECURE_AUTH, + OPT_DATE_FORMAT, + OPT_TIME_FORMAT, + OPT_DATETIME_FORMAT, OPT_LOG_QUERIES_NOT_USING_INDEXES }; @@ -4530,6 +4578,21 @@ The minimum value for this variable is 4096.", (gptr*) &global_system_variables.default_week_format, (gptr*) &max_system_variables.default_week_format, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 3L, 0, 1, 0}, + { "date-format", OPT_DATE_FORMAT, + "The DATE format.", + (gptr*) &opt_datetime_formats[DATE_FORMAT_TYPE], + (gptr*) &opt_datetime_formats[DATE_FORMAT_TYPE], + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + { "datetime-format", OPT_DATETIME_FORMAT, + "The DATETIME/TIMESTAMP format.", + (gptr*) &opt_datetime_formats[DATETIME_FORMAT_TYPE], + (gptr*) &opt_datetime_formats[DATETIME_FORMAT_TYPE], + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + { "time-format", OPT_TIME_FORMAT, + "The TIME format.", + (gptr*) &opt_datetime_formats[TIME_FORMAT_TYPE], + (gptr*) &opt_datetime_formats[TIME_FORMAT_TYPE], + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; @@ -4907,6 +4970,10 @@ static void mysql_init_variables(void) max_system_variables.max_join_size= (ulonglong) HA_POS_ERROR; global_system_variables.old_passwords= 0; + init_global_datetime_format(DATE_FORMAT_TYPE, 0); + init_global_datetime_format(TIME_FORMAT_TYPE, 0); + init_global_datetime_format(DATETIME_FORMAT_TYPE, 0); + /* Variables that depends on compile options */ #ifndef DBUG_OFF default_dbug_option=IF_WIN("d:t:i:O,\\mysqld.trace", diff --git a/sql/protocol.cc b/sql/protocol.cc index 79420fb71d5..0fe759cff67 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -833,17 +833,12 @@ bool Protocol_simple::store(TIME *tm) field_pos++; #endif char buff[40]; - uint length; - length= my_sprintf(buff,(buff, "%04d-%02d-%02d %02d:%02d:%02d", - (int) tm->year, - (int) tm->month, - (int) tm->day, - (int) tm->hour, - (int) tm->minute, - (int) tm->second)); - if (tm->second_part) - length+= my_sprintf(buff+length,(buff+length, ".%06d", (int)tm->second_part)); - return net_store_data((char*) buff, length); + String tmp((char*) buff,sizeof(buff),&my_charset_bin); + DATETIME_FORMAT *tmp_format= (&t_datetime_frm + (current_thd, DATETIME_FORMAT_TYPE).datetime_format); + make_datetime(&tmp, tm, 1, tm->second_part, + tmp_format->format, tmp_format->format_length, 1); + return net_store_data((char*) tmp.ptr(), tmp.length()); } @@ -855,12 +850,12 @@ bool Protocol_simple::store_date(TIME *tm) field_pos++; #endif char buff[40]; - uint length; - length= my_sprintf(buff,(buff, "%04d-%02d-%02d", - (int) tm->year, - (int) tm->month, - (int) tm->day)); - return net_store_data((char*) buff, length); + String tmp((char*) buff,sizeof(buff),&my_charset_bin); + DATETIME_FORMAT *tmp_format= (&t_datetime_frm + (current_thd, DATE_FORMAT_TYPE).datetime_format); + make_datetime(&tmp, tm, 1, 0, + tmp_format->format, tmp_format->format_length, 1); + return net_store_data((char*) tmp.ptr(), tmp.length()); } @@ -878,16 +873,14 @@ bool Protocol_simple::store_time(TIME *tm) field_pos++; #endif char buff[40]; - uint length; + String tmp((char*) buff,sizeof(buff),&my_charset_bin); + DATETIME_FORMAT *tmp_format= (&t_datetime_frm + (current_thd, TIME_FORMAT_TYPE).datetime_format); uint day= (tm->year || tm->month) ? 0 : tm->day; - length= my_sprintf(buff,(buff, "%s%02ld:%02d:%02d", - tm->neg ? "-" : "", - (long) day*24L+(long) tm->hour, - (int) tm->minute, - (int) tm->second)); - if (tm->second_part) - length+= my_sprintf(buff+length,(buff+length, ".%06d", (int)tm->second_part)); - return net_store_data((char*) buff, length); + tm->hour= (long) day*24L+(long) tm->hour; + make_datetime(&tmp, tm, 0, tm->second_part, + tmp_format->format, tmp_format->format_length, 1); + return net_store_data((char*) tmp.ptr(), tmp.length()); } diff --git a/sql/set_var.cc b/sql/set_var.cc index 582010a4602..48580960399 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -403,6 +403,9 @@ sys_var *sys_variables[]= &sys_collation_server, &sys_concurrent_insert, &sys_connect_timeout, + &g_datetime_frm(DATE_FORMAT_TYPE), + &g_datetime_frm(DATETIME_FORMAT_TYPE), + &g_datetime_frm(TIME_FORMAT_TYPE), &sys_default_week_format, &sys_delay_key_write, &sys_delayed_insert_limit, @@ -540,6 +543,8 @@ struct show_var_st init_vars[]= { {sys_concurrent_insert.name,(char*) &sys_concurrent_insert, SHOW_SYS}, {sys_connect_timeout.name, (char*) &sys_connect_timeout, SHOW_SYS}, {"datadir", mysql_real_data_home, SHOW_CHAR}, + {"date_format", (char*) &g_datetime_frm(DATE_FORMAT_TYPE), SHOW_SYS}, + {"datetime_format", (char*) &g_datetime_frm(DATETIME_FORMAT_TYPE), SHOW_SYS}, {"default_week_format", (char*) &sys_default_week_format, SHOW_SYS}, {sys_delay_key_write.name, (char*) &sys_delay_key_write, SHOW_SYS}, {sys_delayed_insert_limit.name, (char*) &sys_delayed_insert_limit,SHOW_SYS}, @@ -696,6 +701,7 @@ struct show_var_st init_vars[]= { #endif {"thread_stack", (char*) &thread_stack, SHOW_LONG}, {sys_tx_isolation.name, (char*) &sys_tx_isolation, SHOW_SYS}, + {"time_format", (char*) &g_datetime_frm(TIME_FORMAT_TYPE), SHOW_SYS}, #ifdef HAVE_TZNAME {"timezone", time_zone, SHOW_CHAR}, #endif @@ -709,9 +715,75 @@ struct show_var_st init_vars[]= { {NullS, NullS, SHOW_LONG} }; + /* Functions to check and update variables */ +char *update_datetime_format(THD *thd, enum enum_var_type type, + enum datetime_format_types format_type, + DATETIME_FORMAT *tmp_format) +{ + char *old_value; + if (type == OPT_GLOBAL) + { + pthread_mutex_lock(&LOCK_global_system_variables); + old_value= g_datetime_frm(format_type).datetime_format.format; + g_datetime_frm(format_type).datetime_format= *tmp_format; + pthread_mutex_unlock(&LOCK_global_system_variables); + } + else + { + old_value= t_datetime_frm(thd,format_type).datetime_format.format; + t_datetime_frm(thd, format_type).datetime_format= *tmp_format; + } + return old_value; +} + + +bool sys_var_datetime_format::update(THD *thd, set_var *var) +{ + DATETIME_FORMAT tmp_format; + char *old_value; + uint new_length; + + if ((new_length= var->value->str_value.length())) + { + if (!make_format(&tmp_format, format_type, + var->value->str_value.ptr(), + new_length, 1)) + return 1; + } + + old_value= update_datetime_format(thd, var->type, format_type, &tmp_format); + my_free(old_value, MYF(MY_ALLOW_ZERO_PTR)); + return 0; +} + +byte *sys_var_datetime_format::value_ptr(THD *thd, enum_var_type type, + LEX_STRING *base) +{ + if (type == OPT_GLOBAL) + return (byte*) g_datetime_frm(format_type).datetime_format.format; + return (byte*) t_datetime_frm(thd, format_type).datetime_format.format; +} + +void sys_var_datetime_format::set_default(THD *thd, enum_var_type type) +{ + DATETIME_FORMAT tmp_format; + char *old_value; + uint new_length; + + if ((new_length= strlen(opt_datetime_formats[format_type]))) + { + if (!make_format(&tmp_format, format_type, + opt_datetime_formats[format_type], + new_length, 1)) + return; + } + + old_value= update_datetime_format(thd, type, format_type, &tmp_format); + my_free(old_value, MYF(MY_ALLOW_ZERO_PTR)); +} /* The following 3 functions need to be changed in 4.1 when we allow diff --git a/sql/set_var.h b/sql/set_var.h index 70667c719e5..4c67c5ccc59 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -49,6 +49,8 @@ public: const char *name; sys_after_update_func after_update; + sys_var() + {} sys_var(const char *name_arg) :name(name_arg),after_update(0) {} sys_var(const char *name_arg,sys_after_update_func func) @@ -188,6 +190,9 @@ public: class sys_var_thd :public sys_var { public: + sys_var_thd() + :sys_var() + {} sys_var_thd(const char *name_arg) :sys_var(name_arg) {} @@ -555,6 +560,51 @@ public: }; +class sys_var_datetime_format :public sys_var_thd +{ +public: + enum datetime_format_types format_type; + DATETIME_FORMAT datetime_format; + sys_var_datetime_format(): sys_var_thd() + {} + + void clean() + { + my_free(datetime_format.format, MYF(MY_ALLOW_ZERO_PTR)); + datetime_format.format=0; + } + + /* + It's for copying of global_system_variables structure + in THD constructor. + */ + inline sys_var_datetime_format& operator= (sys_var_datetime_format& s) + { + if (&s != this) + { + name= s.name; name_length= s.name_length; + datetime_format= s.datetime_format; + datetime_format.format= (my_strdup_with_length + (s.datetime_format.format, + s.datetime_format. + format_length, MYF(0))); + format_type= s.format_type; + } + return *this; + } + + SHOW_TYPE type() { return SHOW_CHAR; } + bool check_update_type(Item_result type) + { + return type != STRING_RESULT; /* Only accept strings */ + } + bool check_default(enum_var_type type) { return 0; } + bool update(THD *thd, set_var *var); + byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); + void set_default(THD *thd, enum_var_type type); +}; + + /* Variable that you can only read from */ class sys_var_readonly: public sys_var diff --git a/sql/sql_class.cc b/sql/sql_class.cc index e240aed7e9f..588031643d4 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -279,6 +279,9 @@ void THD::cleanup(void) close_thread_tables(this); } close_temporary_tables(this); + variables.datetime_formats[DATE_FORMAT_TYPE].clean(); + variables.datetime_formats[TIME_FORMAT_TYPE].clean(); + variables.datetime_formats[DATETIME_FORMAT_TYPE].clean(); delete_dynamic(&user_var_events); hash_free(&user_vars); if (global_read_lock) diff --git a/sql/sql_class.h b/sql/sql_class.h index f4687a9aa74..06bc29dbb2a 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -421,6 +421,7 @@ struct system_variables CHARSET_INFO *collation_server; CHARSET_INFO *collation_database; CHARSET_INFO *collation_connection; + sys_var_datetime_format datetime_formats[3]; }; void free_tmp_table(THD *thd, TABLE *entry); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 7286be82706..8491ba7675d 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -78,6 +78,7 @@ inline Item *or_or_concat(THD *thd, Item* A, Item* B) CHARSET_INFO *charset; thr_lock_type lock_type; interval_type interval; + datetime_format_types datetime_format_type; st_select_lex *select_lex; chooser_compare_func_creator boolfunc2creator; } @@ -482,6 +483,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token GEOMETRYCOLLECTION %token GROUP_CONCAT_SYM %token GROUP_UNIQUE_USERS +%token GET_FORMAT %token HOUR_MICROSECOND_SYM %token HOUR_MINUTE_SYM %token HOUR_SECOND_SYM @@ -644,6 +646,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); UDF_CHAR_FUNC UDF_FLOAT_FUNC UDF_INT_FUNC UDA_CHAR_SUM UDA_FLOAT_SUM UDA_INT_SUM +%type <datetime_format_type> datetime_format_type; %type <interval> interval %type <db_type> table_types @@ -2581,6 +2584,8 @@ simple_expr: { $$= new Item_func_spatial_collection(* $3, Geometry::wkbGeometryCollection, Geometry::wkbPoint); } + | GET_FORMAT '(' datetime_format_type ',' expr ')' + { $$= new Item_func_get_format($3, $5); } | HOUR_SYM '(' expr ')' { $$= new Item_func_hour($3); } | IF '(' expr ',' expr ',' expr ')' @@ -3194,6 +3199,11 @@ interval: | YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; } | YEAR_SYM { $$=INTERVAL_YEAR; }; +datetime_format_type: + DATE_SYM {$$=DATE_FORMAT_TYPE;} + | TIME_SYM {$$=TIME_FORMAT_TYPE;} + | DATETIME {$$=DATETIME_FORMAT_TYPE;}; + table_alias: /* empty */ | AS @@ -4541,6 +4551,7 @@ keyword: | FLUSH_SYM {} | GEOMETRY_SYM {} | GEOMETRYCOLLECTION {} + | GET_FORMAT {} | GRANTS {} | GLOBAL_SYM {} | HANDLER_SYM {} diff --git a/sql/structs.h b/sql/structs.h index 05ebdba7a37..d9be230c049 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -24,6 +24,11 @@ typedef struct st_date_format { /* How to print date */ uint pos[6]; /* Positions to YY.MM.DD HH:MM:SS */ } DATE_FORMAT; +typedef struct st_datetime_format { + byte dt_pos[8]; + char *format; + uint format_length; +} DATETIME_FORMAT; typedef struct st_keyfile_info { /* used with ha_info() */ byte ref[MAX_REFLENGTH]; /* Pointer to current row */ @@ -110,8 +115,8 @@ typedef struct st_read_record { /* Parameter to read_record */ bool print_error, ignore_not_found_rows; } READ_RECORD; -enum timestamp_type { TIMESTAMP_NONE, TIMESTAMP_DATE, TIMESTAMP_FULL, - TIMESTAMP_TIME }; +enum timestamp_type { TIMESTAMP_NONE, WRONG_TIMESTAMP_FULL, TIMESTAMP_DATE, TIMESTAMP_FULL, + TIMESTAMP_TIME}; typedef struct st_time { uint year,month,day,hour,minute,second; diff --git a/sql/time.cc b/sql/time.cc index 70ae8dcd8ed..f2e41afa560 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -27,6 +27,10 @@ uchar *days_in_month= (uchar*) "\037\034\037\036\037\036\037\037\036\037\036\037 /* Init some variabels needed when using my_local_time */ /* Currently only my_time_zone is inited */ +bool parse_datetime_formats(datetime_format_types format_type, + const char *format_str, uint format_length, + byte *dt_pos); + static long my_time_zone=0; void init_time(void) @@ -316,10 +320,12 @@ ulong convert_month_to_period(ulong month) */ timestamp_type -str_to_TIME(const char *str, uint length, TIME *l_time,bool fuzzy_date) +str_to_TIME(const char *str, uint length, TIME *l_time,bool fuzzy_date,THD *thd) { - uint field_length,year_length,digits,i,number_of_fields,date[7]; + uint field_length= 0, year_length= 0, digits, i, number_of_fields; + uint date[7], date_len[7]; uint not_zero_date; + bool is_internal_format= 0; const char *pos; const char *end=str+length; bool found_delimitier= 0; @@ -336,24 +342,32 @@ str_to_TIME(const char *str, uint length, TIME *l_time,bool fuzzy_date) (YYYY-MM-DD, YYYYMMDD, YYYYYMMDDHHMMSS) */ for (pos=str; pos != end && my_isdigit(&my_charset_latin1,*pos) ; pos++) ; + /* Check for internal format */ digits= (uint) (pos-str); - year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2; - field_length=year_length-1; + + if (pos == end || digits>=12) + { + is_internal_format= 1; + year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2; + field_length=year_length-1; + date_len[0]= year_length; + } not_zero_date= 0; for (i=0 ; i < 6 && str != end && my_isdigit(&my_charset_latin1,*str) ; i++) { + if (!is_internal_format) + date_len[i]= 1; uint tmp_value=(uint) (uchar) (*str++ - '0'); - while (str != end && my_isdigit(&my_charset_latin1,str[0]) && - field_length--) + while (str != end && my_isdigit(&my_charset_latin1,str[0]) + && (is_internal_format && field_length-- || !is_internal_format) ) { tmp_value=tmp_value*10 + (uint) (uchar) (*str - '0'); str++; + if (!is_internal_format) + date_len[i]+= 1; } - if (found_delimitier && (int) field_length < 0) - { - /* The number can't match any valid date or datetime string */ + if (i == 2 && *str == '.') DBUG_RETURN(TIMESTAMP_NONE); - } date[i]=tmp_value; not_zero_date|= tmp_value; if (i == 2 && str != end && *str == 'T') @@ -371,7 +385,8 @@ str_to_TIME(const char *str, uint length, TIME *l_time,bool fuzzy_date) found_delimitier=1; // Should be a 'normal' date } } - field_length=1; // Rest fields can only be 2 + if (is_internal_format) + field_length=1; // Rest fields can only be 2 } /* Handle second fractions */ if (i == 6 && (uint) (end-str) >= 2 && *str == '.' && @@ -389,14 +404,69 @@ str_to_TIME(const char *str, uint length, TIME *l_time,bool fuzzy_date) else date[6]=0; - if (year_length == 2 && i >=2 && (date[1] || date[2])) - date[0]+= (date[0] < YY_PART_YEAR ? 2000 : 1900); + while (str != end && (my_ispunct(&my_charset_latin1,*str) || + my_isspace(&my_charset_latin1,*str))) + str++; + + uint add_hours= 0; + if (!my_strnncoll(&my_charset_latin1, + (const uchar *)str, 2, + (const uchar *)"PM", 2)) + add_hours= 12; + number_of_fields=i; while (i < 6) date[i++]=0; - if (number_of_fields < 3 || date[1] > 12 || - date[2] > 31 || date[3] > 23 || date[4] > 59 || date[5] > 59 || - (!fuzzy_date && (date[1] == 0 || date[2] == 0))) + + if (!is_internal_format) + { + byte *frm_pos; + + if (number_of_fields <= 3) + { + frm_pos= t_datetime_frm(thd, DATE_FORMAT_TYPE).datetime_format.dt_pos; + l_time->hour= 0; + l_time->minute= 0; + l_time->second= 0; + } + else + { + frm_pos= t_datetime_frm(thd, DATETIME_FORMAT_TYPE).datetime_format.dt_pos; + l_time->hour= date[(int) frm_pos[3]]; + l_time->minute=date[(int) frm_pos[4]]; + l_time->second=date[(int) frm_pos[5]]; + if (frm_pos[6] == 1) + { + if (l_time->hour > 12) + DBUG_RETURN(WRONG_TIMESTAMP_FULL); + l_time->hour= l_time->hour%12 + add_hours; + } + } + + l_time->year= date[(int) frm_pos[0]]; + l_time->month= date[(int) frm_pos[1]]; + l_time->day= date[(int) frm_pos[2]]; + year_length= date_len[(int) frm_pos[0]]; + } + else + { + l_time->year= date[0]; + l_time->month= date[1]; + l_time->day= date[2]; + l_time->hour= date[3]; + l_time->minute=date[4]; + l_time->second=date[5]; + } + l_time->second_part=date[6]; + l_time->neg= 0; + if (year_length == 2 && i >=2 && (l_time->month || l_time->day)) + l_time->year+= (l_time->year < YY_PART_YEAR ? 2000 : 1900); + + + if (number_of_fields < 3 || l_time->month > 12 || + l_time->day > 31 || l_time->hour > 23 || + l_time->minute > 59 || l_time->second > 59 || + (!fuzzy_date && (l_time->month == 0 || l_time->day == 0))) { /* Only give warning for a zero date if there is some garbage after */ if (!not_zero_date) // If zero date @@ -411,53 +481,46 @@ str_to_TIME(const char *str, uint length, TIME *l_time,bool fuzzy_date) } } if (not_zero_date) - current_thd->cuted_fields++; - DBUG_RETURN(TIMESTAMP_NONE); + thd->cuted_fields++; + DBUG_RETURN(WRONG_TIMESTAMP_FULL); } - if (str != end && current_thd->count_cuted_fields) + if (str != end && thd->count_cuted_fields) { for (; str != end ; str++) { if (!my_isspace(&my_charset_latin1,*str)) { - current_thd->cuted_fields++; + thd->cuted_fields++; break; } } } - l_time->year= date[0]; - l_time->month= date[1]; - l_time->day= date[2]; - l_time->hour= date[3]; - l_time->minute=date[4]; - l_time->second=date[5]; - l_time->second_part=date[6]; - l_time->neg= 0; + DBUG_RETURN(l_time->time_type= (number_of_fields <= 3 ? TIMESTAMP_DATE : TIMESTAMP_FULL)); } -time_t str_to_timestamp(const char *str,uint length) +time_t str_to_timestamp(const char *str,uint length, THD *thd) { TIME l_time; long not_used; - if (str_to_TIME(str,length,&l_time,0) == TIMESTAMP_NONE) + if (str_to_TIME(str,length,&l_time,0,thd) <= WRONG_TIMESTAMP_FULL) return(0); if (l_time.year >= TIMESTAMP_MAX_YEAR || l_time.year < 1900+YY_PART_YEAR) { - current_thd->cuted_fields++; + thd->cuted_fields++; return(0); } return(my_gmt_sec(&l_time, ¬_used)); } -longlong str_to_datetime(const char *str,uint length,bool fuzzy_date) +longlong str_to_datetime(const char *str,uint length,bool fuzzy_date, THD *thd) { TIME l_time; - if (str_to_TIME(str,length,&l_time,fuzzy_date) == TIMESTAMP_NONE) + if (str_to_TIME(str,length,&l_time,fuzzy_date,thd) <= WRONG_TIMESTAMP_FULL) return(0); return (longlong) (l_time.year*LL(10000000000) + l_time.month*LL(100000000)+ @@ -484,12 +547,13 @@ longlong str_to_datetime(const char *str,uint length,bool fuzzy_date) 1 error */ -bool str_to_time(const char *str,uint length,TIME *l_time) +bool str_to_time(const char *str,uint length,TIME *l_time, THD *thd) { long date[5],value; const char *end=str+length; bool found_days,found_hours; uint state; + byte *frm_pos= t_datetime_frm(thd, TIME_FORMAT_TYPE).datetime_format.dt_pos; l_time->neg=0; for (; str != end && @@ -507,8 +571,11 @@ bool str_to_time(const char *str,uint length,TIME *l_time) /* Check first if this is a full TIMESTAMP */ if (length >= 12) { // Probably full timestamp - if (str_to_TIME(str,length,l_time,1) == TIMESTAMP_FULL) - return 0; // Was an ok timestamp + enum timestamp_type tres= str_to_TIME(str,length,l_time,1,thd); + if (tres == TIMESTAMP_FULL) + return 0; + else if (tres == WRONG_TIMESTAMP_FULL) + return 1; } /* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */ @@ -533,7 +600,7 @@ bool str_to_time(const char *str,uint length,TIME *l_time) found_days=1; str++; // Skip space; } - else if ((end-str) > 1 && *str == ':' && + else if ((end-str) > 1 && *str == frm_pos[7] && my_isdigit(&my_charset_latin1,str[1])) { date[0]=0; // Assume we found hours @@ -559,8 +626,8 @@ bool str_to_time(const char *str,uint length,TIME *l_time) for (value=0; str != end && my_isdigit(&my_charset_latin1,*str) ; str++) value=value*10L + (long) (*str - '0'); date[state++]=value; - if (state == 4 || (end-str) < 2 || *str != ':' || - !my_isdigit(&my_charset_latin1,str[1])) + if (state == 4 || (end-str) < 2 || *str != frm_pos[7] || + !my_isdigit(&my_charset_latin1,str[1])) break; str++; // Skip ':' } @@ -577,7 +644,6 @@ bool str_to_time(const char *str,uint length,TIME *l_time) else bzero((char*) (date+state), sizeof(long)*(4-state)); } - fractional: /* Get fractional second part */ if ((end-str) >= 2 && *str == '.' && my_isdigit(&my_charset_latin1,str[1])) @@ -593,6 +659,20 @@ bool str_to_time(const char *str,uint length,TIME *l_time) else date[4]=0; + while (str != end && !my_isalpha(&my_charset_latin1,*str)) + str++; + + if ( (end-str)>= 2 && + !my_strnncoll(&my_charset_latin1, + (const uchar *)str, 2, + (const uchar *)"PM", 2) && + frm_pos[6] == 1) + { + uint days_i= date[1]/24; + uint hours_i= date[1]%24; + date[1]= hours_i%12 + 12 + 24*days_i; + } + /* Some simple checks */ if (date[2] >= 60 || date[3] >= 60) { @@ -601,9 +681,9 @@ bool str_to_time(const char *str,uint length,TIME *l_time) } l_time->month=0; l_time->day=date[0]; - l_time->hour=date[1]; - l_time->minute=date[2]; - l_time->second=date[3]; + l_time->hour=date[frm_pos[3] + 1]; + l_time->minute=date[frm_pos[4] + 1]; + l_time->second=date[frm_pos[5] + 1]; l_time->second_part=date[4]; l_time->time_type= TIMESTAMP_TIME; @@ -648,3 +728,165 @@ void calc_time_from_sec(TIME *to, long seconds, long microseconds) to->second= t_seconds%60L; to->second_part= microseconds; } + + +DATETIME_FORMAT *make_format(DATETIME_FORMAT *datetime_format, + datetime_format_types format_type, + const char *format_str, + uint format_length, bool is_alloc) +{ + if (format_length && + !parse_datetime_formats(format_type, format_str, + format_length, + datetime_format->dt_pos)) + { + if (is_alloc) + { + if (!(datetime_format->format= my_strdup_with_length(format_str, + format_length, + MYF(0)))) + return 0; + } + else + datetime_format->format= (char *) format_str; + datetime_format->format_length= format_length; + return datetime_format; + } + return 0; +} + + +bool parse_datetime_formats(datetime_format_types format_type, + const char *format_str, uint format_length, + byte *dt_pos) +{ + uint pos= 0; + dt_pos[0]= dt_pos[1]= dt_pos[2]= dt_pos[3]= + dt_pos[4]= dt_pos[5]= dt_pos[6]= dt_pos[7]= -1; + + const char *ptr=format_str; + const char *end=ptr+format_length; + bool need_p= 0; + + for (; ptr != end; ptr++) + { + if (*ptr == '%' && ptr+1 != end) + { + switch (*++ptr) { + case 'y': + case 'Y': + if (dt_pos[0] > -1) + return 1; + dt_pos[0]= pos; + break; + case 'c': + case 'm': + if (dt_pos[1] > -1) + return 1; + dt_pos[1]= pos; + break; + case 'd': + case 'e': + if (dt_pos[2] > -1) + return 1; + dt_pos[2]= pos; + break; + case 'H': + case 'k': + case 'h': + case 'I': + case 'l': + if (dt_pos[3] > -1) + return 1; + dt_pos[3]= pos; + need_p= (*ptr == 'h' || *ptr == 'l' || *ptr == 'I'); + break; + case 'i': + if (dt_pos[4] > -1) + return 1; + dt_pos[4]= pos; + break; + case 's': + case 'S': + if (dt_pos[5] > -1) + return 1; + dt_pos[5]= pos; + break; + case 'p': + if (dt_pos[6] > -1) + return 1; + /* %p should be last in format string */ + if (format_type == DATE_FORMAT_TYPE || + (pos != 6 && format_type == DATETIME_FORMAT_TYPE) || + (pos != 3 && format_type == TIME_FORMAT_TYPE)) + return 1; + dt_pos[6]= 1; + break; + default: + return 1; + } + if (dt_pos[6] == -1) + pos++; + } + } + + if (pos > 5 && format_type == DATETIME_FORMAT_TYPE && + (dt_pos[0] + dt_pos[1] + dt_pos[2] + + dt_pos[3] + dt_pos[4] + dt_pos[5] != 15) || + pos > 2 && format_type == DATE_FORMAT_TYPE && + (dt_pos[0] + dt_pos[1] + dt_pos[2] != 3) || + pos > 2 && format_type == TIME_FORMAT_TYPE && + (dt_pos[3] + dt_pos[4] + dt_pos[5] != 3) || + (need_p && dt_pos[6] != 1)) + return 1; + + /* + Check for valid separators between date/time parst + */ + uint tmp_len= format_length; + if (dt_pos[6] == 1) + { + end= end - 2; + if (my_ispunct(&my_charset_latin1, *end) || my_isspace(&my_charset_latin1, *end)) + end--; + tmp_len= end - format_str; + } + switch (format_type) { + case DATE_FORMAT_TYPE: + case TIME_FORMAT_TYPE: + if ((tmp_len == 6 && + !my_strnncoll(&my_charset_bin, + (const uchar *) format_str, 6, + (const uchar *) datetime_formats + [format_type][INTERNAL_FORMAT], 6)) || + tmp_len == 8 && + my_ispunct(&my_charset_latin1, *(format_str+2)) && + my_ispunct(&my_charset_latin1, *(format_str+5))) + { + if (format_type == TIME_FORMAT_TYPE && tmp_len == 8) + { + if (*(format_str+2) != *(format_str+5)) + return 1; + dt_pos[7]= *(format_str+2); + } + return 0; + } + break; + case DATETIME_FORMAT_TYPE: + if ((tmp_len == 12 && + !my_strnncoll(&my_charset_bin, + (const uchar *) format_str, 12, + (const uchar *) datetime_formats + [DATETIME_FORMAT_TYPE][INTERNAL_FORMAT], 12)) || + tmp_len == 17 && + my_ispunct(&my_charset_latin1, *(format_str+2)) && + my_ispunct(&my_charset_latin1, *(format_str+5)) && + my_ispunct(&my_charset_latin1, *(format_str+11)) && + my_ispunct(&my_charset_latin1, *(format_str+14)) && + (my_ispunct(&my_charset_latin1, *(format_str+8)) || + my_isspace(&my_charset_latin1, *(format_str+8)))) + return 0; + break; + } + return 1; +} |