improved prepared statements

This commit is contained in:
Pedro 2015-01-06 15:29:18 +01:00
parent 8abfd6094e
commit fed174a892
7 changed files with 83 additions and 17 deletions

View File

@ -86,6 +86,12 @@ Expr* Expr::makeFunctionRef(char* func_name, Expr* expr, bool distinct) {
return e; return e;
} }
Expr* Expr::makePlaceholder(int id) {
Expr* e = new Expr(kExprPlaceholder);
e->ival = id;
return e;
}
Expr::~Expr() { Expr::~Expr() {
delete expr; delete expr;
delete expr2; delete expr2;

View File

@ -112,6 +112,8 @@ struct Expr {
static Expr* makeColumnRef(char* name); static Expr* makeColumnRef(char* name);
static Expr* makeColumnRef(char* table, char* name); static Expr* makeColumnRef(char* table, char* name);
static Expr* makeFunctionRef(char* func_name, Expr* expr, bool distinct); 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 // Zero initializes an Expr object and assigns it to a space in the heap

View File

@ -3,6 +3,7 @@
#include "SQLStatement.h" #include "SQLStatement.h"
#include "SelectStatement.h" #include "SelectStatement.h"
#include <algorithm>
namespace hsql { namespace hsql {
@ -15,15 +16,34 @@ struct PrepareStatement : SQLStatement {
PrepareStatement() : PrepareStatement() :
SQLStatement(kStmtPrepare), SQLStatement(kStmtPrepare),
name(NULL), name(NULL),
stmt(NULL) {} query(NULL) {}
virtual ~PrepareStatement() { virtual ~PrepareStatement() {
delete stmt; delete query;
delete name; 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<void*> 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; const char* name;
SQLStatement* stmt; SQLStatementList* query;
std::vector<Expr*> placeholders;
}; };

View File

@ -6,6 +6,8 @@
#define __STATEMENT_H__ #define __STATEMENT_H__
#include "List.h" #include "List.h"
#include "Expr.h"
#include <vector>
namespace hsql { namespace hsql {
@ -63,7 +65,7 @@ public:
virtual ~SQLStatementList() { virtual ~SQLStatementList() {
delete parser_msg; delete parser_msg;
} }
bool isValid; bool isValid;
const char* parser_msg; const char* parser_msg;
int error_line; int error_line;

View File

@ -49,6 +49,7 @@ int yyerror(YYLTYPE* llocp, SQLStatementList** result, yyscan_t scanner, const c
yylloc->first_line = yylloc->last_line; \ yylloc->first_line = yylloc->last_line; \
yylloc->first_column = yylloc->last_column; \ yylloc->first_column = yylloc->last_column; \
for(int i = 0; yytext[i] != '\0'; i++) { \ for(int i = 0; yytext[i] != '\0'; i++) { \
yylloc->total_column++; \
if(yytext[i] == '\n') { \ if(yytext[i] == '\n') { \
yylloc->last_line++; \ yylloc->last_line++; \
yylloc->last_column = 0; \ yylloc->last_column = 0; \
@ -79,6 +80,7 @@ int yyerror(YYLTYPE* llocp, SQLStatementList** result, yyscan_t scanner, const c
@$.last_column = 0; @$.last_column = 0;
@$.first_line = 0; @$.first_line = 0;
@$.last_line = 0; @$.last_line = 0;
@$.total_column = 0;
@$.placeholder_id = 0; @$.placeholder_id = 0;
}; };
@ -220,7 +222,13 @@ int yyerror(YYLTYPE* llocp, SQLStatementList** result, yyscan_t scanner, const c
// Defines our general input. // Defines our general input.
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: statement:
preparable_statement preparable_statement
| prepare_statement { $$ = $1; }
; ;
@ -252,10 +259,10 @@ preparable_statement:
* Prepared Statement * Prepared Statement
******************************/ ******************************/
prepare_statement: prepare_statement:
PREPARE IDENTIFIER ':' preparable_statement { PREPARE IDENTIFIER ':' statement_list opt_semicolon {
$$ = new PrepareStatement(); $$ = new PrepareStatement();
$$->name = $2; $$->name = $2;
$$->stmt = $4; $$->query = $4;
} }
; ;
@ -615,7 +622,10 @@ star_expr:
/* TODO: keep list of placeholders */ /* TODO: keep list of placeholders */
placeholder_expr: placeholder_expr:
'?' { $$ = new Expr(kExprPlaceholder); $$->ival = yylloc.placeholder_id++; } '?' {
$$ = Expr::makePlaceholder(yylloc.total_column);
yyloc.placeholder_list.push_back($$);
}
; ;

View File

@ -1,6 +1,8 @@
#ifndef __PARSER_TYPEDEF_H__ #ifndef __PARSER_TYPEDEF_H__
#define __PARSER_TYPEDEF_H__ #define __PARSER_TYPEDEF_H__
#include <vector>
#ifndef YYtypeDEF_YY_SCANNER_T #ifndef YYtypeDEF_YY_SCANNER_T
#define YYtypeDEF_YY_SCANNER_T #define YYtypeDEF_YY_SCANNER_T
@ -17,7 +19,12 @@ struct HSQL_CUST_LTYPE {
int first_column; int first_column;
int last_line; int last_line;
int last_column; int last_column;
int total_column;
// Placeholder
int placeholder_id; int placeholder_id;
std::vector<void*> placeholder_list;
}; };
#define HSQL_LTYPE HSQL_CUST_LTYPE #define HSQL_LTYPE HSQL_CUST_LTYPE

View File

@ -98,17 +98,36 @@ TEST(DropTableStatementTest) {
TEST(PrepareStatementTest) { 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_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_EQ(prep_stmt->query->size(), 2);
ASSERT(stmt->where_clause->expr2->isType(kExprPlaceholder)); ASSERT_EQ(prep_stmt->query->at(0)->type(), kStmtInsert);
// Check IDs of Placehoders ASSERT_EQ(prep_stmt->query->at(1)->type(), kStmtSelect);
ASSERT_EQ(stmt->select_list->at(0)->ival, 0);
ASSERT_EQ(stmt->where_clause->expr2->ival, 1);
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]);
} }