Code cleanup; Split test files

This commit is contained in:
Pijus Kamandulis 2024-02-18 21:29:42 +02:00
parent 7339e06eee
commit 2702156cb3
12 changed files with 1113 additions and 961 deletions

View File

@ -1,37 +1,5 @@
package parsers package parsers
type LogicalExpressionType int
const (
LogicalExpressionTypeOr LogicalExpressionType = iota
LogicalExpressionTypeAnd
)
type ConstantType int
const (
ConstantTypeString ConstantType = iota
ConstantTypeInteger
ConstantTypeFloat
ConstantTypeBoolean
ConstantTypeParameterConstant
)
type SelectItemType int
const (
SelectItemTypeField SelectItemType = iota
SelectItemTypeObject
SelectItemTypeArray
)
type OrderDirection int
const (
OrderDirectionAsc OrderDirection = iota
OrderDirectionDesc
)
type SelectStmt struct { type SelectStmt struct {
SelectItems []SelectItem SelectItems []SelectItem
Table Table Table Table
@ -45,14 +13,31 @@ type Table struct {
Value string Value string
} }
type SelectItemType int
const (
SelectItemTypeField SelectItemType = iota
SelectItemTypeObject
SelectItemTypeArray
SelectItemTypeConstant
)
type SelectItem struct { type SelectItem struct {
Alias string Alias string
Path []string Path []string
SelectItems []SelectItem SelectItems []SelectItem
Type SelectItemType Type SelectItemType
Value interface{}
IsTopLevel bool IsTopLevel bool
} }
type LogicalExpressionType int
const (
LogicalExpressionTypeOr LogicalExpressionType = iota
LogicalExpressionTypeAnd
)
type LogicalExpression struct { type LogicalExpression struct {
Expressions []interface{} Expressions []interface{}
Operation LogicalExpressionType Operation LogicalExpressionType
@ -64,11 +49,28 @@ type ComparisonExpression struct {
Operation string Operation string
} }
type ConstantType int
const (
ConstantTypeString ConstantType = iota
ConstantTypeInteger
ConstantTypeFloat
ConstantTypeBoolean
ConstantTypeParameterConstant
)
type Constant struct { type Constant struct {
Type ConstantType Type ConstantType
Value interface{} Value interface{}
} }
type OrderDirection int
const (
OrderDirectionAsc OrderDirection = iota
OrderDirectionDesc
)
type OrderExpression struct { type OrderExpression struct {
SelectItem SelectItem SelectItem SelectItem
Direction OrderDirection Direction OrderDirection

File diff suppressed because it is too large Load Diff

View File

@ -186,12 +186,23 @@ SelectProperty <- name:Identifier path:(DotFieldAccess / ArrayFieldAccess)* {
return makeSelectItem(name, path, parsers.SelectItemTypeField) return makeSelectItem(name, path, parsers.SelectItemTypeField)
} }
SelectItem <- selectItem:(SelectArray / SelectObject / SelectProperty) asClause:AsClause? { SelectItem <- selectItem:(Literal / SelectArray / SelectObject / SelectProperty) asClause:AsClause? {
item := selectItem.(parsers.SelectItem) var itemResult parsers.SelectItem
if aliasValue, ok := asClause.(string); ok { switch typedValue := selectItem.(type) {
item.Alias = aliasValue case parsers.SelectItem:
itemResult = typedValue
case parsers.Constant:
itemResult = parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: typedValue,
} }
return item, nil }
if aliasValue, ok := asClause.(string); ok {
itemResult.Alias = aliasValue
}
return itemResult, nil
} }
AsClause <- ws As ws alias:Identifier { return alias, nil } AsClause <- ws As ws alias:Identifier { return alias, nil }
@ -221,7 +232,7 @@ AndExpression <- ex1:ComparisonExpression ex2:(ws And ws ex:ComparisonExpression
} }
ComparisonExpression <- "(" ws ex:OrExpression ws ")" { return ex, nil } ComparisonExpression <- "(" ws ex:OrExpression ws ")" { return ex, nil }
/ left:(Literal / SelectItem) ws op:ComparisonOperator ws right:(Literal / 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 }

View File

@ -0,0 +1,65 @@
package nosql_test
import (
"log"
"reflect"
"testing"
"github.com/pikami/cosmium/parsers"
"github.com/pikami/cosmium/parsers/nosql"
)
// For Parser Debugging
// func Test_ParseTest(t *testing.T) {
// // select c.id, c._self, c._rid, c._ts, [c[\"pk\"]] as _partitionKeyValue from c
// res, err := nosql.Parse("", []byte("SELECT VALUE c.id FROM c"))
// if err != nil {
// log.Fatal(err)
// }
// result, err := json.MarshalIndent(res, "", " ")
// if err != nil {
// fmt.Println(err)
// return
// }
// fmt.Printf("output:\n%v\n", string(result))
// }
func testQueryParse(t *testing.T, query string, expectedQuery parsers.SelectStmt) {
parsedQuery, err := nosql.Parse("", []byte(query))
if err != nil {
log.Fatal(err)
}
if !reflect.DeepEqual(parsedQuery, expectedQuery) {
t.Errorf("parsed query does not match expected structure.\nExpected: %+v\nGot: %+v", expectedQuery, parsedQuery)
}
}
func Test_Parse(t *testing.T) {
t.Run("Should parse SELECT with ORDER BY", func(t *testing.T) {
testQueryParse(
t,
`SELECT c.id, c["pk"] FROM c ORDER BY c.id DESC, c.pk`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "pk"}},
},
Table: parsers.Table{Value: "c"},
OrderExpressions: []parsers.OrderExpression{
{
SelectItem: parsers.SelectItem{Path: []string{"c", "id"}},
Direction: parsers.OrderDirectionDesc,
},
{
SelectItem: parsers.SelectItem{Path: []string{"c", "pk"}},
Direction: parsers.OrderDirectionAsc,
},
},
},
)
})
}

