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

expression: fix compatibility behaviors in zero datetime with MySQL #21220

Merged
merged 14 commits into from
Dec 7, 2020
23 changes: 23 additions & 0 deletions executor/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7002,6 +7002,29 @@ func (s *testSuite) TestIssue20305(c *C) {
tk.MustQuery("SELECT * FROM `t3` where y <= a").Check(testkit.Rows("2155 2156"))
}

func (s *testSuite) TestZeroDateTimeCompatibility(c *C) {
SQLs := []string{
`select YEAR(0000-00-00), YEAR("0000-00-00")`,
`select MONTH(0000-00-00), MONTH("0000-00-00")`,
`select DAYOFWEEK(0000-00-00), DAYOFWEEK("0000-00-00")`,
`select DAYOFMONTH(0000-00-00), DAYOFMONTH("0000-00-00")`,
`select DAYOFYEAR(0000-00-00), DAYOFYEAR("0000-00-00")`,
`select QUARTER(0000-00-00), QUARTER("0000-00-00")`,
`select EXTRACT(DAY FROM 0000-00-00), EXTRACT(DAY FROM "0000-00-00")`,
`select EXTRACT(MONTH FROM 0000-00-00), EXTRACT(MONTH FROM "0000-00-00")`,
`select EXTRACT(YEAR FROM 0000-00-00), EXTRACT(YEAR FROM "0000-00-00")`,
`select EXTRACT(WEEK FROM 0000-00-00), EXTRACT(WEEK FROM "0000-00-00")`,
`select EXTRACT(QUARTER FROM 0000-00-00), EXTRACT(QUARTER FROM "0000-00-00")`,
}
tk := testkit.NewTestKit(c, s.store)

for _, t := range SQLs {
fmt.Println(t)
tk.MustQuery(t).Check(testkit.Rows("0 <nil>"))
c.Assert(tk.Se.GetSessionVars().StmtCtx.WarningCount(), Equals, uint16(1))
}
}

func (s *testSuite) TestOOMActionPriority(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test")
Expand Down
47 changes: 32 additions & 15 deletions expression/builtin_time.go
Original file line number Diff line number Diff line change
Expand Up @@ -1079,7 +1079,8 @@ func (b *builtinMonthSig) evalInt(row chunk.Row) (int64, bool, error) {

if date.IsZero() {
if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() {
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, date.String()))
isNull, err = handleInvalidZeroTime(b.ctx, date)
return 0, isNull, err
}
return 0, false, nil
}
Expand Down Expand Up @@ -1238,7 +1239,8 @@ func (b *builtinDayOfMonthSig) evalInt(row chunk.Row) (int64, bool, error) {
}
if arg.IsZero() {
if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() {
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, arg.String()))
isNull, err = handleInvalidZeroTime(b.ctx, arg)
return 0, isNull, err
}
return 0, false, nil
}
Expand Down Expand Up @@ -1281,7 +1283,8 @@ func (b *builtinDayOfWeekSig) evalInt(row chunk.Row) (int64, bool, error) {
return 0, true, handleInvalidTimeError(b.ctx, err)
}
if arg.InvalidZero() {
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, arg.String()))
isNull, err = handleInvalidZeroTime(b.ctx, arg)
return 0, isNull, err
}
// 1 is Sunday, 2 is Monday, .... 7 is Saturday
return int64(arg.Weekday() + 1), false, nil
Expand Down Expand Up @@ -1323,7 +1326,8 @@ func (b *builtinDayOfYearSig) evalInt(row chunk.Row) (int64, bool, error) {
return 0, isNull, handleInvalidTimeError(b.ctx, err)
}
if arg.InvalidZero() {
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, arg.String()))
isNull, err := handleInvalidZeroTime(b.ctx, arg)
return 0, isNull, err
}

return int64(arg.YearDay()), false, nil
Expand Down Expand Up @@ -1557,7 +1561,8 @@ func (b *builtinYearSig) evalInt(row chunk.Row) (int64, bool, error) {

if date.IsZero() {
if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() {
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, date.String()))
isNull, err := handleInvalidZeroTime(b.ctx, date)
return 0, isNull, err
}
return 0, false, nil
}
Expand Down Expand Up @@ -2664,6 +2669,13 @@ func (b *builtinExtractDatetimeSig) evalInt(row chunk.Row) (int64, bool, error)
if isNull || err != nil {
return 0, isNull, err
}
if dt.IsZero() {
if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() {
isNull, err := handleInvalidZeroTime(b.ctx, dt)
return 0, isNull, err
}
return 0, false, nil
}
res, err := types.ExtractDatetimeNum(&dt, unit)
return res, err != nil, err
}
Expand Down Expand Up @@ -6020,16 +6032,8 @@ func (b *builtinQuarterSig) evalInt(row chunk.Row) (int64, bool, error) {
}

if date.IsZero() {
// MySQL compatibility, #11203
// 0 | 0.0 should be converted to 0 value (not null)
n, err := date.ToNumber().ToInt()
isOriginalIntOrDecimalZero := err == nil && n == 0
// Args like "0000-00-00", "0000-00-00 00:00:00" set Fsp to 6
isOriginalStringZero := date.Fsp() > 0
if isOriginalIntOrDecimalZero && !isOriginalStringZero {
return 0, false, nil
}
return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, date.String()))
isNull, err := handleInvalidZeroTime(b.ctx, date)
return 0, isNull, err
}

