added parsing of prepare and execute statement

This commit is contained in:
Pedro 2014-12-15 16:43:32 +01:00
parent 6f1a2821b2
commit 21503300ca
10 changed files with 195 additions and 44 deletions

View File

@ -71,6 +71,7 @@ struct Expr {
char* alias;
float fval;
int64_t ival;
int64_t ival2;
OperatorType op_type;
char op_char;

View File

@ -74,6 +74,17 @@ ColumnDefinition::~ColumnDefinition() {
delete name;
}
PrepareStatement::~PrepareStatement() {
delete stmt;
delete name;
}
ExecuteStatement::~ExecuteStatement() {
delete name;
delete parameters;
}
/**
* Table.h
*/

View File

@ -9,5 +9,7 @@
#include "UpdateStatement.h"
#include "DeleteStatement.h"
#include "DropStatement.h"
#include "PrepareStatement.h"
#include "ExecuteStatement.h"
#endif

View File

@ -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<Expr*>* parameters;
};
} // namsepace hsql
#endif

View File

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

View File

@ -19,6 +19,8 @@ typedef enum {
kStmtDelete,
kStmtCreate,
kStmtDrop,
kStmtPrepare,
kStmtExecute,
// Following types are not supported yet
kStmtExport,
kStmtRename,

View File

@ -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 <stmt_list> statement_list
%type <statement> statement preparable_statement
%type <statement> prepare_statement execute_statement
%type <exec_stmt> execute_statement
%type <prep_stmt> prepare_statement
%type <select_stmt> select_statement select_with_paren select_no_paren select_clause
%type <import_stmt> import_statement
%type <create_stmt> 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++; }
;

View File

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

View File

@ -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);
@ -93,3 +102,26 @@ TEST(DropTable) {
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);
}

View File

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