View File

@ -0,0 +1,104 @@
package nosql_test
import (
"testing"
"github.com/pikami/cosmium/parsers"
)
func Test_Parse_Select(t *testing.T) {
t.Run("Should parse simple SELECT", func(t *testing.T) {
testQueryParse(
t,
`SELECT c.id, c["pk"] FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "pk"}},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse SELECT TOP", func(t *testing.T) {
testQueryParse(
t,
`SELECT TOP 1 c.id FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
},
Table: parsers.Table{Value: "c"},
Count: 1,
},
)
})
t.Run("Should parse SELECT VALUE", func(t *testing.T) {
testQueryParse(
t,
`SELECT VALUE c.id FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}, IsTopLevel: true},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse SELECT *", func(t *testing.T) {
testQueryParse(
t,
`SELECT * FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c"}, IsTopLevel: true},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse SELECT array", func(t *testing.T) {
testQueryParse(
t,
`SELECT [c.id, c.pk] as arr FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Alias: "arr",
Type: parsers.SelectItemTypeArray,
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "pk"}},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse SELECT object", func(t *testing.T) {
testQueryParse(
t,
`SELECT { id: c.id, _pk: c.pk } AS obj FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Alias: "obj",
Type: parsers.SelectItemTypeObject,
SelectItems: []parsers.SelectItem{
{Alias: "id", Path: []string{"c", "id"}},
{Alias: "_pk", Path: []string{"c", "pk"}},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
}

View File

@ -1,298 +0,0 @@
package nosql_test
import (
"log"
"reflect"
"testing"
"github.com/pikami/cosmium/parsers"
"github.com/pikami/cosmium/parsers/nosql"
)
// For Parser Debugging
// func Test_ParseTest(t *testing.T) {
// // select c.id, c._self, c._rid, c._ts, [c[\"pk\"]] as _partitionKeyValue from c
// res, err := nosql.Parse("", []byte("SELECT VALUE c.id FROM c"))
// if err != nil {
// log.Fatal(err)
// }
// result, err := json.MarshalIndent(res, "", " ")
// if err != nil {
// fmt.Println(err)
// return
// }
// fmt.Printf("output:\n%v\n", string(result))
// }
func testQueryParse(t *testing.T, query string, expectedQuery parsers.SelectStmt) {
parsedQuery, err := nosql.Parse("", []byte(query))
if err != nil {
log.Fatal(err)
}
if !reflect.DeepEqual(parsedQuery, expectedQuery) {
t.Errorf("parsed query does not match expected structure.\nExpected: %+v\nGot: %+v", expectedQuery, parsedQuery)
}
}
func Test_Parse(t *testing.T) {
t.Run("Should parse simple SELECT", func(t *testing.T) {
testQueryParse(
t,
`SELECT c.id, c["pk"] FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "pk"}},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse SELECT TOP", func(t *testing.T) {
testQueryParse(
t,
`SELECT TOP 1 c.id FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
},
Table: parsers.Table{Value: "c"},
Count: 1,
},
)
})
t.Run("Should parse SELECT VALUE", func(t *testing.T) {
testQueryParse(
t,
`SELECT VALUE c.id FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}, IsTopLevel: true},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse SELECT *", func(t *testing.T) {
testQueryParse(
t,
`SELECT * FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c"}, IsTopLevel: true},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse SELECT array", func(t *testing.T) {
testQueryParse(
t,
`SELECT [c.id, c.pk] as arr FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Alias: "arr",
Type: parsers.SelectItemTypeArray,
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "pk"}},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse SELECT object", func(t *testing.T) {
testQueryParse(
t,
`SELECT { id: c.id, _pk: c.pk } AS obj FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Alias: "obj",
Type: parsers.SelectItemTypeObject,
SelectItems: []parsers.SelectItem{
{Alias: "id", Path: []string{"c", "id"}},
{Alias: "_pk", Path: []string{"c", "pk"}},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse SELECT with ORDER BY", func(t *testing.T) {
testQueryParse(
t,
`SELECT c.id, c["pk"] FROM c ORDER BY c.id DESC, c.pk`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "pk"}},
},
Table: parsers.Table{Value: "c"},
OrderExpressions: []parsers.OrderExpression{
{
SelectItem: parsers.SelectItem{Path: []string{"c", "id"}},
Direction: parsers.OrderDirectionDesc,
},
{
SelectItem: parsers.SelectItem{Path: []string{"c", "pk"}},
Direction: parsers.OrderDirectionAsc,
},
},
},
)
})
t.Run("Should parse SELECT with single WHERE condition", func(t *testing.T) {
testQueryParse(
t,
`select c.id
FROM c
WHERE c.isCool=true`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
},
Table: parsers.Table{Value: "c"},
Filters: parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
Right: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
},
},
)
})
t.Run("Should parse SELECT with multiple WHERE conditions", func(t *testing.T) {
testQueryParse(
t,
`select c.id, c._self AS self, c._rid, c._ts
FROM c
WHERE c.id="12345" OR c.pk=123`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "_self"}, Alias: "self"},
{Path: []string{"c", "_rid"}},
{Path: []string{"c", "_ts"}},
},
Table: parsers.Table{Value: "c"},
Filters: parsers.LogicalExpression{
Operation: parsers.LogicalExpressionTypeOr,
Expressions: []interface{}{
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.Constant{Type: parsers.ConstantTypeString, Value: "12345"},
},
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "pk"}},
Right: parsers.Constant{Type: parsers.ConstantTypeInteger, Value: 123},
},
},
},
},
)
})
t.Run("Should parse SELECT with grouped WHERE conditions", func(t *testing.T) {
testQueryParse(
t,
`select c.id
FROM c
WHERE c.isCool=true AND (c.id = "123" OR c.id = "456")`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
},
Table: parsers.Table{Value: "c"},
Filters: parsers.LogicalExpression{
Operation: parsers.LogicalExpressionTypeAnd,
Expressions: []interface{}{
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
Right: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
},
parsers.LogicalExpression{
Operation: parsers.LogicalExpressionTypeOr,
Expressions: []interface{}{
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.Constant{Type: parsers.ConstantTypeString, Value: "123"},
},
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.Constant{Type: parsers.ConstantTypeString, Value: "456"},
},
},
},
},
},
},
)
})
t.Run("Should correctly parse literals in conditions", func(t *testing.T) {
testQueryParse(
t,
`select c.id
FROM c
WHERE c.boolean=true
AND c.integer=1
AND c.float=6.9
AND c.string="hello"
AND c.param=@param_id1`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{{Path: []string{"c", "id"}, Alias: ""}},
Table: parsers.Table{Value: "c"},
Filters: parsers.LogicalExpression{
Expressions: []interface{}{
parsers.ComparisonExpression{
Left: parsers.SelectItem{Path: []string{"c", "boolean"}},
Right: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
Operation: "=",
},
parsers.ComparisonExpression{
Left: parsers.SelectItem{Path: []string{"c", "integer"}},
Right: parsers.Constant{Type: parsers.ConstantTypeInteger, Value: 1},
Operation: "=",
},
parsers.ComparisonExpression{
Left: parsers.SelectItem{Path: []string{"c", "float"}},
Right: parsers.Constant{Type: parsers.ConstantTypeFloat, Value: 6.9},
Operation: "=",
},
parsers.ComparisonExpression{
Left: parsers.SelectItem{Path: []string{"c", "string"}},
Right: parsers.Constant{Type: parsers.ConstantTypeString, Value: "hello"},
Operation: "=",
},
parsers.ComparisonExpression{
Left: parsers.SelectItem{Path: []string{"c", "param"}},
Right: parsers.Constant{Type: parsers.ConstantTypeParameterConstant, Value: "@param_id1"},
Operation: "=",
},
},
Operation: parsers.LogicalExpressionTypeAnd,
},
},
)
})
}

