Skip to content

Commit

Permalink
Support add datetime with real interval (#10347) (#10418)
Browse files Browse the repository at this point in the history
  • Loading branch information
erjiaqing authored and zz-jason committed May 11, 2019
1 parent 7370996 commit 26d05fa
Show file tree
Hide file tree
Showing 2 changed files with 266 additions and 2 deletions.
246 changes: 244 additions & 2 deletions expression/builtin_time.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,21 +216,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{}
)

Expand Down Expand Up @@ -2631,6 +2637,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, nano, err := types.ParseDurationValue(unit, interval)
if err := handleInvalidTimeError(ctx, err); err != nil {
Expand Down Expand Up @@ -2710,7 +2724,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
}

Expand All @@ -2729,6 +2743,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,
Expand All @@ -2744,6 +2763,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,
Expand All @@ -2759,6 +2783,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,
Expand Down Expand Up @@ -2834,6 +2863,39 @@ func (b *builtinAddDateStringIntSig) evalTime(row chunk.Row) (types.Time, bool,
return result, isNull || err != nil, errors.Trace(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
Expand Down Expand Up @@ -2933,6 +2995,39 @@ func (b *builtinAddDateIntIntSig) evalTime(row chunk.Row) (types.Time, bool, err
return result, isNull || err != nil, errors.Trace(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
Expand Down Expand Up @@ -3032,6 +3127,39 @@ func (b *builtinAddDateDatetimeIntSig) evalTime(row chunk.Row) (types.Time, bool
return result, isNull || err != nil, errors.Trace(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
Expand Down Expand Up @@ -3080,7 +3208,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
}

Expand All @@ -3099,6 +3227,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,
Expand All @@ -3114,6 +3247,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,
Expand All @@ -3129,6 +3267,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,
Expand Down Expand Up @@ -3204,6 +3347,39 @@ func (b *builtinSubDateStringIntSig) evalTime(row chunk.Row) (types.Time, bool,
return result, isNull || err != nil, errors.Trace(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
Expand Down Expand Up @@ -3301,6 +3477,39 @@ func (b *builtinSubDateIntIntSig) evalTime(row chunk.Row) (types.Time, bool, err
return result, isNull || err != nil, errors.Trace(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
Expand Down Expand Up @@ -3400,6 +3609,39 @@ func (b *builtinSubDateDatetimeIntSig) evalTime(row chunk.Row) (types.Time, bool
return result, isNull || err != nil, errors.Trace(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
Expand Down
22 changes: 22 additions & 0 deletions expression/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3957,3 +3957,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))
}
}

0 comments on commit 26d05fa

Please sign in to comment.