Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implementing quarter() function #2258

Merged
merged 10 commits into from
Jan 17, 2024
Merged
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