diff --git a/expression/builtin/builtin.go b/expression/builtin/builtin.go index 71530b272114c..cb41f826a4277 100644 --- a/expression/builtin/builtin.go +++ b/expression/builtin/builtin.go @@ -76,6 +76,7 @@ var Funcs = map[string]Func{ "curtime": {builtinCurrentTime, 0, 1, false, false}, "date": {builtinDate, 1, 1, true, false}, "day": {builtinDay, 1, 1, true, false}, + "dayname": {builtinDayName, 1, 1, true, false}, "dayofmonth": {builtinDayOfMonth, 1, 1, true, false}, "dayofweek": {builtinDayOfWeek, 1, 1, true, false}, "dayofyear": {builtinDayOfYear, 1, 1, true, false}, diff --git a/expression/builtin/time.go b/expression/builtin/time.go index 69eec6e2079ae..8531e1a121edd 100644 --- a/expression/builtin/time.go +++ b/expression/builtin/time.go @@ -162,6 +162,22 @@ func builtinNow(args []interface{}, ctx map[interface{}]interface{}) (interface{ return t.RoundFrac(int(fsp)) } +// See http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_dayname +func builtinDayName(args []interface{}, ctx map[interface{}]interface{}) (interface{}, error) { + v, err := builtinWeekDay(args, ctx) + if err != nil { + return nil, err + } + if types.IsNil(v) { + return nil, nil + } + weekday := v.(int64) + if (weekday < 0) || (weekday >= int64(len(mysql.WeekdayNames))) { + return nil, errors.Errorf("no name for invalid weekday: %d.", weekday) + } + return mysql.WeekdayNames[weekday], nil +} + // See http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_dayofmonth func builtinDayOfMonth(args []interface{}, ctx map[interface{}]interface{}) (interface{}, error) { // TODO: some invalid format like 2000-00-00 will return 0 too. diff --git a/expression/builtin/time_test.go b/expression/builtin/time_test.go index a7d75c3115b87..49241e4f2d5e7 100644 --- a/expression/builtin/time_test.go +++ b/expression/builtin/time_test.go @@ -53,12 +53,13 @@ func (s *testBuiltinSuite) TestDate(c *C) { DayOfWeek int64 DayOfYear int64 WeekDay int64 + DayName string Week int64 WeekOfYear int64 YearWeek int64 }{ - {"2000-01-01", 2000, 1, 1, 7, 1, 5, 52, 52, 199952}, - {"2011-11-11", 2011, 11, 11, 6, 315, 4, 45, 45, 201145}, + {"2000-01-01", 2000, 1, 1, 7, 1, 5, "Saturday", 52, 52, 199952}, + {"2011-11-11", 2011, 11, 11, 6, 315, 4, "Friday", 45, 45, 201145}, } for _, t := range tbl { @@ -87,6 +88,10 @@ func (s *testBuiltinSuite) TestDate(c *C) { c.Assert(err, IsNil) c.Assert(v, DeepEquals, t.WeekDay) + v, err = builtinDayName(args, nil) + c.Assert(err, IsNil) + c.Assert(v, DeepEquals, t.DayName) + v, err = builtinWeek(args, nil) c.Assert(err, IsNil) c.Assert(v, DeepEquals, t.Week) @@ -109,12 +114,13 @@ func (s *testBuiltinSuite) TestDate(c *C) { DayOfWeek interface{} DayOfYear interface{} WeekDay interface{} + DayName interface{} Week interface{} WeekOfYear interface{} YearWeek interface{} }{ - {nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}, - {"0000-00-00", int64(0), int64(0), int64(0), nil, nil, nil, nil, nil, nil}, + {nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}, + {"0000-00-00", int64(0), int64(0), int64(0), nil, nil, nil, nil, nil, nil, nil}, } for _, t := range tblNil { @@ -143,6 +149,10 @@ func (s *testBuiltinSuite) TestDate(c *C) { c.Assert(err, IsNil) c.Assert(v, DeepEquals, t.WeekDay) + v, err = builtinWeekDay(args, nil) + c.Assert(err, IsNil) + c.Assert(v, DeepEquals, t.DayName) + v, err = builtinWeek(args, nil) c.Assert(err, IsNil) c.Assert(v, DeepEquals, t.Week) diff --git a/mysql/time.go b/mysql/time.go index 4447875f48f44..09d029f0cd1cc 100644 --- a/mysql/time.go +++ b/mysql/time.go @@ -95,6 +95,17 @@ var ( MinTimestamp = time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC) // MaxTimestamp is the maximum for mysql timestamp type. MaxTimestamp = time.Date(2038, 1, 19, 3, 14, 7, 999999, time.UTC) + + // WeekdayNames lists names of weekdays, which are used in builtin time function `dayname`. + WeekdayNames = []string{ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday", + } ) // Time is the struct for handling datetime, timestamp and date. diff --git a/parser/parser.y b/parser/parser.y index bffa3ce46993d..7a9357bf9d04e 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -112,6 +112,7 @@ import ( dateAdd "DATE_ADD" dateSub "DATE_SUB" day "DAY" + dayname "DAYNAME" dayofmonth "DAYOFMONTH" dayofweek "DAYOFWEEK" dayofyear "DAYOFYEAR" @@ -1688,7 +1689,7 @@ UnReservedKeyword: | "REPEATABLE" | "COMMITTED" | "UNCOMMITTED" | "ONLY" | "SERIALIZABLE" | "LEVEL" | "VARIABLES" NotKeywordToken: - "ABS" | "ADDDATE" | "COALESCE" | "CONCAT" | "CONCAT_WS" | "COUNT" | "DAY" | "DATE_ADD" | "DATE_SUB" | "DAYOFMONTH" + "ABS" | "ADDDATE" | "COALESCE" | "CONCAT" | "CONCAT_WS" | "COUNT" | "DAY" | "DATE_ADD" | "DATE_SUB" | "DAYNAME" | "DAYOFMONTH" | "DAYOFWEEK" | "DAYOFYEAR" | "FOUND_ROWS" | "GROUP_CONCAT"| "HOUR" | "IFNULL" | "LENGTH" | "LOCATE" | "MAX" | "MICROSECOND" | "MIN" | "MINUTE" | "NULLIF" | "MONTH" | "NOW" | "POW" | "POWER" | "RAND" | "SECOND" | "SQL_CALC_FOUND_ROWS" | "SUBDATE" | "SUBSTRING" %prec lowerThanLeftParen | "SUBSTRING_INDEX" | "SUM" | "TRIM" | "WEEKDAY" | "WEEKOFYEAR" @@ -2182,6 +2183,10 @@ FunctionCallNonKeyword: { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} } +| "DAYNAME" '(' Expression ')' + { + $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} + } | "DAYOFWEEK" '(' Expression ')' { $$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}} diff --git a/parser/parser_test.go b/parser/parser_test.go index 7e5f182307d93..ad36ccaf8e778 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -41,7 +41,7 @@ func (s *testParserSuite) TestSimple(c *C) { "collation", "comment", "avg_row_length", "checksum", "compression", "connection", "key_block_size", "max_rows", "min_rows", "national", "row", "quarter", "escape", "grants", "status", "fields", "triggers", "delay_key_write", "isolation", "repeatable", "committed", "uncommitted", "only", "serializable", "level", - "curtime", "variables", + "curtime", "variables", "dayname", } for _, kw := range unreservedKws { src := fmt.Sprintf("SELECT %s FROM tbl;", kw) @@ -326,7 +326,6 @@ func (s *testParserSuite) TestExpression(c *C) { func (s *testParserSuite) TestBuiltin(c *C) { table := []testCase{ // For buildin functions - {"SELECT DAYOFMONTH('2007-02-03');", true}, {"SELECT POW(1, 2)", true}, {"SELECT POW(1, 0.5)", true}, {"SELECT POW(1, -1)", true}, @@ -363,8 +362,6 @@ func (s *testParserSuite) TestBuiltin(c *C) { {`SELECT LOCATE('bar', 'foobarbar');`, true}, {`SELECT LOCATE('bar', 'foobarbar', 5);`, true}, - {"select current_date, current_date(), curdate()", true}, - // For delete statement {"DELETE t1, t2 FROM t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.id=t2.id AND t2.id=t3.id;", true}, {"DELETE FROM t1, t2 USING t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.id=t2.id AND t2.id=t3.id;", true}, @@ -402,6 +399,31 @@ func (s *testParserSuite) TestBuiltin(c *C) { {"select curtime()", true}, {"select curtime(6)", true}, + // for microsecond, second, minute, hour + {"SELECT MICROSECOND('2009-12-31 23:59:59.000010');", true}, + {"SELECT SECOND('10:05:03');", true}, + {"SELECT MINUTE('2008-02-03 10:05:03');", true}, + {"SELECT HOUR('10:05:03');", true}, + + // for date, day, weekday + {"SELECT DATE('2003-12-31 01:02:03');", true}, + {"SELECT CURRENT_DATE, CURRENT_DATE(), CURDATE()", true}, + {"SELECT DAY('2007-02-03');", true}, + {"SELECT DAYOFMONTH('2007-02-03');", true}, + {"SELECT DAYOFWEEK('2007-02-03');", true}, + {"SELECT DAYOFYEAR('2007-02-03');", true}, + {"SELECT DAYNAME('2007-02-03');", true}, + {"SELECT WEEKDAY('2007-02-03');", true}, + + // for week, month, year + {"SELECT WEEK('2007-02-03');", true}, + {"SELECT WEEK('2007-02-03', 0);", true}, + {"SELECT WEEKOFYEAR('2007-02-03');", true}, + {"SELECT MONTH('2007-02-03');", true}, + {"SELECT YEAR('2007-02-03');", true}, + {"SELECT YEARWEEK('2007-02-03');", true}, + {"SELECT YEARWEEK('2007-02-03', 0);", true}, + // For time extract {`select extract(microsecond from "2011-11-11 10:10:10.123456")`, true}, {`select extract(second from "2011-11-11 10:10:10.123456")`, true}, diff --git a/parser/scanner.l b/parser/scanner.l index ca30f01a61084..ce095747e8184 100644 --- a/parser/scanner.l +++ b/parser/scanner.l @@ -331,6 +331,7 @@ databases {d}{a}{t}{a}{b}{a}{s}{e}{s} date_add {d}{a}{t}{e}_{a}{d}{d} date_sub {d}{a}{t}{e}_{s}{u}{b} day {d}{a}{y} +dayname {d}{a}{y}{n}{a}{m}{e} dayofweek {d}{a}{y}{o}{f}{w}{e}{e}{k} dayofmonth {d}{a}{y}{o}{f}{m}{o}{n}{t}{h} dayofyear {d}{a}{y}{o}{f}{y}{e}{a}{r} @@ -710,6 +711,8 @@ year_month {y}{e}{a}{r}_{m}{o}{n}{t}{h} {date_sub} return dateSub {day} lval.item = string(l.val) return day +{dayname} lval.item = string(l.val) + return dayname {dayofweek} lval.item = string(l.val) return dayofweek {dayofmonth} lval.item = string(l.val)