refact: moved select stuff to seperate file

This commit is contained in:
Yehoshua Sandler 2025-05-13 18:46:02 -05:00
parent 325f660a04
commit 50bfba4c09
6 changed files with 529 additions and 958 deletions

View File

@ -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{}{}
}()

View File

@ -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
}

View File

@ -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
}
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
if column.AggregateFunction > 0 {
workingSlice = fmt.Sprintf(
"%s(%s)",
AggregateFunctionTypeString(column.AggregateFunction),
column.Name,
)
} 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{}
}
}
workingSlice = column.Name
}
selectQuery.Columns = columns
return nil
if column.Alias != "" {
workingSlice += fmt.Sprintf(" AS %s", column.Alias)
}
func parseSelectTable(p *parser) error {
selectQuery := p.query.(*Select)
return workingSlice
_, 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
func (q *Select) GetFullSql() string {
var workingSqlSlice []string
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
workingSqlSlice = append(workingSqlSlice, "SELECT")
if q.IsWildcard {
workingSqlSlice = append(workingSqlSlice, "*")
} 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
for i, column := range q.Columns {
if i < (len(q.Columns) - 1) {
workingSqlSlice = append(workingSqlSlice, GetFullStringFromColumn(column)+",")
} else {
endRangeIndex = foundJoinKeywords[i+1].Index
workingSqlSlice = append(workingSqlSlice, GetFullStringFromColumn(column))
}
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
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, " ")
//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
return fullSql
}

View File

@ -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)
}
}
}
}
}
})
}
}

View File

@ -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
}
}
tableErr := parseSelectTable(p)
if tableErr != nil {
return tableErr
}
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)
conditionalsErr := parseSelectConditionals(p)
if conditionalsErr != nil {
return conditionalsErr
}
fullSql := strings.Join(workingSqlSlice, " ")
return fullSql
ordersByErr := parseOrderBys(p)
if ordersByErr != nil {
return ordersByErr
}
func ParseSelectStatement(sql string) Select {
query := Select{}
parseJoins(p)
passedSELECT := false
passedColumns := false
passedFROM := false
passedTable := false
passedWHERE := false
passedConditionals := false
passedOrderByKeywords := false
passesOrderByColumns := false
return nil
}
lookBehindBuffer := [10]sqllexer.Token{}
var workingConditional = Conditional{}
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)
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
continue
}
for i := startRange; i <= endRange; i++ {
token := p.tokens[i]
// 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)
if token.Type == sqllexer.FUNCTION {
unshiftBuffer(&lookBehindBuffer, token)
workingColumn.AggregateFunction = AggregateFunctionTypeByName(token.Value)
continue
} else if token.Type == sqllexer.PUNCTUATION {
if token.Value == "," {
} else if token.Type == sqllexer.PUNCTUATION && token.Value == "," {
columns = append(columns, workingColumn)
workingColumn = Column{}
}
continue
} else if token.Type == sqllexer.IDENT {
unshiftBuffer(&lookBehindBuffer, *token)
unshiftBuffer(&lookBehindBuffer, token)
if lookBehindBuffer[0].Type == sqllexer.ALIAS_INDICATOR {
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)
unshiftBuffer(&lookBehindBuffer, token)
continue
} else if token.Type == sqllexer.SPACE {
continue
} else {
} else if i == endRange {
if workingColumn.Name != "" {
columns = append(columns, workingColumn)
workingColumn = Column{}
}
passedColumns = true
query.Columns = columns
}
}
// TODO: make sure to check for other keywords that are allowed
if !passedFROM && strings.ToUpper(token.Value) == "FROM" {
passedFROM = true
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
}
if !passedTable && token.Type == sqllexer.IDENT {
passedTable = true
query.Table.Name = token.Value
} 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 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
} else {
break
}
}
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)
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 != "" {
query.Conditionals = append(query.Conditionals, workingConditional)
selectQuery.Conditionals = append(selectQuery.Conditionals, workingConditional)
workingConditional = Conditional{}
}
if IsTokenEndOfStatement(token) {
break
}
continue
return nil
}
// Checking For ORDER BY
if passedConditionals && !passedOrderByKeywords && token.Type == sqllexer.KEYWORD {
unshiftBuffer(&lookBehindBuffer, *token)
func parseOrderBys(p *parser) error {
selectQuery := p.query.(*Select)
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 strings.ToUpper(directionKeyword) == "DESC" {
isDescend = true
}
orderBys = append(orderBys, OrderBy{
Key: orderByColumnName,
IsDescend: isDescend,
_, byKeywordIndex := p.findToken(func(t Token) bool {
return t.Type == sqllexer.KEYWORD && strings.ToUpper(t.Value) == "BY"
})
if IsTokenEndOfStatement(token) {
query.OrderBys = orderBys
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
}
}
query.OrderBys = orderBys
//var ons []Conditional
var workingOn Conditional
if IsTokenEndOfStatement(token) {
_, 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)
}
}
return query
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
}

View File

@ -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{
@ -90,6 +124,7 @@ func TestParseSelectStatement(t *testing.T) {
},
{
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)
}
}
}
}
}
})
}
}