Skip to content

Commit

Permalink
add support for the LEAST function (pingcap#2360)
Browse files Browse the repository at this point in the history
  • Loading branch information
zyguan authored and hanfei1991 committed Dec 31, 2016
1 parent b5c9650 commit f0fbe16
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 12 deletions.
1 change: 1 addition & 0 deletions ast/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ const (
// common functions
Coalesce = "coalesce"
Greatest = "greatest"
Least = "least"

// math functions
Abs = "abs"
Expand Down
2 changes: 1 addition & 1 deletion bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ func doDMLWorks(s Session) {
mysql.SystemDB, mysql.TiDBTable, bootstrappedVar, bootstrappedVarTrue, bootstrappedVarTrue)
mustExecute(s, sql)

sql = fmt.Sprintf(`INSERT INTO %s.%s VALUES("%s", "%d", "Bootstrap version. Do not delete")`,
sql = fmt.Sprintf(`INSERT INTO %s.%s VALUES("%s", "%d", "Bootstrap version. Do not delete.")`,
mysql.SystemDB, mysql.TiDBTable, tidbServerVersionVar, currentBootstrapVersion)
mustExecute(s, sql)

Expand Down
32 changes: 30 additions & 2 deletions expression/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ var Funcs = map[string]Func{
ast.Coalesce: {builtinCoalesce, 1, -1},
ast.IsNull: {builtinIsNull, 1, 1},
ast.Greatest: {builtinGreatest, 2, -1},
ast.Least: {builtinLeast, 2, -1},

// math functions
ast.Abs: {builtinAbs, 1, 1},
Expand Down Expand Up @@ -307,11 +308,13 @@ func builtinIsNull(args []types.Datum, _ context.Context) (d types.Datum, err er

// See http://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_greatest
func builtinGreatest(args []types.Datum, ctx context.Context) (d types.Datum, err error) {
if args[0].IsNull() {
return
}
max := 0
sc := ctx.GetSessionVars().StmtCtx
for i := 0; i < len(args); i++ {
for i := 1; i < len(args); i++ {
if args[i].IsNull() {
d.SetNull()
return
}

Expand All @@ -327,3 +330,28 @@ func builtinGreatest(args []types.Datum, ctx context.Context) (d types.Datum, er
d = args[max]
return
}

// See http://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_least
func builtinLeast(args []types.Datum, ctx context.Context) (d types.Datum, err error) {
if args[0].IsNull() {
return
}
min := 0
sc := ctx.GetSessionVars().StmtCtx
for i := 1; i < len(args); i++ {
if args[i].IsNull() {
return
}

var cmp int
if cmp, err = args[i].CompareDatum(sc, args[min]); err != nil {
return
}

if cmp < 0 {
min = i
}
}
d = args[min]
return
}
38 changes: 32 additions & 6 deletions expression/builtin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,23 +70,49 @@ func (s *testEvaluatorSuite) TestCoalesce(c *C) {
c.Assert(v, testutil.DatumEquals, types.NewDatum(nil))
}

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

v, err := builtinGreatest(types.MakeDatums(2, 0), s.ctx)
var datums []types.Datum

datums = types.MakeDatums(2, 0)
v, err := builtinGreatest(datums, s.ctx)
c.Assert(err, IsNil)
c.Assert(v.GetInt64(), Equals, int64(2))
v, err = builtinLeast(datums, s.ctx)
c.Assert(err, IsNil)
c.Assert(v.GetInt64(), Equals, int64(0))

v, err = builtinGreatest(types.MakeDatums(34.0, 3.0, 5.0, 767.0), s.ctx)
datums = types.MakeDatums(34.0, 3.0, 5.0, 767.0)
v, err = builtinGreatest(datums, s.ctx)
c.Assert(err, IsNil)
c.Assert(v.GetFloat64(), Equals, float64(767.0))
v, err = builtinLeast(datums, s.ctx)
c.Assert(err, IsNil)
c.Assert(v.GetFloat64(), Equals, float64(3.0))

v, err = builtinGreatest(types.MakeDatums("B", "A", "C"), s.ctx)
datums = types.MakeDatums("B", "A", "C")
v, err = builtinGreatest(datums, s.ctx)
c.Assert(err, IsNil)
c.Assert(v.GetString(), Equals, "C")
v, err = builtinLeast(datums, s.ctx)
c.Assert(err, IsNil)
c.Assert(v.GetString(), Equals, "A")

// GREATEST() and LEAST() return NULL if any argument is NULL.
datums = types.MakeDatums(nil, 1, 2)
v, err = builtinGreatest(datums, s.ctx)
c.Assert(err, IsNil)
c.Assert(v.IsNull(), IsTrue)
v, err = builtinLeast(datums, s.ctx)
c.Assert(err, IsNil)
c.Assert(v.IsNull(), IsTrue)

// GREATEST() returns NULL if any argument is NULL.
v, err = builtinGreatest(types.MakeDatums(1, nil, 2), s.ctx)
datums = types.MakeDatums(1, nil, 2)
v, err = builtinGreatest(datums, s.ctx)
c.Assert(err, IsNil)
c.Assert(v.IsNull(), IsTrue)
v, err = builtinLeast(datums, s.ctx)
c.Assert(err, IsNil)
c.Assert(v.IsNull(), IsTrue)
}
Expand Down
1 change: 1 addition & 0 deletions parser/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ var tokenMap = map[string]int{
"KEYS": keys,
"LAST_INSERT_ID": lastInsertID,
"LEADING": leading,
"LEAST": least,
"LEFT": left,
"LENGTH": length,
"LESS": less,
Expand Down
7 changes: 6 additions & 1 deletion parser/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ import (
lastInsertID "LAST_INSERT_ID"
lcase "LCASE"
length "LENGTH"
least "LEAST"
ln "LN"
locate "LOCATE"
log "LOG"
Expand Down Expand Up @@ -2093,7 +2094,7 @@ ReservedKeyword:
NotKeywordToken:
"ABS" | "ADDDATE" | "ADMIN" | "COALESCE" | "CONCAT" | "CONCAT_WS" | "CONNECTION_ID" | "CUR_TIME"| "COUNT" | "DAY"
| "DATE_ADD" | "DATE_FORMAT" | "DATE_SUB" | "DAYNAME" | "DAYOFMONTH" | "DAYOFWEEK" | "DAYOFYEAR" | "FOUND_ROWS"
| "GROUP_CONCAT"| "GREATEST" | "HOUR" | "HEX" | "UNHEX" | "IFNULL" | "ISNULL" | "LAST_INSERT_ID" | "LCASE" | "LENGTH" | "LOCATE" | "LOWER" | "LTRIM"
| "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" | "SLEEP" | "SQL_CALC_FOUND_ROWS" | "STR_TO_DATE" | "SUBDATE" | "SUBSTRING" %prec lowerThanLeftParen |
"SUBSTRING_INDEX" | "SUM" | "TRIM" | "RTRIM" | "UCASE" | "UPPER" | "VERSION" | "WEEKDAY" | "WEEKOFYEAR" | "YEARWEEK" | "ROUND"
Expand Down Expand Up @@ -2698,6 +2699,10 @@ FunctionCallNonKeyword:
{
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)}
}
| "LEAST" '(' ExpressionList ')'
{
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: $3.([]ast.ExprNode)}
}
| "HOUR" '(' Expression ')'
{
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1), Args: []ast.ExprNode{$3.(ast.ExprNode)}}
Expand Down
4 changes: 3 additions & 1 deletion parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (s *testParserSuite) TestSimple(c *C) {
"delay_key_write", "isolation", "partitions", "repeatable", "committed", "uncommitted", "only", "serializable", "level",
"curtime", "variables", "dayname", "version", "btree", "hash", "row_format", "dynamic", "fixed", "compressed",
"compact", "redundant", "sql_no_cache sql_no_cache", "sql_cache sql_cache", "action", "round",
"enable", "disable", "reverse", "space", "privileges", "get_lock", "release_lock", "sleep", "no", "greatest",
"enable", "disable", "reverse", "space", "privileges", "get_lock", "release_lock", "sleep", "no", "greatest", "least",
"binlog", "hex", "unhex", "function", "indexes", "from_unixtime", "processlist", "events", "less", "than", "timediff",
"ln", "log", "log2", "log10",
}
Expand Down Expand Up @@ -526,6 +526,8 @@ func (s *testParserSuite) TestBuiltin(c *C) {

{"SELECT CONVERT('111', SIGNED);", true},

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

// Information Functions
{"SELECT DATABASE();", true},
{"SELECT SCHEMA();", true},
Expand Down
2 changes: 1 addition & 1 deletion plan/typeinferer.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ func (v *typeInferrer) handleFuncCallExpr(x *ast.FuncCallExpr) {
if x.FnName.L == "abs" && tp.Tp == mysql.TypeDatetime {
tp = types.NewFieldType(mysql.TypeDouble)
}
case "greatest":
case "greatest", "least":
for _, arg := range x.Args {
InferType(v.sc, arg)
}
Expand Down
4 changes: 4 additions & 0 deletions plan/typeinferer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ func (ts *testTypeInferrerSuite) TestInferType(c *C) {
{"greatest('TiDB', 'D', 'd')", mysql.TypeVarString, "utf8"},
{"greatest(1.1, 2.2)", mysql.TypeNewDecimal, charset.CharsetBin},
{"greatest('TiDB', 3)", mysql.TypeVarString, "utf8"},
{"least(1, 2, 3)", mysql.TypeLonglong, charset.CharsetBin},
{"least('TiDB', 'D', 'd')", mysql.TypeVarString, "utf8"},
{"least(1.1, 2.2)", mysql.TypeNewDecimal, charset.CharsetBin},
{"least('TiDB', 3)", mysql.TypeVarString, "utf8"},
{"hex('TiDB')", mysql.TypeVarString, "utf8"},
{"hex(12)", mysql.TypeVarString, "utf8"},
{"unhex('TiDB')", mysql.TypeVarString, "utf8"},
Expand Down

0 comments on commit f0fbe16

Please sign in to comment.