Skip to content

Commit

Permalink
*: Support sleep function (pingcap#1457)
Browse files Browse the repository at this point in the history
* *: support sleep function

* evaluator: add SQL mode judgement
  • Loading branch information
zimulala authored Jul 16, 2016
1 parent 61cdf69 commit 7654b6b
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 6 deletions.
2 changes: 2 additions & 0 deletions evaluator/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ var Funcs = map[string]Func{
"nullif": {builtinNullIf, 2, 2},

// miscellaneous functions
"sleep": {builtinSleep, 1, 1},

// get_lock() and release_lock() is parsed but do nothing.
// It is used for preventing error in Ruby's activerecord migrations.
"get_lock": {builtinLock, 2, 2},
Expand Down
37 changes: 37 additions & 0 deletions evaluator/builtin_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package evaluator
import (
"regexp"
"strings"
"time"

"github.com/juju/errors"
"github.com/pingcap/tidb/context"
Expand All @@ -25,6 +26,42 @@ import (
"github.com/pingcap/tidb/util/types"
)

// See http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_sleep
func builtinSleep(args []types.Datum, ctx context.Context) (d types.Datum, err error) {
if ctx == nil {
return d, errors.Errorf("Missing context when evalue builtin")
}

sessVars := variable.GetSessionVars(ctx)
if args[0].IsNull() {
if sessVars.StrictSQLMode {
return d, errors.New("Incorrect arguments to sleep.")
}
d.SetInt64(0)
return
}
// processing argument is negative
zero := types.NewIntDatum(0)
ret, err := args[0].CompareDatum(zero)
if err != nil {
return d, errors.Trace(err)
}
if ret == -1 {
if sessVars.StrictSQLMode {
return d, errors.New("Incorrect arguments to sleep.")
}
d.SetInt64(0)
return
}

// TODO: consider it's interrupted using KILL QUERY from other session, or
// interrupted by time out.
duration := time.Duration(args[0].GetFloat64() * float64(time.Second.Nanoseconds()))
time.Sleep(duration)
d.SetInt64(0)
return
}

func builtinAndAnd(args []types.Datum, _ context.Context) (d types.Datum, err error) {
leftDatum := args[0]
rightDatum := args[1]
Expand Down
38 changes: 38 additions & 0 deletions evaluator/evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,44 @@ func (s *testEvaluatorSuite) TestBetween(c *C) {
s.runTests(c, cases)
}

func (s *testEvaluatorSuite) TestSleep(c *C) {
defer testleak.AfterTest(c)()
ctx := mock.NewContext()
variable.BindSessionVars(ctx)
sessVars := variable.GetSessionVars(ctx)

// non-strict model
sessVars.StrictSQLMode = false
d := make([]types.Datum, 1)
_, err := builtinSleep(d, nil)
c.Assert(err, NotNil)
ret, err := builtinSleep(d, ctx)
c.Assert(err, IsNil)
c.Assert(ret, DeepEquals, types.NewIntDatum(0))
d[0].SetInt64(-1)
ret, err = builtinSleep(d, ctx)
c.Assert(err, IsNil)
c.Assert(ret, DeepEquals, types.NewIntDatum(0))

// for error case under the strict model
sessVars.StrictSQLMode = true
d[0].SetNull()
_, err = builtinSleep(d, ctx)
c.Assert(err, NotNil)
d[0].SetFloat64(-2.5)
_, err = builtinSleep(d, ctx)
c.Assert(err, NotNil)

// strict model
d[0].SetFloat64(0.5)
start := time.Now()
ret, err = builtinSleep(d, ctx)
c.Assert(err, IsNil)
c.Assert(ret, DeepEquals, types.NewIntDatum(0))
sub := time.Now().Sub(start)
c.Assert(sub.Nanoseconds(), GreaterEqual, int64(0.5*1e9))
}

func (s *testEvaluatorSuite) TestBinopComparison(c *C) {
defer testleak.AfterTest(c)()
ctx := mock.NewContext()
Expand Down
16 changes: 11 additions & 5 deletions parser/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ import (
set "SET"
share "SHARE"
show "SHOW"
sleep "SLEEP"
signed "SIGNED"
some "SOME"
space "SPACE"
Expand Down Expand Up @@ -1964,11 +1965,12 @@ UnReservedKeyword:

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"| "HOUR"
| "IFNULL" | "ISNULL" | "LAST_INSERT_ID" | "LCASE" | "LENGTH" | "LOCATE" | "LOWER" | "LTRIM" | "MAX" | "MICROSECOND" | "MIN"
| "MINUTE" | "NULLIF" | "MONTH" | "MONTHNAME" | "NOW" | "POW" | "POWER" | "RAND" | "SECOND" | "SQL_CALC_FOUND_ROWS" | "SUBDATE"
| "SUBSTRING" %prec lowerThanLeftParen | "SUBSTRING_INDEX" | "SUM" | "TRIM" | "RTRIM" | "UCASE" | "UPPER" | "VERSION"
| "WEEKDAY" | "WEEKOFYEAR" | "YEARWEEK" | "ROUND" | "STATS_PERSISTENT" | "GET_LOCK" | "RELEASE_LOCK"
| "DATE_ADD" | "DATE_FORMAT" | "DATE_SUB" | "DAYNAME" | "DAYOFMONTH" | "DAYOFWEEK" | "DAYOFYEAR" | "FOUND_ROWS"
| "GROUP_CONCAT"| "HOUR" | "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" | "SUBDATE" | "SUBSTRING" %prec lowerThanLeftParen | "SUBSTRING_INDEX"
| "SUM" | "TRIM" | "RTRIM" | "UCASE" | "UPPER" | "VERSION" | "WEEKDAY" | "WEEKOFYEAR" | "YEARWEEK" | "ROUND"
| "STATS_PERSISTENT" | "GET_LOCK" | "RELEASE_LOCK"

/************************************************************************************
*
Expand Down Expand Up @@ -2653,6 +2655,10 @@ FunctionCallNonKeyword:
{
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}}
}
| "SLEEP" '(' Expression ')'
{
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}}
}
| "SPACE" '(' Expression ')'
{
$$ = &ast.FuncCallExpr{FnName: model.NewCIStr($1.(string)), Args: []ast.ExprNode{$3.(ast.ExprNode)}}
Expand Down
5 changes: 4 additions & 1 deletion parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (s *testParserSuite) TestSimple(c *C) {
"delay_key_write", "isolation", "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",
"enable", "disable", "reverse", "space", "privileges", "get_lock", "release_lock", "sleep",
}
for _, kw := range unreservedKws {
src := fmt.Sprintf("SELECT %s FROM tbl;", kw)
Expand Down Expand Up @@ -526,6 +526,9 @@ func (s *testParserSuite) TestBuiltin(c *C) {
// Repeat
{`SELECT REPEAT("a", 10);`, true},

// Sleep
{`SELECT SLEEP(10);`, true},

// For date_add
{`select date_add("2011-11-11 10:10:10.123456", interval 10 microsecond)`, true},
{`select date_add("2011-11-11 10:10:10.123456", interval 10 second)`, true},
Expand Down
3 changes: 3 additions & 0 deletions parser/scanner.l
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ session {s}{e}{s}{s}{i}{o}{n}
set {s}{e}{t}
share {s}{h}{a}{r}{e}
show {s}{h}{o}{w}
sleep {s}{l}{e}{e}{p}
some {s}{o}{m}{e}
space {s}{p}{a}{c}{e}
start {s}{t}{a}{r}{t}
Expand Down Expand Up @@ -1026,6 +1027,8 @@ redundant lval.item = string(l.val)
{set} return set
{share} return share
{show} return show
{sleep} lval.item = string(l.val)
return sleep
{subdate} lval.item = string(l.val)
return subDate
{strcmp} lval.item = string(l.val)
Expand Down
18 changes: 18 additions & 0 deletions session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1975,6 +1975,24 @@ func (s *testSessionSuite) TestRetryPreparedStmt(c *C) {
c.Assert(err, IsNil)
}

func (s *testSessionSuite) TestSleep(c *C) {
defer testleak.AfterTest(c)()
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)

mustExecSQL(c, se, "select sleep(0.01);")
mustExecSQL(c, se, "drop table if exists t;")
mustExecSQL(c, se, "create table t (a int);")
mustExecSQL(c, se, "insert t values (sleep(0.02));")
r := mustExecSQL(c, se, "select * from t;")
row, err := r.Next()
c.Assert(err, IsNil)
match(c, row.Data, 0)

err = store.Close()
c.Assert(err, IsNil)
}

func (s *testSessionSuite) TestIssue893(c *C) {
defer testleak.AfterTest(c)()
store := newStore(c, s.dbName)
Expand Down

0 comments on commit 7654b6b

Please sign in to comment.