From dd7cabf89127508c44792d4cc89c596fd14be2b8 Mon Sep 17 00:00:00 2001 From: ixior462 Date: Wed, 26 Feb 2025 02:33:31 +0100 Subject: [PATCH 1/7] Upgrade go version and fix issues from Go Report Card --- .github/workflows/end2end-tests.yml | 2 +- .github/workflows/unit-tests.yml | 2 +- engine/engine.go | 118 ++++----- engine/engine_error_handling_test.go | 2 +- go.mod | 2 +- parser/parser.go | 363 +++++++++++++++------------ parser/parser_test.go | 2 +- 7 files changed, 259 insertions(+), 232 deletions(-) diff --git a/.github/workflows/end2end-tests.yml b/.github/workflows/end2end-tests.yml index 8f6f0c9..0079f03 100644 --- a/.github/workflows/end2end-tests.yml +++ b/.github/workflows/end2end-tests.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go: [ '1.21.13', '1.22.7', '1.23.1' ] + go: [ '1.23.6', '1.24.0' ] steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 835a56b..82e6a3c 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go: [ '1.21.13', '1.22.7', '1.23.1' ] + go: [ '1.23.6', '1.24.0' ] steps: - uses: actions/checkout@v3 diff --git a/engine/engine.go b/engine/engine.go index 4ddf188..56cea17 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -28,8 +28,11 @@ func (engine *DbEngine) Evaluate(sequences *ast.Sequence) (string, error) { commands := sequences.Commands result := "" + var err error for _, command := range commands { - + if err != nil { + return "", err + } switch mappedCommand := command.(type) { case *ast.WhereCommand: continue @@ -42,33 +45,22 @@ func (engine *DbEngine) Evaluate(sequences *ast.Sequence) (string, error) { case *ast.JoinCommand: continue case *ast.CreateCommand: - err := engine.createTable(mappedCommand) - if err != nil { - return "", err - } + err = engine.createTable(mappedCommand) result += "Table '" + mappedCommand.Name.GetToken().Literal + "' has been created\n" continue case *ast.InsertCommand: - err := engine.insertIntoTable(mappedCommand) - if err != nil { - return "", err - } + err = engine.insertIntoTable(mappedCommand) result += "Data Inserted\n" continue case *ast.SelectCommand: - selectOutput, err := engine.getSelectResponse(mappedCommand) - if err != nil { - return "", err - } + var selectOutput *Table + selectOutput, err = engine.getSelectResponse(mappedCommand) result += selectOutput.ToString() + "\n" continue case *ast.DeleteCommand: deleteCommand := command.(*ast.DeleteCommand) if deleteCommand.HasWhereCommand() { - err := engine.deleteFromTable(mappedCommand, deleteCommand.WhereCommand) - if err != nil { - return "", err - } + err = engine.deleteFromTable(mappedCommand, deleteCommand.WhereCommand) } result += "Data from '" + mappedCommand.Name.GetToken().Literal + "' has been deleted\n" continue @@ -77,18 +69,14 @@ func (engine *DbEngine) Evaluate(sequences *ast.Sequence) (string, error) { result += "Table: '" + mappedCommand.Name.GetToken().Literal + "' has been dropped\n" continue case *ast.UpdateCommand: - err := engine.updateTable(mappedCommand) - if err != nil { - return "", err - } + err = engine.updateTable(mappedCommand) result += "Table: '" + mappedCommand.Name.GetToken().Literal + "' has been updated\n" continue default: return "", &UnsupportedCommandTypeFromParserError{variable: fmt.Sprintf("%s", command)} } } - - return result, nil + return result, err } // getSelectResponse - Returns Select response basing on ast.OrderByCommand and ast.WhereCommand included in this Select @@ -322,63 +310,67 @@ func evaluateColumnTypeOfAggregateFunc(space ast.Space) token.Token { func aggregateColumnContent(space ast.Space, columnValues []ValueInterface) (ValueInterface, error) { if space.AggregateFunc.Type == token.COUNT { - if space.ColumnName.Type == token.ASTERISK { - return IntegerValue{Value: len(columnValues)}, nil - } - count := 0 - for _, value := range columnValues { - if value.GetType() != NullType { - count++ - } - } - return IntegerValue{Value: count}, nil + return getCount(space, columnValues) } if len(columnValues) == 0 { return NullValue{}, nil } switch space.AggregateFunc.Type { case token.MAX: - maxValue, err := getMax(columnValues) - if err != nil { - return nil, err - } - return maxValue, nil + return getMax(columnValues) case token.MIN: - minValue, err := getMin(columnValues) - if err != nil { - return nil, err - } - return minValue, nil + return getMin(columnValues) case token.SUM: - if columnValues[0].GetType() == StringType { - return IntegerValue{Value: 0}, nil - } else { - sum := 0 - for _, value := range columnValues { - if value.GetType() != NullType { - num, err := strconv.Atoi(value.ToString()) - if err != nil { - return nil, err - } - sum += num - } + return getSum(columnValues) + default: + return getAvg(columnValues) + } +} + +func getCount(space ast.Space, columnValues []ValueInterface) (ValueInterface, error) { + if space.ColumnName.Type == token.ASTERISK { + return IntegerValue{Value: len(columnValues)}, nil + } + count := 0 + for _, value := range columnValues { + if value.GetType() != NullType { + count++ + } + } + return IntegerValue{Value: count}, nil +} + +func getAvg(columnValues []ValueInterface) (ValueInterface, error) { + if columnValues[0].GetType() == StringType { + return IntegerValue{Value: 0}, nil + } else { + sum := 0 + for _, value := range columnValues { + num, err := strconv.Atoi(value.ToString()) + if err != nil { + return nil, err } - return IntegerValue{Value: sum}, nil + sum += num } - default: - if columnValues[0].GetType() == StringType { - return IntegerValue{Value: 0}, nil - } else { - sum := 0 - for _, value := range columnValues { + return IntegerValue{Value: sum / len(columnValues)}, nil + } +} + +func getSum(columnValues []ValueInterface) (ValueInterface, error) { + if columnValues[0].GetType() == StringType { + return IntegerValue{Value: 0}, nil + } else { + sum := 0 + for _, value := range columnValues { + if value.GetType() != NullType { num, err := strconv.Atoi(value.ToString()) if err != nil { return nil, err } sum += num } - return IntegerValue{Value: sum / len(columnValues)}, nil } + return IntegerValue{Value: sum}, nil } } diff --git a/engine/engine_error_handling_test.go b/engine/engine_error_handling_test.go index f40f659..9c7f88b 100644 --- a/engine/engine_error_handling_test.go +++ b/engine/engine_error_handling_test.go @@ -118,7 +118,7 @@ func getErrorMessage(t *testing.T, input string, testIndex int) string { parserInstance := parser.New(lexerInstance) sequences, parserError := parserInstance.ParseSequence() if parserError != nil { - t.Fatalf("[%d] Error has occured in parser not in engine, error: %s", testIndex, parserError.Error()) + t.Fatalf("[%d] Error has occurred in parser not in engine, error: %s", testIndex, parserError.Error()) } engine := New() diff --git a/go.mod b/go.mod index aed510b..d6e4689 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/LissaGreense/GO4SQL -go 1.21 +go 1.23 diff --git a/parser/parser.go b/parser/parser.go index 3cc7e0e..e600b4a 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -263,44 +263,9 @@ func (parser *Parser) parseSelectCommand() (ast.Command, error) { selectCommand.Space = append(selectCommand.Space, ast.Space{ColumnName: parser.currentToken}) parser.nextToken() } else { - for parser.currentToken.Type == token.IDENT || isAggregateFunction(parser.currentToken.Type) { - if parser.currentToken.Type != token.IDENT { - aggregateFunction := parser.currentToken - parser.nextToken() - err := validateTokenAndSkip(parser, []token.Type{token.LPAREN}) - if err != nil { - return nil, err - } - if aggregateFunction.Type == token.COUNT { - err = validateToken(parser.currentToken.Type, []token.Type{token.IDENT, token.ASTERISK}) - } else { - err = validateToken(parser.currentToken.Type, []token.Type{token.IDENT}) - } - if err != nil { - return nil, err - } - selectCommand.Space = append(selectCommand.Space, ast.Space{ColumnName: parser.currentToken, AggregateFunc: &aggregateFunction}) - parser.nextToken() - - err = validateTokenAndSkip(parser, []token.Type{token.RPAREN}) - if err != nil { - return nil, err - } - } else { - // Get column name - err = validateToken(parser.currentToken.Type, []token.Type{token.IDENT}) - if err != nil { - return nil, err - } - selectCommand.Space = append(selectCommand.Space, ast.Space{ColumnName: parser.currentToken}) - parser.nextToken() - } - - if parser.currentToken.Type != token.COMMA { - break - } - // Ignore token.COMMA - parser.nextToken() + command, err := parser.parseSelectSpace(selectCommand) + if err != nil { + return command, err } } @@ -331,6 +296,54 @@ func (parser *Parser) parseSelectCommand() (ast.Command, error) { return selectCommand, nil } +func (parser *Parser) parseSelectSpace(selectCommand *ast.SelectCommand) (ast.Command, error) { + for parser.currentToken.Type == token.IDENT || isAggregateFunction(parser.currentToken.Type) { + if parser.currentToken.Type != token.IDENT { + err := parser.parseAggregateFunction(selectCommand) + if err != nil { + return nil, err + } + } else { + // Get column name + err := validateToken(parser.currentToken.Type, []token.Type{token.IDENT}) + if err != nil { + return nil, err + } + selectCommand.Space = append(selectCommand.Space, ast.Space{ColumnName: parser.currentToken}) + parser.nextToken() + } + + if parser.currentToken.Type != token.COMMA { + break + } + // Ignore token.COMMA + parser.nextToken() + } + return nil, nil +} + +func (parser *Parser) parseAggregateFunction(selectCommand *ast.SelectCommand) error { + aggregateFunction := parser.currentToken + parser.nextToken() + err := validateTokenAndSkip(parser, []token.Type{token.LPAREN}) + if err != nil { + return err + } + if aggregateFunction.Type == token.COUNT { + err = validateToken(parser.currentToken.Type, []token.Type{token.IDENT, token.ASTERISK}) + } else { + err = validateToken(parser.currentToken.Type, []token.Type{token.IDENT}) + } + if err != nil { + return err + } + selectCommand.Space = append(selectCommand.Space, ast.Space{ColumnName: parser.currentToken, AggregateFunc: &aggregateFunction}) + parser.nextToken() + + err = validateTokenAndSkip(parser, []token.Type{token.RPAREN}) + return err +} + func (parser *Parser) getColumnName(err error, selectCommand *ast.SelectCommand, aggregateFunction token.Token) error { // Get column name err = validateToken(parser.currentToken.Type, []token.Type{token.IDENT, token.ASTERISK}) @@ -353,17 +366,16 @@ func isAggregateFunction(t token.Type) bool { func (parser *Parser) parseWhereCommand() (ast.Command, error) { // token.WHERE already at current position in parser whereCommand := &ast.WhereCommand{Token: parser.currentToken} - expressionIsValid := false // Ignore token.WHERE parser.nextToken() var err error - expressionIsValid, whereCommand.Expression, err = parser.getExpression() + whereCommand.Expression, err = parser.getExpression() if err != nil { return nil, err } - if !expressionIsValid { + if whereCommand.Expression == nil { return nil, &LogicalExpressionParsingError{} } @@ -601,13 +613,12 @@ func (parser *Parser) parseJoinCommand() (ast.Command, error) { return nil, err } - var expressionIsValid bool - expressionIsValid, joinCommand.Expression, err = parser.getExpression() + joinCommand.Expression, err = parser.getExpression() if err != nil { return nil, err } - if !expressionIsValid { + if joinCommand.Expression == nil { return nil, &LogicalExpressionParsingError{} } @@ -706,7 +717,7 @@ func (parser *Parser) parseUpdateCommand() (ast.Command, error) { // - ast.BooleanExpression // - ast.ConditionExpression // - ast.ContainExpression -func (parser *Parser) getExpression() (bool, ast.Expression, error) { +func (parser *Parser) getExpression() (ast.Expression, error) { if parser.currentToken.Type == token.IDENT || parser.currentToken.Type == token.LITERAL || @@ -717,39 +728,39 @@ func (parser *Parser) getExpression() (bool, ast.Expression, error) { leftSide, isAnonymitifier, err := parser.getExpressionLeftSideValue() if err != nil { - return false, nil, err + return nil, err } - isValidExpression := false - var expression ast.Expression - - if parser.currentToken.Type == token.EQUAL || parser.currentToken.Type == token.NOT { - isValidExpression, expression, err = parser.getConditionalExpression(leftSide, isAnonymitifier) - } else if parser.currentToken.Type == token.IN || parser.currentToken.Type == token.NOTIN { - isValidExpression, expression, err = parser.getContainExpression(leftSide, isAnonymitifier) - } else if leftSide.Type == token.TRUE || leftSide.Type == token.FALSE { - expression = &ast.BooleanExpression{Boolean: leftSide} - isValidExpression = true - err = nil - } + expression, err := parser.getExpressionLeaf(leftSide, isAnonymitifier) if err != nil { - return false, nil, err + return nil, err } - if (parser.currentToken.Type == token.AND || parser.currentToken.Type == token.OR) && isValidExpression { - isValidExpression, expression, err = parser.getOperationExpression(expression) + if (parser.currentToken.Type == token.AND || parser.currentToken.Type == token.OR) && expression != nil { + expression, err = parser.getOperationExpression(expression) } if err != nil { - return false, nil, err + return nil, err } - if isValidExpression { - return true, expression, nil + if expression != nil { + return expression, nil } } - return false, nil, nil + return nil, nil +} + +func (parser *Parser) getExpressionLeaf(leftSide token.Token, isAnonymitifier bool) (ast.Expression, error) { + if parser.currentToken.Type == token.EQUAL || parser.currentToken.Type == token.NOT { + return parser.getConditionalExpression(leftSide, isAnonymitifier) + } else if parser.currentToken.Type == token.IN || parser.currentToken.Type == token.NOTIN { + return parser.getContainExpression(leftSide, isAnonymitifier) + } else if leftSide.Type == token.TRUE || leftSide.Type == token.FALSE { + return &ast.BooleanExpression{Boolean: leftSide}, nil + } + return nil, nil } func (parser *Parser) getExpressionLeftSideValue() (token.Token, bool, error) { @@ -782,30 +793,30 @@ func (parser *Parser) getExpressionLeftSideValue() (token.Token, bool, error) { } // getOperationExpression - Return ast.OperationExpression created from tokens and validate the syntax -func (parser *Parser) getOperationExpression(expression ast.Expression) (bool, *ast.OperationExpression, error) { +func (parser *Parser) getOperationExpression(expression ast.Expression) (*ast.OperationExpression, error) { operationExpression := &ast.OperationExpression{} operationExpression.Left = expression operationExpression.Operation = parser.currentToken parser.nextToken() - expressionIsValid, expression, err := parser.getExpression() + expression, err := parser.getExpression() if err != nil { - return false, nil, err + return nil, err } - if !expressionIsValid { - return false, nil, &LogicalExpressionParsingError{afterToken: &operationExpression.Operation.Literal} + if expression == nil { + return nil, &LogicalExpressionParsingError{afterToken: &operationExpression.Operation.Literal} } operationExpression.Right = expression - return true, operationExpression, nil + return operationExpression, nil } // getConditionalExpression - Return ast.ConditionExpression created from tokens and validate the syntax -func (parser *Parser) getConditionalExpression(leftSide token.Token, isAnonymitifier bool) (bool, *ast.ConditionExpression, error) { +func (parser *Parser) getConditionalExpression(leftSide token.Token, isAnonymitifier bool) (*ast.ConditionExpression, error) { conditionalExpression := &ast.ConditionExpression{Condition: parser.currentToken} if isAnonymitifier { @@ -831,21 +842,21 @@ func (parser *Parser) getConditionalExpression(leftSide token.Token, isAnonymiti finishedWithApostrophe := parser.skipIfCurrentTokenIsApostrophe() err := validateApostropheWrapping(startedWithApostrophe, finishedWithApostrophe, conditionalExpression.Right.GetToken()) if err != nil { - return false, nil, err + return nil, err } } else { - return false, conditionalExpression, &SyntaxError{expecting: []string{token.APOSTROPHE, token.IDENT, token.LITERAL, token.NULL}, got: parser.currentToken.Literal} + return nil, &SyntaxError{expecting: []string{token.APOSTROPHE, token.IDENT, token.LITERAL, token.NULL}, got: parser.currentToken.Literal} } - return true, conditionalExpression, nil + return conditionalExpression, nil } // getContainExpression - Return ast.ContainExpression created from tokens and validate the syntax -func (parser *Parser) getContainExpression(leftSide token.Token, isAnonymitifier bool) (bool, *ast.ContainExpression, error) { +func (parser *Parser) getContainExpression(leftSide token.Token, isAnonymitifier bool) (*ast.ContainExpression, error) { containExpression := &ast.ContainExpression{} if isAnonymitifier { - return false, nil, &SyntaxError{expecting: []string{token.IDENT}, got: "'" + leftSide.Literal + "'"} + return nil, &SyntaxError{expecting: []string{token.IDENT}, got: "'" + leftSide.Literal + "'"} } containExpression.Left = ast.Identifier{Token: leftSide} @@ -861,7 +872,7 @@ func (parser *Parser) getContainExpression(leftSide token.Token, isAnonymitifier err := validateTokenAndSkip(parser, []token.Type{token.LPAREN}) if err != nil { - return false, nil, err + return nil, err } for parser.currentToken.Type == token.IDENT || parser.currentToken.Type == token.LITERAL || parser.currentToken.Type == token.NULL || parser.currentToken.Type == token.APOSTROPHE { @@ -869,7 +880,7 @@ func (parser *Parser) getContainExpression(leftSide token.Token, isAnonymitifier err = validateToken(parser.currentToken.Type, []token.Type{token.IDENT, token.LITERAL, token.NULL}) if err != nil { - return false, nil, err + return nil, err } currentAnonymitifier := ast.Anonymitifier{Token: parser.currentToken} containExpression.Right = append(containExpression.Right, currentAnonymitifier) @@ -880,12 +891,12 @@ func (parser *Parser) getContainExpression(leftSide token.Token, isAnonymitifier err = validateApostropheWrapping(startedWithApostrophe, finishedWithApostrophe, currentAnonymitifier.GetToken()) if err != nil { - return false, nil, err + return nil, err } if parser.currentToken.Type != token.COMMA { if parser.currentToken.Type != token.RPAREN { - return false, nil, &SyntaxError{expecting: []string{token.COMMA, token.RPAREN}, got: string(parser.currentToken.Type)} + return nil, &SyntaxError{expecting: []string{token.COMMA, token.RPAREN}, got: string(parser.currentToken.Type)} } break } @@ -896,10 +907,10 @@ func (parser *Parser) getContainExpression(leftSide token.Token, isAnonymitifier err = validateTokenAndSkip(parser, []token.Type{token.RPAREN}) if err != nil { - return false, nil, err + return nil, err } - return true, containExpression, err + return containExpression, err } // ParseSequence - Return ast.Sequence (sequence of commands) created from client input after tokenization @@ -926,90 +937,15 @@ func (parser *Parser) ParseSequence() (*ast.Sequence, error) { case token.DROP: command, err = parser.parseDropCommand() case token.WHERE: - lastCommand, parserError := parser.getLastCommand(sequence, token.WHERE) - if parserError != nil { - return nil, parserError - } - - if lastCommand.TokenLiteral() == token.SELECT { - newCommand, err := parser.parseWhereCommand() - if err != nil { - return nil, err - } - lastCommand.(*ast.SelectCommand).WhereCommand = newCommand.(*ast.WhereCommand) - } else if lastCommand.TokenLiteral() == token.DELETE { - newCommand, err := parser.parseWhereCommand() - if err != nil { - return nil, err - } - lastCommand.(*ast.DeleteCommand).WhereCommand = newCommand.(*ast.WhereCommand) - } else if lastCommand.TokenLiteral() == token.UPDATE { - newCommand, err := parser.parseWhereCommand() - if err != nil { - return nil, err - } - lastCommand.(*ast.UpdateCommand).WhereCommand = newCommand.(*ast.WhereCommand) - } else { - return nil, &SyntaxCommandExpectedError{command: "WHERE", neededCommands: []string{"SELECT", "DELETE", "UPDATE"}} - } + err = parser.updateLastCommandWithWhereConstraints(sequence) case token.ORDER: - lastCommand, parserError := parser.getLastCommand(sequence, token.ORDER) - if parserError != nil { - return nil, parserError - } - - if lastCommand.TokenLiteral() != token.SELECT { - return nil, &SyntaxCommandExpectedError{command: "ORDER BY", neededCommands: []string{"SELECT"}} - } - - selectCommand := lastCommand.(*ast.SelectCommand) - newCommand, err := parser.parseOrderByCommand() - if err != nil { - return nil, err - } - selectCommand.OrderByCommand = newCommand.(*ast.OrderByCommand) + err = parser.updateSelectCommandWithOrderByConstraints(sequence) case token.LIMIT: - lastCommand, parserError := parser.getLastCommand(sequence, token.LIMIT) - if parserError != nil { - return nil, parserError - } - if lastCommand.TokenLiteral() != token.SELECT { - return nil, &SyntaxCommandExpectedError{command: "LIMIT", neededCommands: []string{"SELECT"}} - } - selectCommand := lastCommand.(*ast.SelectCommand) - newCommand, err := parser.parseLimitCommand() - if err != nil { - return nil, err - } - selectCommand.LimitCommand = newCommand.(*ast.LimitCommand) + err = parser.updateSelectCommandWithLimitConstraints(sequence) case token.OFFSET: - lastCommand, parserError := parser.getLastCommand(sequence, token.OFFSET) - if parserError != nil { - return nil, parserError - } - if lastCommand.TokenLiteral() != token.SELECT { - return nil, &SyntaxCommandExpectedError{command: "OFFSET", neededCommands: []string{"SELECT"}} - } - selectCommand := lastCommand.(*ast.SelectCommand) - newCommand, err := parser.parseOffsetCommand() - if err != nil { - return nil, err - } - selectCommand.OffsetCommand = newCommand.(*ast.OffsetCommand) + err = parser.updateSelectCommandWithOffsetConstraints(sequence) case token.JOIN, token.LEFT, token.RIGHT, token.INNER, token.FULL: - lastCommand, parserError := parser.getLastCommand(sequence, token.JOIN) - if parserError != nil { - return nil, parserError - } - if lastCommand.TokenLiteral() != token.SELECT { - return nil, &SyntaxCommandExpectedError{command: "JOIN", neededCommands: []string{"SELECT"}} - } - selectCommand := lastCommand.(*ast.SelectCommand) - newCommand, err := parser.parseJoinCommand() - if err != nil { - return nil, err - } - selectCommand.JoinCommand = newCommand.(*ast.JoinCommand) + err = parser.updateSelectCommandWithJoinConstraints(sequence) default: return nil, &SyntaxInvalidCommandError{invalidCommand: parser.currentToken.Literal} } @@ -1023,10 +959,109 @@ func (parser *Parser) ParseSequence() (*ast.Sequence, error) { sequence.Commands = append(sequence.Commands, command) } } - return sequence, nil } +func (parser *Parser) updateSelectCommandWithJoinConstraints(sequence *ast.Sequence) error { + lastCommand, parserError := parser.getLastCommand(sequence, token.JOIN) + if parserError != nil { + return parserError + } + if lastCommand.TokenLiteral() != token.SELECT { + return &SyntaxCommandExpectedError{command: "JOIN", neededCommands: []string{"SELECT"}} + } + selectCommand := lastCommand.(*ast.SelectCommand) + newCommand, err := parser.parseJoinCommand() + if err != nil { + return err + } + selectCommand.JoinCommand = newCommand.(*ast.JoinCommand) + return nil +} + +func (parser *Parser) updateSelectCommandWithOffsetConstraints(sequence *ast.Sequence) error { + lastCommand, parserError := parser.getLastCommand(sequence, token.OFFSET) + if parserError != nil { + return parserError + } + if lastCommand.TokenLiteral() != token.SELECT { + return &SyntaxCommandExpectedError{command: "OFFSET", neededCommands: []string{"SELECT"}} + } + selectCommand := lastCommand.(*ast.SelectCommand) + newCommand, err := parser.parseOffsetCommand() + if err != nil { + return err + } + selectCommand.OffsetCommand = newCommand.(*ast.OffsetCommand) + return nil +} + +func (parser *Parser) updateSelectCommandWithLimitConstraints(sequence *ast.Sequence) error { + lastCommand, parserError := parser.getLastCommand(sequence, token.LIMIT) + if parserError != nil { + return parserError + } + if lastCommand.TokenLiteral() != token.SELECT { + return &SyntaxCommandExpectedError{command: "LIMIT", neededCommands: []string{"SELECT"}} + } + selectCommand := lastCommand.(*ast.SelectCommand) + newCommand, err := parser.parseLimitCommand() + if err != nil { + return err + } + selectCommand.LimitCommand = newCommand.(*ast.LimitCommand) + return nil +} + +func (parser *Parser) updateSelectCommandWithOrderByConstraints(sequence *ast.Sequence) error { + lastCommand, parserError := parser.getLastCommand(sequence, token.ORDER) + if parserError != nil { + return parserError + } + + if lastCommand.TokenLiteral() != token.SELECT { + return &SyntaxCommandExpectedError{command: "ORDER BY", neededCommands: []string{"SELECT"}} + } + + selectCommand := lastCommand.(*ast.SelectCommand) + newCommand, err := parser.parseOrderByCommand() + if err != nil { + return err + } + selectCommand.OrderByCommand = newCommand.(*ast.OrderByCommand) + return nil +} + +func (parser *Parser) updateLastCommandWithWhereConstraints(sequence *ast.Sequence) error { + lastCommand, parserError := parser.getLastCommand(sequence, token.WHERE) + if parserError != nil { + return parserError + } + + if lastCommand.TokenLiteral() == token.SELECT { + newCommand, err := parser.parseWhereCommand() + if err != nil { + return err + } + lastCommand.(*ast.SelectCommand).WhereCommand = newCommand.(*ast.WhereCommand) + } else if lastCommand.TokenLiteral() == token.DELETE { + newCommand, err := parser.parseWhereCommand() + if err != nil { + return err + } + lastCommand.(*ast.DeleteCommand).WhereCommand = newCommand.(*ast.WhereCommand) + } else if lastCommand.TokenLiteral() == token.UPDATE { + newCommand, err := parser.parseWhereCommand() + if err != nil { + return err + } + lastCommand.(*ast.UpdateCommand).WhereCommand = newCommand.(*ast.WhereCommand) + } else { + return &SyntaxCommandExpectedError{command: "WHERE", neededCommands: []string{"SELECT", "DELETE", "UPDATE"}} + } + return nil +} + func (parser *Parser) getLastCommand(sequence *ast.Sequence, currentToken string) (ast.Command, error) { if len(sequence.Commands) == 0 { return nil, &NoPredecessorParserError{command: currentToken} diff --git a/parser/parser_test.go b/parser/parser_test.go index 0823da4..3a00ad8 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -908,7 +908,7 @@ func testSelectStatement(t *testing.T, command ast.Command, expectedTableName st } if !spaceArrayEquals(actualSelectCommand.Space, expectedSpaces) { - t.Errorf("actualSelectCommand has diffrent space than expected. %+v != %+v", actualSelectCommand.Space, expectedSpaces) + t.Errorf("actualSelectCommand has different space than expected. %+v != %+v", actualSelectCommand.Space, expectedSpaces) return false } From 3cba61ee98d13d01f1c72c59dd8d429219a8d02e Mon Sep 17 00:00:00 2001 From: ixior462 Date: Wed, 26 Feb 2025 02:48:49 +0100 Subject: [PATCH 2/7] Fix errorhandling tests --- engine/engine.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engine/engine.go b/engine/engine.go index 56cea17..3fbf33b 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -55,6 +55,9 @@ func (engine *DbEngine) Evaluate(sequences *ast.Sequence) (string, error) { case *ast.SelectCommand: var selectOutput *Table selectOutput, err = engine.getSelectResponse(mappedCommand) + if err != nil { + return "", err + } result += selectOutput.ToString() + "\n" continue case *ast.DeleteCommand: From 4e921423ecb6e8d72cc81a5a9017eeb760d93700 Mon Sep 17 00:00:00 2001 From: ixior462 Date: Wed, 26 Feb 2025 02:55:40 +0100 Subject: [PATCH 3/7] Resolve go report card feed --- engine/engine.go | 3 --- engine/table.go | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/engine.go b/engine/engine.go index 3fbf33b..56cea17 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -55,9 +55,6 @@ func (engine *DbEngine) Evaluate(sequences *ast.Sequence) (string, error) { case *ast.SelectCommand: var selectOutput *Table selectOutput, err = engine.getSelectResponse(mappedCommand) - if err != nil { - return "", err - } result += selectOutput.ToString() + "\n" continue case *ast.DeleteCommand: diff --git a/engine/table.go b/engine/table.go index ec8561a..7a314c6 100644 --- a/engine/table.go +++ b/engine/table.go @@ -72,6 +72,9 @@ func (table *Table) getDistinctTable() *Table { // ToString - Return string contain all values and Column names in Table func (table *Table) ToString() string { + if table == nil { + return "" + } columWidths := getColumWidths(table.Columns) bar := getBar(columWidths) result := bar + "\n" From 63f3bee9d60d04a79c705331f3ea9e9e46d759ff Mon Sep 17 00:00:00 2001 From: ixior462 Date: Sat, 3 May 2025 03:53:10 +0200 Subject: [PATCH 4/7] Refactore engine functions --- ast/ast.go | 30 ++------ engine/engine.go | 155 ++++++++++++++++++++-------------------- engine/generic_value.go | 15 ---- engine/row.go | 3 +- engine/table.go | 4 +- token/token.go | 85 +++++++++++----------- 6 files changed, 132 insertions(+), 160 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index 5aa7657..6e503b1 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -49,9 +49,8 @@ type Tifier interface { func (p *Sequence) TokenLiteral() string { if len(p.Commands) > 0 { return p.Commands[0].TokenLiteral() - } else { - return "" } + return "" } // Identifier - Represent Token with string value that is equal to either column or table name @@ -205,7 +204,7 @@ type SelectCommand struct { func (ls SelectCommand) CommandNode() {} func (ls SelectCommand) TokenLiteral() string { return ls.Token.Literal } -func (ls *SelectCommand) AggregateFunctionAppears() bool { +func (ls SelectCommand) AggregateFunctionAppears() bool { for _, space := range ls.Space { if space.ContainsAggregateFunc() { return true @@ -223,10 +222,7 @@ func (ls *SelectCommand) AggregateFunctionAppears() bool { // SELECT * FROM table; // Returns false func (ls SelectCommand) HasWhereCommand() bool { - if ls.WhereCommand == nil { - return false - } - return true + return ls.WhereCommand != nil } // HasOrderByCommand - returns true if optional OrderByCommand is present in SelectCommand @@ -238,10 +234,7 @@ func (ls SelectCommand) HasWhereCommand() bool { // SELECT * FROM table; // Returns false func (ls SelectCommand) HasOrderByCommand() bool { - if ls.OrderByCommand == nil { - return false - } - return true + return ls.OrderByCommand != nil } // HasLimitCommand - returns true if optional LimitCommand is present in SelectCommand @@ -253,10 +246,7 @@ func (ls SelectCommand) HasOrderByCommand() bool { // SELECT * FROM table; // Returns false func (ls SelectCommand) HasLimitCommand() bool { - if ls.LimitCommand == nil { - return false - } - return true + return ls.LimitCommand != nil } // HasOffsetCommand - returns true if optional OffsetCommand is present in SelectCommand @@ -268,10 +258,7 @@ func (ls SelectCommand) HasLimitCommand() bool { // SELECT * FROM table LIMIT 10; // Returns false func (ls SelectCommand) HasOffsetCommand() bool { - if ls.OffsetCommand == nil { - return false - } - return true + return ls.OffsetCommand != nil } // HasJoinCommand - returns true if optional JoinCommand is present in SelectCommand @@ -283,10 +270,7 @@ func (ls SelectCommand) HasOffsetCommand() bool { // SELECT * FROM table; // Returns false func (ls SelectCommand) HasJoinCommand() bool { - if ls.JoinCommand == nil { - return false - } - return true + return ls.JoinCommand != nil } // UpdateCommand - Part of Command that allow to change existing data diff --git a/engine/engine.go b/engine/engine.go index 56cea17..a9f9a24 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -99,6 +99,8 @@ func (engine *DbEngine) getSelectResponse(selectCommand *ast.SelectCommand) (*Ta } } + // TODO: Implement Functional Options Pattern to avoid too specific methods with redundant logic + // this fragment should be validated and moved to single sequence of logic if selectCommand.HasWhereCommand() { whereCommand := selectCommand.WhereCommand if selectCommand.HasOrderByCommand() { @@ -157,45 +159,52 @@ func (engine *DbEngine) createTable(command *ast.CreateCommand) error { } func (engine *DbEngine) updateTable(command *ast.UpdateCommand) error { - table, exist := engine.Tables[command.Name.Token.Literal] - - if !exist { + table, exists := engine.Tables[command.Name.Token.Literal] + if !exists { return &TableDoesNotExistError{command.Name.Token.Literal} } - columns := table.Columns + columnIndices := make(map[string]int, len(table.Columns)) + for i, col := range table.Columns { + columnIndices[col.Name] = i + } - // TODO: This could be optimized - mappedChanges := make(map[int]ast.Anonymitifier) - for updatedCol, newValue := range command.Changes { - for colIndex := 0; colIndex < len(columns); colIndex++ { - if columns[colIndex].Name == updatedCol.Literal { - mappedChanges[colIndex] = newValue - break - } - if colIndex == len(columns)-1 { - return &ColumnDoesNotExistError{tableName: command.Name.GetToken().Literal, columnName: updatedCol.Literal} + // Map changes to column indices + type change struct { + index int + value ast.Anonymitifier + } + + changes := make([]change, 0, len(command.Changes)) + for colToken, newValue := range command.Changes { + colName := colToken.Literal + colIndex, ok := columnIndices[colName] + if !ok { + return &ColumnDoesNotExistError{ + tableName: command.Name.Token.Literal, + columnName: colName, } } + changes = append(changes, change{index: colIndex, value: newValue}) } - numberOfRows := len(columns[0].Values) - for rowIndex := 0; rowIndex < numberOfRows; rowIndex++ { + for rowIndex := 0; rowIndex < len(table.Columns[0].Values); rowIndex++ { if command.HasWhereCommand() { - fulfilledFilters, err := isFulfillingFilters(getRow(table, rowIndex), command.WhereCommand.Expression, command.WhereCommand.Token.Literal) + matches, err := isFulfillingFilters(getRow(table, rowIndex), command.WhereCommand.Expression, command.WhereCommand.Token.Literal) if err != nil { return err } - if !fulfilledFilters { + if !matches { continue } } - for colIndex, value := range mappedChanges { - interfaceValue, err := getInterfaceValue(value.GetToken()) + + for _, change := range changes { + val, err := getInterfaceValue(change.value.GetToken()) if err != nil { return err } - table.Columns[colIndex].Values[rowIndex] = interfaceValue + table.Columns[change.index].Values[rowIndex] = val } } @@ -212,13 +221,23 @@ func (engine *DbEngine) insertIntoTable(command *ast.InsertCommand) error { columns := table.Columns if len(command.Values) != len(columns) { - return &InvalidNumberOfParametersError{expectedNumber: len(columns), actualNumber: len(command.Values), commandName: command.Token.Literal} + return &InvalidNumberOfParametersError{ + expectedNumber: len(columns), + actualNumber: len(command.Values), + commandName: command.Token.Literal, + } } for i := range columns { expectedToken := tokenMapper(columns[i].Type.Type) - if (expectedToken != command.Values[i].Type) && (command.Values[i].Type != token.NULL) { - return &InvalidValueTypeError{expectedType: string(expectedToken), actualType: string(command.Values[i].Type), commandName: command.Token.Literal} + colValueType := command.Values[i].Type + + if (expectedToken != colValueType) && (colValueType != token.NULL) { + return &InvalidValueTypeError{ + expectedType: string(expectedToken), + actualType: string(colValueType), + commandName: command.Token.Literal, + } } interfaceValue, err := getInterfaceValue(command.Values[i]) if err != nil { @@ -237,11 +256,8 @@ func (engine *DbEngine) selectFromProvidedTable(command *ast.SelectCommand, tabl selectedTable := &Table{Columns: make([]*Column, 0)} for i := 0; i < len(command.Space); i++ { - var columnType token.Token - var columnName string + col := &Column{} var columnValues []ValueInterface - var err error - value := make([]ValueInterface, 0) currentSpace := command.Space[i] if currentSpace.ColumnName.Type == token.ASTERISK && currentSpace.AggregateFunc.Type == token.COUNT { @@ -249,33 +265,28 @@ func (engine *DbEngine) selectFromProvidedTable(command *ast.SelectCommand, tabl columnValues = columns[0].Values } } else { + var err error columnValues, err = getValuesOfColumn(currentSpace.ColumnName.Literal, columns) - } - - if err != nil { - return nil, err + if err != nil { + return nil, err + } } if currentSpace.ContainsAggregateFunc() { - columnName = fmt.Sprintf("%s(%s)", currentSpace.AggregateFunc.Literal, + col.Name = fmt.Sprintf("%s(%s)", currentSpace.AggregateFunc.Literal, currentSpace.ColumnName.Literal) - columnType = evaluateColumnTypeOfAggregateFunc(currentSpace) - aggregatedValue, aggregateErr := aggregateColumnContent(currentSpace, columnValues) - if aggregateErr != nil { - return nil, aggregateErr + col.Type = evaluateColumnTypeOfAggregateFunc(currentSpace) + aggregatedValue, err := aggregateColumnContent(currentSpace, columnValues) + if err != nil { + return nil, err } - value = append(value, aggregatedValue) + col.Values = []ValueInterface{aggregatedValue} } else { - columnName = currentSpace.ColumnName.Literal - columnType = currentSpace.ColumnName - value = append(value, columnValues[0]) + col.Name = currentSpace.ColumnName.Literal + col.Type = currentSpace.ColumnName + col.Values = []ValueInterface{columnValues[0]} } - - selectedTable.Columns = append(selectedTable.Columns, &Column{ - Name: columnName, - Type: columnType, - Values: value, - }) + selectedTable.Columns = append(selectedTable.Columns, col) } return selectedTable, nil } else if command.Space[0].ColumnName.Type == token.ASTERISK { @@ -327,9 +338,9 @@ func aggregateColumnContent(space ast.Space, columnValues []ValueInterface) (Val } } -func getCount(space ast.Space, columnValues []ValueInterface) (ValueInterface, error) { +func getCount(space ast.Space, columnValues []ValueInterface) (*IntegerValue, error) { if space.ColumnName.Type == token.ASTERISK { - return IntegerValue{Value: len(columnValues)}, nil + return &IntegerValue{Value: len(columnValues)}, nil } count := 0 for _, value := range columnValues { @@ -337,28 +348,20 @@ func getCount(space ast.Space, columnValues []ValueInterface) (ValueInterface, e count++ } } - return IntegerValue{Value: count}, nil + return &IntegerValue{Value: count}, nil } -func getAvg(columnValues []ValueInterface) (ValueInterface, error) { - if columnValues[0].GetType() == StringType { - return IntegerValue{Value: 0}, nil - } else { - sum := 0 - for _, value := range columnValues { - num, err := strconv.Atoi(value.ToString()) - if err != nil { - return nil, err - } - sum += num - } - return IntegerValue{Value: sum / len(columnValues)}, nil +func getAvg(columnValues []ValueInterface) (*IntegerValue, error) { + sum, err := getSum(columnValues) + if err != nil { + return nil, err } + return &IntegerValue{Value: sum.Value / len(columnValues)}, nil } -func getSum(columnValues []ValueInterface) (ValueInterface, error) { +func getSum(columnValues []ValueInterface) (*IntegerValue, error) { if columnValues[0].GetType() == StringType { - return IntegerValue{Value: 0}, nil + return &IntegerValue{Value: 0}, nil } else { sum := 0 for _, value := range columnValues { @@ -370,7 +373,7 @@ func getSum(columnValues []ValueInterface) (ValueInterface, error) { sum += num } } - return IntegerValue{Value: sum}, nil + return &IntegerValue{Value: sum}, nil } } @@ -415,21 +418,17 @@ func (engine *DbEngine) selectFromTableWithWhere(selectCommand *ast.SelectComman // selectFromTableWithWhereAndOrderBy - Return Table containing all values requested by SelectCommand, // filtered by WhereCommand and sorted by OrderByCommand func (engine *DbEngine) selectFromTableWithWhereAndOrderBy(selectCommand *ast.SelectCommand, whereCommand *ast.WhereCommand, orderByCommand *ast.OrderByCommand, table *Table) (*Table, error) { - filteredTable, err := engine.getFilteredTable(table, whereCommand, false, selectCommand.Name.GetToken().Literal) - - if err != nil { - return nil, err + if len(table.Columns) == 0 || len(table.Columns[0].Values) == 0 { + return engine.selectFromProvidedTable(selectCommand, &Table{Columns: []*Column{}}) } - emptyTable := getCopyOfTableWithoutRows(table) - - sortedTable, err := engine.getSortedTable(orderByCommand, filteredTable, emptyTable, selectCommand.Name.GetToken().Literal) + filteredTable, err := engine.getFilteredTable(table, whereCommand, false, selectCommand.Name.GetToken().Literal) if err != nil { return nil, err } - return engine.selectFromProvidedTable(selectCommand, sortedTable) + return engine.selectFromTableWithOrderBy(selectCommand, orderByCommand, filteredTable) } // selectFromTableWithOrderBy - Return Table containing all values requested by SelectCommand and sorted by OrderByCommand @@ -666,7 +665,7 @@ func isFulfillingFilters(row map[string]ValueInterface, expressionTree ast.Expre case *ast.OperationExpression: return processOperationExpression(row, mappedExpression, commandName) case *ast.BooleanExpression: - return processBooleanExpression(mappedExpression) + return processBooleanExpression(mappedExpression), nil case *ast.ConditionExpression: return processConditionExpression(row, mappedExpression, commandName) case *ast.ContainExpression: @@ -750,11 +749,11 @@ func processOperationExpression(row map[string]ValueInterface, operationExpressi return false, &UnsupportedOperationTokenError{operationExpression.Operation.Literal} } -func processBooleanExpression(booleanExpression *ast.BooleanExpression) (bool, error) { +func processBooleanExpression(booleanExpression *ast.BooleanExpression) bool { if booleanExpression.Boolean.Literal == token.TRUE { - return true, nil + return true } - return false, nil + return false } func getTifierValue(tifier ast.Tifier, row map[string]ValueInterface) (ValueInterface, error) { diff --git a/engine/generic_value.go b/engine/generic_value.go index d5c26c9..048f49f 100644 --- a/engine/generic_value.go +++ b/engine/generic_value.go @@ -2,7 +2,6 @@ package engine import ( "errors" - "fmt" "log" "strconv" ) @@ -38,20 +37,6 @@ type StringValue struct { type NullValue struct { } -// HandleValue - Function to take an instance of ValueInterface and cast to a specific implementation -func CastValueInterface(v ValueInterface) { - switch value := v.(type) { - case IntegerValue: - fmt.Printf("IntegerValue with Value: %d\n", value.Value) - case StringValue: - fmt.Printf("StringValue with Value: %s\n", value.Value) - case NullValue: - fmt.Println("NullValue (no value)") - default: - fmt.Println("Unknown type") - } -} - // ToString implementations func (value IntegerValue) ToString() string { return strconv.Itoa(value.Value) } func (value StringValue) ToString() string { return value.Value } diff --git a/engine/row.go b/engine/row.go index 6160956..b31f881 100644 --- a/engine/row.go +++ b/engine/row.go @@ -12,8 +12,7 @@ func MapTableToRows(table *Table) Rows { numberOfRows := len(table.Columns[0].Values) for rowIndex := 0; rowIndex < numberOfRows; rowIndex++ { - row := getRow(table, rowIndex) - rows = append(rows, row) + rows = append(rows, getRow(table, rowIndex)) } return Rows{rows: rows} } diff --git a/engine/table.go b/engine/table.go index 7a314c6..e4589c5 100644 --- a/engine/table.go +++ b/engine/table.go @@ -7,9 +7,11 @@ import ( // Table - Contain Columns that store values in engine type Table struct { - Columns []*Column + Columns Columns } +type Columns []*Column + func (table *Table) isEqual(secondTable *Table) bool { if len(table.Columns) != len(secondTable.Columns) { return false diff --git a/token/token.go b/token/token.go index 381a168..6842c54 100644 --- a/token/token.go +++ b/token/token.go @@ -9,76 +9,79 @@ type Token struct { } const ( - // ASTERISK - Operators + // Operators ASTERISK = "*" - // IDENT - Identifiers + literals - IDENT = "IDENT" // tab, car, apple... - LITERAL = "LITERAL" // 1343456 + // Identifiers & Literals + IDENT = "IDENT" // e.g., table, column names + LITERAL = "LITERAL" // e.g., numeric or string literals - // COMMA - Delimiters - COMMA = "," - SEMICOLON = ";" - - // EOF - Special tokens - EOF = "" + // Delimiters + COMMA = "," + SEMICOLON = ";" APOSTROPHE = "'" - // LPAREN - Paren + // Parentheses LPAREN = "(" RPAREN = ")" - // CREATE - Keywords - CREATE = "CREATE" - DROP = "DROP" - TABLE = "TABLE" - INSERT = "INSERT" - INTO = "INTO" - VALUES = "VALUES" - SELECT = "SELECT" + // Special Tokens + EOF = "" + ILLEGAL = "ILLEGAL" + + // Commands + CREATE = "CREATE" + DROP = "DROP" + TABLE = "TABLE" + INSERT = "INSERT" + INTO = "INTO" + VALUES = "VALUES" + SELECT = "SELECT" + DELETE = "DELETE" + UPDATE = "UPDATE" + + // Clauses FROM = "FROM" WHERE = "WHERE" - DELETE = "DELETE" ORDER = "ORDER" BY = "BY" ASC = "ASC" DESC = "DESC" LIMIT = "LIMIT" OFFSET = "OFFSET" - UPDATE = "UPDATE" SET = "SET" DISTINCT = "DISTINCT" - JOIN = "JOIN" - INNER = "INNER" - FULL = "FULL" - LEFT = "LEFT" - RIGHT = "RIGHT" - ON = "ON" - MIN = "MIN" - MAX = "MAX" - COUNT = "COUNT" - SUM = "SUM" - AVG = "AVG" - IN = "IN" - NOTIN = "NOTIN" - NULL = "NULL" + TO = "TO" - TO = "TO" + // Joins + JOIN = "JOIN" + INNER = "INNER" + FULL = "FULL" + LEFT = "LEFT" + RIGHT = "RIGHT" + ON = "ON" - // EQUAL - Logical operations + // Aggregates + MIN = "MIN" + MAX = "MAX" + COUNT = "COUNT" + SUM = "SUM" + AVG = "AVG" + + // Logical EQUAL = "EQUAL" NOT = "NOT" AND = "AND" OR = "OR" TRUE = "TRUE" FALSE = "FALSE" + IN = "IN" + NOTIN = "NOTIN" + NULL = "NULL" - // TEXT - Data types + // Data Types TEXT = "TEXT" INT = "INT" - - // ILLEGAL - System - ILLEGAL = "ILLEGAL" ) var keywords = map[string]Type{ From 04ccd648b9c3bd48c707c8f745d229c6588062ad Mon Sep 17 00:00:00 2001 From: ixior462 Date: Sat, 17 May 2025 18:01:08 +0200 Subject: [PATCH 5/7] Change getSelectResponse function --- engine/engine.go | 143 ++++++++------------------------ engine/table.go | 33 -------- engine/table_transformations.go | 110 ++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 143 deletions(-) create mode 100644 engine/table_transformations.go diff --git a/engine/engine.go b/engine/engine.go index a9f9a24..be7cf68 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -79,60 +79,55 @@ func (engine *DbEngine) Evaluate(sequences *ast.Sequence) (string, error) { return result, err } -// getSelectResponse - Returns Select response basing on ast.OrderByCommand and ast.WhereCommand included in this Select +// getSelectResponse - processes a SELECT query represented by the ast.SelectCommand and applies a pipeline of +// transformations based on options applied to ast.SelectCommand func (engine *DbEngine) getSelectResponse(selectCommand *ast.SelectCommand) (*Table, error) { var table *Table var err error if selectCommand.HasJoinCommand() { - joinCommand := selectCommand.JoinCommand - table, err = engine.joinTables(joinCommand, selectCommand.Name.Token.Literal) - if err != nil { - return nil, err - } + table, err = engine.joinTables(selectCommand.JoinCommand, selectCommand.Name.Token.Literal) } else { - var exist bool - table, exist = engine.Tables[selectCommand.Name.Token.Literal] - - if !exist { + var exists bool + table, exists = engine.Tables[selectCommand.Name.Token.Literal] + if !exists { return nil, &TableDoesNotExistError{selectCommand.Name.Token.Literal} } } - // TODO: Implement Functional Options Pattern to avoid too specific methods with redundant logic - // this fragment should be validated and moved to single sequence of logic + if err != nil { + return nil, err + } + + // Build transformation pipeline + var transformers []TableTransformer + + if selectCommand.HasOrderByCommand() { + transformers = append(transformers, engine.withOrderBy(selectCommand)) + } + if selectCommand.HasWhereCommand() { - whereCommand := selectCommand.WhereCommand - if selectCommand.HasOrderByCommand() { - orderByCommand := selectCommand.OrderByCommand - table, err = engine.selectFromTableWithWhereAndOrderBy(selectCommand, whereCommand, orderByCommand, table) - if err != nil { - return nil, err - } - } else { - table, err = engine.selectFromTableWithWhere(selectCommand, whereCommand, table) - if err != nil { - return nil, err - } - } - } else if selectCommand.HasOrderByCommand() { - table, err = engine.selectFromTableWithOrderBy(selectCommand, selectCommand.OrderByCommand, table) - if err != nil { - return nil, err - } - } else { - table, err = engine.selectFromProvidedTable(selectCommand, table) - if err != nil { - return nil, err - } + transformers = append(transformers, engine.withWhere(selectCommand)) + } + + if len(transformers) == 0 { + transformers = append(transformers, engine.withVanillaSelect(selectCommand)) } - if selectCommand.HasLimitCommand() || selectCommand.HasOffsetCommand() { - table.applyOffsetAndLimit(selectCommand) + if selectCommand.HasOffsetCommand() || selectCommand.HasLimitCommand() { + transformers = append(transformers, engine.withOffsetLimit(selectCommand)) } if selectCommand.HasDistinct { - table = table.getDistinctTable() + transformers = append(transformers, engine.withDistinct()) + } + + // Apply transformations + for _, transform := range transformers { + table, err = transform(table) + if err != nil { + return nil, err + } } return table, nil @@ -400,50 +395,6 @@ func (engine *DbEngine) dropTable(dropCommand *ast.DropCommand) { delete(engine.Tables, dropCommand.Name.GetToken().Literal) } -// selectFromTableWithWhere - Return Table containing all values requested by SelectCommand and filtered by WhereCommand -func (engine *DbEngine) selectFromTableWithWhere(selectCommand *ast.SelectCommand, whereCommand *ast.WhereCommand, table *Table) (*Table, error) { - if len(table.Columns) == 0 || len(table.Columns[0].Values) == 0 { - return engine.selectFromProvidedTable(selectCommand, &Table{Columns: []*Column{}}) - } - - filteredTable, err := engine.getFilteredTable(table, whereCommand, false, selectCommand.Name.GetToken().Literal) - - if err != nil { - return nil, err - } - - return engine.selectFromProvidedTable(selectCommand, filteredTable) -} - -// selectFromTableWithWhereAndOrderBy - Return Table containing all values requested by SelectCommand, -// filtered by WhereCommand and sorted by OrderByCommand -func (engine *DbEngine) selectFromTableWithWhereAndOrderBy(selectCommand *ast.SelectCommand, whereCommand *ast.WhereCommand, orderByCommand *ast.OrderByCommand, table *Table) (*Table, error) { - if len(table.Columns) == 0 || len(table.Columns[0].Values) == 0 { - return engine.selectFromProvidedTable(selectCommand, &Table{Columns: []*Column{}}) - } - - filteredTable, err := engine.getFilteredTable(table, whereCommand, false, selectCommand.Name.GetToken().Literal) - - if err != nil { - return nil, err - } - - return engine.selectFromTableWithOrderBy(selectCommand, orderByCommand, filteredTable) -} - -// selectFromTableWithOrderBy - Return Table containing all values requested by SelectCommand and sorted by OrderByCommand -func (engine *DbEngine) selectFromTableWithOrderBy(selectCommand *ast.SelectCommand, orderByCommand *ast.OrderByCommand, table *Table) (*Table, error) { - emptyTable := getCopyOfTableWithoutRows(table) - - sortedTable, err := engine.getSortedTable(orderByCommand, table, emptyTable, selectCommand.Name.GetToken().Literal) - - if err != nil { - return nil, err - } - - return engine.selectFromProvidedTable(selectCommand, sortedTable) -} - func (engine *DbEngine) getSortedTable(orderByCommand *ast.OrderByCommand, table *Table, copyOfTable *Table, tableName string) (*Table, error) { sortPatterns := orderByCommand.SortPatterns @@ -614,34 +565,6 @@ func addColumnsWithPrefix(finalTable *Table, columnsToAdd []*Column, prefix stri } } -func (table *Table) applyOffsetAndLimit(command *ast.SelectCommand) { - var offset = 0 - var limitRaw = -1 - - if command.HasLimitCommand() { - limitRaw = command.LimitCommand.Count - } - if command.HasOffsetCommand() { - offset = command.OffsetCommand.Count - } - - for _, column := range table.Columns { - var limit int - - if limitRaw == -1 || limitRaw+offset > len(column.Values) { - limit = len(column.Values) - } else { - limit = limitRaw + offset - } - - if offset > len(column.Values) || limit == 0 { - column.Values = make([]ValueInterface, 0) - } else { - column.Values = column.Values[offset:limit] - } - } -} - func xor(fulfilledFilters bool, negation bool) bool { return (fulfilledFilters || negation) && !(fulfilledFilters && negation) } diff --git a/engine/table.go b/engine/table.go index e4589c5..79513d4 100644 --- a/engine/table.go +++ b/engine/table.go @@ -2,7 +2,6 @@ package engine import ( "github.com/LissaGreense/GO4SQL/token" - "hash/adler32" ) // Table - Contain Columns that store values in engine @@ -40,38 +39,6 @@ func (table *Table) isEqual(secondTable *Table) bool { return true } -// getDistinctTable - Takes input table, and returns new one without any duplicates -func (table *Table) getDistinctTable() *Table { - distinctTable := getCopyOfTableWithoutRows(table) - - rowsCount := len(table.Columns[0].Values) - - checksumSet := map[uint32]struct{}{} - - for iRow := 0; iRow < rowsCount; iRow++ { - - mergedColumnValues := "" - for iColumn := range table.Columns { - fieldValue := table.Columns[iColumn].Values[iRow].ToString() - if table.Columns[iColumn].Type.Literal == token.TEXT { - fieldValue = "'" + fieldValue + "'" - } - mergedColumnValues += fieldValue - } - checksum := adler32.Checksum([]byte(mergedColumnValues)) - - _, exist := checksumSet[checksum] - if !exist { - checksumSet[checksum] = struct{}{} - for i, column := range distinctTable.Columns { - column.Values = append(column.Values, table.Columns[i].Values[iRow]) - } - } - } - - return distinctTable -} - // ToString - Return string contain all values and Column names in Table func (table *Table) ToString() string { if table == nil { diff --git a/engine/table_transformations.go b/engine/table_transformations.go new file mode 100644 index 0000000..1c02977 --- /dev/null +++ b/engine/table_transformations.go @@ -0,0 +1,110 @@ +package engine + +import ( + "github.com/LissaGreense/GO4SQL/ast" + "github.com/LissaGreense/GO4SQL/token" + "hash/adler32" +) + +// TableTransformer defines a function that takes a Table as input and then applies a transformation +type TableTransformer func(*Table) (*Table, error) + +// --- Basic SELECT --- +func (engine *DbEngine) withVanillaSelect(cmd *ast.SelectCommand) TableTransformer { + return func(tbl *Table) (*Table, error) { + return engine.selectFromProvidedTable(cmd, tbl) + } +} + +// --- WHERE only --- +func (engine *DbEngine) withWhere(cmd *ast.SelectCommand) TableTransformer { + return func(tbl *Table) (*Table, error) { + if len(tbl.Columns) == 0 || len(tbl.Columns[0].Values) == 0 { + return engine.selectFromProvidedTable(cmd, &Table{Columns: []*Column{}}) + } + filtered, err := engine.getFilteredTable(tbl, cmd.WhereCommand, false, cmd.Name.GetToken().Literal) + if err != nil { + return nil, err + } + return engine.selectFromProvidedTable(cmd, filtered) + } +} + +// --- ORDER BY only --- +func (engine *DbEngine) withOrderBy(cmd *ast.SelectCommand) TableTransformer { + return func(tbl *Table) (*Table, error) { + emptyTable := getCopyOfTableWithoutRows(tbl) + sorted, err := engine.getSortedTable(cmd.OrderByCommand, tbl, emptyTable, cmd.Name.GetToken().Literal) + if err != nil { + return nil, err + } + return engine.selectFromProvidedTable(cmd, sorted) + } +} + +// --- OFFSET + LIMIT --- +func (engine *DbEngine) withOffsetLimit(cmd *ast.SelectCommand) TableTransformer { + return func(tbl *Table) (*Table, error) { + var offset = 0 + var limitRaw = -1 + + if cmd.HasLimitCommand() { + limitRaw = cmd.LimitCommand.Count + } + if cmd.HasOffsetCommand() { + offset = cmd.OffsetCommand.Count + } + + for _, column := range tbl.Columns { + var limit int + + if limitRaw == -1 || limitRaw+offset > len(column.Values) { + limit = len(column.Values) + } else { + limit = limitRaw + offset + } + + if offset > len(column.Values) || limit == 0 { + column.Values = make([]ValueInterface, 0) + } else { + column.Values = column.Values[offset:limit] + } + } + + return tbl, nil + } +} + +// --- DISTINCT --- +func (engine *DbEngine) withDistinct() TableTransformer { + return func(tbl *Table) (*Table, error) { + distinctTable := getCopyOfTableWithoutRows(tbl) + + rowsCount := len(tbl.Columns[0].Values) + + checksumSet := map[uint32]struct{}{} + + for iRow := 0; iRow < rowsCount; iRow++ { + + mergedColumnValues := "" + for iColumn := range tbl.Columns { + fieldValue := tbl.Columns[iColumn].Values[iRow].ToString() + if tbl.Columns[iColumn].Type.Literal == token.TEXT { + fieldValue = "'" + fieldValue + "'" + } + mergedColumnValues += fieldValue + } + checksum := adler32.Checksum([]byte(mergedColumnValues)) + + _, exist := checksumSet[checksum] + if !exist { + checksumSet[checksum] = struct{}{} + for i, column := range distinctTable.Columns { + column.Values = append(column.Values, tbl.Columns[i].Values[iRow]) + } + } + } + + return distinctTable, nil + } +} From 365a60fcac3401b47aef9b4fc6b6df792ff59aa9 Mon Sep 17 00:00:00 2001 From: LissaGreense Date: Fri, 23 May 2025 22:26:09 +0200 Subject: [PATCH 6/7] Refactor select response logic --- e2e/e2e_test.sh | 0 engine/engine.go | 28 ++--- engine/query_processor.go | 178 ++++++++++++++++++++++++++++++++ engine/table_transformations.go | 110 -------------------- 4 files changed, 189 insertions(+), 127 deletions(-) mode change 100644 => 100755 e2e/e2e_test.sh create mode 100644 engine/query_processor.go delete mode 100644 engine/table_transformations.go diff --git a/e2e/e2e_test.sh b/e2e/e2e_test.sh old mode 100644 new mode 100755 diff --git a/engine/engine.go b/engine/engine.go index be7cf68..ad170fc 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -99,38 +99,32 @@ func (engine *DbEngine) getSelectResponse(selectCommand *ast.SelectCommand) (*Ta return nil, err } - // Build transformation pipeline - var transformers []TableTransformer + processor := NewSelectProcessor(engine, selectCommand) + // Build the transformation pipeline using the builder pattern if selectCommand.HasOrderByCommand() { - transformers = append(transformers, engine.withOrderBy(selectCommand)) + processor.WithOrderByClause() } if selectCommand.HasWhereCommand() { - transformers = append(transformers, engine.withWhere(selectCommand)) + processor.WithWhereClause() } - if len(transformers) == 0 { - transformers = append(transformers, engine.withVanillaSelect(selectCommand)) + // If no WHERE or ORDER BY, the vanilla select (projection) is applied first. + // Otherwise, WHERE/ORDER BY are applied, and then the projection happens within them (handled by their respective transformers). + if !selectCommand.HasOrderByCommand() && !selectCommand.HasWhereCommand() { + processor.WithVanillaSelectClause() } if selectCommand.HasOffsetCommand() || selectCommand.HasLimitCommand() { - transformers = append(transformers, engine.withOffsetLimit(selectCommand)) + processor.WithOffsetLimitClause() } if selectCommand.HasDistinct { - transformers = append(transformers, engine.withDistinct()) + processor.WithDistinctClause() } - // Apply transformations - for _, transform := range transformers { - table, err = transform(table) - if err != nil { - return nil, err - } - } - - return table, nil + return processor.Process(table) } // createTable - initialize new table in engine with specified name diff --git a/engine/query_processor.go b/engine/query_processor.go new file mode 100644 index 0000000..ddc50ec --- /dev/null +++ b/engine/query_processor.go @@ -0,0 +1,178 @@ +package engine + +import ( + "hash/adler32" + + "github.com/LissaGreense/GO4SQL/ast" + "github.com/LissaGreense/GO4SQL/token" +) + +// TableTransformer defines a function that takes a Table as input and then applies a transformation +type TableTransformer func(*Table) (*Table, error) + +// SelectProcessor handles the step-by-step processing of a SELECT query using a builder pattern. +type SelectProcessor struct { + engine *DbEngine + cmd *ast.SelectCommand + transformers []TableTransformer +} + +// NewSelectProcessor creates a new SelectProcessor. +func NewSelectProcessor(engine *DbEngine, cmd *ast.SelectCommand) *SelectProcessor { + return &SelectProcessor{ + engine: engine, + cmd: cmd, + transformers: []TableTransformer{}, // Initialize as empty slice + } +} + +// WithVanillaSelectClause adds the vanilla select (projection) transformation. +func (sp *SelectProcessor) WithVanillaSelectClause() *SelectProcessor { + sp.transformers = append(sp.transformers, sp.getVanillaSelectTransformer()) + return sp +} + +// WithWhereClause adds the WHERE clause transformation. +func (sp *SelectProcessor) WithWhereClause() *SelectProcessor { + sp.transformers = append(sp.transformers, sp.getWhereTransformer()) + return sp +} + +// WithOrderByClause adds the ORDER BY clause transformation. +func (sp *SelectProcessor) WithOrderByClause() *SelectProcessor { + sp.transformers = append(sp.transformers, sp.getOrderByTransformer()) + return sp +} + +// WithOffsetLimitClause adds the OFFSET and LIMIT clause transformation. +func (sp *SelectProcessor) WithOffsetLimitClause() *SelectProcessor { + sp.transformers = append(sp.transformers, sp.getOffsetLimitTransformer()) + return sp +} + +// WithDistinctClause adds the DISTINCT clause transformation. +func (sp *SelectProcessor) WithDistinctClause() *SelectProcessor { + sp.transformers = append(sp.transformers, sp.getDistinctTransformer()) + return sp +} + +// Process applies the configured pipeline of transformations to the initialTable. +func (sp *SelectProcessor) Process(initialTable *Table) (*Table, error) { + table := initialTable + var err error + + for _, transform := range sp.transformers { + table, err = transform(table) + if err != nil { + return nil, err + } + } + return table, nil +} + +// --- Private Transformer Getters --- + +func (sp *SelectProcessor) getVanillaSelectTransformer() TableTransformer { + return func(tbl *Table) (*Table, error) { + return sp.engine.selectFromProvidedTable(sp.cmd, tbl) + } +} + +func (sp *SelectProcessor) getWhereTransformer() TableTransformer { + return func(tbl *Table) (*Table, error) { + if len(tbl.Columns) == 0 || (len(tbl.Columns) > 0 && len(tbl.Columns[0].Values) == 0) { + return sp.engine.selectFromProvidedTable(sp.cmd, &Table{Columns: []*Column{}}) + } + filtered, err := sp.engine.getFilteredTable(tbl, sp.cmd.WhereCommand, false, sp.cmd.Name.GetToken().Literal) + if err != nil { + return nil, err + } + return sp.engine.selectFromProvidedTable(sp.cmd, filtered) + } +} + +func (sp *SelectProcessor) getOrderByTransformer() TableTransformer { + return func(tbl *Table) (*Table, error) { + emptyTable := getCopyOfTableWithoutRows(tbl) + sorted, err := sp.engine.getSortedTable(sp.cmd.OrderByCommand, tbl, emptyTable, sp.cmd.Name.GetToken().Literal) + if err != nil { + return nil, err + } + return sp.engine.selectFromProvidedTable(sp.cmd, sorted) + } +} + +func (sp *SelectProcessor) getOffsetLimitTransformer() TableTransformer { + return func(tbl *Table) (*Table, error) { + var offset = 0 + var limitRaw = -1 + + if sp.cmd.HasLimitCommand() { + limitRaw = sp.cmd.LimitCommand.Count + } + if sp.cmd.HasOffsetCommand() { + offset = sp.cmd.OffsetCommand.Count + } + + if len(tbl.Columns) == 0 { + return tbl, nil + } + + for _, column := range tbl.Columns { + var limit int + + if limitRaw == -1 || limitRaw+offset > len(column.Values) { + limit = len(column.Values) + } else { + limit = limitRaw + offset + } + + if offset >= len(column.Values) { + column.Values = make([]ValueInterface, 0) + } else if offset < len(column.Values) && limit > offset { + column.Values = column.Values[offset:limit] + } else { + column.Values = make([]ValueInterface, 0) + } + } + return tbl, nil + } +} + +func (sp *SelectProcessor) getDistinctTransformer() TableTransformer { + return func(tbl *Table) (*Table, error) { + if len(tbl.Columns) == 0 || len(tbl.Columns[0].Values) == 0 { + return tbl, nil + } + + distinctTable := getCopyOfTableWithoutRows(tbl) + rowsCount := len(tbl.Columns[0].Values) + checksumSet := make(map[uint32]struct{}) + + for iRow := range rowsCount { + mergedColumnValues := "" + for iColumn := range tbl.Columns { + if iRow < len(tbl.Columns[iColumn].Values) { + fieldValue := tbl.Columns[iColumn].Values[iRow].ToString() + if tbl.Columns[iColumn].Type.Literal == token.TEXT { + fieldValue = "'" + fieldValue + "'" + } + mergedColumnValues += fieldValue + } else { + mergedColumnValues += "" + } + } + checksum := adler32.Checksum([]byte(mergedColumnValues)) + + if _, exist := checksumSet[checksum]; !exist { + checksumSet[checksum] = struct{}{} + for i, column := range distinctTable.Columns { + if iRow < len(tbl.Columns[i].Values) { + column.Values = append(column.Values, tbl.Columns[i].Values[iRow]) + } + } + } + } + return distinctTable, nil + } +} diff --git a/engine/table_transformations.go b/engine/table_transformations.go deleted file mode 100644 index 1c02977..0000000 --- a/engine/table_transformations.go +++ /dev/null @@ -1,110 +0,0 @@ -package engine - -import ( - "github.com/LissaGreense/GO4SQL/ast" - "github.com/LissaGreense/GO4SQL/token" - "hash/adler32" -) - -// TableTransformer defines a function that takes a Table as input and then applies a transformation -type TableTransformer func(*Table) (*Table, error) - -// --- Basic SELECT --- -func (engine *DbEngine) withVanillaSelect(cmd *ast.SelectCommand) TableTransformer { - return func(tbl *Table) (*Table, error) { - return engine.selectFromProvidedTable(cmd, tbl) - } -} - -// --- WHERE only --- -func (engine *DbEngine) withWhere(cmd *ast.SelectCommand) TableTransformer { - return func(tbl *Table) (*Table, error) { - if len(tbl.Columns) == 0 || len(tbl.Columns[0].Values) == 0 { - return engine.selectFromProvidedTable(cmd, &Table{Columns: []*Column{}}) - } - filtered, err := engine.getFilteredTable(tbl, cmd.WhereCommand, false, cmd.Name.GetToken().Literal) - if err != nil { - return nil, err - } - return engine.selectFromProvidedTable(cmd, filtered) - } -} - -// --- ORDER BY only --- -func (engine *DbEngine) withOrderBy(cmd *ast.SelectCommand) TableTransformer { - return func(tbl *Table) (*Table, error) { - emptyTable := getCopyOfTableWithoutRows(tbl) - sorted, err := engine.getSortedTable(cmd.OrderByCommand, tbl, emptyTable, cmd.Name.GetToken().Literal) - if err != nil { - return nil, err - } - return engine.selectFromProvidedTable(cmd, sorted) - } -} - -// --- OFFSET + LIMIT --- -func (engine *DbEngine) withOffsetLimit(cmd *ast.SelectCommand) TableTransformer { - return func(tbl *Table) (*Table, error) { - var offset = 0 - var limitRaw = -1 - - if cmd.HasLimitCommand() { - limitRaw = cmd.LimitCommand.Count - } - if cmd.HasOffsetCommand() { - offset = cmd.OffsetCommand.Count - } - - for _, column := range tbl.Columns { - var limit int - - if limitRaw == -1 || limitRaw+offset > len(column.Values) { - limit = len(column.Values) - } else { - limit = limitRaw + offset - } - - if offset > len(column.Values) || limit == 0 { - column.Values = make([]ValueInterface, 0) - } else { - column.Values = column.Values[offset:limit] - } - } - - return tbl, nil - } -} - -// --- DISTINCT --- -func (engine *DbEngine) withDistinct() TableTransformer { - return func(tbl *Table) (*Table, error) { - distinctTable := getCopyOfTableWithoutRows(tbl) - - rowsCount := len(tbl.Columns[0].Values) - - checksumSet := map[uint32]struct{}{} - - for iRow := 0; iRow < rowsCount; iRow++ { - - mergedColumnValues := "" - for iColumn := range tbl.Columns { - fieldValue := tbl.Columns[iColumn].Values[iRow].ToString() - if tbl.Columns[iColumn].Type.Literal == token.TEXT { - fieldValue = "'" + fieldValue + "'" - } - mergedColumnValues += fieldValue - } - checksum := adler32.Checksum([]byte(mergedColumnValues)) - - _, exist := checksumSet[checksum] - if !exist { - checksumSet[checksum] = struct{}{} - for i, column := range distinctTable.Columns { - column.Values = append(column.Values, tbl.Columns[i].Values[iRow]) - } - } - } - - return distinctTable, nil - } -} From 610d98dc8bfcf0f9cda4dd7a4922264dae5df919 Mon Sep 17 00:00:00 2001 From: ixior462 Date: Fri, 23 May 2025 22:54:25 +0200 Subject: [PATCH 7/7] Improve comments --- ast/ast.go | 4 ++-- engine/column.go | 2 +- engine/engine.go | 4 ++-- engine/errors.go | 22 +++++++++++----------- engine/query_processor.go | 2 +- engine/table.go | 2 +- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index 6e503b1..3c10f13 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -61,7 +61,7 @@ type Identifier struct { func (ls Identifier) IsIdentifier() bool { return true } func (ls Identifier) GetToken() token.Token { return ls.Token } -// Anonymitifier - Represent Token with string value that is equal to simple value that is put into columns +// Anonymitifier - Represent Token with a string value that is equal to a simple value that is put into columns type Anonymitifier struct { Token token.Token // the token.IDENT token } @@ -106,7 +106,7 @@ func (ls ConditionExpression) GetIdentifiers() []Identifier { return identifiers } -// ContainExpression - TokenType of Expression that represents structure for IN operator +// ContainExpression - TokenType of Expression that represents structure for-IN-operator // // Example: // colName IN ('value1', 'value2', 'value3') diff --git a/engine/column.go b/engine/column.go index 419b157..d583b90 100644 --- a/engine/column.go +++ b/engine/column.go @@ -4,7 +4,7 @@ import ( "github.com/LissaGreense/GO4SQL/token" ) -// Column - part of the Table containing name of Column and values in it +// Column - part of the Table containing the name of Column and values in it type Column struct { Name string Type token.Token diff --git a/engine/engine.go b/engine/engine.go index ad170fc..658d569 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -650,7 +650,7 @@ func processOperationExpression(row map[string]ValueInterface, operationExpressi } right, err := isFulfillingFilters(row, operationExpression.Right, commandName) - return left && right, err + return right, err } if operationExpression.Operation.Type == token.OR { @@ -660,7 +660,7 @@ func processOperationExpression(row map[string]ValueInterface, operationExpressi } right, err := isFulfillingFilters(row, operationExpression.Right, commandName) - return left || right, err + return right, err } return false, &UnsupportedOperationTokenError{operationExpression.Operation.Literal} diff --git a/engine/errors.go b/engine/errors.go index 81910de..e0497ad 100644 --- a/engine/errors.go +++ b/engine/errors.go @@ -2,8 +2,8 @@ package engine import "strconv" -// TableAlreadyExistsError - error thrown when user tries to create table using name that already -// exists in database +// TableAlreadyExistsError - error thrown when a user tries to create table using name that already +// exists in a database type TableAlreadyExistsError struct { tableName string } @@ -12,7 +12,7 @@ func (m *TableAlreadyExistsError) Error() string { return "table with the name of " + m.tableName + " already exists" } -// TableDoesNotExistError - error thrown when user tries to make operation on un-existing table +// TableDoesNotExistError - error thrown when the user tries to make operation on an unexisting table type TableDoesNotExistError struct { tableName string } @@ -21,7 +21,7 @@ func (m *TableDoesNotExistError) Error() string { return "table with the name of " + m.tableName + " doesn't exist" } -// ColumnDoesNotExistError - error thrown when user tries to make operation on un-existing column +// ColumnDoesNotExistError - error thrown when the user tries to make operation on an unexisting column type ColumnDoesNotExistError struct { tableName string columnName string @@ -32,7 +32,7 @@ func (m *ColumnDoesNotExistError) Error() string { } // InvalidNumberOfParametersError - error thrown when user provides invalid number of expected parameters -// (ex. fewer values in insert than defined ) +// (ex. fewer values in insert than defined) type InvalidNumberOfParametersError struct { expectedNumber int actualNumber int @@ -43,7 +43,7 @@ func (m *InvalidNumberOfParametersError) Error() string { return "invalid number of parameters in " + m.commandName + " command, should be: " + strconv.Itoa(m.expectedNumber) + ", but got: " + strconv.Itoa(m.actualNumber) } -// InvalidValueTypeError - error thrown when user provides value of different type than expected +// InvalidValueTypeError - error thrown when a user provides value of a different type than expected type InvalidValueTypeError struct { expectedType string actualType string @@ -54,7 +54,7 @@ func (m *InvalidValueTypeError) Error() string { return "invalid value type provided in " + m.commandName + " command, expecting: " + m.expectedType + ", got: " + m.actualType } -// UnsupportedValueType - error thrown when engine found unsupported data type to be stored inside +// UnsupportedValueType - error thrown when the engine found unsupported data type to be stored inside // the columns type UnsupportedValueType struct { variable string @@ -74,8 +74,8 @@ func (m *UnsupportedOperationTokenError) Error() string { return "unsupported operation token has been used: " + m.variable } -// UnsupportedConditionalTokenError - error thrown when engine found unsupported conditional token -// inside expression (supported are: EQUAL, NOT) +// UnsupportedConditionalTokenError - error thrown when the engine found unsupported conditional token +// inside the expression (supported are: EQUAL, NOT) type UnsupportedConditionalTokenError struct { variable string commandName string @@ -85,7 +85,7 @@ func (m *UnsupportedConditionalTokenError) Error() string { return "operation '" + m.variable + "' provided in " + m.commandName + " command isn't allowed" } -// UnsupportedExpressionTypeError - error thrown when engine found unsupported expression type +// UnsupportedExpressionTypeError - error thrown when the engine found an unsupported expression type type UnsupportedExpressionTypeError struct { variable string commandName string @@ -95,7 +95,7 @@ func (m *UnsupportedExpressionTypeError) Error() string { return "unsupported expression has been used in " + m.commandName + "command: " + m.variable } -// UnsupportedCommandTypeFromParserError - error thrown when engine found unsupported command +// UnsupportedCommandTypeFromParserError - error thrown when the engine found unsupported command // from parser type UnsupportedCommandTypeFromParserError struct { variable string diff --git a/engine/query_processor.go b/engine/query_processor.go index ddc50ec..46b4343 100644 --- a/engine/query_processor.go +++ b/engine/query_processor.go @@ -22,7 +22,7 @@ func NewSelectProcessor(engine *DbEngine, cmd *ast.SelectCommand) *SelectProcess return &SelectProcessor{ engine: engine, cmd: cmd, - transformers: []TableTransformer{}, // Initialize as empty slice + transformers: []TableTransformer{}, // Initialize as an empty slice } } diff --git a/engine/table.go b/engine/table.go index 79513d4..a3fedd3 100644 --- a/engine/table.go +++ b/engine/table.go @@ -39,7 +39,7 @@ func (table *Table) isEqual(secondTable *Table) bool { return true } -// ToString - Return string contain all values and Column names in Table +// ToString - Return string contains all values and Column names in Table func (table *Table) ToString() string { if table == nil { return ""