diff --git a/README.md b/README.md index 3cb3796..f21f59e 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,10 @@ To create the full parser code run `make build`. The parser library code is crea To use the SQL Parser in your own code, you only need to include `SQLParser.h` and build+link all the source files from the parser with your project. See `hyrise/src/lib/access/sql/SQLQueryParser.cpp` for how it's used in Hyrise. +### Run tests + +To execute all tests run: `./run_tests.sh`. + ### Update in Hyrise Run `./deploy_to_hyris.sh path/to/hyrise` to update the SQL parser within Hyrise. diff --git a/frontend-hyrise/index.html b/frontend-hyrise/index.html index 87f0514..f6be1fc 100644 --- a/frontend-hyrise/index.html +++ b/frontend-hyrise/index.html @@ -177,7 +177,7 @@ $(function() { }); addBuggyQuery('GROUP', 'SELECT AVG(grade) FROM (SELECT city, AVG(grade) FROM students GROUP BY city) t1'); - addBuggyQuery('UNION', 'SELECT name FROM students WHERE grade > 2.0 UNION SELECT name FROM students'); + addBuggyQuery('UNION (kills hyrise)', 'SELECT name FROM students WHERE grade > 2.0 UNION SELECT name FROM students'); ////////////////////////////////////////////////// diff --git a/frontend-hyrise/sample-queries.sql b/frontend-hyrise/sample-queries.sql index 9242b7e..f8c9d78 100644 --- a/frontend-hyrise/sample-queries.sql +++ b/frontend-hyrise/sample-queries.sql @@ -1,6 +1,6 @@ # Load Tables CREATE TABLE IF NOT EXISTS students FROM TBL FILE 'test/students.tbl'; -CREATE TABLE IF NOT EXISTS test FROM TBL FILE 'test/lin_xxs.tbl'; +CREATE TABLE IF NOT EXISTS test_big FROM TBL FILE 'test/lin_xxs.tbl'; CREATE TABLE IF NOT EXISTS companies FROM TBL FILE 'test/tables/companies.tbl'; CREATE TABLE IF NOT EXISTS employees FROM TBL FILE 'test/tables/employees.tbl'; # SELECT @@ -14,4 +14,15 @@ SELECT * FROM students WHERE grade = 1.3 UNION SELECT * FROM students WHERE grad # JOIN SELECT * FROM companies JOIN employees ON company_id = employee_company_id; # INSERT -INSERT INTO students VALUES ('Max', 10101, 'Musterhausen', 3.3); \ No newline at end of file +INSERT INTO students VALUES ('Max Mustermann', 10101, 'Musterhausen', 1.7); +# CREATE +CREATE TABLE IF NOT EXISTS test (v1 INTEGER, v2 INTEGER, v3 INTEGER); +# INSERT/SELECT +INSERT INTO test VALUES (1, 23, 45);| +INSERT INTO test VALUES (1, 23, 45); +INSERT INTO test VALUES (1, 23, 45); +SELECT * FROM test; +# CREATE/INSERT/SELECT +CREATE TABLE IF NOT EXISTS test (v1 INTEGER, v2 INTEGER, v3 INTEGER); +INSERT INTO test VALUES (1, 12, 43); +SELECT * FROM test; \ No newline at end of file diff --git a/run_tests.sh b/run_tests.sh index 8df5f8e..ec18e0f 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -1,8 +1,9 @@ #!/bin/sh echo "Compiling..." -make clean -C src/ >/dev/null -make tests -C src/ >/dev/null -make grammar_test -C src/ >/dev/null +make clean -C src/ >/dev/null || exit 1 +make tests -C src/ >/dev/null || exit 1 +make grammar_test -C src/ >/dev/null || exit 1 + echo "Running tests:" ./bin/grammar_test -f "test/valid_queries.sql" ./bin/tests diff --git a/src/Makefile b/src/Makefile index eff5f64..6fcf897 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,10 +1,14 @@ +BUILD_DIR = ../build/ +BIN_DIR = ../bin + CC = g++ CFLAGS = -O3 -Ilib/ -I./ -Iparser/ -std=c++11 -pthread -Wall -g -SOURCES = $(shell find lib/ -name '*.cpp') parser/bison_parser.cpp parser/flex_lexer.cpp parser/SQLParser.cpp -BUILD_DIR = ../build/ -BIN_DIR = ../bin + +LIB_SOURCES = $(shell find lib/ -name '*.cpp') parser/bison_parser.cpp parser/flex_lexer.cpp parser/SQLParser.cpp +TEST_SOURCES = $(shell find tests/ -name '*.cpp') + # release build is always using bison @@ -16,16 +20,16 @@ build: clean cp parser/*.cpp $(BUILD_DIR) -analysis: $(SOURCES) sql_analysis.cpp - $(CC) $(CFLAGS) $(SOURCES) sql_analysis.cpp -o $(BIN_DIR)/analysis +analysis: $(LIB_SOURCES) sql_analysis.cpp + $(CC) $(CFLAGS) $(LIB_SOURCES) sql_analysis.cpp -o $(BIN_DIR)/analysis -grammar_test: $(SOURCES) sql_grammar_test.cpp - $(CC) $(CFLAGS) $(SOURCES) sql_grammar_test.cpp -o $(BIN_DIR)/grammar_test +grammar_test: $(LIB_SOURCES) sql_grammar_test.cpp + $(CC) $(CFLAGS) $(LIB_SOURCES) sql_grammar_test.cpp -o $(BIN_DIR)/grammar_test -tests: $(SOURCES) $(TESTS_MAIN) - $(CC) $(CFLAGS) $(SOURCES) sql_tests.cpp -o $(BIN_DIR)/tests +tests: $(LIB_SOURCES) $(TEST_SOURCES) sql_tests.cpp + $(CC) $(CFLAGS) $(LIB_SOURCES) $(TEST_SOURCES) sql_tests.cpp -o $(BIN_DIR)/tests parser/bison_parser.cpp: diff --git a/src/parser/bison_parser.y b/src/parser/bison_parser.y index fd80cbc..238d36f 100644 --- a/src/parser/bison_parser.y +++ b/src/parser/bison_parser.y @@ -108,8 +108,8 @@ typedef void* yyscan_t; ** Token Definition *********************************/ %token IDENTIFIER STRING -%token FLOAT -%token INT +%token FLOATVAL +%token INTVAL %token NOTEQUALS LESSEQ GREATEREQ /* SQL Keywords */ @@ -124,7 +124,7 @@ typedef void* yyscan_t; %token LIMIT LOCAL MERGE MINUS ORDER OUTER RIGHT TABLE UNION %token USING WHERE CALL DATE DESC DROP FILE FROM FULL HASH %token INTO JOIN LEFT LIKE LOAD NULL PART PLAN SHOW TEXT -%token TIME VIEW WITH ADD ALL AND ASC CSV FOR KEY NOT OFF +%token TIME VIEW WITH ADD ALL AND ASC CSV FOR INT KEY NOT OFF %token SET TBL TOP AS BY IF IN IS OF ON OR TO @@ -268,8 +268,10 @@ column_def: } ; + column_type: - INTEGER { $$ = ColumnDefinition::INT; } + INT { $$ = ColumnDefinition::INT; } + | INTEGER { $$ = ColumnDefinition::INT; } | DOUBLE { $$ = ColumnDefinition::DOUBLE; } | TEXT { $$ = ColumnDefinition::TEXT; } ; @@ -522,12 +524,12 @@ string_literal: num_literal: - FLOAT { $$ = Expr::makeLiteral($1); } + FLOATVAL { $$ = Expr::makeLiteral($1); } | int_literal ; int_literal: - INT { $$ = Expr::makeLiteral($1); } + INTVAL { $$ = Expr::makeLiteral($1); } ; star_expr: diff --git a/src/parser/flex_lexer.l b/src/parser/flex_lexer.l index d35ae94..2193268 100644 --- a/src/parser/flex_lexer.l +++ b/src/parser/flex_lexer.l @@ -149,6 +149,7 @@ AND TOKEN(AND) ASC TOKEN(ASC) CSV TOKEN(CSV) FOR TOKEN(FOR) +INT TOKEN(INT) KEY TOKEN(KEY) NOT TOKEN(NOT) OFF TOKEN(OFF) @@ -177,12 +178,12 @@ TO TOKEN(TO) [0-9]+"."[0-9]* | "."[0-9]* { yylval->fval = atof(yytext); - return SQL_FLOAT; + return SQL_FLOATVAL; } [0-9]+ { yylval->ival = atol(yytext); - return SQL_INT; + return SQL_INTVAL; } \"[^\"\n]+\" { diff --git a/src/parser/sql_keywords.txt b/src/parser/sql_keywords.txt index 81481e5..c38766f 100644 --- a/src/parser/sql_keywords.txt +++ b/src/parser/sql_keywords.txt @@ -147,6 +147,7 @@ DATE TIME TIMESTAMP INTEGER +INT DOUBLE NVARCHAR TEXT diff --git a/src/sql_tests.cpp b/src/sql_tests.cpp index bd7bc52..60c9419 100644 --- a/src/sql_tests.cpp +++ b/src/sql_tests.cpp @@ -2,27 +2,12 @@ * sql_tests.cpp */ +#include "tests/test.h" #include "SQLParser.h" #include "sqlhelper.h" -#include "tests/test.h" - - using namespace hsql; - - -TEST(Select) { - StatementList* stmt_list = SQLParser::parseSQLString("SELECT * FROM students;"); - ASSERT(stmt_list->isValid); - ASSERT_EQ(stmt_list->size(), 1); - ASSERT(stmt_list->at(0)->type == kStmtSelect); - - SelectStatement* stmt = (SelectStatement*) stmt_list->at(0); - ASSERT_NULL(stmt->where_clause); -} - - TEST(Delete) { StatementList* stmt_list = SQLParser::parseSQLString("DELETE FROM students WHERE grade > 2.0;"); ASSERT(stmt_list->isValid); @@ -38,7 +23,7 @@ TEST(Delete) { } TEST(Create) { - StatementList* stmt_list = SQLParser::parseSQLString("CREATE TABLE students (name TEXT, student_number INTEGER, city TEXT, grade DOUBLE)"); + StatementList* stmt_list = SQLParser::parseSQLString("CREATE TABLE students (name TEXT, student_number INT, city INTEGER, grade DOUBLE)"); ASSERT(stmt_list->isValid); ASSERT_EQ(stmt_list->size(), 1); ASSERT_EQ(stmt_list->at(0)->type, kStmtCreate); @@ -49,8 +34,12 @@ TEST(Create) { ASSERT_NOTNULL(stmt->columns); ASSERT_EQ(stmt->columns->size(), 4); ASSERT_STREQ(stmt->columns->at(0)->name, "name"); - ASSERT_EQ(stmt->columns->at(0)->type, ColumnDefinition::TEXT); + ASSERT_STREQ(stmt->columns->at(1)->name, "student_number"); + ASSERT_STREQ(stmt->columns->at(2)->name, "city"); ASSERT_STREQ(stmt->columns->at(3)->name, "grade"); + ASSERT_EQ(stmt->columns->at(0)->type, ColumnDefinition::TEXT); + ASSERT_EQ(stmt->columns->at(1)->type, ColumnDefinition::INT); + ASSERT_EQ(stmt->columns->at(2)->type, ColumnDefinition::INT); ASSERT_EQ(stmt->columns->at(3)->type, ColumnDefinition::DOUBLE); } diff --git a/src/tests/select.cpp b/src/tests/select.cpp new file mode 100644 index 0000000..5073cfc --- /dev/null +++ b/src/tests/select.cpp @@ -0,0 +1,15 @@ + +#include "test.h" +#include "SQLParser.h" + +using namespace hsql; + +TEST(Select) { + StatementList* stmt_list = SQLParser::parseSQLString("SELECT * FROM students;"); + ASSERT(stmt_list->isValid); + ASSERT_EQ(stmt_list->size(), 1); + ASSERT(stmt_list->at(0)->type == kStmtSelect); + + SelectStatement* stmt = (SelectStatement*) stmt_list->at(0); + ASSERT_NULL(stmt->where_clause); +} diff --git a/src/tests/test.cpp b/src/tests/test.cpp new file mode 100644 index 0000000..949bf04 --- /dev/null +++ b/src/tests/test.cpp @@ -0,0 +1,55 @@ + +#include "test.h" + + +class TestsManager { + // Note: static initialization fiasco + // http://www.parashift.com/c++-faq-lite/static-init-order.html + // http://www.parashift.com/c++-faq-lite/static-init-order-on-first-use.html +public: + static std::vector& test_names() { + static std::vector* test_names = new std::vector; + return *test_names; + } + + static std::vector& tests() { + static std::vector* tests = new std::vector; + return *tests; + } +}; + + + +int AddTest(void (*foo)(void), std::string name) { + TestsManager::tests().push_back(foo); + TestsManager::test_names().push_back(name); + return 0; +} + + + +void RunTests() { + size_t num_failed = 0; + for (size_t i = 0; i < TestsManager::tests().size(); ++i) { + printf("\033[0;32m{ running}\033[0m %s\n", TestsManager::test_names()[i].c_str()); + + try { + // Run test + (*TestsManager::tests()[i])(); + printf("\033[0;32m{ ok}\033[0m %s\n", TestsManager::test_names()[i].c_str()); + + } catch (AssertionFailedException& e) { + printf("\033[1;31m{ failed} %s\n", TestsManager::test_names()[i].c_str()); + printf("\tAssertion failed: %s\n\033[0m", e.what()); + num_failed++; + } + + } +} + + + +int main() { + RunTests(); + return 0; +} \ No newline at end of file diff --git a/src/tests/test.h b/src/tests/test.h index ad038ba..274e96c 100644 --- a/src/tests/test.h +++ b/src/tests/test.h @@ -1,7 +1,8 @@ #ifndef __TEST_H__ #define __TEST_H__ - +#include +#include #include @@ -44,40 +45,9 @@ protected: }; -std::vector g_test_names; -std::vector g_tests; - -int AddTest(void (*foo)(void), std::string name) { - g_tests.push_back(foo); - g_test_names.push_back(name); - return 0; -} -void RunTests() { - size_t num_failed = 0; - for (size_t i = 0; i < g_tests.size(); ++i) { - printf("\033[0;32m{ running}\033[0m %s\n", g_test_names[i].c_str()); - - try { - // Run test - (*g_tests[i])(); - printf("\033[0;32m{ ok}\033[0m %s\n", g_test_names[i].c_str()); - - } catch (AssertionFailedException& e) { - printf("\033[1;31m{ failed} %s\n", g_test_names[i].c_str()); - printf("\tAssertion failed: %s\n\033[0m", e.what()); - num_failed++; - } - - } -} - - -int main() { - RunTests(); - return 0; -} +int AddTest(void (*foo)(void), std::string name);