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;
}
Expr* Expr::makePlaceholder(int id) {
Expr* e = new Expr(kExprPlaceholder);
e->ival = id;
return e;
}
Expr::~Expr() {
delete expr;
delete expr2;

View File

@ -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

View File

@ -3,6 +3,7 @@
#include "SQLStatement.h"
#include "SelectStatement.h"
#include <algorithm>
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<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;
SQLStatement* stmt;
SQLStatementList* query;
std::vector<Expr*> placeholders;
};

View File

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

View File

@ -1,6 +1,8 @@
#ifndef __PARSER_TYPEDEF_H__
#define __PARSER_TYPEDEF_H__
#include <vector>
#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<void*> placeholder_list;
};
#define HSQL_LTYPE HSQL_CUST_LTYPE

View File

@ -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]);
}