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

Commit 124343f

Browse files
authored
Merge pull request #539 from theodesp/feature/Trim
Add Trim, Ltrim, Rtrim
2 parents 8cb8028 + 75feecc commit 124343f

File tree

3 files changed

+205
-0
lines changed

3 files changed

+205
-0
lines changed

sql/expression/function/registry.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,7 @@ var Defaults = sql.Functions{
5858
"sqrt": sql.Function1(NewSqrt),
5959
"pow": sql.Function2(NewPower),
6060
"power": sql.Function2(NewPower),
61+
"ltrim": sql.Function1(NewTrimFunc(lTrimType)),
62+
"rtrim": sql.Function1(NewTrimFunc(rTrimType)),
63+
"trim": sql.Function1(NewTrimFunc(bTrimType)),
6164
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package function
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
"strings"
7+
"unicode"
8+
9+
"gopkg.in/src-d/go-mysql-server.v0/sql/expression"
10+
"gopkg.in/src-d/go-mysql-server.v0/sql"
11+
)
12+
13+
type trimType rune
14+
const (
15+
lTrimType trimType = 'l'
16+
rTrimType trimType = 'r'
17+
bTrimType trimType = 'b'
18+
)
19+
20+
// NewTrimFunc returns a Trim creator function with a specific trimType.
21+
func NewTrimFunc(tType trimType) func(e sql.Expression) sql.Expression {
22+
return func(e sql.Expression) sql.Expression {
23+
return NewTrim(tType, e)
24+
}
25+
}
26+
27+
// NewTrim creates a new Trim expression.
28+
func NewTrim(tType trimType, str sql.Expression) sql.Expression {
29+
return &Trim{expression.UnaryExpression{Child: str}, tType}
30+
}
31+
32+
// Trim is a function that returns the string with prefix or suffix spaces removed based on the trimType
33+
type Trim struct {
34+
expression.UnaryExpression
35+
trimType
36+
}
37+
38+
// Type implements the Expression interface.
39+
func (t *Trim) Type() sql.Type { return sql.Text }
40+
41+
func (t *Trim) String() string {
42+
switch t.trimType {
43+
case lTrimType:
44+
return fmt.Sprintf("ltrim(%s)", t.Child)
45+
case rTrimType:
46+
return fmt.Sprintf("rtrim(%s)", t.Child)
47+
default:
48+
return fmt.Sprintf("trim(%s)", t.Child)
49+
}
50+
}
51+
52+
// IsNullable implements the Expression interface.
53+
func (t *Trim) IsNullable() bool {
54+
return t.Child.IsNullable()
55+
}
56+
57+
// TransformUp implements the Expression interface.
58+
func (t *Trim) TransformUp(f sql.TransformExprFunc) (sql.Expression, error) {
59+
str, err := t.Child.TransformUp(f)
60+
if err != nil {
61+
return nil, err
62+
}
63+
64+
return f(NewTrim(t.trimType, str))
65+
}
66+
67+
// Eval implements the Expression interface.
68+
func (t *Trim) Eval(
69+
ctx *sql.Context,
70+
row sql.Row,
71+
) (interface{}, error) {
72+
str, err := t.Child.Eval(ctx, row)
73+
if err != nil {
74+
return nil, err
75+
}
76+
77+
if str == nil {
78+
return nil, nil
79+
}
80+
81+
str, err = sql.Text.Convert(str)
82+
if err != nil {
83+
return nil, sql.ErrInvalidType.New(reflect.TypeOf(str))
84+
}
85+
86+
switch t.trimType {
87+
case lTrimType:
88+
return strings.TrimLeftFunc(str.(string), unicode.IsSpace), nil
89+
case rTrimType:
90+
return strings.TrimRightFunc(str.(string), unicode.IsSpace), nil
91+
default:
92+
return strings.TrimFunc(str.(string), unicode.IsSpace), nil
93+
}
94+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package function
2+
3+
import (
4+
"testing"
5+
6+
"gopkg.in/src-d/go-mysql-server.v0/sql/expression"
7+
"gopkg.in/src-d/go-mysql-server.v0/sql"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestTrim(t *testing.T) {
12+
f := NewTrimFunc(bTrimType)(expression.NewGetField(0, sql.Text, "", false), )
13+
testCases := []struct {
14+
name string
15+
row sql.Row
16+
expected interface{}
17+
err bool
18+
}{
19+
{"null input", sql.NewRow(nil), nil, false},
20+
{"trimmed string", sql.NewRow("foo"), "foo", false},
21+
{"spaces in both sides", sql.NewRow(" foo "), "foo", false},
22+
{"spaces in left side", sql.NewRow(" foo"), "foo", false},
23+
{"spaces in right side", sql.NewRow("foo "), "foo", false},
24+
{"two words with spaces", sql.NewRow(" foo bar "), "foo bar", false},
25+
{"different kinds of spaces", sql.NewRow("\r\tfoo bar \v"), "foo bar", false},
26+
}
27+
for _, tt := range testCases {
28+
t.Run(tt.name, func(t *testing.T) {
29+
t.Helper()
30+
require := require.New(t)
31+
ctx := sql.NewEmptyContext()
32+
33+
v, err := f.Eval(ctx, tt.row)
34+
if tt.err {
35+
require.Error(err)
36+
} else {
37+
require.NoError(err)
38+
require.Equal(tt.expected, v)
39+
}
40+
})
41+
}
42+
}
43+
44+
func TestLTrim(t *testing.T) {
45+
f := NewTrimFunc(lTrimType)(expression.NewGetField(0, sql.Text, "", false), )
46+
testCases := []struct {
47+
name string
48+
row sql.Row
49+
expected interface{}
50+
err bool
51+
}{
52+
{"null input", sql.NewRow(nil), nil, false},
53+
{"trimmed string", sql.NewRow("foo"), "foo", false},
54+
{"spaces in both sides", sql.NewRow(" foo "), "foo ", false},
55+
{"spaces in left side", sql.NewRow(" foo"), "foo", false},
56+
{"spaces in right side", sql.NewRow("foo "), "foo ", false},
57+
{"two words with spaces", sql.NewRow(" foo bar "), "foo bar ", false},
58+
{"different kinds of spaces", sql.NewRow("\r\tfoo bar \v"), "foo bar \v", false},
59+
}
60+
for _, tt := range testCases {
61+
t.Run(tt.name, func(t *testing.T) {
62+
t.Helper()
63+
require := require.New(t)
64+
ctx := sql.NewEmptyContext()
65+
66+
v, err := f.Eval(ctx, tt.row)
67+
if tt.err {
68+
require.Error(err)
69+
} else {
70+
require.NoError(err)
71+
require.Equal(tt.expected, v)
72+
}
73+
})
74+
}
75+
}
76+
77+
func TestRTrim(t *testing.T) {
78+
f := NewTrimFunc(rTrimType)(expression.NewGetField(0, sql.Text, "", false), )
79+
testCases := []struct {
80+
name string
81+
row sql.Row
82+
expected interface{}
83+
err bool
84+
}{
85+
{"null input", sql.NewRow(nil), nil, false},
86+
{"trimmed string", sql.NewRow("foo"), "foo", false},
87+
{"spaces in both sides", sql.NewRow(" foo "), " foo", false},
88+
{"spaces in left side", sql.NewRow(" foo"), " foo", false},
89+
{"spaces in right side", sql.NewRow("foo "), "foo", false},
90+
{"two words with spaces", sql.NewRow(" foo bar "), " foo bar", false},
91+
{"different kinds of spaces", sql.NewRow("\r\tfoo bar \v"), "\r\tfoo bar", false},
92+
}
93+
for _, tt := range testCases {
94+
t.Run(tt.name, func(t *testing.T) {
95+
t.Helper()
96+
require := require.New(t)
97+
ctx := sql.NewEmptyContext()
98+
99+
v, err := f.Eval(ctx, tt.row)
100+
if tt.err {
101+
require.Error(err)
102+
} else {
103+
require.NoError(err)
104+
require.Equal(tt.expected, v)
105+
}
106+
})
107+
}
108+
}

0 commit comments

Comments
 (0)