Skip to content

Commit

Permalink
*: added builtin function sqrt (pingcap#2493)
Browse files Browse the repository at this point in the history
  • Loading branch information
zhexuany authored and XuHuaiyu committed Jan 18, 2017
1 parent bab721c commit 72403f3
Show file tree
Hide file tree
Showing 9 changed files with 81 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 @@ -85,6 +85,7 @@ const (
Rand = "rand"
Round = "round"
Sign = "sign"
Sqrt = "sqrt"

// time functions
Curdate = "curdate"
Expand Down
1 change: 1 addition & 0 deletions expression/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ var Funcs = map[string]Func{
ast.Rand: {builtinRand, 0, 1},
ast.Round: {builtinRound, 1, 2},
ast.Sign: {builtinSign, 1, 1},
ast.Sqrt: {builtinSqrt, 1, 1},
ast.Conv: {builtinConv, 3, 3},
ast.CRC32: {builtinCRC32, 1, 1},

Expand Down
45 changes: 45 additions & 0 deletions expression/builtin_math.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ var (
_ functionClass = &convFunctionClass{}
_ functionClass = &crc32FunctionClass{}
_ functionClass = &signFunctionClass{}
_ functionClass = &sqrtFunctionClass{}
_ functionClass = &arithmeticFunctionClass{}
)

Expand All @@ -57,6 +58,7 @@ var (
_ builtinFunc = &builtinConvSig{}
_ builtinFunc = &builtinCRC32Sig{}
_ builtinFunc = &builtinSignSig{}
_ builtinFunc = &builtinSqrtSig{}
_ builtinFunc = &builtinArithmeticSig{}
)

Expand Down Expand Up @@ -552,6 +554,49 @@ func builtinSign(args []types.Datum, ctx context.Context) (d types.Datum, err er
return d, nil
}

type sqrtFunctionClass struct {
baseFunctionClass
}

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

type builtinSqrtSig struct {
baseBuiltinFunc
}

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

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

sc := ctx.GetSessionVars().StmtCtx
f, err := args[0].ToFloat64(sc)
if err != nil {
return d, errors.Trace(err)
}

// negative value does not have any square root in rational number
// Need return null directly.
if f < 0 {
d.SetNull()
return d, nil
}

d.SetFloat64(math.Sqrt(f))
return
}

type arithmeticFunctionClass struct {
baseFunctionClass

Expand Down
23 changes: 23 additions & 0 deletions expression/builtin_math_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,3 +292,26 @@ func (s *testEvaluatorSuite) TestSign(c *C) {
c.Assert(v, testutil.DatumEquals, types.NewDatum(t.ret))
}
}

func (s *testEvaluatorSuite) TestSqrt(c *C) {
defer testleak.AfterTest(c)()
tbl := []struct {
Arg interface{}
Ret interface{}
}{
{nil, nil},
{int64(1), float64(1)},
{float64(4), float64(2)},
{"4", float64(2)},
{"9", float64(3)},
{"-16", nil},
}

Dtbl := tblToDtbl(tbl)

for _, t := range Dtbl {
v, err := builtinSqrt(t["Arg"], s.ctx)
c.Assert(err, IsNil)
c.Assert(v, DeepEquals, t["Ret"][0], Commentf("arg:%v", t["Arg"]))
}
}
1 change: 1 addition & 0 deletions parser/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ var tokenMap = map[string]int{
"SNAPSHOT": snapshot,
"SOME": some,
"SPACE": space,
"SQRT": sqrt,
"START": start,
"STARTING": starting,
"STATS_PERSISTENT": statsPersistent,
Expand Down
8 changes: 7 additions & 1 deletion parser/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ import (
second "SECOND"
sign "SIGN"
sleep "SLEEP"
sqrt "SQRT"
calcFoundRows "SQL_CALC_FOUND_ROWS"
strcmp "STRCMP"
strToDate "STR_TO_DATE"
Expand Down Expand Up @@ -2145,7 +2146,7 @@ NotKeywordToken:
| "DATEDIFF" | "DATE_ADD" | "DATE_FORMAT" | "DATE_SUB" | "DAYNAME" | "DAYOFMONTH" | "DAYOFWEEK" | "DAYOFYEAR" | "FROM_DAYS" | "FIND_IN_SET" | "FOUND_ROWS"
| "GROUP_CONCAT"| "GREATEST" | "LEAST" | "HOUR" | "HEX" | "UNHEX" | "IFNULL" | "ISNULL" | "LAST_INSERT_ID" | "LCASE" | "LENGTH" | "LOCATE" | "LOWER" | "LTRIM"
| "MAX" | "MICROSECOND" | "MIN" | "MINUTE" | "NULLIF" | "MONTH" | "MONTHNAME" | "NOW" | "POW" | "POWER" | "RAND"
| "SECOND" | "SIGN" | "SLEEP" | "SQL_CALC_FOUND_ROWS" | "STR_TO_DATE" | "SUBDATE" | "SUBSTRING" %prec lowerThanLeftParen |
| "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"

Expand Down Expand Up @@ -2929,6 +2930,11 @@ FunctionCallNonKeyword:
{
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: []ast.ExprNode{$3.(ast.ExprNode)}}
}
| "SQRT" '(' Expression ')'
{
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: []ast.ExprNode{$3.(ast.ExprNode)}}
}

| "SLEEP" '(' Expression ')'
{
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: []ast.ExprNode{$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 @@ -523,6 +523,7 @@ func (s *testParserSuite) TestBuiltin(c *C) {
{"SELECT CONV(10+'10'+'10'+X'0a',10,10);", true},
{"SELECT CRC32('MySQL');", true},
{"SELECT SIGN(0);", true},
{"SELECT SQRT(0);", true},

{"SELECT SUBSTR('Quadratically',5);", true},
{"SELECT SUBSTR('Quadratically',5, 3);", true},
Expand Down
2 changes: 1 addition & 1 deletion plan/typeinferer.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ func (v *typeInferrer) handleFuncCallExpr(x *ast.FuncCallExpr) {
} else {
tp = types.NewFieldType(mysql.TypeLonglong)
}
case "ln", "log", "log2", "log10":
case "ln", "log", "log2", "log10", "sqrt":
tp = types.NewFieldType(mysql.TypeDouble)
case "pow", "power", "rand":
tp = types.NewFieldType(mysql.TypeDouble)
Expand Down
1 change: 1 addition & 0 deletions plan/typeinferer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ func (ts *testTypeInferrerSuite) TestInferType(c *C) {
{"LOG(3, 10)", mysql.TypeDouble, charset.CharsetBin},
{"LOG2(3)", mysql.TypeDouble, charset.CharsetBin},
{"LOG10(3)", mysql.TypeDouble, charset.CharsetBin},
{"SQRT(3)", mysql.TypeDouble, charset.CharsetBin},
{"rand()", mysql.TypeDouble, charset.CharsetBin},
{"curdate()", mysql.TypeDate, charset.CharsetBin},
{"current_date()", mysql.TypeDate, charset.CharsetBin},
Expand Down

0 comments on commit 72403f3

Please sign in to comment.