Skip to content

Commit

Permalink
evalengine: Implement TO_DAYS (#15065)
Browse files Browse the repository at this point in the history
Signed-off-by: Noble Mittal <[email protected]>
  • Loading branch information
beingnoble03 authored Jan 29, 2024
1 parent fd99639 commit 6ac1596
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 8 deletions.
4 changes: 2 additions & 2 deletions go/mysql/datetime/datetime.go
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ func (dt *DateTime) addInterval(itv *Interval) bool {
dt.Time.minute = uint8((dur % time.Hour) / time.Minute)
dt.Time.hour = uint16(dur / time.Hour)

daynum := mysqlDayNumber(dt.Date.Year(), dt.Date.Month(), 1) + int(days)
daynum := MysqlDayNumber(dt.Date.Year(), dt.Date.Month(), 1) + int(days)
if daynum < 0 || daynum > maxDay {
return false
}
Expand All @@ -619,7 +619,7 @@ func (dt *DateTime) addInterval(itv *Interval) bool {
return true

case itv.unit.HasDayParts():
daynum := mysqlDayNumber(dt.Date.Year(), dt.Date.Month(), dt.Date.Day())
daynum := MysqlDayNumber(dt.Date.Year(), dt.Date.Month(), dt.Date.Day())
daynum += itv.day
dt.Date.year, dt.Date.month, dt.Date.day = mysqlDateFromDayNumber(daynum)
return true
Expand Down
8 changes: 4 additions & 4 deletions go/mysql/datetime/mydate.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ limitations under the License.

package datetime

// mysqlDayNumber converts a date into an absolute day number.
// MysqlDayNumber converts a date into an absolute day number.
// This is an algorithm that has been reverse engineered from MySQL;
// the tables used as a reference can be found in `testdata/year_to_daynr.json`.
// It is worth noting that this absolute day number does not match the
Expand All @@ -29,7 +29,7 @@ package datetime
// This API should only be used when performing datetime calculations (addition
// and subtraction), so that the results match MySQL's. All other date handling
// operations must use our helpers based on Go's standard library.
func mysqlDayNumber(year, month, day int) int {
func MysqlDayNumber(year, month, day int) int {
if year == 0 && month == 0 {
return 0
}
Expand All @@ -49,8 +49,8 @@ func mysqlDayNumber(year, month, day int) int {
// mysqlDateFromDayNumber converts an absolute day number into a date (a year, month, day triplet).
// This is an algorithm that has been reverse engineered from MySQL;
// the tables used as a reference can be found in `testdata/daynr_to_date.json`.
// See the warning from mysqlDayNumber: the day number used as an argument to
// this function must come from mysqlDayNumber or the results won't be correct.
// See the warning from MysqlDayNumber: the day number used as an argument to
// this function must come from MysqlDayNumber or the results won't be correct.
// This API should only be used when performing datetime calculations (addition
// and subtraction), so that the results match MySQL's. All other date handling
// operations must use our helpers based on Go's standard library.
Expand Down
4 changes: 2 additions & 2 deletions go/mysql/datetime/mydate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func TestDayNumber(t *testing.T) {
require.NoError(t, err)

for year, daynr := range expected {
assert.Equal(t, daynr, mysqlDayNumber(year, 1, 1))
assert.Equal(t, daynr, MysqlDayNumber(year, 1, 1))
}
}

Expand All @@ -54,6 +54,6 @@ func TestDayNumberFields(t *testing.T) {
assert.Equal(t, tc[2], int(m))
assert.Equal(t, tc[3], int(d))

assert.Equalf(t, tc[0], mysqlDayNumber(tc[1], tc[2], tc[3]), "date %d-%d-%d", tc[1], tc[2], tc[3])
assert.Equalf(t, tc[0], MysqlDayNumber(tc[1], tc[2], tc[3]), "date %d-%d-%d", tc[1], tc[2], tc[3])
}
}
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.

16 changes: 16 additions & 0 deletions go/vt/vtgate/evalengine/compiler_asm.go
Original file line number Diff line number Diff line change
Expand Up @@ -3795,6 +3795,22 @@ func (asm *assembler) Fn_LAST_DAY() {
}, "FN LAST_DAY DATETIME(SP-1)")
}

func (asm *assembler) Fn_TO_DAYS() {
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.Date.IsZero() {
env.vm.stack[env.vm.sp-1] = nil
} else {
numDays := datetime.MysqlDayNumber(arg.dt.Date.Year(), arg.dt.Date.Month(), arg.dt.Date.Day())
env.vm.stack[env.vm.sp-1] = env.vm.arena.newEvalInt64(int64(numDays))
}
return 1
}, "FN TO_DAYS DATE(SP-1)")
}

func (asm *assembler) Fn_FROM_DAYS() {
asm.emit(func(env *ExpressionEnv) int {
arg := env.vm.stack[env.vm.sp-1].(*evalInt64)
Expand Down
41 changes: 41 additions & 0 deletions go/vt/vtgate/evalengine/fn_time.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ type (
CallExpr
}

builtinToDays struct {
CallExpr
}

builtinFromDays struct {
CallExpr
}
Expand Down Expand Up @@ -177,6 +181,7 @@ var _ IR = (*builtinMinute)(nil)
var _ IR = (*builtinMonth)(nil)
var _ IR = (*builtinMonthName)(nil)
var _ IR = (*builtinLastDay)(nil)
var _ IR = (*builtinToDays)(nil)
var _ IR = (*builtinFromDays)(nil)
var _ IR = (*builtinQuarter)(nil)
var _ IR = (*builtinSecond)(nil)
Expand Down Expand Up @@ -1254,6 +1259,41 @@ func (call *builtinLastDay) compile(c *compiler) (ctype, error) {
return ctype{Type: sqltypes.Date, Flag: arg.Flag | flagNullable}, nil
}

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

numDays := datetime.MysqlDayNumber(dt.dt.Date.Year(), dt.dt.Date.Month(), dt.dt.Date.Day())
return newEvalInt64(int64(numDays)), nil
}

func (call *builtinToDays) 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, true)
}
c.asm.Fn_TO_DAYS()
c.asm.jumpDestination(skip)
return ctype{Type: sqltypes.Int64, Col: collationNumeric, Flag: arg.Flag | flagNullable}, nil
}

