mirror of https://github.com/pikami/cosmium.git
Code cleanup; Split test files
This commit is contained in:
parent
7339e06eee
commit
2702156cb3
|
@ -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
|
@ -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 }
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
|
@ -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"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
|
@ -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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
|
@ -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}},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
|
@ -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"},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -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"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue