From 6fd3c1bc509bf46be9a8b6a16d03bd051ab26426 Mon Sep 17 00:00:00 2001 From: Guo Song Date: Tue, 30 Apr 2019 14:25:23 +0800 Subject: [PATCH 01/17] fix 9727 --- expression/builtin_time.go | 248 +++++++++++++++++++++++++++++++- expression/builtin_time_test.go | 56 ++++++++ types/time.go | 24 +++- 3 files changed, 320 insertions(+), 8 deletions(-) diff --git a/expression/builtin_time.go b/expression/builtin_time.go index ba08265945dc6..24cce1762afe6 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -217,21 +217,27 @@ var ( _ builtinFunc = &builtinExtractDurationSig{} _ builtinFunc = &builtinAddDateStringStringSig{} _ builtinFunc = &builtinAddDateStringIntSig{} + _ builtinFunc = &builtinAddDateStringRealSig{} _ builtinFunc = &builtinAddDateStringDecimalSig{} _ builtinFunc = &builtinAddDateIntStringSig{} _ builtinFunc = &builtinAddDateIntIntSig{} + _ builtinFunc = &builtinAddDateIntRealSig{} _ builtinFunc = &builtinAddDateIntDecimalSig{} _ builtinFunc = &builtinAddDateDatetimeStringSig{} _ builtinFunc = &builtinAddDateDatetimeIntSig{} + _ builtinFunc = &builtinAddDateDatetimeRealSig{} _ builtinFunc = &builtinAddDateDatetimeDecimalSig{} _ builtinFunc = &builtinSubDateStringStringSig{} _ builtinFunc = &builtinSubDateStringIntSig{} + _ builtinFunc = &builtinSubDateStringRealSig{} _ builtinFunc = &builtinSubDateStringDecimalSig{} _ builtinFunc = &builtinSubDateIntStringSig{} _ builtinFunc = &builtinSubDateIntIntSig{} + _ builtinFunc = &builtinSubDateIntRealSig{} _ builtinFunc = &builtinSubDateIntDecimalSig{} _ builtinFunc = &builtinSubDateDatetimeStringSig{} _ builtinFunc = &builtinSubDateDatetimeIntSig{} + _ builtinFunc = &builtinSubDateDatetimeRealSig{} _ builtinFunc = &builtinSubDateDatetimeDecimalSig{} ) @@ -2703,6 +2709,14 @@ func (du *baseDateArithmitical) getIntervalFromInt(ctx sessionctx.Context, args return strconv.FormatInt(interval, 10), false, nil } +func (du *baseDateArithmitical) getIntervalFromReal(ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (string, bool, error) { + interval, isNull, err := args[1].EvalReal(ctx, row) + if isNull || err != nil { + return "", true, err + } + return strconv.FormatFloat(interval, 'f', -1, 64), false, nil +} + func (du *baseDateArithmitical) add(ctx sessionctx.Context, date types.Time, interval string, unit string) (types.Time, bool, error) { year, month, day, dur, err := types.ExtractTimeValue(unit, interval) if err != nil { @@ -2783,7 +2797,7 @@ func (c *addDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres } intervalEvalTp := args[1].GetType().EvalType() - if intervalEvalTp != types.ETString && intervalEvalTp != types.ETDecimal { + if intervalEvalTp != types.ETString && intervalEvalTp != types.ETDecimal && intervalEvalTp != types.ETReal { intervalEvalTp = types.ETInt } @@ -2802,6 +2816,11 @@ func (c *addDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres baseBuiltinFunc: bf, baseDateArithmitical: newDateArighmeticalUtil(), } + case dateEvalTp == types.ETString && intervalEvalTp == types.ETReal: + sig = &builtinAddDateStringRealSig{ + baseBuiltinFunc: bf, + baseDateArithmitical: newDateArighmeticalUtil(), + } case dateEvalTp == types.ETString && intervalEvalTp == types.ETDecimal: sig = &builtinAddDateStringDecimalSig{ baseBuiltinFunc: bf, @@ -2817,6 +2836,11 @@ func (c *addDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres baseBuiltinFunc: bf, baseDateArithmitical: newDateArighmeticalUtil(), } + case dateEvalTp == types.ETInt && intervalEvalTp == types.ETReal: + sig = &builtinAddDateIntRealSig{ + baseBuiltinFunc: bf, + baseDateArithmitical: newDateArighmeticalUtil(), + } case dateEvalTp == types.ETInt && intervalEvalTp == types.ETDecimal: sig = &builtinAddDateIntDecimalSig{ baseBuiltinFunc: bf, @@ -2832,6 +2856,11 @@ func (c *addDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres baseBuiltinFunc: bf, baseDateArithmitical: newDateArighmeticalUtil(), } + case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETReal: + sig = &builtinAddDateIntRealSig{ + baseBuiltinFunc: bf, + baseDateArithmitical: newDateArighmeticalUtil(), + } case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETDecimal: sig = &builtinAddDateDatetimeDecimalSig{ baseBuiltinFunc: bf, @@ -2907,6 +2936,39 @@ func (b *builtinAddDateStringIntSig) evalTime(row chunk.Row) (types.Time, bool, return result, isNull || err != nil, err } +type builtinAddDateStringRealSig struct { + baseBuiltinFunc + baseDateArithmitical +} + +func (b *builtinAddDateStringRealSig) Clone() builtinFunc { + newSig := &builtinAddDateStringRealSig{baseDateArithmitical: b.baseDateArithmitical} + newSig.cloneFrom(&b.baseBuiltinFunc) + return newSig +} + +// evalTime evals ADDDATE(date,INTERVAL expr unit). +// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate +func (b *builtinAddDateStringRealSig) evalTime(row chunk.Row) (types.Time, bool, error) { + unit, isNull, err := b.args[2].EvalString(b.ctx, row) + if isNull || err != nil { + return types.Time{}, true, err + } + + date, isNull, err := b.getDateFromString(b.ctx, b.args, row, unit) + if isNull || err != nil { + return types.Time{}, true, err + } + + interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit) + if isNull || err != nil { + return types.Time{}, true, err + } + + result, isNull, err := b.add(b.ctx, date, interval, unit) + return result, isNull || err != nil, err +} + type builtinAddDateStringDecimalSig struct { baseBuiltinFunc baseDateArithmitical @@ -3006,6 +3068,39 @@ func (b *builtinAddDateIntIntSig) evalTime(row chunk.Row) (types.Time, bool, err return result, isNull || err != nil, err } +type builtinAddDateIntRealSig struct { + baseBuiltinFunc + baseDateArithmitical +} + +func (b *builtinAddDateIntRealSig) Clone() builtinFunc { + newSig := &builtinAddDateIntRealSig{baseDateArithmitical: b.baseDateArithmitical} + newSig.cloneFrom(&b.baseBuiltinFunc) + return newSig +} + +// evalTime evals ADDDATE(date,INTERVAL expr unit). +// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate +func (b *builtinAddDateIntRealSig) evalTime(row chunk.Row) (types.Time, bool, error) { + unit, isNull, err := b.args[2].EvalString(b.ctx, row) + if isNull || err != nil { + return types.Time{}, true, err + } + + date, isNull, err := b.getDateFromInt(b.ctx, b.args, row, unit) + if isNull || err != nil { + return types.Time{}, true, err + } + + interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit) + if isNull || err != nil { + return types.Time{}, true, err + } + + result, isNull, err := b.add(b.ctx, date, interval, unit) + return result, isNull || err != nil, err +} + type builtinAddDateIntDecimalSig struct { baseBuiltinFunc baseDateArithmitical @@ -3096,6 +3191,8 @@ func (b *builtinAddDateDatetimeIntSig) evalTime(row chunk.Row) (types.Time, bool return types.Time{}, true, err } + fmt.Println(date.Type) + interval, isNull, err := b.getIntervalFromInt(b.ctx, b.args, row, unit) if isNull || err != nil { return types.Time{}, true, err @@ -3105,6 +3202,39 @@ func (b *builtinAddDateDatetimeIntSig) evalTime(row chunk.Row) (types.Time, bool return result, isNull || err != nil, err } +type builtinAddDateDatetimeRealSig struct { + baseBuiltinFunc + baseDateArithmitical +} + +func (b *builtinAddDateDatetimeRealSig) Clone() builtinFunc { + newSig := &builtinAddDateDatetimeRealSig{baseDateArithmitical: b.baseDateArithmitical} + newSig.cloneFrom(&b.baseBuiltinFunc) + return newSig +} + +// evalTime evals ADDDATE(date,INTERVAL expr unit). +// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate +func (b *builtinAddDateDatetimeRealSig) evalTime(row chunk.Row) (types.Time, bool, error) { + unit, isNull, err := b.args[2].EvalString(b.ctx, row) + if isNull || err != nil { + return types.Time{}, true, err + } + + date, isNull, err := b.getDateFromDatetime(b.ctx, b.args, row, unit) + if isNull || err != nil { + return types.Time{}, true, err + } + + interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit) + if isNull || err != nil { + return types.Time{}, true, err + } + + result, isNull, err := b.add(b.ctx, date, interval, unit) + return result, isNull || err != nil, err +} + type builtinAddDateDatetimeDecimalSig struct { baseBuiltinFunc baseDateArithmitical @@ -3153,7 +3283,7 @@ func (c *subDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres } intervalEvalTp := args[1].GetType().EvalType() - if intervalEvalTp != types.ETString && intervalEvalTp != types.ETDecimal { + if intervalEvalTp != types.ETString && intervalEvalTp != types.ETDecimal && intervalEvalTp != types.ETReal { intervalEvalTp = types.ETInt } @@ -3172,6 +3302,11 @@ func (c *subDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres baseBuiltinFunc: bf, baseDateArithmitical: newDateArighmeticalUtil(), } + case dateEvalTp == types.ETString && intervalEvalTp == types.ETReal: + sig = &builtinSubDateStringRealSig{ + baseBuiltinFunc: bf, + baseDateArithmitical: newDateArighmeticalUtil(), + } case dateEvalTp == types.ETString && intervalEvalTp == types.ETDecimal: sig = &builtinSubDateStringDecimalSig{ baseBuiltinFunc: bf, @@ -3187,6 +3322,11 @@ func (c *subDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres baseBuiltinFunc: bf, baseDateArithmitical: newDateArighmeticalUtil(), } + case dateEvalTp == types.ETInt && intervalEvalTp == types.ETReal: + sig = &builtinSubDateIntRealSig{ + baseBuiltinFunc: bf, + baseDateArithmitical: newDateArighmeticalUtil(), + } case dateEvalTp == types.ETInt && intervalEvalTp == types.ETDecimal: sig = &builtinSubDateIntDecimalSig{ baseBuiltinFunc: bf, @@ -3202,6 +3342,11 @@ func (c *subDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres baseBuiltinFunc: bf, baseDateArithmitical: newDateArighmeticalUtil(), } + case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETReal: + sig = &builtinSubDateDatetimeRealSig{ + baseBuiltinFunc: bf, + baseDateArithmitical: newDateArighmeticalUtil(), + } case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETDecimal: sig = &builtinSubDateDatetimeDecimalSig{ baseBuiltinFunc: bf, @@ -3277,6 +3422,39 @@ func (b *builtinSubDateStringIntSig) evalTime(row chunk.Row) (types.Time, bool, return result, isNull || err != nil, err } +type builtinSubDateStringRealSig struct { + baseBuiltinFunc + baseDateArithmitical +} + +func (b *builtinSubDateStringRealSig) Clone() builtinFunc { + newSig := &builtinSubDateStringRealSig{baseDateArithmitical: b.baseDateArithmitical} + newSig.cloneFrom(&b.baseBuiltinFunc) + return newSig +} + +// evalTime evals SUBDATE(date,INTERVAL expr unit). +// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate +func (b *builtinSubDateStringRealSig) evalTime(row chunk.Row) (types.Time, bool, error) { + unit, isNull, err := b.args[2].EvalString(b.ctx, row) + if isNull || err != nil { + return types.Time{}, true, err + } + + date, isNull, err := b.getDateFromString(b.ctx, b.args, row, unit) + if isNull || err != nil { + return types.Time{}, true, err + } + + interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit) + if isNull || err != nil { + return types.Time{}, true, err + } + + result, isNull, err := b.sub(b.ctx, date, interval, unit) + return result, isNull || err != nil, err +} + type builtinSubDateStringDecimalSig struct { baseBuiltinFunc baseDateArithmitical @@ -3374,6 +3552,39 @@ func (b *builtinSubDateIntIntSig) evalTime(row chunk.Row) (types.Time, bool, err return result, isNull || err != nil, err } +type builtinSubDateIntRealSig struct { + baseBuiltinFunc + baseDateArithmitical +} + +func (b *builtinSubDateIntRealSig) Clone() builtinFunc { + newSig := &builtinSubDateIntRealSig{baseDateArithmitical: b.baseDateArithmitical} + newSig.cloneFrom(&b.baseBuiltinFunc) + return newSig +} + +// evalTime evals SUBDATE(date,INTERVAL expr unit). +// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate +func (b *builtinSubDateIntRealSig) evalTime(row chunk.Row) (types.Time, bool, error) { + unit, isNull, err := b.args[2].EvalString(b.ctx, row) + if isNull || err != nil { + return types.Time{}, true, err + } + + date, isNull, err := b.getDateFromInt(b.ctx, b.args, row, unit) + if isNull || err != nil { + return types.Time{}, true, err + } + + interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit) + if isNull || err != nil { + return types.Time{}, true, err + } + + result, isNull, err := b.sub(b.ctx, date, interval, unit) + return result, isNull || err != nil, err +} + type builtinSubDateDatetimeStringSig struct { baseBuiltinFunc baseDateArithmitical @@ -3473,6 +3684,39 @@ func (b *builtinSubDateDatetimeIntSig) evalTime(row chunk.Row) (types.Time, bool return result, isNull || err != nil, err } +type builtinSubDateDatetimeRealSig struct { + baseBuiltinFunc + baseDateArithmitical +} + +func (b *builtinSubDateDatetimeRealSig) Clone() builtinFunc { + newSig := &builtinSubDateDatetimeRealSig{baseDateArithmitical: b.baseDateArithmitical} + newSig.cloneFrom(&b.baseBuiltinFunc) + return newSig +} + +// evalTime evals SUBDATE(date,INTERVAL expr unit). +// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate +func (b *builtinSubDateDatetimeRealSig) evalTime(row chunk.Row) (types.Time, bool, error) { + unit, isNull, err := b.args[2].EvalString(b.ctx, row) + if isNull || err != nil { + return types.Time{}, true, err + } + + date, isNull, err := b.getDateFromDatetime(b.ctx, b.args, row, unit) + if isNull || err != nil { + return types.Time{}, true, err + } + + interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit) + if isNull || err != nil { + return types.Time{}, true, err + } + + result, isNull, err := b.sub(b.ctx, date, interval, unit) + return result, isNull || err != nil, err +} + type builtinSubDateDatetimeDecimalSig struct { baseBuiltinFunc baseDateArithmitical diff --git a/expression/builtin_time_test.go b/expression/builtin_time_test.go index dda5dbfc9d100..7d6205a4c1b2b 100644 --- a/expression/builtin_time_test.go +++ b/expression/builtin_time_test.go @@ -2504,6 +2504,62 @@ func (s *testEvaluatorSuite) TestWithTimeZone(c *C) { } } +// Tests #9727, which is caused by overflow +func (s *testEvaluatorSuite) TestDatetimeLargeDuration(c *C) { + fcAdd := funcs[ast.DateAdd] + fcSub := funcs[ast.DateSub] + + testsAdd := []struct { + fc functionClass + inputDate interface{} + duration interface{} + durationUnit string + expect interface{} + }{ + {fcAdd, "1900-01-01 00:00:00", "100000000:214748364700", "MINUTE_SECOND", "8895-03-27 22:11:40"}, + {fcAdd, "1900-01-01 00:00:00", int64(1) << 37, "SECOND", "6255-04-08 15:04:32"}, + {fcAdd, "1900-01-01 00:00:00", int64(1) << 31, "MINUTE", "5983-01-24 02:08:00"}, + {fcAdd, "1900-01-01 00:00:00", int64(1) << 38, "SECOND", nil}, + {fcAdd, "1900-01-01 00:00:00", int64(1) << 33, "MINUTE", nil}, + {fcAdd, "1900-01-01 00:00:00", int64(1) << 30, "HOUR", nil}, + {fcAdd, "1900-01-01 00:00:00", "1000000000:214748364700", "MINUTE_SECOND", nil}, + {fcAdd, 19000101000000, "100000000:214748364700", "MINUTE_SECOND", "8895-03-27 22:11:40"}, + {fcAdd, 19000101000000, int64(1) << 37, "SECOND", "6255-04-08 15:04:32"}, + {fcAdd, 19000101000000, int64(1) << 31, "MINUTE", "5983-01-24 02:08:00"}, + {fcAdd, "1900-01-01 00:00:00", 1.123456789e3, "SECOND", "1900-01-01 00:18:43.456789"}, + {fcAdd, 19000101000000, 1.123456789e3, "SECOND", "1900-01-01 00:18:43.456789"}, + + {fcSub, "8895-03-27 22:11:40", "100000000:214748364700", "MINUTE_SECOND", "1900-01-01 00:00:00"}, + {fcSub, "6255-04-08 15:04:32", int64(1) << 37, "SECOND", "1900-01-01 00:00:00"}, + {fcSub, "5983-01-24 02:08:00", int64(1) << 31, "MINUTE", "1900-01-01 00:00:00"}, + {fcSub, "9999-01-01 00:00:00", int64(1) << 39, "SECOND", nil}, + {fcSub, "9999-01-01 00:00:00", int64(1) << 33, "MINUTE", nil}, + {fcSub, "9999-01-01 00:00:00", int64(1) << 30, "HOUR", nil}, + {fcSub, "9999-01-01 00:00:00", "10000000000:214748364700", "MINUTE_SECOND", nil}, + {fcSub, 88950327221140, "100000000:214748364700", "MINUTE_SECOND", "1900-01-01 00:00:00"}, + {fcSub, 62550408150432, int64(1) << 37, "SECOND", "1900-01-01 00:00:00"}, + {fcSub, 59830124020800, int64(1) << 31, "MINUTE", "1900-01-01 00:00:00"}, + {fcSub, "1900-01-01 00:18:43.456789", 1.123456789e3, "SECOND", "1900-01-01 00:00:00"}, + {fcSub, types.NewDecFromStringForTest("19000101001843.456789"), 1.123456789e3, "SECOND", "1900-01-01 00:00:00"}, + } + + for _, test := range testsAdd { + args := types.MakeDatums(test.inputDate, test.duration, test.durationUnit) + f, err := test.fc.getFunction(s.ctx, s.datumsToConstants(args)) + c.Assert(err, IsNil) + c.Assert(f, NotNil) + v, err := evalBuiltinFunc(f, chunk.Row{}) + c.Assert(err, IsNil) + comment := Commentf("InputDate: %v Duration: %v DurationUnit: %v Expected: %v", test.inputDate, test.duration, test.durationUnit, test.expect) + if test.expect != nil { + c.Assert(v.IsNull(), IsFalse, comment) + c.Assert(v.GetMysqlTime().String(), Equals, test.expect, comment) + } else { + c.Assert(v.IsNull(), IsTrue, comment) + } + } +} + func (s *testEvaluatorSuite) TestTidbParseTso(c *C) { s.ctx.GetSessionVars().TimeZone = time.UTC tests := []struct { diff --git a/types/time.go b/types/time.go index f9b2083842840..3512f5d895744 100644 --- a/types/time.go +++ b/types/time.go @@ -1624,13 +1624,21 @@ func extractSingleTimeValue(unit string, format string) (int64, int64, int64, fl switch strings.ToUpper(unit) { case "MICROSECOND": - return 0, 0, 0, fv * float64(gotime.Microsecond), nil + dayCount := iv / (3600000000 * 24) + fv -= float64(dayCount * 3600000000 * 24) + return 0, 0, dayCount, fv * float64(gotime.Microsecond), nil case "SECOND": - return 0, 0, 0, fv * float64(gotime.Second), nil + dayCount := iv / (3600 * 24) + fv -= float64(dayCount * 3600 * 24) + return 0, 0, dayCount, fv * float64(gotime.Second), nil case "MINUTE": - return 0, 0, 0, float64(iv * int64(gotime.Minute)), nil + dayCount := iv / (60 * 24) + iv %= 60 * 24 + return 0, 0, dayCount, float64(iv * int64(gotime.Minute)), nil case "HOUR": - return 0, 0, 0, float64(iv * int64(gotime.Hour)), nil + dayCount := iv / 24 + iv %= 24 + return 0, 0, dayCount, float64(iv * int64(gotime.Hour)), nil case "DAY": return 0, 0, iv, 0, nil case "WEEK": @@ -1703,8 +1711,12 @@ func extractTimeValue(format string, index, cnt int) (int64, int64, int64, float if err != nil { return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(originalFmt) } - durations := hours*float64(gotime.Hour) + minutes*float64(gotime.Minute) + - seconds*float64(gotime.Second) + microseconds*float64(gotime.Microsecond) + + secondDuration := int64(hours*3600 + minutes*60 + seconds) + days += secondDuration / (3600 * 24) + secondDuration %= 3600 * 24 + + durations := float64(secondDuration)*float64(gotime.Second) + microseconds*float64(gotime.Microsecond) return years, months, days, durations, nil } From 24323717285f6db20d359e74add4f002547c0d8b Mon Sep 17 00:00:00 2001 From: Guo Song Date: Tue, 30 Apr 2019 16:40:46 +0800 Subject: [PATCH 02/17] fix --- expression/builtin_time.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/expression/builtin_time.go b/expression/builtin_time.go index 24cce1762afe6..5d848d602c177 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -2857,7 +2857,7 @@ func (c *addDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres baseDateArithmitical: newDateArighmeticalUtil(), } case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETReal: - sig = &builtinAddDateIntRealSig{ + sig = &builtinAddDateDatetimeRealSig{ baseBuiltinFunc: bf, baseDateArithmitical: newDateArighmeticalUtil(), } @@ -3191,8 +3191,6 @@ func (b *builtinAddDateDatetimeIntSig) evalTime(row chunk.Row) (types.Time, bool return types.Time{}, true, err } - fmt.Println(date.Type) - interval, isNull, err := b.getIntervalFromInt(b.ctx, b.args, row, unit) if isNull || err != nil { return types.Time{}, true, err From c4e83d5836806f7e5f9651607d07870ba6fc16aa Mon Sep 17 00:00:00 2001 From: Guo Song Date: Tue, 30 Apr 2019 19:30:18 +0800 Subject: [PATCH 03/17] fix --- expression/builtin_time.go | 11 +++---- types/time.go | 65 +++++++++++++++++++------------------- types/time_test.go | 51 +++++++++++++++--------------- 3 files changed, 63 insertions(+), 64 deletions(-) diff --git a/expression/builtin_time.go b/expression/builtin_time.go index 5d848d602c177..339ac918c7ce5 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -2718,7 +2718,7 @@ func (du *baseDateArithmitical) getIntervalFromReal(ctx sessionctx.Context, args } func (du *baseDateArithmitical) add(ctx sessionctx.Context, date types.Time, interval string, unit string) (types.Time, bool, error) { - year, month, day, dur, err := types.ExtractTimeValue(unit, interval) + year, month, day, nano, err := types.ParseDurationValue(unit, interval) if err != nil { return types.Time{}, true, handleInvalidTimeError(ctx, err) } @@ -2728,8 +2728,7 @@ func (du *baseDateArithmitical) add(ctx sessionctx.Context, date types.Time, int return types.Time{}, true, err } - duration := time.Duration(dur) - goTime = goTime.Add(duration) + goTime = goTime.Add(time.Duration(nano)) goTime = types.AddDate(year, month, day, goTime) if goTime.Nanosecond() == 0 { @@ -2750,18 +2749,18 @@ func (du *baseDateArithmitical) add(ctx sessionctx.Context, date types.Time, int } func (du *baseDateArithmitical) sub(ctx sessionctx.Context, date types.Time, interval string, unit string) (types.Time, bool, error) { - year, month, day, dur, err := types.ExtractTimeValue(unit, interval) + year, month, day, nano, err := types.ParseDurationValue(unit, interval) if err != nil { return types.Time{}, true, handleInvalidTimeError(ctx, err) } - year, month, day, dur = -year, -month, -day, -dur + year, month, day, nano = -year, -month, -day, -nano goTime, err := date.Time.GoTime(time.Local) if err != nil { return types.Time{}, true, err } - duration := time.Duration(dur) + duration := time.Duration(nano) goTime = goTime.Add(duration) goTime = types.AddDate(year, month, day, goTime) diff --git a/types/time.go b/types/time.go index 3512f5d895744..f4b3eda35e157 100644 --- a/types/time.go +++ b/types/time.go @@ -1615,7 +1615,7 @@ func ExtractDurationNum(d *Duration, unit string) (int64, error) { } } -func extractSingleTimeValue(unit string, format string) (int64, int64, int64, float64, error) { +func parseSingleTimeValue(unit string, format string) (int64, int64, int64, int64, error) { fv, err := strconv.ParseFloat(format, 64) if err != nil { return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) @@ -1626,19 +1626,19 @@ func extractSingleTimeValue(unit string, format string) (int64, int64, int64, fl case "MICROSECOND": dayCount := iv / (3600000000 * 24) fv -= float64(dayCount * 3600000000 * 24) - return 0, 0, dayCount, fv * float64(gotime.Microsecond), nil + return 0, 0, dayCount, int64(fv * float64(gotime.Microsecond)), nil case "SECOND": dayCount := iv / (3600 * 24) fv -= float64(dayCount * 3600 * 24) - return 0, 0, dayCount, fv * float64(gotime.Second), nil + return 0, 0, dayCount, int64(fv * float64(gotime.Second)), nil case "MINUTE": dayCount := iv / (60 * 24) iv %= 60 * 24 - return 0, 0, dayCount, float64(iv * int64(gotime.Minute)), nil + return 0, 0, dayCount, iv * int64(gotime.Minute), nil case "HOUR": dayCount := iv / 24 iv %= 24 - return 0, 0, dayCount, float64(iv * int64(gotime.Hour)), nil + return 0, 0, dayCount, iv * int64(gotime.Hour), nil case "DAY": return 0, 0, iv, 0, nil case "WEEK": @@ -1654,10 +1654,11 @@ func extractSingleTimeValue(unit string, format string) (int64, int64, int64, fl return 0, 0, 0, 0, errors.Errorf("invalid singel timeunit - %s", unit) } -// extractTimeValue extracts years, months, days, microseconds from a string +// parseTimeValue gets years, months, days, nanoseconds from a string +// nanosecond will not exceed length of single day // MySQL permits any punctuation delimiter in the expr format. // See https://dev.mysql.com/doc/refman/8.0/en/expressions.html#temporal-intervals -func extractTimeValue(format string, index, cnt int) (int64, int64, int64, float64, error) { +func parseTimeValue(format string, index, cnt int) (int64, int64, int64, int64, error) { neg := false originalFmt := format format = strings.TrimSpace(format) @@ -1695,59 +1696,57 @@ func extractTimeValue(format string, index, cnt int) (int64, int64, int64, float return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(originalFmt) } - hours, err := strconv.ParseFloat(fields[HourIndex], 64) + hours, err := strconv.ParseInt(fields[HourIndex], 10, 64) if err != nil { return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(originalFmt) } - minutes, err := strconv.ParseFloat(fields[MinuteIndex], 64) + minutes, err := strconv.ParseInt(fields[MinuteIndex], 10, 64) if err != nil { return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(originalFmt) } - seconds, err := strconv.ParseFloat(fields[SecondIndex], 64) + seconds, err := strconv.ParseInt(fields[SecondIndex], 10, 64) if err != nil { return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(originalFmt) } - microseconds, err := strconv.ParseFloat(alignFrac(fields[MicrosecondIndex], MaxFsp), 64) + microseconds, err := strconv.ParseInt(alignFrac(fields[MicrosecondIndex], MaxFsp), 10, 64) if err != nil { return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(originalFmt) } - - secondDuration := int64(hours*3600 + minutes*60 + seconds) - days += secondDuration / (3600 * 24) - secondDuration %= 3600 * 24 - - durations := float64(secondDuration)*float64(gotime.Second) + microseconds*float64(gotime.Microsecond) - - return years, months, days, durations, nil + seconds = hours*3600 + minutes*60 + seconds + days += seconds / (3600 * 24) + seconds %= 3600 * 24 + return years, months, days, seconds*int64(gotime.Second) + microseconds*int64(gotime.Microsecond), nil } -// ExtractTimeValue extracts time value from time unit and format. -func ExtractTimeValue(unit string, format string) (int64, int64, int64, float64, error) { +// ParseDurationValue parses time value from time unit and format. +// Returns y years m months d days + n nanoseconds +// Nanoseconds will no longer than one day. +func ParseDurationValue(unit string, format string) (y int64, m int64, d int64, n int64, _ error) { switch strings.ToUpper(unit) { case "MICROSECOND", "SECOND", "MINUTE", "HOUR", "DAY", "WEEK", "MONTH", "QUARTER", "YEAR": - return extractSingleTimeValue(unit, format) + return parseSingleTimeValue(unit, format) case "SECOND_MICROSECOND": - return extractTimeValue(format, MicrosecondIndex, SecondMicrosecondMaxCnt) + return parseTimeValue(format, MicrosecondIndex, SecondMicrosecondMaxCnt) case "MINUTE_MICROSECOND": - return extractTimeValue(format, MicrosecondIndex, MinuteMicrosecondMaxCnt) + return parseTimeValue(format, MicrosecondIndex, MinuteMicrosecondMaxCnt) case "MINUTE_SECOND": - return extractTimeValue(format, SecondIndex, MinuteSecondMaxCnt) + return parseTimeValue(format, SecondIndex, MinuteSecondMaxCnt) case "HOUR_MICROSECOND": - return extractTimeValue(format, MicrosecondIndex, HourMicrosecondMaxCnt) + return parseTimeValue(format, MicrosecondIndex, HourMicrosecondMaxCnt) case "HOUR_SECOND": - return extractTimeValue(format, SecondIndex, HourSecondMaxCnt) + return parseTimeValue(format, SecondIndex, HourSecondMaxCnt) case "HOUR_MINUTE": - return extractTimeValue(format, MinuteIndex, HourMinuteMaxCnt) + return parseTimeValue(format, MinuteIndex, HourMinuteMaxCnt) case "DAY_MICROSECOND": - return extractTimeValue(format, MicrosecondIndex, DayMicrosecondMaxCnt) + return parseTimeValue(format, MicrosecondIndex, DayMicrosecondMaxCnt) case "DAY_SECOND": - return extractTimeValue(format, SecondIndex, DaySecondMaxCnt) + return parseTimeValue(format, SecondIndex, DaySecondMaxCnt) case "DAY_MINUTE": - return extractTimeValue(format, MinuteIndex, DayMinuteMaxCnt) + return parseTimeValue(format, MinuteIndex, DayMinuteMaxCnt) case "DAY_HOUR": - return extractTimeValue(format, HourIndex, DayHourMaxCnt) + return parseTimeValue(format, HourIndex, DayHourMaxCnt) case "YEAR_MONTH": - return extractTimeValue(format, MonthIndex, YearMonthMaxCnt) + return parseTimeValue(format, MonthIndex, YearMonthMaxCnt) default: return 0, 0, 0, 0, errors.Errorf("invalid singel timeunit - %s", unit) } diff --git a/types/time_test.go b/types/time_test.go index c23c0032204c6..0a461d6458cec 100644 --- a/types/time_test.go +++ b/types/time_test.go @@ -1245,37 +1245,37 @@ func (s *testTimeSuite) TestExtractDurationNum(c *C) { c.Assert(err, ErrorMatches, "invalid unit.*") } -func (s *testTimeSuite) TestExtractTimeValue(c *C) { +func (s *testTimeSuite) TestParseDurationValue(c *C) { tbl := []struct { format string unit string res1 int64 res2 int64 res3 int64 - res4 float64 + res4 int64 }{ {"52", "WEEK", 0, 0, 52 * 7, 0}, {"12", "DAY", 0, 0, 12, 0}, {"04", "MONTH", 0, 04, 0, 0}, {"1", "QUARTER", 0, 1 * 3, 0, 0}, {"2019", "YEAR", 2019, 0, 0, 0}, - {"10567890", "SECOND_MICROSECOND", 0, 0, 0, 1.056789e+10}, - {"10.567890", "SECOND_MICROSECOND", 0, 0, 0, 1.056789e+10}, - {"-10.567890", "SECOND_MICROSECOND", 0, 0, 0, -1.056789e+10}, - {"35:10567890", "MINUTE_SECOND", 0, 0, 0, 1.056999e+16}, - {"3510567890", "MINUTE_SECOND", 0, 0, 0, 3.51056789e+18}, - {"11:35:10.567890", "HOUR_MICROSECOND", 0, 0, 0, 4.171056789e+13}, - {"567890", "HOUR_MICROSECOND", 0, 0, 0, 5.6789e+08}, - {"14:00", "HOUR_MINUTE", 0, 0, 0, 5.04e+13}, - {"14", "HOUR_MINUTE", 0, 0, 0, 8.4e+11}, - {"12 14:00:00.345", "DAY_MICROSECOND", 0, 0, 12, 5.0400345e+13}, - {"12 14:00:00", "DAY_SECOND", 0, 0, 12, 5.04e+13}, - {"12 14:00", "DAY_MINUTE", 0, 0, 12, 5.04e+13}, - {"12 14", "DAY_HOUR", 0, 0, 12, 5.04e+13}, - {"1:1", "DAY_HOUR", 0, 0, 1, 3.6e+12}, - {"aa1bb1", "DAY_HOUR", 0, 0, 1, 3.6e+12}, - {"-1:1", "DAY_HOUR", 0, 0, -1, -3.6e+12}, - {"-aa1bb1", "DAY_HOUR", 0, 0, -1, -3.6e+12}, + {"10567890", "SECOND_MICROSECOND", 0, 0, 0, 10567890000}, + {"10.567890", "SECOND_MICROSECOND", 0, 0, 0, 10567890000}, + {"-10.567890", "SECOND_MICROSECOND", 0, 0, 0, -10567890000}, + {"35:10567890", "MINUTE_SECOND", 0, 0, 122, 29190000000000}, // 122 * 3600 * 24 + 29190 = 35 * 60 + 10567890 + {"3510567890", "MINUTE_SECOND", 0, 0, 40631, 49490000000000}, // 40631 * 3600 * 24 + 49490 = 3510567890 + {"11:35:10.567890", "HOUR_MICROSECOND", 0, 0, 0, 41710567890000}, // = (11 * 3600 + 35 * 60) * 1000000000 + 10567890000 + {"567890", "HOUR_MICROSECOND", 0, 0, 0, 567890000}, + {"14:00", "HOUR_MINUTE", 0, 0, 0, 50400000000000}, + {"14", "HOUR_MINUTE", 0, 0, 0, 840000000000}, + {"12 14:00:00.345", "DAY_MICROSECOND", 0, 0, 12, 50400345000000}, + {"12 14:00:00", "DAY_SECOND", 0, 0, 12, 50400000000000}, + {"12 14:00", "DAY_MINUTE", 0, 0, 12, 50400000000000}, + {"12 14", "DAY_HOUR", 0, 0, 12, 50400000000000}, + {"1:1", "DAY_HOUR", 0, 0, 1, 3600000000000}, + {"aa1bb1", "DAY_HOUR", 0, 0, 1, 3600000000000}, + {"-1:1", "DAY_HOUR", 0, 0, -1, -3600000000000}, + {"-aa1bb1", "DAY_HOUR", 0, 0, -1, -3600000000000}, {"2019-12", "YEAR_MONTH", 2019, 12, 0, 0}, {"1 1", "YEAR_MONTH", 1, 1, 0, 0}, {"aa1bb1", "YEAR_MONTH", 1, 1, 0, 0}, @@ -1284,12 +1284,13 @@ func (s *testTimeSuite) TestExtractTimeValue(c *C) { {" \t\n\r\n - aa1bb1 \t\n ", "YEAR_MONTH", -1, -1, 0, 0}, } for _, col := range tbl { - res1, res2, res3, res4, err := types.ExtractTimeValue(col.unit, col.format) - c.Assert(res1, Equals, col.res1) - c.Assert(res2, Equals, col.res2) - c.Assert(res3, Equals, col.res3) - c.Assert(res4, Equals, col.res4) - c.Assert(err, IsNil) + comment := Commentf("Extract %v Unit %v", col.format, col.unit) + res1, res2, res3, res4, err := types.ParseDurationValue(col.unit, col.format) + c.Assert(res1, Equals, col.res1, comment) + c.Assert(res2, Equals, col.res2, comment) + c.Assert(res3, Equals, col.res3, comment) + c.Assert(res4, Equals, col.res4, comment) + c.Assert(err, IsNil, comment) } } From b100b0afb0b6668102339609b5853b88f90c293e Mon Sep 17 00:00:00 2001 From: Guo Song Date: Tue, 30 Apr 2019 20:27:54 +0800 Subject: [PATCH 04/17] use integration test --- expression/builtin_time_test.go | 56 --------------------------------- expression/integration_test.go | 43 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 56 deletions(-) diff --git a/expression/builtin_time_test.go b/expression/builtin_time_test.go index 7d6205a4c1b2b..dda5dbfc9d100 100644 --- a/expression/builtin_time_test.go +++ b/expression/builtin_time_test.go @@ -2504,62 +2504,6 @@ func (s *testEvaluatorSuite) TestWithTimeZone(c *C) { } } -// Tests #9727, which is caused by overflow -func (s *testEvaluatorSuite) TestDatetimeLargeDuration(c *C) { - fcAdd := funcs[ast.DateAdd] - fcSub := funcs[ast.DateSub] - - testsAdd := []struct { - fc functionClass - inputDate interface{} - duration interface{} - durationUnit string - expect interface{} - }{ - {fcAdd, "1900-01-01 00:00:00", "100000000:214748364700", "MINUTE_SECOND", "8895-03-27 22:11:40"}, - {fcAdd, "1900-01-01 00:00:00", int64(1) << 37, "SECOND", "6255-04-08 15:04:32"}, - {fcAdd, "1900-01-01 00:00:00", int64(1) << 31, "MINUTE", "5983-01-24 02:08:00"}, - {fcAdd, "1900-01-01 00:00:00", int64(1) << 38, "SECOND", nil}, - {fcAdd, "1900-01-01 00:00:00", int64(1) << 33, "MINUTE", nil}, - {fcAdd, "1900-01-01 00:00:00", int64(1) << 30, "HOUR", nil}, - {fcAdd, "1900-01-01 00:00:00", "1000000000:214748364700", "MINUTE_SECOND", nil}, - {fcAdd, 19000101000000, "100000000:214748364700", "MINUTE_SECOND", "8895-03-27 22:11:40"}, - {fcAdd, 19000101000000, int64(1) << 37, "SECOND", "6255-04-08 15:04:32"}, - {fcAdd, 19000101000000, int64(1) << 31, "MINUTE", "5983-01-24 02:08:00"}, - {fcAdd, "1900-01-01 00:00:00", 1.123456789e3, "SECOND", "1900-01-01 00:18:43.456789"}, - {fcAdd, 19000101000000, 1.123456789e3, "SECOND", "1900-01-01 00:18:43.456789"}, - - {fcSub, "8895-03-27 22:11:40", "100000000:214748364700", "MINUTE_SECOND", "1900-01-01 00:00:00"}, - {fcSub, "6255-04-08 15:04:32", int64(1) << 37, "SECOND", "1900-01-01 00:00:00"}, - {fcSub, "5983-01-24 02:08:00", int64(1) << 31, "MINUTE", "1900-01-01 00:00:00"}, - {fcSub, "9999-01-01 00:00:00", int64(1) << 39, "SECOND", nil}, - {fcSub, "9999-01-01 00:00:00", int64(1) << 33, "MINUTE", nil}, - {fcSub, "9999-01-01 00:00:00", int64(1) << 30, "HOUR", nil}, - {fcSub, "9999-01-01 00:00:00", "10000000000:214748364700", "MINUTE_SECOND", nil}, - {fcSub, 88950327221140, "100000000:214748364700", "MINUTE_SECOND", "1900-01-01 00:00:00"}, - {fcSub, 62550408150432, int64(1) << 37, "SECOND", "1900-01-01 00:00:00"}, - {fcSub, 59830124020800, int64(1) << 31, "MINUTE", "1900-01-01 00:00:00"}, - {fcSub, "1900-01-01 00:18:43.456789", 1.123456789e3, "SECOND", "1900-01-01 00:00:00"}, - {fcSub, types.NewDecFromStringForTest("19000101001843.456789"), 1.123456789e3, "SECOND", "1900-01-01 00:00:00"}, - } - - for _, test := range testsAdd { - args := types.MakeDatums(test.inputDate, test.duration, test.durationUnit) - f, err := test.fc.getFunction(s.ctx, s.datumsToConstants(args)) - c.Assert(err, IsNil) - c.Assert(f, NotNil) - v, err := evalBuiltinFunc(f, chunk.Row{}) - c.Assert(err, IsNil) - comment := Commentf("InputDate: %v Duration: %v DurationUnit: %v Expected: %v", test.inputDate, test.duration, test.durationUnit, test.expect) - if test.expect != nil { - c.Assert(v.IsNull(), IsFalse, comment) - c.Assert(v.GetMysqlTime().String(), Equals, test.expect, comment) - } else { - c.Assert(v.IsNull(), IsTrue, comment) - } - } -} - func (s *testEvaluatorSuite) TestTidbParseTso(c *C) { s.ctx.GetSessionVars().TimeZone = time.UTC tests := []struct { diff --git a/expression/integration_test.go b/expression/integration_test.go index 33b8f5a8076da..f695f635c2c94 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -4235,3 +4235,46 @@ where datediff(b.date8, date(from_unixtime(a.starttime))) >= 0` tk.MustQuery(q) } + +func (s *testIntegrationSuite) TestIssue10329(c *C) { + tk := testkit.NewTestKit(c, s.store) + defer s.cleanEnv(c) + + cases := []struct { + sql string + result string + }{ + {`SELECT "1900-01-01 00:00:00" + INTERVAL "100000000:214748364700" MINUTE_SECOND ;`, "8895-03-27 22:11:40"}, + {`SELECT "1900-01-01 00:00:00" + INTERVAL 1 << 37 SECOND ;`, "6255-04-08 15:04:32"}, + {`SELECT "1900-01-01 00:00:00" + INTERVAL 1 << 31 MINUTE;`, "5983-01-24 02:08:00"}, + {`SELECT "1900-01-01 00:00:00" + INTERVAL 1 << 38 SECOND ;`, ""}, + {`SELECT "1900-01-01 00:00:00" + INTERVAL 1 << 33 MINUTE;`, ""}, + {`SELECT "1900-01-01 00:00:00" + INTERVAL 1 << 30 HOUR;`, ""}, + {`SELECT "1900-01-01 00:00:00" + INTERVAL "1000000000:214748364700" MINUTE_SECOND ;`, ""}, + {`SELECT 19000101000000 + INTERVAL "100000000:214748364700" MINUTE_SECOND ;`, "8895-03-27 22:11:40"}, + {`SELECT 19000101000000 + INTERVAL 1 << 37 SECOND ;`, "6255-04-08 15:04:32"}, + {`SELECT 19000101000000 + INTERVAL 1 << 31 MINUTE;`, "5983-01-24 02:08:00"}, + {`SELECT "1900-01-01 00:00:00" + INTERVAL 1.123456789e3 SECOND ;`, "1900-01-01 00:18:43.456789"}, + {`SELECT 19000101000000 + INTERVAL 1.123456789e3 SECOND ;`, "1900-01-01 00:18:43.456789"}, + + {`SELECT "8895-03-27 22:11:40" - INTERVAL "100000000:214748364700" MINUTE_SECOND ;`, "1900-01-01 00:00:00"}, + {`SELECT "6255-04-08 15:04:32" - INTERVAL 1 << 37 SECOND ;`, "1900-01-01 00:00:00"}, + {`SELECT "5983-01-24 02:08:00" - INTERVAL 1 << 31 MINUTE;`, "1900-01-01 00:00:00"}, + {`SELECT "9999-01-01 00:00:00" - INTERVAL 1 << 39 SECOND ;`, ""}, + {`SELECT "9999-01-01 00:00:00" - INTERVAL 1 << 33 MINUTE;`, ""}, + {`SELECT "9999-01-01 00:00:00" - INTERVAL 1 << 30 HOUR;`, ""}, + {`SELECT "9999-01-01 00:00:00" - INTERVAL "10000000000:214748364700" MINUTE_SECOND ;`, ""}, + {`SELECT 88950327221140 - INTERVAL "100000000:214748364700" MINUTE_SECOND ;`, "1900-01-01 00:00:00"}, + {`SELECT 62550408150432 - INTERVAL 1 << 37 SECOND ;`, "1900-01-01 00:00:00"}, + {`SELECT 59830124020800 - INTERVAL 1 << 31 MINUTE;`, "1900-01-01 00:00:00"}, + {`SELECT "1900-01-01 00:18:43.456789" - INTERVAL 1.123456789e3 SECOND ;`, "1900-01-01 00:00:00"}, + {`SELECT 19000101001843.456789 - INTERVAL 1.123456789e3 SECOND ;`, "1900-01-01 00:00:00"}, + + {`select date("1900-01-01") - interval 1.123456789e3 second;`, "1899-12-31 23:41:16.543211"}, + {`select 19000101001843 - interval 1.123e3 second;`, "1900-01-01 00:00:00"}, + } + + for _, c := range cases { + tk.MustQuery(c.sql).Check(testkit.Rows(c.result)) + } +} From 65de3977fe6e82008e969ef048b9dbc29c360c99 Mon Sep 17 00:00:00 2001 From: Guo Song Date: Sun, 5 May 2019 09:34:05 +0800 Subject: [PATCH 05/17] improve coverage --- expression/integration_test.go | 54 +++++++++++++++++----------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/expression/integration_test.go b/expression/integration_test.go index f695f635c2c94..c641276a503bb 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -4236,7 +4236,7 @@ where tk.MustQuery(q) } -func (s *testIntegrationSuite) TestIssue10329(c *C) { +func (s *testIntegrationSuite) TestIssue9727(c *C) { tk := testkit.NewTestKit(c, s.store) defer s.cleanEnv(c) @@ -4244,34 +4244,34 @@ func (s *testIntegrationSuite) TestIssue10329(c *C) { sql string result string }{ - {`SELECT "1900-01-01 00:00:00" + INTERVAL "100000000:214748364700" MINUTE_SECOND ;`, "8895-03-27 22:11:40"}, - {`SELECT "1900-01-01 00:00:00" + INTERVAL 1 << 37 SECOND ;`, "6255-04-08 15:04:32"}, - {`SELECT "1900-01-01 00:00:00" + INTERVAL 1 << 31 MINUTE;`, "5983-01-24 02:08:00"}, - {`SELECT "1900-01-01 00:00:00" + INTERVAL 1 << 38 SECOND ;`, ""}, - {`SELECT "1900-01-01 00:00:00" + INTERVAL 1 << 33 MINUTE;`, ""}, - {`SELECT "1900-01-01 00:00:00" + INTERVAL 1 << 30 HOUR;`, ""}, - {`SELECT "1900-01-01 00:00:00" + INTERVAL "1000000000:214748364700" MINUTE_SECOND ;`, ""}, - {`SELECT 19000101000000 + INTERVAL "100000000:214748364700" MINUTE_SECOND ;`, "8895-03-27 22:11:40"}, - {`SELECT 19000101000000 + INTERVAL 1 << 37 SECOND ;`, "6255-04-08 15:04:32"}, - {`SELECT 19000101000000 + INTERVAL 1 << 31 MINUTE;`, "5983-01-24 02:08:00"}, - {`SELECT "1900-01-01 00:00:00" + INTERVAL 1.123456789e3 SECOND ;`, "1900-01-01 00:18:43.456789"}, - {`SELECT 19000101000000 + INTERVAL 1.123456789e3 SECOND ;`, "1900-01-01 00:18:43.456789"}, - - {`SELECT "8895-03-27 22:11:40" - INTERVAL "100000000:214748364700" MINUTE_SECOND ;`, "1900-01-01 00:00:00"}, - {`SELECT "6255-04-08 15:04:32" - INTERVAL 1 << 37 SECOND ;`, "1900-01-01 00:00:00"}, - {`SELECT "5983-01-24 02:08:00" - INTERVAL 1 << 31 MINUTE;`, "1900-01-01 00:00:00"}, - {`SELECT "9999-01-01 00:00:00" - INTERVAL 1 << 39 SECOND ;`, ""}, - {`SELECT "9999-01-01 00:00:00" - INTERVAL 1 << 33 MINUTE;`, ""}, - {`SELECT "9999-01-01 00:00:00" - INTERVAL 1 << 30 HOUR;`, ""}, - {`SELECT "9999-01-01 00:00:00" - INTERVAL "10000000000:214748364700" MINUTE_SECOND ;`, ""}, + {`SELECT "1900-01-01 00:00:00" + INTERVAL "100000000:214748364700" MINUTE_SECOND;`, "8895-03-27 22:11:40"}, + {`SELECT "1900-01-01 00:00:00" + INTERVAL 1 << 37 SECOND;`, "6255-04-08 15:04:32"}, + {`SELECT "1900-01-01 00:00:00" + INTERVAL 1 << 31 MINUTE;`, "5983-01-24 02:08:00"}, + {`SELECT "1900-01-01 00:00:00" + INTERVAL 1 << 38 SECOND;`, ""}, + {`SELECT "1900-01-01 00:00:00" + INTERVAL 1 << 33 MINUTE;`, ""}, + {`SELECT "1900-01-01 00:00:00" + INTERVAL 1 << 30 HOUR;`, ""}, + {`SELECT "1900-01-01 00:00:00" + INTERVAL "1000000000:214748364700" MINUTE_SECOND;`, ""}, + {`SELECT 19000101000000 + INTERVAL "100000000:214748364700" MINUTE_SECOND;`, "8895-03-27 22:11:40"}, + {`SELECT 19000101000000 + INTERVAL 1 << 37 SECOND;`, "6255-04-08 15:04:32"}, + {`SELECT 19000101000000 + INTERVAL 1 << 31 MINUTE;`, "5983-01-24 02:08:00"}, + {`SELECT "1900-01-01 00:00:00" + INTERVAL 1.123456789e3 SECOND;`, "1900-01-01 00:18:43.456789"}, + {`SELECT 19000101000000 + INTERVAL 1.123456789e3 SECOND;`, "1900-01-01 00:18:43.456789"}, + {`select date("1900-01-01") + interval 1.123456789e3 second;`, "1900-01-01 00:18:43.456789"}, + + {`SELECT "8895-03-27 22:11:40" - INTERVAL "100000000:214748364700" MINUTE_SECOND;`, "1900-01-01 00:00:00"}, + {`SELECT "6255-04-08 15:04:32" - INTERVAL 1 << 37 SECOND;`, "1900-01-01 00:00:00"}, + {`SELECT "5983-01-24 02:08:00" - INTERVAL 1 << 31 MINUTE;`, "1900-01-01 00:00:00"}, + {`SELECT "9999-01-01 00:00:00" - INTERVAL 1 << 39 SECOND;`, ""}, + {`SELECT "9999-01-01 00:00:00" - INTERVAL 1 << 33 MINUTE;`, ""}, + {`SELECT "9999-01-01 00:00:00" - INTERVAL 1 << 30 HOUR;`, ""}, + {`SELECT "9999-01-01 00:00:00" - INTERVAL "10000000000:214748364700" MINUTE_SECOND;`, ""}, {`SELECT 88950327221140 - INTERVAL "100000000:214748364700" MINUTE_SECOND ;`, "1900-01-01 00:00:00"}, - {`SELECT 62550408150432 - INTERVAL 1 << 37 SECOND ;`, "1900-01-01 00:00:00"}, - {`SELECT 59830124020800 - INTERVAL 1 << 31 MINUTE;`, "1900-01-01 00:00:00"}, - {`SELECT "1900-01-01 00:18:43.456789" - INTERVAL 1.123456789e3 SECOND ;`, "1900-01-01 00:00:00"}, - {`SELECT 19000101001843.456789 - INTERVAL 1.123456789e3 SECOND ;`, "1900-01-01 00:00:00"}, - + {`SELECT 62550408150432 - INTERVAL 1 << 37 SECOND;`, "1900-01-01 00:00:00"}, + {`SELECT 59830124020800 - INTERVAL 1 << 31 MINUTE;`, "1900-01-01 00:00:00"}, + {`SELECT "1900-01-01 00:18:43.456789" - INTERVAL 1.123456789e3 SECOND;`, "1900-01-01 00:00:00"}, + {`SELECT 19000101001843.456789 - INTERVAL 1.123456789e3 SECOND;`, "1900-01-01 00:00:00"}, {`select date("1900-01-01") - interval 1.123456789e3 second;`, "1899-12-31 23:41:16.543211"}, - {`select 19000101001843 - interval 1.123e3 second;`, "1900-01-01 00:00:00"}, + {`select 19000101000000 - interval 1.123456789e3 second;`, "1899-12-31 23:41:16.543211"}, } for _, c := range cases { From b583a6268c0c60c1410ebbd5e8a00b6f92b8e562 Mon Sep 17 00:00:00 2001 From: Guo Song Date: Sun, 5 May 2019 14:31:31 +0800 Subject: [PATCH 06/17] simplify pr --- expression/builtin_time.go | 246 +-------------------------------- expression/integration_test.go | 7 - 2 files changed, 2 insertions(+), 251 deletions(-) diff --git a/expression/builtin_time.go b/expression/builtin_time.go index 339ac918c7ce5..10b36679dabdf 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -217,27 +217,21 @@ var ( _ builtinFunc = &builtinExtractDurationSig{} _ builtinFunc = &builtinAddDateStringStringSig{} _ builtinFunc = &builtinAddDateStringIntSig{} - _ builtinFunc = &builtinAddDateStringRealSig{} _ builtinFunc = &builtinAddDateStringDecimalSig{} _ builtinFunc = &builtinAddDateIntStringSig{} _ builtinFunc = &builtinAddDateIntIntSig{} - _ builtinFunc = &builtinAddDateIntRealSig{} _ builtinFunc = &builtinAddDateIntDecimalSig{} _ builtinFunc = &builtinAddDateDatetimeStringSig{} _ builtinFunc = &builtinAddDateDatetimeIntSig{} - _ builtinFunc = &builtinAddDateDatetimeRealSig{} _ builtinFunc = &builtinAddDateDatetimeDecimalSig{} _ builtinFunc = &builtinSubDateStringStringSig{} _ builtinFunc = &builtinSubDateStringIntSig{} - _ builtinFunc = &builtinSubDateStringRealSig{} _ builtinFunc = &builtinSubDateStringDecimalSig{} _ builtinFunc = &builtinSubDateIntStringSig{} _ builtinFunc = &builtinSubDateIntIntSig{} - _ builtinFunc = &builtinSubDateIntRealSig{} _ builtinFunc = &builtinSubDateIntDecimalSig{} _ builtinFunc = &builtinSubDateDatetimeStringSig{} _ builtinFunc = &builtinSubDateDatetimeIntSig{} - _ builtinFunc = &builtinSubDateDatetimeRealSig{} _ builtinFunc = &builtinSubDateDatetimeDecimalSig{} ) @@ -2709,14 +2703,6 @@ func (du *baseDateArithmitical) getIntervalFromInt(ctx sessionctx.Context, args return strconv.FormatInt(interval, 10), false, nil } -func (du *baseDateArithmitical) getIntervalFromReal(ctx sessionctx.Context, args []Expression, row chunk.Row, unit string) (string, bool, error) { - interval, isNull, err := args[1].EvalReal(ctx, row) - if isNull || err != nil { - return "", true, err - } - return strconv.FormatFloat(interval, 'f', -1, 64), false, nil -} - func (du *baseDateArithmitical) add(ctx sessionctx.Context, date types.Time, interval string, unit string) (types.Time, bool, error) { year, month, day, nano, err := types.ParseDurationValue(unit, interval) if err != nil { @@ -2796,7 +2782,7 @@ func (c *addDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres } intervalEvalTp := args[1].GetType().EvalType() - if intervalEvalTp != types.ETString && intervalEvalTp != types.ETDecimal && intervalEvalTp != types.ETReal { + if intervalEvalTp != types.ETString && intervalEvalTp != types.ETDecimal { intervalEvalTp = types.ETInt } @@ -2815,11 +2801,6 @@ func (c *addDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres baseBuiltinFunc: bf, baseDateArithmitical: newDateArighmeticalUtil(), } - case dateEvalTp == types.ETString && intervalEvalTp == types.ETReal: - sig = &builtinAddDateStringRealSig{ - baseBuiltinFunc: bf, - baseDateArithmitical: newDateArighmeticalUtil(), - } case dateEvalTp == types.ETString && intervalEvalTp == types.ETDecimal: sig = &builtinAddDateStringDecimalSig{ baseBuiltinFunc: bf, @@ -2835,11 +2816,6 @@ func (c *addDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres baseBuiltinFunc: bf, baseDateArithmitical: newDateArighmeticalUtil(), } - case dateEvalTp == types.ETInt && intervalEvalTp == types.ETReal: - sig = &builtinAddDateIntRealSig{ - baseBuiltinFunc: bf, - baseDateArithmitical: newDateArighmeticalUtil(), - } case dateEvalTp == types.ETInt && intervalEvalTp == types.ETDecimal: sig = &builtinAddDateIntDecimalSig{ baseBuiltinFunc: bf, @@ -2855,11 +2831,6 @@ func (c *addDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres baseBuiltinFunc: bf, baseDateArithmitical: newDateArighmeticalUtil(), } - case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETReal: - sig = &builtinAddDateDatetimeRealSig{ - baseBuiltinFunc: bf, - baseDateArithmitical: newDateArighmeticalUtil(), - } case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETDecimal: sig = &builtinAddDateDatetimeDecimalSig{ baseBuiltinFunc: bf, @@ -2935,39 +2906,6 @@ func (b *builtinAddDateStringIntSig) evalTime(row chunk.Row) (types.Time, bool, return result, isNull || err != nil, err } -type builtinAddDateStringRealSig struct { - baseBuiltinFunc - baseDateArithmitical -} - -func (b *builtinAddDateStringRealSig) Clone() builtinFunc { - newSig := &builtinAddDateStringRealSig{baseDateArithmitical: b.baseDateArithmitical} - newSig.cloneFrom(&b.baseBuiltinFunc) - return newSig -} - -// evalTime evals ADDDATE(date,INTERVAL expr unit). -// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate -func (b *builtinAddDateStringRealSig) evalTime(row chunk.Row) (types.Time, bool, error) { - unit, isNull, err := b.args[2].EvalString(b.ctx, row) - if isNull || err != nil { - return types.Time{}, true, err - } - - date, isNull, err := b.getDateFromString(b.ctx, b.args, row, unit) - if isNull || err != nil { - return types.Time{}, true, err - } - - interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit) - if isNull || err != nil { - return types.Time{}, true, err - } - - result, isNull, err := b.add(b.ctx, date, interval, unit) - return result, isNull || err != nil, err -} - type builtinAddDateStringDecimalSig struct { baseBuiltinFunc baseDateArithmitical @@ -3067,39 +3005,6 @@ func (b *builtinAddDateIntIntSig) evalTime(row chunk.Row) (types.Time, bool, err return result, isNull || err != nil, err } -type builtinAddDateIntRealSig struct { - baseBuiltinFunc - baseDateArithmitical -} - -func (b *builtinAddDateIntRealSig) Clone() builtinFunc { - newSig := &builtinAddDateIntRealSig{baseDateArithmitical: b.baseDateArithmitical} - newSig.cloneFrom(&b.baseBuiltinFunc) - return newSig -} - -// evalTime evals ADDDATE(date,INTERVAL expr unit). -// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate -func (b *builtinAddDateIntRealSig) evalTime(row chunk.Row) (types.Time, bool, error) { - unit, isNull, err := b.args[2].EvalString(b.ctx, row) - if isNull || err != nil { - return types.Time{}, true, err - } - - date, isNull, err := b.getDateFromInt(b.ctx, b.args, row, unit) - if isNull || err != nil { - return types.Time{}, true, err - } - - interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit) - if isNull || err != nil { - return types.Time{}, true, err - } - - result, isNull, err := b.add(b.ctx, date, interval, unit) - return result, isNull || err != nil, err -} - type builtinAddDateIntDecimalSig struct { baseBuiltinFunc baseDateArithmitical @@ -3199,39 +3104,6 @@ func (b *builtinAddDateDatetimeIntSig) evalTime(row chunk.Row) (types.Time, bool return result, isNull || err != nil, err } -type builtinAddDateDatetimeRealSig struct { - baseBuiltinFunc - baseDateArithmitical -} - -func (b *builtinAddDateDatetimeRealSig) Clone() builtinFunc { - newSig := &builtinAddDateDatetimeRealSig{baseDateArithmitical: b.baseDateArithmitical} - newSig.cloneFrom(&b.baseBuiltinFunc) - return newSig -} - -// evalTime evals ADDDATE(date,INTERVAL expr unit). -// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate -func (b *builtinAddDateDatetimeRealSig) evalTime(row chunk.Row) (types.Time, bool, error) { - unit, isNull, err := b.args[2].EvalString(b.ctx, row) - if isNull || err != nil { - return types.Time{}, true, err - } - - date, isNull, err := b.getDateFromDatetime(b.ctx, b.args, row, unit) - if isNull || err != nil { - return types.Time{}, true, err - } - - interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit) - if isNull || err != nil { - return types.Time{}, true, err - } - - result, isNull, err := b.add(b.ctx, date, interval, unit) - return result, isNull || err != nil, err -} - type builtinAddDateDatetimeDecimalSig struct { baseBuiltinFunc baseDateArithmitical @@ -3280,7 +3152,7 @@ func (c *subDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres } intervalEvalTp := args[1].GetType().EvalType() - if intervalEvalTp != types.ETString && intervalEvalTp != types.ETDecimal && intervalEvalTp != types.ETReal { + if intervalEvalTp != types.ETString && intervalEvalTp != types.ETDecimal { intervalEvalTp = types.ETInt } @@ -3299,11 +3171,6 @@ func (c *subDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres baseBuiltinFunc: bf, baseDateArithmitical: newDateArighmeticalUtil(), } - case dateEvalTp == types.ETString && intervalEvalTp == types.ETReal: - sig = &builtinSubDateStringRealSig{ - baseBuiltinFunc: bf, - baseDateArithmitical: newDateArighmeticalUtil(), - } case dateEvalTp == types.ETString && intervalEvalTp == types.ETDecimal: sig = &builtinSubDateStringDecimalSig{ baseBuiltinFunc: bf, @@ -3319,11 +3186,6 @@ func (c *subDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres baseBuiltinFunc: bf, baseDateArithmitical: newDateArighmeticalUtil(), } - case dateEvalTp == types.ETInt && intervalEvalTp == types.ETReal: - sig = &builtinSubDateIntRealSig{ - baseBuiltinFunc: bf, - baseDateArithmitical: newDateArighmeticalUtil(), - } case dateEvalTp == types.ETInt && intervalEvalTp == types.ETDecimal: sig = &builtinSubDateIntDecimalSig{ baseBuiltinFunc: bf, @@ -3339,11 +3201,6 @@ func (c *subDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres baseBuiltinFunc: bf, baseDateArithmitical: newDateArighmeticalUtil(), } - case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETReal: - sig = &builtinSubDateDatetimeRealSig{ - baseBuiltinFunc: bf, - baseDateArithmitical: newDateArighmeticalUtil(), - } case dateEvalTp == types.ETDatetime && intervalEvalTp == types.ETDecimal: sig = &builtinSubDateDatetimeDecimalSig{ baseBuiltinFunc: bf, @@ -3419,39 +3276,6 @@ func (b *builtinSubDateStringIntSig) evalTime(row chunk.Row) (types.Time, bool, return result, isNull || err != nil, err } -type builtinSubDateStringRealSig struct { - baseBuiltinFunc - baseDateArithmitical -} - -func (b *builtinSubDateStringRealSig) Clone() builtinFunc { - newSig := &builtinSubDateStringRealSig{baseDateArithmitical: b.baseDateArithmitical} - newSig.cloneFrom(&b.baseBuiltinFunc) - return newSig -} - -// evalTime evals SUBDATE(date,INTERVAL expr unit). -// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate -func (b *builtinSubDateStringRealSig) evalTime(row chunk.Row) (types.Time, bool, error) { - unit, isNull, err := b.args[2].EvalString(b.ctx, row) - if isNull || err != nil { - return types.Time{}, true, err - } - - date, isNull, err := b.getDateFromString(b.ctx, b.args, row, unit) - if isNull || err != nil { - return types.Time{}, true, err - } - - interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit) - if isNull || err != nil { - return types.Time{}, true, err - } - - result, isNull, err := b.sub(b.ctx, date, interval, unit) - return result, isNull || err != nil, err -} - type builtinSubDateStringDecimalSig struct { baseBuiltinFunc baseDateArithmitical @@ -3549,39 +3373,6 @@ func (b *builtinSubDateIntIntSig) evalTime(row chunk.Row) (types.Time, bool, err return result, isNull || err != nil, err } -type builtinSubDateIntRealSig struct { - baseBuiltinFunc - baseDateArithmitical -} - -func (b *builtinSubDateIntRealSig) Clone() builtinFunc { - newSig := &builtinSubDateIntRealSig{baseDateArithmitical: b.baseDateArithmitical} - newSig.cloneFrom(&b.baseBuiltinFunc) - return newSig -} - -// evalTime evals SUBDATE(date,INTERVAL expr unit). -// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate -func (b *builtinSubDateIntRealSig) evalTime(row chunk.Row) (types.Time, bool, error) { - unit, isNull, err := b.args[2].EvalString(b.ctx, row) - if isNull || err != nil { - return types.Time{}, true, err - } - - date, isNull, err := b.getDateFromInt(b.ctx, b.args, row, unit) - if isNull || err != nil { - return types.Time{}, true, err - } - - interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit) - if isNull || err != nil { - return types.Time{}, true, err - } - - result, isNull, err := b.sub(b.ctx, date, interval, unit) - return result, isNull || err != nil, err -} - type builtinSubDateDatetimeStringSig struct { baseBuiltinFunc baseDateArithmitical @@ -3681,39 +3472,6 @@ func (b *builtinSubDateDatetimeIntSig) evalTime(row chunk.Row) (types.Time, bool return result, isNull || err != nil, err } -type builtinSubDateDatetimeRealSig struct { - baseBuiltinFunc - baseDateArithmitical -} - -func (b *builtinSubDateDatetimeRealSig) Clone() builtinFunc { - newSig := &builtinSubDateDatetimeRealSig{baseDateArithmitical: b.baseDateArithmitical} - newSig.cloneFrom(&b.baseBuiltinFunc) - return newSig -} - -// evalTime evals SUBDATE(date,INTERVAL expr unit). -// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate -func (b *builtinSubDateDatetimeRealSig) evalTime(row chunk.Row) (types.Time, bool, error) { - unit, isNull, err := b.args[2].EvalString(b.ctx, row) - if isNull || err != nil { - return types.Time{}, true, err - } - - date, isNull, err := b.getDateFromDatetime(b.ctx, b.args, row, unit) - if isNull || err != nil { - return types.Time{}, true, err - } - - interval, isNull, err := b.getIntervalFromReal(b.ctx, b.args, row, unit) - if isNull || err != nil { - return types.Time{}, true, err - } - - result, isNull, err := b.sub(b.ctx, date, interval, unit) - return result, isNull || err != nil, err -} - type builtinSubDateDatetimeDecimalSig struct { baseBuiltinFunc baseDateArithmitical diff --git a/expression/integration_test.go b/expression/integration_test.go index c641276a503bb..befd35d07ce13 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -4254,9 +4254,6 @@ func (s *testIntegrationSuite) TestIssue9727(c *C) { {`SELECT 19000101000000 + INTERVAL "100000000:214748364700" MINUTE_SECOND;`, "8895-03-27 22:11:40"}, {`SELECT 19000101000000 + INTERVAL 1 << 37 SECOND;`, "6255-04-08 15:04:32"}, {`SELECT 19000101000000 + INTERVAL 1 << 31 MINUTE;`, "5983-01-24 02:08:00"}, - {`SELECT "1900-01-01 00:00:00" + INTERVAL 1.123456789e3 SECOND;`, "1900-01-01 00:18:43.456789"}, - {`SELECT 19000101000000 + INTERVAL 1.123456789e3 SECOND;`, "1900-01-01 00:18:43.456789"}, - {`select date("1900-01-01") + interval 1.123456789e3 second;`, "1900-01-01 00:18:43.456789"}, {`SELECT "8895-03-27 22:11:40" - INTERVAL "100000000:214748364700" MINUTE_SECOND;`, "1900-01-01 00:00:00"}, {`SELECT "6255-04-08 15:04:32" - INTERVAL 1 << 37 SECOND;`, "1900-01-01 00:00:00"}, @@ -4268,10 +4265,6 @@ func (s *testIntegrationSuite) TestIssue9727(c *C) { {`SELECT 88950327221140 - INTERVAL "100000000:214748364700" MINUTE_SECOND ;`, "1900-01-01 00:00:00"}, {`SELECT 62550408150432 - INTERVAL 1 << 37 SECOND;`, "1900-01-01 00:00:00"}, {`SELECT 59830124020800 - INTERVAL 1 << 31 MINUTE;`, "1900-01-01 00:00:00"}, - {`SELECT "1900-01-01 00:18:43.456789" - INTERVAL 1.123456789e3 SECOND;`, "1900-01-01 00:00:00"}, - {`SELECT 19000101001843.456789 - INTERVAL 1.123456789e3 SECOND;`, "1900-01-01 00:00:00"}, - {`select date("1900-01-01") - interval 1.123456789e3 second;`, "1899-12-31 23:41:16.543211"}, - {`select 19000101000000 - interval 1.123456789e3 second;`, "1899-12-31 23:41:16.543211"}, } for _, c := range cases { From d22eca1e9bf2730670d95a332f594c0d66ddec3a Mon Sep 17 00:00:00 2001 From: Guo Song Date: Sun, 5 May 2019 20:27:12 +0800 Subject: [PATCH 07/17] avoid float number for precision --- expression/integration_test.go | 6 ++++++ types/time.go | 30 ++++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/expression/integration_test.go b/expression/integration_test.go index be134598bf68c..57ec48182702e 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -4265,6 +4265,12 @@ func (s *testIntegrationSuite) TestIssue9727(c *C) { {`SELECT 88950327221140 - INTERVAL "100000000:214748364700" MINUTE_SECOND ;`, "1900-01-01 00:00:00"}, {`SELECT 62550408150432 - INTERVAL 1 << 37 SECOND;`, "1900-01-01 00:00:00"}, {`SELECT 59830124020800 - INTERVAL 1 << 31 MINUTE;`, "1900-01-01 00:00:00"}, + + {`SELECT 10000101000000 + INTERVAL "111111111111111111" MICROSECOND;`, `4520-12-21 05:31:51.111111`}, + {`SELECT 10000101000000 + INTERVAL "111111111111.111111" SECOND;`, `4520-12-21 05:31:51.111111`}, + {`SELECT 10000101000000 + INTERVAL "111111111111.111111111" SECOND;`, `4520-12-21 05:31:51.111111`}, + {`SELECT 10000101000000 + INTERVAL "111111111111.111" SECOND;`, `4520-12-21 05:31:51.111000`}, + {`SELECT 10000101000000 + INTERVAL "111111111111." SECOND;`, `4520-12-21 05:31:51`}, } for _, c := range cases { diff --git a/types/time.go b/types/time.go index d1cc697e5d456..a46f627f334a5 100644 --- a/types/time.go +++ b/types/time.go @@ -1616,21 +1616,39 @@ func ExtractDurationNum(d *Duration, unit string) (int64, error) { } func parseSingleTimeValue(unit string, format string) (int64, int64, int64, int64, error) { - fv, err := strconv.ParseFloat(format, 64) + // As format is a preformatted number, it format should be A[.[B]]. + decimalPointPos := strings.IndexRune(format, '.') + if decimalPointPos == -1 { + decimalPointPos = len(format) + } + iv, err := strconv.ParseInt(format[0:decimalPointPos], 10, 64) if err != nil { return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) } - iv := int64(math.Round(fv)) switch strings.ToUpper(unit) { case "MICROSECOND": dayCount := iv / (3600000000 * 24) - fv -= float64(dayCount * 3600000000 * 24) - return 0, 0, dayCount, int64(fv * float64(gotime.Microsecond)), nil + iv %= 3600000000 * 24 + return 0, 0, dayCount, iv * int64(gotime.Microsecond), nil case "SECOND": dayCount := iv / (3600 * 24) - fv -= float64(dayCount * 3600 * 24) - return 0, 0, dayCount, int64(fv * float64(gotime.Second)), nil + iv %= 3600 * 24 + dv := int64(0) + lf := len(format) - 1 + // Has fractional part + if decimalPointPos < lf { + if lf-decimalPointPos >= 6 { + if dv, err = strconv.ParseInt(format[decimalPointPos+1:decimalPointPos+7], 10, 64); err != nil { + return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) + } + } else { + if dv, err = strconv.ParseInt(format[decimalPointPos+1:]+"000000"[:6-(lf-decimalPointPos)], 10, 64); err != nil { + return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) + } + } + } + return 0, 0, dayCount, iv*int64(gotime.Second) + dv*int64(gotime.Microsecond), nil case "MINUTE": dayCount := iv / (60 * 24) iv %= 60 * 24 From 18860bf5b38dc1e38883293549924decebacad51 Mon Sep 17 00:00:00 2001 From: Guo Song Date: Mon, 6 May 2019 10:09:40 +0800 Subject: [PATCH 08/17] fix rounding --- types/time.go | 62 +++++++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/types/time.go b/types/time.go index a46f627f334a5..e2baa39218bf9 100644 --- a/types/time.go +++ b/types/time.go @@ -1616,7 +1616,7 @@ func ExtractDurationNum(d *Duration, unit string) (int64, error) { } func parseSingleTimeValue(unit string, format string) (int64, int64, int64, int64, error) { - // As format is a preformatted number, it format should be A[.[B]]. + // Format is a preformatted number, it format should be A[.[B]]. decimalPointPos := strings.IndexRune(format, '.') if decimalPointPos == -1 { decimalPointPos = len(format) @@ -1625,48 +1625,52 @@ func parseSingleTimeValue(unit string, format string) (int64, int64, int64, int6 if err != nil { return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) } + riv := iv // Rounded integer value + dv := int64(0) + lf := len(format) - 1 + // Has fraction part + if decimalPointPos < lf { + if lf-decimalPointPos >= 6 { + if dv, err = strconv.ParseInt(format[decimalPointPos+1:decimalPointPos+7], 10, 64); err != nil { + return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) + } + } else { + if dv, err = strconv.ParseInt(format[decimalPointPos+1:]+"000000"[:6-(lf-decimalPointPos)], 10, 64); err != nil { + return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) + } + } + if dv >= 500000 { // Round up + riv++ + } + } switch strings.ToUpper(unit) { case "MICROSECOND": - dayCount := iv / (3600000000 * 24) - iv %= 3600000000 * 24 - return 0, 0, dayCount, iv * int64(gotime.Microsecond), nil + dayCount := riv / (3600000000 * 24) + riv %= 3600000000 * 24 + return 0, 0, dayCount, riv * int64(gotime.Microsecond), nil case "SECOND": dayCount := iv / (3600 * 24) iv %= 3600 * 24 - dv := int64(0) - lf := len(format) - 1 - // Has fractional part - if decimalPointPos < lf { - if lf-decimalPointPos >= 6 { - if dv, err = strconv.ParseInt(format[decimalPointPos+1:decimalPointPos+7], 10, 64); err != nil { - return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) - } - } else { - if dv, err = strconv.ParseInt(format[decimalPointPos+1:]+"000000"[:6-(lf-decimalPointPos)], 10, 64); err != nil { - return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) - } - } - } return 0, 0, dayCount, iv*int64(gotime.Second) + dv*int64(gotime.Microsecond), nil case "MINUTE": - dayCount := iv / (60 * 24) - iv %= 60 * 24 - return 0, 0, dayCount, iv * int64(gotime.Minute), nil + dayCount := riv / (60 * 24) + riv %= 60 * 24 + return 0, 0, dayCount, riv * int64(gotime.Minute), nil case "HOUR": - dayCount := iv / 24 - iv %= 24 - return 0, 0, dayCount, iv * int64(gotime.Hour), nil + dayCount := riv / 24 + riv %= 24 + return 0, 0, dayCount, riv * int64(gotime.Hour), nil case "DAY": - return 0, 0, iv, 0, nil + return 0, 0, riv, 0, nil case "WEEK": - return 0, 0, 7 * iv, 0, nil + return 0, 0, 7 * riv, 0, nil case "MONTH": - return 0, iv, 0, 0, nil + return 0, riv, 0, 0, nil case "QUARTER": - return 0, 3 * iv, 0, 0, nil + return 0, 3 * riv, 0, 0, nil case "YEAR": - return iv, 0, 0, 0, nil + return riv, 0, 0, 0, nil } return 0, 0, 0, 0, errors.Errorf("invalid singel timeunit - %s", unit) From 430e3d759d137252ef1fcd11fd5c351093f0ce5f Mon Sep 17 00:00:00 2001 From: Guo Song Date: Mon, 6 May 2019 16:44:43 +0800 Subject: [PATCH 09/17] add more tests --- expression/builtin_time.go | 8 +++++--- expression/integration_test.go | 10 ++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/expression/builtin_time.go b/expression/builtin_time.go index e3b2b9be63f0c..0c2b6b71e655d 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -2677,14 +2677,14 @@ func (du *baseDateArithmitical) getIntervalFromDecimal(ctx sessionctx.Context, a interval = "00:" + interval case "SECOND_MICROSECOND": /* keep interval as original decimal */ - case "SECOND", "MICROSECOND": - args[1] = WrapWithCastAsReal(ctx, args[1]) + case "SECOND": + // Decimal's EvalString is like %f format. interval, isNull, err = args[1].EvalString(ctx, row) if isNull || err != nil { return "", true, err } default: - // YEAR, QUARTER, MONTH, WEEK, DAY, HOUR, MINUTE + // YEAR, QUARTER, MONTH, WEEK, DAY, HOUR, MINUTE, MICROSECOND args[1] = WrapWithCastAsInt(ctx, args[1]) interval, isNull, err = args[1].EvalString(ctx, row) if isNull || err != nil { @@ -2692,6 +2692,8 @@ func (du *baseDateArithmitical) getIntervalFromDecimal(ctx sessionctx.Context, a } } + fmt.Printf("baseDateArithmitical.getIntervalFromDecimal interval %v\n", interval) + return interval, false, nil } diff --git a/expression/integration_test.go b/expression/integration_test.go index bcbc804c0b1cb..ce1e410191ede 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -4282,7 +4282,17 @@ func (s *testIntegrationSuite) TestIssue9727(c *C) { {`SELECT 10000101000000 + INTERVAL "111111111111.111111" SECOND;`, `4520-12-21 05:31:51.111111`}, {`SELECT 10000101000000 + INTERVAL "111111111111.111111111" SECOND;`, `4520-12-21 05:31:51.111111`}, {`SELECT 10000101000000 + INTERVAL "111111111111.111" SECOND;`, `4520-12-21 05:31:51.111000`}, + {`SELECT 10000101000000 + INTERVAL "111111111111.111111111" SECOND;`, `4520-12-21 05:31:51.111111`}, + {`SELECT 10000101000000 + INTERVAL "111111111111.111" SECOND;`, `4520-12-21 05:31:51.111000`}, {`SELECT 10000101000000 + INTERVAL "111111111111." SECOND;`, `4520-12-21 05:31:51`}, + {`SELECT 10000101000000 + INTERVAL "111111111111111111.5" MICROSECOND;`, `4520-12-21 05:31:51.111112`}, + {`SELECT 10000101000000 + INTERVAL "111111111111111112.5" MICROSECOND;`, `4520-12-21 05:31:51.111113`}, + {`SELECT 10000101000000 + INTERVAL "111111111111111111.500000" MICROSECOND;`, `4520-12-21 05:31:51.111112`}, + {`SELECT 10000101000000 + INTERVAL "111111111111111111.50000000" MICROSECOND;`, `4520-12-21 05:31:51.111112`}, + {`SELECT 10000101000000 + INTERVAL "111111111111111111.6" MICROSECOND;`, `4520-12-21 05:31:51.111112`}, + {`SELECT 10000101000000 + INTERVAL "111111111111111111.499999" MICROSECOND;`, `4520-12-21 05:31:51.111111`}, + {`SELECT 10000101000000 + INTERVAL "111111111111111111.499999999" MICROSECOND;`, `4520-12-21 05:31:51.111111`}, + {`SELECT 10000101000000 + INTERVAL "111111111111111111.499999999999" MICROSECOND;`, `4520-12-21 05:31:51.111111`}, } for _, c := range cases { From 54c17b5981e7756d4f8e0f2be674640d6e7dfa37 Mon Sep 17 00:00:00 2001 From: Guo Song Date: Mon, 6 May 2019 16:49:10 +0800 Subject: [PATCH 10/17] remove some redundant tests --- expression/integration_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/expression/integration_test.go b/expression/integration_test.go index ce1e410191ede..2e1b851cfc243 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -4282,8 +4282,6 @@ func (s *testIntegrationSuite) TestIssue9727(c *C) { {`SELECT 10000101000000 + INTERVAL "111111111111.111111" SECOND;`, `4520-12-21 05:31:51.111111`}, {`SELECT 10000101000000 + INTERVAL "111111111111.111111111" SECOND;`, `4520-12-21 05:31:51.111111`}, {`SELECT 10000101000000 + INTERVAL "111111111111.111" SECOND;`, `4520-12-21 05:31:51.111000`}, - {`SELECT 10000101000000 + INTERVAL "111111111111.111111111" SECOND;`, `4520-12-21 05:31:51.111111`}, - {`SELECT 10000101000000 + INTERVAL "111111111111.111" SECOND;`, `4520-12-21 05:31:51.111000`}, {`SELECT 10000101000000 + INTERVAL "111111111111." SECOND;`, `4520-12-21 05:31:51`}, {`SELECT 10000101000000 + INTERVAL "111111111111111111.5" MICROSECOND;`, `4520-12-21 05:31:51.111112`}, {`SELECT 10000101000000 + INTERVAL "111111111111111112.5" MICROSECOND;`, `4520-12-21 05:31:51.111113`}, @@ -4291,7 +4289,6 @@ func (s *testIntegrationSuite) TestIssue9727(c *C) { {`SELECT 10000101000000 + INTERVAL "111111111111111111.50000000" MICROSECOND;`, `4520-12-21 05:31:51.111112`}, {`SELECT 10000101000000 + INTERVAL "111111111111111111.6" MICROSECOND;`, `4520-12-21 05:31:51.111112`}, {`SELECT 10000101000000 + INTERVAL "111111111111111111.499999" MICROSECOND;`, `4520-12-21 05:31:51.111111`}, - {`SELECT 10000101000000 + INTERVAL "111111111111111111.499999999" MICROSECOND;`, `4520-12-21 05:31:51.111111`}, {`SELECT 10000101000000 + INTERVAL "111111111111111111.499999999999" MICROSECOND;`, `4520-12-21 05:31:51.111111`}, } From d75132adb59d4a95a832686ab4a4e986648901c4 Mon Sep 17 00:00:00 2001 From: Guo Song Date: Mon, 6 May 2019 17:21:52 +0800 Subject: [PATCH 11/17] address comments --- expression/builtin_time.go | 2 -- types/time.go | 16 +++++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/expression/builtin_time.go b/expression/builtin_time.go index 0c2b6b71e655d..05cba7e191219 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -2692,8 +2692,6 @@ func (du *baseDateArithmitical) getIntervalFromDecimal(ctx sessionctx.Context, a } } - fmt.Printf("baseDateArithmitical.getIntervalFromDecimal interval %v\n", interval) - return interval, false, nil } diff --git a/types/time.go b/types/time.go index e2baa39218bf9..8d13c4d588e58 100644 --- a/types/time.go +++ b/types/time.go @@ -1632,6 +1632,7 @@ func parseSingleTimeValue(unit string, format string) (int64, int64, int64, int6 // Has fraction part if decimalPointPos < lf { if lf-decimalPointPos >= 6 { + // MySQL rounds down to 1e-6. if dv, err = strconv.ParseInt(format[decimalPointPos+1:decimalPointPos+7], 10, 64); err != nil { return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) } @@ -1640,22 +1641,23 @@ func parseSingleTimeValue(unit string, format string) (int64, int64, int64, int6 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) } } - if dv >= 500000 { // Round up + if dv >= 500000 { // Round up, and we should keep 6 digits for microsecond, so dv should in [000000, 999999]. riv++ } } + const dayLength = int64(24 * gotime.Hour) switch strings.ToUpper(unit) { case "MICROSECOND": - dayCount := riv / (3600000000 * 24) - riv %= 3600000000 * 24 + dayCount := riv / (dayLength / int64(gotime.Microsecond)) + riv %= dayLength / int64(gotime.Microsecond) return 0, 0, dayCount, riv * int64(gotime.Microsecond), nil case "SECOND": - dayCount := iv / (3600 * 24) - iv %= 3600 * 24 + dayCount := iv / (dayLength / int64(gotime.Second)) + iv %= dayLength / int64(gotime.Second) return 0, 0, dayCount, iv*int64(gotime.Second) + dv*int64(gotime.Microsecond), nil case "MINUTE": - dayCount := riv / (60 * 24) - riv %= 60 * 24 + dayCount := riv / (dayLength / int64(gotime.Minute)) + riv %= dayLength / int64(gotime.Minute) return 0, 0, dayCount, riv * int64(gotime.Minute), nil case "HOUR": dayCount := riv / 24 From 9a527d2aebfd4390d9b660928d0ee4519e001f61 Mon Sep 17 00:00:00 2001 From: Guo Song Date: Tue, 7 May 2019 16:38:42 +0800 Subject: [PATCH 12/17] address comments --- types/time.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/types/time.go b/types/time.go index 8d13c4d588e58..09d76b57eedb4 100644 --- a/types/time.go +++ b/types/time.go @@ -1645,19 +1645,19 @@ func parseSingleTimeValue(unit string, format string) (int64, int64, int64, int6 riv++ } } - const dayLength = int64(24 * gotime.Hour) + const gotimeDay = 24 * gotime.Hour switch strings.ToUpper(unit) { case "MICROSECOND": - dayCount := riv / (dayLength / int64(gotime.Microsecond)) - riv %= dayLength / int64(gotime.Microsecond) + dayCount := riv / int64(gotimeDay/gotime.Microsecond) + riv %= int64(gotimeDay / gotime.Microsecond) return 0, 0, dayCount, riv * int64(gotime.Microsecond), nil case "SECOND": - dayCount := iv / (dayLength / int64(gotime.Second)) - iv %= dayLength / int64(gotime.Second) + dayCount := iv / (gotimeDay / int64(gotime.Second)) + iv %= int64(gotimeDay / gotime.Second) return 0, 0, dayCount, iv*int64(gotime.Second) + dv*int64(gotime.Microsecond), nil case "MINUTE": - dayCount := riv / (dayLength / int64(gotime.Minute)) - riv %= dayLength / int64(gotime.Minute) + dayCount := riv / (gotimeDay / int64(gotime.Minute)) + riv %= int64(gotimeDay / gotime.Minute) return 0, 0, dayCount, riv * int64(gotime.Minute), nil case "HOUR": dayCount := riv / 24 From 355112a80c3c71f76c4b1d522a8f406ee1545f20 Mon Sep 17 00:00:00 2001 From: Guo Song Date: Wed, 8 May 2019 14:57:56 +0800 Subject: [PATCH 13/17] fix --- types/time.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/time.go b/types/time.go index 09d76b57eedb4..d0305d8b6eff2 100644 --- a/types/time.go +++ b/types/time.go @@ -1652,11 +1652,11 @@ func parseSingleTimeValue(unit string, format string) (int64, int64, int64, int6 riv %= int64(gotimeDay / gotime.Microsecond) return 0, 0, dayCount, riv * int64(gotime.Microsecond), nil case "SECOND": - dayCount := iv / (gotimeDay / int64(gotime.Second)) + dayCount := iv / int64(gotimeDay/gotime.Second) iv %= int64(gotimeDay / gotime.Second) return 0, 0, dayCount, iv*int64(gotime.Second) + dv*int64(gotime.Microsecond), nil case "MINUTE": - dayCount := riv / (gotimeDay / int64(gotime.Minute)) + dayCount := riv / int64(gotimeDay/gotime.Minute) riv %= int64(gotimeDay / gotime.Minute) return 0, 0, dayCount, riv * int64(gotime.Minute), nil case "HOUR": From 4dc5c08e44895e0a423cf2238455c425b8794056 Mon Sep 17 00:00:00 2001 From: Guo Song Date: Thu, 9 May 2019 16:35:24 +0800 Subject: [PATCH 14/17] throw warnings --- expression/builtin_time.go | 16 ++++----- types/time.go | 21 ++++++------ types/time_test.go | 66 +++++++++++++++++++++----------------- 3 files changed, 57 insertions(+), 46 deletions(-) diff --git a/expression/builtin_time.go b/expression/builtin_time.go index 05cba7e191219..f0cfb7a07acd3 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -2705,12 +2705,12 @@ func (du *baseDateArithmitical) getIntervalFromInt(ctx sessionctx.Context, args func (du *baseDateArithmitical) add(ctx sessionctx.Context, date types.Time, interval string, unit string) (types.Time, bool, error) { year, month, day, nano, err := types.ParseDurationValue(unit, interval) - if err != nil { - return types.Time{}, true, handleInvalidTimeError(ctx, err) + if err := handleInvalidTimeError(ctx, err); err != nil { + return types.Time{}, true, err } goTime, err := date.Time.GoTime(time.Local) - if err != nil { + if err := handleInvalidTimeError(ctx, err); err != nil { return types.Time{}, true, err } @@ -2725,7 +2725,7 @@ func (du *baseDateArithmitical) add(ctx sessionctx.Context, date types.Time, int date.Time = types.FromGoTime(goTime) overflow, err := types.DateTimeIsOverflow(ctx.GetSessionVars().StmtCtx, date) - if err != nil { + if err := handleInvalidTimeError(ctx, err); err != nil { return types.Time{}, true, err } if overflow { @@ -2736,13 +2736,13 @@ func (du *baseDateArithmitical) add(ctx sessionctx.Context, date types.Time, int func (du *baseDateArithmitical) sub(ctx sessionctx.Context, date types.Time, interval string, unit string) (types.Time, bool, error) { year, month, day, nano, err := types.ParseDurationValue(unit, interval) - if err != nil { - return types.Time{}, true, handleInvalidTimeError(ctx, err) + if err := handleInvalidTimeError(ctx, err); err != nil { + return types.Time{}, true, err } year, month, day, nano = -year, -month, -day, -nano goTime, err := date.Time.GoTime(time.Local) - if err != nil { + if err := handleInvalidTimeError(ctx, err); err != nil { return types.Time{}, true, err } @@ -2758,7 +2758,7 @@ func (du *baseDateArithmitical) sub(ctx sessionctx.Context, date types.Time, int date.Time = types.FromGoTime(goTime) overflow, err := types.DateTimeIsOverflow(ctx.GetSessionVars().StmtCtx, date) - if err != nil { + if err := handleInvalidTimeError(ctx, err); err != nil { return types.Time{}, true, err } if overflow { diff --git a/types/time.go b/types/time.go index d0305d8b6eff2..eac8eac60fdf1 100644 --- a/types/time.go +++ b/types/time.go @@ -1644,35 +1644,38 @@ func parseSingleTimeValue(unit string, format string) (int64, int64, int64, int6 if dv >= 500000 { // Round up, and we should keep 6 digits for microsecond, so dv should in [000000, 999999]. riv++ } + if unit != "SECOND" { + err = ErrTruncatedWrongValue.GenWithStackByArgs(format) + } } const gotimeDay = 24 * gotime.Hour switch strings.ToUpper(unit) { case "MICROSECOND": dayCount := riv / int64(gotimeDay/gotime.Microsecond) riv %= int64(gotimeDay / gotime.Microsecond) - return 0, 0, dayCount, riv * int64(gotime.Microsecond), nil + return 0, 0, dayCount, riv * int64(gotime.Microsecond), err case "SECOND": dayCount := iv / int64(gotimeDay/gotime.Second) iv %= int64(gotimeDay / gotime.Second) - return 0, 0, dayCount, iv*int64(gotime.Second) + dv*int64(gotime.Microsecond), nil + return 0, 0, dayCount, iv*int64(gotime.Second) + dv*int64(gotime.Microsecond), err case "MINUTE": dayCount := riv / int64(gotimeDay/gotime.Minute) riv %= int64(gotimeDay / gotime.Minute) - return 0, 0, dayCount, riv * int64(gotime.Minute), nil + return 0, 0, dayCount, riv * int64(gotime.Minute), err case "HOUR": dayCount := riv / 24 riv %= 24 - return 0, 0, dayCount, riv * int64(gotime.Hour), nil + return 0, 0, dayCount, riv * int64(gotime.Hour), err case "DAY": - return 0, 0, riv, 0, nil + return 0, 0, riv, 0, err case "WEEK": - return 0, 0, 7 * riv, 0, nil + return 0, 0, 7 * riv, 0, err case "MONTH": - return 0, riv, 0, 0, nil + return 0, riv, 0, 0, err case "QUARTER": - return 0, 3 * riv, 0, 0, nil + return 0, 3 * riv, 0, 0, err case "YEAR": - return riv, 0, 0, 0, nil + return riv, 0, 0, 0, err } return 0, 0, 0, 0, errors.Errorf("invalid singel timeunit - %s", unit) diff --git a/types/time_test.go b/types/time_test.go index 572e4a0bf0455..b7b5a17aa897b 100644 --- a/types/time_test.go +++ b/types/time_test.go @@ -19,6 +19,7 @@ import ( . "github.com/pingcap/check" "github.com/pingcap/parser/mysql" + "github.com/pingcap/parser/terror" "github.com/pingcap/tidb/sessionctx/stmtctx" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util/mock" @@ -1253,35 +1254,38 @@ func (s *testTimeSuite) TestParseDurationValue(c *C) { res2 int64 res3 int64 res4 int64 + err *terror.Error }{ - {"52", "WEEK", 0, 0, 52 * 7, 0}, - {"12", "DAY", 0, 0, 12, 0}, - {"04", "MONTH", 0, 04, 0, 0}, - {"1", "QUARTER", 0, 1 * 3, 0, 0}, - {"2019", "YEAR", 2019, 0, 0, 0}, - {"10567890", "SECOND_MICROSECOND", 0, 0, 0, 10567890000}, - {"10.567890", "SECOND_MICROSECOND", 0, 0, 0, 10567890000}, - {"-10.567890", "SECOND_MICROSECOND", 0, 0, 0, -10567890000}, - {"35:10567890", "MINUTE_SECOND", 0, 0, 122, 29190000000000}, // 122 * 3600 * 24 + 29190 = 35 * 60 + 10567890 - {"3510567890", "MINUTE_SECOND", 0, 0, 40631, 49490000000000}, // 40631 * 3600 * 24 + 49490 = 3510567890 - {"11:35:10.567890", "HOUR_MICROSECOND", 0, 0, 0, 41710567890000}, // = (11 * 3600 + 35 * 60) * 1000000000 + 10567890000 - {"567890", "HOUR_MICROSECOND", 0, 0, 0, 567890000}, - {"14:00", "HOUR_MINUTE", 0, 0, 0, 50400000000000}, - {"14", "HOUR_MINUTE", 0, 0, 0, 840000000000}, - {"12 14:00:00.345", "DAY_MICROSECOND", 0, 0, 12, 50400345000000}, - {"12 14:00:00", "DAY_SECOND", 0, 0, 12, 50400000000000}, - {"12 14:00", "DAY_MINUTE", 0, 0, 12, 50400000000000}, - {"12 14", "DAY_HOUR", 0, 0, 12, 50400000000000}, - {"1:1", "DAY_HOUR", 0, 0, 1, 3600000000000}, - {"aa1bb1", "DAY_HOUR", 0, 0, 1, 3600000000000}, - {"-1:1", "DAY_HOUR", 0, 0, -1, -3600000000000}, - {"-aa1bb1", "DAY_HOUR", 0, 0, -1, -3600000000000}, - {"2019-12", "YEAR_MONTH", 2019, 12, 0, 0}, - {"1 1", "YEAR_MONTH", 1, 1, 0, 0}, - {"aa1bb1", "YEAR_MONTH", 1, 1, 0, 0}, - {"-1 1", "YEAR_MONTH", -1, -1, 0, 0}, - {"-aa1bb1", "YEAR_MONTH", -1, -1, 0, 0}, - {" \t\n\r\n - aa1bb1 \t\n ", "YEAR_MONTH", -1, -1, 0, 0}, + {"52", "WEEK", 0, 0, 52 * 7, 0, nil}, + {"12", "DAY", 0, 0, 12, 0, nil}, + {"04", "MONTH", 0, 04, 0, 0, nil}, + {"1", "QUARTER", 0, 1 * 3, 0, 0, nil}, + {"2019", "YEAR", 2019, 0, 0, 0, nil}, + {"10567890", "SECOND_MICROSECOND", 0, 0, 0, 10567890000, nil}, + {"10.567890", "SECOND_MICROSECOND", 0, 0, 0, 10567890000, nil}, + {"-10.567890", "SECOND_MICROSECOND", 0, 0, 0, -10567890000, nil}, + {"35:10567890", "MINUTE_SECOND", 0, 0, 122, 29190000000000, nil}, // 122 * 3600 * 24 + 29190 = 35 * 60 + 10567890 + {"3510567890", "MINUTE_SECOND", 0, 0, 40631, 49490000000000, nil}, // 40631 * 3600 * 24 + 49490 = 3510567890 + {"11:35:10.567890", "HOUR_MICROSECOND", 0, 0, 0, 41710567890000, nil}, // = (11 * 3600 + 35 * 60) * 1000000000 + 10567890000 + {"567890", "HOUR_MICROSECOND", 0, 0, 0, 567890000, nil}, + {"14:00", "HOUR_MINUTE", 0, 0, 0, 50400000000000, nil}, + {"14", "HOUR_MINUTE", 0, 0, 0, 840000000000, nil}, + {"12 14:00:00.345", "DAY_MICROSECOND", 0, 0, 12, 50400345000000, nil}, + {"12 14:00:00", "DAY_SECOND", 0, 0, 12, 50400000000000, nil}, + {"12 14:00", "DAY_MINUTE", 0, 0, 12, 50400000000000, nil}, + {"12 14", "DAY_HOUR", 0, 0, 12, 50400000000000, nil}, + {"1:1", "DAY_HOUR", 0, 0, 1, 3600000000000, nil}, + {"aa1bb1", "DAY_HOUR", 0, 0, 1, 3600000000000, nil}, + {"-1:1", "DAY_HOUR", 0, 0, -1, -3600000000000, nil}, + {"-aa1bb1", "DAY_HOUR", 0, 0, -1, -3600000000000, nil}, + {"2019-12", "YEAR_MONTH", 2019, 12, 0, 0, nil}, + {"1 1", "YEAR_MONTH", 1, 1, 0, 0, nil}, + {"aa1bb1", "YEAR_MONTH", 1, 1, 0, 0, nil}, + {"-1 1", "YEAR_MONTH", -1, -1, 0, 0, nil}, + {"-aa1bb1", "YEAR_MONTH", -1, -1, 0, 0, nil}, + {" \t\n\r\n - aa1bb1 \t\n ", "YEAR_MONTH", -1, -1, 0, 0, nil}, + {"1.111", "MICROSECOND", 0, 0, 0, 1000, types.ErrTruncatedWrongValue}, + {"1.111", "DAY", 0, 0, 1, 0, types.ErrTruncatedWrongValue}, } for _, col := range tbl { comment := Commentf("Extract %v Unit %v", col.format, col.unit) @@ -1290,7 +1294,11 @@ func (s *testTimeSuite) TestParseDurationValue(c *C) { c.Assert(res2, Equals, col.res2, comment) c.Assert(res3, Equals, col.res3, comment) c.Assert(res4, Equals, col.res4, comment) - c.Assert(err, IsNil, comment) + if col.err == nil { + c.Assert(err, IsNil, comment) + } else { + c.Assert(col.err.Equal(err), IsTrue) + } } } From e50a2fedf1cdbbff0cf9384b5955ec7b361198a0 Mon Sep 17 00:00:00 2001 From: Guo Song Date: Thu, 9 May 2019 17:03:48 +0800 Subject: [PATCH 15/17] fix ci --- expression/integration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expression/integration_test.go b/expression/integration_test.go index 2e1b851cfc243..b64a090733146 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -1847,7 +1847,7 @@ func (s *testIntegrationSuite) TestTimeBuiltin(c *C) { {"\"2011-11-11 00:00:00\"", "10", "MINUTE", "2011-11-11 00:10:00", "2011-11-10 23:50:00"}, {"\"2011-11-11 00:00:00\"", "10", "SECOND", "2011-11-11 00:00:10", "2011-11-10 23:59:50"}, - {"\"2011-11-11\"", "\"abc1000\"", "MICROSECOND", "", ""}, + {"\"2011-11-11\"", "\"abc1000\"", "MICROSECOND", "1000-01-01 00:00:00", "1000-01-01 00:00:00"}, {"\"20111111 10:10:10\"", "\"1\"", "DAY", "", ""}, {"\"2011-11-11\"", "\"10\"", "SECOND_MICROSECOND", "2011-11-11 00:00:00.100000", "2011-11-10 23:59:59.900000"}, {"\"2011-11-11\"", "\"10.0000\"", "MINUTE_MICROSECOND", "2011-11-11 00:00:10", "2011-11-10 23:59:50"}, From a74aa2d5c10bf9faa3ff8ddc951d43309927d260 Mon Sep 17 00:00:00 2001 From: Guo Song Date: Thu, 9 May 2019 17:43:19 +0800 Subject: [PATCH 16/17] fix --- expression/integration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expression/integration_test.go b/expression/integration_test.go index b64a090733146..8db2b2d930e18 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -1847,7 +1847,7 @@ func (s *testIntegrationSuite) TestTimeBuiltin(c *C) { {"\"2011-11-11 00:00:00\"", "10", "MINUTE", "2011-11-11 00:10:00", "2011-11-10 23:50:00"}, {"\"2011-11-11 00:00:00\"", "10", "SECOND", "2011-11-11 00:00:10", "2011-11-10 23:59:50"}, - {"\"2011-11-11\"", "\"abc1000\"", "MICROSECOND", "1000-01-01 00:00:00", "1000-01-01 00:00:00"}, + {"\"2011-11-11\"", "\"abc1000\"", "MICROSECOND", "2011-11-11 00:00:00", "2011-11-11 00:00:00"}, {"\"20111111 10:10:10\"", "\"1\"", "DAY", "", ""}, {"\"2011-11-11\"", "\"10\"", "SECOND_MICROSECOND", "2011-11-11 00:00:00.100000", "2011-11-10 23:59:59.900000"}, {"\"2011-11-11\"", "\"10.0000\"", "MINUTE_MICROSECOND", "2011-11-11 00:00:10", "2011-11-10 23:59:50"}, From dee0fb49fdb6479883dd078c1e7fe42b4a3f4ff7 Mon Sep 17 00:00:00 2001 From: Guo Song Date: Fri, 10 May 2019 14:01:34 +0800 Subject: [PATCH 17/17] fix --- types/time.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/types/time.go b/types/time.go index eac8eac60fdf1..b284b20ed382c 100644 --- a/types/time.go +++ b/types/time.go @@ -1621,6 +1621,10 @@ func parseSingleTimeValue(unit string, format string) (int64, int64, int64, int6 if decimalPointPos == -1 { decimalPointPos = len(format) } + sign := int64(1) + if len(format) > 0 && format[0] == '-' { + sign = int64(-1) + } iv, err := strconv.ParseInt(format[0:decimalPointPos], 10, 64) if err != nil { return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) @@ -1642,7 +1646,7 @@ func parseSingleTimeValue(unit string, format string) (int64, int64, int64, int6 } } if dv >= 500000 { // Round up, and we should keep 6 digits for microsecond, so dv should in [000000, 999999]. - riv++ + riv += sign } if unit != "SECOND" { err = ErrTruncatedWrongValue.GenWithStackByArgs(format)