mirror of
https://github.com/pikami/cosmium.git
synced 2025-12-19 08:50:46 +00:00
Added support for arithmetics inside queries
This commit is contained in:
91
query_executors/memory_executor/arithmetics_test.go
Normal file
91
query_executors/memory_executor/arithmetics_test.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package memoryexecutor_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pikami/cosmium/parsers"
|
||||
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
|
||||
testutils "github.com/pikami/cosmium/test_utils"
|
||||
)
|
||||
|
||||
func Test_Execute_Arithmetics(t *testing.T) {
|
||||
mockData := []memoryexecutor.RowType{
|
||||
map[string]interface{}{"id": 1, "a": 420},
|
||||
map[string]interface{}{"id": 2, "a": 6.9},
|
||||
map[string]interface{}{"id": 3},
|
||||
}
|
||||
|
||||
t.Run("Should execute simple arithmetics", func(t *testing.T) {
|
||||
testQueryExecute(
|
||||
t,
|
||||
parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{
|
||||
Path: []string{"c", "id"},
|
||||
Type: parsers.SelectItemTypeField,
|
||||
},
|
||||
{
|
||||
Type: parsers.SelectItemTypeBinaryExpression,
|
||||
Alias: "result",
|
||||
Value: parsers.BinaryExpression{
|
||||
Operation: "+",
|
||||
Left: testutils.SelectItem_Path("c", "a"),
|
||||
Right: parsers.SelectItem{
|
||||
Type: parsers.SelectItemTypeBinaryExpression,
|
||||
Value: parsers.BinaryExpression{
|
||||
Operation: "*",
|
||||
Left: testutils.SelectItem_Constant_Float(2.0),
|
||||
Right: testutils.SelectItem_Constant_Int(3),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Table: parsers.Table{SelectItem: testutils.SelectItem_Path("c")},
|
||||
},
|
||||
mockData,
|
||||
[]memoryexecutor.RowType{
|
||||
map[string]interface{}{"id": 1, "result": 426.0},
|
||||
map[string]interface{}{"id": 2, "result": 12.9},
|
||||
map[string]interface{}{"id": 3, "result": nil},
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("Should execute arithmetics in WHERE clause", func(t *testing.T) {
|
||||
testQueryExecute(
|
||||
t,
|
||||
parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
testutils.SelectItem_Path("c", "id"),
|
||||
{
|
||||
Alias: "result",
|
||||
Type: parsers.SelectItemTypeBinaryExpression,
|
||||
Value: parsers.BinaryExpression{
|
||||
Operation: "*",
|
||||
Left: testutils.SelectItem_Path("c", "a"),
|
||||
Right: testutils.SelectItem_Constant_Int(2),
|
||||
},
|
||||
},
|
||||
},
|
||||
Table: parsers.Table{SelectItem: testutils.SelectItem_Path("c")},
|
||||
Filters: parsers.ComparisonExpression{
|
||||
Operation: ">",
|
||||
Left: parsers.SelectItem{
|
||||
Type: parsers.SelectItemTypeBinaryExpression,
|
||||
Value: parsers.BinaryExpression{
|
||||
Operation: "*",
|
||||
Left: testutils.SelectItem_Path("c", "a"),
|
||||
Right: testutils.SelectItem_Constant_Int(2),
|
||||
},
|
||||
},
|
||||
Right: testutils.SelectItem_Constant_Int(500),
|
||||
},
|
||||
},
|
||||
mockData,
|
||||
[]memoryexecutor.RowType{
|
||||
map[string]interface{}{"id": 1, "result": 840.0},
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -82,6 +82,15 @@ func (r rowContext) resolveSelectItem(selectItem parsers.SelectItem) interface{}
|
||||
return nil
|
||||
}
|
||||
|
||||
if selectItem.Type == parsers.SelectItemTypeBinaryExpression {
|
||||
if typedSelectItem, ok := selectItem.Value.(parsers.BinaryExpression); ok {
|
||||
return r.selectItem_SelectItemTypeBinaryExpression(typedSelectItem)
|
||||
}
|
||||
|
||||
logger.ErrorLn("parsers.SelectItem has incorrect Value type (expected parsers.BinaryExpression)")
|
||||
return nil
|
||||
}
|
||||
|
||||
return r.selectItem_SelectItemTypeField(selectItem)
|
||||
}
|
||||
|
||||
@@ -317,6 +326,45 @@ func (r rowContext) selectItem_SelectItemTypeFunctionCall(functionCall parsers.F
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r rowContext) selectItem_SelectItemTypeBinaryExpression(binaryExpression parsers.BinaryExpression) interface{} {
|
||||
if binaryExpression.Left == nil || binaryExpression.Right == nil {
|
||||
logger.Debug("parsers.BinaryExpression has nil Left or Right value")
|
||||
return nil
|
||||
}
|
||||
|
||||
leftValue := r.resolveSelectItem(binaryExpression.Left.(parsers.SelectItem))
|
||||
rightValue := r.resolveSelectItem(binaryExpression.Right.(parsers.SelectItem))
|
||||
|
||||
if leftValue == nil || rightValue == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
leftNumber, leftIsNumber := numToFloat64(leftValue)
|
||||
rightNumber, rightIsNumber := numToFloat64(rightValue)
|
||||
|
||||
if !leftIsNumber || !rightIsNumber {
|
||||
logger.Debug("Binary expression operands are not numbers, returning nil")
|
||||
return nil
|
||||
}
|
||||
|
||||
switch binaryExpression.Operation {
|
||||
case "+":
|
||||
return leftNumber + rightNumber
|
||||
case "-":
|
||||
return leftNumber - rightNumber
|
||||
case "*":
|
||||
return leftNumber * rightNumber
|
||||
case "/":
|
||||
if rightNumber == 0 {
|
||||
logger.Debug("Division by zero in binary expression")
|
||||
return nil
|
||||
}
|
||||
return leftNumber / rightNumber
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r rowContext) selectItem_SelectItemTypeField(selectItem parsers.SelectItem) interface{} {
|
||||
value := r.tables[selectItem.Path[0]]
|
||||
|
||||
@@ -352,6 +400,7 @@ func (r rowContext) selectItem_SelectItemTypeField(selectItem parsers.SelectItem
|
||||
}
|
||||
|
||||
func compareValues(val1, val2 interface{}) int {
|
||||
// Handle nil values
|
||||
if val1 == nil && val2 == nil {
|
||||
return 0
|
||||
} else if val1 == nil {
|
||||
@@ -360,27 +409,24 @@ func compareValues(val1, val2 interface{}) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Handle number values
|
||||
val1Number, val1IsNumber := numToFloat64(val1)
|
||||
val2Number, val2IsNumber := numToFloat64(val2)
|
||||
if val1IsNumber && val2IsNumber {
|
||||
if val1Number < val2Number {
|
||||
return -1
|
||||
} else if val1Number > val2Number {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Handle different types
|
||||
if reflect.TypeOf(val1) != reflect.TypeOf(val2) {
|
||||
return 1
|
||||
}
|
||||
|
||||
switch val1 := val1.(type) {
|
||||
case int:
|
||||
val2 := val2.(int)
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
case float64:
|
||||
val2 := val2.(float64)
|
||||
if val1 < val2 {
|
||||
return -1
|
||||
} else if val1 > val2 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
case string:
|
||||
val2 := val2.(string)
|
||||
return strings.Compare(val1, val2)
|
||||
|
||||
@@ -605,10 +605,30 @@ func numToInt(ex interface{}) (int, bool) {
|
||||
|
||||
func numToFloat64(num interface{}) (float64, bool) {
|
||||
switch val := num.(type) {
|
||||
case float64:
|
||||
return val, true
|
||||
case int:
|
||||
return float64(val), true
|
||||
case int8:
|
||||
return float64(val), true
|
||||
case int16:
|
||||
return float64(val), true
|
||||
case int32:
|
||||
return float64(val), true
|
||||
case int64:
|
||||
return float64(val), true
|
||||
case uint:
|
||||
return float64(val), true
|
||||
case uint8:
|
||||
return float64(val), true
|
||||
case uint16:
|
||||
return float64(val), true
|
||||
case uint32:
|
||||
return float64(val), true
|
||||
case uint64:
|
||||
return float64(val), true
|
||||
case float32:
|
||||
return float64(val), true
|
||||
case float64:
|
||||
return val, true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user