feat: parse DISTINCT and ORDER BY

This commit is contained in:
Yehoshua Sandler 2025-04-07 19:03:29 -05:00
parent a0a6b8822e
commit 68e3669c1a
2 changed files with 128 additions and 16 deletions

View File

@ -1,6 +1,7 @@
package q
import (
"fmt"
"strings"
"github.com/DataDog/go-sqllexer"
@ -11,7 +12,14 @@ type Select struct {
Table string
Columns []string
Conditionals []Conditional
OrderBys []OrderBy
IsWildcard bool
IsDistinct bool
}
type OrderBy struct {
Key string
IsDescend bool
}
func (q *Select) GetFullSql() string {
@ -33,10 +41,11 @@ func (q *Select) GetFullSql() string {
workingSqlSlice = append(workingSqlSlice, "FROM "+q.Table)
// TODO: need to account for `AND` and `OR`s and stuff
for _, condition := range q.Conditionals {
workingSqlSlice = append(workingSqlSlice, condition.Key)
workingSqlSlice = append(workingSqlSlice, condition.Operator)
workingSqlSlice = append(workingSqlSlice, condition.Value) // TODO: need to account for `AND` and `OR`s and stuff
workingSqlSlice = append(workingSqlSlice, condition.Value)
}
fullSql := strings.Join(workingSqlSlice, " ")
@ -44,6 +53,22 @@ func (q *Select) GetFullSql() string {
return fullSql
}
func mutateSelectFromKeyword(query *Select, keyword string) {
switch strings.ToUpper(keyword) {
case "DISTINCT":
query.IsDistinct = true
}
}
// TODO: make this an array of tokens instead
func unshiftBuffer(buf *[10]string, value string) {
for i := 9; i >= 1; i-- {
buf[i] = buf[i-1]
}
buf[0] = value
}
func ParseSelectStatement(sql string) Select {
query := Select{}
@ -52,10 +77,14 @@ func ParseSelectStatement(sql string) Select {
passedFROM := false
passedTable := false
passedWHERE := false
passedConditionals := false
passedOrderBy := false
lookBehindBuffer := [10]string{} // TODO: make this an array of tokens instead
var workingConditional = Conditional{}
var columns []string
var orderBys []OrderBy
lexer := sqllexer.New(sql)
for {
token := lexer.Scan()
@ -70,41 +99,53 @@ func ParseSelectStatement(sql string) Select {
continue
}
// For any keywords that are before the columns or wildcard
if passedSELECT && len(columns) == 0 && !passedColumns {
if token.Type == sqllexer.KEYWORD {
mutateSelectFromKeyword(&query, token.Value)
continue
}
}
if !passedColumns {
if token.Type == sqllexer.WILDCARD {
passedColumns = true
columns = make([]string, 0)
query.IsWildcard = true
continue
} else if token.Type == sqllexer.IDENT {
columns = append(columns, token.Value)
continue
} else if token.Type == sqllexer.PUNCTUATION || token.Type == sqllexer.SPACE {
continue
} else {
passedColumns = true // TODO: make sure that I should be doing this
passedColumns = true
query.Columns = columns
continue
}
}
if !passedFROM && strings.ToUpper(token.Value) == "FROM" {
passedFROM = true
continue
} else if !passedFROM {
continue // TODO: make sure to check for other keywords that are allowed
// continue // TODO: make sure to check for other keywords that are allowed
}
if !passedTable && token.Type == sqllexer.IDENT {
passedTable = true
query.Table = token.Value
continue
} else if !passedTable {
continue
}
if !passedWHERE && strings.ToUpper(token.Value) == "WHERE" {
if !passedWHERE && token.Type == sqllexer.KEYWORD && strings.ToUpper(token.Value) == "WHERE" {
passedWHERE = true
} else if !passedWHERE {
continue
}
if passedWHERE && !passedConditionals {
if token.Type == sqllexer.IDENT {
workingConditional.Key = token.Value
} else if token.Type == sqllexer.OPERATOR {
@ -116,8 +157,31 @@ func ParseSelectStatement(sql string) Select {
if workingConditional.Key != "" && workingConditional.Operator != "" && workingConditional.Value != "" {
query.Conditionals = append(query.Conditionals, workingConditional)
workingConditional = Conditional{}
passedConditionals = true
}
continue
}
// Checking For ORDER BY
if !passedOrderBy && token.Type == sqllexer.KEYWORD {
unshiftBuffer(&lookBehindBuffer, token.Value)
if strings.ToUpper(lookBehindBuffer[1]) == "ORDER" && strings.ToUpper(lookBehindBuffer[0]) == "BY" {
passedOrderBy = true
}
fmt.Printf("VALUE: %s %s \n", lookBehindBuffer[1], lookBehindBuffer[0])
}
if passedOrderBy && token.Type == sqllexer.IDENT {
orderBys = append(orderBys, OrderBy{
Key: token.Value, // TODO: need to be able to get the ASC or DESC keyword too
})
query.OrderBys = orderBys
}
}
return query

View File

@ -26,6 +26,41 @@ func TestParseSelectStatement(t *testing.T) {
},
},
},
{
input: "SELECT CustomerName, City FROM Customers",
expected: Select{
Table: "Customers",
IsWildcard: false,
Columns: []string{
"CustomerName",
"City",
},
},
},
{
input: "SELECT DISTINCT Country FROM Nations;",
expected: Select{
Table: "Nations",
Columns: []string{
"Country",
},
},
},
{
input: "SELECT * FROM Orders ORDER BY StreetNumber, CountryCode;",
expected: Select{
Table: "Orders",
IsWildcard: true,
OrderBys: []OrderBy{
{
Key: "StreetNumber",
},
{
Key: "CountryCode",
},
},
},
},
}
for _, sql := range testSqlStatements {
@ -73,6 +108,19 @@ func TestParseSelectStatement(t *testing.T) {
}
}
if len(answer.OrderBys) != len(expected.OrderBys) {
t.Errorf("got %d number of orderBys for Select.OrderBys, expected %d", len(answer.OrderBys), len(expected.OrderBys))
} else {
for i, expectedOrderBy := range expected.OrderBys {
if expectedOrderBy.Key != answer.OrderBys[i].Key {
t.Errorf("got %s for Select.OrderBys[%d].Key, expected %s", answer.OrderBys[i].Key, i, expectedOrderBy.Key)
}
if expectedOrderBy.IsDescend != answer.OrderBys[i].IsDescend {
t.Errorf("got %#v for Select.OrderBys[%d].IsDescend, expected %#v", answer.OrderBys[i].IsDescend, i, expectedOrderBy.IsDescend)
}
}
}
})
}
}