func (b *builtinFromDays) eval(env *ExpressionEnv) (eval, error) {
arg, err := b.arg1(env)
if arg == nil {
Expand All @@ -1279,6 +1319,7 @@ func (call *builtinFromDays) compile(c *compiler) (ctype, error) {
}

skip := c.compileNullCheck1(arg)

switch arg.Type {
case sqltypes.Int64:
default:
Expand Down
25 changes: 25 additions & 0 deletions go/vt/vtgate/evalengine/testcases/cases.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ var Cases = []TestCase{
{Run: FnMonth},
{Run: FnMonthName},
{Run: FnLastDay},
{Run: FnToDays},
{Run: FnFromDays},
{Run: FnQuarter},
{Run: FnSecond},
Expand Down Expand Up @@ -1769,6 +1770,30 @@ func FnLastDay(yield Query) {
}
}

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

dates := []string{
`DATE'0000-00-00'`,
`0`,
`'0000-00-00'`,
`DATE'2023-09-03 00:00:00'`,
`DATE'2023-09-03 07:00:00'`,
`DATE'0000-00-00 00:00:00'`,
`950501`,
`'2007-10-07'`,
`'2008-10-07'`,
`'08-10-07'`,
`'0000-01-01'`,
}

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

func FnFromDays(yield Query) {
for _, d := range inputConversions {
yield(fmt.Sprintf("FROM_DAYS(%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 @@ -419,6 +419,11 @@ func (ast *astCompiler) translateFuncExpr(fn *sqlparser.FuncExpr) (IR, error) {
return nil, argError(method)
}
return &builtinLastDay{CallExpr: call}, nil
case "to_days":
if len(args) != 1 {
return nil, argError(method)
}
return &builtinToDays{CallExpr: call}, nil
case "from_days":
if len(args) != 1 {
return nil, argError(method)
Expand Down

0 comments on commit 6ac1596

Please sign in to comment.