Prepared Statements (#43)
Changed PREPARE syntax to be closer to the standard.
This commit is contained in:
parent
5e6cd2d84f
commit
f85a5e7b52
10
Makefile
10
Makefile
|
@ -19,14 +19,19 @@ LIBFLAGS = -shared
|
||||||
TARGET = libsqlparser.so
|
TARGET = libsqlparser.so
|
||||||
INSTALL = /usr/local
|
INSTALL = /usr/local
|
||||||
|
|
||||||
CTESTFLAGS = -Wall -Isrc/ -Itest/ -L./ -std=c++11 -lstdc++ -O3
|
CTESTFLAGS = -Wall -Isrc/ -Itest/ -L./ -std=c++11 -lstdc++
|
||||||
|
|
||||||
# Set compile mode to -g or -O3.
|
# Set compile mode to -g or -O3.
|
||||||
|
MODE_LOG = ""
|
||||||
mode ?= release
|
mode ?= release
|
||||||
ifeq ($(mode), debug)
|
ifeq ($(mode), debug)
|
||||||
CFLAGS += -g
|
CFLAGS += -g
|
||||||
|
CTESTFLAGS += -g
|
||||||
|
MODE_LOG = "Building in \033[1;31mdebug\033[0m mode"
|
||||||
else
|
else
|
||||||
CFLAGS += -O3
|
CFLAGS += -O3
|
||||||
|
CTESTFLAGS += -O3
|
||||||
|
MODE_LOG = "Building in \033[0;32mrelease\033[0m mode ('make mode=debug' for debug mode)"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
GMAKE = make mode=$(mode)
|
GMAKE = make mode=$(mode)
|
||||||
|
@ -36,7 +41,6 @@ all: library
|
||||||
library: $(TARGET)
|
library: $(TARGET)
|
||||||
|
|
||||||
$(TARGET): $(LIBOBJ)
|
$(TARGET): $(LIBOBJ)
|
||||||
echo $(mode)
|
|
||||||
$(CXX) $(LIBFLAGS) -o $(TARGET) $(LIBOBJ)
|
$(CXX) $(LIBFLAGS) -o $(TARGET) $(LIBOBJ)
|
||||||
|
|
||||||
$(SRCPARSER)/flex_lexer.o: $(SRCPARSER)/flex_lexer.cpp $(SRCPARSER)/bison_parser.cpp
|
$(SRCPARSER)/flex_lexer.o: $(SRCPARSER)/flex_lexer.cpp $(SRCPARSER)/bison_parser.cpp
|
||||||
|
@ -108,3 +112,5 @@ format:
|
||||||
astyle --options=astyle.options $(ALLTEST)
|
astyle --options=astyle.options $(ALLTEST)
|
||||||
astyle --options=astyle.options $(EXAMPLESRC)
|
astyle --options=astyle.options $(EXAMPLESRC)
|
||||||
|
|
||||||
|
log_mode:
|
||||||
|
@echo $(MODE_LOG)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
|
||||||
#include "SQLParserResult.h"
|
#include "SQLParserResult.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace hsql {
|
namespace hsql {
|
||||||
|
|
||||||
|
@ -95,4 +96,15 @@ namespace hsql {
|
||||||
errorColumn_ = -1;
|
errorColumn_ = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Does NOT take ownership.
|
||||||
|
void SQLParserResult::addParameter(Expr* parameter) {
|
||||||
|
parameters_.push_back(parameter);
|
||||||
|
std::sort(parameters_.begin(), parameters_.end(),
|
||||||
|
[](const Expr* a, const Expr* b) { return a->ival < b->ival; });
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<Expr*>& SQLParserResult::parameters() {
|
||||||
|
return parameters_;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace hsql
|
} // namespace hsql
|
||||||
|
|
|
@ -63,6 +63,11 @@ namespace hsql {
|
||||||
// Deletes all statements and other data within the result.
|
// Deletes all statements and other data within the result.
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
// Does NOT take ownership.
|
||||||
|
void addParameter(Expr* parameter);
|
||||||
|
|
||||||
|
const std::vector<Expr*>& parameters();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// List of statements within the result.
|
// List of statements within the result.
|
||||||
std::vector<SQLStatement*> statements_;
|
std::vector<SQLStatement*> statements_;
|
||||||
|
@ -78,6 +83,9 @@ namespace hsql {
|
||||||
|
|
||||||
// Column number of the occurrance of the error in the query.
|
// Column number of the occurrance of the error in the query.
|
||||||
int errorColumn_;
|
int errorColumn_;
|
||||||
|
|
||||||
|
// Does NOT have ownership.
|
||||||
|
std::vector<Expr*> parameters_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace hsql
|
} // namespace hsql
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -48,7 +48,7 @@
|
||||||
extern int hsql_debug;
|
extern int hsql_debug;
|
||||||
#endif
|
#endif
|
||||||
/* "%code requires" blocks. */
|
/* "%code requires" blocks. */
|
||||||
#line 36 "bison_parser.y" /* yacc.c:1909 */
|
#line 34 "bison_parser.y" /* yacc.c:1909 */
|
||||||
|
|
||||||
// %code requires block
|
// %code requires block
|
||||||
|
|
||||||
|
@ -58,18 +58,18 @@ extern int hsql_debug;
|
||||||
|
|
||||||
// Auto update column and line number
|
// Auto update column and line number
|
||||||
#define YY_USER_ACTION \
|
#define YY_USER_ACTION \
|
||||||
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++; \
|
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; \
|
||||||
} \
|
} \
|
||||||
else { \
|
else { \
|
||||||
yylloc->last_column++; \
|
yylloc->last_column++; \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
#line 75 "bison_parser.h" /* yacc.c:1909 */
|
#line 75 "bison_parser.h" /* yacc.c:1909 */
|
||||||
|
|
||||||
|
@ -214,7 +214,7 @@ extern int hsql_debug;
|
||||||
|
|
||||||
union HSQL_STYPE
|
union HSQL_STYPE
|
||||||
{
|
{
|
||||||
#line 95 "bison_parser.y" /* yacc.c:1909 */
|
#line 92 "bison_parser.y" /* yacc.c:1909 */
|
||||||
|
|
||||||
double fval;
|
double fval;
|
||||||
int64_t ival;
|
int64_t ival;
|
||||||
|
|
|
@ -24,8 +24,6 @@ int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const cha
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
%}
|
%}
|
||||||
/*********************************
|
/*********************************
|
||||||
** Section 2: Bison Parser Declarations
|
** Section 2: Bison Parser Declarations
|
||||||
|
@ -42,18 +40,18 @@ int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const cha
|
||||||
|
|
||||||
// Auto update column and line number
|
// Auto update column and line number
|
||||||
#define YY_USER_ACTION \
|
#define YY_USER_ACTION \
|
||||||
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++; \
|
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; \
|
||||||
} \
|
} \
|
||||||
else { \
|
else { \
|
||||||
yylloc->last_column++; \
|
yylloc->last_column++; \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define the names of the created files (defined in Makefile)
|
// Define the names of the created files (defined in Makefile)
|
||||||
|
@ -77,7 +75,6 @@ int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const cha
|
||||||
@$.first_line = 0;
|
@$.first_line = 0;
|
||||||
@$.last_line = 0;
|
@$.last_line = 0;
|
||||||
@$.total_column = 0;
|
@$.total_column = 0;
|
||||||
@$.placeholder_id = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -184,13 +181,13 @@ int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const cha
|
||||||
%type <delete_stmt> delete_statement truncate_statement
|
%type <delete_stmt> delete_statement truncate_statement
|
||||||
%type <update_stmt> update_statement
|
%type <update_stmt> update_statement
|
||||||
%type <drop_stmt> drop_statement
|
%type <drop_stmt> drop_statement
|
||||||
%type <sval> table_name opt_alias alias file_path
|
%type <sval> table_name opt_alias alias file_path prepare_target_query
|
||||||
%type <bval> opt_not_exists opt_distinct
|
%type <bval> opt_not_exists opt_distinct
|
||||||
%type <uval> import_file_type opt_join_type column_type
|
%type <uval> import_file_type opt_join_type column_type
|
||||||
%type <table> from_clause table_ref table_ref_atomic table_ref_name
|
%type <table> from_clause table_ref table_ref_atomic table_ref_name
|
||||||
%type <table> join_clause table_ref_name_no_alias
|
%type <table> join_clause table_ref_name_no_alias
|
||||||
%type <expr> expr operand scalar_expr unary_expr binary_expr logic_expr exists_expr
|
%type <expr> expr operand scalar_expr unary_expr binary_expr logic_expr exists_expr
|
||||||
%type <expr> function_expr between_expr star_expr expr_alias placeholder_expr
|
%type <expr> function_expr between_expr star_expr expr_alias param_expr
|
||||||
%type <expr> column_name literal int_literal num_literal string_literal
|
%type <expr> column_name literal int_literal num_literal string_literal
|
||||||
%type <expr> comp_expr opt_where join_condition opt_having case_expr in_expr
|
%type <expr> comp_expr opt_where join_condition opt_having case_expr in_expr
|
||||||
%type <limit> opt_limit opt_top
|
%type <limit> opt_limit opt_top
|
||||||
|
@ -238,11 +235,21 @@ int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const cha
|
||||||
// Defines our general input.
|
// Defines our general input.
|
||||||
input:
|
input:
|
||||||
statement_list opt_semicolon {
|
statement_list opt_semicolon {
|
||||||
for (SQLStatement* stmt : *$1) {
|
for (SQLStatement* stmt : *$1) {
|
||||||
// Transfers ownership of the statement.
|
// Transfers ownership of the statement.
|
||||||
result->addStatement(stmt);
|
result->addStatement(stmt);
|
||||||
}
|
}
|
||||||
delete $1;
|
|
||||||
|
unsigned param_id = 0;
|
||||||
|
for (void* param : yyloc.param_list) {
|
||||||
|
if (param != nullptr) {
|
||||||
|
Expr* expr = (Expr*) param;
|
||||||
|
expr->ival = param_id;
|
||||||
|
result->addParameter(expr);
|
||||||
|
++param_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete $1;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -253,11 +260,7 @@ statement_list:
|
||||||
;
|
;
|
||||||
|
|
||||||
statement:
|
statement:
|
||||||
prepare_statement {
|
prepare_statement
|
||||||
$1->setPlaceholders(yyloc.placeholder_list);
|
|
||||||
yyloc.placeholder_list.clear();
|
|
||||||
$$ = $1;
|
|
||||||
}
|
|
||||||
| preparable_statement
|
| preparable_statement
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -279,22 +282,15 @@ preparable_statement:
|
||||||
* Prepared Statement
|
* Prepared Statement
|
||||||
******************************/
|
******************************/
|
||||||
prepare_statement:
|
prepare_statement:
|
||||||
PREPARE IDENTIFIER ':' preparable_statement {
|
PREPARE IDENTIFIER FROM prepare_target_query {
|
||||||
$$ = new PrepareStatement();
|
$$ = new PrepareStatement();
|
||||||
$$->name = $2;
|
$$->name = $2;
|
||||||
$$->query = new SQLParserResult($4);
|
$$->query = $4;
|
||||||
}
|
|
||||||
| PREPARE IDENTIFIER '{' statement_list opt_semicolon '}' {
|
|
||||||
$$ = new PrepareStatement();
|
|
||||||
$$->name = $2;
|
|
||||||
$$->query = new SQLParserResult();
|
|
||||||
for (SQLStatement* stmt : *$4) {
|
|
||||||
$$->query->addStatement(stmt);
|
|
||||||
}
|
|
||||||
delete $4;
|
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
prepare_target_query: STRING
|
||||||
|
|
||||||
execute_statement:
|
execute_statement:
|
||||||
EXECUTE IDENTIFIER {
|
EXECUTE IDENTIFIER {
|
||||||
$$ = new ExecuteStatement();
|
$$ = new ExecuteStatement();
|
||||||
|
@ -717,7 +713,7 @@ column_name:
|
||||||
literal:
|
literal:
|
||||||
string_literal
|
string_literal
|
||||||
| num_literal
|
| num_literal
|
||||||
| placeholder_expr
|
| param_expr
|
||||||
;
|
;
|
||||||
|
|
||||||
string_literal:
|
string_literal:
|
||||||
|
@ -738,10 +734,11 @@ star_expr:
|
||||||
'*' { $$ = new Expr(kExprStar); }
|
'*' { $$ = new Expr(kExprStar); }
|
||||||
;
|
;
|
||||||
|
|
||||||
placeholder_expr:
|
param_expr:
|
||||||
'?' {
|
'?' {
|
||||||
$$ = Expr::makePlaceholder(yylloc.total_column);
|
$$ = Expr::makeParameter(yylloc.total_column);
|
||||||
yyloc.placeholder_list.push_back($$);
|
$$->ival2 = yyloc.param_list.size();
|
||||||
|
yyloc.param_list.push_back($$);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -22,15 +22,12 @@ struct HSQL_CUST_LTYPE {
|
||||||
|
|
||||||
int total_column;
|
int total_column;
|
||||||
|
|
||||||
// Placeholder
|
// Parameters.
|
||||||
int placeholder_id;
|
// int param_id;
|
||||||
std::vector<void*> placeholder_list;
|
std::vector<void*> param_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define HSQL_LTYPE HSQL_CUST_LTYPE
|
#define HSQL_LTYPE HSQL_CUST_LTYPE
|
||||||
#define HSQL_LTYPE_IS_DECLARED 1
|
#define HSQL_LTYPE_IS_DECLARED 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -118,8 +118,8 @@ namespace hsql {
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr* Expr::makePlaceholder(int id) {
|
Expr* Expr::makeParameter(int id) {
|
||||||
Expr* e = new Expr(kExprPlaceholder);
|
Expr* e = new Expr(kExprParameter);
|
||||||
e->ival = id;
|
e->ival = id;
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
@ -160,7 +160,7 @@ namespace hsql {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Expr::isLiteral() const {
|
bool Expr::isLiteral() const {
|
||||||
return isType(kExprLiteralInt) || isType(kExprLiteralFloat) || isType(kExprLiteralString) || isType(kExprPlaceholder);
|
return isType(kExprLiteralInt) || isType(kExprLiteralFloat) || isType(kExprLiteralString) || isType(kExprParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Expr::hasAlias() const {
|
bool Expr::hasAlias() const {
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace hsql {
|
||||||
kExprLiteralString,
|
kExprLiteralString,
|
||||||
kExprLiteralInt,
|
kExprLiteralInt,
|
||||||
kExprStar,
|
kExprStar,
|
||||||
kExprPlaceholder,
|
kExprParameter,
|
||||||
kExprColumnRef,
|
kExprColumnRef,
|
||||||
kExprFunctionRef,
|
kExprFunctionRef,
|
||||||
kExprOperator,
|
kExprOperator,
|
||||||
|
@ -124,7 +124,7 @@ namespace hsql {
|
||||||
|
|
||||||
static Expr* makeFunctionRef(char* func_name, std::vector<Expr*>* exprList, bool distinct);
|
static Expr* makeFunctionRef(char* func_name, std::vector<Expr*>* exprList, bool distinct);
|
||||||
|
|
||||||
static Expr* makePlaceholder(int id);
|
static Expr* makeParameter(int id);
|
||||||
|
|
||||||
static Expr* makeSelect(SelectStatement* select);
|
static Expr* makeSelect(SelectStatement* select);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
#include "PrepareStatement.h"
|
||||||
|
|
||||||
|
namespace hsql {
|
||||||
|
// PrepareStatement
|
||||||
|
PrepareStatement::PrepareStatement() :
|
||||||
|
SQLStatement(kStmtPrepare),
|
||||||
|
name(NULL),
|
||||||
|
query(NULL) {}
|
||||||
|
|
||||||
|
PrepareStatement::~PrepareStatement() {
|
||||||
|
free(name);
|
||||||
|
free(query);
|
||||||
|
}
|
||||||
|
} // namespace hsql
|
|
@ -1,32 +1,20 @@
|
||||||
#ifndef __SQLPARSER__PREPARE_STATEMENT_H__
|
#ifndef __SQLPARSER__PREPARE_STATEMENT_H__
|
||||||
#define __SQLPARSER__PREPARE_STATEMENT_H__
|
#define __SQLPARSER__PREPARE_STATEMENT_H__
|
||||||
|
|
||||||
#include "../SQLParserResult.h"
|
|
||||||
#include "SQLStatement.h"
|
#include "SQLStatement.h"
|
||||||
#include "SelectStatement.h"
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace hsql {
|
namespace hsql {
|
||||||
|
|
||||||
// Represents SQL Prepare statements.
|
// Represents SQL Prepare statements.
|
||||||
// Example: "PREPARE ins_prep: SELECT * FROM t1 WHERE c1 = ? AND c2 = ?"
|
// Example: PREPARE test FROM 'SELECT * FROM test WHERE a = ?;'
|
||||||
struct PrepareStatement : SQLStatement {
|
struct PrepareStatement : SQLStatement {
|
||||||
PrepareStatement();
|
PrepareStatement();
|
||||||
virtual ~PrepareStatement();
|
virtual ~PrepareStatement();
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
char* name;
|
char* name;
|
||||||
|
|
||||||
// The result that is stored within this prepared statement.
|
// The query that is supposed to be prepared.
|
||||||
SQLParserResult* query;
|
char* query;
|
||||||
|
|
||||||
// The expressions are not owned by this statement.
|
|
||||||
// Rather they are owned by the query and destroyed, when
|
|
||||||
// the query is destroyed.
|
|
||||||
std::vector<Expr*> placeholders;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namsepace hsql
|
} // namsepace hsql
|
||||||
|
|
|
@ -139,29 +139,6 @@ namespace hsql {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrepareStatement
|
|
||||||
PrepareStatement::PrepareStatement() :
|
|
||||||
SQLStatement(kStmtPrepare),
|
|
||||||
name(NULL),
|
|
||||||
query(NULL) {}
|
|
||||||
|
|
||||||
PrepareStatement::~PrepareStatement() {
|
|
||||||
delete query;
|
|
||||||
free(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrepareStatement::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 (uintmax_t i = 0; i < placeholders.size(); ++i) placeholders[i]->ival = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SelectStatement.h
|
// SelectStatement.h
|
||||||
|
|
||||||
// OrderDescription
|
// OrderDescription
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
#include "sql_asserts.h"
|
#include "sql_asserts.h"
|
||||||
#include "SQLParser.h"
|
#include "SQLParser.h"
|
||||||
|
|
||||||
using hsql::kExprPlaceholder;
|
using hsql::kExprParameter;
|
||||||
|
using hsql::kExprLiteralInt;
|
||||||
|
|
||||||
using hsql::kStmtDrop;
|
using hsql::kStmtDrop;
|
||||||
using hsql::kStmtExecute;
|
using hsql::kStmtExecute;
|
||||||
|
@ -21,63 +22,76 @@ using hsql::SelectStatement;
|
||||||
|
|
||||||
|
|
||||||
TEST(PrepareSingleStatementTest) {
|
TEST(PrepareSingleStatementTest) {
|
||||||
const std::string query = "PREPARE test: SELECT * FROM students WHERE grade = ?;";
|
TEST_PARSE_SINGLE_SQL(
|
||||||
TEST_PARSE_SINGLE_SQL(query, kStmtPrepare, PrepareStatement, result, prepare);
|
"PREPARE test FROM 'SELECT * FROM students WHERE grade = ?';",
|
||||||
|
kStmtPrepare,
|
||||||
|
PrepareStatement,
|
||||||
|
result,
|
||||||
|
prepare);
|
||||||
|
|
||||||
const SelectStatement* select = (const SelectStatement*) prepare->query->getStatement(0);
|
ASSERT_STREQ(prepare->name, "test");
|
||||||
|
ASSERT_STREQ(prepare->query, "SELECT * FROM students WHERE grade = ?");
|
||||||
|
|
||||||
|
TEST_PARSE_SINGLE_SQL(
|
||||||
|
prepare->query,
|
||||||
|
kStmtSelect,
|
||||||
|
SelectStatement,
|
||||||
|
result2,
|
||||||
|
select);
|
||||||
|
|
||||||
|
ASSERT_EQ(result2.parameters().size(), 1);
|
||||||
|
ASSERT(select->whereClause->expr2->isType(kExprParameter))
|
||||||
|
ASSERT_EQ(select->whereClause->expr2->ival, 0)
|
||||||
|
|
||||||
ASSERT(select->whereClause->isSimpleOp('='));
|
|
||||||
ASSERT_EQ(select->whereClause->expr2, prepare->placeholders[0])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrepareMultiStatementTest) {
|
TEST(DeallocatePrepareStatementTest) {
|
||||||
const std::string query = "PREPARE test {"
|
TEST_PARSE_SINGLE_SQL(
|
||||||
"INSERT INTO test VALUES(?);"
|
"DEALLOCATE PREPARE test;",
|
||||||
"SELECT ?, test FROM test WHERE c1 = ?;"
|
kStmtDrop,
|
||||||
"};"
|
DropStatement,
|
||||||
"PREPARE stmt: SELECT * FROM data WHERE c1 = ?;"
|
result,
|
||||||
"DEALLOCATE PREPARE stmt;";
|
drop);
|
||||||
|
|
||||||
TEST_PARSE_SQL_QUERY(query, result, 3);
|
|
||||||
|
|
||||||
TEST_CAST_STMT(result, 0, kStmtPrepare, PrepareStatement, prep1);
|
|
||||||
TEST_CAST_STMT(result, 1, kStmtPrepare, PrepareStatement, prep2);
|
|
||||||
TEST_CAST_STMT(result, 2, kStmtDrop, DropStatement, drop);
|
|
||||||
|
|
||||||
// Prepare Statement #1
|
|
||||||
ASSERT_STREQ(prep1->name, "test");
|
|
||||||
ASSERT_EQ(prep1->placeholders.size(), 3);
|
|
||||||
ASSERT_EQ(prep1->query->size(), 2);
|
|
||||||
|
|
||||||
TEST_CAST_STMT((*prep1->query), 0, kStmtInsert, InsertStatement, insert);
|
|
||||||
TEST_CAST_STMT((*prep1->query), 1, kStmtSelect, SelectStatement, select);
|
|
||||||
|
|
||||||
ASSERT(insert->values->at(0)->isType(kExprPlaceholder));
|
|
||||||
ASSERT(select->selectList->at(0)->isType(kExprPlaceholder));
|
|
||||||
ASSERT(select->whereClause->expr2->isType(kExprPlaceholder));
|
|
||||||
|
|
||||||
// Check IDs of placeholders
|
|
||||||
ASSERT_EQ(insert->values->at(0)->ival, 0);
|
|
||||||
ASSERT_EQ(insert->values->at(0), prep1->placeholders[0]);
|
|
||||||
|
|
||||||
ASSERT_EQ(select->selectList->at(0)->ival, 1);
|
|
||||||
ASSERT_EQ(select->selectList->at(0), prep1->placeholders[1]);
|
|
||||||
|
|
||||||
ASSERT_EQ(select->whereClause->expr2->ival, 2);
|
|
||||||
ASSERT_EQ(select->whereClause->expr2, prep1->placeholders[2]);
|
|
||||||
|
|
||||||
// Prepare Statement #2
|
|
||||||
ASSERT_STREQ(prep2->name, "stmt");
|
|
||||||
ASSERT_EQ(prep2->placeholders.size(), 1);
|
|
||||||
|
|
||||||
// Deallocate Statement
|
|
||||||
ASSERT_EQ(drop->type, kDropPreparedStatement);
|
ASSERT_EQ(drop->type, kDropPreparedStatement);
|
||||||
ASSERT_STREQ(drop->name, "stmt");
|
ASSERT_STREQ(drop->name, "test");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(StatementWithParameters) {
|
||||||
|
TEST_PARSE_SINGLE_SQL(
|
||||||
|
"SELECT * FROM test WHERE a = ? AND b = ?",
|
||||||
|
kStmtSelect,
|
||||||
|
SelectStatement,
|
||||||
|
result,
|
||||||
|
stmt);
|
||||||
|
|
||||||
|
const hsql::Expr* eq1 = stmt->whereClause->expr;
|
||||||
|
const hsql::Expr* eq2 = stmt->whereClause->expr2;
|
||||||
|
|
||||||
|
ASSERT_EQ(result.parameters().size(), 2);
|
||||||
|
|
||||||
|
ASSERT(eq1->isSimpleOp('='))
|
||||||
|
ASSERT(eq1->expr->isType(hsql::kExprColumnRef))
|
||||||
|
ASSERT(eq1->expr2->isType(kExprParameter))
|
||||||
|
ASSERT_EQ(eq1->expr2->ival, 0)
|
||||||
|
ASSERT_EQ(result.parameters()[0], eq1->expr2);
|
||||||
|
|
||||||
|
|
||||||
|
ASSERT(eq2->isSimpleOp('='))
|
||||||
|
ASSERT(eq2->expr->isType(hsql::kExprColumnRef))
|
||||||
|
ASSERT(eq2->expr2->isType(kExprParameter))
|
||||||
|
ASSERT_EQ(eq2->expr2->ival, 1)
|
||||||
|
ASSERT_EQ(result.parameters()[1], eq2->expr2);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(ExecuteStatementTest) {
|
TEST(ExecuteStatementTest) {
|
||||||
TEST_PARSE_SINGLE_SQL("EXECUTE test(1, 2);", kStmtExecute, ExecuteStatement, result, stmt);
|
TEST_PARSE_SINGLE_SQL(
|
||||||
|
"EXECUTE test(1, 2);",
|
||||||
|
kStmtExecute,
|
||||||
|
ExecuteStatement,
|
||||||
|
result,
|
||||||
|
stmt);
|
||||||
|
|
||||||
ASSERT_STREQ(stmt->name, "test");
|
ASSERT_STREQ(stmt->name, "test");
|
||||||
ASSERT_EQ(stmt->parameters->size(), 2);
|
ASSERT_EQ(stmt->parameters->size(), 2);
|
||||||
|
|
|
@ -11,6 +11,7 @@ SELECT * FROM t1 UNION (SELECT * FROM t2 UNION SELECT * FROM t3) ORDER BY col1;
|
||||||
SELECT TOP 10 * FROM t1 ORDER BY col1, col2;
|
SELECT TOP 10 * FROM t1 ORDER BY col1, col2;
|
||||||
SELECT a, MAX(b), MAX(c, d), CUSTOM(q, UP(r)) AS f FROM t1;
|
SELECT a, MAX(b), MAX(c, d), CUSTOM(q, UP(r)) AS f FROM t1;
|
||||||
SELECT * FROM t WHERE a BETWEEN 1 and c;
|
SELECT * FROM t WHERE a BETWEEN 1 and c;
|
||||||
|
SELECT * FROM t WHERE a = ? AND b = ?;
|
||||||
SELECT City.name, Product.category, SUM(price) FROM fact INNER JOIN City ON fact.city_id = City.id INNER JOIN Product ON fact.product_id = Product.id GROUP BY City.name, Product.category;
|
SELECT City.name, Product.category, SUM(price) FROM fact INNER JOIN City ON fact.city_id = City.id INNER JOIN Product ON fact.product_id = Product.id GROUP BY City.name, Product.category;
|
||||||
# JOIN
|
# JOIN
|
||||||
SELECT t1.a, t1.b, t2.c FROM "table" AS t1 JOIN (SELECT * FROM foo JOIN bar ON foo.id = bar.id) t2 ON t1.a = t2.b WHERE (t1.b OR NOT t1.a) AND t2.c = 12.5
|
SELECT t1.a, t1.b, t2.c FROM "table" AS t1 JOIN (SELECT * FROM foo JOIN bar ON foo.id = bar.id) t2 ON t1.a = t2.b WHERE (t1.b OR NOT t1.a) AND t2.c = 12.5
|
||||||
|
@ -37,8 +38,8 @@ UPDATE students SET grade = 1.0;
|
||||||
# DROP
|
# DROP
|
||||||
DROP TABLE students;
|
DROP TABLE students;
|
||||||
# PREPARE
|
# PREPARE
|
||||||
PREPARE prep_inst: INSERT INTO test VALUES (?, ?, ?);
|
PREPARE prep_inst FROM 'INSERT INTO test VALUES (?, ?, ?)';
|
||||||
PREPARE prep2 { INSERT INTO test VALUES (?, 0, 0); INSERT INTO test VALUES (0, ?, 0); INSERT INTO test VALUES (0, 0, ?); };
|
PREPARE prep2 FROM 'INSERT INTO test VALUES (?, 0, 0); INSERT INTO test VALUES (0, ?, 0); INSERT INTO test VALUES (0, 0, ?);';
|
||||||
EXECUTE prep_inst(1, 2, 3);
|
EXECUTE prep_inst(1, 2, 3);
|
||||||
EXECUTE prep;
|
EXECUTE prep;
|
||||||
DEALLOCATE PREPARE prep;
|
DEALLOCATE PREPARE prep;
|
||||||
|
@ -50,3 +51,4 @@ DEALLOCATE PREPARE prep;
|
||||||
!CREATE TABLE "table" FROM TBL FILE 'students.tbl';SELECT 1
|
!CREATE TABLE "table" FROM TBL FILE 'students.tbl';SELECT 1
|
||||||
!CREATE TABLE "table" FROM TBL FILE 'students.tbl';1
|
!CREATE TABLE "table" FROM TBL FILE 'students.tbl';1
|
||||||
!INSERT INTO test_table VALUESd (1, 2, 'test');
|
!INSERT INTO test_table VALUESd (1, 2, 'test');
|
||||||
|
!SELECT * FROM t WHERE a = ? AND b = ?;SELECT 1;
|
Loading…
Reference in New Issue