From 0d84245241e995d500eabcfc46084f4c681f71c9 Mon Sep 17 00:00:00 2001 From: tiancaiamao Date: Thu, 16 Jan 2020 10:08:50 +0800 Subject: [PATCH] expression,table: fix insert partitioned table bug when the time zone change (#14370) (#14476) --- expression/builtin.go | 4 +++ expression/builtin_compare.go | 4 +++ expression/builtin_time.go | 15 +++++++-- expression/scalar_function.go | 3 ++ table/tables/partition_test.go | 57 ++++++++++++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 3 deletions(-) diff --git a/expression/builtin.go b/expression/builtin.go index da5f0a6509a0b..618dccae2466a 100644 --- a/expression/builtin.go +++ b/expression/builtin.go @@ -288,6 +288,10 @@ type builtinFunc interface { Clone() builtinFunc } +type builtinFuncNew interface { + evalIntWithCtx(ctx sessionctx.Context, row chunk.Row) (val int64, isNull bool, err error) +} + // baseFunctionClass will be contained in every struct that implement functionClass interface. type baseFunctionClass struct { funcName string diff --git a/expression/builtin_compare.go b/expression/builtin_compare.go index f5a369fe32c31..3c4095c6a0dae 100644 --- a/expression/builtin_compare.go +++ b/expression/builtin_compare.go @@ -1455,6 +1455,10 @@ func (b *builtinLTIntSig) Clone() builtinFunc { return newSig } +func (b *builtinLTIntSig) evalIntWithCtx(ctx sessionctx.Context, row chunk.Row) (val int64, isNull bool, err error) { + return resOfLT(CompareInt(ctx, b.args[0], b.args[1], row, row)) +} + func (b *builtinLTIntSig) evalInt(row chunk.Row) (val int64, isNull bool, err error) { return resOfLT(CompareInt(b.ctx, b.args[0], b.args[1], row, row)) } diff --git a/expression/builtin_time.go b/expression/builtin_time.go index 9b88af516f94c..96455e57190be 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -123,6 +123,10 @@ var ( _ functionClass = &subDateFunctionClass{} ) +var ( + _ builtinFuncNew = &builtinUnixTimestampIntSig{} +) + var ( _ builtinFunc = &builtinDateSig{} _ builtinFunc = &builtinDateLiteralSig{} @@ -4315,15 +4319,20 @@ func (b *builtinUnixTimestampIntSig) Clone() builtinFunc { // evalInt evals a UNIX_TIMESTAMP(time). // See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_unix-timestamp func (b *builtinUnixTimestampIntSig) evalInt(row chunk.Row) (int64, bool, error) { - val, isNull, err := b.args[0].EvalTime(b.ctx, row) + return b.evalIntWithCtx(b.ctx, row) +} + +func (b *builtinUnixTimestampIntSig) evalIntWithCtx(ctx sessionctx.Context, row chunk.Row) (int64, bool, error) { + val, isNull, err := b.args[0].EvalTime(ctx, row) if err != nil && terror.ErrorEqual(types.ErrInvalidTimeFormat.GenWithStackByArgs(val), err) { - // Return 0 for invalid date time. return 0, false, nil } if isNull { return 0, true, nil } - t, err := val.Time.GoTime(getTimeZone(b.ctx)) + + tz := ctx.GetSessionVars().Location() + t, err := val.Time.GoTime(tz) if err != nil { return 0, false, nil } diff --git a/expression/scalar_function.go b/expression/scalar_function.go index dff91c56abd95..3fac1edfb6b5a 100644 --- a/expression/scalar_function.go +++ b/expression/scalar_function.go @@ -231,6 +231,9 @@ func (sf *ScalarFunction) Eval(row chunk.Row) (d types.Datum, err error) { // EvalInt implements Expression interface. func (sf *ScalarFunction) EvalInt(ctx sessionctx.Context, row chunk.Row) (int64, bool, error) { + if f, ok := sf.Function.(builtinFuncNew); ok { + return f.evalIntWithCtx(ctx, row) + } return sf.Function.evalInt(row) } diff --git a/table/tables/partition_test.go b/table/tables/partition_test.go index 9a2cfb38eb539..8a4af61de0d0d 100644 --- a/table/tables/partition_test.go +++ b/table/tables/partition_test.go @@ -308,3 +308,60 @@ func (ts *testSuite) TestLocateRangePartitionErr(c *C) { _, err := tk.Exec("INSERT INTO t_month_data_monitor VALUES (4, '2019-04-04')") c.Assert(table.ErrNoPartitionForGivenValue.Equal(err), IsTrue) } + +func (ts *testSuite) TestTimeZoneChange(c *C) { + tk := testkit.NewTestKitWithInit(c, ts.store) + tk.MustExec("use test") + createTable := `CREATE TABLE timezone_test ( + id int(11) NOT NULL, + creation_dt timestamp DEFAULT CURRENT_TIMESTAMP ) PARTITION BY RANGE ( unix_timestamp(creation_dt) ) +( PARTITION p5 VALUES LESS THAN ( UNIX_TIMESTAMP('2020-01-03 15:10:00') ), + PARTITION p6 VALUES LESS THAN ( UNIX_TIMESTAMP('2020-01-03 15:15:00') ), + PARTITION p7 VALUES LESS THAN ( UNIX_TIMESTAMP('2020-01-03 15:20:00') ), + PARTITION p8 VALUES LESS THAN ( UNIX_TIMESTAMP('2020-01-03 15:25:00') ), + PARTITION p9 VALUES LESS THAN (MAXVALUE) )` + tk.MustExec("SET @@time_zone = 'Asia/Shanghai'") + tk.MustExec(createTable) + tk.MustQuery("SHOW CREATE TABLE timezone_test").Check(testkit.Rows("timezone_test CREATE TABLE `timezone_test` (\n" + + " `id` int(11) NOT NULL,\n" + + " `creation_dt` timestamp DEFAULT CURRENT_TIMESTAMP\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" + + "PARTITION BY RANGE ( unix_timestamp(`creation_dt`) ) (\n" + + " PARTITION p5 VALUES LESS THAN (1578035400),\n" + + " PARTITION p6 VALUES LESS THAN (1578035700),\n" + + " PARTITION p7 VALUES LESS THAN (1578036000),\n" + + " PARTITION p8 VALUES LESS THAN (1578036300),\n" + + " PARTITION p9 VALUES LESS THAN (MAXVALUE)\n)")) + tk.MustExec("DROP TABLE timezone_test") + + // Note that the result of "show create table" varies with time_zone. + tk.MustExec("SET @@time_zone = 'UTC'") + tk.MustExec(createTable) + tk.MustQuery("SHOW CREATE TABLE timezone_test").Check(testkit.Rows("timezone_test CREATE TABLE `timezone_test` (\n" + + " `id` int(11) NOT NULL,\n" + + " `creation_dt` timestamp DEFAULT CURRENT_TIMESTAMP\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" + + "PARTITION BY RANGE ( unix_timestamp(`creation_dt`) ) (\n" + + " PARTITION p5 VALUES LESS THAN (1578064200),\n" + + " PARTITION p6 VALUES LESS THAN (1578064500),\n" + + " PARTITION p7 VALUES LESS THAN (1578064800),\n" + + " PARTITION p8 VALUES LESS THAN (1578065100),\n" + + " PARTITION p9 VALUES LESS THAN (MAXVALUE)\n)")) + + // Change time zone and insert data, check the data locates in the correct partition. + tk.MustExec("SET @@time_zone = 'Asia/Shanghai'") + tk.MustExec("INSERT INTO timezone_test VALUES (1,'2020-01-03 15:16:59')") + tk.MustQuery("SELECT * FROM timezone_test PARTITION (p5)").Check(testkit.Rows("1 2020-01-03 15:16:59")) + tk.MustQuery("SELECT * FROM timezone_test PARTITION (p6)").Check(testkit.Rows()) + tk.MustQuery("SELECT * FROM timezone_test PARTITION (p7)").Check(testkit.Rows()) + tk.MustQuery("SELECT * FROM timezone_test PARTITION (p8)").Check(testkit.Rows()) + tk.MustQuery("SELECT * FROM timezone_test PARTITION (p9)").Check(testkit.Rows()) + + tk.MustExec("SET @@time_zone = 'UTC'") + tk.MustExec("INSERT INTO timezone_test VALUES (1,'2020-01-03 15:16:59')") + tk.MustQuery("SELECT * FROM timezone_test PARTITION (p5)").Check(testkit.Rows("1 2020-01-03 07:16:59")) + tk.MustQuery("SELECT * FROM timezone_test PARTITION (p6)").Check(testkit.Rows()) + tk.MustQuery("SELECT * FROM timezone_test PARTITION (p7)").Check(testkit.Rows("1 2020-01-03 15:16:59")) + tk.MustQuery("SELECT * FROM timezone_test PARTITION (p8)").Check(testkit.Rows()) + tk.MustQuery("SELECT * FROM timezone_test PARTITION (p9)").Check(testkit.Rows()) +}