Skip to content

Commit

Permalink
*: add_date can return mysql.Time (#9830) (#10718)
Browse files Browse the repository at this point in the history
  • Loading branch information
winoros authored and zz-jason committed Jun 20, 2019
1 parent dc4f2f1 commit 7b8e174
Show file tree
Hide file tree
Showing 8 changed files with 552 additions and 65 deletions.
133 changes: 130 additions & 3 deletions expression/builtin_time.go
Original file line number Diff line number Diff line change
Expand Up @@ -2727,6 +2727,18 @@ func (du *baseDateArithmitical) add(ctx sessionctx.Context, date types.Time, int
return date, false, nil
}

func (du *baseDateArithmitical) addDuration(ctx sessionctx.Context, d types.Duration, interval string, unit string) (types.Duration, bool, error) {
dur, err := types.ExtractDurationValue(unit, interval)
if err != nil {
return types.ZeroDuration, true, handleInvalidTimeError(ctx, err)
}
retDur, err := d.Add(dur)
if err != nil {
return types.ZeroDuration, true, err
}
return retDur, false, nil
}

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 := handleInvalidTimeError(ctx, err); err != nil {
Expand Down Expand Up @@ -2770,7 +2782,7 @@ func (c *addDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres
}

dateEvalTp := args[0].GetType().EvalType()
if dateEvalTp != types.ETString && dateEvalTp != types.ETInt {
if dateEvalTp != types.ETString && dateEvalTp != types.ETInt && dateEvalTp != types.ETDuration {
dateEvalTp = types.ETDatetime
}

Expand All @@ -2780,8 +2792,35 @@ func (c *addDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres
}

argTps := []types.EvalType{dateEvalTp, intervalEvalTp, types.ETString}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETDatetime, argTps...)
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDatetimeFullWidth, types.UnspecifiedLength
var bf baseBuiltinFunc
if dateEvalTp == types.ETDuration {
unit, _, err := args[2].EvalString(ctx, chunk.Row{})
if err != nil {
return nil, err
}
internalFsp := 0
switch unit {
// If the unit has micro second, then the fsp must be the MaxFsp.
case "MICROSECOND", "SECOND_MICROSECOND", "MINUTE_MICROSECOND", "HOUR_MICROSECOND", "DAY_MICROSECOND":
internalFsp = types.MaxFsp
// If the unit is second, the fsp is related with the arg[1]'s.
case "SECOND":
internalFsp = types.MaxFsp
if intervalEvalTp != types.ETString {
internalFsp = mathutil.Min(args[1].GetType().Decimal, types.MaxFsp)
}
// Otherwise, the fsp should be 0.
}
bf = newBaseBuiltinFuncWithTp(ctx, args, types.ETDuration, argTps...)
arg0Dec, err := getExpressionFsp(ctx, args[0])
if err != nil {
return nil, err
}
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDurationWidthWithFsp, mathutil.Max(arg0Dec, internalFsp)
} else {
bf = newBaseBuiltinFuncWithTp(ctx, args, types.ETDatetime, argTps...)
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDatetimeFullWidth, types.UnspecifiedLength
}

switch {
case dateEvalTp == types.ETString && intervalEvalTp == types.ETString:
Expand Down Expand Up @@ -2844,6 +2883,21 @@ func (c *addDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres
baseBuiltinFunc: bf,
baseDateArithmitical: newDateArighmeticalUtil(),
}
case dateEvalTp == types.ETDuration && intervalEvalTp == types.ETString:
sig = &builtinAddDateDurationStringSig{
baseBuiltinFunc: bf,
baseDateArithmitical: newDateArighmeticalUtil(),
}
case dateEvalTp == types.ETDuration && intervalEvalTp == types.ETInt:
sig = &builtinAddDateDurationIntSig{
baseBuiltinFunc: bf,
baseDateArithmitical: newDateArighmeticalUtil(),
}
case dateEvalTp == types.ETDuration && intervalEvalTp == types.ETDecimal:
sig = &builtinAddDateDurationDecimalSig{
baseBuiltinFunc: bf,
baseDateArithmitical: newDateArighmeticalUtil(),
}
}
return sig, nil
}
Expand Down Expand Up @@ -3244,6 +3298,79 @@ func (b *builtinAddDateDatetimeDecimalSig) evalTime(row chunk.Row) (types.Time,
return result, isNull || err != nil, errors.Trace(err)
}

type builtinAddDateDurationStringSig struct {
baseBuiltinFunc
baseDateArithmitical
}

func (b *builtinAddDateDurationStringSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
if isNull || err != nil {
return types.ZeroDuration, true, err
}

dur, isNull, err := b.args[0].EvalDuration(b.ctx, row)
if isNull || err != nil {
return types.ZeroDuration, true, err
}

interval, isNull, err := b.getIntervalFromString(b.ctx, b.args, row, unit)
if isNull || err != nil {
return types.ZeroDuration, true, err
}

result, isNull, err := b.addDuration(b.ctx, dur, interval, unit)
return result, isNull || err != nil, err
}

type builtinAddDateDurationIntSig struct {
baseBuiltinFunc
baseDateArithmitical
}

func (b *builtinAddDateDurationIntSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
if isNull || err != nil {
return types.ZeroDuration, true, err
}

dur, isNull, err := b.args[0].EvalDuration(b.ctx, row)
if isNull || err != nil {
return types.ZeroDuration, true, err
}
interval, isNull, err := b.getIntervalFromInt(b.ctx, b.args, row, unit)
if isNull || err != nil {
return types.ZeroDuration, true, err
}

result, isNull, err := b.addDuration(b.ctx, dur, interval, unit)
return result, isNull || err != nil, err
}

type builtinAddDateDurationDecimalSig struct {
baseBuiltinFunc
baseDateArithmitical
}

func (b *builtinAddDateDurationDecimalSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
if isNull || err != nil {
return types.ZeroDuration, true, err
}

dur, isNull, err := b.args[0].EvalDuration(b.ctx, row)
if isNull || err != nil {
return types.ZeroDuration, true, err
}
interval, isNull, err := b.getIntervalFromDecimal(b.ctx, b.args, row, unit)
if isNull || err != nil {
return types.ZeroDuration, true, err
}

result, isNull, err := b.addDuration(b.ctx, dur, interval, unit)
return result, isNull || err != nil, err
}

type subDateFunctionClass struct {
baseFunctionClass
}
Expand Down
27 changes: 27 additions & 0 deletions expression/builtin_time_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1742,6 +1742,33 @@ func (s *testEvaluatorSuite) TestDateArithFuncs(c *C) {
c.Assert(err, IsNil)
c.Assert(v.GetMysqlTime().String(), Equals, test.expected)
}
testDurations := []struct {
dur string
fsp int
unit string
format interface{}
expected string
}{
{
dur: "00:00:00",
fsp: 0,
unit: "MICROSECOND",
format: "100",
expected: "00:00:00.000100",
},
}
for _, tt := range testDurations {
dur, _, ok, err := types.StrToDuration(nil, tt.dur, tt.fsp)
c.Assert(err, IsNil)
c.Assert(ok, IsTrue)
args = types.MakeDatums(dur, tt.format, tt.unit)
f, err = fcAdd.getFunction(s.ctx, s.datumsToConstants(args))
c.Assert(err, IsNil)
c.Assert(f, NotNil)
v, err = evalBuiltinFunc(f, chunk.Row{})
c.Assert(err, IsNil)
c.Assert(v.GetMysqlDuration().String(), Equals, tt.expected)
}
}

