From 9465a53da91a982db5e81c7160882dab86af361e Mon Sep 17 00:00:00 2001 From: lzmhhh123 Date: Mon, 23 Nov 2020 17:05:34 +0800 Subject: [PATCH 01/10] expression: fix compatibility behaviors in zero datetime with MySQL --- expression/builtin_time.go | 42 +++++++++++++++++++++++++++++++++ expression/builtin_time_test.go | 37 +++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/expression/builtin_time.go b/expression/builtin_time.go index d8fe5b39710d3..4b0098efbf0b8 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -1079,6 +1079,15 @@ func (b *builtinMonthSig) evalInt(row chunk.Row) (int64, bool, error) { if date.IsZero() { if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() { + // 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())) } return 0, false, nil @@ -1238,6 +1247,15 @@ func (b *builtinDayOfMonthSig) evalInt(row chunk.Row) (int64, bool, error) { } if arg.IsZero() { if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() { + // MySQL compatibility, #11203 + // 0 | 0.0 should be converted to 0 value (not null) + n, err := arg.ToNumber().ToInt() + isOriginalIntOrDecimalZero := err == nil && n == 0 + // Args like "0000-00-00", "0000-00-00 00:00:00" set Fsp to 6 + isOriginalStringZero := arg.Fsp() > 0 + if isOriginalIntOrDecimalZero && !isOriginalStringZero { + return 0, false, nil + } return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, arg.String())) } return 0, false, nil @@ -1557,6 +1575,15 @@ func (b *builtinYearSig) evalInt(row chunk.Row) (int64, bool, error) { if date.IsZero() { if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() { + // 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())) } return 0, false, nil @@ -2664,6 +2691,21 @@ 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() { + // MySQL compatibility, #11203 + // 0 | 0.0 should be converted to 0 value (not null) + n, err := dt.ToNumber().ToInt() + isOriginalIntOrDecimalZero := err == nil && n == 0 + // Args like "0000-00-00", "0000-00-00 00:00:00" set Fsp to 6 + isOriginalStringZero := dt.Fsp() > 0 + if isOriginalIntOrDecimalZero && !isOriginalStringZero { + return 0, false, nil + } + return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, date.String())) + } + return 0, false, nil + } res, err := types.ExtractDatetimeNum(&dt, unit) return res, err != nil, err } diff --git a/expression/builtin_time_test.go b/expression/builtin_time_test.go index 8725bee7d50d5..f0302c855ca4e 100644 --- a/expression/builtin_time_test.go +++ b/expression/builtin_time_test.go @@ -2854,3 +2854,40 @@ func (s *testEvaluatorSuite) TestGetIntervalFromDecimal(c *C) { c.Assert(interval, Equals, test.expect) } } + +func (s *testEvaluatorSuite) TestZeroDateTimeCompatibility(c *C) { + tests := []struct { + f builtinFunc + res int64 + isNull bool + hasWarn bool + }{ + {&builtinYearSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}}}}, 0, false, false}, + {&builtinYearSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, + {&builtinDayOfMonthSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}}}}, 0, false, false}, + {&builtinDayOfMonthSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, + {&builtinDayOfYearSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}}}}, 0, false, false}, + {&builtinDayOfYearSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, + {&builtinDayOfWeekSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}}}}, 0, false, false}, + {&builtinDayOfWeekSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, + {&builtinQuarterSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}}}}, 0, false, false}, + {&builtinQuarterSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, + {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}, &Constant{Value: types.NewStringDatum("DAY"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, false, false}, + {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}, &Constant{Value: types.NewStringDatum("DAY"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, + {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}, &Constant{Value: types.NewStringDatum("Month"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, false, false}, + {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}, &Constant{Value: types.NewStringDatum("Month"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, + {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}, &Constant{Value: types.NewStringDatum("YEAR"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, false, false}, + {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}, &Constant{Value: types.NewStringDatum("YEAR"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, + {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}, &Constant{Value: types.NewStringDatum("QUARTER"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, false, false}, + {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}, &Constant{Value: types.NewStringDatum("QUARTER"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, + {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}, &Constant{Value: types.NewStringDatum("WEEK"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, false, false}, + {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}, &Constant{Value: types.NewStringDatum("WEEK"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, + } + + for _, t := range tests { + val, isNull, hasWarn := t.f.evalInt(chunk.Row{}) + c.Assert(val, Equals, t.res) + c.Assert(isNull, Equals, t.isNull) + c.Assert(hasWarn != nil, Equals, t.hasWarn) + } +} From 1c58da87b87c856e83ab52cd0eb4238c6f21355f Mon Sep 17 00:00:00 2001 From: lzmhhh123 Date: Mon, 23 Nov 2020 17:07:17 +0800 Subject: [PATCH 02/10] fix --- expression/builtin_time.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expression/builtin_time.go b/expression/builtin_time.go index 4b0098efbf0b8..fb4eb5a54af93 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -2702,7 +2702,7 @@ func (b *builtinExtractDatetimeSig) evalInt(row chunk.Row) (int64, bool, error) if isOriginalIntOrDecimalZero && !isOriginalStringZero { return 0, false, nil } - return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, date.String())) + return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, dt.String())) } return 0, false, nil } From f9026ada990b0fdd49b7f4f7a093c89fac10b8fc Mon Sep 17 00:00:00 2001 From: lzmhhh123 Date: Mon, 23 Nov 2020 17:38:49 +0800 Subject: [PATCH 03/10] fix --- executor/executor_test.go | 28 +++++++++++++++++++++++++ expression/builtin_time.go | 18 ++++++++++++++++ expression/builtin_time_test.go | 37 --------------------------------- 3 files changed, 46 insertions(+), 37 deletions(-) diff --git a/executor/executor_test.go b/executor/executor_test.go index 79a3ffeee89b7..f77001b7713ce 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -7058,3 +7058,31 @@ func (s *testSuite) TestIssue20305(c *C) { tk.MustExec("INSERT INTO `t3` VALUES (2069, 70), (2010, 11), (2155, 2156), (2069, 69)") 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")`, + } + + store, err := mockstore.NewMockStore() + c.Assert(err, IsNil) + _, err = session.BootstrapSession(store) + c.Assert(err, IsNil) + tk := testkit.NewTestKit(c, store) + + for _, t := range SQLs { + fmt.Println(t) + tk.MustQuery(t).Check(testkit.Rows("0 ")) + c.Assert(tk.Se.GetSessionVars().StmtCtx.WarningCount(), Equals, uint16(1)) + } +} diff --git a/expression/builtin_time.go b/expression/builtin_time.go index fb4eb5a54af93..660a0118dd08f 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -1299,6 +1299,15 @@ func (b *builtinDayOfWeekSig) evalInt(row chunk.Row) (int64, bool, error) { return 0, true, handleInvalidTimeError(b.ctx, err) } if arg.InvalidZero() { + // MySQL compatibility, #11203 + // 0 | 0.0 should be converted to 0 value (not null) + n, err := arg.ToNumber().ToInt() + isOriginalIntOrDecimalZero := err == nil && n == 0 + // Args like "0000-00-00", "0000-00-00 00:00:00" set Fsp to 6 + isOriginalStringZero := arg.Fsp() > 0 + if isOriginalIntOrDecimalZero && !isOriginalStringZero { + return 0, false, nil + } return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, arg.String())) } // 1 is Sunday, 2 is Monday, .... 7 is Saturday @@ -1341,6 +1350,15 @@ func (b *builtinDayOfYearSig) evalInt(row chunk.Row) (int64, bool, error) { return 0, isNull, handleInvalidTimeError(b.ctx, err) } if arg.InvalidZero() { + // MySQL compatibility, #11203 + // 0 | 0.0 should be converted to 0 value (not null) + n, err := arg.ToNumber().ToInt() + isOriginalIntOrDecimalZero := err == nil && n == 0 + // Args like "0000-00-00", "0000-00-00 00:00:00" set Fsp to 6 + isOriginalStringZero := arg.Fsp() > 0 + if isOriginalIntOrDecimalZero && !isOriginalStringZero { + return 0, false, nil + } return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, arg.String())) } diff --git a/expression/builtin_time_test.go b/expression/builtin_time_test.go index f0302c855ca4e..8725bee7d50d5 100644 --- a/expression/builtin_time_test.go +++ b/expression/builtin_time_test.go @@ -2854,40 +2854,3 @@ func (s *testEvaluatorSuite) TestGetIntervalFromDecimal(c *C) { c.Assert(interval, Equals, test.expect) } } - -func (s *testEvaluatorSuite) TestZeroDateTimeCompatibility(c *C) { - tests := []struct { - f builtinFunc - res int64 - isNull bool - hasWarn bool - }{ - {&builtinYearSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}}}}, 0, false, false}, - {&builtinYearSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, - {&builtinDayOfMonthSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}}}}, 0, false, false}, - {&builtinDayOfMonthSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, - {&builtinDayOfYearSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}}}}, 0, false, false}, - {&builtinDayOfYearSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, - {&builtinDayOfWeekSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}}}}, 0, false, false}, - {&builtinDayOfWeekSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, - {&builtinQuarterSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}}}}, 0, false, false}, - {&builtinQuarterSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, - {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}, &Constant{Value: types.NewStringDatum("DAY"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, false, false}, - {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}, &Constant{Value: types.NewStringDatum("DAY"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, - {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}, &Constant{Value: types.NewStringDatum("Month"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, false, false}, - {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}, &Constant{Value: types.NewStringDatum("Month"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, - {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}, &Constant{Value: types.NewStringDatum("YEAR"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, false, false}, - {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}, &Constant{Value: types.NewStringDatum("YEAR"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, - {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}, &Constant{Value: types.NewStringDatum("QUARTER"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, false, false}, - {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}, &Constant{Value: types.NewStringDatum("QUARTER"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, - {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewIntDatum(0), RetType: types.NewFieldType(mysql.TypeLonglong)}, &Constant{Value: types.NewStringDatum("WEEK"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, false, false}, - {&builtinExtractDatetimeSig{baseBuiltinFunc{args: []Expression{&Constant{Value: types.NewStringDatum("0000-00-00"), RetType: types.NewFieldType(mysql.TypeVarchar)}, &Constant{Value: types.NewStringDatum("WEEK"), RetType: types.NewFieldType(mysql.TypeVarchar)}}}}, 0, true, true}, - } - - for _, t := range tests { - val, isNull, hasWarn := t.f.evalInt(chunk.Row{}) - c.Assert(val, Equals, t.res) - c.Assert(isNull, Equals, t.isNull) - c.Assert(hasWarn != nil, Equals, t.hasWarn) - } -} From 533342ac44649699f973a7d43c468afba81ea2d8 Mon Sep 17 00:00:00 2001 From: lzmhhh123 Date: Mon, 23 Nov 2020 17:41:55 +0800 Subject: [PATCH 04/10] remove fmt.print --- executor/executor_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/executor/executor_test.go b/executor/executor_test.go index f77001b7713ce..ab2a1572a7a89 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -7081,7 +7081,6 @@ func (s *testSuite) TestZeroDateTimeCompatibility(c *C) { tk := testkit.NewTestKit(c, store) for _, t := range SQLs { - fmt.Println(t) tk.MustQuery(t).Check(testkit.Rows("0 ")) c.Assert(tk.Se.GetSessionVars().StmtCtx.WarningCount(), Equals, uint16(1)) } From fe3d89cb1f5a3054287e808f4ca2c81ad0ad4009 Mon Sep 17 00:00:00 2001 From: lzmhhh123 Date: Fri, 4 Dec 2020 15:45:18 +0800 Subject: [PATCH 05/10] address comment Signed-off-by: lzmhhh123 --- expression/builtin_time.go | 109 +++++++++------------------------ expression/builtin_time_vec.go | 32 +++++----- 2 files changed, 47 insertions(+), 94 deletions(-) diff --git a/expression/builtin_time.go b/expression/builtin_time.go index 660a0118dd08f..9c7c623d17c55 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -829,16 +829,8 @@ func (b *builtinDateFormatSig) evalString(row chunk.Row) (string, bool, error) { } if t.InvalidZero() { - // 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 "", true, nil - } - return "", true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, t.String())) + isNull, err := handleInvalidZeroTime(b.ctx, t) + return "", isNull, err } res, err := t.DateFormat(formatMask) @@ -1079,16 +1071,8 @@ func (b *builtinMonthSig) evalInt(row chunk.Row) (int64, bool, error) { if date.IsZero() { if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() { - // 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 0, false, nil } @@ -1247,16 +1231,8 @@ func (b *builtinDayOfMonthSig) evalInt(row chunk.Row) (int64, bool, error) { } if arg.IsZero() { if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() { - // MySQL compatibility, #11203 - // 0 | 0.0 should be converted to 0 value (not null) - n, err := arg.ToNumber().ToInt() - isOriginalIntOrDecimalZero := err == nil && n == 0 - // Args like "0000-00-00", "0000-00-00 00:00:00" set Fsp to 6 - isOriginalStringZero := arg.Fsp() > 0 - if isOriginalIntOrDecimalZero && !isOriginalStringZero { - return 0, false, nil - } - 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 } @@ -1299,16 +1275,8 @@ func (b *builtinDayOfWeekSig) evalInt(row chunk.Row) (int64, bool, error) { return 0, true, handleInvalidTimeError(b.ctx, err) } if arg.InvalidZero() { - // MySQL compatibility, #11203 - // 0 | 0.0 should be converted to 0 value (not null) - n, err := arg.ToNumber().ToInt() - isOriginalIntOrDecimalZero := err == nil && n == 0 - // Args like "0000-00-00", "0000-00-00 00:00:00" set Fsp to 6 - isOriginalStringZero := arg.Fsp() > 0 - if isOriginalIntOrDecimalZero && !isOriginalStringZero { - return 0, false, nil - } - 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 @@ -1350,16 +1318,8 @@ func (b *builtinDayOfYearSig) evalInt(row chunk.Row) (int64, bool, error) { return 0, isNull, handleInvalidTimeError(b.ctx, err) } if arg.InvalidZero() { - // MySQL compatibility, #11203 - // 0 | 0.0 should be converted to 0 value (not null) - n, err := arg.ToNumber().ToInt() - isOriginalIntOrDecimalZero := err == nil && n == 0 - // Args like "0000-00-00", "0000-00-00 00:00:00" set Fsp to 6 - isOriginalStringZero := arg.Fsp() > 0 - if isOriginalIntOrDecimalZero && !isOriginalStringZero { - return 0, false, nil - } - return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, arg.String())) + isNull, err := handleInvalidZeroTime(b.ctx, t) + return 0, isNull, err } return int64(arg.YearDay()), false, nil @@ -1593,16 +1553,8 @@ func (b *builtinYearSig) evalInt(row chunk.Row) (int64, bool, error) { if date.IsZero() { if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() { - // 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, t) + return 0, isNull, err } return 0, false, nil } @@ -2711,16 +2663,8 @@ func (b *builtinExtractDatetimeSig) evalInt(row chunk.Row) (int64, bool, error) } if dt.IsZero() { if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() { - // MySQL compatibility, #11203 - // 0 | 0.0 should be converted to 0 value (not null) - n, err := dt.ToNumber().ToInt() - isOriginalIntOrDecimalZero := err == nil && n == 0 - // Args like "0000-00-00", "0000-00-00 00:00:00" set Fsp to 6 - isOriginalStringZero := dt.Fsp() > 0 - if isOriginalIntOrDecimalZero && !isOriginalStringZero { - return 0, false, nil - } - return 0, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, dt.String())) + isNull, err := handleInvalidZeroTime(b.ctx, t) + return 0, isNull, err } return 0, false, nil } @@ -6080,16 +6024,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, t) + return 0, isNull, err } return int64((date.Month() + 2) / 3), false, nil @@ -7080,3 +7016,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 true, nil + } + return true, handleInvalidTimeError(ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, t.String())) +} diff --git a/expression/builtin_time_vec.go b/expression/builtin_time_vec.go index cf91b12b7835e..7e2dd557a9235 100644 --- a/expression/builtin_time_vec.go +++ b/expression/builtin_time_vec.go @@ -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 @@ -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 @@ -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) @@ -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()) @@ -2452,11 +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) - continue + result.SetNull(i, isNull) } i64s[i] = int64(ds[i].Weekday() + 1) } @@ -2703,11 +2707,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) - continue + result.SetNull(i, isNull) } i64s[i] = 0 continue From 3a401af20dcfa13f353450709c4d1f83ff7ce77b Mon Sep 17 00:00:00 2001 From: lzmhhh123 Date: Fri, 4 Dec 2020 15:46:49 +0800 Subject: [PATCH 06/10] fix Signed-off-by: lzmhhh123 --- expression/builtin_time_vec.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/expression/builtin_time_vec.go b/expression/builtin_time_vec.go index 7e2dd557a9235..4de37e855d73f 100644 --- a/expression/builtin_time_vec.go +++ b/expression/builtin_time_vec.go @@ -2461,6 +2461,7 @@ func (b *builtinDayOfWeekSig) vecEvalInt(input *chunk.Chunk, result *chunk.Colum return err } result.SetNull(i, isNull) + continue } i64s[i] = int64(ds[i].Weekday() + 1) } @@ -2712,6 +2713,7 @@ func (b *builtinDayOfMonthSig) vecEvalInt(input *chunk.Chunk, result *chunk.Colu return err } result.SetNull(i, isNull) + continue } i64s[i] = 0 continue From e09cd173e9e45c94f9abe37a2d1a3f5860a29d17 Mon Sep 17 00:00:00 2001 From: lzmhhh123 Date: Fri, 4 Dec 2020 15:47:51 +0800 Subject: [PATCH 07/10] address comment Signed-off-by: lzmhhh123 --- executor/executor_test.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/executor/executor_test.go b/executor/executor_test.go index bda5238066746..47cbf99f71406 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -7016,12 +7016,7 @@ func (s *testSuite) TestZeroDateTimeCompatibility(c *C) { `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")`, } - - store, err := mockstore.NewMockStore() - c.Assert(err, IsNil) - _, err = session.BootstrapSession(store) - c.Assert(err, IsNil) - tk := testkit.NewTestKit(c, store) + tk := testkit.NewTestKit(c, s.store) for _, t := range SQLs { tk.MustQuery(t).Check(testkit.Rows("0 ")) From 34a2401ef285f03116e49028ca1a7093902d507f Mon Sep 17 00:00:00 2001 From: lzmhhh123 Date: Fri, 4 Dec 2020 15:51:40 +0800 Subject: [PATCH 08/10] fix Signed-off-by: lzmhhh123 --- expression/builtin_time.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/expression/builtin_time.go b/expression/builtin_time.go index 9c7c623d17c55..e2521a2d22560 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -1318,7 +1318,7 @@ func (b *builtinDayOfYearSig) evalInt(row chunk.Row) (int64, bool, error) { return 0, isNull, handleInvalidTimeError(b.ctx, err) } if arg.InvalidZero() { - isNull, err := handleInvalidZeroTime(b.ctx, t) + isNull, err := handleInvalidZeroTime(b.ctx, arg) return 0, isNull, err } @@ -1553,7 +1553,7 @@ func (b *builtinYearSig) evalInt(row chunk.Row) (int64, bool, error) { if date.IsZero() { if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() { - isNull, err := handleInvalidZeroTime(b.ctx, t) + isNull, err := handleInvalidZeroTime(b.ctx, date) return 0, isNull, err } return 0, false, nil @@ -2663,7 +2663,7 @@ func (b *builtinExtractDatetimeSig) evalInt(row chunk.Row) (int64, bool, error) } if dt.IsZero() { if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() { - isNull, err := handleInvalidZeroTime(b.ctx, t) + isNull, err := handleInvalidZeroTime(b.ctx, dt) return 0, isNull, err } return 0, false, nil @@ -6024,7 +6024,7 @@ func (b *builtinQuarterSig) evalInt(row chunk.Row) (int64, bool, error) { } if date.IsZero() { - isNull, err := handleInvalidZeroTime(b.ctx, t) + isNull, err := handleInvalidZeroTime(b.ctx, date) return 0, isNull, err } From 34207048dcd2f434f99aec5a6f128cea5eab76b3 Mon Sep 17 00:00:00 2001 From: lzmhhh123 Date: Fri, 4 Dec 2020 16:19:01 +0800 Subject: [PATCH 09/10] fix Signed-off-by: lzmhhh123 --- executor/executor_test.go | 1 + expression/builtin_time.go | 14 +++++++++++--- expression/builtin_time_vec_test.go | 4 ++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/executor/executor_test.go b/executor/executor_test.go index 47cbf99f71406..25918d1655ff4 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -7019,6 +7019,7 @@ func (s *testSuite) TestZeroDateTimeCompatibility(c *C) { tk := testkit.NewTestKit(c, s.store) for _, t := range SQLs { + fmt.Println(t) tk.MustQuery(t).Check(testkit.Rows("0 ")) c.Assert(tk.Se.GetSessionVars().StmtCtx.WarningCount(), Equals, uint16(1)) } diff --git a/expression/builtin_time.go b/expression/builtin_time.go index e2521a2d22560..ce04c001b5573 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -829,8 +829,16 @@ func (b *builtinDateFormatSig) evalString(row chunk.Row) (string, bool, error) { } if t.InvalidZero() { - isNull, err := handleInvalidZeroTime(b.ctx, t) - return "", isNull, err + // 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 "", true, nil + } + return "", true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, t.String())) } res, err := t.DateFormat(formatMask) @@ -7025,7 +7033,7 @@ func handleInvalidZeroTime(ctx sessionctx.Context, t types.Time) (bool, error) { // Args like "0000-00-00", "0000-00-00 00:00:00" set Fsp to 6 isOriginalStringZero := t.Fsp() > 0 if isOriginalIntOrDecimalZero && !isOriginalStringZero { - return true, nil + return false, nil } return true, handleInvalidTimeError(ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, t.String())) } diff --git a/expression/builtin_time_vec_test.go b/expression/builtin_time_vec_test.go index a7b2e857fbd00..08bacb1a31517 100644 --- a/expression/builtin_time_vec_test.go +++ b/expression/builtin_time_vec_test.go @@ -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) } From 5900ab16298e1125bfe76acd56351411bf67555c Mon Sep 17 00:00:00 2001 From: lzmhhh123 Date: Mon, 7 Dec 2020 11:37:51 +0800 Subject: [PATCH 10/10] modify vec Signed-off-by: lzmhhh123 --- expression/builtin_time_vec.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/expression/builtin_time_vec.go b/expression/builtin_time_vec.go index 4de37e855d73f..9c56d149c37e3 100644 --- a/expression/builtin_time_vec.go +++ b/expression/builtin_time_vec.go @@ -237,6 +237,17 @@ func (b *builtinExtractDatetimeSig) vecEvalInt(input *chunk.Chunk, result *chunk if result.IsNull(i) { continue } + if ds[i].IsZero() { + i64s[i] = 0 + if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() { + isNull, err := handleInvalidZeroTime(b.ctx, ds[i]) + if err != nil { + return err + } + result.SetNull(i, isNull) + } + continue + } res, err := types.ExtractDatetimeNum(&ds[i], buf.GetString(i)) if err != nil { return err