Implement LEFT,LENGTH,LTRIM,REPLACE,REPLICATE,REVERSE,RIGHT,RTRIM,SUBSTRING,TRIM functions

This commit is contained in:
Pijus Kamandulis 2024-02-24 21:24:20 +02:00
parent f356f26d26
commit 2431307a12
7 changed files with 2244 additions and 541 deletions

View File

@ -91,6 +91,16 @@ const (
FunctionCallIn FunctionCallType = "In" FunctionCallIn FunctionCallType = "In"
FunctionCallUpper FunctionCallType = "Upper" FunctionCallUpper FunctionCallType = "Upper"
FunctionCallLower FunctionCallType = "Lower" FunctionCallLower FunctionCallType = "Lower"
FunctionCallLeft FunctionCallType = "Left"
FunctionCallLength FunctionCallType = "Length"
FunctionCallLTrim FunctionCallType = "LTrim"
FunctionCallReplace FunctionCallType = "Replace"
FunctionCallReplicate FunctionCallType = "Replicate"
FunctionCallReverse FunctionCallType = "Reverse"
FunctionCallRight FunctionCallType = "Right"
FunctionCallRTrim FunctionCallType = "RTrim"
FunctionCallSubstring FunctionCallType = "Substring"
FunctionCallTrim FunctionCallType = "Trim"
) )
type FunctionCall struct { type FunctionCall struct {

File diff suppressed because it is too large Load Diff

View File

@ -107,6 +107,10 @@ func makeOrderExpression(field interface{}, order interface{}) (parsers.OrderExp
return value, nil return value, nil
} }
func createFunctionCall(functionType parsers.FunctionCallType, arguments []interface{}) (parsers.FunctionCall, error) {
return parsers.FunctionCall{Type: functionType, Arguments: arguments}, nil
}
func joinStrings(array []interface{}) string { func joinStrings(array []interface{}) string {
var stringsArray []string var stringsArray []string
for _, elem := range array { for _, elem := range array {
@ -314,26 +318,76 @@ StringFunctions <- StringEqualsExpression
/ ThreeArgumentStringFunctionExpression / ThreeArgumentStringFunctionExpression
/ UpperExpression / UpperExpression
/ LowerExpression / LowerExpression
/ LeftExpression
/ LengthExpression
/ LTrimExpression
/ ReplaceExpression
/ ReplicateExpression
/ ReverseExpression
/ RightExpression
/ RTrimExpression
/ SubstringExpression
/ TrimExpression
UpperExpression <- "UPPER"i ws "(" ex:SelectItem ")" { UpperExpression <- "UPPER"i ws "(" ex:SelectItem ")" {
return parsers.FunctionCall{Type: parsers.FunctionCallUpper, Arguments: []interface{}{ex}}, nil return createFunctionCall(parsers.FunctionCallUpper, []interface{}{ex})
} }
LowerExpression <- "LOWER"i ws "(" ex:SelectItem ")" { LowerExpression <- "LOWER"i ws "(" ex:SelectItem ")" {
return parsers.FunctionCall{Type: parsers.FunctionCallLower, Arguments: []interface{}{ex}}, nil return createFunctionCall(parsers.FunctionCallLower, []interface{}{ex})
} }
StringEqualsExpression <- StringEquals ws "(" ws ex1:SelectItem ws "," ws ex2:SelectItem ws ignoreCase:("," ws boolean:SelectItem { return boolean, nil })? ")" { StringEqualsExpression <- StringEquals ws "(" ws ex1:SelectItem ws "," ws ex2:SelectItem ws ignoreCase:("," ws boolean:SelectItem { return boolean, nil })? ")" {
return parsers.FunctionCall{Type: parsers.FunctionCallStringEquals, Arguments: []interface{}{ex1, ex2, ignoreCase}}, nil return createFunctionCall(parsers.FunctionCallStringEquals, []interface{}{ex1, ex2, ignoreCase})
} }
ToStringExpression <- "TOSTRING"i ws "(" ws ex:SelectItem ws ")" { ToStringExpression <- "TOSTRING"i ws "(" ws ex:SelectItem ws ")" {
return parsers.FunctionCall{Type: parsers.FunctionCallToString, Arguments: []interface{}{ex}}, nil return createFunctionCall(parsers.FunctionCallToString, []interface{}{ex})
} }
ConcatExpression <- "CONCAT"i ws "(" ws ex1:SelectItem others:(ws "," ws ex:SelectItem { return ex, nil })+ ws ")" { ConcatExpression <- "CONCAT"i ws "(" ws ex1:SelectItem others:(ws "," ws ex:SelectItem { return ex, nil })+ ws ")" {
arguments := append([]interface{}{ex1}, others.([]interface{})...) arguments := append([]interface{}{ex1}, others.([]interface{})...)
return parsers.FunctionCall{Type: parsers.FunctionCallConcat, Arguments: arguments}, nil return createFunctionCall(parsers.FunctionCallConcat, arguments)
}
LeftExpression <- "LEFT"i ws "(" ws ex:SelectItem ws "," ws length:SelectItem ws ")" {
return createFunctionCall(parsers.FunctionCallLeft, []interface{}{ex, length})
}
LengthExpression <- "LENGTH"i ws "(" ws ex:SelectItem ws ")" {
return createFunctionCall(parsers.FunctionCallLength, []interface{}{ex})
}
LTrimExpression <- "LTRIM"i ws "(" ws ex:SelectItem ws ")" {
return createFunctionCall(parsers.FunctionCallLTrim, []interface{}{ex})
}
ReplaceExpression <- "REPLACE"i ws "(" ws ex1:SelectItem ws "," ws ex2:SelectItem ws "," ws ex3:SelectItem ws ")" {
return createFunctionCall(parsers.FunctionCallReplace, []interface{}{ex1, ex2, ex3})
}
ReplicateExpression <- "REPLICATE"i ws "(" ws ex1:SelectItem ws "," ws ex2:SelectItem ws ")" {
return createFunctionCall(parsers.FunctionCallReplicate, []interface{}{ex1, ex2})
}
ReverseExpression <- "REVERSE"i ws "(" ws ex:SelectItem ws ")" {
return createFunctionCall(parsers.FunctionCallReverse, []interface{}{ex})
}
RightExpression <- "RIGHT"i ws "(" ws ex:SelectItem ws "," ws length:SelectItem ws ")" {
return createFunctionCall(parsers.FunctionCallRight, []interface{}{ex, length})
}
RTrimExpression <- "RTRIM"i ws "(" ws ex:SelectItem ws ")" {
return createFunctionCall(parsers.FunctionCallRTrim, []interface{}{ex})
}
SubstringExpression <- "SUBSTRING"i ws "(" ws ex:SelectItem ws "," ws startPos:SelectItem ws "," ws length:SelectItem ws ")" {
return createFunctionCall(parsers.FunctionCallSubstring, []interface{}{ex, startPos, length})
}
TrimExpression <- "TRIM"i ws "(" ws ex:SelectItem ws ")" {
return createFunctionCall(parsers.FunctionCallTrim, []interface{}{ex})
} }
ThreeArgumentStringFunctionExpression <- function:ThreeArgumentStringFunction ws "(" ws ex1:SelectItem ws "," ws ex2:SelectItem ws ignoreCase:("," ws boolean:SelectItem { return boolean, nil })? ")" { ThreeArgumentStringFunctionExpression <- function:ThreeArgumentStringFunction ws "(" ws ex1:SelectItem ws "," ws ex2:SelectItem ws ignoreCase:("," ws boolean:SelectItem { return boolean, nil })? ")" {

View File

@ -336,4 +336,293 @@ func Test_Execute_StringFunctions(t *testing.T) {
}, },
) )
}) })
t.Run("Should parse function LEFT()", func(t *testing.T) {
testQueryParse(
t,
`SELECT LEFT(c.id, 5) FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallLeft,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 5,
},
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse function LENGTH()", func(t *testing.T) {
testQueryParse(
t,
`SELECT LENGTH(c.id) FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallLength,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse function LTRIM()", func(t *testing.T) {
testQueryParse(
t,
`SELECT LTRIM(c.id) FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallLTrim,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse function REPLACE()", func(t *testing.T) {
testQueryParse(
t,
`SELECT REPLACE(c.id, "old", "new") FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallReplace,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "old",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "new",
},
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse function REPLICATE()", func(t *testing.T) {
testQueryParse(
t,
`SELECT REPLICATE(c.id, 3) FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallReplicate,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 3,
},
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse function REVERSE()", func(t *testing.T) {
testQueryParse(
t,
`SELECT REVERSE(c.id) FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallReverse,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse function RIGHT()", func(t *testing.T) {
testQueryParse(
t,
`SELECT RIGHT(c.id, 3) FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallRight,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 3,
},
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse function RTRIM()", func(t *testing.T) {
testQueryParse(
t,
`SELECT RTRIM(c.id) FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallRTrim,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse function SUBSTRING()", func(t *testing.T) {
testQueryParse(
t,
`SELECT SUBSTRING(c.id, 1, 5) FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallSubstring,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 1,
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 5,
},
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse function TRIM()", func(t *testing.T) {
testQueryParse(
t,
`SELECT TRIM(c.id) FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallTrim,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
} }

View File

@ -194,6 +194,26 @@ func getFieldValue(field parsers.SelectItem, queryParameters map[string]interfac
return strings_Upper(typedValue.Arguments, queryParameters, row) return strings_Upper(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallLower: case parsers.FunctionCallLower:
return strings_Lower(typedValue.Arguments, queryParameters, row) return strings_Lower(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallLeft:
return strings_Left(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallLength:
return strings_Length(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallLTrim:
return strings_LTrim(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallReplace:
return strings_Replace(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallReplicate:
return strings_Replicate(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallReverse:
return strings_Reverse(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallRight:
return strings_Right(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallRTrim:
return strings_RTrim(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallSubstring:
return strings_Substring(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallTrim:
return strings_Trim(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallIsDefined: case parsers.FunctionCallIsDefined:
return typeChecking_IsDefined(typedValue.Arguments, queryParameters, row) return typeChecking_IsDefined(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallIn: case parsers.FunctionCallIn:

View File

@ -9,9 +9,9 @@ import (
func Test_Execute_StringFunctions(t *testing.T) { func Test_Execute_StringFunctions(t *testing.T) {
mockData := []memoryexecutor.RowType{ mockData := []memoryexecutor.RowType{
map[string]interface{}{"id": "123", "pk": "aaa", "str": "hello", "rng_type": true}, map[string]interface{}{"id": "123", "pk": "aaa", "str": "hello", "rng_type": true, "str2": " hello "},
map[string]interface{}{"id": "456", "pk": "bbb", "str": "world", "rng_type": 159}, map[string]interface{}{"id": "456", "pk": "bbb", "str": "world", "rng_type": 159, "str2": " world "},
map[string]interface{}{"id": "789", "pk": "AAA", "str": "cool world"}, map[string]interface{}{"id": "789", "pk": "AAA", "str": "cool world", "str2": " cool world "},
} }
t.Run("Should execute function STRINGEQUALS(ex1, ex2, ignoreCase)", func(t *testing.T) { t.Run("Should execute function STRINGEQUALS(ex1, ex2, ignoreCase)", func(t *testing.T) {
@ -377,4 +377,393 @@ func Test_Execute_StringFunctions(t *testing.T) {
}, },
) )
}) })
t.Run("Should execute function LEFT()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
{
Alias: "left",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallLeft,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 3,
},
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"str": "hello", "left": "hel"},
map[string]interface{}{"str": "world", "left": "wor"},
map[string]interface{}{"str": "cool world", "left": "coo"},
},
)
})
t.Run("Should execute function LENGTH()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
{
Alias: "length",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallLength,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"str": "hello", "length": 5},
map[string]interface{}{"str": "world", "length": 5},
map[string]interface{}{"str": "cool world", "length": 10},
},
)
})
t.Run("Should execute function LTRIM()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
{
Alias: "ltrimmed",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallLTrim,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "str2"},
Type: parsers.SelectItemTypeField,
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"str": "hello", "ltrimmed": "hello "},
map[string]interface{}{"str": "world", "ltrimmed": "world "},
map[string]interface{}{"str": "cool world", "ltrimmed": "cool world "},
},
)
})
t.Run("Should execute function REPLACE()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
{
Alias: "replaced",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallReplace,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "world",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "universe",
},
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"str": "hello", "replaced": "hello"},
map[string]interface{}{"str": "world", "replaced": "universe"},
map[string]interface{}{"str": "cool world", "replaced": "cool universe"},
},
)
})
t.Run("Should execute function REPLICATE()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
{
Alias: "replicated",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallReplicate,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 3,
},
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"str": "hello", "replicated": "hellohellohello"},
map[string]interface{}{"str": "world", "replicated": "worldworldworld"},
map[string]interface{}{"str": "cool world", "replicated": "cool worldcool worldcool world"},
},
)
})
t.Run("Should execute function REVERSE()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
{
Alias: "reversed",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallReverse,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"str": "hello", "reversed": "olleh"},
map[string]interface{}{"str": "world", "reversed": "dlrow"},
map[string]interface{}{"str": "cool world", "reversed": "dlrow looc"},
},
)
})
t.Run("Should execute function RIGHT()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
{
Alias: "right",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallRight,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 3,
},
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"str": "hello", "right": "llo"},
map[string]interface{}{"str": "world", "right": "rld"},
map[string]interface{}{"str": "cool world", "right": "rld"},
},
)
})
t.Run("Should execute function RTRIM()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
{
Alias: "rtrimmed",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallRTrim,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "str2"},
Type: parsers.SelectItemTypeField,
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"str": "hello", "rtrimmed": " hello"},
map[string]interface{}{"str": "world", "rtrimmed": " world"},
map[string]interface{}{"str": "cool world", "rtrimmed": " cool world"},
},
)
})
t.Run("Should execute function SUBSTRING()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
{
Alias: "substring",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallSubstring,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 2,
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 4,
},
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"str": "hello", "substring": "llo"},
map[string]interface{}{"str": "world", "substring": "rld"},
map[string]interface{}{"str": "cool world", "substring": "ol w"},
},
)
})
t.Run("Should execute function TRIM()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
{
Alias: "trimmed",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallTrim,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "str2"},
Type: parsers.SelectItemTypeField,
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"str": "hello", "trimmed": "hello"},
map[string]interface{}{"str": "world", "trimmed": "world"},
map[string]interface{}{"str": "cool world", "trimmed": "cool world"},
},
)
})
} }