func (s *testEvaluatorSuite) TestTimestamp(c *C) {
Expand Down
6 changes: 0 additions & 6 deletions mysql/const_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ package mysql_test

import (
"flag"
"testing"

. "github.com/pingcap/check"
"github.com/pingcap/parser"
Expand All @@ -30,11 +29,6 @@ import (
"golang.org/x/net/context"
)

func TestT(t *testing.T) {
CustomVerboseFlag = true
TestingT(t)
}

var _ = Suite(&testMySQLConstSuite{})

type testMySQLConstSuite struct {
Expand Down
85 changes: 53 additions & 32 deletions types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package types

import (
"github.com/pingcap/errors"
"github.com/pingcap/parser/mysql"
"github.com/pingcap/parser/terror"
parser_types "github.com/pingcap/parser/types"
Expand Down Expand Up @@ -57,27 +58,45 @@ var (
// ErrWarnDataOutOfRange is returned when the value in a numeric column that is outside the permissible range of the column data type.
// See https://dev.mysql.com/doc/refman/5.5/en/out-of-range-and-overflow.html for details
ErrWarnDataOutOfRange = terror.ClassTypes.New(codeDataOutOfRange, mysql.MySQLErrName[mysql.ErrWarnDataOutOfRange])
// ErrDuplicatedValueInType is returned when enum column has duplicated value.
ErrDuplicatedValueInType = terror.ClassTypes.New(codeDuplicatedValueInType, mysql.MySQLErrName[mysql.ErrDuplicatedValueInType])
// ErrDatetimeFunctionOverflow is returned when the calculation in datetime function cause overflow.
ErrDatetimeFunctionOverflow = terror.ClassTypes.New(codeDatetimeFunctionOverflow, mysql.MySQLErrName[mysql.ErrDatetimeFunctionOverflow])
// ErrInvalidTimeFormat is returned when the time format is not correct.
ErrInvalidTimeFormat = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, "invalid time format: '%v'")
// ErrInvalidWeekModeFormat is returned when the week mode is wrong.
ErrInvalidWeekModeFormat = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, "invalid week mode format: '%v'")
// ErrInvalidYearFormat is returned when the input is not a valid year format.
ErrInvalidYearFormat = errors.New("invalid year format")
// ErrInvalidYear is returned when the input value is not a valid year.
ErrInvalidYear = errors.New("invalid year")
// ErrIncorrectDatetimeValue is returned when the input is not valid date time value.
ErrIncorrectDatetimeValue = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, "Incorrect datetime value: '%s'")
// ErrTruncatedWrongValue is returned then
ErrTruncatedWrongValue = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, mysql.MySQLErrName[mysql.ErrTruncatedWrongValue])
)

