diff --git a/executor/executor_test.go b/executor/executor_test.go index df561d5970aae..3d4638e667bca 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -6285,22 +6285,16 @@ func (s *testSuite) TestIssue19667(c *C) { tk.MustQuery(`SELECT DATE_ADD(a, INTERVAL 1 SECOND) FROM t`).Check(testkit.Rows("1988-04-17 02:00:00")) } -func (s *testSuite) TestTxnRetry(c *C) { +func (s *testSuite) TestIssue20305(c *C) { tk := testkit.NewTestKit(c, s.store) - tk2 := testkit.NewTestKit(c, s.store) - tk.MustExec("use test;") - tk2.MustExec("use test;") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (a int);") - tk.MustExec("insert into t values (1)") - tk.MustExec("set @@tidb_disable_txn_auto_retry=0;") - tk.MustExec("set autocommit=0;") - tk.MustQuery("select * from t;").Check(testkit.Rows("1")) - tk.MustExec("SET SQL_SELECT_LIMIT=DEFAULT;") - - tk2.MustExec("update t set a=2") - - tk.MustExec("update t set a=3") - tk.MustExec("commit") - tk.MustQuery("select * from t").Check(testkit.Rows("3")) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t2 (a year(4))") + tk.MustExec("insert into t2 values(69)") + tk.MustQuery("select * from t2 where a <= 69").Check(testkit.Rows("2069")) + // the following test is a regression test that matches MySQL's behavior. + tk.MustExec("drop table if exists t3") + tk.MustExec("CREATE TABLE `t3` (`y` year DEFAULT NULL, `a` int DEFAULT NULL)") + 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")) } diff --git a/expression/builtin_compare.go b/expression/builtin_compare.go index c5b5d060787bf..d618c683d0a66 100644 --- a/expression/builtin_compare.go +++ b/expression/builtin_compare.go @@ -1282,7 +1282,8 @@ func RefineComparedConstant(ctx sessionctx.Context, targetFieldType types.FieldT } // refineArgs will rewrite the arguments if the compare expression is `int column non-int constant` or -// `non-int constant int column`. E.g., `a < 1.1` will be rewritten to `a < 2`. +// `non-int constant int column`. E.g., `a < 1.1` will be rewritten to `a < 2`. It also handles comparing year type +// with int constant if the int constant falls into a sensible year representation. func (c *compareFunctionClass) refineArgs(ctx sessionctx.Context, args []Expression) []Expression { if ContainMutableConst(ctx, args) { return args @@ -1325,6 +1326,22 @@ func (c *compareFunctionClass) refineArgs(ctx sessionctx.Context, args []Express } } } + // int constant [cmp] year type + if arg0IsCon && arg0IsInt && arg1Type.Tp == mysql.TypeYear { + adjusted, failed := types.AdjustYear(arg0.Value.GetInt64(), false) + if failed == nil { + arg0.Value.SetInt64(adjusted) + finalArg0 = arg0 + } + } + // year type [cmp] int constant + if arg1IsCon && arg1IsInt && arg0Type.Tp == mysql.TypeYear { + adjusted, failed := types.AdjustYear(arg1.Value.GetInt64(), false) + if failed == nil { + arg1.Value.SetInt64(adjusted) + finalArg1 = arg1 + } + } if isExceptional && (c.op == opcode.EQ || c.op == opcode.NullEQ) { // This will always be false. return []Expression{NewZero(), NewOne()} diff --git a/types/time.go b/types/time.go index b4d1ce898b321..7a8ba205986a7 100644 --- a/types/time.go +++ b/types/time.go @@ -1224,8 +1224,8 @@ func adjustYear(y int) int { } // AdjustYear is used for adjusting year and checking its validation. -func AdjustYear(y int64, shouldAdjust bool) (int64, error) { - if y == 0 && !shouldAdjust { +func AdjustYear(y int64, adjustZero bool) (int64, error) { + if y == 0 && !adjustZero { return y, nil } y = int64(adjustYear(int(y))) diff --git a/util/ranger/ranger_test.go b/util/ranger/ranger_test.go index 2aaf271cae238..27f26c1cb46f1 100644 --- a/util/ranger/ranger_test.go +++ b/util/ranger/ranger_test.go @@ -1234,6 +1234,7 @@ func (s *testRangerSuite) TestIndexRangeForYear(c *C) { testKit.MustExec("INSERT INTO t VALUES (1), (70), (99), (0), ('0')") testKit.MustQuery("SELECT * FROM t WHERE a < 15698").Check(testkit.Rows("0", "1970", "1999", "2000", "2001")) testKit.MustQuery("SELECT * FROM t WHERE a <= 0").Check(testkit.Rows("0")) + testKit.MustQuery("SELECT * FROM t WHERE a <= 1").Check(testkit.Rows("0", "1970", "1999", "2000", "2001")) testKit.MustQuery("SELECT * FROM t WHERE a < 2000").Check(testkit.Rows("0", "1970", "1999")) testKit.MustQuery("SELECT * FROM t WHERE a > -1").Check(testkit.Rows("0", "1970", "1999", "2000", "2001")) @@ -1261,14 +1262,21 @@ func (s *testRangerSuite) TestIndexRangeForYear(c *C) { { indexPos: 0, exprStr: `a not in (1, 2, 70)`, - accessConds: "[not(in(test.t.a, 1, 2, 70))]", + accessConds: "[not(in(test.t.a, 1, 2, 70))]", // this is in accordance with MySQL, MySQL won't interpret 70 here as 1970 filterConds: "[]", resultStr: `[(NULL,1970) (1970,2001) (2002,+inf]]`, }, + { + indexPos: 0, + exprStr: `a = 1 or a = 2 or a = 70`, + accessConds: "[or(eq(test.t.a, 2001), or(eq(test.t.a, 2002), eq(test.t.a, 1970)))]", // this is in accordance with MySQL, MySQL won't interpret 70 here as 1970 + filterConds: "[]", + resultStr: `[[1970,1970] [2001,2002]]`, + }, { indexPos: 0, exprStr: `a not in (99)`, - accessConds: "[ne(test.t.a, 99)]", + accessConds: "[ne(test.t.a, 1999)]", // this is in accordance with MySQL filterConds: "[]", resultStr: `[[-inf,1999) (1999,+inf]]`, }, @@ -1296,7 +1304,7 @@ func (s *testRangerSuite) TestIndexRangeForYear(c *C) { { indexPos: 0, exprStr: `a != 1`, - accessConds: "[ne(test.t.a, 1)]", + accessConds: "[ne(test.t.a, 2001)]", filterConds: "[]", resultStr: `[[-inf,2001) (2001,+inf]]`, }, @@ -1309,13 +1317,13 @@ func (s *testRangerSuite) TestIndexRangeForYear(c *C) { }, { exprStr: "a < 99 or a > 01", - accessConds: "[or(lt(test.t.a, 99), gt(test.t.a, 1))]", + accessConds: "[or(lt(test.t.a, 1999), gt(test.t.a, 2001))]", filterConds: "[]", resultStr: "[[-inf,1999) (2001,+inf]]", }, { exprStr: "a >= 70 and a <= 69", - accessConds: "[ge(test.t.a, 70) le(test.t.a, 69)]", + accessConds: "[ge(test.t.a, 1970) le(test.t.a, 2069)]", filterConds: "[]", resultStr: "[[1970,2069]]", },