View File

@ -111,6 +111,139 @@ func strings_Lower(arguments []interface{}, queryParameters map[string]interface
return strings.ToLower(convertToString(value)) return strings.ToLower(convertToString(value))
} }
func strings_Left(arguments []interface{}, queryParameters map[string]interface{}, row RowType) string {
var ok bool
var length int
str := parseString(arguments[0], queryParameters, row)
lengthEx := getFieldValue(arguments[1].(parsers.SelectItem), queryParameters, row)
if length, ok = lengthEx.(int); !ok {
fmt.Println("strings_Left - got parameters of wrong type")
return ""
}
if length <= 0 {
return ""
}
if len(str) <= length {
return str
}
return str[:length]
}
func strings_Length(arguments []interface{}, queryParameters map[string]interface{}, row RowType) int {
str := parseString(arguments[0], queryParameters, row)
return len(str)
}
func strings_LTrim(arguments []interface{}, queryParameters map[string]interface{}, row RowType) string {
str := parseString(arguments[0], queryParameters, row)
return strings.TrimLeft(str, " ")
}
func strings_Replace(arguments []interface{}, queryParameters map[string]interface{}, row RowType) string {
str := parseString(arguments[0], queryParameters, row)
oldStr := parseString(arguments[1], queryParameters, row)
newStr := parseString(arguments[2], queryParameters, row)
return strings.Replace(str, oldStr, newStr, -1)
}
func strings_Replicate(arguments []interface{}, queryParameters map[string]interface{}, row RowType) string {
var ok bool
var times int
str := parseString(arguments[0], queryParameters, row)
timesEx := getFieldValue(arguments[1].(parsers.SelectItem), queryParameters, row)
if times, ok = timesEx.(int); !ok {
fmt.Println("strings_Replicate - got parameters of wrong type")
return ""
}
if times <= 0 {
return ""
}
if len(str) <= times {
return str
}
return strings.Repeat(str, times)
}
func strings_Reverse(arguments []interface{}, queryParameters map[string]interface{}, row RowType) string {
str := parseString(arguments[0], queryParameters, row)
runes := []rune(str)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
func strings_Right(arguments []interface{}, queryParameters map[string]interface{}, row RowType) string {
var ok bool
var length int
str := parseString(arguments[0], queryParameters, row)
lengthEx := getFieldValue(arguments[1].(parsers.SelectItem), queryParameters, row)
if length, ok = lengthEx.(int); !ok {
fmt.Println("strings_Right - got parameters of wrong type")
return ""
}
if length <= 0 {
return ""
}
if len(str) <= length {
return str
}
return str[len(str)-length:]
}
func strings_RTrim(arguments []interface{}, queryParameters map[string]interface{}, row RowType) string {
str := parseString(arguments[0], queryParameters, row)
return strings.TrimRight(str, " ")
}
func strings_Substring(arguments []interface{}, queryParameters map[string]interface{}, row RowType) string {
var ok bool
var startPos int
var length int
str := parseString(arguments[0], queryParameters, row)
startPosEx := getFieldValue(arguments[1].(parsers.SelectItem), queryParameters, row)
lengthEx := getFieldValue(arguments[2].(parsers.SelectItem), queryParameters, row)
if startPos, ok = startPosEx.(int); !ok {
fmt.Println("strings_Substring - got start parameters of wrong type")
return ""
}
if length, ok = lengthEx.(int); !ok {
fmt.Println("strings_Substring - got length parameters of wrong type")
return ""
}
if startPos >= len(str) {
return ""
}
endPos := startPos + length
if endPos > len(str) {
endPos = len(str)
}
return str[startPos:endPos]
}
func strings_Trim(arguments []interface{}, queryParameters map[string]interface{}, row RowType) string {
str := parseString(arguments[0], queryParameters, row)
return strings.TrimSpace(str)
}
func getBoolFlag(arguments []interface{}, queryParameters map[string]interface{}, row RowType) bool { func getBoolFlag(arguments []interface{}, queryParameters map[string]interface{}, row RowType) bool {
ignoreCase := false ignoreCase := false
if len(arguments) > 2 && arguments[2] != nil { if len(arguments) > 2 && arguments[2] != nil {