View File

@ -0,0 +1,183 @@
package nosql_test
import (
"testing"
"github.com/pikami/cosmium/parsers"
)
func Test_Parse_Were(t *testing.T) {
t.Run("Should parse SELECT with single WHERE condition", func(t *testing.T) {
testQueryParse(
t,
`select c.id
FROM c
WHERE c.isCool=true`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
},
Table: parsers.Table{Value: "c"},
Filters: parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
},
},
},
)
})
t.Run("Should parse SELECT with multiple WHERE conditions", func(t *testing.T) {
testQueryParse(
t,
`select c.id, c._self AS self, c._rid, c._ts
FROM c
WHERE c.id="12345" OR c.pk=123`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "_self"}, Alias: "self"},
{Path: []string{"c", "_rid"}},
{Path: []string{"c", "_ts"}},
},
Table: parsers.Table{Value: "c"},
Filters: parsers.LogicalExpression{
Operation: parsers.LogicalExpressionTypeOr,
Expressions: []interface{}{
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "12345"},
},
},
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "pk"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeInteger, Value: 123},
},
},
},
},
},
)
})
t.Run("Should parse SELECT with grouped WHERE conditions", func(t *testing.T) {
testQueryParse(
t,
`select c.id
FROM c
WHERE c.isCool=true AND (c.id = "123" OR c.id = "456")`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
},
Table: parsers.Table{Value: "c"},
Filters: parsers.LogicalExpression{
Operation: parsers.LogicalExpressionTypeAnd,
Expressions: []interface{}{
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
},
},
parsers.LogicalExpression{
Operation: parsers.LogicalExpressionTypeOr,
Expressions: []interface{}{
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "123"},
},
},
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "456"},
},
},
},
},
},
},
},
)
})
t.Run("Should correctly parse literals in conditions", func(t *testing.T) {
testQueryParse(
t,
`select c.id
FROM c
WHERE c.boolean=true
AND c.integer=1
AND c.float=6.9
AND c.string="hello"
AND c.param=@param_id1`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{{Path: []string{"c", "id"}, Alias: ""}},
Table: parsers.Table{Value: "c"},
Filters: parsers.LogicalExpression{
Expressions: []interface{}{
parsers.ComparisonExpression{
Left: parsers.SelectItem{Path: []string{"c", "boolean"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
},
Operation: "=",
},
parsers.ComparisonExpression{
Left: parsers.SelectItem{Path: []string{"c", "integer"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeInteger, Value: 1},
},
Operation: "=",
},
parsers.ComparisonExpression{
Left: parsers.SelectItem{Path: []string{"c", "float"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeFloat, Value: 6.9},
},
Operation: "=",
},
parsers.ComparisonExpression{
Left: parsers.SelectItem{Path: []string{"c", "string"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "hello"},
},
Operation: "=",
},
parsers.ComparisonExpression{
Left: parsers.SelectItem{Path: []string{"c", "param"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeParameterConstant, Value: "@param_id1"},
},
Operation: "=",
},
},
Operation: parsers.LogicalExpressionTypeAnd,
},
},
)
})
}

