query-interpreter/q/parse.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
}