Skip to content
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: add builtinAddTimeSig #3290

Merged
merged 58 commits into from Jun 5, 2017
Merged

expression: add builtinAddTimeSig #3290

merged 58 commits into from Jun 5, 2017

Conversation

hawkingrei
Copy link
Member

No description provided.

@CLAassistant
Copy link

CLAassistant commented May 17, 2017

CLA assistant check
All committers have signed the CLA.

@shenli
Copy link
Member

shenli commented May 17, 2017

@hawkingrei Thanks for your PR!
Please sign the CLA.

Copy link
Member

@shenli shenli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add unit test and modify plan/typeinferer.go.

return d, errFunctionNotExists.GenByArgs("ADDTIME")
args, err := b.evalArgs(row)
if err != nil {
return types.Datum{}, errors.Trace(err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return d, errors.Trace(err)

@hanfei1991
Copy link
Member

any test cases?

@hawkingrei
Copy link
Member Author

I will complete the test case.

@shenli
Copy link
Member

shenli commented May 18, 2017

@hanfei1991 PTAL

return d, errors.Trace(err)
}

duration := args[1].GetMysqlDuration()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should convert it to duration

@hawkingrei
Copy link
Member Author

Grean all

Have some problem.

ADDTIME(expr1,expr2)
1、 when expr2 expression is “1 1:1:1.000002”, it gives a error "invalid time format")” .it happen in
utils/time.go:343
2、 what is the type of result in addtime ?
expr1 (datetime) + expr2(duration) return datetime?
expr1 (duration) + expr2(duration) return duration?
3、 ADDTIME(expr1,expr2) only support that expr1 is datetime expression ,not time

@zimulala zimulala added the contribution This PR is from a community contributor. label May 22, 2017
@shenli
Copy link
Member

shenli commented May 22, 2017

Is it the same behaviour with MySQL?

ADDTIME(expr1,expr2)
1、 when expr2 expression is “1 1:1:1.000002”, it gives a error "invalid time format")” .it happen in
utils/time.go:343

You can check MySQL's return type by using mysqlworkbench.

2、 what is the type of result in addtime ?
expr1 (datetime) + expr2(duration) return datetime?
expr1 (duration) + expr2(duration) return duration?

You can restrict the type of expr1.

3、 ADDTIME(expr1,expr2) only support that expr1 is datetime expression ,not time

