Skip to content

Commit

Permalink
add LOWER function (#510)
Browse files Browse the repository at this point in the history
  • Loading branch information
agonist committed Nov 17, 2023
1 parent f94c703 commit b034800
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 1 deletion.
3 changes: 2 additions & 1 deletion cmd/genji/doc/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ var builtinDocs = functionDocs{
"sum": "The sum function returns the sum of all values taken by the arg1 expression in a group.",
"avg": "The avg function returns the average of all values taken by the arg1 expression in a group.",
"typeof": "The typeof function returns the type of arg1.",
"len": "Then len function returns length of the arg1 expression if arg1 evals to string, array or document, either returns NULL.",
"len": "The len function returns length of the arg1 expression if arg1 evals to string, array or document, either returns NULL.",
"lower": "The lower function returns arg1 to lower-case if arg1 evals to string",
}

var mathDocs = functionDocs{
Expand Down
48 changes: 48 additions & 0 deletions internal/expr/functions/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package functions

import (
"fmt"
"strings"

"github.com/cockroachdb/errors"
"github.com/genjidb/genji/document"
Expand Down Expand Up @@ -67,6 +68,13 @@ var builtinFunctions = Definitions{
return &Len{Expr: args[0]}, nil
},
},
"lower": &definition{
name: "lower",
arity: 1,
constructorFn: func(args ...expr.Expr) (expr.Function, error) {
return &Lower{Expr: args[0]}, nil
},
},
}

// BuiltinDefinitions returns a map of builtin functions.
Expand Down Expand Up @@ -717,3 +725,43 @@ func (s *Len) Params() []expr.Expr { return []expr.Expr{s.Expr} }
func (s *Len) String() string {
return fmt.Sprintf("LEN(%v)", s.Expr)
}

// Lower is the LOWER function
// It returns the lower-case version of a string
type Lower struct {
Expr expr.Expr
}

func (s* Lower) Eval(env *environment.Environment) (types.Value, error) {
val, err := s.Expr.Eval(env)
if err != nil {
return nil, err
}

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

lowerCaseString := strings.ToLower(types.As[string](val))

return types.NewTextValue(lowerCaseString), nil
}

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

o, ok := other.(*Lower)
if !ok {
return false
}

return expr.Equal(s.Expr, o.Expr)
}

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

func (s *Lower) String() string {
return fmt.Sprintf("LOWER(%v)", s.Expr)
}
113 changes: 113 additions & 0 deletions sqltests/SELECT/lower.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
-- setup:
CREATE TABLE test(
a TEXT,
b INT,
c BOOL,
d DOUBLE,
e ARRAY,
f (
...
)
);

INSERT INTO test (a, b, c, d, e, f) VALUES (
"FOO",
42,
true,
42.42,
["A", "b", "C", "d", "E"],
{
a: "HELLO",
b: "WorlD"
}
);

-- test: TEXT value
SELECT LOWER(a) FROM test;
/* result:
{
"LOWER(a)": "foo"
}
*/


-- test: INT value
SELECT LOWER(b) FROM test;
/* result:
{
"LOWER(b)": NULL
}
*/


-- test: BOOL value
SELECT LOWER(c) FROM test;
/* result:
{
"LOWER(c)": NULL
}
*/

-- test: DOUBLE value
SELECT LOWER(d) FROM test;
/* result:
{
"LOWER(d)": NULL
}
*/

-- test: ARRAY value
SELECT LOWER(e) FROM test;
/* result:
{
"LOWER(e)": NULL
}
*/

-- test: DOCUMENT value
SELECT LOWER(f) FROM test;
/* result:
{
"LOWER(f)": NULL
}
*/

-- test: cast INT
SELECT LOWER(CAST(b as TEXT)) FROM test;
/* result:
{
"LOWER(CAST(b AS text))": "42"
}
*/

-- test: cast BOOL
SELECT LOWER(CAST(c as TEXT)) FROM test;
/* result:
{
"LOWER(CAST(c AS text))": "true"
}
*/

-- test: cast DOUBLE
SELECT LOWER(CAST(d as TEXT)) FROM test;
/* result:
{
"LOWER(CAST(d AS text))": "42.42"
}
*/

-- test: cast ARRAY
SELECT LOWER(CAST(e as TEXT)) FROM test;
/* result:
{
"LOWER(CAST(e AS text))": "[\"a\", \"b\", \"c\", \"d\", \"e\"]"
}
*/

-- test: cast DOCUMENT
SELECT LOWER(CAST(f as TEXT)) FROM test;
/* result:
{
"LOWER(CAST(f AS text))": "{\"a\": \"hello\", \"b\": \"world\"}"
}
*/

0 comments on commit b034800

Please sign in to comment.