*: Support sleep function (#1457)
* *: support sleep function * evaluator: add SQL mode judgement
This commit is contained in:
@ -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},
|
||||
|
||||
@ -16,6 +16,7 @@ package evaluator
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/juju/errors"
|
||||
"github.com/pingcap/tidb/context"
|
||||
@ -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]
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -270,6 +270,7 @@ import (
|
||||
set "SET"
|
||||
share "SHARE"
|
||||
show "SHOW"
|
||||
sleep "SLEEP"
|
||||
signed "SIGNED"
|
||||
some "SOME"
|
||||
space "SPACE"
|
||||
@ -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"
|
||||
|
||||
/************************************************************************************
|
||||
*
|
||||
@ -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)}}
|
||||
|
||||
@ -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)
|
||||
@ -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},
|
||||
|
||||
@ -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}
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
Reference in New Issue
Block a user