-
Notifications
You must be signed in to change notification settings - Fork 5.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
expression: rewrite builtin function MAKETIME #4396
Changes from 5 commits
f6d2aa4
06aec63
38a95e2
398c749
edfd0ec
b4a91d2
655d7b7
ab01731
e0140a3
b0016dd
4544ae4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2658,34 +2658,54 @@ func (c *makeTimeFunctionClass) getFunction(ctx context.Context, args []Expressi | |
if err := c.verifyArgs(args); err != nil { | ||
return nil, errors.Trace(err) | ||
} | ||
sig := &builtinMakeTimeSig{newBaseBuiltinFunc(args, ctx)} | ||
evelTp, flen, decimal := fieldTp2EvalTp(args[2].GetType()), 10, 0 | ||
switch evelTp { | ||
case tpInt: | ||
case tpReal, tpDecimal: | ||
decimal = args[2].GetType().Decimal | ||
if decimal > 6 || decimal == types.UnspecifiedLength { | ||
decimal = 6 | ||
} | ||
if decimal > 0 { | ||
flen += 1 + decimal | ||
} | ||
default: | ||
flen, decimal = 17, 6 | ||
} | ||
bf := newBaseBuiltinFuncWithTp(args, ctx, tpDuration, tpInt, tpInt, tpReal) | ||
bf.tp.Flen, bf.tp.Decimal = flen, decimal | ||
sig := &builtinMakeTimeSig{baseDurationBuiltinFunc{bf}} | ||
return sig.setSelf(sig), nil | ||
} | ||
|
||
type builtinMakeTimeSig struct { | ||
baseBuiltinFunc | ||
baseDurationBuiltinFunc | ||
} | ||
|
||
// eval evals a builtinMakeTimeSig. | ||
// evalDuration evals a builtinMakeTimeIntSig. | ||
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_maketime | ||
func (b *builtinMakeTimeSig) eval(row []types.Datum) (d types.Datum, err error) { | ||
args, err := b.evalArgs(row) | ||
if err != nil { | ||
return d, errors.Trace(err) | ||
func (b *builtinMakeTimeSig) evalDuration(row []types.Datum) (types.Duration, bool, error) { | ||
sc, dur := b.ctx.GetSessionVars().StmtCtx, types.ZeroDuration | ||
dur.Fsp = types.MaxFsp | ||
hour, isNull, err := b.args[0].EvalInt(row, sc) | ||
if isNull || err != nil { | ||
return dur, isNull, errors.Trace(handleInvalidTimeError(b.ctx, err)) | ||
} | ||
// MAKETIME(hour, minute, second) | ||
if args[0].IsNull() || args[1].IsNull() || args[2].IsNull() { | ||
return | ||
minute, isNull, err := b.args[1].EvalInt(row, sc) | ||
if isNull || err != nil { | ||
return dur, isNull, errors.Trace(handleInvalidTimeError(b.ctx, err)) | ||
} | ||
|
||
var ( | ||
hour int64 | ||
minute int64 | ||
second float64 | ||
overflow bool | ||
) | ||
sc := b.ctx.GetSessionVars().StmtCtx | ||
hour, _ = args[0].ToInt64(sc) | ||
if minute < 0 || minute >= 60 { | ||
return dur, true, nil | ||
} | ||
second, isNull, err := b.args[2].EvalReal(row, sc) | ||
if isNull || err != nil { | ||
return dur, isNull, errors.Trace(err) | ||
} | ||
if second < 0 || second >= 60 { | ||
return dur, true, nil | ||
} | ||
var overflow bool | ||
// MySQL TIME datatype: https://dev.mysql.com/doc/refman/5.7/en/time.html | ||
// ranges from '-838:59:59.000000' to '838:59:59.000000' | ||
if hour < -838 { | ||
|
@@ -2695,16 +2715,6 @@ func (b *builtinMakeTimeSig) eval(row []types.Datum) (d types.Datum, err error) | |
hour = 838 | ||
overflow = true | ||
} | ||
|
||
minute, _ = args[1].ToInt64(sc) | ||
if minute < 0 || minute >= 60 { | ||
return | ||
} | ||
|
||
second, _ = args[2].ToFloat64(sc) | ||
if second < 0 || second >= 60 { | ||
return | ||
} | ||
if hour == -838 || hour == 838 { | ||
if second > 59 { | ||
second = 59 | ||
|
@@ -2714,24 +2724,12 @@ func (b *builtinMakeTimeSig) eval(row []types.Datum) (d types.Datum, err error) | |
minute = 59 | ||
second = 59 | ||
} | ||
|
||
var dur types.Duration | ||
fsp := types.MaxFsp | ||
if args[2].Kind() != types.KindString { | ||
sec, _ := args[2].ToString() | ||
secs := strings.Split(sec, ".") | ||
if len(secs) <= 1 { | ||
fsp = 0 | ||
} else if len(secs[1]) < fsp { | ||
fsp = len(secs[1]) | ||
} | ||
} | ||
fsp := b.self.getRetTp().Decimal | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
dur, err = types.ParseDuration(fmt.Sprintf("%02d:%02d:%v", hour, minute, second), fsp) | ||
if err != nil { | ||
return d, errors.Trace(err) | ||
return dur, true, errors.Trace(err) | ||
} | ||
d.SetMysqlDuration(dur) | ||
return | ||
return dur, false, nil | ||
} | ||
|
||
type periodAddFunctionClass struct { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1615,37 +1615,35 @@ func (s *testEvaluatorSuite) TestMakeTime(c *C) { | |
{[]interface{}{12, 15, -30}, nil}, | ||
|
||
{[]interface{}{12, 15, "30.10"}, "12:15:30.100000"}, | ||
{[]interface{}{12, 15, "30.00"}, "12:15:30.000000"}, | ||
{[]interface{}{12, 15, 30.0000001}, "12:15:30.000000"}, | ||
{[]interface{}{12, 15, "30.20"}, "12:15:30.200000"}, | ||
{[]interface{}{12, 15, 30.3000001}, "12:15:30.300000"}, | ||
{[]interface{}{12, 15, 30.0000005}, "12:15:30.000001"}, | ||
{[]interface{}{"12", "15", 30.1}, "12:15:30.1"}, | ||
{[]interface{}{"12", "15", 30.1}, "12:15:30.100000"}, | ||
|
||
{[]interface{}{0, 58.4, 0}, "00:58:00"}, | ||
{[]interface{}{0, "58.4", 0}, "00:58:00"}, | ||
{[]interface{}{0, 58.5, 1}, "00:59:01"}, | ||
{[]interface{}{0, "58.5", 1}, "00:58:01"}, | ||
{[]interface{}{0, 59.5, 1}, nil}, | ||
{[]interface{}{0, "59.5", 1}, "00:59:01"}, | ||
{[]interface{}{0, 1, 59.1}, "00:01:59.1"}, | ||
{[]interface{}{0, 1, 59.1}, "00:01:59.100000"}, | ||
{[]interface{}{0, 1, "59.1"}, "00:01:59.100000"}, | ||
{[]interface{}{0, 1, 59.5}, "00:01:59.5"}, | ||
{[]interface{}{0, 1, 59.5}, "00:01:59.500000"}, | ||
{[]interface{}{0, 1, "59.5"}, "00:01:59.500000"}, | ||
{[]interface{}{23.5, 1, 10}, "24:01:10"}, | ||
{[]interface{}{"23.5", 1, 10}, "23:01:10"}, | ||
|
||
{[]interface{}{0, 0, 0}, "00:00:00"}, | ||
{[]interface{}{"", "", ""}, "00:00:00.000000"}, | ||
{[]interface{}{"h", "m", "s"}, "00:00:00.000000"}, | ||
|
||
{[]interface{}{837, 59, 59.1}, "837:59:59.1"}, | ||
{[]interface{}{838, 59, 59.1}, "838:59:59.0"}, | ||
{[]interface{}{-838, 59, 59.1}, "-838:59:59.0"}, | ||
{[]interface{}{837, 59, 59.1}, "837:59:59.100000"}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in mysql: MySQL > select maketime(837, 59, 59.1);
-------------------------+
| maketime(837, 59, 59.1) |
+-------------------------+
| 837:59:59.1 |
+-------------------------+
1 row in set (0.00 sec)
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep. You can see my integration tests.There's a similar case and the result is correct. Decimals goes wrong here because 'datumsToConstants' don't correctly set constants's decimal(all are set -1, which is not as expected). So here the tests equal to creating a table with a double column, and use it as the third parameter in MAKETIME There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
{[]interface{}{838, 59, 59.1}, "838:59:59.000000"}, | ||
{[]interface{}{-838, 59, 59.1}, "-838:59:59.000000"}, | ||
{[]interface{}{1000, 1, 1}, "838:59:59"}, | ||
{[]interface{}{-1000, 1, 1.23}, "-838:59:59.00"}, | ||
{[]interface{}{-1000, 1, 1.23}, "-838:59:59.000000"}, | ||
{[]interface{}{1000, 59.1, 1}, "838:59:59"}, | ||
{[]interface{}{1000, 59.5, 1}, nil}, | ||
{[]interface{}{1000, 1, 59.1}, "838:59:59.0"}, | ||
{[]interface{}{1000, 1, 59.5}, "838:59:59.0"}, | ||
{[]interface{}{1000, 1, 59.1}, "838:59:59.000000"}, | ||
{[]interface{}{1000, 1, 59.5}, "838:59:59.000000"}, | ||
|
||
{[]interface{}{12, 15, 60}, nil}, | ||
{[]interface{}{12, 15, "60"}, nil}, | ||
|
@@ -1663,6 +1661,7 @@ func (s *testEvaluatorSuite) TestMakeTime(c *C) { | |
for idx, t := range Dtbl { | ||
f, err := maketime.getFunction(s.ctx, datumsToConstants(t["Args"])) | ||
c.Assert(err, IsNil) | ||
c.Assert(f.canBeFolded(), IsTrue) | ||
got, err := f.eval(nil) | ||
c.Assert(err, IsNil) | ||
if t["Want"][0].Kind() == types.KindNull { | ||
|
@@ -1673,6 +1672,25 @@ func (s *testEvaluatorSuite) TestMakeTime(c *C) { | |
c.Assert(got.GetMysqlDuration().String(), Equals, want, Commentf("[%v] - args:%v", idx, t["Args"])) | ||
} | ||
} | ||
|
||
tbl = []struct { | ||
Args []interface{} | ||
Want interface{} | ||
}{ | ||
{[]interface{}{"", "", ""}, "00:00:00"}, | ||
{[]interface{}{"h", "m", "s"}, "00:00:00"}, | ||
} | ||
Dtbl = tblToDtbl(tbl) | ||
maketime = funcs[ast.MakeTime] | ||
for idx, t := range Dtbl { | ||
f, err := maketime.getFunction(s.ctx, datumsToConstants(t["Args"])) | ||
c.Assert(err, IsNil) | ||
c.Assert(f.canBeFolded(), IsTrue) | ||
got, err := f.eval(nil) | ||
c.Assert(err, NotNil) | ||
want, err := t["Want"][0].ToString() | ||
c.Assert(got.GetMysqlDuration().String(), Equals, want, Commentf("[%v] - args:%v", idx, t["Args"])) | ||
} | ||
} | ||
|
||
func (s *testEvaluatorSuite) TestQuarter(c *C) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/ evelTp/ evalTp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@XuHuaiyu Done