From b232a23e7ad3122348bcee1d7f354cf3793a61e4 Mon Sep 17 00:00:00 2001 From: Ling Jin Date: Fri, 18 Dec 2020 17:19:30 +0800 Subject: [PATCH] expression, types: fix datetime and year comparison error (#20233) --- expression/builtin_cast.go | 8 +++++++- expression/builtin_cast_vec.go | 10 +++++++++- expression/builtin_compare.go | 3 +++ expression/integration_test.go | 35 ++++++++++++++++++++++++++++++++++ types/time.go | 11 +++++++++++ 5 files changed, 65 insertions(+), 2 deletions(-) diff --git a/expression/builtin_cast.go b/expression/builtin_cast.go index 4185717ee9dbe..9f55331f2fd46 100644 --- a/expression/builtin_cast.go +++ b/expression/builtin_cast.go @@ -577,7 +577,13 @@ func (b *builtinCastIntAsTimeSig) evalTime(row chunk.Row) (res types.Time, isNul if isNull || err != nil { return res, isNull, err } - res, err = types.ParseTimeFromNum(b.ctx.GetSessionVars().StmtCtx, val, b.tp.Tp, int8(b.tp.Decimal)) + + if b.args[0].GetType().Tp == mysql.TypeYear { + res, err = types.ParseTimeFromYear(b.ctx.GetSessionVars().StmtCtx, val) + } else { + res, err = types.ParseTimeFromNum(b.ctx.GetSessionVars().StmtCtx, val, b.tp.Tp, int8(b.tp.Decimal)) + } + if err != nil { return types.ZeroTime, true, handleInvalidTimeError(b.ctx, err) } diff --git a/expression/builtin_cast_vec.go b/expression/builtin_cast_vec.go index f6a77ffd10f0d..4ec1d138f8f41 100644 --- a/expression/builtin_cast_vec.go +++ b/expression/builtin_cast_vec.go @@ -376,11 +376,19 @@ func (b *builtinCastIntAsTimeSig) vecEvalTime(input *chunk.Chunk, result *chunk. i64s := buf.Int64s() stmt := b.ctx.GetSessionVars().StmtCtx fsp := int8(b.tp.Decimal) + + var tm types.Time for i := 0; i < n; i++ { if buf.IsNull(i) { continue } - tm, err := types.ParseTimeFromNum(stmt, i64s[i], b.tp.Tp, fsp) + + if b.args[0].GetType().Tp == mysql.TypeYear { + tm, err = types.ParseTimeFromYear(stmt, i64s[i]) + } else { + tm, err = types.ParseTimeFromNum(stmt, i64s[i], b.tp.Tp, fsp) + } + if err != nil { if err = handleInvalidTimeError(b.ctx, err); err != nil { return err diff --git a/expression/builtin_compare.go b/expression/builtin_compare.go index 5c73e0e070793..b6655fb8a5df7 100644 --- a/expression/builtin_compare.go +++ b/expression/builtin_compare.go @@ -1059,6 +1059,9 @@ func getBaseCmpType(lhs, rhs types.EvalType, lft, rft *types.FieldType) types.Ev } else if ((lhs == types.ETInt || lft.Hybrid()) || lhs == types.ETDecimal) && ((rhs == types.ETInt || rft.Hybrid()) || rhs == types.ETDecimal) { return types.ETDecimal + } else if types.IsTemporalWithDate(lft.Tp) && rft.Tp == mysql.TypeYear || + lft.Tp == mysql.TypeYear && types.IsTemporalWithDate(rft.Tp) { + return types.ETDatetime } return types.ETReal } diff --git a/expression/integration_test.go b/expression/integration_test.go index 901857343d955..9944506dc15a2 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -7131,6 +7131,41 @@ func (s *testIntegrationSuite) TestIssue16505(c *C) { tk.MustExec("drop table t;") } +func (s *testIntegrationSuite) TestIssue20121(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + // testcase for Datetime vs Year + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a datetime, b year)") + tk.MustExec("insert into t values('2000-05-03 16:44:44', 2018)") + tk.MustExec("insert into t values('2020-10-01 11:11:11', 2000)") + tk.MustExec("insert into t values('2020-10-01 11:11:11', 2070)") + tk.MustExec("insert into t values('2020-10-01 11:11:11', 1999)") + + tk.MustQuery("select * from t where t.a < t.b").Check(testkit.Rows("2000-05-03 16:44:44 2018", "2020-10-01 11:11:11 2070")) + tk.MustQuery("select * from t where t.a > t.b").Check(testkit.Rows("2020-10-01 11:11:11 2000", "2020-10-01 11:11:11 1999")) + + // testcase for Date vs Year + tk.MustExec("drop table if exists tt") + tk.MustExec("create table tt(a date, b year)") + tk.MustExec("insert into tt values('2019-11-11', 2000)") + tk.MustExec("insert into tt values('2019-11-11', 2020)") + tk.MustExec("insert into tt values('2019-11-11', 2022)") + + tk.MustQuery("select * from tt where tt.a > tt.b").Check(testkit.Rows("2019-11-11 2000")) + tk.MustQuery("select * from tt where tt.a < tt.b").Check(testkit.Rows("2019-11-11 2020", "2019-11-11 2022")) + + // testcase for Timestamp vs Year + tk.MustExec("drop table if exists ttt") + tk.MustExec("create table ttt(a timestamp, b year)") + tk.MustExec("insert into ttt values('2019-11-11 11:11:11', 2019)") + tk.MustExec("insert into ttt values('2019-11-11 11:11:11', 2000)") + tk.MustExec("insert into ttt values('2019-11-11 11:11:11', 2022)") + + tk.MustQuery("select * from ttt where ttt.a > ttt.b").Check(testkit.Rows("2019-11-11 11:11:11 2019", "2019-11-11 11:11:11 2000")) + tk.MustQuery("select * from ttt where ttt.a < ttt.b").Check(testkit.Rows("2019-11-11 11:11:11 2022")) +} + func (s *testIntegrationSuite) TestIssue16779(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test") diff --git a/types/time.go b/types/time.go index 5e7c5fa8ae924..485b49734d3af 100644 --- a/types/time.go +++ b/types/time.go @@ -1950,6 +1950,17 @@ func ParseDate(sc *stmtctx.StatementContext, str string) (Time, error) { return ParseTime(sc, str, mysql.TypeDate, MinFsp) } +// ParseTimeFromYear parse a `YYYY` formed year to corresponded Datetime type. +// Note: the invoker must promise the `year` is in the range [MinYear, MaxYear]. +func ParseTimeFromYear(sc *stmtctx.StatementContext, year int64) (Time, error) { + if year == 0 { + return NewTime(ZeroCoreTime, mysql.TypeDate, DefaultFsp), nil + } + + dt := FromDate(int(year), 0, 0, 0, 0, 0, 0) + return NewTime(dt, mysql.TypeDatetime, DefaultFsp), nil +} + // ParseTimeFromNum parses a formatted int64, // returns the value which type is tp. func ParseTimeFromNum(sc *stmtctx.StatementContext, num int64, tp byte, fsp int8) (Time, error) {