diff --git a/src/Makefile b/src/Makefile index 09d46ce..eff5f64 100644 --- a/src/Makefile +++ b/src/Makefile @@ -36,3 +36,8 @@ clean: rm -f *.o *~ $(BIN_DIR)/analysis $(TESTS_BIN) $(BIN_DIR)/grammar_test $(BIN_DIR)/tests rm -rf $(BUILD_DIR) make clean -C parser/ + + +test: tests grammar_test + @./$(BIN_DIR)/grammar_test -f ../test/valid_queries.sql + @./$(BIN_DIR)/tests \ No newline at end of file diff --git a/src/lib/UpdateStatement.h b/src/lib/UpdateStatement.h index 3430610..e06a3f3 100644 --- a/src/lib/UpdateStatement.h +++ b/src/lib/UpdateStatement.h @@ -20,7 +20,7 @@ struct UpdateStatement : Statement { virtual ~UpdateStatement(); // defined in destructors.cpp TableRef* table; - List updates; + List* updates; Expr* where; }; diff --git a/src/lib/destructors.cpp b/src/lib/destructors.cpp index 44d6eae..be76b0e 100644 --- a/src/lib/destructors.cpp +++ b/src/lib/destructors.cpp @@ -38,6 +38,15 @@ DeleteStatement::~DeleteStatement() { delete expr; } +/** + * UpdateStatement.h + */ +UpdateStatement::~UpdateStatement() { + delete table; + delete updates; + delete where; +} + /** * SelectStatement.h */ diff --git a/src/lib/sqlhelper.cpp b/src/lib/sqlhelper.cpp index 9a945c5..dbb05ea 100644 --- a/src/lib/sqlhelper.cpp +++ b/src/lib/sqlhelper.cpp @@ -5,7 +5,6 @@ namespace hsql { -void printExpression(Expr* expr, uint num_indent); void printOperatorExpression(Expr* expr, uint num_indent); const char* indent(uint num_indent) { return std::string(num_indent, '\t').c_str(); } diff --git a/src/lib/sqlhelper.h b/src/lib/sqlhelper.h index a935a4b..a34f453 100644 --- a/src/lib/sqlhelper.h +++ b/src/lib/sqlhelper.h @@ -11,6 +11,7 @@ void printSelectStatementInfo(SelectStatement* stmt, uint num_indent); void printImportStatementInfo(ImportStatement* stmt, uint num_indent); void printInsertStatementInfo(InsertStatement* stmt, uint num_indent); void printCreateStatementInfo(CreateStatement* stmt, uint num_indent); +void printExpression(Expr* expr, uint num_indent); } // namespace hsql diff --git a/src/parser/bison_parser.y b/src/parser/bison_parser.y index e623132..fd80cbc 100644 --- a/src/parser/bison_parser.y +++ b/src/parser/bison_parser.y @@ -84,6 +84,7 @@ typedef void* yyscan_t; hsql::CreateStatement* create_stmt; hsql::InsertStatement* insert_stmt; hsql::DeleteStatement* delete_stmt; + hsql::UpdateStatement* update_stmt; hsql::TableRef* table; hsql::Expr* expr; @@ -91,12 +92,14 @@ typedef void* yyscan_t; hsql::OrderType order_type; hsql::LimitDescription* limit; hsql::ColumnDefinition* column_t; + hsql::UpdateClause* update_t; hsql::StatementList* stmt_list; hsql::List* slist; hsql::List* expr_list; hsql::List* table_list; hsql::List* column_list_t; + hsql::List* update_list_t; } @@ -135,11 +138,12 @@ typedef void* yyscan_t; %type create_statement %type insert_statement %type delete_statement truncate_statement +%type update_statement %type table_name opt_alias alias file_path %type opt_not_exists %type import_file_type opt_join_type column_type %type from_clause table_ref table_ref_atomic table_ref_name -%type
join_clause join_table +%type
join_clause join_table table_ref_name_no_alias %type expr scalar_expr unary_expr binary_expr function_expr star_expr expr_alias %type column_name literal int_literal num_literal string_literal %type comp_expr opt_where join_condition @@ -151,6 +155,8 @@ typedef void* yyscan_t; %type ident_commalist opt_column_list %type column_def %type column_def_commalist +%type update_clause +%type update_clause_commalist /****************************** ** Token Precedence and Associativity @@ -198,6 +204,7 @@ statement: | insert_statement { $$ = $1; } | delete_statement { $$ = $1; } | truncate_statement { $$ = $1; } + | update_statement { $$ = $1; } ; @@ -316,6 +323,34 @@ opt_column_list: | /* empty */ { $$ = NULL; } ; + +/****************************** + * Update Statement + * UPDATE students SET grade = 1.3, name='Felix Fürstenberg' WHERE name = 'Max Mustermann'; + ******************************/ + +update_statement: + UPDATE table_ref_name_no_alias SET update_clause_commalist opt_where { + $$ = new UpdateStatement(); + $$->table = $2; + $$->updates = $4; + $$->where = $5; + } + ; + +update_clause_commalist: + update_clause { $$ = new List($1); } + | update_clause_commalist ',' update_clause { $1->push_back($3); $$ = $1; } + ; + +update_clause: + IDENTIFIER '=' literal { + $$ = new UpdateClause(); + $$->column = $1; + $$->value = $3; + } + ; + /****************************** * Select Statement ******************************/ @@ -539,7 +574,16 @@ table_ref_name: tbl->alias = $2; $$ = tbl; } - ; + ; + + +table_ref_name_no_alias: + table_name { + $$ = new TableRef(kTableName); + $$->name = $1; + } + ; + table_name: IDENTIFIER diff --git a/src/sql_grammar_test.cpp b/src/sql_grammar_test.cpp index b34a757..295c9b1 100644 --- a/src/sql_grammar_test.cpp +++ b/src/sql_grammar_test.cpp @@ -86,9 +86,9 @@ int main(int argc, char *argv[]) { } if (num_failed == 0) { - printf("All %lu grammar tests completed successfully!\n", queries.size()); + printf("\033[0;32m{ ok} \033[0mAll %lu grammar tests completed successfully!\n", queries.size()); } else { - fprintf(stderr, "Some grammar tests failed! %d out of %lu tests failed!\n", num_failed, queries.size()); + fprintf(stderr, "\033[0;31m{ failed} \033[0mSome grammar tests failed! %d out of %lu tests failed!\n", num_failed, queries.size()); } diff --git a/src/sql_tests.cpp b/src/sql_tests.cpp index 5543d5a..bd7bc52 100644 --- a/src/sql_tests.cpp +++ b/src/sql_tests.cpp @@ -3,14 +3,16 @@ */ #include "SQLParser.h" +#include "sqlhelper.h" #include "tests/test.h" + using namespace hsql; -TEST(SelectTest) { +TEST(Select) { StatementList* stmt_list = SQLParser::parseSQLString("SELECT * FROM students;"); ASSERT(stmt_list->isValid); ASSERT_EQ(stmt_list->size(), 1); @@ -21,7 +23,7 @@ TEST(SelectTest) { } -TEST(DeleteTest) { +TEST(Delete) { StatementList* stmt_list = SQLParser::parseSQLString("DELETE FROM students WHERE grade > 2.0;"); ASSERT(stmt_list->isValid); ASSERT_EQ(stmt_list->size(), 1); @@ -35,7 +37,7 @@ TEST(DeleteTest) { ASSERT_EQ(stmt->expr->expr2->fval, 2.0); } -TEST(CreateTable) { +TEST(Create) { StatementList* stmt_list = SQLParser::parseSQLString("CREATE TABLE students (name TEXT, student_number INTEGER, city TEXT, grade DOUBLE)"); ASSERT(stmt_list->isValid); ASSERT_EQ(stmt_list->size(), 1); @@ -50,4 +52,42 @@ TEST(CreateTable) { ASSERT_EQ(stmt->columns->at(0)->type, ColumnDefinition::TEXT); ASSERT_STREQ(stmt->columns->at(3)->name, "grade"); ASSERT_EQ(stmt->columns->at(3)->type, ColumnDefinition::DOUBLE); +} + + +TEST(Update) { + StatementList* stmt_list = SQLParser::parseSQLString("UPDATE students SET grade = 5.0, name = 'test' WHERE name = 'Max Mustermann';"); + ASSERT(stmt_list->isValid); + ASSERT_EQ(stmt_list->size(), 1); + ASSERT_EQ(stmt_list->at(0)->type, kStmtUpdate); + + UpdateStatement* stmt = (UpdateStatement*) stmt_list->at(0); + ASSERT_NOTNULL(stmt->table); + ASSERT_STREQ(stmt->table->name, "students"); + + ASSERT_NOTNULL(stmt->updates); + ASSERT_EQ(stmt->updates->size(), 2); + ASSERT_STREQ(stmt->updates->at(0)->column, "grade"); + ASSERT_STREQ(stmt->updates->at(1)->column, "name"); + ASSERT(stmt->updates->at(0)->value->isType(kExprLiteralFloat)); + ASSERT(stmt->updates->at(1)->value->isType(kExprLiteralString)); + ASSERT_EQ(stmt->updates->at(0)->value->fval, 5.0); + ASSERT_STREQ(stmt->updates->at(1)->value->name, "test"); + + ASSERT_NOTNULL(stmt->where); + ASSERT(stmt->where->isType(kExprOperator)); + ASSERT(stmt->where->isSimpleOp('=')); + ASSERT_STREQ(stmt->where->expr->name, "name"); + ASSERT_STREQ(stmt->where->expr2->name, "Max Mustermann"); + +} + + +TEST(Insert) { + StatementList* stmt_list = SQLParser::parseSQLString("INSERT INTO students VALUES ('Max Mustermann', 12345, 'Musterhausen', 2.0)"); + ASSERT(stmt_list->isValid); + ASSERT_EQ(stmt_list->size(), 1); + ASSERT_EQ(stmt_list->at(0)->type, kStmtInsert); + + // TODO } \ No newline at end of file diff --git a/src/tests/test.h b/src/tests/test.h index 87c2b69..ad038ba 100644 --- a/src/tests/test.h +++ b/src/tests/test.h @@ -1,11 +1,16 @@ #ifndef __TEST_H__ #define __TEST_H__ + +#include + + #define TEST(name) \ void name(); \ namespace g_dummy { int _##name = AddTest(name, #name); } \ void name() + #define ASSERT(cond) if (!(cond)) throw AssertionFailedException(#cond); #define ASSERT_TRUE(cond) ASSERT(cond); @@ -17,7 +22,11 @@ #define ASSERT_STREQ(a, b) \ if (std::string(a).compare(std::string(b)) != 0) throw AssertionFailedException(#a " == " #b) #define ASSERT_EQ(a, b) \ + if (a != b) { \ + std::cout << "Actual values: " << a << " != " << b << std::endl; \ + } \ ASSERT(a == b); + class AssertionFailedException: public std::exception { diff --git a/test/valid_queries.sql b/test/valid_queries.sql index b3d7e35..053354d 100644 --- a/test/valid_queries.sql +++ b/test/valid_queries.sql @@ -23,4 +23,8 @@ INSERT INTO test_table SELECT * FROM students; # DELETE DELETE FROM students WHERE grade > 3.0 DELETE FROM students -TRUNCATE students \ No newline at end of file +TRUNCATE students +# UPDATE +UPDATE students SET grade = 1.3 WHERE name = 'Max Mustermann'; +UPDATE students SET grade = 1.3, name='Felix Fürstenberg' WHERE name = 'Max Mustermann'; +UPDATE students SET grade = 1.0; \ No newline at end of file