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

Commit

Permalink
Merge pull request #649 from juanjux/sql-sleep
Browse files Browse the repository at this point in the history
Added MySQL SLEEP function
  • Loading branch information
ajnavarro committed Apr 11, 2019
2 parents d03de5f + a161184 commit 87f17d6
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 0 deletions.
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)
}
})
}
}

0 comments on commit 87f17d6

Please sign in to comment.