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 }