@@ -1785,8 +1785,36 @@ type builtinAddTimeSig struct {

// eval evals a builtinAddTimeSig.
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_addtime
// ADDTIME(expr1,expr2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove line 1788,1789
it's useless which has been described in the URL.
and expr1 is a time or datetime expression, but expr2 is a time expression.

mysql> select addtime("12:00:01", 1);
+------------------------+
| addtime("12:00:01", 1) |
+------------------------+
| 12:00:02 |
+------------------------+

case mysql.TypeDatetime,mysql.TypeDate,mysql.TypeTimestamp:
tp = types.NewFieldType(mysql.TypeDatetime)
case mysql.TypeTime:
tp = types.NewFieldType(mysql.TypeVarString)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be mysql.TypeDuration

case ast.AddTime:
t := x.Args[0].GetType().Tp
switch t {
case mysql.TypeDatetime,mysql.TypeDate,mysql.TypeTimestamp:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

args[0].Tp == mysql.TypeDate should be contained in default
and the chs should be setted to 'utf8' in the default case.

for _, t := range tbl {
tmpInput := types.NewStringDatum(t.Input)
tmpInputDuration := types.NewStringDatum(t.InputDuration)
//tmpResult := types.NewStringDatum(t.result)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this line if it's useless.

@@ -367,6 +367,18 @@ func (v *typeInferrer) handleFuncCallExpr(x *ast.FuncCallExpr) {
tp.Charset, tp.Collate = types.DefaultCharsetForType(tp.Tp)
}
// number related
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this line to line 381

@@ -177,6 +177,8 @@ func (ts *testTypeInferrerSuite) TestInferType(c *C) {
{"weekday('2009-12-31 23:59:59.000010')", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag},
{"weekofyear('2009-12-31 23:59:59.000010')", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag},
{"yearweek('2009-12-31 23:59:59.000010')", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag},
{"addtime('2007-12-31 23:59:59.999999', '1 1:1:1.000002')", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'2009-12-31 23:59:59.000010' is a string but not a datetime,
you can use c_datetime (which can be found in line51).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thx,i will complete this function as soon as possible

InputDuration string
expect string
}{
//{"2007-12-31 23:59:59.999999", "1 1:1:1.000002", "2008-01-02 01:01:01.000001"},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which will TiDB get of these 4 cases?

if err != nil {
return d, errors.Trace(err)
}
arg1, err := types.ParseDuration(s, getFsp(s))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems the fsp of the result will always be types.MaxFsp if getFsp(s) >0;
and be types.MinFsp if getFsp(s) == 0

@hawkingrei
Copy link
Member Author

hawkingrei commented Jun 1, 2017

@XuHuaiyu
Fixed all.

Sorry. I forget to push some fixed code.

add some invalid cases for “aa: bb: cc”
IsDuration checks the format of string whether is Duration or not. but it doesn't check the invalid. invalid checking will be done by parseDuration.

@@ -1770,6 +1770,72 @@ func getTimeZone(ctx context.Context) *time.Location {
return ret
}

// isDuration returns a boolean indicating whether the str is the format of duration.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. s/is the format/matches the format
  2. add comment for the format that str should match.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@XuHuaiyu Use regular expression install of the old way.

c.Assert(result, Equals, t.expect)
}

result := isDuration("aa:bb:cc")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need the comparison of these two cases here.

@XuHuaiyu
Copy link
Contributor

XuHuaiyu commented Jun 1, 2017

PTAL
@coocood @tiancaiamao

@@ -1770,6 +1770,52 @@ func getTimeZone(ctx context.Context) *time.Location {
return ret
}

// isDuration returns a boolean indicating whether the str is the format of duration.
func isDuration(str string) bool {
const durationArgReg = `^(|[-]?)(|\d{1,2}\s)(\d{2,3}:\d{2}:\d{2}|\d{1,2}:\d{2}|\d{1,6})(|\.\d*)$`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can pre-compile the regexp and store it as a global variable, rather than compile it every time.
Or maybe it's a non-frequency function and you can just leave as it is.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And add comment for it, better with a example: "xx:xx:xx", otherwise, nobody can understand this regexp.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return r.MatchString(str)
}

// strDatetimeAddDuration adds duration to datetime string, returns a dutam value.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/dutam/datum

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return result, errors.Trace(err)
}
if getFsp(d) != 0 {
tmpDuration.Fsp = types.MaxFsp
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this MySQL's behavior ?
By the way, you can fsp := getFsp(d) to avoid call getFsp again and again.

Copy link
Member Author

@hawkingrei hawkingrei Jun 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tiancaiamao
Yes
mysql> select addtime("1:23","1.98");
+------------------------+
| addtime("1:23","1.98") |
+------------------------+
| 01:23:01.980000 |
+------------------------+
1 row in set (0.00 sec)

mysql> select addtime("123","1:23.98");
+--------------------------+
| addtime("123","1:23.98") |
+--------------------------+
| 01:24:23.980000 |
+--------------------------+
1 row in set (0.00 sec)

if err != nil {
return d, errors.Trace(err)
}
if args[0].IsNull() || args[1].IsNull() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to check len(args) == 2 , or is it already done in type infer?

Copy link
Member Author

@hawkingrei hawkingrei Jun 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tiancaiamao it is already done in buildin.go

@hawkingrei
Copy link
Member Author

hawkingrei commented Jun 2, 2017

@tiancaiamao
Copy link
Contributor

LGTM @XuHuaiyu

@tiancaiamao tiancaiamao added the status/LGT1 Indicates that a PR has LGTM 1. label Jun 5, 2017
@@ -367,6 +367,18 @@ func (v *typeInferrer) handleFuncCallExpr(x *ast.FuncCallExpr) {
tp.Charset, tp.Collate = types.DefaultCharsetForType(tp.Tp)
}
// number related
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment should be in line 381. @hawkingrei

@hawkingrei
Copy link
Member Author

@XuHuaiyu Fix it

@XuHuaiyu
Copy link
Contributor

XuHuaiyu commented Jun 5, 2017

LGTM

@XuHuaiyu XuHuaiyu added status/LGT2 Indicates that a PR has LGTM 2. and removed status/LGT1 Indicates that a PR has LGTM 1. labels Jun 5, 2017
@XuHuaiyu XuHuaiyu merged commit e669009 into pingcap:master Jun 5, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
contribution This PR is from a community contributor. status/LGT2 Indicates that a PR has LGTM 2.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants