Implement DISTINCT clause

This commit is contained in:
Pijus Kamandulis 2024-02-27 21:10:03 +02:00
parent f3f3966dd5
commit 5ff923ce2c
6 changed files with 1017 additions and 913 deletions

View File

@ -4,6 +4,7 @@ type SelectStmt struct {
SelectItems []SelectItem
Table Table
Filters interface{}
Distinct bool
Count int
Parameters map[string]interface{}
OrderExpressions []OrderExpression

File diff suppressed because it is too large Load Diff

View File

@ -3,24 +3,32 @@ package nosql
import "github.com/pikami/cosmium/parsers"
func makeSelectStmt(columns, table, whereClause interface{}, count interface{}, orderList interface{}) (parsers.SelectStmt, error) {
func makeSelectStmt(
columns, table,
whereClause interface{}, distinctClause interface{},
count interface{}, orderList interface{},
) (parsers.SelectStmt, error) {
selectStmt := parsers.SelectStmt{
SelectItems: columns.([]parsers.SelectItem),
Table: table.(parsers.Table),
}
switch v := whereClause.(type) {
case parsers.ComparisonExpression, parsers.LogicalExpression, parsers.Constant, parsers.SelectItem:
selectStmt.Filters = v
}
switch v := whereClause.(type) {
case parsers.ComparisonExpression, parsers.LogicalExpression, parsers.Constant, parsers.SelectItem:
selectStmt.Filters = v
}
if distinctClause != nil {
selectStmt.Distinct = true
}
if n, ok := count.(int); ok {
selectStmt.Count = n
}
if orderExpressions, ok := orderList.([]parsers.OrderExpression); ok {
selectStmt.OrderExpressions = orderExpressions
}
if orderExpressions, ok := orderList.([]parsers.OrderExpression); ok {
selectStmt.OrderExpressions = orderExpressions
}
return selectStmt, nil
}
@ -136,13 +144,17 @@ Input <- selectStmt:SelectStmt {
return selectStmt, nil
}
SelectStmt <- Select ws topClause:TopClause? ws columns:Selection ws
SelectStmt <- Select ws
distinctClause:DistinctClause? ws
topClause:TopClause? ws columns:Selection ws
From ws table:TableName ws
whereClause:(ws Where ws condition:Condition { return condition, nil })?
orderByClause:OrderByClause? {
return makeSelectStmt(columns, table, whereClause, topClause, orderByClause)
return makeSelectStmt(columns, table, whereClause, distinctClause, topClause, orderByClause)
}
DistinctClause <- "DISTINCT"i
TopClause <- Top ws count:Integer {
return count, nil
}

View File

@ -22,6 +22,20 @@ func Test_Parse_Select(t *testing.T) {
)
})
t.Run("Should parse SELECT DISTINCT", func(t *testing.T) {
testQueryParse(
t,
`SELECT DISTINCT c.id FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "id"}},
},
Table: parsers.Table{Value: "c"},
Distinct: true,
},
)
})
t.Run("Should parse SELECT TOP", func(t *testing.T) {
testQueryParse(
t,

View File

@ -35,6 +35,18 @@ func Execute(query parsers.SelectStmt, data []RowType) []RowType {
ctx.orderBy(query.OrderExpressions, result)
}
// Apply select
selectedData := make([]RowType, 0)
for _, row := range result {
selectedData = append(selectedData, ctx.selectRow(query.SelectItems, row))
}
result = selectedData
// Apply distinct
if query.Distinct {
result = deduplicate(result)
}
// Apply result limit
if query.Count > 0 {
count := func() int {
@ -46,13 +58,7 @@ func Execute(query parsers.SelectStmt, data []RowType) []RowType {
result = result[:count]
}
// Apply select
selectedData := make([]RowType, 0)
for _, row := range result {
selectedData = append(selectedData, ctx.selectRow(query.SelectItems, row))
}
return selectedData
return result
}
func (c memoryExecutorContext) selectRow(selectItems []parsers.SelectItem, row RowType) interface{} {
@ -349,3 +355,23 @@ func compareValues(val1, val2 interface{}) int {
return 1
}
}
func deduplicate(slice []RowType) []RowType {
var result []RowType
for i := 0; i < len(slice); i++ {
unique := true
for j := 0; j < len(result); j++ {
if compareValues(slice[i], result[j]) == 0 {
unique = false
break
}
}
if unique {
result = append(result, slice[i])
}
}
return result
}

View File

@ -35,6 +35,24 @@ func Test_Execute_Select(t *testing.T) {
)
})
t.Run("Should execute SELECT DISTINCT", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{Path: []string{"c", "pk"}},
},
Table: parsers.Table{Value: "c"},
Distinct: true,
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"pk": 123},
map[string]interface{}{"pk": 456},
},
)
})
t.Run("Should execute SELECT TOP", func(t *testing.T) {
testQueryExecute(
t,