refact: moved select stuff to seperate file
This commit is contained in:
parent
325f660a04
commit
50bfba4c09
4
main.go
4
main.go
@ -75,12 +75,12 @@ func HandlePostQuery(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
var query q.Select
|
||||
var query q.Query
|
||||
done := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
defer cancel()
|
||||
query = q.ParseSelectStatement(request.SQL)
|
||||
query, err = q.Parse(request.SQL)
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
|
||||
2
q/dto.go
2
q/dto.go
@ -150,7 +150,7 @@ type Select struct {
|
||||
IsDistinct bool `json:"is_distinct"`
|
||||
}
|
||||
|
||||
func MarshalSelect(selectStatement Select) ([]byte, error) {
|
||||
func MarshalSelect(selectStatement Query) ([]byte, error) {
|
||||
jsonData, err := json.MarshalIndent(selectStatement, "", " ")
|
||||
return jsonData, err
|
||||
}
|
||||
|
||||
375
q/parse.go
375
q/parse.go
@ -133,362 +133,53 @@ func findQueryType(p *parser) (string, error) {
|
||||
return firstCommand.Value, nil
|
||||
}
|
||||
|
||||
func parseSelectStatement(p *parser) error {
|
||||
selectQuery := p.query.(*Select)
|
||||
func GetFullStringFromColumn(column Column) string {
|
||||
var workingSlice string
|
||||
|
||||
distinctErr := parseDistinct(p)
|
||||
if distinctErr != nil {
|
||||
return distinctErr
|
||||
if column.AggregateFunction > 0 {
|
||||
workingSlice = fmt.Sprintf(
|
||||
"%s(%s)",
|
||||
AggregateFunctionTypeString(column.AggregateFunction),
|
||||
column.Name,
|
||||
)
|
||||
} else {
|
||||
workingSlice = column.Name
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
if column.Alias != "" {
|
||||
workingSlice += fmt.Sprintf(" AS %s", column.Alias)
|
||||
}
|
||||
|
||||
tableErr := parseSelectTable(p)
|
||||
if tableErr != nil {
|
||||
return tableErr
|
||||
}
|
||||
return workingSlice
|
||||
|
||||
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)
|
||||
func (q *Select) GetFullSql() string {
|
||||
var workingSqlSlice []string
|
||||
|
||||
foundDistinctKeyword, _ := p.findToken(func(t Token) bool {
|
||||
return t.Type == sqllexer.KEYWORD && strings.ToUpper(t.Value) == "DISTINCT"
|
||||
})
|
||||
workingSqlSlice = append(workingSqlSlice, "SELECT")
|
||||
|
||||
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
|
||||
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 {
|
||||
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{}
|
||||
workingSqlSlice = append(workingSqlSlice, GetFullStringFromColumn(column))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectQuery.Columns = columns
|
||||
workingSqlSlice = append(workingSqlSlice, "FROM "+q.Table.Name) // TODO: figure out what to do from alias
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
344
q/parse_test.go
344
q/parse_test.go
@ -1,344 +0,0 @@
|
||||
package q
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type ParsingTest struct {
|
||||
input string
|
||||
expected Query
|
||||
}
|
||||
|
||||
func TestParseSelectStatement_StateMachine(t *testing.T) {
|
||||
var testSqlStatements = []ParsingTest{
|
||||
{
|
||||
input: "SELECT * FROM users WHERE age >= 30",
|
||||
expected: &Select{
|
||||
Type: SELECT,
|
||||
Table: Table{Name: "users"},
|
||||
IsWildcard: true,
|
||||
Conditionals: []Conditional{
|
||||
{
|
||||
Key: "age",
|
||||
Operator: ">=",
|
||||
Value: "30",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "SELECT CustomerName, City FROM Customers",
|
||||
expected: &Select{
|
||||
Type: SELECT,
|
||||
Table: Table{Name: "Customers"},
|
||||
IsWildcard: false,
|
||||
Columns: []Column{
|
||||
{
|
||||
Name: "CustomerName",
|
||||
},
|
||||
{
|
||||
Name: "City",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "SELECT DISTINCT username, email FROM users",
|
||||
expected: &Select{
|
||||
Type: SELECT,
|
||||
IsDistinct: true,
|
||||
Table: Table{Name: "users"},
|
||||
IsWildcard: false,
|
||||
Columns: []Column{
|
||||
{
|
||||
Name: "username",
|
||||
},
|
||||
{
|
||||
Name: "email",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "SELECT CustomerName AS Customer, City AS town FROM Customers AS People",
|
||||
expected: &Select{
|
||||
Type: SELECT,
|
||||
Table: Table{Name: "Customers", Alias: "People"},
|
||||
IsWildcard: false,
|
||||
Columns: []Column{
|
||||
{
|
||||
Name: "CustomerName",
|
||||
Alias: "Customer",
|
||||
},
|
||||
{
|
||||
Name: "City",
|
||||
Alias: "town",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "SELECT * FROM Orders ORDER BY StreetNumber, CountryCode;",
|
||||
expected: &Select{
|
||||
Type: SELECT,
|
||||
Table: Table{Name: "Orders"},
|
||||
IsWildcard: true,
|
||||
OrderBys: []OrderBy{
|
||||
{
|
||||
Key: "StreetNumber",
|
||||
},
|
||||
{
|
||||
Key: "CountryCode",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "SELECT * FROM ZipCodes ORDER BY Code ASC, StateName DESC",
|
||||
expected: &Select{
|
||||
Type: SELECT,
|
||||
Table: Table{Name: "ZipCodes"},
|
||||
IsWildcard: true,
|
||||
OrderBys: []OrderBy{
|
||||
{
|
||||
Key: "Code",
|
||||
IsDescend: false,
|
||||
},
|
||||
{
|
||||
Key: "StateName",
|
||||
IsDescend: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "SELECT id, streetNumber AS streetNum, streetName, city, state FROM Addresses WHERE state = 'AL' AND zip > 9000 OR zip <= 12000 ORDER BY zip DESC, streetNumber",
|
||||
expected: &Select{
|
||||
Type: SELECT,
|
||||
Table: Table{Name: "Addresses"},
|
||||
IsWildcard: false,
|
||||
Columns: []Column{
|
||||
{
|
||||
Name: "id",
|
||||
},
|
||||
{
|
||||
Name: "streetNumber",
|
||||
Alias: "streetNum",
|
||||
},
|
||||
{
|
||||
Name: "streetName",
|
||||
},
|
||||
{
|
||||
Name: "city",
|
||||
},
|
||||
{
|
||||
Name: "state",
|
||||
},
|
||||
},
|
||||
Conditionals: []Conditional{
|
||||
{
|
||||
Key: "state",
|
||||
Operator: "=",
|
||||
Value: "'AL'",
|
||||
},
|
||||
{
|
||||
Key: "zip",
|
||||
Operator: ">",
|
||||
Value: "9000",
|
||||
Extension: "AND",
|
||||
},
|
||||
{
|
||||
Key: "zip",
|
||||
Operator: "<=",
|
||||
Value: "12000",
|
||||
Extension: "OR",
|
||||
},
|
||||
},
|
||||
OrderBys: []OrderBy{
|
||||
{
|
||||
Key: "zip",
|
||||
IsDescend: true,
|
||||
},
|
||||
{
|
||||
Key: "streetNumber",
|
||||
IsDescend: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "SELECT ProductID, ProductName, CategoryName FROM Products INNER JOIN Categories ON Products.CategoryID = Categories.CategoryID AND Products.SupplierID = Categories.SupplierID LEFT JOIN Stores ON Products.StoreID = Stores.ID ;",
|
||||
expected: &Select{
|
||||
Type: SELECT,
|
||||
Table: Table{Name: "Products"},
|
||||
Columns: []Column{
|
||||
{Name: "ProductID"},
|
||||
{Name: "ProductName"},
|
||||
{Name: "CategoryName"},
|
||||
},
|
||||
Joins: []Join{
|
||||
{
|
||||
Type: INNER,
|
||||
MainTable: Table{
|
||||
Name: "Products",
|
||||
},
|
||||
JoiningTable: Table{
|
||||
Name: "Categories",
|
||||
},
|
||||
Ons: []Conditional{
|
||||
{
|
||||
Key: "Products.CategoryID",
|
||||
Operator: "=",
|
||||
Value: "Categories.CategoryID",
|
||||
},
|
||||
{
|
||||
Key: "Products.SupplierID",
|
||||
Operator: "=",
|
||||
Value: "Categories.SupplierID",
|
||||
Extension: "AND",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: LEFT,
|
||||
MainTable: Table{
|
||||
Name: "Products",
|
||||
},
|
||||
JoiningTable: Table{
|
||||
Name: "Stores",
|
||||
},
|
||||
Ons: []Conditional{
|
||||
{
|
||||
Key: "Products.StoreID",
|
||||
Operator: "=",
|
||||
Value: "Stores.ID",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, sql := range testSqlStatements {
|
||||
testName := fmt.Sprintf("%s", sql.input)
|
||||
expected := sql.expected.(*Select)
|
||||
|
||||
t.Run(testName, func(t *testing.T) {
|
||||
answer, err := Parse(sql.input)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
answerAsSelect := answer.(*Select)
|
||||
|
||||
if answerAsSelect.Type != expected.Type {
|
||||
t.Errorf("got %d for Select.Type, expected %d", answerAsSelect.Type, expected.Type)
|
||||
}
|
||||
if answerAsSelect.IsWildcard != expected.IsWildcard {
|
||||
t.Errorf("got %#v for Select.IsWildcard, expected %#v", answerAsSelect.IsWildcard, expected.IsWildcard)
|
||||
}
|
||||
if answerAsSelect.Table.Name != expected.Table.Name {
|
||||
t.Errorf("got %s for Select.Table.Name, expected %s", answerAsSelect.Table.Name, expected.Table.Name)
|
||||
}
|
||||
if answerAsSelect.Table.Alias != expected.Table.Alias {
|
||||
t.Errorf("got %s for Select.Table.Alias, expected %s", answerAsSelect.Table.Alias, expected.Table.Alias)
|
||||
}
|
||||
if answerAsSelect.IsDistinct != expected.IsDistinct {
|
||||
t.Errorf("got %v for Select.IsDistinct, expected %v", answerAsSelect.IsDistinct, expected.IsDistinct)
|
||||
}
|
||||
|
||||
if len(answerAsSelect.Columns) != len(expected.Columns) {
|
||||
t.Errorf("got %d number of columns for Select.Columns, expected %d", len(answerAsSelect.Columns), len(expected.Columns))
|
||||
} else {
|
||||
for i, expectedColumn := range expected.Columns {
|
||||
if expectedColumn.Name != answerAsSelect.Columns[i].Name {
|
||||
t.Errorf("got %s for Select.Column[%d].Name, expected %s", answerAsSelect.Columns[i].Name, i, expectedColumn.Name)
|
||||
}
|
||||
if expectedColumn.Alias != answerAsSelect.Columns[i].Alias {
|
||||
t.Errorf("got %s for Select.Column[%d].Alias, expected %s", answerAsSelect.Columns[i].Alias, i, expectedColumn.Alias)
|
||||
}
|
||||
if expectedColumn.AggregateFunction != answerAsSelect.Columns[i].AggregateFunction {
|
||||
t.Errorf("got %d for Select.Column[%d].AggregateFunction, expected %d", answerAsSelect.Columns[i].AggregateFunction, i, expectedColumn.AggregateFunction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(answerAsSelect.Conditionals) != len(expected.Conditionals) {
|
||||
t.Errorf("got %d number of conditionals for Select.Conditionals, expected %d", len(answerAsSelect.Conditionals), len(expected.Conditionals))
|
||||
} else {
|
||||
for i, expectedCondition := range expected.Conditionals {
|
||||
if expectedCondition.Key != answerAsSelect.Conditionals[i].Key {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Key, expected %s", answerAsSelect.Conditionals[i].Key, i, expectedCondition.Key)
|
||||
}
|
||||
if expectedCondition.Operator != answerAsSelect.Conditionals[i].Operator {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Operator, expected %s", answerAsSelect.Conditionals[i].Operator, i, expectedCondition.Operator)
|
||||
}
|
||||
if expectedCondition.Value != answerAsSelect.Conditionals[i].Value {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Value, expected %s", answerAsSelect.Conditionals[i].Value, i, expectedCondition.Value)
|
||||
}
|
||||
if expectedCondition.Extension != answerAsSelect.Conditionals[i].Extension {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Extension, expected %s", answerAsSelect.Conditionals[i].Extension, i, expectedCondition.Extension)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(answerAsSelect.OrderBys) != len(expected.OrderBys) {
|
||||
t.Errorf("got %d number of orderBys for Select.OrderBys, expected %d", len(answerAsSelect.OrderBys), len(expected.OrderBys))
|
||||
} else {
|
||||
for i, expectedOrderBy := range expected.OrderBys {
|
||||
if expectedOrderBy.Key != answerAsSelect.OrderBys[i].Key {
|
||||
t.Errorf("got %s for Select.OrderBys[%d].Key, expected %s", answerAsSelect.OrderBys[i].Key, i, expectedOrderBy.Key)
|
||||
}
|
||||
if expectedOrderBy.IsDescend != answerAsSelect.OrderBys[i].IsDescend {
|
||||
t.Errorf("got %#v for Select.OrderBys[%d].IsDescend, expected %#v", answerAsSelect.OrderBys[i].IsDescend, i, expectedOrderBy.IsDescend)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(answerAsSelect.Joins) != len(expected.Joins) {
|
||||
t.Errorf("got %d number of joins for Select.Joinss, expected %d", len(answerAsSelect.Joins), len(expected.Joins))
|
||||
} else {
|
||||
for i, expectedJoin := range expected.Joins {
|
||||
if answerAsSelect.Joins[i].Type != expectedJoin.Type {
|
||||
t.Errorf("got %d for Select.Joins[%d].Type, expected %d", answerAsSelect.Joins[i].Type, i, expectedJoin.Type)
|
||||
}
|
||||
if answerAsSelect.Joins[i].MainTable.Name != expectedJoin.MainTable.Name {
|
||||
t.Errorf("got %s for Select.Joins[%d].MainTable.Name, expected %s", answerAsSelect.Joins[i].MainTable.Name, i, expectedJoin.MainTable.Name)
|
||||
}
|
||||
if answerAsSelect.Joins[i].MainTable.Alias != expectedJoin.MainTable.Alias {
|
||||
t.Errorf("got %s for Select.Joins[%d].MainTable.Alias, expected %s", answerAsSelect.Joins[i].MainTable.Alias, i, expectedJoin.MainTable.Alias)
|
||||
}
|
||||
if answerAsSelect.Joins[i].JoiningTable.Name != expectedJoin.JoiningTable.Name {
|
||||
t.Errorf("got %s for Select.Joins[%d].JoiningTable.Name, expected %s", answerAsSelect.Joins[i].JoiningTable.Name, i, expectedJoin.JoiningTable.Name)
|
||||
}
|
||||
if answerAsSelect.Joins[i].JoiningTable.Alias != expectedJoin.JoiningTable.Alias {
|
||||
t.Errorf("got %s for Select.Joins[%d].JoiningTable.Alias, expected %s", answerAsSelect.Joins[i].JoiningTable.Alias, i, expectedJoin.JoiningTable.Alias)
|
||||
}
|
||||
|
||||
if len(answerAsSelect.Joins[i].Ons) != len(expectedJoin.Ons) {
|
||||
t.Errorf("got %d number of ons for Select.Joins.Ons, expected %d", len(answerAsSelect.Joins[i].Ons), len(expectedJoin.Ons))
|
||||
} else {
|
||||
for on_i, expectedCondition := range expected.Joins[i].Ons {
|
||||
if expectedCondition.Key != answerAsSelect.Joins[i].Ons[on_i].Key {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Key, expected %s", answerAsSelect.Joins[i].Ons[on_i].Key, on_i, expectedCondition.Key)
|
||||
}
|
||||
if expectedCondition.Operator != answerAsSelect.Joins[i].Ons[on_i].Operator {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Operator, expected %s", answerAsSelect.Joins[i].Ons[on_i].Operator, on_i, expectedCondition.Operator)
|
||||
}
|
||||
if expectedCondition.Value != answerAsSelect.Joins[i].Ons[on_i].Value {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Value, expected %s", answerAsSelect.Joins[i].Ons[on_i].Value, on_i, expectedCondition.Value)
|
||||
}
|
||||
if expectedCondition.Extension != answerAsSelect.Joins[i].Ons[on_i].Extension {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Extension, expected %s", answerAsSelect.Joins[i].Ons[on_i].Extension, on_i, expectedCondition.Extension)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
520
q/select.go
520
q/select.go
@ -7,244 +7,362 @@ import (
|
||||
"github.com/DataDog/go-sqllexer"
|
||||
)
|
||||
|
||||
func GetFullStringFromColumn(column Column) string {
|
||||
var workingSlice string
|
||||
func parseSelectStatement(p *parser) error {
|
||||
selectQuery := p.query.(*Select)
|
||||
|
||||
if column.AggregateFunction > 0 {
|
||||
workingSlice = fmt.Sprintf(
|
||||
"%s(%s)",
|
||||
AggregateFunctionTypeString(column.AggregateFunction),
|
||||
column.Name,
|
||||
)
|
||||
} else {
|
||||
workingSlice = column.Name
|
||||
distinctErr := parseDistinct(p)
|
||||
if distinctErr != nil {
|
||||
return distinctErr
|
||||
}
|
||||
|
||||
if column.Alias != "" {
|
||||
workingSlice += fmt.Sprintf(" AS %s", column.Alias)
|
||||
}
|
||||
foundWildcard, _ := p.findToken(func(t Token) bool {
|
||||
return t.Type == sqllexer.WILDCARD
|
||||
})
|
||||
|
||||
return workingSlice
|
||||
selectQuery.IsWildcard = foundWildcard != nil
|
||||
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
if !selectQuery.IsWildcard {
|
||||
err := parseSelectColumns(p)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
tableErr := parseSelectTable(p)
|
||||
if tableErr != nil {
|
||||
return tableErr
|
||||
}
|
||||
|
||||
fullSql := strings.Join(workingSqlSlice, " ")
|
||||
conditionalsErr := parseSelectConditionals(p)
|
||||
if conditionalsErr != nil {
|
||||
return conditionalsErr
|
||||
}
|
||||
|
||||
return fullSql
|
||||
ordersByErr := parseOrderBys(p)
|
||||
if ordersByErr != nil {
|
||||
return ordersByErr
|
||||
}
|
||||
|
||||
parseJoins(p)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseSelectStatement(sql string) Select {
|
||||
query := Select{}
|
||||
func parseDistinct(p *parser) error {
|
||||
selectQuery := p.query.(*Select)
|
||||
|
||||
passedSELECT := false
|
||||
passedColumns := false
|
||||
passedFROM := false
|
||||
passedTable := false
|
||||
passedWHERE := false
|
||||
passedConditionals := false
|
||||
passedOrderByKeywords := false
|
||||
passesOrderByColumns := false
|
||||
foundDistinctKeyword, _ := p.findToken(func(t Token) bool {
|
||||
return t.Type == sqllexer.KEYWORD && strings.ToUpper(t.Value) == "DISTINCT"
|
||||
})
|
||||
|
||||
lookBehindBuffer := [10]sqllexer.Token{}
|
||||
var workingConditional = Conditional{}
|
||||
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)
|
||||
|
||||
var columns []Column
|
||||
var orderBys []OrderBy
|
||||
lexer := sqllexer.New(sql)
|
||||
for {
|
||||
token := lexer.Scan()
|
||||
startRange := selectCommandIndex + 1
|
||||
endRange := fromKeywordIndex - 1
|
||||
|
||||
if !passedSELECT && strings.ToUpper(token.Value) != "SELECT" {
|
||||
break
|
||||
} else if !passedSELECT {
|
||||
passedSELECT = true
|
||||
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)
|
||||
|
||||
// For any keywords that are before the columns or wildcard
|
||||
if passedSELECT && len(columns) == 0 && !passedColumns {
|
||||
if token.Type == sqllexer.KEYWORD {
|
||||
switch strings.ToUpper(token.Value) {
|
||||
case "DISTINCT":
|
||||
query.IsDistinct = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !passedColumns {
|
||||
if token.Type == sqllexer.WILDCARD {
|
||||
passedColumns = true
|
||||
columns = make([]Column, 0)
|
||||
query.IsWildcard = true
|
||||
continue
|
||||
} else if token.Type == sqllexer.FUNCTION {
|
||||
unshiftBuffer(&lookBehindBuffer, *token)
|
||||
workingColumn.AggregateFunction = AggregateFunctionTypeByName(token.Value)
|
||||
continue
|
||||
} else if token.Type == sqllexer.PUNCTUATION {
|
||||
if token.Value == "," {
|
||||
columns = append(columns, workingColumn)
|
||||
workingColumn = Column{}
|
||||
}
|
||||
continue
|
||||
} else if token.Type == sqllexer.IDENT {
|
||||
unshiftBuffer(&lookBehindBuffer, *token)
|
||||
|
||||
if lookBehindBuffer[0].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 token.Type == sqllexer.SPACE {
|
||||
continue
|
||||
if lookBehindBuffer[1].Type == sqllexer.ALIAS_INDICATOR {
|
||||
workingColumn.Alias = token.Value
|
||||
} else {
|
||||
if workingColumn.Name != "" {
|
||||
columns = append(columns, workingColumn)
|
||||
workingColumn = Column{}
|
||||
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
|
||||
}
|
||||
|
||||
passedColumns = true
|
||||
query.Columns = columns
|
||||
break // Stop after finding first keyword
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: make sure to check for other keywords that are allowed
|
||||
if !passedFROM && strings.ToUpper(token.Value) == "FROM" {
|
||||
passedFROM = true
|
||||
continue
|
||||
}
|
||||
|
||||
if !passedTable && token.Type == sqllexer.IDENT {
|
||||
passedTable = true
|
||||
query.Table.Name = token.Value
|
||||
continue
|
||||
} else if !passedTable {
|
||||
continue
|
||||
}
|
||||
|
||||
if !passedWHERE && token.Type == sqllexer.KEYWORD && strings.ToUpper(token.Value) == "WHERE" {
|
||||
passedWHERE = true
|
||||
continue
|
||||
} else if !passedWHERE && token.Type == sqllexer.KEYWORD && strings.ToUpper(token.Value) != "WHERE" {
|
||||
passedWHERE = true
|
||||
}
|
||||
|
||||
if passedWHERE && !passedConditionals {
|
||||
if token.Type == sqllexer.KEYWORD && strings.ToUpper(token.Value) != "AND" && strings.ToUpper(token.Value) != "OR" && strings.ToUpper(token.Value) != "NOT" {
|
||||
passedConditionals = true
|
||||
}
|
||||
}
|
||||
|
||||
if passedWHERE && !passedConditionals {
|
||||
if token.Type == sqllexer.IDENT {
|
||||
workingConditional.Key = token.Value
|
||||
} else if token.Type == sqllexer.OPERATOR {
|
||||
workingConditional.Operator = token.Value
|
||||
} else if token.Type == sqllexer.BOOLEAN || token.Type == sqllexer.NULL || token.Type == sqllexer.STRING || token.Type == sqllexer.NUMBER {
|
||||
workingConditional.Value = token.Value
|
||||
} else if token.Type == sqllexer.KEYWORD {
|
||||
if strings.ToUpper(token.Value) == "AND" || strings.ToUpper(token.Value) == "OR" {
|
||||
workingConditional.Extension = strings.ToUpper(token.Value)
|
||||
}
|
||||
// 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
|
||||
}
|
||||
|
||||
if workingConditional.Key != "" && workingConditional.Operator != "" && workingConditional.Value != "" {
|
||||
query.Conditionals = append(query.Conditionals, workingConditional)
|
||||
workingConditional = Conditional{}
|
||||
}
|
||||
}
|
||||
|
||||
if IsTokenEndOfStatement(token) {
|
||||
//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
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// Checking For ORDER BY
|
||||
if passedConditionals && !passedOrderByKeywords && token.Type == sqllexer.KEYWORD {
|
||||
unshiftBuffer(&lookBehindBuffer, *token)
|
||||
|
||||
if strings.ToUpper(lookBehindBuffer[1].Value) == "ORDER" && strings.ToUpper(lookBehindBuffer[0].Value) == "BY" {
|
||||
passedOrderByKeywords = true
|
||||
}
|
||||
}
|
||||
|
||||
if passedOrderByKeywords && !passesOrderByColumns {
|
||||
|
||||
if token.Type == sqllexer.IDENT || token.Type == sqllexer.KEYWORD {
|
||||
unshiftBuffer(&lookBehindBuffer, *token)
|
||||
continue
|
||||
}
|
||||
|
||||
if token.Type == sqllexer.PUNCTUATION || token.Type == sqllexer.EOF {
|
||||
|
||||
var orderByColumnName string
|
||||
var directionKeyword string
|
||||
var isDescend bool = false
|
||||
|
||||
if lookBehindBuffer[0].Type == sqllexer.KEYWORD {
|
||||
orderByColumnName = lookBehindBuffer[1].Value
|
||||
directionKeyword = lookBehindBuffer[0].Value
|
||||
} else if lookBehindBuffer[0].Type == sqllexer.IDENT {
|
||||
orderByColumnName = lookBehindBuffer[0].Value
|
||||
if t.Type == sqllexer.IDENT {
|
||||
if workingOn.Key == "" {
|
||||
workingOn.Key = t.Value
|
||||
} else {
|
||||
workingOn.Value = t.Value
|
||||
}
|
||||
|
||||
if strings.ToUpper(directionKeyword) == "DESC" {
|
||||
isDescend = true
|
||||
}
|
||||
|
||||
orderBys = append(orderBys, OrderBy{
|
||||
Key: orderByColumnName,
|
||||
IsDescend: isDescend,
|
||||
})
|
||||
|
||||
if IsTokenEndOfStatement(token) {
|
||||
query.OrderBys = orderBys
|
||||
break
|
||||
} 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{}
|
||||
}
|
||||
}
|
||||
|
||||
query.OrderBys = orderBys
|
||||
|
||||
if IsTokenEndOfStatement(token) {
|
||||
break
|
||||
}
|
||||
|
||||
selectQuery.Joins = append(selectQuery.Joins, workingJoin)
|
||||
workingJoin = Join{}
|
||||
}
|
||||
|
||||
return query
|
||||
return nil
|
||||
}
|
||||
|
||||
242
q/select_test.go
242
q/select_test.go
@ -5,11 +5,17 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseSelectStatement(t *testing.T) {
|
||||
type ParsingTest struct {
|
||||
input string
|
||||
expected Query
|
||||
}
|
||||
|
||||
func TestParseSelectStatement_StateMachine(t *testing.T) {
|
||||
var testSqlStatements = []ParsingTest{
|
||||
{
|
||||
input: "SELECT * FROM users WHERE age >= 30",
|
||||
expected: &Select{
|
||||
Type: SELECT,
|
||||
Table: Table{Name: "users"},
|
||||
IsWildcard: true,
|
||||
Conditionals: []Conditional{
|
||||
@ -24,6 +30,7 @@ func TestParseSelectStatement(t *testing.T) {
|
||||
{
|
||||
input: "SELECT CustomerName, City FROM Customers",
|
||||
expected: &Select{
|
||||
Type: SELECT,
|
||||
Table: Table{Name: "Customers"},
|
||||
IsWildcard: false,
|
||||
Columns: []Column{
|
||||
@ -37,12 +44,36 @@ func TestParseSelectStatement(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "SELECT DISTINCT Country FROM Nations;",
|
||||
input: "SELECT DISTINCT username, email FROM users",
|
||||
expected: &Select{
|
||||
Table: Table{Name: "Nations"},
|
||||
Type: SELECT,
|
||||
IsDistinct: true,
|
||||
Table: Table{Name: "users"},
|
||||
IsWildcard: false,
|
||||
Columns: []Column{
|
||||
{
|
||||
Name: "Country",
|
||||
Name: "username",
|
||||
},
|
||||
{
|
||||
Name: "email",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "SELECT CustomerName AS Customer, City AS town FROM Customers AS People",
|
||||
expected: &Select{
|
||||
Type: SELECT,
|
||||
Table: Table{Name: "Customers", Alias: "People"},
|
||||
IsWildcard: false,
|
||||
Columns: []Column{
|
||||
{
|
||||
Name: "CustomerName",
|
||||
Alias: "Customer",
|
||||
},
|
||||
{
|
||||
Name: "City",
|
||||
Alias: "town",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -50,6 +81,7 @@ func TestParseSelectStatement(t *testing.T) {
|
||||
{
|
||||
input: "SELECT * FROM Orders ORDER BY StreetNumber, CountryCode;",
|
||||
expected: &Select{
|
||||
Type: SELECT,
|
||||
Table: Table{Name: "Orders"},
|
||||
IsWildcard: true,
|
||||
OrderBys: []OrderBy{
|
||||
@ -65,6 +97,7 @@ func TestParseSelectStatement(t *testing.T) {
|
||||
{
|
||||
input: "SELECT * FROM ZipCodes ORDER BY Code ASC, StateName DESC",
|
||||
expected: &Select{
|
||||
Type: SELECT,
|
||||
Table: Table{Name: "ZipCodes"},
|
||||
IsWildcard: true,
|
||||
OrderBys: []OrderBy{
|
||||
@ -80,8 +113,9 @@ func TestParseSelectStatement(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "SELECT id, streetNumber, streetName, city, state FROM Addresses WHERE state = 'AL' AND zip > 9000 OR zip <= 12000 ORDER BY zip DESC, streetNumber",
|
||||
input: "SELECT id, streetNumber AS streetNum, streetName, city, state FROM Addresses WHERE state = 'AL' AND zip > 9000 OR zip <= 12000 ORDER BY zip DESC, streetNumber",
|
||||
expected: &Select{
|
||||
Type: SELECT,
|
||||
Table: Table{Name: "Addresses"},
|
||||
IsWildcard: false,
|
||||
Columns: []Column{
|
||||
@ -89,7 +123,8 @@ func TestParseSelectStatement(t *testing.T) {
|
||||
Name: "id",
|
||||
},
|
||||
{
|
||||
Name: "streetNumber",
|
||||
Name: "streetNumber",
|
||||
Alias: "streetNum",
|
||||
},
|
||||
{
|
||||
Name: "streetName",
|
||||
@ -132,32 +167,58 @@ func TestParseSelectStatement(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
// {
|
||||
// input: "SELECT ProductID, ProductName, CategoryName FROM Products INNER JOIN Categories ON Products.CategoryID = Categories.CategoryID; ",
|
||||
// expected: Select{
|
||||
// Table: "Products",
|
||||
// Columns: []Column{
|
||||
// {Name: "ProductID"},
|
||||
// {Name: "ProductName"},
|
||||
// {Name: "CategoryName"},
|
||||
// },
|
||||
// Joins: []Join{
|
||||
// {
|
||||
// Type: INNER,
|
||||
// Table: Table{
|
||||
// Name: "Categories",
|
||||
// },
|
||||
// Ons: []Conditional{
|
||||
// {
|
||||
// Key: "Products.CategoryID",
|
||||
// Operator: "=",
|
||||
// Value: "Categories.CategoryID",
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
{
|
||||
input: "SELECT ProductID, ProductName, CategoryName FROM Products INNER JOIN Categories ON Products.CategoryID = Categories.CategoryID AND Products.SupplierID = Categories.SupplierID LEFT JOIN Stores ON Products.StoreID = Stores.ID ;",
|
||||
expected: &Select{
|
||||
Type: SELECT,
|
||||
Table: Table{Name: "Products"},
|
||||
Columns: []Column{
|
||||
{Name: "ProductID"},
|
||||
{Name: "ProductName"},
|
||||
{Name: "CategoryName"},
|
||||
},
|
||||
Joins: []Join{
|
||||
{
|
||||
Type: INNER,
|
||||
MainTable: Table{
|
||||
Name: "Products",
|
||||
},
|
||||
JoiningTable: Table{
|
||||
Name: "Categories",
|
||||
},
|
||||
Ons: []Conditional{
|
||||
{
|
||||
Key: "Products.CategoryID",
|
||||
Operator: "=",
|
||||
Value: "Categories.CategoryID",
|
||||
},
|
||||
{
|
||||
Key: "Products.SupplierID",
|
||||
Operator: "=",
|
||||
Value: "Categories.SupplierID",
|
||||
Extension: "AND",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: LEFT,
|
||||
MainTable: Table{
|
||||
Name: "Products",
|
||||
},
|
||||
JoiningTable: Table{
|
||||
Name: "Stores",
|
||||
},
|
||||
Ons: []Conditional{
|
||||
{
|
||||
Key: "Products.StoreID",
|
||||
Operator: "=",
|
||||
Value: "Stores.ID",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, sql := range testSqlStatements {
|
||||
@ -165,74 +226,119 @@ func TestParseSelectStatement(t *testing.T) {
|
||||
expected := sql.expected.(*Select)
|
||||
|
||||
t.Run(testName, func(t *testing.T) {
|
||||
answer := ParseSelectStatement(sql.input)
|
||||
answer, err := Parse(sql.input)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if answer.Table.Alias != expected.Table.Alias {
|
||||
t.Errorf("got %s for Select.Table.Alias, expected %s", answer.Table.Alias, expected.Table.Alias)
|
||||
answerAsSelect := answer.(*Select)
|
||||
|
||||
if answerAsSelect.Type != expected.Type {
|
||||
t.Errorf("got %d for Select.Type, expected %d", answerAsSelect.Type, expected.Type)
|
||||
}
|
||||
if answer.Table.Name != expected.Table.Name {
|
||||
t.Errorf("got %s for Select.Table.Name, expected %s", answer.Table.Name, expected.Table.Name)
|
||||
if answerAsSelect.IsWildcard != expected.IsWildcard {
|
||||
t.Errorf("got %#v for Select.IsWildcard, expected %#v", answerAsSelect.IsWildcard, expected.IsWildcard)
|
||||
}
|
||||
if answer.IsWildcard != expected.IsWildcard {
|
||||
t.Errorf("got %#v for Select.IsWildcard, expected %#v", answer.IsWildcard, expected.IsWildcard)
|
||||
if answerAsSelect.Table.Name != expected.Table.Name {
|
||||
t.Errorf("got %s for Select.Table.Name, expected %s", answerAsSelect.Table.Name, expected.Table.Name)
|
||||
}
|
||||
if len(answer.Columns) != len(expected.Columns) {
|
||||
t.Errorf("got %d number of columns for Select.Columns, expected %d", len(answer.Columns), len(expected.Columns))
|
||||
if answerAsSelect.Table.Alias != expected.Table.Alias {
|
||||
t.Errorf("got %s for Select.Table.Alias, expected %s", answerAsSelect.Table.Alias, expected.Table.Alias)
|
||||
}
|
||||
if answerAsSelect.IsDistinct != expected.IsDistinct {
|
||||
t.Errorf("got %v for Select.IsDistinct, expected %v", answerAsSelect.IsDistinct, expected.IsDistinct)
|
||||
}
|
||||
|
||||
if len(answerAsSelect.Columns) != len(expected.Columns) {
|
||||
t.Errorf("got %d number of columns for Select.Columns, expected %d", len(answerAsSelect.Columns), len(expected.Columns))
|
||||
} else {
|
||||
for i, expectedColumn := range expected.Columns {
|
||||
if expectedColumn.Name != answer.Columns[i].Name {
|
||||
t.Errorf("got %s for Select.Column[%d].Name, expected %s", answer.Columns[i].Name, i, expectedColumn.Name)
|
||||
if expectedColumn.Name != answerAsSelect.Columns[i].Name {
|
||||
t.Errorf("got %s for Select.Column[%d].Name, expected %s", answerAsSelect.Columns[i].Name, i, expectedColumn.Name)
|
||||
}
|
||||
if expectedColumn.Alias != answer.Columns[i].Alias {
|
||||
t.Errorf("got %s for Select.Column[%ORDER].Alias, expected %s", answer.Columns[i].Alias, i, expectedColumn.Alias)
|
||||
if expectedColumn.Alias != answerAsSelect.Columns[i].Alias {
|
||||
t.Errorf("got %s for Select.Column[%d].Alias, expected %s", answerAsSelect.Columns[i].Alias, i, expectedColumn.Alias)
|
||||
}
|
||||
if expectedColumn.AggregateFunction != answer.Columns[i].AggregateFunction {
|
||||
t.Errorf("got %d for Select.Column[%d].AggregateFunction, expected %d", answer.Columns[i].AggregateFunction, i, expectedColumn.AggregateFunction)
|
||||
if expectedColumn.AggregateFunction != answerAsSelect.Columns[i].AggregateFunction {
|
||||
t.Errorf("got %d for Select.Column[%d].AggregateFunction, expected %d", answerAsSelect.Columns[i].AggregateFunction, i, expectedColumn.AggregateFunction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(answer.Conditionals) != len(expected.Conditionals) {
|
||||
t.Errorf("got %d number of conditionals for Select.Conditionals, expected %d", len(answer.Conditionals), len(expected.Conditionals))
|
||||
if len(answerAsSelect.Conditionals) != len(expected.Conditionals) {
|
||||
t.Errorf("got %d number of conditionals for Select.Conditionals, expected %d", len(answerAsSelect.Conditionals), len(expected.Conditionals))
|
||||
} else {
|
||||
for i, expectedCondition := range expected.Conditionals {
|
||||
if expectedCondition.Key != answer.Conditionals[i].Key {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Key, expected %s", answer.Conditionals[i].Key, i, expectedCondition.Key)
|
||||
if expectedCondition.Key != answerAsSelect.Conditionals[i].Key {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Key, expected %s", answerAsSelect.Conditionals[i].Key, i, expectedCondition.Key)
|
||||
}
|
||||
if expectedCondition.Operator != answer.Conditionals[i].Operator {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Operator, expected %s", answer.Conditionals[i].Operator, i, expectedCondition.Operator)
|
||||
if expectedCondition.Operator != answerAsSelect.Conditionals[i].Operator {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Operator, expected %s", answerAsSelect.Conditionals[i].Operator, i, expectedCondition.Operator)
|
||||
}
|
||||
if expectedCondition.Value != answer.Conditionals[i].Value {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Value, expected %s", answer.Conditionals[i].Value, i, expectedCondition.Value)
|
||||
if expectedCondition.Value != answerAsSelect.Conditionals[i].Value {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Value, expected %s", answerAsSelect.Conditionals[i].Value, i, expectedCondition.Value)
|
||||
}
|
||||
if expectedCondition.Extension != answer.Conditionals[i].Extension {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Extension, expected %s", answer.Conditionals[i].Extension, i, expectedCondition.Extension)
|
||||
if expectedCondition.Extension != answerAsSelect.Conditionals[i].Extension {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Extension, expected %s", answerAsSelect.Conditionals[i].Extension, i, expectedCondition.Extension)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
if len(answerAsSelect.OrderBys) != len(expected.OrderBys) {
|
||||
t.Errorf("got %d number of orderBys for Select.OrderBys, expected %d", len(answerAsSelect.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.Key != answerAsSelect.OrderBys[i].Key {
|
||||
t.Errorf("got %s for Select.OrderBys[%d].Key, expected %s", answerAsSelect.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)
|
||||
if expectedOrderBy.IsDescend != answerAsSelect.OrderBys[i].IsDescend {
|
||||
t.Errorf("got %#v for Select.OrderBys[%d].IsDescend, expected %#v", answerAsSelect.OrderBys[i].IsDescend, i, expectedOrderBy.IsDescend)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(answer.Joins) != len(expected.Joins) {
|
||||
t.Errorf("got %d number of joins for Select.Joinss, expected %d", len(answer.Joins), len(expected.Joins))
|
||||
if len(answerAsSelect.Joins) != len(expected.Joins) {
|
||||
t.Errorf("got %d number of joins for Select.Joinss, expected %d", len(answerAsSelect.Joins), len(expected.Joins))
|
||||
} else {
|
||||
for i, expectedJoin := range expected.Joins {
|
||||
t.Errorf("got %d for Select.Joins[%d].Type, expected %d", answer.Joins[i].Type, i, expectedJoin.Type)
|
||||
if answerAsSelect.Joins[i].Type != expectedJoin.Type {
|
||||
t.Errorf("got %d for Select.Joins[%d].Type, expected %d", answerAsSelect.Joins[i].Type, i, expectedJoin.Type)
|
||||
}
|
||||
if answerAsSelect.Joins[i].MainTable.Name != expectedJoin.MainTable.Name {
|
||||
t.Errorf("got %s for Select.Joins[%d].MainTable.Name, expected %s", answerAsSelect.Joins[i].MainTable.Name, i, expectedJoin.MainTable.Name)
|
||||
}
|
||||
if answerAsSelect.Joins[i].MainTable.Alias != expectedJoin.MainTable.Alias {
|
||||
t.Errorf("got %s for Select.Joins[%d].MainTable.Alias, expected %s", answerAsSelect.Joins[i].MainTable.Alias, i, expectedJoin.MainTable.Alias)
|
||||
}
|
||||
if answerAsSelect.Joins[i].JoiningTable.Name != expectedJoin.JoiningTable.Name {
|
||||
t.Errorf("got %s for Select.Joins[%d].JoiningTable.Name, expected %s", answerAsSelect.Joins[i].JoiningTable.Name, i, expectedJoin.JoiningTable.Name)
|
||||
}
|
||||
if answerAsSelect.Joins[i].JoiningTable.Alias != expectedJoin.JoiningTable.Alias {
|
||||
t.Errorf("got %s for Select.Joins[%d].JoiningTable.Alias, expected %s", answerAsSelect.Joins[i].JoiningTable.Alias, i, expectedJoin.JoiningTable.Alias)
|
||||
}
|
||||
|
||||
if len(answerAsSelect.Joins[i].Ons) != len(expectedJoin.Ons) {
|
||||
t.Errorf("got %d number of ons for Select.Joins.Ons, expected %d", len(answerAsSelect.Joins[i].Ons), len(expectedJoin.Ons))
|
||||
} else {
|
||||
for on_i, expectedCondition := range expected.Joins[i].Ons {
|
||||
if expectedCondition.Key != answerAsSelect.Joins[i].Ons[on_i].Key {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Key, expected %s", answerAsSelect.Joins[i].Ons[on_i].Key, on_i, expectedCondition.Key)
|
||||
}
|
||||
if expectedCondition.Operator != answerAsSelect.Joins[i].Ons[on_i].Operator {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Operator, expected %s", answerAsSelect.Joins[i].Ons[on_i].Operator, on_i, expectedCondition.Operator)
|
||||
}
|
||||
if expectedCondition.Value != answerAsSelect.Joins[i].Ons[on_i].Value {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Value, expected %s", answerAsSelect.Joins[i].Ons[on_i].Value, on_i, expectedCondition.Value)
|
||||
}
|
||||
if expectedCondition.Extension != answerAsSelect.Joins[i].Ons[on_i].Extension {
|
||||
t.Errorf("got %s for Select.Conditionals[%d].Extension, expected %s", answerAsSelect.Joins[i].Ons[on_i].Extension, on_i, expectedCondition.Extension)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user