diff options
-rw-r--r-- | include/mysql.h | 1 | ||||
-rw-r--r-- | include/mysql_com.h | 1 | ||||
-rw-r--r-- | libmysql/libmysql.c | 23 | ||||
-rw-r--r-- | libmysql/libmysql.def | 1 | ||||
-rw-r--r-- | mysql-test/r/cast.result | 4 | ||||
-rw-r--r-- | mysql-test/r/func_sapdb.result | 200 | ||||
-rw-r--r-- | mysql-test/t/func_sapdb.test | 99 | ||||
-rw-r--r-- | sql/field.cc | 2 | ||||
-rw-r--r-- | sql/item_create.cc | 35 | ||||
-rw-r--r-- | sql/item_create.h | 7 | ||||
-rw-r--r-- | sql/item_timefunc.cc | 630 | ||||
-rw-r--r-- | sql/item_timefunc.h | 115 | ||||
-rw-r--r-- | sql/lex.h | 16 | ||||
-rw-r--r-- | sql/mysql_priv.h | 4 | ||||
-rw-r--r-- | sql/protocol.cc | 17 | ||||
-rw-r--r-- | sql/sql_parse.cc | 5 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 2 | ||||
-rw-r--r-- | sql/sql_string.cc | 17 | ||||
-rw-r--r-- | sql/sql_string.h | 2 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 36 | ||||
-rw-r--r-- | sql/time.cc | 14 |
21 files changed, 1149 insertions, 82 deletions
diff --git a/include/mysql.h b/include/mysql.h index bd63a10ba45..27d6b03bd0b 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -566,6 +566,7 @@ unsigned long STDCALL mysql_param_count(MYSQL_STMT * stmt); my_bool STDCALL mysql_bind_param(MYSQL_STMT * stmt, MYSQL_BIND * bnd); my_bool STDCALL mysql_bind_result(MYSQL_STMT * stmt, MYSQL_BIND * bnd); my_bool STDCALL mysql_stmt_close(MYSQL_STMT * stmt); +my_bool STDCALL mysql_stmt_reset(MYSQL_STMT * stmt); my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt); unsigned int STDCALL mysql_stmt_errno(MYSQL_STMT * stmt); const char *STDCALL mysql_stmt_error(MYSQL_STMT * stmt); diff --git a/include/mysql_com.h b/include/mysql_com.h index e87001ff27d..5eb59a1c7ab 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -44,6 +44,7 @@ enum enum_server_command COM_TIME, COM_DELAYED_INSERT, COM_CHANGE_USER, COM_BINLOG_DUMP, COM_TABLE_DUMP, COM_CONNECT_OUT, COM_REGISTER_SLAVE, COM_PREPARE, COM_EXECUTE, COM_LONG_DATA, COM_CLOSE_STMT, + COM_RESET_STMT, COM_END /* Must be last */ }; diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 771278c1dbb..1461d2fa128 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -3316,6 +3316,7 @@ mysql_stmt_data_seek(MYSQL_STMT *stmt, my_ulonglong row) } else DBUG_PRINT("exit", ("stmt doesn't contain any resultset")); + DBUG_VOID_RETURN; } @@ -3422,6 +3423,28 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) } /* + Reset the statement buffers in server +*/ + +my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt) +{ + char buff[MYSQL_STMT_HEADER]; + MYSQL *mysql; + DBUG_ENTER("mysql_stmt_reset"); + DBUG_ASSERT(stmt != 0); + + mysql= stmt->mysql->last_used_con; + int4store(buff, stmt->stmt_id); /* Send stmt id to server */ + if (advanced_command(mysql, COM_RESET_STMT,buff,MYSQL_STMT_HEADER,0,0,1)) + { + set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, + mysql->net.sqlstate); + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + +/* Return statement error code */ diff --git a/libmysql/libmysql.def b/libmysql/libmysql.def index d641ca57274..51869eacc4b 100644 --- a/libmysql/libmysql.def +++ b/libmysql/libmysql.def @@ -98,6 +98,7 @@ EXPORTS mysql_stat mysql_stmt_affected_rows mysql_stmt_close + mysql_stmt_reset mysql_stmt_data_seek mysql_stmt_errno mysql_stmt_error diff --git a/mysql-test/r/cast.result b/mysql-test/r/cast.result index a14ea4d61a6..5d185bee005 100644 --- a/mysql-test/r/cast.result +++ b/mysql-test/r/cast.result @@ -44,10 +44,10 @@ t1 CREATE TABLE `t1` ( drop table t1; select cast("2001-1-1" as date) = "2001-01-01"; cast("2001-1-1" as date) = "2001-01-01" -0 +1 select cast("2001-1-1" as datetime) = "2001-01-01 00:00:00"; cast("2001-1-1" as datetime) = "2001-01-01 00:00:00" -0 +1 select cast("1:2:3" as TIME) = "1:02:03"; cast("1:2:3" as TIME) = "1:02:03" 0 diff --git a/mysql-test/r/func_sapdb.result b/mysql-test/r/func_sapdb.result new file mode 100644 index 00000000000..e330c73727b --- /dev/null +++ b/mysql-test/r/func_sapdb.result @@ -0,0 +1,200 @@ +drop table if exists t1, test; +select extract(DAY_MICROSECOND FROM "1999-01-02 10:11:12.000123"); +extract(DAY_MICROSECOND FROM "1999-01-02 10:11:12.000123") +2101112000123 +select extract(HOUR_MICROSECOND FROM "1999-01-02 10:11:12.000123"); +extract(HOUR_MICROSECOND FROM "1999-01-02 10:11:12.000123") +101112000123 +select extract(MINUTE_MICROSECOND FROM "1999-01-02 10:11:12.000123"); +extract(MINUTE_MICROSECOND FROM "1999-01-02 10:11:12.000123") +1112000123 +select extract(SECOND_MICROSECOND FROM "1999-01-02 10:11:12.000123"); +extract(SECOND_MICROSECOND FROM "1999-01-02 10:11:12.000123") +12000123 +select extract(MICROSECOND FROM "1999-01-02 10:11:12.000123"); +extract(MICROSECOND FROM "1999-01-02 10:11:12.000123") +123 +select date_format("1997-12-31 23:59:59.000002", "%f"); +date_format("1997-12-31 23:59:59.000002", "%f") +000002 +select date_add("1997-12-31 23:59:59.000002",INTERVAL "10000 99:99:99.999999" DAY_MICROSECOND); +date_add("1997-12-31 23:59:59.000002",INTERVAL "10000 99:99:99.999999" DAY_MICROSECOND) +2025-05-23 04:40:39.000001 +select date_add("1997-12-31 23:59:59.000002",INTERVAL "10000:99:99.999999" HOUR_MICROSECOND); +date_add("1997-12-31 23:59:59.000002",INTERVAL "10000:99:99.999999" HOUR_MICROSECOND) +1999-02-21 17:40:39.000001 +select date_add("1997-12-31 23:59:59.000002",INTERVAL "10000:99.999999" MINUTE_MICROSECOND); +date_add("1997-12-31 23:59:59.000002",INTERVAL "10000:99.999999" MINUTE_MICROSECOND) +1998-01-07 22:41:39.000001 +select date_add("1997-12-31 23:59:59.000002",INTERVAL "10000.999999" SECOND_MICROSECOND); +date_add("1997-12-31 23:59:59.000002",INTERVAL "10000.999999" SECOND_MICROSECOND) +1998-01-01 02:46:40.000001 +select date_add("1997-12-31 23:59:59.000002",INTERVAL "999999" MICROSECOND); +date_add("1997-12-31 23:59:59.000002",INTERVAL "999999" MICROSECOND) +1998-01-01 00:00:00.000001 +select date_sub("1998-01-01 00:00:00.000001",INTERVAL "1 1:1:1.000002" DAY_MICROSECOND); +date_sub("1998-01-01 00:00:00.000001",INTERVAL "1 1:1:1.000002" DAY_MICROSECOND) +1997-12-30 22:58:58.999999 +select date_sub("1998-01-01 00:00:00.000001",INTERVAL "1:1:1.000002" HOUR_MICROSECOND); +date_sub("1998-01-01 00:00:00.000001",INTERVAL "1:1:1.000002" HOUR_MICROSECOND) +1997-12-31 22:58:58.999999 +select date_sub("1998-01-01 00:00:00.000001",INTERVAL "1:1.000002" MINUTE_MICROSECOND); +date_sub("1998-01-01 00:00:00.000001",INTERVAL "1:1.000002" MINUTE_MICROSECOND) +1997-12-31 23:58:58.999999 +select date_sub("1998-01-01 00:00:00.000001",INTERVAL "1.000002" SECOND_MICROSECOND); +date_sub("1998-01-01 00:00:00.000001",INTERVAL "1.000002" SECOND_MICROSECOND) +1997-12-31 23:59:58.999999 +select date_sub("1998-01-01 00:00:00.000001",INTERVAL "000002" MICROSECOND); +date_sub("1998-01-01 00:00:00.000001",INTERVAL "000002" MICROSECOND) +1997-12-31 23:59:59.999999 +select adddate("1997-12-31 23:59:59.000001", 10); +adddate("1997-12-31 23:59:59.000001", 10) +1998-01-10 23:59:59.000001 +select subdate("1997-12-31 23:59:59.000001", 10); +subdate("1997-12-31 23:59:59.000001", 10) +1997-12-21 23:59:59.000001 +select datediff("1997-12-31 23:59:59.000001","1997-12-30"); +datediff("1997-12-31 23:59:59.000001","1997-12-30") +1 +select datediff("1997-11-31 23:59:59.000001","1997-12-31"); +datediff("1997-11-31 23:59:59.000001","1997-12-31") +-30 +select datediff("1997-11-31 23:59:59.000001",null); +datediff("1997-11-31 23:59:59.000001",null) +NULL +select weekofyear("1997-11-31 23:59:59.000001"); +weekofyear("1997-11-31 23:59:59.000001") +49 +select makedate(1997,1); +makedate(1997,1) +1997-01-01 +select makedate(1997,0); +makedate(1997,0) +NULL +select addtime("1997-12-31 23:59:59.999999", "1 1:1:1.000002"); +addtime("1997-12-31 23:59:59.999999", "1 1:1:1.000002") +1998-01-02 01:01:01.000001 +select subtime("1997-12-31 23:59:59.000001", "1 1:1:1.000002"); +subtime("1997-12-31 23:59:59.000001", "1 1:1:1.000002") +1997-12-30 22:58:57.999999 +select addtime("1997-12-31 23:59:59.999999", "1998-01-01 01:01:01.999999"); +addtime("1997-12-31 23:59:59.999999", "1998-01-01 01:01:01.999999") +NULL +select subtime("1997-12-31 23:59:59.999999", "1998-01-01 01:01:01.999999"); +subtime("1997-12-31 23:59:59.999999", "1998-01-01 01:01:01.999999") +NULL +select subtime("01:00:00.999999", "02:00:00.999998"); +subtime("01:00:00.999999", "02:00:00.999998") +-00:59:59.999999 +select subtime("02:01:01.999999", "01:01:01.999999"); +subtime("02:01:01.999999", "01:01:01.999999") +01:00:00.000000 +select timediff("1997-01-01 23:59:59.000001","1995-12-31 23:59:59.000002"); +timediff("1997-01-01 23:59:59.000001","1995-12-31 23:59:59.000002") +8807:59:59.999999 +select timediff("1997-12-31 23:59:59.000001","1997-12-30 01:01:01.000002"); +timediff("1997-12-31 23:59:59.000001","1997-12-30 01:01:01.000002") +46:58:57.999999 +select timediff("1997-12-30 23:59:59.000001","1997-12-31 23:59:59.000002"); +timediff("1997-12-30 23:59:59.000001","1997-12-31 23:59:59.000002") +-23:59:59.999999 +select timediff("1997-12-31 23:59:59.000001","23:59:59.000001"); +timediff("1997-12-31 23:59:59.000001","23:59:59.000001") +NULL +select timediff("2000:01:01 00:00:00", "2000:01:01 00:00:00.000001"); +timediff("2000:01:01 00:00:00", "2000:01:01 00:00:00.000001") +-00:00:00.000001 +select maketime(10,11,12); +maketime(10,11,12) +10:11:12 +select maketime(25,11,12); +maketime(25,11,12) +25:11:12 +select maketime(-25,11,12); +maketime(-25,11,12) +-25:11:12 +select timestamp("2001-12-01", "01:01:01.999999"); +timestamp("2001-12-01", "01:01:01.999999") +2001-12-01 01:01:01.999999 +select timestamp("2001-13-01", "01:01:01.000001"); +timestamp("2001-13-01", "01:01:01.000001") +NULL +select timestamp("2001-12-01", "25:01:01"); +timestamp("2001-12-01", "25:01:01") +2001-12-02 01:01:01 +select timestamp("2001-12-01 01:01:01.000100"); +timestamp("2001-12-01 01:01:01.000100") +2001-12-01 01:01:01.000100 +select timestamp("2001-12-01"); +timestamp("2001-12-01") +2001-12-01 00:00:00 +select day("1997-12-31 23:59:59.000001"); +day("1997-12-31 23:59:59.000001") +31 +select date("1997-12-31 23:59:59.000001"); +date("1997-12-31 23:59:59.000001") +1997-12-31 +select date("1997-13-31 23:59:59.000001"); +date("1997-13-31 23:59:59.000001") +NULL +select time("1997-12-31 23:59:59.000001"); +time("1997-12-31 23:59:59.000001") +23:59:59.000001 +select time("1997-12-31 25:59:59.000001"); +time("1997-12-31 25:59:59.000001") +NULL +select microsecond("1997-12-31 23:59:59.000001"); +microsecond("1997-12-31 23:59:59.000001") +1 +create table t1 +select makedate(1997,1) as f1, +addtime(cast("1997-12-31 23:59:59.000001" as datetime), "1 1:1:1.000002") as f2, +addtime(cast("23:59:59.999999" as time) , "1 1:1:1.000002") as f3, +timediff("1997-12-31 23:59:59.000001","1997-12-30 01:01:01.000002") as f4, +timediff("1997-12-30 23:59:59.000001","1997-12-31 23:59:59.000002") as f5, +maketime(10,11,12) as f6, +timestamp(cast("2001-12-01" as date), "01:01:01") as f7, +date("1997-12-31 23:59:59.000001") as f8, +time("1997-12-31 23:59:59.000001") as f9; +describe t1; +Field Type Null Key Default Extra +f1 date 0000-00-00 +f2 datetime 0000-00-00 00:00:00 +f3 time 00:00:00 +f4 time 00:00:00 +f5 time 00:00:00 +f6 time 00:00:00 +f7 datetime 0000-00-00 00:00:00 +f8 date 0000-00-00 +f9 time 00:00:00 +select * from t1; +f1 f2 f3 f4 f5 f6 f7 f8 f9 +1997-01-01 1998-01-02 01:01:00 49:01:01 46:58:57 -23:59:59 10:11:12 2001-12-01 01:01:01 1997-12-31 23:59:59 +create table test(t1 datetime, t2 time, t3 time, t4 datetime); +insert into test values +('2001-01-01 01:01:01', '01:01:01', null, '2001-02-01 01:01:01'), +('2001-01-01 01:01:01', '-01:01:01', '-23:59:59', "1997-12-31 23:59:59.000001"), +('1997-12-31 23:59:59.000001', '-23:59:59', '-01:01:01', '2001-01-01 01:01:01'), +('2001-01-01 01:01:01', '01:01:01', '-1 01:01:01', null), +('2001-01-01 01:01:01', null, '-1 01:01:01', null), +(null, null, null, null), +('2001-01-01 01:01:01', '01:01:01', '1 01:01:01', '2001-01-01 01:01:01'); +SELECT ADDTIME(t1,t2) As ttt, ADDTIME(t2, t3) As qqq from test; +ttt qqq +2001-01-01 02:02:02 NULL +2001-01-01 00:00:00 -25:01:00 +1997-12-31 00:00:00 -25:01:00 +2001-01-01 02:02:02 -24:00:00 +NULL NULL +NULL NULL +2001-01-01 02:02:02 26:02:02 +SELECT TIMEDIFF(t1,t4) As ttt, TIMEDIFF(t2, t3) As qqq from test; +ttt qqq +-744:00:00 NULL +26305:01:02 22:58:58 +-26305:01:02 -22:58:58 +NULL 26:02:02 +NULL NULL +NULL NULL +00:00:00 -24:00:00 +drop table t1, test; diff --git a/mysql-test/t/func_sapdb.test b/mysql-test/t/func_sapdb.test new file mode 100644 index 00000000000..afd84fe9630 --- /dev/null +++ b/mysql-test/t/func_sapdb.test @@ -0,0 +1,99 @@ +--disable_warnings +drop table if exists t1, test; +--enable_warnings + + +# +# time functions +# +select extract(DAY_MICROSECOND FROM "1999-01-02 10:11:12.000123"); +select extract(HOUR_MICROSECOND FROM "1999-01-02 10:11:12.000123"); +select extract(MINUTE_MICROSECOND FROM "1999-01-02 10:11:12.000123"); +select extract(SECOND_MICROSECOND FROM "1999-01-02 10:11:12.000123"); +select extract(MICROSECOND FROM "1999-01-02 10:11:12.000123"); +select date_format("1997-12-31 23:59:59.000002", "%f"); + +select date_add("1997-12-31 23:59:59.000002",INTERVAL "10000 99:99:99.999999" DAY_MICROSECOND); +select date_add("1997-12-31 23:59:59.000002",INTERVAL "10000:99:99.999999" HOUR_MICROSECOND); +select date_add("1997-12-31 23:59:59.000002",INTERVAL "10000:99.999999" MINUTE_MICROSECOND); +select date_add("1997-12-31 23:59:59.000002",INTERVAL "10000.999999" SECOND_MICROSECOND); +select date_add("1997-12-31 23:59:59.000002",INTERVAL "999999" MICROSECOND); + +select date_sub("1998-01-01 00:00:00.000001",INTERVAL "1 1:1:1.000002" DAY_MICROSECOND); +select date_sub("1998-01-01 00:00:00.000001",INTERVAL "1:1:1.000002" HOUR_MICROSECOND); +select date_sub("1998-01-01 00:00:00.000001",INTERVAL "1:1.000002" MINUTE_MICROSECOND); +select date_sub("1998-01-01 00:00:00.000001",INTERVAL "1.000002" SECOND_MICROSECOND); +select date_sub("1998-01-01 00:00:00.000001",INTERVAL "000002" MICROSECOND); + +#Date functions +select adddate("1997-12-31 23:59:59.000001", 10); +select subdate("1997-12-31 23:59:59.000001", 10); + +select datediff("1997-12-31 23:59:59.000001","1997-12-30"); +select datediff("1997-11-31 23:59:59.000001","1997-12-31"); +select datediff("1997-11-31 23:59:59.000001",null); + +select weekofyear("1997-11-31 23:59:59.000001"); + +select makedate(1997,1); +select makedate(1997,0); + +#Time functions + +select addtime("1997-12-31 23:59:59.999999", "1 1:1:1.000002"); +select subtime("1997-12-31 23:59:59.000001", "1 1:1:1.000002"); +select addtime("1997-12-31 23:59:59.999999", "1998-01-01 01:01:01.999999"); +select subtime("1997-12-31 23:59:59.999999", "1998-01-01 01:01:01.999999"); +select subtime("01:00:00.999999", "02:00:00.999998"); +select subtime("02:01:01.999999", "01:01:01.999999"); + +select timediff("1997-01-01 23:59:59.000001","1995-12-31 23:59:59.000002"); +select timediff("1997-12-31 23:59:59.000001","1997-12-30 01:01:01.000002"); +select timediff("1997-12-30 23:59:59.000001","1997-12-31 23:59:59.000002"); +select timediff("1997-12-31 23:59:59.000001","23:59:59.000001"); +select timediff("2000:01:01 00:00:00", "2000:01:01 00:00:00.000001"); + +select maketime(10,11,12); +select maketime(25,11,12); +select maketime(-25,11,12); + +#Extraction functions +select timestamp("2001-12-01", "01:01:01.999999"); +select timestamp("2001-13-01", "01:01:01.000001"); +select timestamp("2001-12-01", "25:01:01"); +select timestamp("2001-12-01 01:01:01.000100"); +select timestamp("2001-12-01"); +select day("1997-12-31 23:59:59.000001"); +select date("1997-12-31 23:59:59.000001"); +select date("1997-13-31 23:59:59.000001"); +select time("1997-12-31 23:59:59.000001"); +select time("1997-12-31 25:59:59.000001"); +select microsecond("1997-12-31 23:59:59.000001"); + +create table t1 +select makedate(1997,1) as f1, + addtime(cast("1997-12-31 23:59:59.000001" as datetime), "1 1:1:1.000002") as f2, + addtime(cast("23:59:59.999999" as time) , "1 1:1:1.000002") as f3, + timediff("1997-12-31 23:59:59.000001","1997-12-30 01:01:01.000002") as f4, + timediff("1997-12-30 23:59:59.000001","1997-12-31 23:59:59.000002") as f5, + maketime(10,11,12) as f6, + timestamp(cast("2001-12-01" as date), "01:01:01") as f7, + date("1997-12-31 23:59:59.000001") as f8, + time("1997-12-31 23:59:59.000001") as f9; +describe t1; +select * from t1; + +create table test(t1 datetime, t2 time, t3 time, t4 datetime); +insert into test values +('2001-01-01 01:01:01', '01:01:01', null, '2001-02-01 01:01:01'), +('2001-01-01 01:01:01', '-01:01:01', '-23:59:59', "1997-12-31 23:59:59.000001"), +('1997-12-31 23:59:59.000001', '-23:59:59', '-01:01:01', '2001-01-01 01:01:01'), +('2001-01-01 01:01:01', '01:01:01', '-1 01:01:01', null), +('2001-01-01 01:01:01', null, '-1 01:01:01', null), +(null, null, null, null), +('2001-01-01 01:01:01', '01:01:01', '1 01:01:01', '2001-01-01 01:01:01'); + +SELECT ADDTIME(t1,t2) As ttt, ADDTIME(t2, t3) As qqq from test; +SELECT TIMEDIFF(t1,t4) As ttt, TIMEDIFF(t2, t3) As qqq from test; + +drop table t1, test; diff --git a/sql/field.cc b/sql/field.cc index 4aa58180a69..2f89dd43c3f 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -3152,11 +3152,13 @@ bool Field_time::get_time(TIME *ltime) ltime->neg= 1; tmp=-tmp; } + ltime->day= 0; ltime->hour= (int) (tmp/10000); tmp-=ltime->hour*10000; ltime->minute= (int) tmp/100; ltime->second= (int) tmp % 100; ltime->second_part=0; + ltime->time_type= TIMESTAMP_TIME; return 0; } diff --git a/sql/item_create.cc b/sql/item_create.cc index 90f42cee959..eaa27c1009d 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -691,3 +691,38 @@ Item *create_func_uncompressed_length(Item* a) #endif +Item *create_func_datediff(Item *a, Item *b) +{ + return new Item_func_minus(new Item_func_to_days(a), + new Item_func_to_days(b)); +} + +Item *create_func_weekofyear(Item *a) +{ + return new Item_func_week(a, new Item_int((char*) "0", 3, 1)); +} + +Item *create_func_makedate(Item* a,Item* b) +{ + return new Item_func_makedate(a, b); +} + +Item *create_func_addtime(Item* a,Item* b) +{ + return new Item_func_add_time(a, b, 0, 0); +} + +Item *create_func_subtime(Item* a,Item* b) +{ + return new Item_func_add_time(a, b, 0, 1); +} + +Item *create_func_timediff(Item* a,Item* b) +{ + return new Item_func_timediff(a, b); +} + +Item *create_func_maketime(Item* a,Item* b,Item* c) +{ + return new Item_func_maketime(a, b, c); +} diff --git a/sql/item_create.h b/sql/item_create.h index 4151f59a87f..32e452c15dd 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -146,3 +146,10 @@ Item *create_func_compress(Item *a); Item *create_func_uncompress(Item *a); Item *create_func_uncompressed_length(Item *a); +Item *create_func_datediff(Item *a, Item *b); +Item *create_func_weekofyear(Item *a); +Item *create_func_makedate(Item* a,Item* b); +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); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 61c869cddba..4fdb1b13d24 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -29,6 +29,8 @@ ** Todo: Move month and days to language files */ +#define MAX_DAY_NUMBER 3652424L + static String month_names[] = { String("January", &my_charset_latin1), @@ -55,6 +57,82 @@ 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 +{ + const char* format_str; + uint length; +}; + +static struct date_time_format date_time_formats[]= +{ + {"%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} +}; + + +/* + 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. +*/ + +String *make_datetime(String *str, TIME *ltime, + enum date_time_format_types format) +{ + char *buff; + CHARSET_INFO *cs= &my_charset_bin; + uint length= date_time_formats[format].length + 32; + const char* format_str= date_time_formats[format].format_str; + + if (str->alloc(length)) + return 0; + + 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; + } + + str->length(length); + str->set_charset(cs); + return str; +} + /* ** Get a array of positive numbers from a string object. ** Each number is separated by 1 non digit character @@ -309,7 +387,7 @@ static bool get_interval_value(Item *args,interval_type int_type, CHARSET_INFO *cs=str_value->charset(); bzero((char*) t,sizeof(*t)); - if ((int) int_type <= INTERVAL_SECOND) + if ((int) int_type <= INTERVAL_MICROSECOND) { value=(long) args->val_int(); if (args->null_value) @@ -352,6 +430,9 @@ static bool get_interval_value(Item *args,interval_type int_type, case INTERVAL_HOUR: t->hour=value; break; + case INTERVAL_MICROSECOND: + t->second_part=value; + break; case INTERVAL_MINUTE: t->minute=value; break; @@ -370,6 +451,15 @@ static bool get_interval_value(Item *args,interval_type int_type, t->day=array[0]; t->hour=array[1]; break; + case INTERVAL_DAY_MICROSECOND: + if (get_interval_info(str,length,cs,5,array)) + return (1); + t->day=array[0]; + t->hour=array[1]; + t->minute=array[2]; + t->second=array[3]; + t->second_part=array[4]; + break; case INTERVAL_DAY_MINUTE: if (get_interval_info(str,length,cs,3,array)) return (1); @@ -385,6 +475,14 @@ static bool get_interval_value(Item *args,interval_type int_type, t->minute=array[2]; t->second=array[3]; break; + case INTERVAL_HOUR_MICROSECOND: + if (get_interval_info(str,length,cs,4,array)) + return (1); + t->hour=array[0]; + t->minute=array[1]; + t->second=array[2]; + t->second_part=array[3]; + break; case INTERVAL_HOUR_MINUTE: if (get_interval_info(str,length,cs,2,array)) return (1); @@ -398,12 +496,25 @@ static bool get_interval_value(Item *args,interval_type int_type, t->minute=array[1]; t->second=array[2]; break; + case INTERVAL_MINUTE_MICROSECOND: + if (get_interval_info(str,length,cs,3,array)) + return (1); + t->minute=array[0]; + t->second=array[1]; + t->second_part=array[2]; + break; case INTERVAL_MINUTE_SECOND: if (get_interval_info(str,length,cs,2,array)) return (1); t->minute=array[0]; t->second=array[1]; break; + case INTERVAL_SECOND_MICROSECOND: + if (get_interval_info(str,length,cs,2,array)) + return (1); + t->second=array[0]; + t->second_part=array[1]; + break; } return 0; } @@ -687,6 +798,9 @@ uint Item_func_date_format::format_length(const String *format) case 'T': /* time, 24-hour (hh:mm:ss) */ size += 8; break; + case 'f': /* microseconds */ + size += 6; + break; case 'w': /* day (of the week), numeric */ case '%': default: @@ -798,8 +912,8 @@ String *Item_func_date_format::val_str(String *str) null_value=1; return 0; } - length= my_sprintf(intbuff, (intbuff,"%d",l_time.day)); - str->append(intbuff, length); + 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 @@ -821,41 +935,45 @@ String *Item_func_date_format::val_str(String *str) } break; case 'Y': - sprintf(intbuff,"%04d",l_time.year); - str->append(intbuff,4); + length= int10_to_str(l_time.year, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 4, '0'); break; case 'y': - sprintf(intbuff,"%02d",l_time.year%100); - str->append(intbuff,2); + length= int10_to_str(l_time.year%100, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 2, '0'); break; case 'm': - sprintf(intbuff,"%02d",l_time.month); - str->append(intbuff,2); + length= int10_to_str(l_time.month, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 2, '0'); break; case 'c': - sprintf(intbuff,"%d",l_time.month); - str->append(intbuff); + length= int10_to_str(l_time.month, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 1, '0'); break; case 'd': - sprintf(intbuff,"%02d",l_time.day); - str->append(intbuff,2); + length= int10_to_str(l_time.day, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 2, '0'); break; case 'e': - sprintf(intbuff,"%d",l_time.day); - str->append(intbuff); + 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': - sprintf(intbuff,"%02d",l_time.hour); - str->append(intbuff,2); + length= int10_to_str(l_time.hour, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 2, '0'); break; case 'h': case 'I': - sprintf(intbuff,"%02d", (l_time.hour+11)%12+1); - str->append(intbuff,2); + 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 */ - sprintf(intbuff,"%02d",l_time.minute); - str->append(intbuff,2); + 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) @@ -863,52 +981,60 @@ String *Item_func_date_format::val_str(String *str) null_value=1; return 0; } - sprintf(intbuff,"%03d", - (int) (calc_daynr(l_time.year,l_time.month,l_time.day) - - calc_daynr(l_time.year,1,1)) + 1); - str->append(intbuff,3); + 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': - sprintf(intbuff,"%d",l_time.hour); - str->append(intbuff); + length= int10_to_str(l_time.hour, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 1, '0'); break; case 'l': - sprintf(intbuff,"%d", (l_time.hour+11)%12+1); - str->append(intbuff); + 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': - sprintf(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= 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': - sprintf(intbuff,"%02d",l_time.second); - str->append(intbuff); + length= int10_to_str(l_time.second, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 2, '0'); break; case 'T': - sprintf(intbuff,"%02d:%02d:%02d", l_time.hour, l_time.minute, - l_time.second); - str->append(intbuff); + 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; - sprintf(intbuff,"%02d",calc_week(&l_time, 0, (*ptr) == 'U', &year)); - str->append(intbuff,2); + 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; - sprintf(intbuff,"%02d",calc_week(&l_time, 1, (*ptr) == 'V', &year)); - str->append(intbuff,2); + 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': @@ -916,14 +1042,15 @@ String *Item_func_date_format::val_str(String *str) { uint year; (void) calc_week(&l_time, 1, (*ptr) == 'X', &year); - sprintf(intbuff,"%04d",year); - str->append(intbuff,4); + 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); - sprintf(intbuff,"%d",weekday); - str->append(intbuff,1); + length= int10_to_str(weekday, intbuff, 10) - intbuff; + str->append_with_prefill(intbuff, length, 1, '0'); + break; default: str->append(*ptr); @@ -1005,7 +1132,7 @@ void Item_date_add_interval::fix_length_and_dec() enum_field_types arg0_field_type; set_charset(default_charset()); maybe_null=1; - max_length=19*default_charset()->mbmaxlen; + max_length=26*MY_CHARSET_BIN_MB_MAXLEN; value.alloc(32); /* @@ -1051,39 +1178,55 @@ bool Item_date_add_interval::get_date(TIME *ltime, bool fuzzy_date) null_value=0; switch (int_type) { case INTERVAL_SECOND: + case INTERVAL_SECOND_MICROSECOND: + case INTERVAL_MICROSECOND: case INTERVAL_MINUTE: case INTERVAL_HOUR: + case INTERVAL_MINUTE_MICROSECOND: case INTERVAL_MINUTE_SECOND: + case INTERVAL_HOUR_MICROSECOND: case INTERVAL_HOUR_SECOND: case INTERVAL_HOUR_MINUTE: + case INTERVAL_DAY_MICROSECOND: case INTERVAL_DAY_SECOND: case INTERVAL_DAY_MINUTE: case INTERVAL_DAY_HOUR: - long sec,days,daynr; + long sec,days,daynr,microseconds,extra_sec; ltime->time_type=TIMESTAMP_FULL; // Return full date + microseconds= ltime->second_part + sign*interval.second_part; + extra_sec= microseconds/1000000L; + microseconds= microseconds%1000000L; sec=((ltime->day-1)*3600*24L+ltime->hour*3600+ltime->minute*60+ ltime->second + sign*(interval.day*3600*24L + - interval.hour*3600+interval.minute*60+interval.second)); + interval.hour*3600+interval.minute*60+interval.second))+ + extra_sec; + + if (microseconds < 0) + { + microseconds+= 1000000L; + sec--; + } days=sec/(3600*24L); sec=sec-days*3600*24L; if (sec < 0) { days--; sec+=3600*24L; } + ltime->second_part= microseconds; ltime->second=sec % 60; ltime->minute=sec/60 % 60; ltime->hour=sec/3600; daynr= calc_daynr(ltime->year,ltime->month,1) + days; get_date_from_daynr(daynr,<ime->year,<ime->month,<ime->day); - if (daynr < 0 || daynr >= 3652424) // Day number from year 0 to 9999-12-31 + if (daynr < 0 || daynr >= MAX_DAY_NUMBER) // Day number from year 0 to 9999-12-31 goto null_date; break; case INTERVAL_DAY: period= calc_daynr(ltime->year,ltime->month,ltime->day) + sign*interval.day; - if (period < 0 || period >= 3652424) // Daynumber from year 0 to 9999-12-31 + if (period < 0 || period >= MAX_DAY_NUMBER) // Daynumber from year 0 to 9999-12-31 goto null_date; get_date_from_daynr((long) period,<ime->year,<ime->month,<ime->day); break; @@ -1124,34 +1267,21 @@ bool Item_date_add_interval::get_date(TIME *ltime, bool fuzzy_date) String *Item_date_add_interval::val_str(String *str) { TIME ltime; - CHARSET_INFO *cs=default_charset(); - uint32 l; + enum date_time_format_types format; if (Item_date_add_interval::get_date(<ime,0)) return 0; + if (ltime.time_type == TIMESTAMP_DATE) - { - l=11*cs->mbmaxlen+32; - if (str->alloc(l)) - goto null_date; - l=cs->cset->snprintf(cs,(char*) str->ptr(),l,"%04d-%02d-%02d", - ltime.year,ltime.month,ltime.day); - str->length(l); - } + format= DATE_ONLY; + else if (ltime.second_part) + format= DATE_TIME_MICROSECOND; else - { - l=20*cs->mbmaxlen+32; - if (str->alloc(l)) - goto null_date; - l=cs->cset->snprintf(cs,(char*) str->ptr(),l,"%04d-%02d-%02d %02d:%02d:%02d", - ltime.year,ltime.month,ltime.day, - ltime.hour,ltime.minute,ltime.second); - str->length(l); - } - str->set_charset(cs); - return str; + format= DATE_TIME; + + if (make_datetime(str, <ime, format)) + return str; - null_date: null_value=1; return 0; } @@ -1188,6 +1318,11 @@ void Item_extract::fix_length_and_dec() case INTERVAL_MINUTE: max_length=2; date_value=0; break; case INTERVAL_MINUTE_SECOND: max_length=4; date_value=0; break; case INTERVAL_SECOND: max_length=2; date_value=0; break; + case INTERVAL_MICROSECOND: max_length=2; date_value=0; break; + case INTERVAL_DAY_MICROSECOND: max_length=20; date_value=0; break; + case INTERVAL_HOUR_MICROSECOND: max_length=13; date_value=0; break; + case INTERVAL_MINUTE_MICROSECOND: max_length=11; date_value=0; break; + case INTERVAL_SECOND_MICROSECOND: max_length=9; date_value=0; break; } } @@ -1234,6 +1369,21 @@ longlong Item_extract::val_int() case INTERVAL_MINUTE: return (long) ltime.minute*neg; case INTERVAL_MINUTE_SECOND: return (long) (ltime.minute*100+ltime.second)*neg; case INTERVAL_SECOND: return (long) ltime.second*neg; + case INTERVAL_MICROSECOND: return (long) ltime.second_part*neg; + case INTERVAL_DAY_MICROSECOND: return (((longlong)ltime.day*1000000L + + (longlong)ltime.hour*10000L + + ltime.minute*100 + + ltime.second)*1000000L + + ltime.second_part)*neg; + case INTERVAL_HOUR_MICROSECOND: return (((longlong)ltime.hour*10000L + + ltime.minute*100 + + ltime.second)*1000000L + + ltime.second_part)*neg; + case INTERVAL_MINUTE_MICROSECOND: return (((longlong)(ltime.minute*100+ + ltime.second))*1000000L+ + ltime.second_part)*neg; + case INTERVAL_SECOND_MICROSECOND: return ((longlong)ltime.second*1000000L+ + ltime.second_part)*neg; } return 0; // Impossible } @@ -1247,3 +1397,337 @@ void Item_typecast::print(String *str) str->append(func_name()); str->append(')'); } + +String *Item_datetime_typecast::val_str(String *str) +{ + TIME ltime; + + if (!get_arg0_date(<ime,1) && + make_datetime(str, <ime, ltime.second_part ? + DATE_TIME_MICROSECOND : DATE_TIME)) + return str; + +null_date: + null_value=1; + return 0; +} + + +bool Item_time_typecast::get_time(TIME *ltime) +{ + bool res= get_arg0_time(ltime); + ltime->time_type= TIMESTAMP_TIME; + return res; +} + + +String *Item_time_typecast::val_str(String *str) +{ + TIME ltime; + + if (!get_arg0_time(<ime) && + make_datetime(str, <ime, ltime.second_part ? TIME_MICROSECOND : TIME_ONLY)) + return str; + + null_value=1; + return 0; +} + + +bool Item_date_typecast::get_date(TIME *ltime, bool fuzzy_date) +{ + bool res= get_arg0_date(ltime,1); + ltime->time_type= TIMESTAMP_DATE; + return res; +} + + +String *Item_date_typecast::val_str(String *str) +{ + TIME ltime; + + if (!get_arg0_date(<ime,1) && + make_datetime(str, <ime, DATE_ONLY)) + return str; + +null_date: + null_value=1; + return 0; +} + +/* + MAKEDATE(a,b) is a date function that creates a date value + from a year and day value. +*/ + +String *Item_func_makedate::val_str(String *str) +{ + TIME l_time; + long daynr= args[1]->val_int(); + long yearnr= args[0]->val_int(); + long days; + + if (args[0]->null_value || args[1]->null_value || + yearnr < 0 || daynr <= 0) + goto null_date; + + days= calc_daynr(yearnr,1,1) + daynr - 1; + if (days > 0 || days < MAX_DAY_NUMBER) // Day number from year 0 to 9999-12-31 + { + 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)) + return str; + } + +null_date: + null_value=1; + return 0; +} + + +void Item_func_add_time::fix_length_and_dec() +{ + enum_field_types arg0_field_type; + decimals=0; + max_length=26*MY_CHARSET_BIN_MB_MAXLEN; + + /* + The field type for the result of an Item_func_add_time function is defined as + follows: + + - If first arg is a MYSQL_TYPE_DATETIME or MYSQL_TYPE_TIMESTAMP + result is MYSQL_TYPE_DATETIME + - If first arg is a MYSQL_TYPE_TIME result is MYSQL_TYPE_TIME + - Otherwise the result is MYSQL_TYPE_STRING + */ + + cached_field_type= MYSQL_TYPE_STRING; + arg0_field_type= args[0]->field_type(); + if (arg0_field_type == MYSQL_TYPE_DATE || + arg0_field_type == MYSQL_TYPE_DATETIME || + arg0_field_type == MYSQL_TYPE_TIMESTAMP) + cached_field_type= MYSQL_TYPE_DATETIME; + else if (arg0_field_type == MYSQL_TYPE_TIME) + cached_field_type= MYSQL_TYPE_TIME; +} + +/* + ADDTIME(t,a) and SUBTIME(t,a) are time functions that calculate a time/datetime value + + t: time_or_datetime_expression + a: time_expression + + Result: Time value or datetime value +*/ + +String *Item_func_add_time::val_str(String *str) +{ + TIME l_time1, l_time2, l_time3; + bool is_time= 0; + long microseconds, seconds, days= 0; + int l_sign= sign; + + null_value=0; + l_time3.neg= 0; + if (is_date) // TIMESTAMP function + { + if (get_arg0_date(&l_time1,1) || + args[1]->get_time(&l_time2) || + l_time1.time_type == TIMESTAMP_TIME || + l_time2.time_type != TIMESTAMP_TIME) + goto null_date; + } + else // ADDTIME function + { + if (args[0]->get_time(&l_time1) || + args[1]->get_time(&l_time2) || + l_time2.time_type == TIMESTAMP_FULL) + goto null_date; + is_time= (l_time1.time_type == TIMESTAMP_TIME); + if (is_time && (l_time2.neg == l_time1.neg && l_time1.neg)) + l_time3.neg= 1; + } + if (l_time1.neg != l_time2.neg) + l_sign= -l_sign; + + microseconds= l_time1.second_part + l_sign*l_time2.second_part; + seconds= (l_time1.hour*3600L + l_time1.minute*60L + l_time1.second + + (l_time2.day*86400L + l_time2.hour*3600L + + l_time2.minute*60L + l_time2.second)*l_sign); + if (is_time) + seconds+= l_time1.day*86400L; + else + days+= calc_daynr((uint) l_time1.year,(uint) l_time1.month, (uint) l_time1.day); + seconds= seconds + microseconds/1000000L; + microseconds= microseconds%1000000L; + days+= seconds/86400L; + seconds= seconds%86400L; + + if (microseconds < 0) + { + microseconds+= 1000000L; + seconds--; + } + if (seconds < 0) + { + days+= seconds/86400L - 1; + seconds+= 86400L; + } + if (days < 0) + { + if (!is_time) + goto null_date; + if (microseconds) + { + microseconds= 1000000L - microseconds; + seconds++; + } + seconds= 86400L - seconds; + days= -(++days); + l_time3.neg= 1; + } + + calc_time_from_sec(&l_time3, seconds, microseconds); + if (!is_time) + { + 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)) + return str; + goto null_date; + } + + l_time3.hour+= days*24; + if (make_datetime(str, &l_time3, + l_time1.second_part || l_time2.second_part ? + TIME_MICROSECOND : TIME_ONLY)) + return str; + +null_date: + null_value=1; + return 0; +} + +/* + TIMEDIFF(t,s) is a time function that calculates the + time value between a start and end time. + + t and s: time_or_datetime_expression + Result: Time value +*/ + +String *Item_func_timediff::val_str(String *str) +{ + longlong seconds; + long microseconds; + long days; + int l_sign= 1; + TIME l_time1 ,l_time2, l_time3; + + null_value= 0; + if (args[0]->get_time(&l_time1) || + args[1]->get_time(&l_time2) || + l_time1.time_type != l_time2.time_type) + goto null_date; + + if (l_time1.neg != l_time2.neg) + l_sign= -l_sign; + + if (l_time1.time_type == TIMESTAMP_TIME) // Time value + days= l_time1.day - l_sign*l_time2.day; + else // DateTime value + days= (calc_daynr((uint) l_time1.year, + (uint) l_time1.month, + (uint) l_time1.day) - + l_sign*calc_daynr((uint) l_time2.year, + (uint) l_time2.month, + (uint) l_time2.day)); + + microseconds= l_time1.second_part - l_sign*l_time2.second_part; + seconds= ((longlong) days*86400L + l_time1.hour*3600L + + l_time1.minute*60L + l_time1.second + microseconds/1000000L - + (longlong)l_sign*(l_time2.hour*3600L+l_time2.minute*60L+l_time2.second)); + + l_time3.neg= 0; + if (seconds < 0) + { + seconds= -seconds; + l_time3.neg= 1; + } + else if (seconds == 0 && microseconds < 0) + { + microseconds= -microseconds; + l_time3.neg= 1; + } + if (microseconds < 0) + { + microseconds+= 1000000L; + seconds--; + } + if ((l_time2.neg == l_time1.neg) && l_time1.neg) + 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)) + return str; + +null_date: + null_value=1; + return 0; +} + +/* + MAKETIME(h,m,s) is a time function that calculates a time value + from the total number of hours, minutes, and seconds. + Result: Time value +*/ + +String *Item_func_maketime::val_str(String *str) +{ + TIME ltime; + + long hour= args[0]->val_int(); + long minute= args[1]->val_int(); + long second= args[2]->val_int(); + + if ((null_value=(args[0]->null_value || + args[1]->null_value || + args[2]->null_value || + minute > 59 || minute < 0 || + second > 59 || second < 0))) + goto null_date; + + ltime.neg= 0; + if (hour < 0) + { + ltime.neg= 1; + hour= -hour; + } + ltime.hour= (ulong)hour; + ltime.minute= (ulong)minute; + ltime.second= (ulong)second; + if (make_datetime(str, <ime, TIME_ONLY)) + return str; + +null_date: + return 0; +} + +/* + MICROSECOND(a) is a function ( extraction) that extracts the microseconds from a. + + a: Datetime or time value + Result: int value +*/ +longlong Item_func_microsecond::val_int() +{ + TIME ltime; + if (!get_arg0_time(<ime)) + return ltime.second_part; + return 0; +} diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 87563cf9f47..20b95f8e22d 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -478,9 +478,10 @@ public: enum interval_type { INTERVAL_YEAR, INTERVAL_MONTH, INTERVAL_DAY, INTERVAL_HOUR, INTERVAL_MINUTE, - INTERVAL_SECOND, INTERVAL_YEAR_MONTH, INTERVAL_DAY_HOUR, INTERVAL_DAY_MINUTE, - INTERVAL_DAY_SECOND, INTERVAL_HOUR_MINUTE, INTERVAL_HOUR_SECOND, - INTERVAL_MINUTE_SECOND + INTERVAL_SECOND, INTERVAL_MICROSECOND ,INTERVAL_YEAR_MONTH, INTERVAL_DAY_HOUR, + INTERVAL_DAY_MINUTE, INTERVAL_DAY_SECOND, INTERVAL_HOUR_MINUTE, + INTERVAL_HOUR_SECOND, INTERVAL_MINUTE_SECOND, INTERVAL_DAY_MICROSECOND, + INTERVAL_HOUR_MICROSECOND, INTERVAL_MINUTE_MICROSECOND, INTERVAL_SECOND_MICROSECOND }; @@ -556,6 +557,8 @@ class Item_date_typecast :public Item_typecast { public: Item_date_typecast(Item *a) :Item_typecast(a) {} + String *val_str(String *str); + bool get_date(TIME *ltime, bool fuzzy_date); const char *func_name() const { return "date"; } enum_field_types field_type() const { return MYSQL_TYPE_DATE; } Field *tmp_table_field() { return result_field; } @@ -570,6 +573,8 @@ class Item_time_typecast :public Item_typecast { public: Item_time_typecast(Item *a) :Item_typecast(a) {} + String *val_str(String *str); + bool get_time(TIME *ltime); const char *func_name() const { return "time"; } enum_field_types field_type() const { return MYSQL_TYPE_TIME; } Field *tmp_table_field() { return result_field; } @@ -584,6 +589,7 @@ class Item_datetime_typecast :public Item_typecast { public: Item_datetime_typecast(Item *a) :Item_typecast(a) {} + String *val_str(String *str); const char *func_name() const { return "datetime"; } enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } Field *tmp_table_field() { return result_field; } @@ -592,3 +598,106 @@ public: return (new Field_datetime(maybe_null, name, t_arg, default_charset())); } }; + +class Item_func_makedate :public Item_str_func +{ +public: + Item_func_makedate(Item *a,Item *b) :Item_str_func(a,b) {} + String *val_str(String *str); + const char *func_name() const { return "makedate"; } + enum_field_types field_type() const { return MYSQL_TYPE_DATE; } + void fix_length_and_dec() + { + decimals=0; + max_length=8*MY_CHARSET_BIN_MB_MAXLEN; + } + Field *tmp_table_field() { return result_field; } + Field *tmp_table_field(TABLE *t_arg) + { + return (new Field_date(maybe_null, name, t_arg, &my_charset_bin)); + } +}; + + +class Item_func_add_time :public Item_str_func +{ + const bool is_date; + int sign; + enum_field_types cached_field_type; + +public: + Item_func_add_time(Item *a, Item *b, bool type_arg, bool neg_arg) + :Item_str_func(a, b), is_date(type_arg) { sign= neg_arg ? -1 : 1; } + String *val_str(String *str); + const char *func_name() const { return "addtime"; } + enum_field_types field_type() const { return cached_field_type; } + void fix_length_and_dec(); + +/* + TODO: + Change this when we support + microseconds in TIME/DATETIME +*/ + Field *tmp_table_field() { return result_field; } + Field *tmp_table_field(TABLE *t_arg) + { + if (cached_field_type == MYSQL_TYPE_TIME) + return (new Field_time(maybe_null, name, t_arg, &my_charset_bin)); + else if (cached_field_type == MYSQL_TYPE_DATETIME) + return (new Field_datetime(maybe_null, name, t_arg, &my_charset_bin)); + return (new Field_string(max_length, maybe_null, name, t_arg, &my_charset_bin)); + } +}; + +class Item_func_timediff :public Item_str_func +{ +public: + Item_func_timediff(Item *a, Item *b) + :Item_str_func(a, b) {} + String *val_str(String *str); + const char *func_name() const { return "timediff"; } + enum_field_types field_type() const { return MYSQL_TYPE_TIME; } + void fix_length_and_dec() + { + decimals=0; + max_length=17*MY_CHARSET_BIN_MB_MAXLEN; + } + Field *tmp_table_field() { return result_field; } + Field *tmp_table_field(TABLE *t_arg) + { + return (new Field_time(maybe_null, name, t_arg, &my_charset_bin)); + } +}; + +class Item_func_maketime :public Item_str_func +{ +public: + Item_func_maketime(Item *a, Item *b, Item *c) + :Item_str_func(a, b ,c) {} + String *val_str(String *str); + const char *func_name() const { return "maketime"; } + enum_field_types field_type() const { return MYSQL_TYPE_TIME; } + void fix_length_and_dec() + { + decimals=0; + max_length=8*MY_CHARSET_BIN_MB_MAXLEN; + } + Field *tmp_table_field() { return result_field; } + Field *tmp_table_field(TABLE *t_arg) + { + return (new Field_time(maybe_null, name, t_arg, &my_charset_bin)); + } +}; + +class Item_func_microsecond :public Item_int_func +{ +public: + Item_func_microsecond(Item *a) :Item_int_func(a) {} + longlong val_int(); + const char *func_name() const { return "microsecond"; } + void fix_length_and_dec() + { + decimals=0; + maybe_null=1; + } +}; diff --git a/sql/lex.h b/sql/lex.h index bb6e7a81ab4..a148baad07f 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -116,6 +116,7 @@ static SYMBOL symbols[] = { { "DATETIME", SYM(DATETIME),0,0}, { "DAY", SYM(DAY_SYM),0,0}, { "DAY_HOUR", SYM(DAY_HOUR_SYM),0,0}, + { "DAY_MICROSECOND", SYM(DAY_MICROSECOND_SYM),0,0}, { "DAY_MINUTE", SYM(DAY_MINUTE_SYM),0,0}, { "DAY_SECOND", SYM(DAY_SECOND_SYM),0,0}, { "DEC", SYM(DECIMAL_SYM),0,0}, @@ -186,6 +187,7 @@ static SYMBOL symbols[] = { { "HELP", SYM(HELP_SYM),0,0}, { "HIGH_PRIORITY", SYM(HIGH_PRIORITY),0,0}, { "HOUR", SYM(HOUR_SYM),0,0}, + { "HOUR_MICROSECOND", SYM(HOUR_MICROSECOND_SYM),0,0}, { "HOUR_MINUTE", SYM(HOUR_MINUTE_SYM),0,0}, { "HOUR_SECOND", SYM(HOUR_SECOND_SYM),0,0}, { "HOSTS", SYM(HOSTS_SYM),0,0}, @@ -259,9 +261,11 @@ static SYMBOL symbols[] = { { "MERGE", SYM(MERGE_SYM),0,0}, { "MEDIUM", SYM(MEDIUM_SYM),0,0}, { "MEMORY", SYM(MEMORY_SYM),0,0}, + { "MICROSECOND", SYM(MICROSECOND_SYM),0,0}, { "MIDDLEINT", SYM(MEDIUMINT),0,0}, /* For powerbuilder */ { "MIN_ROWS", SYM(MIN_ROWS),0,0}, { "MINUTE", SYM(MINUTE_SYM),0,0}, + { "MINUTE_MICROSECOND", SYM(MINUTE_MICROSECOND_SYM),0,0}, { "MINUTE_SECOND", SYM(MINUTE_SECOND_SYM),0,0}, { "MOD", SYM(MOD_SYM),0,0}, { "MODE", SYM(MODE_SYM),0,0}, @@ -337,6 +341,7 @@ static SYMBOL symbols[] = { { "ROWS", SYM(ROWS_SYM),0,0}, { "RTREE", SYM(RTREE_SYM),0,0}, { "SECOND", SYM(SECOND_SYM),0,0}, + { "SECOND_MICROSECOND", SYM(SECOND_MICROSECOND_SYM),0,0}, { "SEPARATOR", SYM(SEPARATOR_SYM),0,0}, { "SELECT", SYM(SELECT_SYM),0,0}, { "SERIAL", SYM(SERIAL_SYM),0,0}, @@ -427,7 +432,8 @@ static SYMBOL symbols[] = { static SYMBOL sql_functions[] = { { "ABS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_abs)}, { "ACOS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_acos)}, - { "ADDDATE", SYM(DATE_ADD_INTERVAL),0,0}, + { "ADDDATE", SYM(ADDDATE_SYM),0,0}, + { "ADDTIME", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_addtime)}, { "AES_ENCRYPT", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_aes_encrypt)}, { "AES_DECRYPT", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_aes_decrypt)}, { "AREA", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_area)}, @@ -470,6 +476,7 @@ static SYMBOL sql_functions[] = { { "CURDATE", SYM(CURDATE),0,0}, { "CURTIME", SYM(CURTIME),0,0}, { "DATE_ADD", SYM(DATE_ADD_INTERVAL),0,0}, + { "DATEDIFF", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_datediff)}, { "DATE_FORMAT", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_date_format)}, { "DATE_SUB", SYM(DATE_SUB_INTERVAL),0,0}, { "DAYNAME", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_dayname)}, @@ -545,6 +552,8 @@ static SYMBOL sql_functions[] = { { "LPAD", SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_lpad)}, { "LTRIM", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ltrim)}, { "MAKE_SET", SYM(MAKE_SET_SYM),0,0}, + { "MAKEDATE", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_makedate)}, + { "MAKETIME", SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_maketime)}, { "MASTER_POS_WAIT", SYM(MASTER_POS_WAIT),0,0}, { "MAX", SYM(MAX_SYM),0,0}, { "MBRCONTAINS", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_contains)}, @@ -605,7 +614,7 @@ static SYMBOL sql_functions[] = { { "RTRIM", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_rtrim)}, { "SEC_TO_TIME", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sec_to_time)}, { "SESSION_USER", SYM(USER),0,0}, - { "SUBDATE", SYM(DATE_SUB_INTERVAL),0,0}, + { "SUBDATE", SYM(SUBDATE_SYM),0,0}, { "SIGN", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sign)}, { "SIN", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sin)}, { "SHA", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sha)}, @@ -620,12 +629,14 @@ static SYMBOL sql_functions[] = { { "STRCMP", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_strcmp)}, { "SUBSTRING", SYM(SUBSTRING),0,0}, { "SUBSTRING_INDEX", SYM(SUBSTRING_INDEX),0,0}, + { "SUBTIME", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_subtime)}, { "SUM", SYM(SUM_SYM),0,0}, { "SYSDATE", SYM(NOW_SYM),0,0}, { "SYSTEM_USER", SYM(USER),0,0}, { "TAN", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_tan)}, { "TIME_FORMAT", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_time_format)}, { "TIME_TO_SEC", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_time_to_sec)}, + { "TIMEDIFF", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_timediff)}, { "TO_DAYS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_to_days)}, { "TOUCHES", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_touches)}, { "TRIM", SYM(TRIM),0,0}, @@ -639,6 +650,7 @@ static SYMBOL sql_functions[] = { { "VERSION", SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_version)}, { "WEEK", SYM(WEEK_SYM),0,0}, { "WEEKDAY", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_weekday)}, + { "WEEKOFYEAR", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_weekofyear)}, { "WITHIN", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_within)}, { "X", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_x)}, { "Y", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_y)}, diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 5ad2cc56b8c..edbc30bfe87 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -224,6 +224,8 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset; #define RAID_BLOCK_SIZE 1024 +#define MY_CHARSET_BIN_MB_MAXLEN 1 + #ifdef EXTRA_DEBUG /* Sync points allow us to force the server to reach a certain line of code @@ -557,6 +559,7 @@ void free_prep_stmt(PREP_STMT *stmt, TREE_FREE mode, void *not_used); bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length); void mysql_stmt_execute(THD *thd, char *packet); void mysql_stmt_free(THD *thd, char *packet); +void mysql_stmt_reset(THD *thd, char *packet); void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length); int check_insert_fields(THD *thd,TABLE *table,List<Item> &fields, List<Item> &values, ulong counter); @@ -851,6 +854,7 @@ longlong str_to_datetime(const char *str,uint length,bool fuzzy_date); timestamp_type str_to_TIME(const char *str, uint length, TIME *l_time, bool fuzzy_date); void localtime_to_TIME(TIME *to, struct tm *from); +void calc_time_from_sec(TIME *to, long seconds, long microseconds); int test_if_number(char *str,int *res,bool allow_wildcards); void change_byte(byte *,uint,char,char); diff --git a/sql/protocol.cc b/sql/protocol.cc index 1a1d1f0a585..1b9256c7723 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -823,6 +823,13 @@ bool Protocol_simple::store(Field *field) } +/* + TODO: + Second_part format ("%06") needs to change when + we support 0-6 decimals for time. +*/ + + bool Protocol_simple::store(TIME *tm) { #ifndef DEBUG_OFF @@ -840,6 +847,8 @@ bool Protocol_simple::store(TIME *tm) (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); } @@ -861,6 +870,12 @@ bool Protocol_simple::store_date(TIME *tm) } +/* + TODO: + Second_part format ("%06") needs to change when + we support 0-6 decimals for time. +*/ + bool Protocol_simple::store_time(TIME *tm) { #ifndef DEBUG_OFF @@ -876,6 +891,8 @@ bool Protocol_simple::store_time(TIME *tm) (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); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 0db6ce8a642..00917e296ca 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1290,6 +1290,11 @@ restore_user: mysql_stmt_free(thd, packet); break; } + case COM_RESET_STMT: + { + mysql_stmt_reset(thd, packet); + break; + } case COM_QUERY: { if (alloc_query(thd, packet, packet_length)) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index c38fb44db1c..550e4bbe086 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -965,7 +965,7 @@ void mysql_stmt_reset(THD *thd, char *packet) PREP_STMT *stmt; DBUG_ENTER("mysql_stmt_reset"); - if (!(stmt=find_prepared_statement(thd, stmt_id, "close"))) + if (!(stmt= find_prepared_statement(thd, stmt_id, "reset"))) { send_error(thd); DBUG_VOID_RETURN; diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 0a3e8d0db9f..e7d7b08c93c 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -390,6 +390,23 @@ bool String::append(IO_CACHE* file, uint32 arg_length) return FALSE; } +bool String::append_with_prefill(const char *s,uint32 arg_length, + uint32 full_length, char fill_char) +{ + int t_length= arg_length > full_length ? arg_length : full_length; + + if (realloc(str_length + t_length)) + return TRUE; + t_length= full_length - arg_length; + if (t_length > 0) + { + bfill(Ptr+str_length, t_length, fill_char); + str_length=str_length + t_length; + } + append(s, arg_length); + return FALSE; +} + uint32 String::numchars() { return str_charset->cset->numchars(str_charset, Ptr, Ptr+str_length); diff --git a/sql/sql_string.h b/sql/sql_string.h index e88c9389589..d446d26298b 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -189,6 +189,8 @@ public: bool append(const char *s,uint32 arg_length=0); bool append(const char *s,uint32 arg_length, CHARSET_INFO *cs); bool append(IO_CACHE* file, uint32 arg_length); + bool append_with_prefill(const char *s, uint32 arg_length, + uint32 full_length, char fill_char); int strstr(const String &search,uint32 offset=0); // Returns offset to substring or -1 int strstr_case(const String &s,uint32 offset=0); int strrstr(const String &search,uint32 offset=0); // Returns offset to substring or -1 diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 6072be28dca..4aa5b70407e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -163,6 +163,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token VARIANCE_SYM %token STOP_SYM %token SUM_SYM +%token ADDDATE_SYM %token SUPER_SYM %token TRUNCATE_SYM %token UNLOCK_SYM @@ -431,6 +432,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token VARYING %token ZEROFILL +%token ADDDATE_SYM %token AGAINST %token ATAN %token BETWEEN_SYM @@ -445,6 +447,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token DATE_ADD_INTERVAL %token DATE_SUB_INTERVAL %token DAY_HOUR_SYM +%token DAY_MICROSECOND_SYM %token DAY_MINUTE_SYM %token DAY_SECOND_SYM %token DAY_SYM @@ -467,6 +470,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token GEOMETRYCOLLECTION %token GROUP_CONCAT_SYM %token GROUP_UNIQUE_USERS +%token HOUR_MICROSECOND_SYM %token HOUR_MINUTE_SYM %token HOUR_SECOND_SYM %token HOUR_SYM @@ -481,6 +485,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token LOCATE %token MAKE_SET_SYM %token MASTER_POS_WAIT +%token MICROSECOND_SYM +%token MINUTE_MICROSECOND_SYM %token MINUTE_SECOND_SYM %token MINUTE_SYM %token MODE_SYM @@ -505,7 +511,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token RIGHT %token ROUND %token SECOND_SYM +%token SECOND_MICROSECOND_SYM %token SHARE_SYM +%token SUBDATE_SYM %token SUBSTRING %token SUBSTRING_INDEX %token TRIM @@ -2334,6 +2342,10 @@ simple_expr: { $$= ((Item*(*)(Item*,Item*))($1.symbol->create_func))($3,$5);} | FUNC_ARG3 '(' expr ',' expr ',' expr ')' { $$= ((Item*(*)(Item*,Item*,Item*))($1.symbol->create_func))($3,$5,$7);} + | ADDDATE_SYM '(' expr ',' expr ')' + { $$= new Item_date_add_interval($3, $5, INTERVAL_DAY, 0);} + | ADDDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')' + { $$= new Item_date_add_interval($3, $6, $7, 0); } | ATAN '(' expr ')' { $$= new Item_func_atan($3); } | ATAN '(' expr ',' expr ')' @@ -2368,6 +2380,10 @@ simple_expr: $$= new Item_func_database(); Lex->safe_to_cache_query=0; } + | DATE_SYM '(' expr ')' + { $$= new Item_date_typecast($3); } + | DAY_SYM '(' expr ')' + { $$= new Item_func_dayofmonth($3); } | ELT_FUNC '(' expr ',' expr_list ')' { $$= new Item_func_elt($3, *$5); } | MAKE_SET_SYM '(' expr ',' expr_list ')' @@ -2484,6 +2500,8 @@ simple_expr: $$= new Item_master_pos_wait($3, $5, $7); Lex->safe_to_cache_query=0; } + | MICROSECOND_SYM '(' expr ')' + { $$= new Item_func_microsecond($3); } | MINUTE_SYM '(' expr ')' { $$= new Item_func_minute($3); } | MOD_SYM '(' expr ',' expr ')' @@ -2545,6 +2563,10 @@ simple_expr: | ROUND '(' expr ')' { $$= new Item_func_round($3, new Item_int((char*)"0",0,1),0); } | ROUND '(' expr ',' expr ')' { $$= new Item_func_round($3,$5,0); } + | SUBDATE_SYM '(' expr ',' expr ')' + { $$= new Item_date_add_interval($3, $5, INTERVAL_DAY, 1);} + | SUBDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')' + { $$= new Item_date_add_interval($3, $6, $7, 1); } | SECOND_SYM '(' expr ')' { $$= new Item_func_second($3); } | SUBSTRING '(' expr ',' expr ',' expr ')' @@ -2557,6 +2579,12 @@ simple_expr: { $$= new Item_func_substr($3,$5); } | SUBSTRING_INDEX '(' expr ',' expr ',' expr ')' { $$= new Item_func_substr_index($3,$5,$7); } + | TIME_SYM '(' expr ')' + { $$= new Item_time_typecast($3); } + | TIMESTAMP '(' expr ')' + { $$= new Item_datetime_typecast($3); } + | TIMESTAMP '(' expr ',' expr ')' + { $$= new Item_func_add_time($3, $5, 1, 0); } | TRIM '(' expr ')' { $$= new Item_func_trim($3); } | TRIM '(' LEADING expr FROM expr ')' @@ -2973,15 +3001,20 @@ using_list: interval: DAY_HOUR_SYM { $$=INTERVAL_DAY_HOUR; } + | DAY_MICROSECOND_SYM { $$=INTERVAL_DAY_MICROSECOND; } | DAY_MINUTE_SYM { $$=INTERVAL_DAY_MINUTE; } | DAY_SECOND_SYM { $$=INTERVAL_DAY_SECOND; } | DAY_SYM { $$=INTERVAL_DAY; } + | HOUR_MICROSECOND_SYM { $$=INTERVAL_HOUR_MICROSECOND; } | HOUR_MINUTE_SYM { $$=INTERVAL_HOUR_MINUTE; } | HOUR_SECOND_SYM { $$=INTERVAL_HOUR_SECOND; } | HOUR_SYM { $$=INTERVAL_HOUR; } + | MICROSECOND_SYM { $$=INTERVAL_MICROSECOND; } + | MINUTE_MICROSECOND_SYM { $$=INTERVAL_MINUTE_MICROSECOND; } | MINUTE_SECOND_SYM { $$=INTERVAL_MINUTE_SECOND; } | MINUTE_SYM { $$=INTERVAL_MINUTE; } | MONTH_SYM { $$=INTERVAL_MONTH; } + | SECOND_MICROSECOND_SYM { $$=INTERVAL_SECOND_MICROSECOND; } | SECOND_SYM { $$=INTERVAL_SECOND; } | YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; } | YEAR_SYM { $$=INTERVAL_YEAR; }; @@ -4300,6 +4333,7 @@ user: keyword: ACTION {} + | ADDDATE_SYM {} | AFTER_SYM {} | AGAINST {} | AGGREGATE_SYM {} @@ -4395,6 +4429,7 @@ keyword: | MEDIUM_SYM {} | MERGE_SYM {} | MEMORY_SYM {} + | MICROSECOND_SYM {} | MINUTE_SYM {} | MIN_ROWS {} | MODIFY_SYM {} @@ -4460,6 +4495,7 @@ keyword: | STATUS_SYM {} | STOP_SYM {} | STRING_SYM {} + | SUBDATE_SYM {} | SUBJECT_SYM {} | SUPER_SYM {} | TEMPORARY {} diff --git a/sql/time.cc b/sql/time.cc index b6ca306e523..70ae8dcd8ed 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -432,6 +432,7 @@ str_to_TIME(const char *str, uint length, TIME *l_time,bool fuzzy_date) 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)); } @@ -581,7 +582,7 @@ bool str_to_time(const char *str,uint length,TIME *l_time) /* Get fractional second part */ if ((end-str) >= 2 && *str == '.' && my_isdigit(&my_charset_latin1,str[1])) { - uint field_length=3; + uint field_length=5; str++; value=(uint) (uchar) (*str - '0'); while (++str != end && my_isdigit(&my_charset_latin1,str[0]) && @@ -604,6 +605,7 @@ bool str_to_time(const char *str,uint length,TIME *l_time) l_time->minute=date[2]; l_time->second=date[3]; l_time->second_part=date[4]; + l_time->time_type= TIMESTAMP_TIME; /* Check if there is garbage at end of the TIME specification */ if (str != end && current_thd->count_cuted_fields) @@ -636,3 +638,13 @@ void localtime_to_TIME(TIME *to, struct tm *from) to->minute= (int) from->tm_min; to->second= (int) from->tm_sec; } + +void calc_time_from_sec(TIME *to, long seconds, long microseconds) +{ + long t_seconds; + to->hour= seconds/3600L; + t_seconds= seconds%3600L; + to->minute= t_seconds/60L; + to->second= t_seconds%60L; + to->second_part= microseconds; +} |