View File

@ -16,7 +16,6 @@ func Execute(query parsers.SelectStmt, data []RowType) []RowType {
// Apply Filter // Apply Filter
for _, row := range data { for _, row := range data {
// Check if the row satisfies the filter conditions
if evaluateFilters(query.Filters, query.Parameters, row) { if evaluateFilters(query.Filters, query.Parameters, row) {
result = append(result, row) result = append(result, row)
} }
@ -24,7 +23,7 @@ func Execute(query parsers.SelectStmt, data []RowType) []RowType {
// Apply order // Apply order
if query.OrderExpressions != nil && len(query.OrderExpressions) > 0 { if query.OrderExpressions != nil && len(query.OrderExpressions) > 0 {
orderBy(query.OrderExpressions, result) orderBy(query.OrderExpressions, query.Parameters, result)
} }
// Apply result limit // Apply result limit
@ -41,16 +40,16 @@ func Execute(query parsers.SelectStmt, data []RowType) []RowType {
// Apply select // Apply select
selectedData := make([]RowType, 0) selectedData := make([]RowType, 0)
for _, row := range result { for _, row := range result {
selectedData = append(selectedData, selectRow(query.SelectItems, row)) selectedData = append(selectedData, selectRow(query.SelectItems, query.Parameters, row))
} }
return selectedData return selectedData
} }
func selectRow(selectItems []parsers.SelectItem, row RowType) interface{} { func selectRow(selectItems []parsers.SelectItem, queryParameters map[string]interface{}, row RowType) interface{} {
// When the first value is top level, select it instead // When the first value is top level, select it instead
if len(selectItems) > 0 && selectItems[0].IsTopLevel { if len(selectItems) > 0 && selectItems[0].IsTopLevel {
return getFieldValue(selectItems[0], row) return getFieldValue(selectItems[0], queryParameters, row)
} }
// Construct a new row based on the selected columns // Construct a new row based on the selected columns
@ -58,41 +57,48 @@ func selectRow(selectItems []parsers.SelectItem, row RowType) interface{} {
for index, column := range selectItems { for index, column := range selectItems {
destinationName := column.Alias destinationName := column.Alias
if destinationName == "" { if destinationName == "" {
if len(column.Path) < 1 { if len(column.Path) > 0 {
destinationName = fmt.Sprintf("$%d", index+1)
} else {
destinationName = column.Path[len(column.Path)-1] destinationName = column.Path[len(column.Path)-1]
} else {
destinationName = fmt.Sprintf("$%d", index+1)
} }
} }
newRow[destinationName] = getFieldValue(column, row) newRow[destinationName] = getFieldValue(column, queryParameters, row)
} }
return newRow return newRow
} }
// Helper function to evaluate filter conditions recursively func evaluateFilters(expr ExpressionType, queryParameters map[string]interface{}, row RowType) bool {
func evaluateFilters(expr ExpressionType, Parameters map[string]interface{}, row RowType) bool {
if expr == nil { if expr == nil {
return true return true
} }
switch typedValue := expr.(type) { switch typedValue := expr.(type) {
case parsers.ComparisonExpression: case parsers.ComparisonExpression:
leftValue := getExpressionParameterValue(typedValue.Left, Parameters, row) leftValue := getExpressionParameterValue(typedValue.Left, queryParameters, row)
rightValue := getExpressionParameterValue(typedValue.Right, Parameters, row) rightValue := getExpressionParameterValue(typedValue.Right, queryParameters, row)
cmp := compareValues(leftValue, rightValue)
switch typedValue.Operation { switch typedValue.Operation {
case "=": case "=":
return leftValue == rightValue return cmp == 0
case "!=": case "!=":
return leftValue != rightValue return cmp != 0
// Handle other comparison operators as needed case "<":
return cmp < 0
case ">":
return cmp > 0
case "<=":
return cmp <= 0
case ">=":
return cmp >= 0
} }
case parsers.LogicalExpression: case parsers.LogicalExpression:
var result bool var result bool
for i, expression := range typedValue.Expressions { for i, expression := range typedValue.Expressions {
expressionResult := evaluateFilters(expression, Parameters, row) expressionResult := evaluateFilters(expression, queryParameters, row)
if i == 0 { if i == 0 {
result = expressionResult result = expressionResult
} }
@ -115,17 +121,16 @@ func evaluateFilters(expr ExpressionType, Parameters map[string]interface{}, row
if value, ok := typedValue.Value.(bool); ok { if value, ok := typedValue.Value.(bool); ok {
return value return value
} }
// TODO: Check if we should do something if it is not a boolean constant
return false return false
} }
return false return false
} }
func getFieldValue(field parsers.SelectItem, row RowType) interface{} { func getFieldValue(field parsers.SelectItem, queryParameters map[string]interface{}, row RowType) interface{} {
if field.Type == parsers.SelectItemTypeArray { if field.Type == parsers.SelectItemTypeArray {
arrayValue := make([]interface{}, 0) arrayValue := make([]interface{}, 0)
for _, selectItem := range field.SelectItems { for _, selectItem := range field.SelectItems {
arrayValue = append(arrayValue, getFieldValue(selectItem, row)) arrayValue = append(arrayValue, getFieldValue(selectItem, queryParameters, row))
} }
return arrayValue return arrayValue
} }
@ -133,11 +138,29 @@ func getFieldValue(field parsers.SelectItem, row RowType) interface{} {
if field.Type == parsers.SelectItemTypeObject { if field.Type == parsers.SelectItemTypeObject {
objectValue := make(map[string]interface{}) objectValue := make(map[string]interface{})
for _, selectItem := range field.SelectItems { for _, selectItem := range field.SelectItems {
objectValue[selectItem.Alias] = getFieldValue(selectItem, row) objectValue[selectItem.Alias] = getFieldValue(selectItem, queryParameters, row)
} }
return objectValue return objectValue
} }
if field.Type == parsers.SelectItemTypeConstant {
var typedValue parsers.Constant
var ok bool
if typedValue, ok = field.Value.(parsers.Constant); !ok {
// TODO: Handle error
fmt.Println("parsers.Constant has incorrect Value type")
}
if typedValue.Type == parsers.ConstantTypeParameterConstant &&
queryParameters != nil {
if key, ok := typedValue.Value.(string); ok {
return queryParameters[key]
}
}
return typedValue.Value
}
value := row value := row
if len(field.Path) > 1 { if len(field.Path) > 1 {
for _, pathSegment := range field.Path[1:] { for _, pathSegment := range field.Path[1:] {
@ -153,31 +176,24 @@ func getFieldValue(field parsers.SelectItem, row RowType) interface{} {
func getExpressionParameterValue( func getExpressionParameterValue(
parameter interface{}, parameter interface{},
Parameters map[string]interface{}, queryParameters map[string]interface{},
row RowType, row RowType,
) interface{} { ) interface{} {
switch typedParameter := parameter.(type) { switch typedParameter := parameter.(type) {
case parsers.SelectItem: case parsers.SelectItem:
return getFieldValue(typedParameter, row) return getFieldValue(typedParameter, queryParameters, row)
case parsers.Constant:
if typedParameter.Type == parsers.ConstantTypeParameterConstant &&
Parameters != nil {
if key, ok := typedParameter.Value.(string); ok {
return Parameters[key]
}
} }
return typedParameter.Value fmt.Println("getExpressionParameterValue - got incorrect parameter type")
}
// TODO: Handle error
return nil return nil
} }
func orderBy(orderBy []parsers.OrderExpression, data []RowType) { func orderBy(orderBy []parsers.OrderExpression, queryParameters map[string]interface{}, data []RowType) {
less := func(i, j int) bool { less := func(i, j int) bool {
for _, order := range orderBy { for _, order := range orderBy {
val1 := getFieldValue(order.SelectItem, data[i]) val1 := getFieldValue(order.SelectItem, queryParameters, data[i])
val2 := getFieldValue(order.SelectItem, data[j]) val2 := getFieldValue(order.SelectItem, queryParameters, data[j])
cmp := compareValues(val1, val2) cmp := compareValues(val1, val2)
if cmp != 0 { if cmp != 0 {
@ -203,9 +219,26 @@ func compareValues(val1, val2 interface{}) int {
return 1 return 1
} }
return 0 return 0
case float64:
val2 := val2.(float64)
if val1 < val2 {
return -1
} else if val1 > val2 {
return 1
}
return 0
case string: case string:
val2 := val2.(string) val2 := val2.(string)
return strings.Compare(val1, val2) return strings.Compare(val1, val2)
case bool:
val2 := val2.(bool)
if val1 == val2 {
return 0
} else if val1 {
return 1
} else {
return -1
}
// TODO: Add more types // TODO: Add more types
default: default:
return 0 return 0

View File

@ -0,0 +1,61 @@
package memoryexecutor_test
import (
"reflect"
"testing"
"github.com/pikami/cosmium/parsers"
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
)
func testQueryExecute(
t *testing.T,
query parsers.SelectStmt,
data []memoryexecutor.RowType,
expectedData []memoryexecutor.RowType,
) {
result := memoryexecutor.Execute(query, data)
if !reflect.DeepEqual(result, expectedData) {
t.Errorf("execution result does not match expected data.\nExpected: %+v\nGot: %+v", expectedData, result)
}
}
func Test_Execute(t *testing.T) {
mockData := []memoryexecutor.RowType{
map[string]interface{}{"id": "12345", "pk": 123, "_self": "self1", "_rid": "rid1", "_ts": 123456, "isCool": false},
map[string]interface{}{"id": "67890", "pk": 456, "_self": "self2", "_rid": "rid2", "_ts": 789012, "isCool": true},
map[string]interface{}{"id": "456", "pk": 456, "_self": "self2", "_rid": "rid2", "_ts": 789012, "isCool": true},
map[string]interface{}{"id": "123", "pk": 456, "_self": "self2", "_rid": "rid2", "_ts": 789012, "isCool": true},
}
t.Run("Should execute SELECT with ORDER BY", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "pk"}},
},
Table: parsers.Table{Value: "c"},
OrderExpressions: []parsers.OrderExpression{
{
SelectItem: parsers.SelectItem{Path: []string{"c", "pk"}},
Direction: parsers.OrderDirectionAsc,
},
{
SelectItem: parsers.SelectItem{Path: []string{"c", "id"}},
Direction: parsers.OrderDirectionDesc,
},
},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "12345", "pk": 123},
map[string]interface{}{"id": "67890", "pk": 456},
map[string]interface{}{"id": "456", "pk": 456},
map[string]interface{}{"id": "123", "pk": 456},
},
)
})
}

View File

@ -0,0 +1,140 @@
package memoryexecutor_test
import (
"testing"
"github.com/pikami/cosmium/parsers"
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
)
func Test_Execute_Select(t *testing.T) {
mockData := []memoryexecutor.RowType{
map[string]interface{}{"id": "12345", "pk": 123, "_self": "self1", "_rid": "rid1", "_ts": 123456, "isCool": false},
map[string]interface{}{"id": "67890", "pk": 456, "_self": "self2", "_rid": "rid2", "_ts": 789012, "isCool": true},
map[string]interface{}{"id": "456", "pk": 456, "_self": "self2", "_rid": "rid2", "_ts": 789012, "isCool": true},
map[string]interface{}{"id": "123", "pk": 456, "_self": "self2", "_rid": "rid2", "_ts": 789012, "isCool": true},
}
t.Run("Should execute simple SELECT", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "pk"}},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "12345", "pk": 123},
map[string]interface{}{"id": "67890", "pk": 456},
map[string]interface{}{"id": "456", "pk": 456},
map[string]interface{}{"id": "123", "pk": 456},
},
)
})
t.Run("Should execute SELECT TOP", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "pk"}},
},
Table: parsers.Table{Value: "c"},
Count: 1,
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "12345", "pk": 123},
},
)
})
t.Run("Should execute SELECT VALUE", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}, IsTopLevel: true},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
"12345",
"67890",
"456",
"123",
},
)
})
t.Run("Should execute SELECT *", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c"}, IsTopLevel: true},
},
Table: parsers.Table{Value: "c"},
},
mockData,
mockData,
)
})
t.Run("Should execute SELECT array", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Alias: "arr",
Type: parsers.SelectItemTypeArray,
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "pk"}},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"arr": []interface{}{"12345", 123}},
map[string]interface{}{"arr": []interface{}{"67890", 456}},
map[string]interface{}{"arr": []interface{}{"456", 456}},
map[string]interface{}{"arr": []interface{}{"123", 456}},
},
)
})
t.Run("Should execute SELECT object", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Alias: "obj",
Type: parsers.SelectItemTypeObject,
SelectItems: []parsers.SelectItem{
{Alias: "id", Path: []string{"c", "id"}},
{Alias: "_pk", Path: []string{"c", "pk"}},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"obj": map[string]interface{}{"id": "12345", "_pk": 123}},
map[string]interface{}{"obj": map[string]interface{}{"id": "67890", "_pk": 456}},
map[string]interface{}{"obj": map[string]interface{}{"id": "456", "_pk": 456}},
map[string]interface{}{"obj": map[string]interface{}{"id": "123", "_pk": 456}},
},
)
})
}

