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 1 commit
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",
}
11 changes: 9 additions & 2 deletions internal/expr/functions/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ type Packages map[string]Definitions

func DefaultPackages() Packages {
return Packages{
"": BuiltinDefinitions(),
"math": MathFunctions(),
"": BuiltinDefinitions(),
"math": MathFunctions(),
"strings": StringsDefinitions(),
}
}
Expand Down Expand Up @@ -57,13 +57,20 @@ func (fd *definition) Name() string {
}

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 {
if fd.arity < 0 {
fd.arity = 0
agonist marked this conversation as resolved.
Show resolved Hide resolved
}
args := make([]string, 0, fd.arity)
for i := 0; i < fd.arity; i++ {
args = append(args, fmt.Sprintf("arg%d", i+1))
Expand Down
90 changes: 90 additions & 0 deletions internal/expr/functions/strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,27 @@ var stringsFunctions = Definitions{
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,72 @@ func (s *Upper) Params() []expr.Expr { return []expr.Expr{s.Expr} }
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)
}

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

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
}
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 {
agonist marked this conversation as resolved.
Show resolved Hide resolved
if other == nil || len(s.Expr) != len(other){
return false
}

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

return true
}

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