Skip to content

Commit

Permalink
Support add datetime with real interval (pingcap#10347)
Browse files Browse the repository at this point in the history
  • Loading branch information
erjiaqing committed May 10, 2019
1 parent d22ecd0 commit 9909e04
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, dur, err := types.ExtractTimeValue(unit, interval)
if err != nil {
Expand Down Expand Up @@ -2711,7 +2725,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 @@ -2730,6 +2744,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 @@ -2745,6 +2764,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 @@ -2760,6 +2784,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 @@ -2835,6 +2864,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 @@ -2934,6 +2996,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 @@ -3033,6 +3128,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 @@ -3081,7 +3209,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 @@ -3100,6 +3228,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 @@ -3115,6 +3248,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 @@ -3130,6 +3268,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 @@ -3205,6 +3348,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 @@ -3302,6 +3478,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 @@ -3401,6 +3610,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 @@ -3908,3 +3908,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 9909e04

Please sign in to comment.