View File

@ -1,305 +0,0 @@
package memoryexecutor_test
import (
"reflect"
"testing"
"github.com/pikami/cosmium/parsers"
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
)
func testQueryExecute(
t *testing.T,
query parsers.SelectStmt,
data []memoryexecutor.RowType,
expectedData []memoryexecutor.RowType,
) {
result := memoryexecutor.Execute(query, data)
if !reflect.DeepEqual(result, expectedData) {
t.Errorf("execution result does not match expected data.\nExpected: %+v\nGot: %+v", expectedData, result)
}
}
func Test_Execute(t *testing.T) {
mockData := []memoryexecutor.RowType{
map[string]interface{}{"id": "12345", "pk": 123, "_self": "self1", "_rid": "rid1", "_ts": 123456, "isCool": false},
map[string]interface{}{"id": "67890", "pk": 456, "_self": "self2", "_rid": "rid2", "_ts": 789012, "isCool": true},
map[string]interface{}{"id": "456", "pk": 456, "_self": "self2", "_rid": "rid2", "_ts": 789012, "isCool": true},
map[string]interface{}{"id": "123", "pk": 456, "_self": "self2", "_rid": "rid2", "_ts": 789012, "isCool": true},
}
t.Run("Should execute simple SELECT", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "pk"}},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "12345", "pk": 123},
map[string]interface{}{"id": "67890", "pk": 456},
map[string]interface{}{"id": "456", "pk": 456},
map[string]interface{}{"id": "123", "pk": 456},
},
)
})
t.Run("Should execute SELECT TOP", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "pk"}},
},
Table: parsers.Table{Value: "c"},
Count: 1,
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "12345", "pk": 123},
},
)
})
t.Run("Should execute SELECT VALUE", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}, IsTopLevel: true},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
"12345",
"67890",
"456",
"123",
},
)
})
t.Run("Should execute SELECT *", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c"}, IsTopLevel: true},
},
Table: parsers.Table{Value: "c"},
},
mockData,
mockData,
)
})
t.Run("Should execute SELECT array", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Alias: "arr",
Type: parsers.SelectItemTypeArray,
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "pk"}},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"arr": []interface{}{"12345", 123}},
map[string]interface{}{"arr": []interface{}{"67890", 456}},
map[string]interface{}{"arr": []interface{}{"456", 456}},
map[string]interface{}{"arr": []interface{}{"123", 456}},
},
)
})
t.Run("Should execute SELECT object", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Alias: "obj",
Type: parsers.SelectItemTypeObject,
SelectItems: []parsers.SelectItem{
{Alias: "id", Path: []string{"c", "id"}},
{Alias: "_pk", Path: []string{"c", "pk"}},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"obj": map[string]interface{}{"id": "12345", "_pk": 123}},
map[string]interface{}{"obj": map[string]interface{}{"id": "67890", "_pk": 456}},
map[string]interface{}{"obj": map[string]interface{}{"id": "456", "_pk": 456}},
map[string]interface{}{"obj": map[string]interface{}{"id": "123", "_pk": 456}},
},
)
})
t.Run("Should execute SELECT with ORDER BY", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "pk"}},
},
Table: parsers.Table{Value: "c"},
OrderExpressions: []parsers.OrderExpression{
{
SelectItem: parsers.SelectItem{Path: []string{"c", "pk"}},
Direction: parsers.OrderDirectionAsc,
},
{
SelectItem: parsers.SelectItem{Path: []string{"c", "id"}},
Direction: parsers.OrderDirectionDesc,
},
},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "12345", "pk": 123},
map[string]interface{}{"id": "67890", "pk": 456},
map[string]interface{}{"id": "456", "pk": 456},
map[string]interface{}{"id": "123", "pk": 456},
},
)
})
t.Run("Should execute SELECT with single WHERE condition", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
},
Table: parsers.Table{Value: "c"},
Filters: parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
Right: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "67890"},
map[string]interface{}{"id": "456"},
map[string]interface{}{"id": "123"},
},
)
})
t.Run("Should execute SELECT with WHERE condition with defined parameter constant", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
},
Table: parsers.Table{Value: "c"},
Filters: parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.Constant{Type: parsers.ConstantTypeParameterConstant, Value: "@param_id"},
},
Parameters: map[string]interface{}{
"@param_id": "456",
},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "456"},
},
)
})
t.Run("Should execute SELECT with multiple WHERE conditions", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "_self"}, Alias: "self"},
},
Table: parsers.Table{Value: "c"},
Filters: parsers.LogicalExpression{
Operation: parsers.LogicalExpressionTypeAnd,
Expressions: []interface{}{
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.Constant{Type: parsers.ConstantTypeString, Value: "67890"},
},
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "pk"}},
Right: parsers.Constant{Type: parsers.ConstantTypeInteger, Value: 456},
},
},
},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "67890", "self": "self2"},
},
)
})
t.Run("Should execute SELECT with grouped WHERE conditions", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
},
Table: parsers.Table{Value: "c"},
Filters: parsers.LogicalExpression{
Operation: parsers.LogicalExpressionTypeAnd,
Expressions: []interface{}{
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
Right: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
},
parsers.LogicalExpression{
Operation: parsers.LogicalExpressionTypeOr,
Expressions: []interface{}{
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.Constant{Type: parsers.ConstantTypeString, Value: "123"},
},
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.Constant{Type: parsers.ConstantTypeString, Value: "456"},
},
},
},
},
},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "456"},
map[string]interface{}{"id": "123"},
},
)
})
}