const (
codeBadNumber terror.ErrCode = 1

codeDataTooLong = terror.ErrCode(mysql.ErrDataTooLong)
codeIllegalValueForType = terror.ErrCode(mysql.ErrIllegalValueForType)
codeTruncated = terror.ErrCode(mysql.WarnDataTruncated)
codeOverflow = terror.ErrCode(mysql.ErrDataOutOfRange)
codeDivByZero = terror.ErrCode(mysql.ErrDivisionByZero)
codeTooBigDisplayWidth = terror.ErrCode(mysql.ErrTooBigDisplaywidth)
codeTooBigFieldLength = terror.ErrCode(mysql.ErrTooBigFieldlength)
codeTooBigSet = terror.ErrCode(mysql.ErrTooBigSet)
codeTooBigScale = terror.ErrCode(mysql.ErrTooBigScale)
codeTooBigPrecision = terror.ErrCode(mysql.ErrTooBigPrecision)
codeWrongFieldSpec = terror.ErrCode(mysql.ErrWrongFieldSpec)
codeTruncatedWrongValue = terror.ErrCode(mysql.ErrTruncatedWrongValue)
codeUnknown = terror.ErrCode(mysql.ErrUnknown)
codeInvalidDefault = terror.ErrCode(mysql.ErrInvalidDefault)
codeMBiggerThanD = terror.ErrCode(mysql.ErrMBiggerThanD)
codeDataOutOfRange = terror.ErrCode(mysql.ErrWarnDataOutOfRange)
codeDataTooLong = terror.ErrCode(mysql.ErrDataTooLong)
codeIllegalValueForType = terror.ErrCode(mysql.ErrIllegalValueForType)
codeTruncated = terror.ErrCode(mysql.WarnDataTruncated)
codeOverflow = terror.ErrCode(mysql.ErrDataOutOfRange)
codeDivByZero = terror.ErrCode(mysql.ErrDivisionByZero)
codeTooBigDisplayWidth = terror.ErrCode(mysql.ErrTooBigDisplaywidth)
codeTooBigFieldLength = terror.ErrCode(mysql.ErrTooBigFieldlength)
codeTooBigSet = terror.ErrCode(mysql.ErrTooBigSet)
codeTooBigScale = terror.ErrCode(mysql.ErrTooBigScale)
codeTooBigPrecision = terror.ErrCode(mysql.ErrTooBigPrecision)
codeWrongFieldSpec = terror.ErrCode(mysql.ErrWrongFieldSpec)
codeTruncatedWrongValue = terror.ErrCode(mysql.ErrTruncatedWrongValue)
codeUnknown = terror.ErrCode(mysql.ErrUnknown)
codeInvalidDefault = terror.ErrCode(mysql.ErrInvalidDefault)
codeMBiggerThanD = terror.ErrCode(mysql.ErrMBiggerThanD)
codeDataOutOfRange = terror.ErrCode(mysql.ErrWarnDataOutOfRange)
codeDuplicatedValueInType = terror.ErrCode(mysql.ErrDuplicatedValueInType)
codeDatetimeFunctionOverflow = terror.ErrCode(mysql.ErrDatetimeFunctionOverflow)
)

