diff --git a/src/lib/Expr.cpp b/src/lib/Expr.cpp index c919ec4..f8ec75d 100644 --- a/src/lib/Expr.cpp +++ b/src/lib/Expr.cpp @@ -86,6 +86,12 @@ Expr* Expr::makeFunctionRef(char* func_name, Expr* expr, bool distinct) { return e; } +Expr* Expr::makePlaceholder(int id) { + Expr* e = new Expr(kExprPlaceholder); + e->ival = id; + return e; +} + Expr::~Expr() { delete expr; delete expr2; diff --git a/src/lib/Expr.h b/src/lib/Expr.h index 4588c80..b66dac3 100644 --- a/src/lib/Expr.h +++ b/src/lib/Expr.h @@ -112,6 +112,8 @@ struct Expr { static Expr* makeColumnRef(char* name); static Expr* makeColumnRef(char* table, char* name); static Expr* makeFunctionRef(char* func_name, Expr* expr, bool distinct); + + static Expr* makePlaceholder(int id); }; // Zero initializes an Expr object and assigns it to a space in the heap diff --git a/src/lib/statements/PrepareStatement.h b/src/lib/statements/PrepareStatement.h index 4688d60..cf050fb 100644 --- a/src/lib/statements/PrepareStatement.h +++ b/src/lib/statements/PrepareStatement.h @@ -3,6 +3,7 @@ #include "SQLStatement.h" #include "SelectStatement.h" +#include namespace hsql { @@ -15,15 +16,34 @@ struct PrepareStatement : SQLStatement { PrepareStatement() : SQLStatement(kStmtPrepare), name(NULL), - stmt(NULL) {} + query(NULL) {} virtual ~PrepareStatement() { - delete stmt; + delete query; delete name; } + /** + * @param vector of placeholders that the parser found + * + * When setting the placeholders we need to make sure that they are in the correct order. + * To ensure that, during parsing we store the character position use that to sort the list here. + */ + void setPlaceholders(std::vector ph) { + for (void* e : ph) { + if (e != NULL) + placeholders.push_back((Expr*) e); + } + // Sort by col-id + std::sort(placeholders.begin(), placeholders.end(), [](Expr* i, Expr* j) -> bool { return (i->ival < j->ival); }); + + // Set the placeholder id on the Expr. This replaces the previously stored column id + for (uint i = 0; i < placeholders.size(); ++i) placeholders[i]->ival = i; + } + const char* name; - SQLStatement* stmt; + SQLStatementList* query; + std::vector placeholders; }; diff --git a/src/lib/statements/SQLStatement.h b/src/lib/statements/SQLStatement.h index 7ab818e..3ef79d0 100644 --- a/src/lib/statements/SQLStatement.h +++ b/src/lib/statements/SQLStatement.h @@ -6,6 +6,8 @@ #define __STATEMENT_H__ #include "List.h" +#include "Expr.h" +#include namespace hsql { @@ -63,7 +65,7 @@ public: virtual ~SQLStatementList() { delete parser_msg; } - + bool isValid; const char* parser_msg; int error_line; diff --git a/src/parser/bison_parser.y b/src/parser/bison_parser.y index 874c4d6..ae5d6d0 100644 --- a/src/parser/bison_parser.y +++ b/src/parser/bison_parser.y @@ -49,6 +49,7 @@ int yyerror(YYLTYPE* llocp, SQLStatementList** result, yyscan_t scanner, const c yylloc->first_line = yylloc->last_line; \ yylloc->first_column = yylloc->last_column; \ for(int i = 0; yytext[i] != '\0'; i++) { \ + yylloc->total_column++; \ if(yytext[i] == '\n') { \ yylloc->last_line++; \ yylloc->last_column = 0; \ @@ -79,6 +80,7 @@ int yyerror(YYLTYPE* llocp, SQLStatementList** result, yyscan_t scanner, const c @$.last_column = 0; @$.first_line = 0; @$.last_line = 0; + @$.total_column = 0; @$.placeholder_id = 0; }; @@ -220,7 +222,13 @@ int yyerror(YYLTYPE* llocp, SQLStatementList** result, yyscan_t scanner, const c // Defines our general input. input: - statement_list opt_semicolon { *result = $1; } + prepare_statement { + $1->setPlaceholders(yyloc.placeholder_list); + *result = new SQLStatementList($1); + } + | statement_list opt_semicolon { + *result = $1; + } ; @@ -231,7 +239,6 @@ statement_list: statement: preparable_statement - | prepare_statement { $$ = $1; } ; @@ -252,10 +259,10 @@ preparable_statement: * Prepared Statement ******************************/ prepare_statement: - PREPARE IDENTIFIER ':' preparable_statement { + PREPARE IDENTIFIER ':' statement_list opt_semicolon { $$ = new PrepareStatement(); $$->name = $2; - $$->stmt = $4; + $$->query = $4; } ; @@ -615,7 +622,10 @@ star_expr: /* TODO: keep list of placeholders */ placeholder_expr: - '?' { $$ = new Expr(kExprPlaceholder); $$->ival = yylloc.placeholder_id++; } + '?' { + $$ = Expr::makePlaceholder(yylloc.total_column); + yyloc.placeholder_list.push_back($$); + } ; diff --git a/src/parser/parser_typedef.h b/src/parser/parser_typedef.h index 1cdb83f..70c9322 100644 --- a/src/parser/parser_typedef.h +++ b/src/parser/parser_typedef.h @@ -1,6 +1,8 @@ #ifndef __PARSER_TYPEDEF_H__ #define __PARSER_TYPEDEF_H__ +#include + #ifndef YYtypeDEF_YY_SCANNER_T #define YYtypeDEF_YY_SCANNER_T @@ -17,7 +19,12 @@ struct HSQL_CUST_LTYPE { int first_column; int last_line; int last_column; + + int total_column; + + // Placeholder int placeholder_id; + std::vector placeholder_list; }; #define HSQL_LTYPE HSQL_CUST_LTYPE diff --git a/src/sql_tests.cpp b/src/sql_tests.cpp index 52885b2..4940d2e 100644 --- a/src/sql_tests.cpp +++ b/src/sql_tests.cpp @@ -98,17 +98,36 @@ TEST(DropTableStatementTest) { TEST(PrepareStatementTest) { - TEST_PARSE_SINGLE_SQL("PREPARE test: SELECT ?, test FROM t2 WHERE c1 = ?;", kStmtPrepare, PrepareStatement, prep_stmt); + TEST_PARSE_SINGLE_SQL("PREPARE test:" + "INSERT INTO test VALUES(?);" + "SELECT ?, test FROM test WHERE c1 = ?;" + "", kStmtPrepare, PrepareStatement, prep_stmt); - ASSERT_EQ(prep_stmt->stmt->type(), kStmtSelect); ASSERT_STREQ(prep_stmt->name, "test"); + ASSERT_EQ(prep_stmt->placeholders.size(), 3); - 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); + + ASSERT_EQ(prep_stmt->query->size(), 2); + ASSERT_EQ(prep_stmt->query->at(0)->type(), kStmtInsert); + ASSERT_EQ(prep_stmt->query->at(1)->type(), kStmtSelect); + + + InsertStatement* insert = (InsertStatement*) prep_stmt->query->at(0); + SelectStatement* select = (SelectStatement*) prep_stmt->query->at(1); + + ASSERT(insert->values->at(0)->isType(kExprPlaceholder)); + ASSERT(select->select_list->at(0)->isType(kExprPlaceholder)); + ASSERT(select->where_clause->expr2->isType(kExprPlaceholder)); + + // Check IDs of placeholders + ASSERT_EQ(insert->values->at(0)->ival, 0); + ASSERT_EQ(insert->values->at(0), prep_stmt->placeholders[0]); + + ASSERT_EQ(select->select_list->at(0)->ival, 1); + ASSERT_EQ(select->select_list->at(0), prep_stmt->placeholders[1]); + + ASSERT_EQ(select->where_clause->expr2->ival, 2); + ASSERT_EQ(select->where_clause->expr2, prep_stmt->placeholders[2]); }