feat: parse DISTINCT and ORDER BY
This commit is contained in:
parent
a0a6b8822e
commit
68e3669c1a
94
q/select.go
94
q/select.go
@ -1,6 +1,7 @@
|
|||||||
package q
|
package q
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/DataDog/go-sqllexer"
|
"github.com/DataDog/go-sqllexer"
|
||||||
@ -11,7 +12,14 @@ type Select struct {
|
|||||||
Table string
|
Table string
|
||||||
Columns []string
|
Columns []string
|
||||||
Conditionals []Conditional
|
Conditionals []Conditional
|
||||||
|
OrderBys []OrderBy
|
||||||
IsWildcard bool
|
IsWildcard bool
|
||||||
|
IsDistinct bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrderBy struct {
|
||||||
|
Key string
|
||||||
|
IsDescend bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Select) GetFullSql() string {
|
func (q *Select) GetFullSql() string {
|
||||||
@ -33,10 +41,11 @@ func (q *Select) GetFullSql() string {
|
|||||||
|
|
||||||
workingSqlSlice = append(workingSqlSlice, "FROM "+q.Table)
|
workingSqlSlice = append(workingSqlSlice, "FROM "+q.Table)
|
||||||
|
|
||||||
|
// TODO: need to account for `AND` and `OR`s and stuff
|
||||||
for _, condition := range q.Conditionals {
|
for _, condition := range q.Conditionals {
|
||||||
workingSqlSlice = append(workingSqlSlice, condition.Key)
|
workingSqlSlice = append(workingSqlSlice, condition.Key)
|
||||||
workingSqlSlice = append(workingSqlSlice, condition.Operator)
|
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, " ")
|
fullSql := strings.Join(workingSqlSlice, " ")
|
||||||
@ -44,6 +53,22 @@ func (q *Select) GetFullSql() string {
|
|||||||
return fullSql
|
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 {
|
func ParseSelectStatement(sql string) Select {
|
||||||
query := Select{}
|
query := Select{}
|
||||||
|
|
||||||
@ -52,10 +77,14 @@ func ParseSelectStatement(sql string) Select {
|
|||||||
passedFROM := false
|
passedFROM := false
|
||||||
passedTable := false
|
passedTable := false
|
||||||
passedWHERE := false
|
passedWHERE := false
|
||||||
|
passedConditionals := false
|
||||||
|
passedOrderBy := false
|
||||||
|
|
||||||
|
lookBehindBuffer := [10]string{} // TODO: make this an array of tokens instead
|
||||||
var workingConditional = Conditional{}
|
var workingConditional = Conditional{}
|
||||||
|
|
||||||
var columns []string
|
var columns []string
|
||||||
|
var orderBys []OrderBy
|
||||||
lexer := sqllexer.New(sql)
|
lexer := sqllexer.New(sql)
|
||||||
for {
|
for {
|
||||||
token := lexer.Scan()
|
token := lexer.Scan()
|
||||||
@ -70,54 +99,89 @@ func ParseSelectStatement(sql string) Select {
|
|||||||
continue
|
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 !passedColumns {
|
||||||
if token.Type == sqllexer.WILDCARD {
|
if token.Type == sqllexer.WILDCARD {
|
||||||
passedColumns = true
|
passedColumns = true
|
||||||
columns = make([]string, 0)
|
columns = make([]string, 0)
|
||||||
query.IsWildcard = true
|
query.IsWildcard = true
|
||||||
|
continue
|
||||||
} else if token.Type == sqllexer.IDENT {
|
} else if token.Type == sqllexer.IDENT {
|
||||||
columns = append(columns, token.Value)
|
columns = append(columns, token.Value)
|
||||||
continue
|
continue
|
||||||
} else if token.Type == sqllexer.PUNCTUATION || token.Type == sqllexer.SPACE {
|
} else if token.Type == sqllexer.PUNCTUATION || token.Type == sqllexer.SPACE {
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
passedColumns = true // TODO: make sure that I should be doing this
|
passedColumns = true
|
||||||
query.Columns = columns
|
query.Columns = columns
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !passedFROM && strings.ToUpper(token.Value) == "FROM" {
|
if !passedFROM && strings.ToUpper(token.Value) == "FROM" {
|
||||||
passedFROM = true
|
passedFROM = true
|
||||||
|
continue
|
||||||
} else if !passedFROM {
|
} 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 {
|
if !passedTable && token.Type == sqllexer.IDENT {
|
||||||
passedTable = true
|
passedTable = true
|
||||||
query.Table = token.Value
|
query.Table = token.Value
|
||||||
|
continue
|
||||||
} else if !passedTable {
|
} else if !passedTable {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !passedWHERE && strings.ToUpper(token.Value) == "WHERE" {
|
if !passedWHERE && token.Type == sqllexer.KEYWORD && strings.ToUpper(token.Value) == "WHERE" {
|
||||||
passedWHERE = true
|
passedWHERE = true
|
||||||
} else if !passedWHERE {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if token.Type == sqllexer.IDENT {
|
if passedWHERE && !passedConditionals {
|
||||||
workingConditional.Key = token.Value
|
if token.Type == sqllexer.IDENT {
|
||||||
} else if token.Type == sqllexer.OPERATOR {
|
workingConditional.Key = token.Value
|
||||||
workingConditional.Operator = token.Value
|
} else if token.Type == sqllexer.OPERATOR {
|
||||||
} else if token.Type == sqllexer.BOOLEAN || token.Type == sqllexer.NULL || token.Type == sqllexer.STRING || token.Type == sqllexer.NUMBER {
|
workingConditional.Operator = token.Value
|
||||||
workingConditional.Value = token.Value
|
} else if token.Type == sqllexer.BOOLEAN || token.Type == sqllexer.NULL || token.Type == sqllexer.STRING || token.Type == sqllexer.NUMBER {
|
||||||
} // TODO: add captire for data type
|
workingConditional.Value = token.Value
|
||||||
|
} // TODO: add captire for data type
|
||||||
|
|
||||||
|
if workingConditional.Key != "" && workingConditional.Operator != "" && workingConditional.Value != "" {
|
||||||
|
query.Conditionals = append(query.Conditionals, workingConditional)
|
||||||
|
workingConditional = Conditional{}
|
||||||
|
passedConditionals = true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if workingConditional.Key != "" && workingConditional.Operator != "" && workingConditional.Value != "" {
|
|
||||||
query.Conditionals = append(query.Conditionals, workingConditional)
|
|
||||||
workingConditional = Conditional{}
|
|
||||||
continue
|
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
|
return query
|
||||||
|
@ -13,7 +13,7 @@ type ParsingTest struct {
|
|||||||
func TestParseSelectStatement(t *testing.T) {
|
func TestParseSelectStatement(t *testing.T) {
|
||||||
var testSqlStatements = []ParsingTest{
|
var testSqlStatements = []ParsingTest{
|
||||||
{
|
{
|
||||||
input: "SELECT * FROM users WHERE age >= 30",
|
input: "SELECT * FROM users WHERE age >= 30",
|
||||||
expected: Select{
|
expected: Select{
|
||||||
Table: "users",
|
Table: "users",
|
||||||
IsWildcard: true,
|
IsWildcard: true,
|
||||||
@ -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 {
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user