Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sql/expression/function/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ var BuiltIns = []sql.Function{
sql.Function0{Name: "pi", Fn: NewPi},
sql.Function2{Name: "pow", Fn: NewPower},
sql.Function2{Name: "power", Fn: NewPower},
sql.Function1{Name: "quarter", Fn: NewQuarter},
sql.Function1{Name: "radians", Fn: NewRadians},
sql.FunctionN{Name: "rand", Fn: NewRand},
sql.FunctionN{Name: "regexp_like", Fn: NewRegexpLike},
Expand Down
61 changes: 60 additions & 1 deletion sql/expression/function/time.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ func getDate(ctx *sql.Context,

date, err := types.DatetimeMaxPrecision.ConvertWithoutRangeCheck(val)
if err != nil {
date = types.DatetimeMaxPrecision.Zero().(time.Time)
ctx.Warn(1292, "Incorrect datetime value: '%s'", val)
return nil, nil
//date = types.DatetimeMaxPrecision.Zero().(time.Time)
}

return date, nil
Expand Down Expand Up @@ -115,6 +117,60 @@ func (y *Year) WithChildren(children ...sql.Expression) (sql.Expression, error)
return NewYear(children[0]), nil
}

type Quarter struct {
expression.UnaryExpression
}

var _ sql.FunctionExpression = (*Quarter)(nil)
var _ sql.CollationCoercible = (*Quarter)(nil)

// NewQuarter creates a new Month UDF.
func NewQuarter(date sql.Expression) sql.Expression {
return &Quarter{expression.UnaryExpression{Child: date}}
}

// FunctionName implements sql.FunctionExpression
func (q *Quarter) FunctionName() string {
return "quarter"
}

// Description implements sql.FunctionExpression
func (q *Quarter) Description() string {
return "returns the quarter of the given date."
}

func (q *Quarter) String() string { return fmt.Sprintf("%s(%s)", q.FunctionName(), q.Child) }

// Type implements the Expression interface.
func (q *Quarter) Type() sql.Type { return types.Int32 }

// CollationCoercibility implements the interface sql.CollationCoercible.
func (q *Quarter) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
return sql.Collation_binary, 5
}

// Eval implements the Expression interface.
func (q *Quarter) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
mon, err := getDatePart(ctx, q.UnaryExpression, row, month)
if err != nil {
return nil, err
}

if mon == nil {
return nil, nil
}

return (mon.(int32)-1)/3 + 1, nil
}

// WithChildren implements the Expression interface.
func (q *Quarter) WithChildren(children ...sql.Expression) (sql.Expression, error) {
if len(children) != 1 {
return nil, sql.ErrInvalidChildrenNumber.New(q, len(children), 1)
}
return NewQuarter(children[0]), nil
}

// Month is a function that returns the month of a date.
type Month struct {
expression.UnaryExpression
Expand Down Expand Up @@ -550,6 +606,9 @@ func (d *YearWeek) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
if err != nil {
return nil, err
}
if date == nil {
return nil, nil
}
yyyy, ok := year(date).(int32)
if !ok {
return nil, sql.ErrInvalidArgumentDetails.New("YEARWEEK", "invalid year")
Expand Down
145 changes: 133 additions & 12 deletions sql/expression/function/time_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func TestTime_Year(t *testing.T) {
expected interface{}
err bool
}{
{"invalid type", sql.NewRow([]byte{0, 1, 2}), int32(0), false},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false},
{"date as string", sql.NewRow(stringDate), int32(2007), false},
{"date as time", sql.NewRow(time.Now()), int32(time.Now().UTC().Year()), false},
}
Expand Down Expand Up @@ -73,7 +73,7 @@ func TestTime_Month(t *testing.T) {
err bool
}{
{"null date", sql.NewRow(nil), nil, false},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), int32(1), false},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false},
{"date as string", sql.NewRow(stringDate), int32(1), false},
{"date as time", sql.NewRow(time.Now()), int32(time.Now().UTC().Month()), false},
}
Expand All @@ -92,6 +92,127 @@ func TestTime_Month(t *testing.T) {
}
}

