Skip to content
This repository has been archived by the owner on Jan 28, 2021. It is now read-only.

Added MySQL SLEEP function #649

Merged
merged 4 commits into from
Apr 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ We support and actively test against certain third-party clients to ensure compa
|`RPAD(str, len, padstr)`|Returns the string str, right-padded with the string padstr to a length of len characters.|
|`RTRIM(str)`|Returns the string str with trailing space characters removed.|
|`SECOND(date)`|Returns the seconds of the given date.|
|`SLEEP(seconds)`|Wait for the specified number of seconds (can be fractional).|
|`SOUNDEX(str)`|Returns the soundex of a string.|
|`SPLIT(str,sep)`|Receives a string and a separator and returns the parts of the string split by the separator as a JSON array of strings.|
|`SQRT(X)`|Returns the square root of a nonnegative number X.|
Expand Down
1 change: 1 addition & 0 deletions SUPPORTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
- LN
- LOG2
- LOG10
- SLEEP

## Time functions
- DAY
Expand Down
4 changes: 4 additions & 0 deletions engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,10 @@ var queries = []struct {
"SELECT substring(s, 1, 1), count(*) FROM mytable GROUP BY substring(s, 1, 1)",
[]sql.Row{{"f", int64(1)}, {"s", int64(1)}, {"t", int64(1)}},
},
{
"SELECT SLEEP(0.5)",
[]sql.Row{{int(0)}},
},
}

func TestQueries(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions sql/expression/function/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,5 @@ var Defaults = []sql.Function{
sql.Function2{Name: "ifnull", Fn: NewIfNull},
sql.Function2{Name: "nullif", Fn: NewNullIf},
sql.Function0{Name: "now", Fn: NewNow},
sql.Function1{Name: "sleep", Fn: NewSleep},
}
66 changes: 66 additions & 0 deletions sql/expression/function/sleep.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package function

import (
"fmt"
"time"

"gopkg.in/src-d/go-mysql-server.v0/sql"
"gopkg.in/src-d/go-mysql-server.v0/sql/expression"
)

// Sleep is a function that just waits for the specified number of seconds
// and returns 0.
// It can be useful to test timeouts or long queries.
type Sleep struct {
expression.UnaryExpression
}

// NewSleep creates a new Sleep expression.
func NewSleep(e sql.Expression) sql.Expression {
return &Sleep{expression.UnaryExpression{Child: e}}
}

// Eval implements the Expression interface.
func (s *Sleep) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
child, err := s.Child.Eval(ctx, row)

if err != nil {
return nil, err
}

if child == nil {
return nil, nil
}

child, err = sql.Float64.Convert(child)
if err != nil {
return nil, err
}

time.Sleep(time.Duration(child.(float64) * 1000) * time.Millisecond)
return 0, nil
}

// String implements the Stringer interface.
func (s *Sleep) String() string {
return fmt.Sprintf("SLEEP(%s)", s.Child)
}

// IsNullable implements the Expression interface.
func (s *Sleep) IsNullable() bool {
return false
}

// TransformUp implements the Expression interface.
func (s *Sleep) TransformUp(f sql.TransformExprFunc) (sql.Expression, error) {
child, err := s.Child.TransformUp(f)
if err != nil {
return nil, err
}
return f(NewSleep(child))
}

// Type implements the Expression interface.
func (s *Sleep) Type() sql.Type {
return sql.Int32
}
50 changes: 50 additions & 0 deletions sql/expression/function/sleep_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package function

import (
"testing"
"time"

"github.com/stretchr/testify/require"
"gopkg.in/src-d/go-mysql-server.v0/sql"
"gopkg.in/src-d/go-mysql-server.v0/sql/expression"
)

func TestSleep(t *testing.T) {
f := NewSleep(
expression.NewGetField(0, sql.Text, "n", false),
)
testCases := []struct {
name string
row sql.Row
expected interface{}
waitTime float64
err bool
}{
{"null input", sql.NewRow(nil), nil, 0, false},
{"string input", sql.NewRow("foo"), nil, 0, true},
{"int input", sql.NewRow(3), int(0), 3.0, false},
{"number is zero", sql.NewRow(0), int(0), 0, false},
{"negative number", sql.NewRow(-4), int(0), 0, false},
{"positive number", sql.NewRow(4.48), int(0), 4.48, false},
}
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
t.Helper()
require := require.New(t)
ctx := sql.NewEmptyContext()

t1 := time.Now()
v, err := f.Eval(ctx, tt.row)
t2 := time.Now()
if tt.err {
require.Error(err)
} else {
require.NoError(err)
require.Equal(tt.expected, v)

waited := t2.Sub(t1).Seconds()
require.InDelta(waited, tt.waitTime, 0.1)
}
})
}
}