diff --git a/expression/builtin_time.go b/expression/builtin_time.go index 883cad23a64de..821ae9121154c 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 = &builtinAddDateDatetimeRealSig{ + 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 @@ -3105,6 +3200,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 +3281,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 +3300,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 +3320,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 +3340,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 +3420,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 +3550,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 +3682,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/integration_test.go b/expression/integration_test.go index 1b0a35eebc636..c5359eb6ce4a3 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -4267,3 +4267,25 @@ func (s *testIntegrationSuite) TestTimestampDatumEncode(c *C) { )) tk.MustQuery(`select * from t where b = (select max(b) from t)`).Check(testkit.Rows(`1 2019-04-29 11:56:12`)) } + +func (s *testIntegrationSuite) TestDateTimeAddReal(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 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 "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 { + tk.MustQuery(c.sql).Check(testkit.Rows(c.result)) + } +}