186 lines
3.7 KiB
Go
186 lines
3.7 KiB
Go
package q
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/DataDog/go-sqllexer"
|
|
)
|
|
|
|
// Find first token in array that matches condition
|
|
func FindTokenInArray(tokens []Token, condition func(Token) bool) (*Token, int) {
|
|
for i, element := range tokens {
|
|
if condition(element) {
|
|
return &element, i
|
|
}
|
|
}
|
|
return nil, -1
|
|
}
|
|
|
|
type Token = sqllexer.Token
|
|
|
|
type parser struct {
|
|
sql string
|
|
tokens []Token
|
|
index int
|
|
query Query
|
|
lookBehindBuffer []Token
|
|
}
|
|
|
|
func (p *parser) lookAhead(count int) Token {
|
|
return p.tokens[p.index+count]
|
|
}
|
|
|
|
func (p *parser) lookBehind(count int) Token {
|
|
return p.tokens[p.index-count]
|
|
}
|
|
|
|
// Returns pointer of the first found Token and its index in parser.Tokens
|
|
// Returns -1 as the index if not found
|
|
func (p *parser) findToken(condition func(Token) bool) (*Token, int) {
|
|
for i, element := range p.tokens {
|
|
if condition(element) {
|
|
return &element, i
|
|
}
|
|
}
|
|
return nil, -1
|
|
}
|
|
|
|
type FoundToken struct {
|
|
Token Token
|
|
Index int
|
|
}
|
|
|
|
// Returns all tokens that match the condition, along with their indices
|
|
func (p *parser) findAllTokens(condition func(Token) bool) []FoundToken {
|
|
matches := make([]FoundToken, 0)
|
|
|
|
for i, token := range p.tokens {
|
|
if condition(token) {
|
|
matches = append(matches, FoundToken{
|
|
Token: token,
|
|
Index: i,
|
|
})
|
|
}
|
|
}
|
|
|
|
return matches
|
|
}
|
|
|
|
func filter[T any](items []T, fn func(item T) bool) []T {
|
|
filteredItems := []T{}
|
|
for _, value := range items {
|
|
if fn(value) {
|
|
filteredItems = append(filteredItems, value)
|
|
}
|
|
}
|
|
return filteredItems
|
|
}
|
|
|
|
func unshiftBuffer(buf *[10]sqllexer.Token, value sqllexer.Token) {
|
|
for i := 9; i >= 1; i-- {
|
|
buf[i] = buf[i-1]
|
|
}
|
|
|
|
buf[0] = value
|
|
}
|
|
|
|
func Parse(sql string) (Query, error) {
|
|
lexer := sqllexer.New(sql)
|
|
|
|
var tokens []Token
|
|
for {
|
|
t := lexer.Scan()
|
|
tokens = append(tokens, *t)
|
|
|
|
if IsTokenEndOfStatement(t) {
|
|
break
|
|
}
|
|
}
|
|
|
|
p := parser{
|
|
sql: sql,
|
|
tokens: tokens,
|
|
}
|
|
|
|
queryType, err := findQueryType(&p)
|
|
if err != nil {
|
|
return p.query, err
|
|
}
|
|
|
|
switch strings.ToUpper(queryType) {
|
|
case "SELECT":
|
|
p.query = &Select{
|
|
Type: SELECT,
|
|
}
|
|
parseSelectStatement(&p)
|
|
default:
|
|
return p.query, fmt.Errorf("No process defined for determined queryType: %s", queryType)
|
|
}
|
|
|
|
return p.query, nil
|
|
}
|
|
|
|
func findQueryType(p *parser) (string, error) {
|
|
firstCommand, _ := p.findToken(func(t Token) bool {
|
|
return t.Type == sqllexer.COMMAND
|
|
})
|
|
|
|
if firstCommand == nil {
|
|
return "", fmt.Errorf("Could not find query type")
|
|
}
|
|
|
|
return firstCommand.Value, nil
|
|
}
|
|
|
|
func GetFullStringFromColumn(column Column) string {
|
|
var workingSlice string
|
|
|
|
if column.AggregateFunction > 0 {
|
|
workingSlice = fmt.Sprintf(
|
|
"%s(%s)",
|
|
AggregateFunctionTypeString(column.AggregateFunction),
|
|
column.Name,
|
|
)
|
|
} else {
|
|
workingSlice = column.Name
|
|
}
|
|
|
|
if column.Alias != "" {
|
|
workingSlice += fmt.Sprintf(" AS %s", column.Alias)
|
|
}
|
|
|
|
return workingSlice
|
|
|
|
}
|
|
|
|
func (q *Select) GetFullSql() string {
|
|
var workingSqlSlice []string
|
|
|
|
workingSqlSlice = append(workingSqlSlice, "SELECT")
|
|
|
|
if q.IsWildcard {
|
|
workingSqlSlice = append(workingSqlSlice, "*")
|
|
} else {
|
|
for i, column := range q.Columns {
|
|
if i < (len(q.Columns) - 1) {
|
|
workingSqlSlice = append(workingSqlSlice, GetFullStringFromColumn(column)+",")
|
|
} else {
|
|
workingSqlSlice = append(workingSqlSlice, GetFullStringFromColumn(column))
|
|
}
|
|
}
|
|
}
|
|
|
|
workingSqlSlice = append(workingSqlSlice, "FROM "+q.Table.Name) // TODO: figure out what to do from alias
|
|
|
|
for _, condition := range q.Conditionals {
|
|
workingSqlSlice = append(workingSqlSlice, condition.Key)
|
|
workingSqlSlice = append(workingSqlSlice, condition.Operator)
|
|
workingSqlSlice = append(workingSqlSlice, condition.Value)
|
|
}
|
|
|
|
fullSql := strings.Join(workingSqlSlice, " ")
|
|
|
|
return fullSql
|
|
}
|