Skip to content

Commit

Permalink
expression: rewrite builtin function: UTC_TIME (#4304)
Browse files Browse the repository at this point in the history
  • Loading branch information
spongedu authored and hanfei1991 committed Aug 24, 2017
1 parent a5acbd4 commit 7cf42f5
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 21 deletions.
87 changes: 68 additions & 19 deletions expression/builtin_time.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ var (
_ builtinFunc = &builtinTimestampAddSig{}
_ builtinFunc = &builtinToDaysSig{}
_ builtinFunc = &builtinToSecondsSig{}
_ builtinFunc = &builtinUTCTimeSig{}
_ builtinFunc = &builtinUTCTimeWithArgSig{}
_ builtinFunc = &builtinUTCTimeWithoutArgSig{}
_ builtinFunc = &builtinTimestampSig{}
_ builtinFunc = &builtinLastDaySig{}
)
Expand Down Expand Up @@ -3091,36 +3092,84 @@ type utcTimeFunctionClass struct {
baseFunctionClass
}

func (c *utcTimeFunctionClass) getFlenAndDecimal4UTCTime(sc *variable.StatementContext, args []Expression) (flen, decimal int) {
if len(args) == 0 {
flen, decimal = 8, 0
return
}
if constant, ok := args[0].(*Constant); ok {
fsp, isNull, err := constant.EvalInt(nil, sc)
if isNull || err != nil || fsp > int64(types.MaxFsp) {
decimal = types.MaxFsp
} else if fsp < int64(types.MinFsp) {
decimal = types.MinFsp
} else {
decimal = int(fsp)
}
}
if decimal > 0 {
flen = 8 + 1 + decimal
} else {
flen = 8
}
return flen, decimal
}

func (c *utcTimeFunctionClass) getFunction(args []Expression, ctx context.Context) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(err)
}
sig := &builtinUTCTimeSig{newBaseBuiltinFunc(args, ctx)}
argTps := make([]evalTp, 0, 1)
if len(args) == 1 {
argTps = append(argTps, tpInt)
}
bf, err := newBaseBuiltinFuncWithTp(args, ctx, tpDuration, argTps...)
if err != nil {
return nil, errors.Trace(err)
}
bf.tp.Flen, bf.tp.Decimal = c.getFlenAndDecimal4UTCTime(bf.ctx.GetSessionVars().StmtCtx, args)

var sig builtinFunc
if len(args) == 1 {
sig = &builtinUTCTimeWithArgSig{baseDurationBuiltinFunc{bf}}
} else {
sig = &builtinUTCTimeWithoutArgSig{baseDurationBuiltinFunc{bf}}
}
return sig.setSelf(sig), nil
}

type builtinUTCTimeSig struct {
baseBuiltinFunc
type builtinUTCTimeWithoutArgSig struct {
baseDurationBuiltinFunc
}

// eval evals a builtinUTCTimeSig.
// evalDuration evals a builtinUTCTimeWithoutArgSig.
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_utc-time
func (b *builtinUTCTimeSig) eval(row []types.Datum) (d types.Datum, err error) {
const utctimeFormat = "15:04:05.000000"
args, err := b.evalArgs(row)
if err != nil {
return d, errors.Trace(err)
func (b *builtinUTCTimeWithoutArgSig) evalDuration(row []types.Datum) (types.Duration, bool, error) {
// the types.ParseDuration here would never fail, so the err returned can be ignored.
v, _ := types.ParseDuration(time.Now().UTC().Format("00:00:00"), 0)
return v, false, nil
}

type builtinUTCTimeWithArgSig struct {
baseDurationBuiltinFunc
}

// evalDuration evals a builtinUTCTimeWithArgSig.
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_utc-time
func (b *builtinUTCTimeWithArgSig) evalDuration(row []types.Datum) (types.Duration, bool, error) {
fsp, isNull, err := b.args[0].EvalInt(row, b.ctx.GetSessionVars().StmtCtx)
if isNull || err != nil {
return types.Duration{}, isNull, errors.Trace(err)
}
fsp := 0
sc := b.ctx.GetSessionVars().StmtCtx
if len(args) == 1 && !args[0].IsNull() {
if fsp, err = checkFsp(sc, args[0]); err != nil {
d.SetNull()
return d, errors.Trace(err)
}
if fsp > int64(types.MaxFsp) {
return types.Duration{}, true, errors.Errorf("Too-big precision %v specified for 'utc_time'. Maximum is %v.", fsp, types.MaxFsp)
}
d.SetString(time.Now().UTC().Format(utctimeFormat))
return convertToDuration(b.ctx.GetSessionVars().StmtCtx, d, fsp)
if fsp < int64(types.MinFsp) {
return types.Duration{}, true, errors.Errorf("Invalid negative %d specified, must in [0, 6].", fsp)
}
// the types.ParseDuration here would never fail, so the err returned can be ignored.
v, _ := types.ParseDuration(time.Now().UTC().Format("00:00:00.000000"), int(fsp))
return v, false, nil
}

type lastDayFunctionClass struct {
Expand Down
13 changes: 11 additions & 2 deletions expression/builtin_time_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1045,18 +1045,19 @@ func (s *testEvaluatorSuite) TestCurrentTime(c *C) {
func (s *testEvaluatorSuite) TestUTCTime(c *C) {
defer testleak.AfterTest(c)()

tfStr := "15:04:05"
last := time.Now().UTC()
tfStr := "00:00:00"
fc := funcs[ast.UTCTime]

tests := []struct {
param interface{}
expect int
}{{nil, 8}, {0, 8}, {3, 12}, {6, 15}, {-1, 0}, {7, 0}}
}{{0, 8}, {3, 12}, {6, 15}, {-1, 0}, {7, 0}}

for _, test := range tests {
f, err := fc.getFunction(datumsToConstants(types.MakeDatums(test.param)), s.ctx)
c.Assert(err, IsNil)
c.Assert(f.isDeterministic(), IsTrue)
v, err := f.eval(nil)
if test.expect > 0 {
c.Assert(err, IsNil)
Expand All @@ -1067,6 +1068,14 @@ func (s *testEvaluatorSuite) TestUTCTime(c *C) {
c.Assert(err, NotNil)
}
}

f, err := fc.getFunction(make([]Expression, 0, 0), s.ctx)
c.Assert(err, IsNil)
c.Assert(f.isDeterministic(), IsTrue)
v, err := f.eval(nil)
n := v.GetMysqlDuration()
c.Assert(n.String(), HasLen, 8)
c.Assert(n.String(), GreaterEqual, last.Format(tfStr))
}

func (s *testEvaluatorSuite) TestUTCDate(c *C) {
Expand Down
10 changes: 10 additions & 0 deletions plan/typeinfer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1358,6 +1358,16 @@ func (s *testPlanSuite) createTestCase4TimeFuncs() []typeInferTestCase {
{"utc_timestamp(6) ", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 6},
{"utc_timestamp(7) ", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 6},

{"utc_time() ", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 8, 0},
{"utc_time(0) ", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 8, 0},
{"utc_time(1) ", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 10, 1},
{"utc_time(2) ", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 11, 2},
{"utc_time(3) ", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 12, 3},
{"utc_time(4) ", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 13, 4},
{"utc_time(5) ", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 14, 5},
{"utc_time(6) ", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 15, 6},
{"utc_time(7) ", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 15, 6},

{"utc_date() ", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 10, 0},
{"curdate()", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 10, 0},
{"sysdate(4)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 19, 0},
Expand Down

0 comments on commit 7cf42f5

Please sign in to comment.