diff --git a/main.go b/main.go index cf039a0..561b85f 100644 --- a/main.go +++ b/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{}{} }() diff --git a/q/dto.go b/q/dto.go index 7e781e3..1aee6d8 100644 --- a/q/dto.go +++ b/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 } diff --git a/q/parse.go b/q/parse.go index aab700f..0586cf6 100644 --- a/q/parse.go +++ b/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 } diff --git a/q/parse_test.go b/q/parse_test.go deleted file mode 100644 index 98fb027..0000000 --- a/q/parse_test.go +++ /dev/null @@ -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) - } - } - - } - } - } - }) - } -} diff --git a/q/select.go b/q/select.go index 2ff58e5..a8d243e 100644 --- a/q/select.go +++ b/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 } diff --git a/q/select_test.go b/q/select_test.go index a685aa6..98fb027 100644 --- a/q/select_test.go +++ b/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) + } + } + + } } - } - }) } }