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

Commit 6775578

Browse files
authored
Merge pull request #522 from theodesp/feature/LPAD-RPAD
Feature/lpad rpad
2 parents 981d3f0 + 00f9e71 commit 6775578

File tree

5 files changed

+295
-12
lines changed

5 files changed

+295
-12
lines changed

sql/expression/function/logarithm.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import (
1414
// logarithm function
1515
var ErrInvalidArgumentForLogarithm = errors.NewKind("invalid argument value for logarithm: %v")
1616

17-
// LogBaseMaker returns LogBase creator functions with a specific base.
18-
func LogBaseMaker(base float64) func(e sql.Expression) sql.Expression {
17+
// NewLogBaseFunc returns LogBase creator function with a specific base.
18+
func NewLogBaseFunc(base float64) func(e sql.Expression) sql.Expression {
1919
return func(e sql.Expression) sql.Expression {
2020
return NewLogBase(base, e)
2121
}
@@ -90,7 +90,7 @@ type Log struct {
9090
expression.BinaryExpression
9191
}
9292

93-
// NewLn creates a new Log expression.
93+
// NewLog creates a new Log expression.
9494
func NewLog(args ...sql.Expression) (sql.Expression, error) {
9595
argLen := len(args)
9696
if argLen == 0 || argLen > 2 {

sql/expression/function/logarithm_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func TestLn(t *testing.T) {
3232
}
3333

3434
for _, tt := range testCases {
35-
f := LogBaseMaker(math.E)(expression.NewGetField(0, tt.rowType, "", false))
35+
f := NewLogBaseFunc(math.E)(expression.NewGetField(0, tt.rowType, "", false))
3636
t.Run(tt.name, func(t *testing.T) {
3737
t.Helper()
3838
require := require.New(t)
@@ -48,7 +48,7 @@ func TestLn(t *testing.T) {
4848
}
4949

5050
// Test Nil
51-
f := LogBaseMaker(math.E)(expression.NewGetField(0, sql.Float64, "", true))
51+
f := NewLogBaseFunc(math.E)(expression.NewGetField(0, sql.Float64, "", true))
5252
require := require.New(t)
5353
result, err := f.Eval(sql.NewEmptyContext(), sql.NewRow(nil))
5454
require.NoError(err)
@@ -75,7 +75,7 @@ func TestLog2(t *testing.T) {
7575
}
7676

7777
for _, tt := range testCases {
78-
f := LogBaseMaker(float64(2))(expression.NewGetField(0, tt.rowType, "", false))
78+
f := NewLogBaseFunc(float64(2))(expression.NewGetField(0, tt.rowType, "", false))
7979
t.Run(tt.name, func(t *testing.T) {
8080
t.Helper()
8181
require := require.New(t)
@@ -91,7 +91,7 @@ func TestLog2(t *testing.T) {
9191
}
9292

9393
// Test Nil
94-
f := LogBaseMaker(float64(2))(expression.NewGetField(0, sql.Float64, "", true))
94+
f := NewLogBaseFunc(float64(2))(expression.NewGetField(0, sql.Float64, "", true))
9595
require := require.New(t)
9696
result, err := f.Eval(sql.NewEmptyContext(), sql.NewRow(nil))
9797
require.NoError(err)
@@ -118,7 +118,7 @@ func TestLog10(t *testing.T) {
118118
}
119119

120120
for _, tt := range testCases {
121-
f := LogBaseMaker(float64(10))(expression.NewGetField(0, tt.rowType, "", false))
121+
f := NewLogBaseFunc(float64(10))(expression.NewGetField(0, tt.rowType, "", false))
122122
t.Run(tt.name, func(t *testing.T) {
123123
t.Helper()
124124
require := require.New(t)
@@ -134,7 +134,7 @@ func TestLog10(t *testing.T) {
134134
}
135135

136136
// Test Nil
137-
f := LogBaseMaker(float64(10))(expression.NewGetField(0, sql.Float64, "", true))
137+
f := NewLogBaseFunc(float64(10))(expression.NewGetField(0, sql.Float64, "", true))
138138
require := require.New(t)
139139
result, err := f.Eval(sql.NewEmptyContext(), sql.NewRow(nil))
140140
require.NoError(err)

sql/expression/function/registry.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,10 @@ var Defaults = sql.Functions{
4949
"json_extract": sql.FunctionN(NewJSONExtract),
5050
"connection_id": sql.Function0(NewConnectionID),
5151
"soundex": sql.Function1(NewSoundex),
52-
"ln": sql.Function1(LogBaseMaker(float64(math.E))),
53-
"log2": sql.Function1(LogBaseMaker(float64(2))),
54-
"log10": sql.Function1(LogBaseMaker(float64(10))),
52+
"ln": sql.Function1(NewLogBaseFunc(float64(math.E))),
53+
"log2": sql.Function1(NewLogBaseFunc(float64(2))),
54+
"log10": sql.Function1(NewLogBaseFunc(float64(10))),
5555
"log": sql.FunctionN(NewLog),
56+
"rpad": sql.FunctionN(NewPadFunc(rPadType)),
57+
"lpad": sql.FunctionN(NewPadFunc(lPadType)),
5658
}

sql/expression/function/rpad_lpad.go

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
package function
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
"strings"
7+
8+
"gopkg.in/src-d/go-mysql-server.v0/sql"
9+
"gopkg.in/src-d/go-errors.v1"
10+
)
11+
12+
var ErrDivisionByZero = errors.NewKind("division by zero")
13+
14+
type padType rune
15+
const (
16+
lPadType padType = 'l'
17+
rPadType padType = 'r'
18+
)
19+
20+
// NewPadFunc returns a Pad creator function with a specific padType.
21+
func NewPadFunc(pType padType) func(e ...sql.Expression) (sql.Expression, error) {
22+
return func(e ...sql.Expression) (sql.Expression, error) {
23+
return NewPad(pType, e...)
24+
}
25+
}
26+
27+
// NewPad creates a new Pad expression.
28+
func NewPad(pType padType, args ...sql.Expression) (sql.Expression, error) {
29+
argLen := len(args)
30+
if argLen != 3 {
31+
return nil, sql.ErrInvalidArgumentNumber.New("3", argLen)
32+
}
33+
34+
return &Pad{args[0], args[1], args[2], pType}, nil
35+
}
36+
37+
// Pad is a function that pads a string with another string.
38+
type Pad struct {
39+
str sql.Expression
40+
length sql.Expression
41+
padStr sql.Expression
42+
padType padType
43+
}
44+
45+
// Children implements the Expression interface.
46+
func (p *Pad) Children() []sql.Expression {
47+
return []sql.Expression{p.str, p.length, p.padStr}
48+
}
49+
50+
// Resolved implements the Expression interface.
51+
func (p *Pad) Resolved() bool {
52+
return p.str.Resolved() && p.length.Resolved() && (p.padStr.Resolved())
53+
}
54+
55+
// IsNullable implements the Expression interface.
56+
func (p *Pad) IsNullable() bool {
57+
return p.str.IsNullable() || p.length.IsNullable() || p.padStr.IsNullable()
58+
}
59+
60+
// Type implements the Expression interface.
61+
func (p *Pad) Type() sql.Type { return sql.Text }
62+
63+
func (p *Pad) String() string {
64+
if p.padType == lPadType {
65+
return fmt.Sprintf("lpad(%s, %s, %s)", p.str, p.length, p.padStr)
66+
}
67+
return fmt.Sprintf("rpad(%s, %s, %s)", p.str, p.length, p.padStr)
68+
}
69+
70+
// TransformUp implements the Expression interface.
71+
func (p *Pad) TransformUp(f sql.TransformExprFunc) (sql.Expression, error) {
72+
str, err := p.str.TransformUp(f)
73+
if err != nil {
74+
return nil, err
75+
}
76+
77+
len, err := p.length.TransformUp(f)
78+
if err != nil {
79+
return nil, err
80+
}
81+
82+
padStr, err := p.padStr.TransformUp(f)
83+
if err != nil {
84+
return nil, err
85+
}
86+
padded, _ := NewPad(p.padType, str, len, padStr)
87+
return f(padded)
88+
}
89+
90+
// Eval implements the Expression interface.
91+
func (p *Pad) Eval(
92+
ctx *sql.Context,
93+
row sql.Row,
94+
) (interface{}, error) {
95+
str, err := p.str.Eval(ctx, row)
96+
if err != nil {
97+
return nil, err
98+
}
99+
100+
if str == nil {
101+
return nil, nil
102+
}
103+
104+
str, err = sql.Text.Convert(str)
105+
if err != nil {
106+
return nil, sql.ErrInvalidType.New(reflect.TypeOf(str))
107+
}
108+
109+
length, err := p.length.Eval(ctx, row)
110+
if err != nil {
111+
return nil, err
112+
}
113+
114+
if length == nil {
115+
return nil, nil
116+
}
117+
118+
length, err = sql.Int64.Convert(length)
119+
if err != nil {
120+
return nil, err
121+
}
122+
123+
padStr, err := p.padStr.Eval(ctx, row)
124+
if err != nil {
125+
return nil, err
126+
}
127+
128+
if padStr == nil {
129+
return nil, nil
130+
}
131+
132+
padStr, err = sql.Text.Convert(padStr)
133+
if err != nil {
134+
return nil, err
135+
}
136+
137+
return padString(str.(string), length.(int64), padStr.(string), p.padType)
138+
}
139+
140+
func padString(str string, length int64, padStr string, padType padType) (string, error) {
141+
if length <= 0 {
142+
return "", nil
143+
}
144+
if int64(len(str)) >= length {
145+
return str[:length], nil
146+
}
147+
if len(padStr) == 0 {
148+
return "", nil
149+
}
150+
151+
padLen := int(length - int64(len(str)))
152+
quo, rem, err := divmod(int64(padLen), int64(len(padStr)))
153+
if err != nil {
154+
return "", err
155+
}
156+
157+
if padType == lPadType {
158+
result := strings.Repeat(padStr, int(quo)) + padStr[:rem] + str
159+
return result[:length], nil
160+
}
161+
result := str + strings.Repeat(padStr, int(quo)) + padStr[:rem]
162+
return result[(int64(len(result)) - length):], nil
163+
}
164+
165+
func divmod(a, b int64) (quotient, remainder int64, err error) {
166+
if b == 0 {
167+
return 0, 0, ErrDivisionByZero.New()
168+
}
169+
quotient = a / b
170+
remainder = a % b
171+
return
172+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package function
2+
3+
import (
4+
"testing"
5+
6+
"gopkg.in/src-d/go-mysql-server.v0/sql"
7+
"gopkg.in/src-d/go-mysql-server.v0/sql/expression"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestLPad(t *testing.T) {
12+
f, err := NewPad(
13+
lPadType,
14+
expression.NewGetField(0, sql.Text, "str", false),
15+
expression.NewGetField(1, sql.Int64, "len", false),
16+
expression.NewGetField(2, sql.Text, "padStr", false),
17+
)
18+
require.NoError(t, err)
19+
testCases := []struct {
20+
name string
21+
row sql.Row
22+
expected interface{}
23+
err bool
24+
}{
25+
{"null string", sql.NewRow(nil, 1, "bar"), nil, false},
26+
{"null len", sql.NewRow("foo", nil, "bar"), nil, false},
27+
{"null padStr", sql.NewRow("foo", 1, nil), nil, false},
28+
29+
{"negative length", sql.NewRow("foo", -1, "bar"), "", false},
30+
{"length 0", sql.NewRow("foo", 0, "bar"), "", false},
31+
{"invalid length", sql.NewRow("foo", "a", "bar"), "", true},
32+
33+
{"empty padStr and len < len(str)", sql.NewRow("foo", 1, ""), "f", false},
34+
{"empty padStr and len > len(str)", sql.NewRow("foo", 4, ""), "", false},
35+
{"empty padStr and len == len(str)", sql.NewRow("foo", 3, ""), "foo", false},
36+
37+
{"non empty padStr and len < len(str)", sql.NewRow("foo", 1, "abcd"), "f", false},
38+
{"non empty padStr and len == len(str)", sql.NewRow("foo", 3, "abcd"), "foo", false},
39+
40+
{"padStr repeats exactly once", sql.NewRow("foo", 6, "abc"), "abcfoo", false},
41+
{"padStr does not repeat once", sql.NewRow("foo", 5, "abc"), "abfoo", false},
42+
{"padStr repeats many times", sql.NewRow("foo", 10, "abc"), "abcabcafoo", false},
43+
}
44+
for _, tt := range testCases {
45+
t.Run(tt.name, func(t *testing.T) {
46+
t.Helper()
47+
require := require.New(t)
48+
ctx := sql.NewEmptyContext()
49+
50+
v, err := f.Eval(ctx, tt.row)
51+
if tt.err {
52+
require.Error(err)
53+
} else {
54+
require.NoError(err)
55+
require.Equal(tt.expected, v)
56+
}
57+
})
58+
}
59+
}
60+
61+
func TestRPad(t *testing.T) {
62+
f, err := NewPad(
63+
rPadType,
64+
expression.NewGetField(0, sql.Text, "str", false),
65+
expression.NewGetField(1, sql.Int64, "len", false),
66+
expression.NewGetField(2, sql.Text, "padStr", false),
67+
)
68+
require.NoError(t, err)
69+
testCases := []struct {
70+
name string
71+
row sql.Row
72+
expected interface{}
73+
err bool
74+
}{
75+
{"null string", sql.NewRow(nil, 1, "bar"), nil, false},
76+
{"null len", sql.NewRow("foo", nil, "bar"), nil, false},
77+
{"null padStr", sql.NewRow("foo", 1, nil), nil, false},
78+
79+
{"negative length", sql.NewRow("foo", -1, "bar"), "", false},
80+
{"length 0", sql.NewRow("foo", 0, "bar"), "", false},
81+
{"invalid length", sql.NewRow("foo", "a", "bar"), "", true},
82+
83+
{"empty padStr and len < len(str)", sql.NewRow("foo", 1, ""), "f", false},
84+
{"empty padStr and len > len(str)", sql.NewRow("foo", 4, ""), "", false},
85+
{"empty padStr and len == len(str)", sql.NewRow("foo", 3, ""), "foo", false},
86+
87+
{"non empty padStr and len < len(str)", sql.NewRow("foo", 1, "abcd"), "f", false},
88+
{"non empty padStr and len == len(str)", sql.NewRow("foo", 3, "abcd"), "foo", false},
89+
90+
{"padStr repeats exactly once", sql.NewRow("foo", 6, "abc"), "fooabc", false},
91+
{"padStr does not repeat once", sql.NewRow("foo", 5, "abc"), "fooab", false},
92+
{"padStr repeats many times", sql.NewRow("foo", 10, "abc"), "fooabcabca", false},
93+
}
94+
for _, tt := range testCases {
95+
t.Run(tt.name, func(t *testing.T) {
96+
t.Helper()
97+
require := require.New(t)
98+
ctx := sql.NewEmptyContext()
99+
100+
v, err := f.Eval(ctx, tt.row)
101+
if tt.err {
102+
require.Error(err)
103+
} else {
104+
require.NoError(err)
105+
require.Equal(tt.expected, v)
106+
}
107+
})
108+
}
109+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy