Skip to content

Commit

Permalink
evalengine: Implement LAST_DAY (#15038)
Browse files Browse the repository at this point in the history
Signed-off-by: Noble Mittal <[email protected]>
  • Loading branch information
beingnoble03 authored Jan 26, 2024
1 parent 2162883 commit 44d6a6b
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 6 deletions.
12 changes: 12 additions & 0 deletions go/vt/vtgate/evalengine/cached_size.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 18 additions & 1 deletion go/vt/vtgate/evalengine/compiler_asm.go
Original file line number Diff line number Diff line change
Expand Up @@ -3638,7 +3638,7 @@ func (asm *assembler) Fn_MAKEDATE() {
y := env.vm.stack[env.vm.sp-1].(*evalInt64)
yd := env.vm.stack[env.vm.sp-2].(*evalInt64)

t := yearDayToTime(y.i, yd.i)
t := yearDayToTime(env.currentTimezone(), y.i, yd.i)
if t.IsZero() {
env.vm.stack[env.vm.sp-2] = nil
} else {
Expand Down Expand Up @@ -3778,6 +3778,23 @@ func (asm *assembler) Fn_MONTHNAME(col collations.TypedCollation) {
}, "FN MONTHNAME DATE(SP-1)")
}

func (asm *assembler) Fn_LAST_DAY() {
asm.emit(func(env *ExpressionEnv) int {
if env.vm.stack[env.vm.sp-1] == nil {
return 1
}
arg := env.vm.stack[env.vm.sp-1].(*evalTemporal)
if arg.dt.IsZero() {
env.vm.stack[env.vm.sp-1] = nil
return 1
}

d := lastDay(env.currentTimezone(), arg.dt)
env.vm.stack[env.vm.sp-1] = env.vm.arena.newEvalDate(d)
return 1
}, "FN LAST_DAY DATETIME(SP-1)")
}

func (asm *assembler) Fn_QUARTER() {
asm.emit(func(env *ExpressionEnv) int {
if env.vm.stack[env.vm.sp-1] == nil {
Expand Down
59 changes: 54 additions & 5 deletions go/vt/vtgate/evalengine/fn_time.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ type (
collate collations.ID
}

builtinLastDay struct {
CallExpr
}

builtinQuarter struct {
CallExpr
}
Expand Down Expand Up @@ -168,6 +172,7 @@ var _ IR = (*builtinMicrosecond)(nil)
var _ IR = (*builtinMinute)(nil)
var _ IR = (*builtinMonth)(nil)
var _ IR = (*builtinMonthName)(nil)
var _ IR = (*builtinLastDay)(nil)
var _ IR = (*builtinQuarter)(nil)
var _ IR = (*builtinSecond)(nil)
var _ IR = (*builtinTime)(nil)
Expand Down Expand Up @@ -488,7 +493,7 @@ func (b *builtinDayOfWeek) eval(env *ExpressionEnv) (eval, error) {
if d == nil || d.isZero() {
return nil, nil
}
return newEvalInt64(int64(d.dt.Date.ToStdTime(time.Local).Weekday() + 1)), nil
return newEvalInt64(int64(d.dt.Date.ToStdTime(env.currentTimezone()).Weekday() + 1)), nil
}

func (call *builtinDayOfWeek) compile(c *compiler) (ctype, error) {
Expand Down Expand Up @@ -521,7 +526,7 @@ func (b *builtinDayOfYear) eval(env *ExpressionEnv) (eval, error) {
if d == nil || d.isZero() {
return nil, nil
}
return newEvalInt64(int64(d.dt.Date.ToStdTime(time.Local).YearDay())), nil
return newEvalInt64(int64(d.dt.Date.ToStdTime(env.currentTimezone()).YearDay())), nil
}

func (call *builtinDayOfYear) compile(c *compiler) (ctype, error) {
Expand Down Expand Up @@ -751,7 +756,7 @@ func (call *builtinHour) compile(c *compiler) (ctype, error) {
return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: arg.Flag | flagNullable}, nil
}

func yearDayToTime(y, yd int64) time.Time {
func yearDayToTime(loc *time.Location, y, yd int64) time.Time {
if y >= 0 && y < 100 {
if y < 70 {
y += 2000
Expand All @@ -763,7 +768,7 @@ func yearDayToTime(y, yd int64) time.Time {
if y < 0 || y > 9999 || yd < 1 || yd > math.MaxInt32 {
return time.Time{}
}
t := time.Date(int(y), time.January, 1, 0, 0, 0, 0, time.Local).AddDate(0, 0, int(yd-1))
t := time.Date(int(y), time.January, 1, 0, 0, 0, 0, loc).AddDate(0, 0, int(yd-1))
if t.Year() > 9999 {
return time.Time{}
}
Expand Down Expand Up @@ -791,7 +796,7 @@ func (b *builtinMakedate) eval(env *ExpressionEnv) (eval, error) {
y := evalToInt64(year).i
yd := evalToInt64(yearDay).i

t := yearDayToTime(y, yd)
t := yearDayToTime(env.currentTimezone(), y, yd)
if t.IsZero() {
return nil, nil
}
Expand Down Expand Up @@ -1200,6 +1205,50 @@ func (call *builtinMonthName) compile(c *compiler) (ctype, error) {
return ctype{Type: sqltypes.VarChar, Col: col, Flag: arg.Flag | flagNullable}, nil
}

func lastDay(loc *time.Location, dt datetime.DateTime) datetime.Date {
ts := dt.Date.ToStdTime(loc)
firstDayOfMonth := time.Date(ts.Year(), ts.Month(), 1, 0, 0, 0, 0, loc)
lastDayOfMonth := firstDayOfMonth.AddDate(0, 1, -1)

date := datetime.NewDateFromStd(lastDayOfMonth)
return date
}

func (b *builtinLastDay) eval(env *ExpressionEnv) (eval, error) {
date, err := b.arg1(env)
if err != nil {
return nil, err
}
if date == nil {
return nil, nil
}
dt := evalToDateTime(date, -1, env.now, env.sqlmode.AllowZeroDate())
if dt == nil || dt.isZero() {
return nil, nil
}

d := lastDay(env.currentTimezone(), dt.dt)
return newEvalDate(d, env.sqlmode.AllowZeroDate()), nil
}

func (call *builtinLastDay) compile(c *compiler) (ctype, error) {
arg, err := call.Arguments[0].compile(c)
if err != nil {
return ctype{}, err
}

skip := c.compileNullCheck1(arg)

switch arg.Type {
case sqltypes.Date, sqltypes.Datetime:
default:
c.asm.Convert_xD(1, c.sqlmode.AllowZeroDate())
}
c.asm.Fn_LAST_DAY()
c.asm.jumpDestination(skip)
return ctype{Type: sqltypes.Date, Flag: arg.Flag | flagNullable}, nil
}

func (b *builtinQuarter) eval(env *ExpressionEnv) (eval, error) {
date, err := b.arg1(env)
if err != nil {
Expand Down
24 changes: 24 additions & 0 deletions go/vt/vtgate/evalengine/testcases/cases.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ var Cases = []TestCase{
{Run: FnMinute},
{Run: FnMonth},
{Run: FnMonthName},
{Run: FnLastDay},
{Run: FnQuarter},
{Run: FnSecond},
{Run: FnTime},
Expand Down Expand Up @@ -1742,6 +1743,29 @@ func FnMonthName(yield Query) {
}
}

func FnLastDay(yield Query) {
for _, d := range inputConversions {
yield(fmt.Sprintf("LAST_DAY(%s)", d), nil)
}

dates := []string{
`DATE'2024-02-18'`,
`DATE'2023-02-01'`,
`DATE'2100-02-01'`,
`TIMESTAMP'2020-12-31 23:59:59'`,
`TIMESTAMP'2025-01-01 00:00:00'`,
`'2000-02-01'`,
`'2020-12-31 23:59:59'`,
`'2025-01-01 00:00:00'`,
`20250101`,
`'20250101'`,
}

for _, d := range dates {
yield(fmt.Sprintf("LAST_DAY(%s)", d), nil)
}
}

func FnQuarter(yield Query) {
for _, d := range inputConversions {
yield(fmt.Sprintf("QUARTER(%s)", d), nil)
Expand Down
5 changes: 5 additions & 0 deletions go/vt/vtgate/evalengine/translate_builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,11 @@ func (ast *astCompiler) translateFuncExpr(fn *sqlparser.FuncExpr) (IR, error) {
return nil, argError(method)
}
return &builtinMonthName{CallExpr: call, collate: ast.cfg.Collation}, nil
case "last_day":
if len(args) != 1 {
return nil, argError(method)
}
return &builtinLastDay{CallExpr: call}, nil
case "quarter":
if len(args) != 1 {
return nil, argError(method)
Expand Down

0 comments on commit 44d6a6b

Please sign in to comment.