mirror of https://github.com/pikami/cosmium.git
Implement IS_DEFINED function
This commit is contained in:
parent
c17509df38
commit
f4dd150bc8
|
@ -81,6 +81,7 @@ type FunctionCallType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
FunctionCallStringEquals FunctionCallType = "StringEquals"
|
FunctionCallStringEquals FunctionCallType = "StringEquals"
|
||||||
|
FunctionCallIsDefined FunctionCallType = "IsDefined"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FunctionCall struct {
|
type FunctionCall struct {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -15,6 +15,8 @@ func makeSelectStmt(columns, table, whereClause interface{}, count interface{},
|
||||||
selectStmt.Filters = filters
|
selectStmt.Filters = filters
|
||||||
} else if filters, ok := whereClause.(parsers.Constant); ok {
|
} else if filters, ok := whereClause.(parsers.Constant); ok {
|
||||||
selectStmt.Filters = filters
|
selectStmt.Filters = filters
|
||||||
|
} else if filters, ok := whereClause.(parsers.SelectItem); ok {
|
||||||
|
selectStmt.Filters = filters
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, ok := count.(int); ok {
|
if n, ok := count.(int); ok {
|
||||||
|
@ -186,7 +188,7 @@ SelectProperty <- name:Identifier path:(DotFieldAccess / ArrayFieldAccess)* {
|
||||||
return makeSelectItem(name, path, parsers.SelectItemTypeField)
|
return makeSelectItem(name, path, parsers.SelectItemTypeField)
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectItem <- selectItem:(Literal / StringEqualsExpression / SelectArray / SelectObject / SelectProperty) asClause:AsClause? {
|
SelectItem <- selectItem:(Literal / FunctionCall / SelectArray / SelectObject / SelectProperty) asClause:AsClause? {
|
||||||
var itemResult parsers.SelectItem
|
var itemResult parsers.SelectItem
|
||||||
switch typedValue := selectItem.(type) {
|
switch typedValue := selectItem.(type) {
|
||||||
case parsers.SelectItem:
|
case parsers.SelectItem:
|
||||||
|
@ -240,6 +242,7 @@ ComparisonExpression <- "(" ws ex:OrExpression ws ")" { return ex, nil }
|
||||||
/ left:SelectItem ws op:ComparisonOperator ws right:SelectItem {
|
/ left:SelectItem ws op:ComparisonOperator ws right:SelectItem {
|
||||||
return parsers.ComparisonExpression{Left:left,Right:right,Operation:string(op.([]uint8))}, nil
|
return parsers.ComparisonExpression{Left:left,Right:right,Operation:string(op.([]uint8))}, nil
|
||||||
} / ex:BooleanLiteral { return ex, nil }
|
} / ex:BooleanLiteral { return ex, nil }
|
||||||
|
/ ex:SelectItem { return ex, nil }
|
||||||
|
|
||||||
OrderByClause <- OrderBy ws ex1:OrderExpression others:(ws "," ws ex:OrderExpression { return ex, nil })* {
|
OrderByClause <- OrderBy ws ex1:OrderExpression others:(ws "," ws ex:OrderExpression { return ex, nil })* {
|
||||||
return makeOrderByClause(ex1, others)
|
return makeOrderByClause(ex1, others)
|
||||||
|
@ -303,10 +306,16 @@ BooleanLiteral <- ("true"i / "false"i) {
|
||||||
return parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: boolValue}, nil
|
return parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: boolValue}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FunctionCall <- StringEqualsExpression / IsDefined
|
||||||
|
|
||||||
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 parsers.FunctionCall{Type: parsers.FunctionCallStringEquals, Arguments: []interface{}{ex1, ex2, ignoreCase}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IsDefined <- "IS_DEFINED"i ws "(" ws ex:SelectItem ws ")" {
|
||||||
|
return parsers.FunctionCall{Type: parsers.FunctionCallIsDefined, Arguments: []interface{}{ex}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
Integer <- [0-9]+ {
|
Integer <- [0-9]+ {
|
||||||
return strconv.Atoi(string(c.text))
|
return strconv.Atoi(string(c.text))
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
|
|
||||||
func Test_Execute_StringFunctions(t *testing.T) {
|
func Test_Execute_StringFunctions(t *testing.T) {
|
||||||
|
|
||||||
t.Run("Should execute function STRINGEQUALS(ex1, ex2, ignoreCase)", func(t *testing.T) {
|
t.Run("Should parse function STRINGEQUALS(ex1, ex2, ignoreCase)", func(t *testing.T) {
|
||||||
testQueryParse(
|
testQueryParse(
|
||||||
t,
|
t,
|
||||||
`SELECT STRINGEQUALS(c.id, "123", true) FROM c`,
|
`SELECT STRINGEQUALS(c.id, "123", true) FROM c`,
|
||||||
|
@ -46,7 +46,7 @@ func Test_Execute_StringFunctions(t *testing.T) {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should execute function STRINGEQUALS(ex1, ex2)", func(t *testing.T) {
|
t.Run("Should parse function STRINGEQUALS(ex1, ex2)", func(t *testing.T) {
|
||||||
testQueryParse(
|
testQueryParse(
|
||||||
t,
|
t,
|
||||||
`SELECT STRINGEQUALS(c.id, "123") FROM c`,
|
`SELECT STRINGEQUALS(c.id, "123") FROM c`,
|
|
@ -0,0 +1,46 @@
|
||||||
|
package nosql_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pikami/cosmium/parsers"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Execute_TypeCheckingFunctions(t *testing.T) {
|
||||||
|
|
||||||
|
t.Run("Should parse function IS_DEFINED", func(t *testing.T) {
|
||||||
|
testQueryParse(
|
||||||
|
t,
|
||||||
|
`SELECT IS_DEFINED(c.id) FROM c WHERE IS_DEFINED(c.pk)`,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallIsDefined,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
parsers.SelectItem{
|
||||||
|
Path: []string{"c", "id"},
|
||||||
|
Type: parsers.SelectItemTypeField,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
Filters: parsers.SelectItem{
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallIsDefined,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
parsers.SelectItem{
|
||||||
|
Path: []string{"c", "pk"},
|
||||||
|
Type: parsers.SelectItemTypeField,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package memoryexecutor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -122,6 +123,11 @@ func evaluateFilters(expr ExpressionType, queryParameters map[string]interface{}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
case parsers.SelectItem:
|
||||||
|
resolvedValue := getFieldValue(typedValue, queryParameters, row)
|
||||||
|
if value, ok := resolvedValue.(bool); ok {
|
||||||
|
return value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -172,6 +178,8 @@ func getFieldValue(field parsers.SelectItem, queryParameters map[string]interfac
|
||||||
switch typedValue.Type {
|
switch typedValue.Type {
|
||||||
case parsers.FunctionCallStringEquals:
|
case parsers.FunctionCallStringEquals:
|
||||||
return strings_StringEquals(typedValue.Arguments, queryParameters, row)
|
return strings_StringEquals(typedValue.Arguments, queryParameters, row)
|
||||||
|
case parsers.FunctionCallIsDefined:
|
||||||
|
return typeChecking_IsDefined(typedValue.Arguments, queryParameters, row)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,6 +232,10 @@ func orderBy(orderBy []parsers.OrderExpression, queryParameters map[string]inter
|
||||||
}
|
}
|
||||||
|
|
||||||
func compareValues(val1, val2 interface{}) int {
|
func compareValues(val1, val2 interface{}) int {
|
||||||
|
if reflect.TypeOf(val1) != reflect.TypeOf(val2) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
switch val1 := val1.(type) {
|
switch val1 := val1.(type) {
|
||||||
case int:
|
case int:
|
||||||
val2 := val2.(int)
|
val2 := val2.(int)
|
||||||
|
@ -255,6 +267,9 @@ func compareValues(val1, val2 interface{}) int {
|
||||||
}
|
}
|
||||||
// TODO: Add more types
|
// TODO: Add more types
|
||||||
default:
|
default:
|
||||||
return 0
|
if reflect.DeepEqual(val1, val2) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
package memoryexecutor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pikami/cosmium/parsers"
|
||||||
|
)
|
||||||
|
|
||||||
|
func typeChecking_IsDefined(arguments []interface{}, queryParameters map[string]interface{}, row RowType) bool {
|
||||||
|
exItem := arguments[0].(parsers.SelectItem)
|
||||||
|
ex := getFieldValue(exItem, queryParameters, row)
|
||||||
|
|
||||||
|
return ex != nil
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package memoryexecutor_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pikami/cosmium/parsers"
|
||||||
|
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Execute_TypeCheckingFunctions(t *testing.T) {
|
||||||
|
mockData := []memoryexecutor.RowType{
|
||||||
|
map[string]interface{}{"id": "123", "pk": "aaa"},
|
||||||
|
map[string]interface{}{"id": "456"},
|
||||||
|
map[string]interface{}{"id": "789", "pk": ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("Should execute function IS_DEFINED(path)", func(t *testing.T) {
|
||||||
|
testQueryExecute(
|
||||||
|
t,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{Path: []string{"c", "id"}},
|
||||||
|
{
|
||||||
|
Alias: "defined",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallIsDefined,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
parsers.SelectItem{
|
||||||
|
Path: []string{"c", "pk"},
|
||||||
|
Type: parsers.SelectItemTypeField,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
},
|
||||||
|
mockData,
|
||||||
|
[]memoryexecutor.RowType{
|
||||||
|
map[string]interface{}{"id": "123", "defined": true},
|
||||||
|
map[string]interface{}{"id": "456", "defined": false},
|
||||||
|
map[string]interface{}{"id": "789", "defined": true},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue