Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add trim, ltrim, rtrim functions #512

Merged
merged 2 commits into from
Nov 18, 2023
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
6 changes: 4 additions & 2 deletions cmd/genji/doc/doc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ func TestFunctions(t *testing.T) {
t.Run(fmt.Sprintf("%s.%s is documented and has all its arguments mentioned", pkgname, fname), func(t *testing.T) {
str, err := doc.DocString(fmt.Sprintf("%s.%s", pkgname, fname))
assert.NoError(t, err)
for i := 0; i < def.Arity(); i++ {
require.Contains(t, trimDocPromt(str), fmt.Sprintf("arg%d", i+1))
if def.Arity() > 0 {
for i := 0; i < def.Arity(); i++ {
require.Contains(t, trimDocPromt(str), fmt.Sprintf("arg%d", i+1))
}
}
})
}
Expand Down
3 changes: 3 additions & 0 deletions cmd/genji/doc/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,7 @@ var mathDocs = functionDocs{
var stringsDocs = functionDocs{
"lower": "The lower function returns arg1 to lower-case if arg1 evals to string",
"upper": "The upper function returns arg1 to upper-case if arg1 evals to string",
"trim": "The trim function returns arg1 with leading and trailing characters removed. space by default or arg2",
"ltrim": "The ltrim function returns arg1 with leading characters removed. space by default or arg2",
"rtrim": "The rtrim function returns arg1 with trailing characters removed. space by default or arg2",
}
16 changes: 12 additions & 4 deletions internal/expr/functions/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@

func DefaultPackages() Packages {
return Packages{
"": BuiltinDefinitions(),
"math": MathFunctions(),
"": BuiltinDefinitions(),
"math": MathFunctions(),
"strings": StringsDefinitions(),
}
}
Expand Down Expand Up @@ -57,15 +57,23 @@
}

func (fd *definition) Function(args ...expr.Expr) (expr.Function, error) {
if fd.arity == -1 {
return fd.constructorFn(args...)
}

if len(args) != fd.arity {
return nil, fmt.Errorf("%s() takes %d argument(s), not %d", fd.name, fd.arity, len(args))
}
return fd.constructorFn(args...)
}

func (fd *definition) String() string {
args := make([]string, 0, fd.arity)
for i := 0; i < fd.arity; i++ {
arity := fd.arity
if arity < 0 {
arity = 0

Check warning on line 73 in internal/expr/functions/definition.go

View check run for this annotation

Codecov / codecov/patch

internal/expr/functions/definition.go#L73

Added line #L73 was not covered by tests
}
args := make([]string, 0, arity)
for i := 0; i < arity; i++ {
args = append(args, fmt.Sprintf("arg%d", i+1))
}
return fmt.Sprintf("%s(%s)", fd.name, strings.Join(args, ", "))
Expand Down
97 changes: 97 additions & 0 deletions internal/expr/functions/strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,27 @@
return &Upper{Expr: args[0]}, nil
},
},
"trim": &definition{
name: "trim",
arity: -1,
constructorFn: func(args ...expr.Expr) (expr.Function, error) {
return &Trim{Expr: args, TrimFunc: strings.Trim, Name: "TRIM"}, nil
},
},
"ltrim": &definition{
name: "ltrim",
arity: -1,
constructorFn: func(args ...expr.Expr) (expr.Function, error) {
return &Trim{Expr: args, TrimFunc: strings.TrimLeft, Name: "LTRIM"}, nil
},
},
"rtrim": &definition{
name: "rtrim",
arity: -1,
constructorFn: func(args ...expr.Expr) (expr.Function, error) {
return &Trim{Expr: args, TrimFunc: strings.TrimRight, Name: "RTRIM"}, nil
},
},
}