View File

@ -0,0 +1,159 @@
package memoryexecutor_test
import (
"testing"
"github.com/pikami/cosmium/parsers"
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
)
func Test_Execute_Where(t *testing.T) {
mockData := []memoryexecutor.RowType{
map[string]interface{}{"id": "12345", "pk": 123, "_self": "self1", "_rid": "rid1", "_ts": 123456, "isCool": false},
map[string]interface{}{"id": "67890", "pk": 456, "_self": "self2", "_rid": "rid2", "_ts": 789012, "isCool": true},
map[string]interface{}{"id": "456", "pk": 456, "_self": "self2", "_rid": "rid2", "_ts": 789012, "isCool": true},
map[string]interface{}{"id": "123", "pk": 456, "_self": "self2", "_rid": "rid2", "_ts": 789012, "isCool": true},
}
t.Run("Should execute SELECT with single WHERE condition", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
},
Table: parsers.Table{Value: "c"},
Filters: parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
},
},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "67890"},
map[string]interface{}{"id": "456"},
map[string]interface{}{"id": "123"},
},
)
})
t.Run("Should execute SELECT with WHERE condition with defined parameter constant", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
},
Table: parsers.Table{Value: "c"},
Filters: parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeParameterConstant, Value: "@param_id"},
},
},
Parameters: map[string]interface{}{
"@param_id": "456",
},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "456"},
},
)
})
t.Run("Should execute SELECT with multiple WHERE conditions", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
{Path: []string{"c", "_self"}, Alias: "self"},
},
Table: parsers.Table{Value: "c"},
Filters: parsers.LogicalExpression{
Operation: parsers.LogicalExpressionTypeAnd,
Expressions: []interface{}{
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "67890"},
},
},
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "pk"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeInteger, Value: 456},
},
},
},
},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "67890", "self": "self2"},
},
)
})
t.Run("Should execute SELECT with grouped WHERE conditions", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
},
Table: parsers.Table{Value: "c"},
Filters: parsers.LogicalExpression{
Operation: parsers.LogicalExpressionTypeAnd,
Expressions: []interface{}{
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
},
},
parsers.LogicalExpression{
Operation: parsers.LogicalExpressionTypeOr,
Expressions: []interface{}{
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "123"},
},
},
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "456"},
},
},
},
},
},
},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "456"},
map[string]interface{}{"id": "123"},
},
)
})
}