func TestTime_Quarter(t *testing.T) {
ctx := sql.NewEmptyContext()
f := NewQuarter(expression.NewGetField(0, types.LongText, "foo", false))

testCases := []struct {
name string
row sql.Row
expected interface{}
err bool
}{
{
name: "null date",
row: sql.NewRow(nil),
expected: nil,
},
{
name: "1",
row: sql.NewRow(1),
expected: nil,
},
{
name: "1.1",
row: sql.NewRow(1.1),
expected: nil,
},
{
name: "invalid type",
row: sql.NewRow([]byte{0, 1, 2}),
expected: nil,
},
{
name: "date as string",
row: sql.NewRow(stringDate),
expected: int32(1),
},
{
name: "another date as string",
row: sql.NewRow("2008-08-01"),
expected: int32(3),
},
{
name: "january",
row: sql.NewRow("2008-01-01"),
expected: int32(1),
},
{
name: "february",
row: sql.NewRow("2008-02-01"),
expected: int32(1),
},
{
name: "march",
row: sql.NewRow("2008-03-01"),
expected: int32(1),
},
{
name: "april",
row: sql.NewRow("2008-04-01"),
expected: int32(2),
},
{
name: "may",
row: sql.NewRow("2008-05-01"),
expected: int32(2),
},
{
name: "june",
row: sql.NewRow("2008-06-01"),
expected: int32(2),
},
{
name: "july",
row: sql.NewRow("2008-07-01"),
expected: int32(3),
},
{
name: "august",
row: sql.NewRow("2008-08-01"),
expected: int32(3),
},
{
name: "septemeber",
row: sql.NewRow("2008-09-01"),
expected: int32(3),
},
{
name: "october",
row: sql.NewRow("2008-10-01"),
expected: int32(4),
},
{
name: "november",
row: sql.NewRow("2008-11-01"),
expected: int32(4),
},
{
name: "december",
row: sql.NewRow("2008-12-01"),
expected: int32(4),
},
{
name: "date as time",
row: sql.NewRow(time.Now()),
expected: int32((time.Now().UTC().Month()-1)/3 + 1),
},
}

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)
}
})
}
}

func TestTime_Day(t *testing.T) {
ctx := sql.NewEmptyContext()
f := NewDay(expression.NewGetField(0, types.LongText, "foo", false))
Expand All @@ -103,7 +224,7 @@ func TestTime_Day(t *testing.T) {
err bool
}{
{"null date", sql.NewRow(nil), nil, false},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), int32(1), false},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false},
{"date as string", sql.NewRow(stringDate), int32(2), false},
{"date as time", sql.NewRow(time.Now()), int32(time.Now().UTC().Day()), false},
}
Expand Down Expand Up @@ -133,7 +254,7 @@ func TestTime_Weekday(t *testing.T) {
err bool
}{
{"null date", sql.NewRow(nil), nil, false},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), int32(5), false},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false},
{"date as string", sql.NewRow(stringDate), int32(1), false},
{"date as time", sql.NewRow(time.Now()), int32(time.Now().UTC().Weekday()+6) % 7, false},
}
Expand Down Expand Up @@ -163,7 +284,7 @@ func TestTime_Hour(t *testing.T) {
err bool
}{
{"null date", sql.NewRow(nil), nil, false},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), int32(0), false},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false},
{"date as string", sql.NewRow(stringDate), int32(14), false},
{"date as time", sql.NewRow(time.Now()), int32(time.Now().UTC().Hour()), false},
}
Expand Down Expand Up @@ -193,7 +314,7 @@ func TestTime_Minute(t *testing.T) {
err bool
}{
{"null date", sql.NewRow(nil), nil, false},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), int32(0), false},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false},
{"date as string", sql.NewRow(stringDate), int32(15), false},
{"date as time", sql.NewRow(time.Now()), int32(time.Now().UTC().Minute()), false},
}
Expand Down Expand Up @@ -223,7 +344,7 @@ func TestTime_Second(t *testing.T) {
err bool
}{
{"null date", sql.NewRow(nil), nil, false},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), int32(0), false},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false},
{"date as string", sql.NewRow(stringDate), int32(16), false},
{"date as time", sql.NewRow(time.Now()), int32(time.Now().UTC().Second()), false},
}
Expand Down Expand Up @@ -284,7 +405,7 @@ func TestTime_DayOfWeek(t *testing.T) {
err bool
}{
{"null date", sql.NewRow(nil), nil, false},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), int32(7), false},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false},
{"date as string", sql.NewRow(stringDate), int32(3), false},
{"date as time", sql.NewRow(time.Now()), int32(time.Now().UTC().Weekday() + 1), false},
}
Expand Down Expand Up @@ -314,7 +435,7 @@ func TestTime_DayOfYear(t *testing.T) {
err bool
}{
{"null date", sql.NewRow(nil), nil, false},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), int32(1), false},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false},
{"date as string", sql.NewRow(stringDate), int32(2), false},
{"date as time", sql.NewRow(time.Now()), int32(time.Now().UTC().YearDay()), false},
}
Expand Down Expand Up @@ -376,8 +497,8 @@ func TestYearWeek(t *testing.T) {
expected interface{}
err bool
}{
{"null date", sql.NewRow(nil), nil, true},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), int32(1), false},
{"null date", sql.NewRow(nil), nil, false},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), nil, false},
{"date as string", sql.NewRow(stringDate), int32(200653), false},
}

Expand Down Expand Up @@ -495,7 +616,7 @@ func TestDate(t *testing.T) {
err bool
}{
{"null date", sql.NewRow(nil), nil, false},
{"invalid type", sql.NewRow([]byte{0, 1, 2}), types.Date.Zero().(time.Time).Format("2006-01-02"), 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().UTC()), time.Now().UTC().Format("2006-01-02"), false},
}
Expand Down