diff --git a/README.md b/README.md index 62d22ed65..2f7a8d4ba 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ We support and actively test against certain third-party clients to ensure compa |`CONNECTION_ID()`|Return the current connection ID.| |`COUNT(expr)`| Returns a count of the number of non-NULL values of expr in the rows retrieved by a SELECT statement.| |`DAY(date)`|Synonym for DAYOFMONTH().| +|`DATE(date)`|Returns the date part of the given date.| |`DAYOFMONTH(date)`|Return the day of the month (0-31).| |`DAYOFWEEK(date)`|Returns the day of the week of the given date.| |`DAYOFYEAR(date)`|Returns the day of the year of the given date.| diff --git a/SUPPORTED.md b/SUPPORTED.md index 7972581fe..ad07eb87a 100644 --- a/SUPPORTED.md +++ b/SUPPORTED.md @@ -114,8 +114,10 @@ - FROM_BASE64 ## Time functions +- DATE - DAY - WEEKDAY +- DAYOFMONTH - DAYOFWEEK - DAYOFYEAR - HOUR diff --git a/sql/expression/function/registry.go b/sql/expression/function/registry.go index e207c9909..ab0ab8a76 100644 --- a/sql/expression/function/registry.go +++ b/sql/expression/function/registry.go @@ -33,6 +33,7 @@ var Defaults = []sql.Function{ sql.FunctionN{Name: "substring", Fn: NewSubstring}, sql.FunctionN{Name: "mid", Fn: NewSubstring}, sql.FunctionN{Name: "substr", Fn: NewSubstring}, + sql.Function1{Name: "date", Fn: NewDate}, sql.Function1{Name: "year", Fn: NewYear}, sql.Function1{Name: "month", Fn: NewMonth}, sql.Function1{Name: "day", Fn: NewDay}, diff --git a/sql/expression/function/time.go b/sql/expression/function/time.go index b895497a5..95b37c9a0 100644 --- a/sql/expression/function/time.go +++ b/sql/expression/function/time.go @@ -573,3 +573,39 @@ func (n *Now) Eval(*sql.Context, sql.Row) (interface{}, error) { func (n *Now) TransformUp(f sql.TransformExprFunc) (sql.Expression, error) { return f(n) } + +// Date a function takes the DATE part out from a datetime expression. +type Date struct { + expression.UnaryExpression +} + +// NewDate returns a new Date node. +func NewDate(date sql.Expression) sql.Expression { + return &Date{expression.UnaryExpression{Child: date}} +} + +func (d *Date) String() string { return fmt.Sprintf("DATE(%s)", d.Child) } + +// Type implements the Expression interface. +func (d *Date) Type() sql.Type { return sql.Text } + +// Eval implements the Expression interface. +func (d *Date) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { + return getDatePart(ctx, d.UnaryExpression, row, func(v interface{}) interface{} { + if v == nil { + return nil + } + + return v.(time.Time).Format("2006-01-02") + }) +} + +// TransformUp implements the sql.Expression interface. +func (d *Date) TransformUp(f sql.TransformExprFunc) (sql.Expression, error) { + child, err := d.Child.TransformUp(f) + if err != nil { + return nil, err + } + + return f(NewDate(child)) +} diff --git a/sql/expression/function/time_test.go b/sql/expression/function/time_test.go index 7111a2e99..71cdadd63 100644 --- a/sql/expression/function/time_test.go +++ b/sql/expression/function/time_test.go @@ -354,3 +354,34 @@ func TestNow(t *testing.T) { require.NoError(err) require.Equal(date, result) } + +func TestDate(t *testing.T) { + f := NewDate(expression.NewGetField(0, sql.Text, "foo", false)) + ctx := sql.NewEmptyContext() + + testCases := []struct { + name string + row sql.Row + expected interface{} + err bool + }{ + {"null date", sql.NewRow(nil), nil, false}, + {"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false}, + {"date as string", sql.NewRow(stringDate), "2007-01-02", false}, + {"date as time", sql.NewRow(time.Now()), time.Now().Format("2006-01-02"), false}, + {"date as unix timestamp", sql.NewRow(int64(tsDate)), "2009-11-22", false}, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + val, err := f.Eval(ctx, tt.row) + if tt.err { + require.Error(err) + } else { + require.NoError(err) + require.Equal(tt.expected, val) + } + }) + } +}