func StringsDefinitions() Definitions {
Expand Down Expand Up @@ -109,3 +130,79 @@
func (s *Upper) String() string {
return fmt.Sprintf("UPPER(%v)", s.Expr)
}

// TRIM removes leading and trailing characters from a string based on the given input.
// LTRIM removes leading characters
// RTRIM removes trailing characters
// By default remove space " "
type Trim struct {
Expr []expr.Expr
TrimFunc TrimFunc
Name string
}

type TrimFunc func(string, string) string

func (s *Trim) Eval(env *environment.Environment) (types.Value, error) {
if len(s.Expr) > 2 {
return nil, fmt.Errorf("misuse of string function %v()", s.Name)

Check warning on line 148 in internal/expr/functions/strings.go

View check run for this annotation

Codecov / codecov/patch

internal/expr/functions/strings.go#L148

Added line #L148 was not covered by tests
}

input, err := s.Expr[0].Eval(env)
if err != nil {
return nil, err

Check warning on line 153 in internal/expr/functions/strings.go

View check run for this annotation

Codecov / codecov/patch

internal/expr/functions/strings.go#L153

Added line #L153 was not covered by tests
}

if input.Type() != types.TextValue {
return types.NewNullValue(), nil
}

var cutset = " "

if len(s.Expr) == 2 {
remove, err := s.Expr[1].Eval(env)
if err != nil {
return nil, err

Check warning on line 165 in internal/expr/functions/strings.go

View check run for this annotation

Codecov / codecov/patch

internal/expr/functions/strings.go#L165

Added line #L165 was not covered by tests
}
if remove.Type() != types.TextValue {
return types.NewNullValue(), nil
}
cutset = types.As[string](remove)
}

trimmed := s.TrimFunc(types.As[string](input), cutset)

return types.NewTextValue(trimmed), nil
}

func (s *Trim) IsEqual(other expr.Expr) bool {
if other == nil {
return false

Check warning on line 180 in internal/expr/functions/strings.go

View check run for this annotation

Codecov / codecov/patch

internal/expr/functions/strings.go#L178-L180

Added lines #L178 - L180 were not covered by tests
}
o, ok := other.(*Trim)
if !ok {
return false

Check warning on line 184 in internal/expr/functions/strings.go

View check run for this annotation

Codecov / codecov/patch

internal/expr/functions/strings.go#L182-L184

Added lines #L182 - L184 were not covered by tests
}
if len(s.Expr) != len(o.Expr) {
return false

Check warning on line 187 in internal/expr/functions/strings.go

View check run for this annotation

Codecov / codecov/patch

internal/expr/functions/strings.go#L186-L187

Added lines #L186 - L187 were not covered by tests
}

for i := range s.Expr {
if !expr.Equal(s.Expr[i], o.Expr[i]) {
return false

Check warning on line 192 in internal/expr/functions/strings.go

View check run for this annotation

Codecov / codecov/patch

internal/expr/functions/strings.go#L190-L192

Added lines #L190 - L192 were not covered by tests
}
}

return true

Check warning on line 196 in internal/expr/functions/strings.go

View check run for this annotation

Codecov / codecov/patch

internal/expr/functions/strings.go#L196

Added line #L196 was not covered by tests
}

func (s *Trim) Params() []expr.Expr {
return s.Expr
}

func (s *Trim) String() string {
if len(s.Expr) == 1 {
return fmt.Sprintf("%v(%v)", s.Name, s.Expr[0])
}
return fmt.Sprintf("%v(%v, %v)", s.Name, s.Expr[0], s.Expr[1])
}
112 changes: 112 additions & 0 deletions sqltests/SELECT/STRINGS/ltrim.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
-- setup:
CREATE TABLE test(
a TEXT
);

INSERT INTO test (a) VALUES (" hello "), ("!hello!"), (" !hello! ");

-- test: LTRIM TEXT default
SELECT strings.LTRIM(a) FROM test;
/* result:
{
"LTRIM(a)": "hello "
}
{
"LTRIM(a)": "!hello!"
}
{
"LTRIM(a)": "!hello! "
}
*/


-- test: LTRIM TEXT with param
SELECT strings.LTRIM(a, "!") FROM test;
/* result:
{
"LTRIM(a, \"!\")": " hello "
}
{
"LTRIM(a, \"!\")": "hello!"
}
{
"LTRIM(a, \"!\")": " !hello! "
}
*/

-- test: LTRIM TEXT with multiple char params
SELECT strings.LTRIM(a, " !") FROM test;
/* result:
{
"LTRIM(a, \" !\")": "hello "
}
{
"LTRIM(a, \" !\")": "hello!"
}
{
"LTRIM(a, \" !\")": "hello! "
}
*/


-- test: LTRIM TEXT with multiple char params
SELECT strings.LTRIM(a, "hel !") FROM test;
/* result:
{
"LTRIM(a, \"hel !\")": "o "
}
{
"LTRIM(a, \"hel !\")": "o!"
}
{
"LTRIM(a, \"hel !\")": "o! "
}
*/


-- test: LTRIM BOOL
SELECT strings.LTRIM(true);
/* result:
{
"LTRIM(true)": NULL
}
*/

-- test: LTRIM INT
SELECT strings.LTRIM(42);
/* result:
{
"LTRIM(42)": NULL
}
*/

-- test: LTRIM DOUBLE
SELECT strings.LTRIM(42.42);
/* result:
{
"LTRIM(42.42)": NULL
}
*/

-- test: LTRIM ARRAY
SELECT strings.LTRIM([1, 2]);
/* result:
{
"LTRIM([1, 2])": NULL
}
*/
-- test: LTRIM DOCUMENT
SELECT strings.LTRIM({a: 1});
/* result:
{
"LTRIM({a: 1})": NULL
}
*/

-- test: LTRIM STRING wrong param
SELECT strings.LTRIM(" hello ", 42);
/* result:
{
"LTRIM(\" hello \", 42)": NULL
}
*/
112 changes: 112 additions & 0 deletions sqltests/SELECT/STRINGS/rtrim.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
-- setup:
CREATE TABLE test(
a TEXT
);

INSERT INTO test (a) VALUES (" hello "), ("!hello!"), (" !hello! ");

-- test: RTRIM TEXT default
SELECT strings.RTRIM(a) FROM test;
/* result:
{
"RTRIM(a)": " hello"
}
{
"RTRIM(a)": "!hello!"
}
{
"RTRIM(a)": " !hello!"
}
*/


-- test: RTRIM TEXT with param
SELECT strings.RTRIM(a, "!") FROM test;
/* result:
{
"RTRIM(a, \"!\")": " hello "
}
{
"RTRIM(a, \"!\")": "!hello"
}
{
"RTRIM(a, \"!\")": " !hello! "
}
*/

-- test: RTRIM TEXT with multiple char params
SELECT strings.RTRIM(a, " !") FROM test;
/* result:
{
"RTRIM(a, \" !\")": " hello"
}
{
"RTRIM(a, \" !\")": "!hello"
}
{
"RTRIM(a, \" !\")": " !hello"
}
*/


-- test: RTRIM TEXT with multiple char params
SELECT strings.RTRIM(a, "hel !") FROM test;
/* result:
{
"RTRIM(a, \"hel !\")": " hello"
}
{
"RTRIM(a, \"hel !\")": "!hello"
}
{
"RTRIM(a, \"hel !\")": " !hello"
}
*/


-- test: RTRIM BOOL
SELECT strings.RTRIM(true);
/* result:
{
"RTRIM(true)": NULL
}
*/

-- test: RTRIM INT
SELECT strings.RTRIM(42);
/* result:
{
"RTRIM(42)": NULL
}
*/

-- test: RTRIM DOUBLE
SELECT strings.RTRIM(42.42);
/* result:
{
"RTRIM(42.42)": NULL
}
*/

-- test: RTRIM ARRAY
SELECT strings.RTRIM([1, 2]);
/* result:
{
"RTRIM([1, 2])": NULL
}
*/
-- test: RTRIM DOCUMENT
SELECT strings.RTRIM({a: 1});
/* result:
{
"RTRIM({a: 1})": NULL
}
*/

-- test: RTRIM STRING wrong param
SELECT strings.RTRIM(" hello ", 42);
/* result:
{
"RTRIM(\" hello \", 42)": NULL
}
*/
Loading