Skip to content

Commit 6a3a73a

Browse files
winorostiancaiamao
authored andcommitted
*: add_date can return mysql.Time (#9830)
1 parent 373317f commit 6a3a73a

File tree

8 files changed

+497
-75
lines changed

8 files changed

+497
-75
lines changed

expression/builtin_time.go

Lines changed: 130 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2748,6 +2748,18 @@ func (du *baseDateArithmitical) add(ctx sessionctx.Context, date types.Time, int
27482748
return date, false, nil
27492749
}
27502750

2751+
func (du *baseDateArithmitical) addDuration(ctx sessionctx.Context, d types.Duration, interval string, unit string) (types.Duration, bool, error) {
2752+
dur, err := types.ExtractDurationValue(unit, interval)
2753+
if err != nil {
2754+
return types.ZeroDuration, true, handleInvalidTimeError(ctx, err)
2755+
}
2756+
retDur, err := d.Add(dur)
2757+
if err != nil {
2758+
return types.ZeroDuration, true, err
2759+
}
2760+
return retDur, false, nil
2761+
}
2762+
27512763
func (du *baseDateArithmitical) sub(ctx sessionctx.Context, date types.Time, interval string, unit string) (types.Time, bool, error) {
27522764
year, month, day, nano, err := types.ParseDurationValue(unit, interval)
27532765
if err := handleInvalidTimeError(ctx, err); err != nil {
@@ -2791,7 +2803,7 @@ func (c *addDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres
27912803
}
27922804

27932805
dateEvalTp := args[0].GetType().EvalType()
2794-
if dateEvalTp != types.ETString && dateEvalTp != types.ETInt {
2806+
if dateEvalTp != types.ETString && dateEvalTp != types.ETInt && dateEvalTp != types.ETDuration {
27952807
dateEvalTp = types.ETDatetime
27962808
}
27972809

@@ -2801,8 +2813,35 @@ func (c *addDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres
28012813
}
28022814

28032815
argTps := []types.EvalType{dateEvalTp, intervalEvalTp, types.ETString}
2804-
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETDatetime, argTps...)
2805-
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDatetimeFullWidth, types.UnspecifiedLength
2816+
var bf baseBuiltinFunc
2817+
if dateEvalTp == types.ETDuration {
2818+
unit, _, err := args[2].EvalString(ctx, chunk.Row{})
2819+
if err != nil {
2820+
return nil, err
2821+
}
2822+
internalFsp := 0
2823+
switch unit {
2824+
// If the unit has micro second, then the fsp must be the MaxFsp.
2825+
case "MICROSECOND", "SECOND_MICROSECOND", "MINUTE_MICROSECOND", "HOUR_MICROSECOND", "DAY_MICROSECOND":
2826+
internalFsp = types.MaxFsp
2827+
// If the unit is second, the fsp is related with the arg[1]'s.
2828+
case "SECOND":
2829+
internalFsp = types.MaxFsp
2830+
if intervalEvalTp != types.ETString {
2831+
internalFsp = mathutil.Min(args[1].GetType().Decimal, types.MaxFsp)
2832+
}
2833+
// Otherwise, the fsp should be 0.
2834+
}
2835+
bf = newBaseBuiltinFuncWithTp(ctx, args, types.ETDuration, argTps...)
2836+
arg0Dec, err := getExpressionFsp(ctx, args[0])
2837+
if err != nil {
2838+
return nil, err
2839+
}
2840+
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDurationWidthWithFsp, mathutil.Max(arg0Dec, internalFsp)
2841+
} else {
2842+
bf = newBaseBuiltinFuncWithTp(ctx, args, types.ETDatetime, argTps...)
2843+
bf.tp.Flen, bf.tp.Decimal = mysql.MaxDatetimeFullWidth, types.UnspecifiedLength
2844+
}
28062845

28072846
switch {
28082847
case dateEvalTp == types.ETString && intervalEvalTp == types.ETString:
@@ -2865,6 +2904,21 @@ func (c *addDateFunctionClass) getFunction(ctx sessionctx.Context, args []Expres
28652904
baseBuiltinFunc: bf,
28662905
baseDateArithmitical: newDateArighmeticalUtil(),
28672906
}
2907+
case dateEvalTp == types.ETDuration && intervalEvalTp == types.ETString:
2908+
sig = &builtinAddDateDurationStringSig{
2909+
baseBuiltinFunc: bf,
2910+
baseDateArithmitical: newDateArighmeticalUtil(),
2911+
}
2912+
case dateEvalTp == types.ETDuration && intervalEvalTp == types.ETInt:
2913+
sig = &builtinAddDateDurationIntSig{
2914+
baseBuiltinFunc: bf,
2915+
baseDateArithmitical: newDateArighmeticalUtil(),
2916+
}
2917+
case dateEvalTp == types.ETDuration && intervalEvalTp == types.ETDecimal:
2918+
sig = &builtinAddDateDurationDecimalSig{
2919+
baseBuiltinFunc: bf,
2920+
baseDateArithmitical: newDateArighmeticalUtil(),
2921+
}
28682922
}
28692923
return sig, nil
28702924
}
@@ -3265,6 +3319,79 @@ func (b *builtinAddDateDatetimeDecimalSig) evalTime(row chunk.Row) (types.Time,
32653319
return result, isNull || err != nil, err
32663320
}
32673321

3322+
type builtinAddDateDurationStringSig struct {
3323+
baseBuiltinFunc
3324+
baseDateArithmitical
3325+
}
3326+
3327+
func (b *builtinAddDateDurationStringSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
3328+
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
3329+
if isNull || err != nil {
3330+
return types.ZeroDuration, true, err
3331+
}
3332+
3333+
dur, isNull, err := b.args[0].EvalDuration(b.ctx, row)
3334+
if isNull || err != nil {
3335+
return types.ZeroDuration, true, err
3336+
}
3337+
3338+
interval, isNull, err := b.getIntervalFromString(b.ctx, b.args, row, unit)
3339+
if isNull || err != nil {
3340+
return types.ZeroDuration, true, err
3341+
}
3342+
3343+
result, isNull, err := b.addDuration(b.ctx, dur, interval, unit)
3344+
return result, isNull || err != nil, err
3345+
}
3346+
3347+
type builtinAddDateDurationIntSig struct {
3348+
baseBuiltinFunc
3349+
baseDateArithmitical
3350+
}
3351+
3352+
func (b *builtinAddDateDurationIntSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
3353+
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
3354+
if isNull || err != nil {
3355+
return types.ZeroDuration, true, err
3356+
}
3357+
3358+
dur, isNull, err := b.args[0].EvalDuration(b.ctx, row)
3359+
if isNull || err != nil {
3360+
return types.ZeroDuration, true, err
3361+
}
3362+
interval, isNull, err := b.getIntervalFromInt(b.ctx, b.args, row, unit)
3363+
if isNull || err != nil {
3364+
return types.ZeroDuration, true, err
3365+
}
3366+
3367+
result, isNull, err := b.addDuration(b.ctx, dur, interval, unit)
3368+
return result, isNull || err != nil, err
3369+
}
3370+
3371+
type builtinAddDateDurationDecimalSig struct {
3372+
baseBuiltinFunc
3373+
baseDateArithmitical
3374+
}
3375+
3376+
func (b *builtinAddDateDurationDecimalSig) evalDuration(row chunk.Row) (types.Duration, bool, error) {
3377+
unit, isNull, err := b.args[2].EvalString(b.ctx, row)
3378+
if isNull || err != nil {
3379+
return types.ZeroDuration, true, err
3380+
}
3381+
3382+
dur, isNull, err := b.args[0].EvalDuration(b.ctx, row)
3383+
if isNull || err != nil {
3384+
return types.ZeroDuration, true, err
3385+
}
3386+
interval, isNull, err := b.getIntervalFromDecimal(b.ctx, b.args, row, unit)
3387+
if isNull || err != nil {
3388+
return types.ZeroDuration, true, err
3389+
}
3390+
3391+
result, isNull, err := b.addDuration(b.ctx, dur, interval, unit)
3392+
return result, isNull || err != nil, err
3393+
}
3394+
32683395
type subDateFunctionClass struct {
32693396
baseFunctionClass
32703397
}

expression/builtin_time_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1764,6 +1764,33 @@ func (s *testEvaluatorSuite) TestDateArithFuncs(c *C) {
17641764
c.Assert(err, IsNil)
17651765
c.Assert(v.GetMysqlTime().String(), Equals, test.expected)
17661766
}
1767+
testDurations := []struct {
1768+
dur string
1769+
fsp int
1770+
unit string
1771+
format interface{}
1772+
expected string
1773+
}{
1774+
{
1775+
dur: "00:00:00",
1776+
fsp: 0,
1777+
unit: "MICROSECOND",
1778+
format: "100",
1779+
expected: "00:00:00.000100",
1780+
},
1781+
}
1782+
for _, tt := range testDurations {
1783+
dur, _, ok, err := types.StrToDuration(nil, tt.dur, tt.fsp)
1784+
c.Assert(err, IsNil)
1785+
c.Assert(ok, IsTrue)
1786+
args = types.MakeDatums(dur, tt.format, tt.unit)
1787+
f, err = fcAdd.getFunction(s.ctx, s.datumsToConstants(args))
1788+
c.Assert(err, IsNil)
1789+
c.Assert(f, NotNil)
1790+
v, err = evalBuiltinFunc(f, chunk.Row{})
1791+
c.Assert(err, IsNil)
1792+
c.Assert(v.GetMysqlDuration().String(), Equals, tt.expected)
1793+
}
17671794
}
17681795

17691796
func (s *testEvaluatorSuite) TestTimestamp(c *C) {

types/const_test.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ package types_test
1616
import (
1717
"context"
1818
"flag"
19-
"testing"
2019

2120
. "github.com/pingcap/check"
2221
"github.com/pingcap/parser"
@@ -30,11 +29,6 @@ import (
3029
"github.com/pingcap/tidb/util/testleak"
3130
)
3231

33-
func TestT(t *testing.T) {
34-
CustomVerboseFlag = true
35-
TestingT(t)
36-
}
37-
3832
var _ = Suite(&testMySQLConstSuite{})
3933

4034
type testMySQLConstSuite struct {

types/errors.go

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
package types
1515

1616
import (
17+
"github.com/pingcap/errors"
1718
"github.com/pingcap/parser/mysql"
1819
"github.com/pingcap/parser/terror"
1920
parser_types "github.com/pingcap/parser/types"
@@ -59,28 +60,43 @@ var (
5960
ErrWarnDataOutOfRange = terror.ClassTypes.New(codeDataOutOfRange, mysql.MySQLErrName[mysql.ErrWarnDataOutOfRange])
6061
// ErrDuplicatedValueInType is returned when enum column has duplicated value.
6162
ErrDuplicatedValueInType = terror.ClassTypes.New(codeDuplicatedValueInType, mysql.MySQLErrName[mysql.ErrDuplicatedValueInType])
63+
// ErrDatetimeFunctionOverflow is returned when the calculation in datetime function cause overflow.
64+
ErrDatetimeFunctionOverflow = terror.ClassTypes.New(codeDatetimeFunctionOverflow, mysql.MySQLErrName[mysql.ErrDatetimeFunctionOverflow])
65+
// ErrInvalidTimeFormat is returned when the time format is not correct.
66+
ErrInvalidTimeFormat = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, "invalid time format: '%v'")
67+
// ErrInvalidWeekModeFormat is returned when the week mode is wrong.
68+
ErrInvalidWeekModeFormat = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, "invalid week mode format: '%v'")
69+
// ErrInvalidYearFormat is returned when the input is not a valid year format.
70+
ErrInvalidYearFormat = errors.New("invalid year format")
71+
// ErrInvalidYear is returned when the input value is not a valid year.
72+
ErrInvalidYear = errors.New("invalid year")
73+
// ErrIncorrectDatetimeValue is returned when the input is not valid date time value.
74+
ErrIncorrectDatetimeValue = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, "Incorrect datetime value: '%s'")
75+
// ErrTruncatedWrongValue is returned then
76+
ErrTruncatedWrongValue = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, mysql.MySQLErrName[mysql.ErrTruncatedWrongValue])
6277
)
6378

6479
const (
6580
codeBadNumber terror.ErrCode = 1
6681

67-
codeDataTooLong = terror.ErrCode(mysql.ErrDataTooLong)
68-
codeIllegalValueForType = terror.ErrCode(mysql.ErrIllegalValueForType)
69-
codeTruncated = terror.ErrCode(mysql.WarnDataTruncated)
70-
codeOverflow = terror.ErrCode(mysql.ErrDataOutOfRange)
71-
codeDivByZero = terror.ErrCode(mysql.ErrDivisionByZero)
72-
codeTooBigDisplayWidth = terror.ErrCode(mysql.ErrTooBigDisplaywidth)
73-
codeTooBigFieldLength = terror.ErrCode(mysql.ErrTooBigFieldlength)
74-
codeTooBigSet = terror.ErrCode(mysql.ErrTooBigSet)
75-
codeTooBigScale = terror.ErrCode(mysql.ErrTooBigScale)
76-
codeTooBigPrecision = terror.ErrCode(mysql.ErrTooBigPrecision)
77-
codeWrongFieldSpec = terror.ErrCode(mysql.ErrWrongFieldSpec)
78-
codeTruncatedWrongValue = terror.ErrCode(mysql.ErrTruncatedWrongValue)
79-
codeUnknown = terror.ErrCode(mysql.ErrUnknown)
80-
codeInvalidDefault = terror.ErrCode(mysql.ErrInvalidDefault)
81-
codeMBiggerThanD = terror.ErrCode(mysql.ErrMBiggerThanD)
82-
codeDataOutOfRange = terror.ErrCode(mysql.ErrWarnDataOutOfRange)
83-
codeDuplicatedValueInType = terror.ErrCode(mysql.ErrDuplicatedValueInType)
82+
codeDataTooLong = terror.ErrCode(mysql.ErrDataTooLong)
83+
codeIllegalValueForType = terror.ErrCode(mysql.ErrIllegalValueForType)
84+
codeTruncated = terror.ErrCode(mysql.WarnDataTruncated)
85+
codeOverflow = terror.ErrCode(mysql.ErrDataOutOfRange)
86+
codeDivByZero = terror.ErrCode(mysql.ErrDivisionByZero)
87+
codeTooBigDisplayWidth = terror.ErrCode(mysql.ErrTooBigDisplaywidth)
88+
codeTooBigFieldLength = terror.ErrCode(mysql.ErrTooBigFieldlength)
89+
codeTooBigSet = terror.ErrCode(mysql.ErrTooBigSet)
90+
codeTooBigScale = terror.ErrCode(mysql.ErrTooBigScale)
91+
codeTooBigPrecision = terror.ErrCode(mysql.ErrTooBigPrecision)
92+
codeWrongFieldSpec = terror.ErrCode(mysql.ErrWrongFieldSpec)
93+
codeTruncatedWrongValue = terror.ErrCode(mysql.ErrTruncatedWrongValue)
94+
codeUnknown = terror.ErrCode(mysql.ErrUnknown)
95+
codeInvalidDefault = terror.ErrCode(mysql.ErrInvalidDefault)
96+
codeMBiggerThanD = terror.ErrCode(mysql.ErrMBiggerThanD)
97+
codeDataOutOfRange = terror.ErrCode(mysql.ErrWarnDataOutOfRange)
98+
codeDuplicatedValueInType = terror.ErrCode(mysql.ErrDuplicatedValueInType)
99+
codeDatetimeFunctionOverflow = terror.ErrCode(mysql.ErrDatetimeFunctionOverflow)
84100
)
85101

86102
var (
@@ -92,23 +108,24 @@ var (
92108

93109
func init() {
94110
typesMySQLErrCodes := map[terror.ErrCode]uint16{
95-
codeDataTooLong: mysql.ErrDataTooLong,
96-
codeIllegalValueForType: mysql.ErrIllegalValueForType,
97-
codeTruncated: mysql.WarnDataTruncated,
98-
codeOverflow: mysql.ErrDataOutOfRange,
99-
codeDivByZero: mysql.ErrDivisionByZero,
100-
codeTooBigDisplayWidth: mysql.ErrTooBigDisplaywidth,
101-
codeTooBigFieldLength: mysql.ErrTooBigFieldlength,
102-
codeTooBigSet: mysql.ErrTooBigSet,
103-
codeTooBigScale: mysql.ErrTooBigScale,
104-
codeTooBigPrecision: mysql.ErrTooBigPrecision,
105-
codeWrongFieldSpec: mysql.ErrWrongFieldSpec,
106-
codeTruncatedWrongValue: mysql.ErrTruncatedWrongValue,
107-
codeUnknown: mysql.ErrUnknown,
108-
codeInvalidDefault: mysql.ErrInvalidDefault,
109-
codeMBiggerThanD: mysql.ErrMBiggerThanD,
110-
codeDataOutOfRange: mysql.ErrWarnDataOutOfRange,
111-
codeDuplicatedValueInType: mysql.ErrDuplicatedValueInType,
111+
codeDataTooLong: mysql.ErrDataTooLong,
112+
codeIllegalValueForType: mysql.ErrIllegalValueForType,
113+
codeTruncated: mysql.WarnDataTruncated,
114+
codeOverflow: mysql.ErrDataOutOfRange,
115+
codeDivByZero: mysql.ErrDivisionByZero,
116+
codeTooBigDisplayWidth: mysql.ErrTooBigDisplaywidth,
117+
codeTooBigFieldLength: mysql.ErrTooBigFieldlength,
118+
codeTooBigSet: mysql.ErrTooBigSet,
119+
codeTooBigScale: mysql.ErrTooBigScale,
120+
codeTooBigPrecision: mysql.ErrTooBigPrecision,
121+
codeWrongFieldSpec: mysql.ErrWrongFieldSpec,
122+
codeTruncatedWrongValue: mysql.ErrTruncatedWrongValue,
123+
codeUnknown: mysql.ErrUnknown,
124+
codeInvalidDefault: mysql.ErrInvalidDefault,
125+
codeMBiggerThanD: mysql.ErrMBiggerThanD,
126+
codeDataOutOfRange: mysql.ErrWarnDataOutOfRange,
127+
codeDuplicatedValueInType: mysql.ErrDuplicatedValueInType,
128+
codeDatetimeFunctionOverflow: mysql.ErrDatetimeFunctionOverflow,
112129
}
113130
terror.ErrClassToMySQLCodes[terror.ClassTypes] = typesMySQLErrCodes
114131
}

types/fsp_test.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,10 @@ package types
1515

1616
import (
1717
"strconv"
18-
"testing"
1918

2019
. "github.com/pingcap/check"
2120
)
2221

23-
func Test(t *testing.T) {
24-
TestingT(t)
25-
}
26-
2722
var _ = Suite(&FspTest{})
2823

2924
type FspTest struct{}

0 commit comments

Comments
 (0)