369 lines
9.2 KiB
Go
369 lines
9.2 KiB
Go
package q
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/DataDog/go-sqllexer"
|
|
)
|
|
|
|
func parseSelectStatement(p *parser) error {
|
|
selectQuery := p.query.(*Select)
|
|
|
|
distinctErr := parseDistinct(p)
|
|
if distinctErr != nil {
|
|
return distinctErr
|
|
}
|
|
|
|
foundWildcard, _ := p.findToken(func(t Token) bool {
|
|
return t.Type == sqllexer.WILDCARD
|
|
})
|
|
|
|
selectQuery.IsWildcard = foundWildcard != nil
|
|
|
|
if !selectQuery.IsWildcard {
|
|
err := parseSelectColumns(p)
|
|
if err != nil {
|
|
fmt.Println(err.Error())
|
|
return err
|
|
}
|
|
}
|
|
|
|
tableErr := parseSelectTable(p)
|
|
if tableErr != nil {
|
|
return tableErr
|
|
}
|
|
|
|
conditionalsErr := parseSelectConditionals(p)
|
|
if conditionalsErr != nil {
|
|
return conditionalsErr
|
|
}
|
|
|
|
ordersByErr := parseOrderBys(p)
|
|
if ordersByErr != nil {
|
|
return ordersByErr
|
|
}
|
|
|
|
parseJoins(p)
|
|
|
|
return nil
|
|
}
|
|
|
|
func parseDistinct(p *parser) error {
|
|
selectQuery := p.query.(*Select)
|
|
|
|
foundDistinctKeyword, _ := p.findToken(func(t Token) bool {
|
|
return t.Type == sqllexer.KEYWORD && strings.ToUpper(t.Value) == "DISTINCT"
|
|
})
|
|
|
|
if foundDistinctKeyword != nil {
|
|
selectQuery.IsDistinct = true
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func parseSelectColumns(p *parser) error {
|
|
selectQuery := p.query.(*Select)
|
|
|
|
_, selectCommandIndex := p.findToken(func(t Token) bool {
|
|
return t.Type == sqllexer.COMMAND && strings.ToUpper(t.Value) == "SELECT"
|
|
})
|
|
|
|
_, fromKeywordIndex := p.findToken(func(t Token) bool {
|
|
return t.Type == sqllexer.KEYWORD && strings.ToUpper(t.Value) == "FROM"
|
|
})
|
|
|
|
if selectCommandIndex < 0 || fromKeywordIndex < 0 {
|
|
return fmt.Errorf("Could not find range between SELECT and FROM")
|
|
}
|
|
|
|
lookBehindBuffer := [10]Token{}
|
|
var workingColumn Column
|
|
columns := make([]Column, 0)
|
|
|
|
startRange := selectCommandIndex + 1
|
|
endRange := fromKeywordIndex - 1
|
|
|
|
for i := startRange; i <= endRange; i++ {
|
|
token := p.tokens[i]
|
|
|
|
if token.Type == sqllexer.FUNCTION {
|
|
unshiftBuffer(&lookBehindBuffer, token)
|
|
workingColumn.AggregateFunction = AggregateFunctionTypeByName(token.Value)
|
|
continue
|
|
} else if token.Type == sqllexer.PUNCTUATION && token.Value == "," {
|
|
columns = append(columns, workingColumn)
|
|
workingColumn = Column{}
|
|
continue
|
|
} else if token.Type == sqllexer.IDENT {
|
|
unshiftBuffer(&lookBehindBuffer, token)
|
|
|
|
if lookBehindBuffer[1].Type == sqllexer.ALIAS_INDICATOR {
|
|
workingColumn.Alias = token.Value
|
|
} else {
|
|
workingColumn.Name = token.Value
|
|
}
|
|
continue
|
|
} else if token.Type == sqllexer.ALIAS_INDICATOR {
|
|
unshiftBuffer(&lookBehindBuffer, token)
|
|
continue
|
|
} else if i == endRange {
|
|
if workingColumn.Name != "" {
|
|
columns = append(columns, workingColumn)
|
|
workingColumn = Column{}
|
|
}
|
|
}
|
|
}
|
|
|
|
selectQuery.Columns = columns
|
|
|
|
return nil
|
|
}
|
|
|
|
func parseSelectTable(p *parser) error {
|
|
selectQuery := p.query.(*Select)
|
|
|
|
_, fromKeywordIndex := p.findToken(func(t Token) bool {
|
|
return t.Type == sqllexer.KEYWORD && strings.ToUpper(t.Value) == "FROM"
|
|
})
|
|
|
|
if fromKeywordIndex < 0 {
|
|
return fmt.Errorf("Could not FROM keyword to look for table name")
|
|
}
|
|
|
|
var foundTable Table
|
|
|
|
for i := fromKeywordIndex + 1; i < len(p.tokens); i++ {
|
|
t := &p.tokens[i]
|
|
if foundTable.Name == "" && t.Type == sqllexer.IDENT {
|
|
foundTable.Name = p.tokens[i].Value
|
|
continue
|
|
} else if t.Type == sqllexer.IDENT {
|
|
foundTable.Alias = p.tokens[i].Value
|
|
break
|
|
} else if t.Type == sqllexer.SPACE || t.Type == sqllexer.ALIAS_INDICATOR {
|
|
continue
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
|
|
if foundTable.Name == "" {
|
|
return fmt.Errorf("Could not find table name")
|
|
}
|
|
|
|
selectQuery.Table = foundTable
|
|
return nil
|
|
}
|
|
|
|
func parseSelectConditionals(p *parser) error {
|
|
selectQuery := p.query.(*Select)
|
|
|
|
_, whereKeywordIndex := p.findToken(func(t Token) bool {
|
|
return t.Type == sqllexer.KEYWORD && strings.ToUpper(t.Value) == "WHERE"
|
|
})
|
|
|
|
if whereKeywordIndex < 0 {
|
|
return nil // fmt.Errorf("Could not find WHERE to look for conditionals")
|
|
}
|
|
|
|
var workingConditional Conditional
|
|
for i := whereKeywordIndex + 1; i < len(p.tokens); i++ {
|
|
t := &p.tokens[i]
|
|
|
|
if t.Type == sqllexer.KEYWORD && strings.ToUpper(t.Value) != "AND" && strings.ToUpper(t.Value) != "OR" && strings.ToUpper(t.Value) != "NOT" {
|
|
break
|
|
}
|
|
|
|
if t.Type == sqllexer.IDENT {
|
|
workingConditional.Key = t.Value
|
|
} else if t.Type == sqllexer.OPERATOR {
|
|
workingConditional.Operator = t.Value
|
|
} else if t.Type == sqllexer.BOOLEAN || t.Type == sqllexer.NULL || t.Type == sqllexer.STRING || t.Type == sqllexer.NUMBER {
|
|
workingConditional.Value = t.Value
|
|
} else if t.Type == sqllexer.KEYWORD {
|
|
if strings.ToUpper(t.Value) == "AND" || strings.ToUpper(t.Value) == "OR" {
|
|
workingConditional.Extension = strings.ToUpper(t.Value)
|
|
}
|
|
}
|
|
|
|
if workingConditional.Key != "" && workingConditional.Operator != "" && workingConditional.Value != "" {
|
|
selectQuery.Conditionals = append(selectQuery.Conditionals, workingConditional)
|
|
workingConditional = Conditional{}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func parseOrderBys(p *parser) error {
|
|
selectQuery := p.query.(*Select)
|
|
|
|
_, byKeywordIndex := p.findToken(func(t Token) bool {
|
|
return t.Type == sqllexer.KEYWORD && strings.ToUpper(t.Value) == "BY"
|
|
})
|
|
|
|
if byKeywordIndex < 0 {
|
|
return nil
|
|
}
|
|
|
|
var orderBys []OrderBy
|
|
|
|
var workingOrderBy OrderBy
|
|
for i := byKeywordIndex + 1; i < len(p.tokens); i++ {
|
|
t := &p.tokens[i]
|
|
if t.Type == sqllexer.SPACE {
|
|
continue
|
|
} else if t.Type == sqllexer.IDENT && workingOrderBy.Key == "" {
|
|
workingOrderBy.Key = t.Value
|
|
continue
|
|
} else if t.Type == sqllexer.IDENT && workingOrderBy.Key != "" {
|
|
orderBys = append(orderBys, workingOrderBy)
|
|
workingOrderBy.Key = t.Value
|
|
continue
|
|
} else if t.Type == sqllexer.KEYWORD {
|
|
if t.Value == "DESC" {
|
|
workingOrderBy.IsDescend = true
|
|
} else if t.Value != "ASC" {
|
|
break
|
|
}
|
|
} else if t.Type == sqllexer.PUNCTUATION {
|
|
orderBys = append(orderBys, workingOrderBy)
|
|
workingOrderBy = OrderBy{}
|
|
continue
|
|
}
|
|
}
|
|
|
|
if workingOrderBy.Key != "" {
|
|
orderBys = append(orderBys, workingOrderBy)
|
|
}
|
|
|
|
selectQuery.OrderBys = orderBys
|
|
|
|
return nil
|
|
}
|
|
|
|
func parseJoins(p *parser) error {
|
|
selectQuery := p.query.(*Select)
|
|
|
|
foundJoinKeywords := p.findAllTokens(func(t Token) bool {
|
|
return t.Type == sqllexer.COMMAND && strings.ToUpper(t.Value) == "JOIN"
|
|
})
|
|
|
|
if len(foundJoinKeywords) <= 0 {
|
|
return nil
|
|
}
|
|
|
|
type FoundJoinSubslices struct {
|
|
Tokens []Token
|
|
StartingIndexInGreaterStatement int
|
|
}
|
|
|
|
var joinTokenRanges []FoundJoinSubslices
|
|
|
|
for i, foundJoin := range foundJoinKeywords {
|
|
|
|
startRangeIndex := foundJoin.Index
|
|
var endRangeIndex int
|
|
|
|
if i == (len(foundJoinKeywords) - 1) {
|
|
endRangeIndex = len(p.tokens) - 1
|
|
} else {
|
|
endRangeIndex = foundJoinKeywords[i+1].Index
|
|
}
|
|
|
|
joinTokenRanges = append(joinTokenRanges, FoundJoinSubslices{
|
|
Tokens: p.tokens[startRangeIndex:endRangeIndex],
|
|
StartingIndexInGreaterStatement: startRangeIndex,
|
|
})
|
|
}
|
|
|
|
for _, joinRange := range joinTokenRanges {
|
|
var workingJoin Join
|
|
workingJoin.MainTable = selectQuery.Table
|
|
|
|
// check for the join type by looking backwards in the greater statement
|
|
joinTypeSearchIndex := joinRange.StartingIndexInGreaterStatement - 1
|
|
for ; joinTypeSearchIndex >= 0; joinTypeSearchIndex-- {
|
|
if p.tokens[joinTypeSearchIndex].Type == sqllexer.KEYWORD {
|
|
switch strings.ToUpper(p.tokens[joinTypeSearchIndex].Value) {
|
|
case "LEFT":
|
|
workingJoin.Type = LEFT
|
|
break
|
|
case "RIGHT":
|
|
workingJoin.Type = RIGHT
|
|
break
|
|
case "FULL":
|
|
workingJoin.Type = FULL
|
|
break
|
|
case "SELF":
|
|
workingJoin.Type = SELF
|
|
break
|
|
case "INNER":
|
|
workingJoin.Type = INNER
|
|
default:
|
|
workingJoin.Type = INNER
|
|
}
|
|
break // Stop after finding first keyword
|
|
}
|
|
}
|
|
|
|
// Find joined table name
|
|
for i := 1; i < len(joinRange.Tokens); i++ {
|
|
if joinRange.Tokens[i].Type == sqllexer.IDENT {
|
|
workingJoin.JoiningTable.Name = joinRange.Tokens[i].Value
|
|
break // Stop after finding first IDENT
|
|
// TODO: make sure you dont have to check for aliases
|
|
}
|
|
|
|
}
|
|
|
|
//var ons []Conditional
|
|
var workingOn Conditional
|
|
|
|
_, foundOnTokenIndex := FindTokenInArray(joinRange.Tokens, func(t Token) bool {
|
|
return t.Type == sqllexer.KEYWORD && strings.ToUpper(t.Value) == "ON"
|
|
})
|
|
|
|
if foundOnTokenIndex < 0 {
|
|
selectQuery.Joins = append(selectQuery.Joins, workingJoin)
|
|
continue
|
|
}
|
|
|
|
for i := foundOnTokenIndex + 1; i < len(joinRange.Tokens); i++ {
|
|
t := &joinRange.Tokens[i]
|
|
|
|
if t.Type == sqllexer.KEYWORD && strings.ToUpper(t.Value) != "AND" && strings.ToUpper(t.Value) != "OR" && strings.ToUpper(t.Value) != "NOT" {
|
|
break
|
|
}
|
|
|
|
if t.Type == sqllexer.IDENT {
|
|
if workingOn.Key == "" {
|
|
workingOn.Key = t.Value
|
|
} else {
|
|
workingOn.Value = t.Value
|
|
}
|
|
} else if t.Type == sqllexer.OPERATOR {
|
|
workingOn.Operator = t.Value
|
|
} else if t.Type == sqllexer.BOOLEAN || t.Type == sqllexer.NULL || t.Type == sqllexer.STRING || t.Type == sqllexer.NUMBER {
|
|
workingOn.Value = t.Value
|
|
} else if t.Type == sqllexer.KEYWORD {
|
|
if strings.ToUpper(t.Value) == "AND" || strings.ToUpper(t.Value) == "OR" {
|
|
workingOn.Extension = strings.ToUpper(t.Value)
|
|
}
|
|
}
|
|
|
|
if workingOn.Key != "" && workingOn.Operator != "" && workingOn.Value != "" {
|
|
workingJoin.Ons = append(workingJoin.Ons, workingOn)
|
|
workingOn = Conditional{}
|
|
}
|
|
}
|
|
|
|
selectQuery.Joins = append(selectQuery.Joins, workingJoin)
|
|
workingJoin = Join{}
|
|
}
|
|
|
|
return nil
|
|
}
|