Skip to content

Commit

Permalink
*: add builtin function FLOOR (pingcap#2484)
Browse files Browse the repository at this point in the history
  • Loading branch information
zhexuany authored and hanfei1991 committed Jan 22, 2017
1 parent f145a32 commit 383f203
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 2 deletions.
1 change: 1 addition & 0 deletions ast/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const (
// math functions
Abs = "abs"
Ceil = "ceil"
Floor = "floor"
Ceiling = "ceiling"
Conv = "conv"
CRC32 = "crc32"
Expand Down
2 changes: 2 additions & 0 deletions expression/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ var Funcs = map[string]Func{
ast.Abs: {builtinAbs, 1, 1},
ast.Ceil: {builtinCeil, 1, 1},
ast.Ceiling: {builtinCeil, 1, 1},
ast.Floor: {builtinFloor, 1, 1},
ast.Ln: {builtinLog, 1, 1},
ast.Log: {builtinLog, 1, 2},
ast.Log2: {builtinLog2, 1, 1},
Expand Down Expand Up @@ -309,6 +310,7 @@ var funcs = map[string]functionClass{
ast.Abs: &absFunctionClass{baseFunctionClass{ast.Abs, 1, 1}},
ast.Ceil: &ceilFunctionClass{baseFunctionClass{ast.Ceil, 1, 1}},
ast.Ceiling: &ceilFunctionClass{baseFunctionClass{ast.Ceiling, 1, 1}},
ast.Floor: &floorFunctionClass{baseFunctionClass{ast.Floor, 1, 1}},
ast.Ln: &logFunctionClass{baseFunctionClass{ast.Log, 1, 1}},
ast.Log: &logFunctionClass{baseFunctionClass{ast.Log, 1, 2}},
ast.Log2: &log2FunctionClass{baseFunctionClass{ast.Log2, 1, 1}},
Expand Down
44 changes: 44 additions & 0 deletions expression/builtin_math.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
var (
_ functionClass = &absFunctionClass{}
_ functionClass = &ceilFunctionClass{}
_ functionClass = &floorFunctionClass{}
_ functionClass = &logFunctionClass{}
_ functionClass = &log2FunctionClass{}
_ functionClass = &log10FunctionClass{}
Expand All @@ -49,6 +50,7 @@ var (
var (
_ builtinFunc = &builtinAbsSig{}
_ builtinFunc = &builtinCeilSig{}
_ builtinFunc = &builtinFloorSig{}
_ builtinFunc = &builtinLogSig{}
_ builtinFunc = &builtinLog2Sig{}
_ builtinFunc = &builtinLog10Sig{}
Expand Down Expand Up @@ -142,6 +144,48 @@ func builtinCeil(args []types.Datum, ctx context.Context) (d types.Datum, err er
return
}

type floorFunctionClass struct {
baseFunctionClass
}

func (c *floorFunctionClass) getFunction(args []Expression, ctx context.Context) (builtinFunc, error) {
return &builtinFloorSig{newBaseBuiltinFunc(args, ctx)}, errors.Trace(c.verifyArgs(args))
}

type builtinFloorSig struct {
baseBuiltinFunc
}

func (b *builtinFloorSig) eval(row []types.Datum) (types.Datum, error) {
args, err := b.evalArgs(row)
if err != nil {
return types.Datum{}, errors.Trace(err)
}
return builtinFloor(args, b.ctx)
}

// See http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_floor
func builtinFloor(args []types.Datum, ctx context.Context) (d types.Datum, err error) {
if args[0].IsNull() ||
args[0].Kind() == types.KindUint64 || args[0].Kind() == types.KindInt64 {
return args[0], nil
}

// have to set IgnoreTruncate to true in order to getValidPrefix
sc := ctx.GetSessionVars().StmtCtx
tmpIT := sc.IgnoreTruncate
sc.IgnoreTruncate = true
f, err := args[0].ToFloat64(sc)
if err != nil {
sc.IgnoreTruncate = tmpIT
return d, errors.Trace(err)
}

sc.IgnoreTruncate = tmpIT
d.SetFloat64(math.Floor(f))
return
}

type logFunctionClass struct {
baseFunctionClass
}
Expand Down
25 changes: 25 additions & 0 deletions expression/builtin_math_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,31 @@ func (s *testEvaluatorSuite) TestCeil(c *C) {
}
}

func (s *testEvaluatorSuite) TestFloor(c *C) {
defer testleak.AfterTest(c)()
for _, t := range []struct {
num interface{}
ret interface{}
err Checker
}{
{nil, nil, IsNil},
{int64(1), int64(1), IsNil},
{float64(1.23), float64(1), IsNil},
{float64(-1.23), float64(-2), IsNil},
{"1.23", float64(1), IsNil},
{"-1.23", float64(-2), IsNil},
{"-1.b23", float64(-1), IsNil},
{"abce", float64(0), IsNil},
} {
fc := funcs[ast.Floor]
f, err := fc.getFunction(datumsToConstants(types.MakeDatums(t.num)), s.ctx)
c.Assert(err, IsNil)
v, err := f.eval(nil)
c.Assert(err, t.err)
c.Assert(v, testutil.DatumEquals, types.NewDatum(t.ret))
}
}

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

Expand Down
1 change: 1 addition & 0 deletions parser/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ var tokenMap = map[string]int{
"FULL": full,
"FULLTEXT": fulltext,
"FUNCTION": function,
"FLOOR": floor,
"FLUSH": flush,
"GET_LOCK": getLock,
"GLOBAL": global,
Expand Down
7 changes: 6 additions & 1 deletion parser/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ import (
events "EVENTS"
fieldKwd "FIELD_KWD"
findInSet "FIND_IN_SET"
floor "FLOOR"
foundRows "FOUND_ROWS"
fromUnixTime "FROM_UNIXTIME"
grant "GRANT"
Expand Down Expand Up @@ -2148,7 +2149,7 @@ NotKeywordToken:
| "MAX" | "MICROSECOND" | "MIN" | "MINUTE" | "NULLIF" | "MONTH" | "MONTHNAME" | "NOW" | "POW" | "POWER" | "RAND"
| "SECOND" | "SIGN" | "SLEEP" | "SQRT" | "SQL_CALC_FOUND_ROWS" | "STR_TO_DATE" | "SUBDATE" | "SUBSTRING" %prec lowerThanLeftParen |
"SUBSTRING_INDEX" | "SUM" | "TRIM" | "RTRIM" | "UCASE" | "UPPER" | "VERSION" | "WEEKDAY" | "WEEKOFYEAR" | "YEARWEEK" | "ROUND"
| "STATS_PERSISTENT" | "GET_LOCK" | "RELEASE_LOCK" | "CEIL" | "CEILING" | "FROM_UNIXTIME" | "TIMEDIFF" | "LN" | "LOG" | "LOG2" | "LOG10" | "FIELD_KWD"
| "STATS_PERSISTENT" | "GET_LOCK" | "RELEASE_LOCK" | "CEIL" | "CEILING" | "FLOOR" | "FROM_UNIXTIME" | "TIMEDIFF" | "LN" | "LOG" | "LOG2" | "LOG10" | "FIELD_KWD"

/************************************************************************************
*
Expand Down Expand Up @@ -2685,6 +2686,10 @@ FunctionCallNonKeyword:
{
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: []ast.ExprNode{$3.(ast.ExprNode)}}
}
| "FLOOR" '(' Expression ')'
{
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: []ast.ExprNode{$3.(ast.ExprNode)}}
}
| "FIELD_KWD" '(' ExpressionList ')'
{
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)}
Expand Down
1 change: 1 addition & 0 deletions parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ func (s *testParserSuite) TestBuiltin(c *C) {
{"SELECT ROUND(1.23, 1);", true},
{"SELECT CEIL(-1.23);", true},
{"SELECT CEILING(1.23);", true},
{"SELECT FLOOR(-1.23);", true},
{"SELECT LN(1);", true},
{"SELECT LOG(-2);", true},
{"SELECT LOG(2, 65536);", true},
Expand Down
2 changes: 1 addition & 1 deletion plan/typeinferer.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ func (v *typeInferrer) handleFuncCallExpr(x *ast.FuncCallExpr) {
}
case "interval":
tp = types.NewFieldType(mysql.TypeLonglong)
case "ceil", "ceiling":
case "ceil", "ceiling", "floor":
t := x.Args[0].GetType().Tp
if t == mysql.TypeNull || t == mysql.TypeFloat || t == mysql.TypeDouble || t == mysql.TypeVarchar ||
t == mysql.TypeTinyBlob || t == mysql.TypeMediumBlob || t == mysql.TypeLongBlob ||
Expand Down
1 change: 1 addition & 0 deletions plan/typeinferer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ func (ts *testTypeInferrerSuite) TestInferType(c *C) {
{"sign(null)", mysql.TypeLonglong, charset.CharsetBin},
{"unix_timestamp()", mysql.TypeLonglong, charset.CharsetBin},
{"unix_timestamp('2015-11-13 10:20:19')", mysql.TypeLonglong, charset.CharsetBin},
{"floor(1.23)", mysql.TypeLonglong, charset.CharsetBin},
{"field('foo', null)", mysql.TypeLonglong, charset.CharsetBin},
{"find_in_set('foo', 'foo,bar')", mysql.TypeLonglong, charset.CharsetBin},
{"find_in_set('foo', null)", mysql.TypeLonglong, charset.CharsetBin},
Expand Down

0 comments on commit 383f203

Please sign in to comment.