From a5d6e46afe25e0477088a60860b49a68b35fda88 Mon Sep 17 00:00:00 2001 From: spongedc Date: Fri, 7 Sep 2018 00:21:43 +0800 Subject: [PATCH 1/3] expression: Fix some datetime related cases that is inconsistent with MySQL --- expression/builtin_cast.go | 4 +- expression/builtin_time.go | 20 ++++-- expression/builtin_time_test.go | 105 +++++++++++++++++++++++++++++++- expression/integration_test.go | 48 +++++++++++++-- 4 files changed, 163 insertions(+), 14 deletions(-) diff --git a/expression/builtin_cast.go b/expression/builtin_cast.go index 50b88e0bff3dd..aa42a4421365e 100644 --- a/expression/builtin_cast.go +++ b/expression/builtin_cast.go @@ -968,7 +968,7 @@ func (b *builtinCastDecimalAsTimeSig) evalTime(row chunk.Row) (res types.Time, i return res, isNull, errors.Trace(err) } sc := b.ctx.GetSessionVars().StmtCtx - res, err = types.ParseTime(sc, string(val.ToString()), b.tp.Tp, b.tp.Decimal) + res, err = types.ParseTimeFromFloatString(sc, string(val.ToString()), b.tp.Tp, b.tp.Decimal) if err != nil { return res, false, errors.Trace(err) } @@ -1175,7 +1175,7 @@ func (b *builtinCastStringAsTimeSig) evalTime(row chunk.Row) (res types.Time, is sc := b.ctx.GetSessionVars().StmtCtx res, err = types.ParseTime(sc, val, b.tp.Tp, b.tp.Decimal) if err != nil { - return res, false, errors.Trace(err) + return res, false, err } if b.tp.Tp == mysql.TypeDate { // Truncate hh:mm:ss part if the type is Date. diff --git a/expression/builtin_time.go b/expression/builtin_time.go index 5b0d76d7cf069..3d2270585457b 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -993,7 +993,10 @@ func (b *builtinMonthSig) evalInt(row chunk.Row) (int64, bool, error) { } if date.IsZero() { - return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrIncorrectDatetimeValue.GenByArgs(date.String()))) + if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() { + return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrIncorrectDatetimeValue.GenByArgs(date.String()))) + } + return 0, false, nil } return int64(date.Time.Month()), false, nil @@ -1030,9 +1033,9 @@ func (b *builtinMonthNameSig) evalString(row chunk.Row) (string, bool, error) { return "", true, errors.Trace(handleInvalidTimeError(b.ctx, err)) } mon := arg.Time.Month() - if arg.IsZero() || mon < 0 || mon > len(types.MonthNames) { + if (arg.IsZero() && b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode()) || mon < 0 || mon > len(types.MonthNames) { return "", true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrIncorrectDatetimeValue.GenByArgs(arg.String()))) - } else if mon == 0 { + } else if mon == 0 || arg.IsZero(){ return "", true, nil } return types.MonthNames[mon-1], false, nil @@ -1111,7 +1114,10 @@ func (b *builtinDayOfMonthSig) evalInt(row chunk.Row) (int64, bool, error) { return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, err)) } if arg.IsZero() { - return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrIncorrectDatetimeValue.GenByArgs(arg.String()))) + if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() { + return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrIncorrectDatetimeValue.GenByArgs(arg.String()))) + } + return 0, false, nil } return int64(arg.Time.Day()), false, nil } @@ -1393,9 +1399,11 @@ func (b *builtinYearSig) evalInt(row chunk.Row) (int64, bool, error) { } if date.IsZero() { - return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrIncorrectDatetimeValue.GenByArgs(date.String()))) + if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() { + return 0, true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrIncorrectDatetimeValue.GenByArgs(date.String()))) + } + return 0, false, nil } - return int64(date.Time.Year()), false, nil } diff --git a/expression/builtin_time_test.go b/expression/builtin_time_test.go index 1e7c472f2641c..6304c7df627b1 100644 --- a/expression/builtin_time_test.go +++ b/expression/builtin_time_test.go @@ -169,13 +169,116 @@ func (s *testEvaluatorSuite) TestDate(c *C) { Week interface{} WeekOfYear interface{} YearWeek interface{} + }{ + {nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}, + {"0000-00-00 00:00:00", 0, 0, nil, 0, nil, nil, nil, nil, nil, nil, nil}, + {"0000-00-00", 0, 0, nil, 0, nil, nil, nil, nil, nil, nil, nil}, + } + + dtblNil := tblToDtbl(tblNil) + for _, t := range dtblNil { + fc := funcs[ast.Year] + f, err := fc.getFunction(s.ctx, s.datumsToConstants(t["Input"])) + c.Assert(err, IsNil) + v, err := evalBuiltinFunc(f, chunk.Row{}) + c.Assert(err, IsNil) + c.Assert(v, testutil.DatumEquals, t["Year"][0]) + + fc = funcs[ast.Month] + f, err = fc.getFunction(s.ctx, s.datumsToConstants(t["Input"])) + c.Assert(err, IsNil) + v, err = evalBuiltinFunc(f, chunk.Row{}) + c.Assert(err, IsNil) + c.Assert(v, testutil.DatumEquals, t["Month"][0]) + + fc = funcs[ast.MonthName] + f, err = fc.getFunction(s.ctx, s.datumsToConstants(t["Input"])) + c.Assert(err, IsNil) + v, err = evalBuiltinFunc(f, chunk.Row{}) + c.Assert(err, IsNil) + c.Assert(v, testutil.DatumEquals, t["MonthName"][0]) + + fc = funcs[ast.DayOfMonth] + f, err = fc.getFunction(s.ctx, s.datumsToConstants(t["Input"])) + c.Assert(err, IsNil) + v, err = evalBuiltinFunc(f, chunk.Row{}) + c.Assert(err, IsNil) + c.Assert(v, testutil.DatumEquals, t["DayOfMonth"][0]) + + fc = funcs[ast.DayOfWeek] + f, err = fc.getFunction(s.ctx, s.datumsToConstants(t["Input"])) + c.Assert(err, IsNil) + v, err = evalBuiltinFunc(f, chunk.Row{}) + c.Assert(err, IsNil) + c.Assert(v, testutil.DatumEquals, t["DayOfWeek"][0]) + + fc = funcs[ast.DayOfYear] + f, err = fc.getFunction(s.ctx, s.datumsToConstants(t["Input"])) + c.Assert(err, IsNil) + v, err = evalBuiltinFunc(f, chunk.Row{}) + c.Assert(err, IsNil) + c.Assert(v, testutil.DatumEquals, t["DayOfYear"][0]) + + fc = funcs[ast.Weekday] + f, err = fc.getFunction(s.ctx, s.datumsToConstants(t["Input"])) + c.Assert(err, IsNil) + c.Assert(f, NotNil) + v, err = evalBuiltinFunc(f, chunk.Row{}) + c.Assert(err, IsNil) + c.Assert(v, testutil.DatumEquals, t["WeekDay"][0]) + + fc = funcs[ast.DayName] + f, err = fc.getFunction(s.ctx, s.datumsToConstants(t["Input"])) + c.Assert(err, IsNil) + v, err = evalBuiltinFunc(f, chunk.Row{}) + c.Assert(err, IsNil) + c.Assert(v, testutil.DatumEquals, t["DayName"][0]) + + fc = funcs[ast.Week] + f, err = fc.getFunction(s.ctx, s.datumsToConstants(t["Input"])) + c.Assert(err, IsNil) + v, err = evalBuiltinFunc(f, chunk.Row{}) + c.Assert(err, IsNil) + c.Assert(v, testutil.DatumEquals, t["Week"][0]) + + fc = funcs[ast.WeekOfYear] + f, err = fc.getFunction(s.ctx, s.datumsToConstants(t["Input"])) + c.Assert(err, IsNil) + v, err = evalBuiltinFunc(f, chunk.Row{}) + c.Assert(err, IsNil) + c.Assert(v, testutil.DatumEquals, t["WeekOfYear"][0]) + + fc = funcs[ast.YearWeek] + f, err = fc.getFunction(s.ctx, s.datumsToConstants(t["Input"])) + c.Assert(err, IsNil) + v, err = evalBuiltinFunc(f, chunk.Row{}) + c.Assert(err, IsNil) + c.Assert(v, testutil.DatumEquals, t["YearWeek"][0]) + } + + + // test nil with 'NO_ZERO_DATE' set in sql_mode + tblNil = []struct { + Input interface{} + Year interface{} + Month interface{} + MonthName interface{} + DayOfMonth interface{} + DayOfWeek interface{} + DayOfYear interface{} + WeekDay interface{} + DayName interface{} + Week interface{} + WeekOfYear interface{} + YearWeek interface{} }{ {nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}, {"0000-00-00 00:00:00", nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}, {"0000-00-00", nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}, } - dtblNil := tblToDtbl(tblNil) + dtblNil = tblToDtbl(tblNil) + s.ctx.GetSessionVars().SetSystemVar("sql_mode", "NO_ZERO_DATE") for _, t := range dtblNil { fc := funcs[ast.Year] f, err := fc.getFunction(s.ctx, s.datumsToConstants(t["Input"])) diff --git a/expression/integration_test.go b/expression/integration_test.go index c37b478e5453e..05329fc51a7b0 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -1091,10 +1091,16 @@ func (s *testIntegrationSuite) TestTimeBuiltin(c *C) { _, err := tk.Exec(`insert into t select year("aa")`) c.Assert(err, NotNil) c.Assert(terror.ErrorEqual(err, types.ErrInvalidTimeFormat), IsTrue, Commentf("err %v", err)) - _, err = tk.Exec(`insert into t select year("0000-00-00 00:00:00")`) + tk.MustExec(`insert into t select year("0000-00-00 00:00:00")`) + tk.MustExec(`set sql_mode="NO_ZERO_DATE";`) + tk.MustExec(`insert into t select year("0000-00-00 00:00:00")`) + tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|", "Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00.000000'")) + tk.MustExec(`set sql_mode="NO_ZERO_DATE,STRICT_TRANS_TABLES";`) + _, err = tk.Exec(`insert into t select year("0000-00-00 00:00:00");`) c.Assert(err, NotNil) c.Assert(types.ErrIncorrectDatetimeValue.Equal(err), IsTrue, Commentf("err %v", err)) tk.MustExec(`insert into t select 1`) + tk.MustExec(`set sql_mode="STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION";`) _, err = tk.Exec(`update t set a = year("aa")`) c.Assert(terror.ErrorEqual(err, types.ErrInvalidTimeFormat), IsTrue, Commentf("err %v", err)) _, err = tk.Exec(`delete from t where a = year("aa")`) @@ -1114,9 +1120,16 @@ func (s *testIntegrationSuite) TestTimeBuiltin(c *C) { _, err = tk.Exec(`insert into t select month("aa")`) c.Assert(err, NotNil) c.Assert(terror.ErrorEqual(err, types.ErrInvalidTimeFormat), IsTrue) - _, err = tk.Exec(`insert into t select month("0000-00-00 00:00:00")`) + tk.MustExec(`insert into t select month("0000-00-00 00:00:00")`) + tk.MustExec(`set sql_mode="NO_ZERO_DATE";`) + tk.MustExec(`insert into t select month("0000-00-00 00:00:00")`) + tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|", "Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00.000000'")) + tk.MustExec(`set sql_mode="NO_ZERO_DATE,STRICT_TRANS_TABLES";`) + _, err = tk.Exec(`insert into t select month("0000-00-00 00:00:00");`) c.Assert(err, NotNil) - c.Assert(types.ErrIncorrectDatetimeValue.Equal(err), IsTrue) + c.Assert(types.ErrIncorrectDatetimeValue.Equal(err), IsTrue, Commentf("err %v", err)) + tk.MustExec(`insert into t select 1`) + tk.MustExec(`set sql_mode="STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION";`) tk.MustExec(`insert into t select 1`) _, err = tk.Exec(`update t set a = month("aa")`) c.Assert(terror.ErrorEqual(err, types.ErrInvalidTimeFormat), IsTrue) @@ -1442,6 +1455,14 @@ func (s *testIntegrationSuite) TestTimeBuiltin(c *C) { result = tk.MustQuery(`select dayOfYear(null), dayOfYear("2017-08-12"), dayOfYear("0000-00-00"), dayOfYear("2017-00-00"), dayOfYear("0000-00-00 12:12:12"), dayOfYear("2017-00-00 12:12:12")`) result.Check(testkit.Rows(" 224 ")) result = tk.MustQuery(`select dayOfMonth(null), dayOfMonth("2017-08-12"), dayOfMonth("0000-00-00"), dayOfMonth("2017-00-00"), dayOfMonth("0000-00-00 12:12:12"), dayOfMonth("2017-00-00 12:12:12")`) + result.Check(testkit.Rows(" 12 0 0 0 0")) + + tk.MustExec("set sql_mode = 'NO_ZERO_DATE'") + result = tk.MustQuery(`select dayOfWeek(null), dayOfWeek("2017-08-12"), dayOfWeek("0000-00-00"), dayOfWeek("2017-00-00"), dayOfWeek("0000-00-00 12:12:12"), dayOfWeek("2017-00-00 12:12:12")`) + result.Check(testkit.Rows(" 7 ")) + result = tk.MustQuery(`select dayOfYear(null), dayOfYear("2017-08-12"), dayOfYear("0000-00-00"), dayOfYear("2017-00-00"), dayOfYear("0000-00-00 12:12:12"), dayOfYear("2017-00-00 12:12:12")`) + result.Check(testkit.Rows(" 224 ")) + result = tk.MustQuery(`select dayOfMonth(null), dayOfMonth("2017-08-12"), dayOfMonth("0000-00-00"), dayOfMonth("2017-00-00"), dayOfMonth("0000-00-00 12:12:12"), dayOfMonth("2017-00-00 12:12:12")`) result.Check(testkit.Rows(" 12 0 0 0")) tk.MustExec(`drop table if exists t`) @@ -1458,6 +1479,13 @@ func (s *testIntegrationSuite) TestTimeBuiltin(c *C) { _, err = tk.Exec("insert into t value(dayOfMonth('2017-00-00'))") c.Assert(types.ErrIncorrectDatetimeValue.Equal(err), IsTrue) + tk.MustExec("insert into t value(dayOfMonth('0000-00-00'))") + tk.MustExec(`update t set a = dayOfMonth("0000-00-00")`) + tk.MustExec("set sql_mode = 'NO_ZERO_DATE';") + tk.MustExec("insert into t value(dayOfMonth('0000-00-00'))") + tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|", "Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00.000000'")) + tk.MustExec(`update t set a = dayOfMonth("0000-00-00")`) + tk.MustExec("set sql_mode = 'NO_ZERO_DATE,STRICT_TRANS_TABLES';") _, err = tk.Exec("insert into t value(dayOfMonth('0000-00-00'))") c.Assert(types.ErrIncorrectDatetimeValue.Equal(err), IsTrue) tk.MustExec("insert into t value(0)") @@ -1539,8 +1567,15 @@ func (s *testIntegrationSuite) TestTimeBuiltin(c *C) { tk.MustExec(`insert into t value("abc")`) tk.MustExec("set sql_mode = 'STRICT_TRANS_TABLES'") - _, err = tk.Exec("insert into t value(monthname('0000-00-00'))") - c.Assert(types.ErrIncorrectDatetimeValue.Equal(err), IsTrue) + tk.MustExec("insert into t value(monthname('0000-00-00'))") + tk.MustExec(`update t set a = monthname("0000-00-00")`) + tk.MustExec("set sql_mode = 'NO_ZERO_DATE'") + tk.MustExec("insert into t value(monthname('0000-00-00'))") + tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|", "Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00.000000'")) + tk.MustExec(`update t set a = monthname("0000-00-00")`) + tk.MustExec("set sql_mode = ''") + tk.MustExec("insert into t value(monthname('0000-00-00'))") + tk.MustExec("set sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_DATE'") _, err = tk.Exec(`update t set a = monthname("0000-00-00")`) c.Assert(types.ErrIncorrectDatetimeValue.Equal(err), IsTrue) _, err = tk.Exec(`delete from t where a = monthname(123)`) @@ -1880,6 +1915,9 @@ func (s *testIntegrationSuite) TestBuiltin(c *C) { result = tk.MustQuery(`select cast(cast('2017-01-01 01:01:11.12' as date) as datetime(2));`) result.Check(testkit.Rows("2017-01-01 00:00:00.00")) + result = tk.MustQuery(`select cast(20170118.999 as datetime);`) + result.Check(testkit.Rows("2017-01-18 00:00:00")) + // for ISNULL tk.MustExec("drop table if exists t") tk.MustExec("create table t (a int, b int, c int, d char(10), e datetime, f float, g decimal(10, 3))") From a4684b5e806c31abcaa93688d23f8cb6be02c98c Mon Sep 17 00:00:00 2001 From: spongedc Date: Fri, 7 Sep 2018 01:33:13 +0800 Subject: [PATCH 2/3] Fix fmt --- expression/builtin_time.go | 2 +- expression/builtin_time_test.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/expression/builtin_time.go b/expression/builtin_time.go index 3d2270585457b..cdd6f54a2326e 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -1035,7 +1035,7 @@ func (b *builtinMonthNameSig) evalString(row chunk.Row) (string, bool, error) { mon := arg.Time.Month() if (arg.IsZero() && b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode()) || mon < 0 || mon > len(types.MonthNames) { return "", true, errors.Trace(handleInvalidTimeError(b.ctx, types.ErrIncorrectDatetimeValue.GenByArgs(arg.String()))) - } else if mon == 0 || arg.IsZero(){ + } else if mon == 0 || arg.IsZero() { return "", true, nil } return types.MonthNames[mon-1], false, nil diff --git a/expression/builtin_time_test.go b/expression/builtin_time_test.go index 6304c7df627b1..e516c6da08d25 100644 --- a/expression/builtin_time_test.go +++ b/expression/builtin_time_test.go @@ -256,7 +256,6 @@ func (s *testEvaluatorSuite) TestDate(c *C) { c.Assert(v, testutil.DatumEquals, t["YearWeek"][0]) } - // test nil with 'NO_ZERO_DATE' set in sql_mode tblNil = []struct { Input interface{} From 463a9a1242aca890278931e1b579619a14e89315 Mon Sep 17 00:00:00 2001 From: spongedc Date: Fri, 7 Sep 2018 11:04:58 +0800 Subject: [PATCH 3/3] Fix unnecessary changes --- expression/builtin_cast.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expression/builtin_cast.go b/expression/builtin_cast.go index aa42a4421365e..79dfb430b2ca3 100644 --- a/expression/builtin_cast.go +++ b/expression/builtin_cast.go @@ -1175,7 +1175,7 @@ func (b *builtinCastStringAsTimeSig) evalTime(row chunk.Row) (res types.Time, is sc := b.ctx.GetSessionVars().StmtCtx res, err = types.ParseTime(sc, val, b.tp.Tp, b.tp.Decimal) if err != nil { - return res, false, err + return res, false, errors.Trace(err) } if b.tp.Tp == mysql.TypeDate { // Truncate hh:mm:ss part if the type is Date.