var (
Expand All @@ -89,22 +108,24 @@ var (

func init() {
typesMySQLErrCodes := map[terror.ErrCode]uint16{
codeDataTooLong: mysql.ErrDataTooLong,
codeIllegalValueForType: mysql.ErrIllegalValueForType,
codeTruncated: mysql.WarnDataTruncated,
codeOverflow: mysql.ErrDataOutOfRange,
codeDivByZero: mysql.ErrDivisionByZero,
codeTooBigDisplayWidth: mysql.ErrTooBigDisplaywidth,
codeTooBigFieldLength: mysql.ErrTooBigFieldlength,
codeTooBigSet: mysql.ErrTooBigSet,
codeTooBigScale: mysql.ErrTooBigScale,
codeTooBigPrecision: mysql.ErrTooBigPrecision,
codeWrongFieldSpec: mysql.ErrWrongFieldSpec,
codeTruncatedWrongValue: mysql.ErrTruncatedWrongValue,
codeUnknown: mysql.ErrUnknown,
codeInvalidDefault: mysql.ErrInvalidDefault,
codeMBiggerThanD: mysql.ErrMBiggerThanD,
codeDataOutOfRange: mysql.ErrWarnDataOutOfRange,
codeDataTooLong: mysql.ErrDataTooLong,
codeIllegalValueForType: mysql.ErrIllegalValueForType,
codeTruncated: mysql.WarnDataTruncated,
codeOverflow: mysql.ErrDataOutOfRange,
codeDivByZero: mysql.ErrDivisionByZero,
codeTooBigDisplayWidth: mysql.ErrTooBigDisplaywidth,
codeTooBigFieldLength: mysql.ErrTooBigFieldlength,
codeTooBigSet: mysql.ErrTooBigSet,
codeTooBigScale: mysql.ErrTooBigScale,
codeTooBigPrecision: mysql.ErrTooBigPrecision,
codeWrongFieldSpec: mysql.ErrWrongFieldSpec,
codeTruncatedWrongValue: mysql.ErrTruncatedWrongValue,
codeUnknown: mysql.ErrUnknown,
codeInvalidDefault: mysql.ErrInvalidDefault,
codeMBiggerThanD: mysql.ErrMBiggerThanD,
codeDataOutOfRange: mysql.ErrWarnDataOutOfRange,
codeDuplicatedValueInType: mysql.ErrDuplicatedValueInType,
codeDatetimeFunctionOverflow: mysql.ErrDatetimeFunctionOverflow,
}
terror.ErrClassToMySQLCodes[terror.ClassTypes] = typesMySQLErrCodes
}
Loading

0 comments on commit 7b8e174

Please sign in to comment.