From 7ad9b1af7a7ad256fd48d0970dcc40ea9af35d58 Mon Sep 17 00:00:00 2001 From: Yuanjia Zhang Date: Mon, 13 May 2019 19:55:26 +0800 Subject: [PATCH] expression: fix issue that `timestampadd` is not compatible with Mysql (#10431) --- expression/builtin_time.go | 19 ++++++++++++++++++- expression/integration_test.go | 12 ++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/expression/builtin_time.go b/expression/builtin_time.go index ec055d6f4eb74..b96a567f64ae4 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -5649,13 +5649,30 @@ func (b *builtinTimestampAddSig) evalString(row chunk.Row) (string, bool, error) default: return "", true, errors.Trace(types.ErrInvalidTimeFormat.GenWithStackByArgs(unit)) } - r := types.Time{Time: types.FromGoTime(tb), Type: mysql.TypeDatetime, Fsp: fsp} + r := types.Time{Time: types.FromGoTime(tb), Type: b.resolveType(arg.Type, unit), Fsp: fsp} if err = r.Check(b.ctx.GetSessionVars().StmtCtx); err != nil { return "", true, errors.Trace(handleInvalidTimeError(b.ctx, err)) } return r.String(), false, nil } +func (b *builtinTimestampAddSig) resolveType(typ uint8, unit string) uint8 { + // The approach below is from MySQL. + // The field type for the result of an Item_date function is defined as + // follows: + // + //- If first arg is a MYSQL_TYPE_DATETIME result is MYSQL_TYPE_DATETIME + //- If first arg is a MYSQL_TYPE_DATE and the interval type uses hours, + // minutes, seconds or microsecond then type is MYSQL_TYPE_DATETIME. + //- Otherwise the result is MYSQL_TYPE_STRING + // (This is because you can't know if the string contains a DATE, MYSQL_TIME + // or DATETIME argument) + if typ == mysql.TypeDate && (unit == "HOUR" || unit == "MINUTE" || unit == "SECOND" || unit == "MICROSECOND") { + return mysql.TypeDatetime + } + return typ +} + type toDaysFunctionClass struct { baseFunctionClass } diff --git a/expression/integration_test.go b/expression/integration_test.go index ab96348ff247c..8190f6be87a01 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -1300,6 +1300,18 @@ func (s *testIntegrationSuite) TestTimeBuiltin(c *C) { result.Check(testkit.Rows("2017-01-18 12:39:50.123 2017-01-18 12:39:50.999")) result = tk.MustQuery("select timestamp('2003-12-31', '01:01:01.01'), timestamp('2003-12-31 12:34', '01:01:01.01')," + " timestamp('2008-12-31','00:00:00.0'), timestamp('2008-12-31 00:00:00.000');") + + tk.MustQuery(`select timestampadd(second, 1, cast("2001-01-01" as date))`).Check(testkit.Rows("2001-01-01 00:00:01")) + tk.MustQuery(`select timestampadd(hour, 1, cast("2001-01-01" as date))`).Check(testkit.Rows("2001-01-01 01:00:00")) + tk.MustQuery(`select timestampadd(day, 1, cast("2001-01-01" as date))`).Check(testkit.Rows("2001-01-02")) + tk.MustQuery(`select timestampadd(month, 1, cast("2001-01-01" as date))`).Check(testkit.Rows("2001-02-01")) + tk.MustQuery(`select timestampadd(year, 1, cast("2001-01-01" as date))`).Check(testkit.Rows("2002-01-01")) + tk.MustQuery(`select timestampadd(second, 1, cast("2001-01-01" as datetime))`).Check(testkit.Rows("2001-01-01 00:00:01")) + tk.MustQuery(`select timestampadd(hour, 1, cast("2001-01-01" as datetime))`).Check(testkit.Rows("2001-01-01 01:00:00")) + tk.MustQuery(`select timestampadd(day, 1, cast("2001-01-01" as datetime))`).Check(testkit.Rows("2001-01-02 00:00:00")) + tk.MustQuery(`select timestampadd(month, 1, cast("2001-01-01" as datetime))`).Check(testkit.Rows("2001-02-01 00:00:00")) + tk.MustQuery(`select timestampadd(year, 1, cast("2001-01-01" as datetime))`).Check(testkit.Rows("2002-01-01 00:00:00")) + result.Check(testkit.Rows("2003-12-31 01:01:01.01 2003-12-31 13:35:01.01 2008-12-31 00:00:00.0 2008-12-31 00:00:00.000")) result = tk.MustQuery("select timestamp('2003-12-31', 1), timestamp('2003-12-31', -1);") result.Check(testkit.Rows("2003-12-31 00:00:01 2003-12-30 23:59:59"))