Skip to content

Commit

Permalink
expression,parser: add support for INTERVAL function (#2370)
Browse files Browse the repository at this point in the history
  • Loading branch information
zyguan authored and tiancaiamao committed Jan 5, 2017
1 parent 73f1fa9 commit 8e109b7
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 0 deletions.
1 change: 1 addition & 0 deletions ast/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const (
Coalesce = "coalesce"
Greatest = "greatest"
Least = "least"
Interval = "interval"

// math functions
Abs = "abs"
Expand Down
33 changes: 33 additions & 0 deletions expression/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package expression

import (
"sort"
"strings"

"github.com/juju/errors"
Expand Down Expand Up @@ -155,6 +156,7 @@ var Funcs = map[string]Func{
ast.IsNull: {builtinIsNull, 1, 1},
ast.Greatest: {builtinGreatest, 2, -1},
ast.Least: {builtinLeast, 2, -1},
ast.Interval: {builtinInterval, 2, -1},

// math functions
ast.Abs: {builtinAbs, 1, 1},
Expand Down Expand Up @@ -465,3 +467,34 @@ func builtinLeast(args []types.Datum, ctx context.Context) (d types.Datum, err e
d = args[min]
return
}

// See http://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_interval
func builtinInterval(args []types.Datum, ctx context.Context) (d types.Datum, err error) {
if args[0].IsNull() {
d.SetInt64(int64(-1))
return
}
sc := ctx.GetSessionVars().StmtCtx

idx := sort.Search(len(args)-1, func(i int) bool {
d1, d2 := args[0], args[i+1]
if d1.Kind() == types.KindInt64 && d1.Kind() == d2.Kind() {
return d1.GetInt64() < d2.GetInt64()
}
if d1.Kind() == types.KindUint64 && d1.Kind() == d2.Kind() {
return d1.GetUint64() < d2.GetUint64()
}
if d1.Kind() == types.KindInt64 && d2.Kind() == types.KindUint64 {
return d1.GetInt64() < 0 || d1.GetUint64() < d2.GetUint64()
}
if d1.Kind() == types.KindUint64 && d2.Kind() == types.KindInt64 {
return d2.GetInt64() > 0 && d1.GetUint64() < d2.GetUint64()
}
v1, _ := d1.ToFloat64(sc)
v2, _ := d2.ToFloat64(sc)
return v1 < v2
})
d.SetInt64(int64(idx))

return
}
34 changes: 34 additions & 0 deletions expression/builtin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,40 @@ func (s *testEvaluatorSuite) TestGreatestLeastFuncs(c *C) {
c.Assert(v.IsNull(), IsTrue)
}

func (s *testEvaluatorSuite) TestIntervalFunc(c *C) {
defer testleak.AfterTest(c)()

for _, t := range []struct {
args []types.Datum
ret int64
}{
{types.MakeDatums(nil, 1, 2), -1},
{types.MakeDatums(1, 2, 3), 0},
{types.MakeDatums(2, 1, 3), 1},
{types.MakeDatums(3, 1, 2), 2},
{types.MakeDatums(0, "b", "1", "2"), 1},
{types.MakeDatums("a", "b", "1", "2"), 1},
{types.MakeDatums(23, 1, 23, 23, 23, 30, 44, 200), 4},
{types.MakeDatums(23, 1.7, 15.3, 23.1, 30, 44, 200), 2},
{types.MakeDatums(9007199254740992, 9007199254740993), 0},
{types.MakeDatums(uint64(9223372036854775808), uint64(9223372036854775809)), 0},
{types.MakeDatums(9223372036854775807, uint64(9223372036854775808)), 0},
{types.MakeDatums(-9223372036854775807, uint64(9223372036854775808)), 0},
{types.MakeDatums(uint64(9223372036854775806), 9223372036854775807), 0},
{types.MakeDatums(uint64(9223372036854775806), -9223372036854775807), 1},
{types.MakeDatums("9007199254740991", "9007199254740992"), 0},

// tests for appropriate precision loss
{types.MakeDatums(9007199254740992, "9007199254740993"), 1},
{types.MakeDatums("9007199254740992", 9007199254740993), 1},
{types.MakeDatums("9007199254740992", "9007199254740993"), 1},
} {
v, err := builtinInterval(t.args, s.ctx)
c.Assert(err, IsNil)
c.Assert(v.GetInt64(), Equals, t.ret)
}
}

func (s *testEvaluatorSuite) TestIsNullFunc(c *C) {
defer testleak.AfterTest(c)()

Expand Down
4 changes: 4 additions & 0 deletions parser/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,9 @@ import (
%precedence lowerThanSQLCache
%precedence sqlCache sqlNoCache

%precedence lowerThanIntervalKeyword
%precedence interval

%precedence lowerThanSetKeyword
%precedence set

Expand Down Expand Up @@ -2468,6 +2471,7 @@ FunctionNameConflict:
| "UTC_DATE"
| "CURRENT_DATE"
| "VERSION"
| "INTERVAL" %prec lowerThanIntervalKeyword

FunctionCallConflict:
FunctionNameConflict '(' ExpressionListOpt ')'
Expand Down
3 changes: 3 additions & 0 deletions parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,9 @@ func (s *testParserSuite) TestBuiltin(c *C) {

{"SELECT LEAST(1, 2, 3);", true},

{"SELECT INTERVAL(1, 0, 1, 2)", true},
{"SELECT DATE_ADD('2008-01-02', INTERVAL INTERVAL(1, 0, 1) DAY);", true},

// Information Functions
{"SELECT DATABASE();", true},
{"SELECT SCHEMA();", true},
Expand Down
2 changes: 2 additions & 0 deletions plan/typeinferer.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@ func (v *typeInferrer) handleFuncCallExpr(x *ast.FuncCallExpr) {
mergeArithType(tp.Tp, x.Args[i].GetType().Tp)
}
}
case "interval":
tp = types.NewFieldType(mysql.TypeLonglong)
case "ceil", "ceiling":
t := x.Args[0].GetType().Tp
if t == mysql.TypeNull || t == mysql.TypeFloat || t == mysql.TypeDouble || t == mysql.TypeVarchar ||
Expand Down
3 changes: 3 additions & 0 deletions plan/typeinferer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ func (ts *testTypeInferrerSuite) TestInferType(c *C) {
{"least('TiDB', 'D', 'd')", mysql.TypeVarString, "utf8"},
{"least(1.1, 2.2)", mysql.TypeNewDecimal, charset.CharsetBin},
{"least('TiDB', 3)", mysql.TypeVarString, "utf8"},
{"interval(1, 2, 3)", mysql.TypeLonglong, charset.CharsetBin},
{"interval(1.0, 2.0, 3.0)", mysql.TypeLonglong, charset.CharsetBin},
{"interval('1', '2', '3')", mysql.TypeLonglong, charset.CharsetBin},
{"hex('TiDB')", mysql.TypeVarString, "utf8"},
{"hex(12)", mysql.TypeVarString, "utf8"},
{"unhex('TiDB')", mysql.TypeVarString, "utf8"},
Expand Down

0 comments on commit 8e109b7

Please sign in to comment.