diff --git a/src/lib/Expr.h b/src/lib/Expr.h index cd7c2f4..c964c08 100644 --- a/src/lib/Expr.h +++ b/src/lib/Expr.h @@ -71,6 +71,7 @@ struct Expr { char* alias; float fval; int64_t ival; + int64_t ival2; OperatorType op_type; char op_char; diff --git a/src/lib/destructors.cpp b/src/lib/destructors.cpp index 1b9dd8e..51868c0 100644 --- a/src/lib/destructors.cpp +++ b/src/lib/destructors.cpp @@ -74,6 +74,17 @@ ColumnDefinition::~ColumnDefinition() { delete name; } + +PrepareStatement::~PrepareStatement() { + delete stmt; + delete name; +} +ExecuteStatement::~ExecuteStatement() { + delete name; + delete parameters; +} + + /** * Table.h */ diff --git a/src/lib/sqllib.h b/src/lib/sqllib.h index e2b1bc9..791a5be 100644 --- a/src/lib/sqllib.h +++ b/src/lib/sqllib.h @@ -9,5 +9,7 @@ #include "UpdateStatement.h" #include "DeleteStatement.h" #include "DropStatement.h" +#include "PrepareStatement.h" +#include "ExecuteStatement.h" #endif \ No newline at end of file diff --git a/src/lib/statements/ExecuteStatement.h b/src/lib/statements/ExecuteStatement.h new file mode 100644 index 0000000..fc3fe0c --- /dev/null +++ b/src/lib/statements/ExecuteStatement.h @@ -0,0 +1,29 @@ +#ifndef __EXECUTE_STATEMENT_H__ +#define __EXECUTE_STATEMENT_H__ + +#include "SQLStatement.h" + +namespace hsql { + + +/** + * @struct ExecuteStatement + * + */ +struct ExecuteStatement : SQLStatement { + ExecuteStatement() : + SQLStatement(kStmtExecute), + name(NULL), + parameters(NULL) {} + + virtual ~ExecuteStatement(); // defined in destructors.cpp + + const char* name; + List* parameters; +}; + + + + +} // namsepace hsql +#endif \ No newline at end of file diff --git a/src/lib/statements/PrepareStatement.h b/src/lib/statements/PrepareStatement.h new file mode 100644 index 0000000..0cbe6a5 --- /dev/null +++ b/src/lib/statements/PrepareStatement.h @@ -0,0 +1,30 @@ +#ifndef __PREPARE_STATEMENT_H__ +#define __PREPARE_STATEMENT_H__ + +#include "SQLStatement.h" +#include "SelectStatement.h" + +namespace hsql { + + +/** + * @struct PrepareStatement + * + */ +struct PrepareStatement : SQLStatement { + PrepareStatement() : + SQLStatement(kStmtPrepare), + name(NULL), + stmt(NULL) {} + + virtual ~PrepareStatement(); // defined in destructors.cpp + + const char* name; + SQLStatement* stmt; +}; + + + + +} // namsepace hsql +#endif \ No newline at end of file diff --git a/src/lib/statements/SQLStatement.h b/src/lib/statements/SQLStatement.h index bafbeb6..c8380b4 100644 --- a/src/lib/statements/SQLStatement.h +++ b/src/lib/statements/SQLStatement.h @@ -19,6 +19,8 @@ typedef enum { kStmtDelete, kStmtCreate, kStmtDrop, + kStmtPrepare, + kStmtExecute, // Following types are not supported yet kStmtExport, kStmtRename, diff --git a/src/parser/bison_parser.y b/src/parser/bison_parser.y index a9eff8c..c845575 100644 --- a/src/parser/bison_parser.y +++ b/src/parser/bison_parser.y @@ -31,12 +31,34 @@ int yyerror(YYLTYPE* llocp, SQLStatementList** result, yyscan_t scanner, const c return 0; } + + %} /********************************* ** Section 2: Bison Parser Declarations *********************************/ +// Specify code that is included in the generated .h and .c files +%code requires { +// %code requires block +#include "parser_typedef.h" + +// Auto update column and line number +#define YY_USER_ACTION \ + yylloc->first_line = yylloc->last_line; \ + yylloc->first_column = yylloc->last_column; \ + for(int i = 0; yytext[i] != '\0'; i++) { \ + if(yytext[i] == '\n') { \ + yylloc->last_line++; \ + yylloc->last_column = 0; \ + } \ + else { \ + yylloc->last_column++; \ + } \ + } +} + // Define the names of the created files %output "bison_parser.cpp" %defines "bison_parser.h" @@ -51,33 +73,15 @@ int yyerror(YYLTYPE* llocp, SQLStatementList** result, yyscan_t scanner, const c %define parse.error verbose %locations +%initial-action { + // Initialize + @$.first_column = 0; + @$.last_column = 0; + @$.first_line = 0; + @$.last_line = 0; + @$.placeholder_id = 0; +}; -// Specify code that is included in the generated .h and .c files -%code requires { - -#ifndef YYtypeDEF_YY_SCANNER_T -#define YYtypeDEF_YY_SCANNER_T -typedef void* yyscan_t; -#endif - -#define YYSTYPE HSQL_STYPE -#define YYLTYPE HSQL_LTYPE - - -#define YY_USER_ACTION \ - yylloc->first_line = yylloc->last_line; \ - yylloc->first_column = yylloc->last_column; \ - for(int i = 0; yytext[i] != '\0'; i++) { \ - if(yytext[i] == '\n') { \ - yylloc->last_line++; \ - yylloc->last_column = 0; \ - } \ - else { \ - yylloc->last_column++; \ - } \ - } - -} // Define additional parameters for yylex (http://www.gnu.org/software/bison/manual/html_node/Pure-Calling.html) %lex-param { yyscan_t scanner } @@ -98,13 +102,15 @@ typedef void* yyscan_t; bool bval; hsql::SQLStatement* statement; - hsql::SelectStatement* select_stmt; - hsql::ImportStatement* import_stmt; - hsql::CreateStatement* create_stmt; - hsql::InsertStatement* insert_stmt; - hsql::DeleteStatement* delete_stmt; - hsql::UpdateStatement* update_stmt; - hsql::DropStatement* drop_stmt; + hsql::SelectStatement* select_stmt; + hsql::ImportStatement* import_stmt; + hsql::CreateStatement* create_stmt; + hsql::InsertStatement* insert_stmt; + hsql::DeleteStatement* delete_stmt; + hsql::UpdateStatement* update_stmt; + hsql::DropStatement* drop_stmt; + hsql::PrepareStatement* prep_stmt; + hsql::ExecuteStatement* exec_stmt; hsql::TableRef* table; hsql::Expr* expr; @@ -154,7 +160,8 @@ typedef void* yyscan_t; *********************************/ %type statement_list %type statement preparable_statement -%type prepare_statement execute_statement +%type execute_statement +%type prepare_statement %type select_statement select_with_paren select_no_paren select_clause %type import_statement %type create_statement @@ -222,7 +229,7 @@ statement_list: statement: preparable_statement - | prepare_statement + | prepare_statement { $$ = $1; } ; @@ -243,11 +250,19 @@ preparable_statement: * Prepared Statement ******************************/ prepare_statement: - PREPARE IDENTIFIER ':' preparable_statement { $$ = NULL; } + PREPARE IDENTIFIER ':' preparable_statement { + $$ = new PrepareStatement(); + $$->name = $2; + $$->stmt = $4; + } ; execute_statement: - EXECUTE IDENTIFIER '(' literal_list ')' { $$ = NULL; } + EXECUTE IDENTIFIER '(' literal_list ')' { + $$ = new ExecuteStatement(); + $$->name = $2; + $$->parameters = $4; + } ; @@ -586,7 +601,7 @@ star_expr: /* TODO: keep list of placeholders */ placeholder_expr: - '?' { $$ = new Expr(kExprPlaceholder); } + '?' { $$ = new Expr(kExprPlaceholder); $$->ival = yylloc.placeholder_id++; } ; diff --git a/src/parser/parser_typedef.h b/src/parser/parser_typedef.h new file mode 100644 index 0000000..1cdb83f --- /dev/null +++ b/src/parser/parser_typedef.h @@ -0,0 +1,29 @@ +#ifndef __PARSER_TYPEDEF_H__ +#define __PARSER_TYPEDEF_H__ + + +#ifndef YYtypeDEF_YY_SCANNER_T +#define YYtypeDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + + +#define YYSTYPE HSQL_STYPE +#define YYLTYPE HSQL_LTYPE + + +struct HSQL_CUST_LTYPE { + int first_line; + int first_column; + int last_line; + int last_column; + int placeholder_id; +}; + +#define HSQL_LTYPE HSQL_CUST_LTYPE +#define HSQL_LTYPE_IS_DECLARED 1 + + + + +#endif \ No newline at end of file diff --git a/src/sql_tests.cpp b/src/sql_tests.cpp index 9118960..3645aa5 100644 --- a/src/sql_tests.cpp +++ b/src/sql_tests.cpp @@ -8,7 +8,16 @@ using namespace hsql; -TEST(Delete) { +#define PARSE_SINGLE_SQL(query, stmt_type, stmt_class, output_var) \ + SQLStatementList* stmt_list = SQLParser::parseSQLString(query); \ + ASSERT(stmt_list->isValid); \ + ASSERT_EQ(stmt_list->size(), 1); \ + ASSERT_EQ(stmt_list->at(0)->type(), stmt_type); \ + stmt_class* output_var = (stmt_class*) stmt_list->at(0); + + + +TEST(DeleteStatementTest) { SQLStatementList* stmt_list = SQLParser::parseSQLString("DELETE FROM students WHERE grade > 2.0;"); ASSERT(stmt_list->isValid); ASSERT_EQ(stmt_list->size(), 1); @@ -22,7 +31,7 @@ TEST(Delete) { ASSERT_EQ(stmt->expr->expr2->fval, 2.0); } -TEST(Create) { +TEST(CreateStatementTest) { SQLStatementList* 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); @@ -44,7 +53,7 @@ TEST(Create) { } -TEST(Update) { +TEST(UpdateStatementTest) { SQLStatementList* 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); @@ -72,7 +81,7 @@ TEST(Update) { } -TEST(Insert) { +TEST(InsertStatementTest) { SQLStatementList* stmt_list = SQLParser::parseSQLString("INSERT INTO students VALUES ('Max Mustermann', 12345, 'Musterhausen', 2.0)"); ASSERT(stmt_list->isValid); ASSERT_EQ(stmt_list->size(), 1); @@ -82,7 +91,7 @@ TEST(Insert) { } -TEST(DropTable) { +TEST(DropTableStatementTest) { SQLStatementList* stmt_list = SQLParser::parseSQLString("DROP TABLE students"); ASSERT(stmt_list->isValid); ASSERT_EQ(stmt_list->size(), 1); @@ -92,4 +101,27 @@ TEST(DropTable) { ASSERT_EQ(stmt->type, DropStatement::kTable); ASSERT_NOTNULL(stmt->name); ASSERT_STREQ(stmt->name, "students"); +} + + +TEST(PrepareStatementTest) { + PARSE_SINGLE_SQL("PREPARE test: SELECT ?, test FROM t2 WHERE c1 = ?;", kStmtPrepare, PrepareStatement, prep_stmt); + + ASSERT_EQ(prep_stmt->stmt->type(), kStmtSelect); + ASSERT_STREQ(prep_stmt->name, "test"); + + SelectStatement* stmt = (SelectStatement*) prep_stmt->stmt; + ASSERT(stmt->select_list->at(0)->isType(kExprPlaceholder)); + ASSERT(stmt->where_clause->expr2->isType(kExprPlaceholder)); + // Check IDs of Placehoders + ASSERT_EQ(stmt->select_list->at(0)->ival, 0); + ASSERT_EQ(stmt->where_clause->expr2->ival, 1); +} + + +TEST(ExecuteStatementTest) { + PARSE_SINGLE_SQL("EXECUTE test(1, 2);", kStmtExecute, ExecuteStatement, stmt); + + ASSERT_STREQ(stmt->name, "test"); + ASSERT_EQ(stmt->parameters->size(), 2); } \ No newline at end of file diff --git a/test/valid_queries.sql b/test/valid_queries.sql index cd295ad..156ae5b 100644 --- a/test/valid_queries.sql +++ b/test/valid_queries.sql @@ -31,5 +31,5 @@ UPDATE students SET grade = 1.0; # DROP DROP TABLE students; # PREPARE -PREPARE prep_inst: INSERT INTO test VALUES (?, ?, ?); SELECT * FROM test; +PREPARE prep_inst: INSERT INTO test VALUES (?, ?, ?); EXECUTE prep_inst(1, 2, 3); \ No newline at end of file