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 SelectItems []SelectItem
Table Table Table Table
Filters interface{} Filters interface{}
Distinct bool
Count int Count int
Parameters map[string]interface{} Parameters map[string]interface{}
OrderExpressions []OrderExpression 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" 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{ selectStmt := parsers.SelectStmt{
SelectItems: columns.([]parsers.SelectItem), SelectItems: columns.([]parsers.SelectItem),
Table: table.(parsers.Table), Table: table.(parsers.Table),
} }
switch v := whereClause.(type) { switch v := whereClause.(type) {
case parsers.ComparisonExpression, parsers.LogicalExpression, parsers.Constant, parsers.SelectItem: case parsers.ComparisonExpression, parsers.LogicalExpression, parsers.Constant, parsers.SelectItem:
selectStmt.Filters = v selectStmt.Filters = v
} }
if distinctClause != nil {
selectStmt.Distinct = true
}
if n, ok := count.(int); ok { if n, ok := count.(int); ok {
selectStmt.Count = n selectStmt.Count = n
} }
if orderExpressions, ok := orderList.([]parsers.OrderExpression); ok { if orderExpressions, ok := orderList.([]parsers.OrderExpression); ok {
selectStmt.OrderExpressions = orderExpressions selectStmt.OrderExpressions = orderExpressions
} }
return selectStmt, nil return selectStmt, nil
} }
@ -136,13 +144,17 @@ Input <- selectStmt:SelectStmt {
return selectStmt, nil 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 From ws table:TableName ws
whereClause:(ws Where ws condition:Condition { return condition, nil })? whereClause:(ws Where ws condition:Condition { return condition, nil })?
orderByClause:OrderByClause? { 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 { TopClause <- Top ws count:Integer {
return count, nil 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) { t.Run("Should parse SELECT TOP", func(t *testing.T) {
testQueryParse( testQueryParse(
t, t,

View File

@ -35,6 +35,18 @@ func Execute(query parsers.SelectStmt, data []RowType) []RowType {
ctx.orderBy(query.OrderExpressions, result) 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 // Apply result limit
if query.Count > 0 { if query.Count > 0 {
count := func() int { count := func() int {
@ -46,13 +58,7 @@ func Execute(query parsers.SelectStmt, data []RowType) []RowType {
result = result[:count] result = result[:count]
} }
// Apply select return result
selectedData := make([]RowType, 0)
for _, row := range result {
selectedData = append(selectedData, ctx.selectRow(query.SelectItems, row))
}
return selectedData
} }
func (c memoryExecutorContext) selectRow(selectItems []parsers.SelectItem, row RowType) interface{} { func (c memoryExecutorContext) selectRow(selectItems []parsers.SelectItem, row RowType) interface{} {
@ -349,3 +355,23 @@ func compareValues(val1, val2 interface{}) int {
return 1 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) { t.Run("Should execute SELECT TOP", func(t *testing.T) {
testQueryExecute( testQueryExecute(
t, t,