Skip to content

Commit

Permalink
add trim, ltrim, rtrim functions (#512)
Browse files Browse the repository at this point in the history
  • Loading branch information
agonist authored Nov 18, 2023
1 parent c0397ab commit 6dc2964
Show file tree
Hide file tree
Showing 7 changed files with 452 additions and 6 deletions.
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 @@ type Packages map[string]Definitions

func DefaultPackages() Packages {
return Packages{
"": BuiltinDefinitions(),
"math": MathFunctions(),
"": BuiltinDefinitions(),
"math": MathFunctions(),
"strings": StringsDefinitions(),
}
}
Expand Down Expand Up @@ -57,15 +57,23 @@ 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 {
args := make([]string, 0, fd.arity)
for i := 0; i < fd.arity; i++ {
arity := fd.arity
if arity < 0 {
arity = 0
}
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 @@ 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,79 @@ 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 {
if other == nil {
return false
}
o, ok := other.(*Trim)
if !ok {
return false
}
if len(s.Expr) != len(o.Expr) {
return false
}

for i := range s.Expr {
if !expr.Equal(s.Expr[i], o.Expr[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

0 comments on commit 6dc2964

Please sign in to comment.