return int64((date.Month() + 2) / 3), false, nil
Expand Down Expand Up @@ -7020,3 +7024,16 @@ func (b *builtinTidbParseTsoSig) evalTime(row chunk.Row) (types.Time, bool, erro
}
return result, false, nil
}

func handleInvalidZeroTime(ctx sessionctx.Context, t types.Time) (bool, error) {
// MySQL compatibility, #11203
// 0 | 0.0 should be converted to null without warnings
n, err := t.ToNumber().ToInt()
isOriginalIntOrDecimalZero := err == nil && n == 0
// Args like "0000-00-00", "0000-00-00 00:00:00" set Fsp to 6
isOriginalStringZero := t.Fsp() > 0
if isOriginalIntOrDecimalZero && !isOriginalStringZero {
return false, nil
}
return true, handleInvalidTimeError(ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, t.String()))
}
30 changes: 18 additions & 12 deletions expression/builtin_time_vec.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,11 @@ func (b *builtinMonthSig) vecEvalInt(input *chunk.Chunk, result *chunk.Column) e
}
if ds[i].IsZero() {
if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() {
if err := handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, ds[i].String())); err != nil {
isNull, err := handleInvalidZeroTime(b.ctx, ds[i])
if err != nil {
return err
}
result.SetNull(i, true)
result.SetNull(i, isNull)
continue
}
i64s[i] = 0
Expand Down Expand Up @@ -89,10 +90,11 @@ func (b *builtinYearSig) vecEvalInt(input *chunk.Chunk, result *chunk.Column) er
}
if ds[i].IsZero() {
if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() {
if err := handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, ds[i].String())); err != nil {
isNull, err := handleInvalidZeroTime(b.ctx, ds[i])
if err != nil {
return err
}
result.SetNull(i, true)
result.SetNull(i, isNull)
continue
}
i64s[i] = 0
Expand Down Expand Up @@ -923,10 +925,11 @@ func (b *builtinQuarterSig) vecEvalInt(input *chunk.Chunk, result *chunk.Column)
}
date := ds[i]
if date.IsZero() {
if err := handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, date.String())); err != nil {
isNull, err := handleInvalidZeroTime(b.ctx, ds[i])
if err != nil {
return err
}
result.SetNull(i, true)
result.SetNull(i, isNull)
continue
}
i64s[i] = int64((date.Month() + 2) / 3)
Expand Down Expand Up @@ -2068,10 +2071,11 @@ func (b *builtinDayOfYearSig) vecEvalInt(input *chunk.Chunk, result *chunk.Colum
continue
}
if ds[i].InvalidZero() {
if err := handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, ds[i].String())); err != nil {
isNull, err := handleInvalidZeroTime(b.ctx, ds[i])
if err != nil {
return err
}
result.SetNull(i, true)
result.SetNull(i, isNull)
continue
}
i64s[i] = int64(ds[i].YearDay())
Expand Down Expand Up @@ -2452,10 +2456,11 @@ func (b *builtinDayOfWeekSig) vecEvalInt(input *chunk.Chunk, result *chunk.Colum
continue
}
if ds[i].InvalidZero() {
if err := handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, ds[i].String())); err != nil {
isNull, err := handleInvalidZeroTime(b.ctx, ds[i])
if err != nil {
return err
}
result.SetNull(i, true)
result.SetNull(i, isNull)
continue
}
i64s[i] = int64(ds[i].Weekday() + 1)
Expand Down Expand Up @@ -2703,10 +2708,11 @@ func (b *builtinDayOfMonthSig) vecEvalInt(input *chunk.Chunk, result *chunk.Colu
}
if ds[i].IsZero() {
if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() {
if err := handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, ds[i].String())); err != nil {
isNull, err := handleInvalidZeroTime(b.ctx, ds[i])
if err != nil {
return err
}
result.SetNull(i, true)
result.SetNull(i, isNull)
continue
}
i64s[i] = 0
Expand Down
4 changes: 2 additions & 2 deletions expression/builtin_time_vec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -616,9 +616,9 @@ func (s *testEvaluatorSuite) TestVecMonth(c *C) {
f, _, _, result := genVecBuiltinFuncBenchCase(ctx, ast.Month, vecExprBenchCase{retEvalType: types.ETInt, childrenTypes: []types.EvalType{types.ETDatetime}})
c.Assert(ctx.GetSessionVars().StrictSQLMode, IsTrue)
c.Assert(f.vecEvalInt(input, result), IsNil)
c.Assert(len(ctx.GetSessionVars().StmtCtx.GetWarnings()), Equals, 2)
c.Assert(len(ctx.GetSessionVars().StmtCtx.GetWarnings()), Equals, 0)

ctx.GetSessionVars().StmtCtx.InInsertStmt = true
ctx.GetSessionVars().StmtCtx.TruncateAsWarning = false
c.Assert(f.vecEvalInt(input, result), NotNil)
c.Assert(f.vecEvalInt(input, result), IsNil)
}