Merge pull request #34 from torpedro/misc

Interface Improvements
This commit is contained in:
Pedro Flemming 2017-04-14 15:15:23 +02:00 committed by GitHub
commit 9184d5d0c2
33 changed files with 927 additions and 816 deletions

View File

@ -1,4 +1,7 @@
SRC = ./
CPP = $(shell find $(SRC) -name '*.cpp')
CFLAGS = -std=c++11 -lstdc++ -Wall -I../src/ -L../
all: parser_benchmark
@ -6,8 +9,8 @@ all: parser_benchmark
run: parser_benchmark
@export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:../ && ./parser_benchmark
parser_benchmark: parser_benchmark.cpp
$(CXX) $(CFLAGS) parser_benchmark.cpp -o parser_benchmark -lbenchmark -lpthread -lsqlparser
parser_benchmark: $(CPP)
$(CXX) $(CFLAGS) $(CPP) -o parser_benchmark -lbenchmark -lpthread -lsqlparser
clean:
rm -f parser_benchmark

View File

@ -31,6 +31,18 @@ PARSE_QUERY_BENCHMARK(BM_LongSelectElement26,
PARSE_QUERY_BENCHMARK(BM_LongSelectElement52,
"SELECT aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa FROM test;");
// Prepare and Execute benchmarks.
PARSE_QUERY_BENCHMARK(BM_ExecuteStatement,
"EXECUTE procedure;");
PARSE_QUERY_BENCHMARK(BM_ExecuteWith2ParametersStatement,
"EXECUTE procedure(11, 'test');");
PARSE_QUERY_BENCHMARK(BM_ExecuteWith10ParametersStatement,
"EXECUTE procedure(11, 'test', 5.6, 4.2, 'abc', 6, 7, 8, 9, 10000);");
// Benchmark the influence of increasing size of the query, while
// the number of tokens remains unchanged.
static void BM_CharacterCount(benchmark::State& st) {

View File

@ -6,7 +6,7 @@
#include "SQLParser.h"
// contains printing utilities
#include "sqlhelper.h"
#include "util/sqlhelper.h"
int main(int argc, char *argv[]) {
if (argc <= 1) {
@ -16,31 +16,26 @@ int main(int argc, char *argv[]) {
std::string query = argv[1];
// parse a given query
hsql::SQLParserResult* result = hsql::SQLParser::parseSQLString(query);
hsql::SQLParserResult result;
hsql::SQLParser::parseSQLString(query, &result);
// check whether the parsing was successful
if (!result) {
return -1;
}
if (result->isValid()) {
if (result.isValid()) {
printf("Parsed successfully!\n");
printf("Number of statements: %lu\n", result->size());
printf("Number of statements: %lu\n", result.size());
for (uint i = 0; i < result->size(); ++i) {
for (uint i = 0; i < result.size(); ++i) {
// Print a statement summary.
hsql::printStatementInfo(result->getStatement(i));
hsql::printStatementInfo(result.getStatement(i));
}
delete result;
return 0;
} else {
fprintf(stderr, "Given string is not a valid SQL query.\n");
fprintf(stderr, "%s (L%d:%d)\n",
result->errorMsg(),
result->errorLine(),
result->errorColumn());
delete result;
result.errorMsg(),
result.errorLine(),
result.errorColumn());
return -1;
}
}

View File

@ -12,36 +12,51 @@ namespace hsql {
fprintf(stderr, "SQLParser only has static methods atm! Do not initialize!\n");
}
SQLParserResult* SQLParser::parseSQLString(const char* text) {
SQLParserResult* result = NULL;
// static
bool SQLParser::parseSQLString(const char* text, SQLParserResult* result) {
yyscan_t scanner;
YY_BUFFER_STATE state;
if (hsql_lex_init(&scanner)) {
// Couldn't initialize the lexer.
fprintf(stderr, "[Error] SQLParser: Error when initializing lexer!\n");
return NULL;
return false;
}
state = hsql__scan_string(text, scanner);
// Parser and return early if it failed.
if (hsql_parse(&result, scanner)) {
// Returns an error stmt object.
hsql__delete_buffer(state, scanner);
hsql_lex_destroy(scanner);
return result;
}
// Parse the tokens.
// If parsing fails, the result will contain an error object.
int ret = hsql_parse(result, scanner);
bool success = (ret == 0);
result->setIsValid(success);
hsql__delete_buffer(state, scanner);
hsql_lex_destroy(scanner);
return true;
}
// static
bool SQLParser::parseSQLString(const std::string& text, SQLParserResult* result) {
return parseSQLString(text.c_str(), result);
}
// static
SQLParserResult* SQLParser::parseSQLString(const char* text) {
SQLParserResult* result = new SQLParserResult();
if (!SQLParser::parseSQLString(text, result)) {
delete result;
return NULL;
}
return result;
}
// static
SQLParserResult* SQLParser::parseSQLString(const std::string& text) {
return parseSQLString(text.c_str());
}
} // namespace hsql

View File

@ -1,5 +1,5 @@
#ifndef __SQLPARSER_H_
#define __SQLPARSER_H_
#ifndef __SQLPARSER__SQLPARSER_H__
#define __SQLPARSER__SQLPARSER_H__
#include "SQLParserResult.h"
#include "sql/statements.h"
@ -9,10 +9,25 @@ namespace hsql {
// Static methods used to parse SQL strings.
class SQLParser {
public:
// Parses a given constant character SQL string into the result object.
// Returns true if the lexer and parser could run without internal errors.
// This does NOT mean that the SQL string was valid SQL. To check that
// you need to check result->isValid();
static bool parseSQLString(const char* sql, SQLParserResult* result);
// Parses a given SQL string into the result object.
static bool parseSQLString(const std::string& sql, SQLParserResult* result);
// Deprecated:
// Parses a given constant character SQL string.
// Note: This is kept for legacy reasons. It is recommended to use
// the (const char*, SQLParserResult*) implementation.
static SQLParserResult* parseSQLString(const char* sql);
// Deprecated:
// Parses an SQL std::string.
// Note: This is kept for legacy reasons. It is recommended to use
// the (const std::string&, SQLParserResult*) implementation.
static SQLParserResult* parseSQLString(const std::string& sql);
private:

View File

@ -4,21 +4,17 @@
namespace hsql {
SQLParserResult::SQLParserResult() :
isValid_(true),
isValid_(false),
errorMsg_(NULL) {};
SQLParserResult::SQLParserResult(SQLStatement* stmt) :
isValid_(true),
isValid_(false),
errorMsg_(NULL) {
addStatement(stmt);
};
SQLParserResult::~SQLParserResult() {
for (SQLStatement* statement : statements_) {
delete statement;
}
free(errorMsg_);
reset();
}
void SQLParserResult::addStatement(SQLStatement* stmt) {
@ -63,4 +59,30 @@ namespace hsql {
errorColumn_ = errorColumn;
}
const std::vector<SQLStatement*>& SQLParserResult::getStatements() const {
return statements_;
}
std::vector<SQLStatement*> SQLParserResult::releaseStatements() {
std::vector<SQLStatement*> copy = statements_;
statements_.clear();
return copy;
}
void SQLParserResult::reset() {
for (SQLStatement* statement : statements_) {
delete statement;
}
statements_.clear();
isValid_ = false;
free(errorMsg_);
errorMsg_ = NULL;
errorLine_ = -1;
errorColumn_ = -1;
}
} // namespace hsql

View File

@ -1,5 +1,5 @@
#ifndef __SQLPARSERRESULT__
#define __SQLPARSERRESULT__
#ifndef __SQLPARSER__SQLPARSER_RESULT_H__
#define __SQLPARSER__SQLPARSER_RESULT_H__
#include "sql/SQLStatement.h"
@ -15,15 +15,22 @@ namespace hsql {
// Takes ownership of the statement.
SQLParserResult(SQLStatement* stmt);
// Deletes all statements in the resul.
// Deletes all statements in the result.
virtual ~SQLParserResult();
// Set whether parsing was successful.
void setIsValid(bool isValid);
// Returns true if parsing was successful.
bool isValid() const;
// Returns the number of statements in the result.
size_t size() const;
// Set the details of the error, if available.
// Takes ownership of errorMsg.
void setErrorDetails(char* errorMsg, int errorLine, int errorColumn);
// Returns the error message, if an error occurred.
const char* errorMsg() const;
@ -33,23 +40,25 @@ namespace hsql {
// Returns the column number of the occurrance of the error in the query.
int errorColumn() const;
// Adds a statement to the result list of statements.
// SQLParserResult takes ownership of the statement.
void addStatement(SQLStatement* stmt);
// Gets the SQL statement with the given index.
const SQLStatement* getStatement(int index) const;
// Gets the non const SQL statement with the given index.
SQLStatement* getMutableStatement(int index);
// Adds a statement to the result list of statements.
// Takes ownership of the statement.
void addStatement(SQLStatement* stmt);
// Get the list of all statements.
const std::vector<SQLStatement*>& getStatements() const;
// Set whether parsing was successful.
void setIsValid(bool isValid);
// Set the details of the error, if available.
// Takes ownership of errorMsg.
void setErrorDetails(char* errorMsg, int errorLine, int errorColumn);
// Returns a copy of the list of all statements in this result.
// Removes them from this result.
std::vector<SQLStatement*> releaseStatements();
// Deletes all statements and other data within the result.
void reset();
private:
// List of statements within the result.
@ -70,4 +79,4 @@ namespace hsql {
} // namespace hsql
#endif // __SQLPARSERRESULT__
#endif // __SQLPARSER__SQLPARSER_RESULT_H__

File diff suppressed because it is too large Load Diff

View File

@ -48,7 +48,7 @@
extern int hsql_debug;
#endif
/* "%code requires" blocks. */
#line 41 "bison_parser.y" /* yacc.c:1909 */
#line 36 "bison_parser.y" /* yacc.c:1909 */
// %code requires block
@ -214,7 +214,7 @@ extern int hsql_debug;
union HSQL_STYPE
{
#line 100 "bison_parser.y" /* yacc.c:1909 */
#line 95 "bison_parser.y" /* yacc.c:1909 */
double fval;
int64_t ival;
@ -242,7 +242,7 @@ union HSQL_STYPE
hsql::GroupByDescription* group_t;
hsql::UpdateClause* update_t;
hsql::SQLParserResult* stmt_list;
std::vector<hsql::SQLStatement*>* stmt_vec;
std::vector<char*>* str_vec;
std::vector<hsql::TableRef*>* table_vec;
@ -275,6 +275,6 @@ struct HSQL_LTYPE
int hsql_parse (hsql::SQLParserResult** result, yyscan_t scanner);
int hsql_parse (hsql::SQLParserResult* result, yyscan_t scanner);
#endif /* !YY_HSQL_BISON_PARSER_H_INCLUDED */

View File

@ -18,14 +18,9 @@
using namespace hsql;
int yyerror(YYLTYPE* llocp, SQLParserResult** result, yyscan_t scanner, const char *msg) {
delete *result;
SQLParserResult* list = new SQLParserResult();
list->setIsValid(false);
list->setErrorDetails(strdup(msg), llocp->first_line, llocp->first_column);
*result = list;
int yyerror(YYLTYPE* llocp, SQLParserResult* result, yyscan_t scanner, const char *msg) {
result->setIsValid(false);
result->setErrorDetails(strdup(msg), llocp->first_line, llocp->first_column);
return 0;
}
@ -90,7 +85,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult** result, yyscan_t scanner, const ch
%lex-param { yyscan_t scanner }
// Define additional parameters for yyparse
%parse-param { hsql::SQLParserResult** result }
%parse-param { hsql::SQLParserResult* result }
%parse-param { yyscan_t scanner }
@ -124,7 +119,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult** result, yyscan_t scanner, const ch
hsql::GroupByDescription* group_t;
hsql::UpdateClause* update_t;
hsql::SQLParserResult* stmt_list;
std::vector<hsql::SQLStatement*>* stmt_vec;
std::vector<char*>* str_vec;
std::vector<hsql::TableRef*>* table_vec;
@ -147,7 +142,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult** result, yyscan_t scanner, const ch
}
}
delete ($$);
} <str_vec> <table_vec> <column_vec> <update_vec> <expr_vec> <order_vec>
} <str_vec> <table_vec> <column_vec> <update_vec> <expr_vec> <order_vec> <stmt_vec>
%destructor { delete ($$); } <*>
@ -178,7 +173,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult** result, yyscan_t scanner, const ch
/*********************************
** Non-Terminal types (http://www.gnu.org/software/bison/manual/html_node/Type-Decl.html)
*********************************/
%type <stmt_list> statement_list
%type <stmt_vec> statement_list
%type <statement> statement preparable_statement
%type <exec_stmt> execute_statement
%type <prep_stmt> prepare_statement
@ -243,14 +238,18 @@ int yyerror(YYLTYPE* llocp, SQLParserResult** result, yyscan_t scanner, const ch
// Defines our general input.
input:
statement_list opt_semicolon {
*result = $1;
for (SQLStatement* stmt : *$1) {
// Transfers ownership of the statement.
result->addStatement(stmt);
}
delete $1;
}
;
statement_list:
statement { $$ = new SQLParserResult($1); }
| statement_list ';' statement { $1->addStatement($3); $$ = $1; }
statement { $$ = new std::vector<SQLStatement*>(); $$->push_back($1); }
| statement_list ';' statement { $1->push_back($3); $$ = $1; }
;
statement:
@ -288,7 +287,11 @@ prepare_statement:
| PREPARE IDENTIFIER '{' statement_list opt_semicolon '}' {
$$ = new PrepareStatement();
$$->name = $2;
$$->query = $4;
$$->query = new SQLParserResult();
for (SQLStatement* stmt : *$4) {
$$->query->addStatement(stmt);
}
delete $4;
}
;
@ -651,8 +654,8 @@ scalar_expr:
;
unary_expr:
'-' operand { $$ = Expr::makeOpUnary(Expr::UMINUS, $2); }
| NOT operand { $$ = Expr::makeOpUnary(Expr::NOT, $2); }
'-' operand { $$ = Expr::makeOpUnary(kOpMinus, $2); }
| NOT operand { $$ = Expr::makeOpUnary(kOpNot, $2); }
;
binary_expr:
@ -663,20 +666,20 @@ binary_expr:
| operand '*' operand { $$ = Expr::makeOpBinary($1, '*', $3); }
| operand '%' operand { $$ = Expr::makeOpBinary($1, '%', $3); }
| operand '^' operand { $$ = Expr::makeOpBinary($1, '^', $3); }
| operand LIKE operand { $$ = Expr::makeOpBinary($1, Expr::LIKE, $3); }
| operand NOT LIKE operand { $$ = Expr::makeOpBinary($1, Expr::NOT_LIKE, $4); }
| operand LIKE operand { $$ = Expr::makeOpBinary($1, kOpLike, $3); }
| operand NOT LIKE operand { $$ = Expr::makeOpBinary($1, kOpNotLike, $4); }
;
logic_expr:
expr AND expr { $$ = Expr::makeOpBinary($1, Expr::AND, $3); }
| expr OR expr { $$ = Expr::makeOpBinary($1, Expr::OR, $3); }
expr AND expr { $$ = Expr::makeOpBinary($1, kOpAnd, $3); }
| expr OR expr { $$ = Expr::makeOpBinary($1, kOpOr, $3); }
;
in_expr:
operand IN '(' expr_list ')' { $$ = Expr::makeInOperator($1, $4); }
| operand NOT IN '(' expr_list ')' { $$ = Expr::makeOpUnary(Expr::NOT, Expr::makeInOperator($1, $5)); }
| operand NOT IN '(' expr_list ')' { $$ = Expr::makeOpUnary(kOpNot, Expr::makeInOperator($1, $5)); }
| operand IN '(' select_no_paren ')' { $$ = Expr::makeInOperator($1, $4); }
| operand NOT IN '(' select_no_paren ')' { $$ = Expr::makeOpUnary(Expr::NOT, Expr::makeInOperator($1, $5)); }
| operand NOT IN '(' select_no_paren ')' { $$ = Expr::makeOpUnary(kOpNot, Expr::makeInOperator($1, $5)); }
;
// TODO: allow no else specified
@ -686,16 +689,16 @@ case_expr:
exists_expr:
EXISTS '(' select_no_paren ')' { $$ = Expr::makeExists($3); }
| NOT EXISTS '(' select_no_paren ')' { $$ = Expr::makeOpUnary(Expr::NOT, Expr::makeExists($4)); }
| NOT EXISTS '(' select_no_paren ')' { $$ = Expr::makeOpUnary(kOpNot, Expr::makeExists($4)); }
;
comp_expr:
operand '=' operand { $$ = Expr::makeOpBinary($1, '=', $3); }
| operand NOTEQUALS operand { $$ = Expr::makeOpBinary($1, Expr::NOT_EQUALS, $3); }
| operand NOTEQUALS operand { $$ = Expr::makeOpBinary($1, kOpNotEquals, $3); }
| operand '<' operand { $$ = Expr::makeOpBinary($1, '<', $3); }
| operand '>' operand { $$ = Expr::makeOpBinary($1, '>', $3); }
| operand LESSEQ operand { $$ = Expr::makeOpBinary($1, Expr::LESS_EQ, $3); }
| operand GREATEREQ operand { $$ = Expr::makeOpBinary($1, Expr::GREATER_EQ, $3); }
| operand LESSEQ operand { $$ = Expr::makeOpBinary($1, kOpLessEq, $3); }
| operand GREATEREQ operand { $$ = Expr::makeOpBinary($1, kOpGreaterEq, $3); }
;
function_expr:

View File

@ -1,5 +1,5 @@
#ifndef __CREATE_STATEMENT_H__
#define __CREATE_STATEMENT_H__
#ifndef __SQLPARSER__CREATE_STATEMENT_H__
#define __SQLPARSER__CREATE_STATEMENT_H__
#include "SQLStatement.h"

View File

@ -1,5 +1,5 @@
#ifndef __DELETE_STATEMENT_H__
#define __DELETE_STATEMENT_H__
#ifndef __SQLPARSER__DELETE_STATEMENT_H__
#define __SQLPARSER__DELETE_STATEMENT_H__
#include "SQLStatement.h"

View File

@ -1,5 +1,5 @@
#ifndef __DROP_STATEMENT_H__
#define __DROP_STATEMENT_H__
#ifndef __SQLPARSER__DROP_STATEMENT_H__
#define __SQLPARSER__DROP_STATEMENT_H__
#include "SQLStatement.h"

View File

@ -1,5 +1,5 @@
#ifndef __EXECUTE_STATEMENT_H__
#define __EXECUTE_STATEMENT_H__
#ifndef __SQLPARSER__EXECUTE_STATEMENT_H__
#define __SQLPARSER__EXECUTE_STATEMENT_H__
#include "SQLStatement.h"

View File

@ -51,7 +51,7 @@ namespace hsql {
Expr* Expr::makeOpBinary(Expr* expr1, char op, Expr* expr2) {
Expr* e = new Expr(kExprOperator);
e->opType = SIMPLE_OP;
e->opType = kOpSimple;
e->opChar = op;
e->expr = expr1;
e->expr2 = expr2;
@ -61,7 +61,7 @@ namespace hsql {
Expr* Expr::makeBetween(Expr* expr, Expr* left, Expr* right) {
Expr* e = new Expr(kExprOperator);
e->expr = expr;
e->opType = BETWEEN;
e->opType = kOpBetween;
e->exprList = new std::vector<Expr*>();
e->exprList->push_back(left);
e->exprList->push_back(right);
@ -71,7 +71,7 @@ namespace hsql {
Expr* Expr::makeCase(Expr* expr, Expr* then, Expr* other) {
Expr* e = new Expr(kExprOperator);
e->expr = expr;
e->opType = CASE;
e->opType = kOpCase;
e->exprList = new std::vector<Expr*>();
e->exprList->push_back(then);
e->exprList->push_back(other);
@ -132,14 +132,14 @@ namespace hsql {
Expr* Expr::makeExists(SelectStatement* select) {
Expr* e = new Expr(kExprOperator);
e->opType = EXISTS;
e->opType = kOpExists;
e->select = select;
return e;
}
Expr* Expr::makeInOperator(Expr* expr, std::vector<Expr*>* exprList) {
Expr* e = new Expr(kExprOperator);
e->opType = IN;
e->opType = kOpIn;
e->expr = expr;
e->exprList = exprList;
@ -148,39 +148,39 @@ namespace hsql {
Expr* Expr::makeInOperator(Expr* expr, SelectStatement* select) {
Expr* e = new Expr(kExprOperator);
e->opType = IN;
e->opType = kOpIn;
e->expr = expr;
e->select = select;
return e;
}
bool Expr::isType(ExprType e_type) {
return e_type == type;
bool Expr::isType(ExprType exprType) const {
return exprType == type;
}
bool Expr::isLiteral() {
bool Expr::isLiteral() const {
return isType(kExprLiteralInt) || isType(kExprLiteralFloat) || isType(kExprLiteralString) || isType(kExprPlaceholder);
}
bool Expr::hasAlias() {
bool Expr::hasAlias() const {
return alias != NULL;
}
bool Expr::hasTable() {
bool Expr::hasTable() const {
return table != NULL;
}
char* Expr::getName() {
const char* Expr::getName() const {
if (alias != NULL) return alias;
else return name;
}
bool Expr::isSimpleOp() {
return opType == SIMPLE_OP;
bool Expr::isSimpleOp() const {
return opType == kOpSimple;
}
bool Expr::isSimpleOp(char op) {
bool Expr::isSimpleOp(char op) const {
return isSimpleOp() && opChar == op;
}

View File

@ -1,5 +1,5 @@
#ifndef __EXPRESSION_H__
#define __EXPRESSION_H__
#ifndef __SQLPARSER__EXPR_H__
#define __SQLPARSER__EXPR_H__
#include <stdlib.h>
#include <memory>
@ -24,48 +24,45 @@ namespace hsql {
kExprSelect
};
typedef struct Expr Expr;
// Represents SQL expressions (i.e. literals, operators, column_refs).
// TODO: When destructing a placeholder expression, we might need to alter the placeholder_list.
struct Expr {
// Operator types. These are important for expressions of type kExprOperator.
// Trivial types are those that can be described by a single character e.g:
// + - * / < > = %
// Non-trivial are: <> <= >= LIKE ISNULL NOT
enum OperatorType {
NONE,
kOpNone,
// Ternary operators
BETWEEN,
CASE,
kOpBetween,
kOpCase,
// Binary operators.
SIMPLE_OP,
NOT_EQUALS,
LESS_EQ,
GREATER_EQ,
LIKE,
NOT_LIKE,
AND,
OR,
IN,
// Simple operators are identified by the opChar field (e.g. +, -, =, >, <).
kOpSimple,
kOpNotEquals,
kOpLessEq,
kOpGreaterEq,
kOpLike,
kOpNotLike,
kOpAnd,
kOpOr,
kOpIn,
// Unary operators.
NOT,
UMINUS,
ISNULL,
EXISTS
kOpNot,
kOpMinus,
kOpIsNull,
kOpExists
};
typedef struct Expr Expr;
// Represents SQL expressions (i.e. literals, operators, column_refs).
// TODO: When destructing a placeholder expression, we might need to alter the placeholder_list.
struct Expr {
Expr(ExprType type);
// Interesting side-effect:
// Making the destructor virtual used to cause segmentation faults.
// TODO: inspect.
~Expr();
virtual ~Expr();
ExprType type;
@ -88,19 +85,19 @@ namespace hsql {
// Convenience accessor methods.
bool isType(ExprType e_type);
bool isType(ExprType exprType) const;
bool isLiteral();
bool isLiteral() const;
bool hasAlias();
bool hasAlias() const;
bool hasTable();
bool hasTable() const;
char* getName();
const char* getName() const;
bool isSimpleOp();
bool isSimpleOp() const;
bool isSimpleOp(char op);
bool isSimpleOp(char op) const;
// Static constructors.

View File

@ -1,5 +1,5 @@
#ifndef __IMPORT_STATEMENT_H__
#define __IMPORT_STATEMENT_H__
#ifndef __SQLPARSER__IMPORT_STATEMENT_H__
#define __SQLPARSER__IMPORT_STATEMENT_H__
#include "SQLStatement.h"

View File

@ -1,5 +1,5 @@
#ifndef __INSERT_STATEMENT_H__
#define __INSERT_STATEMENT_H__
#ifndef __SQLPARSER__INSERT_STATEMENT_H__
#define __SQLPARSER__INSERT_STATEMENT_H__
#include "SQLStatement.h"
#include "SelectStatement.h"

View File

@ -1,5 +1,5 @@
#ifndef __PREPARE_STATEMENT_H__
#define __PREPARE_STATEMENT_H__
#ifndef __SQLPARSER__PREPARE_STATEMENT_H__
#define __SQLPARSER__PREPARE_STATEMENT_H__
#include "../SQLParserResult.h"
#include "SQLStatement.h"
@ -24,6 +24,8 @@ namespace hsql {
void setPlaceholders(std::vector<void*> ph);
char* name;
// The result that is stored within this prepared statement.
SQLParserResult* query;
// The expressions are not owned by this statement.

View File

@ -1,5 +1,5 @@
#ifndef __SQLSTATEMENT_H__
#define __SQLSTATEMENT_H__
#ifndef __SQLPARSER__SQLSTATEMENT_H__
#define __SQLPARSER__SQLSTATEMENT_H__
#include "Expr.h"
#include <vector>
@ -29,11 +29,13 @@ namespace hsql {
virtual ~SQLStatement();
virtual StatementType type() const;
StatementType type() const;
bool isType(StatementType type) const;
private:
StatementType type_;
};
} // namespace hsql
#endif // __SQLSTATEMENT_H__
#endif // __SQLPARSER__SQLSTATEMENT_H__

View File

@ -1,5 +1,5 @@
#ifndef __SELECT_STATEMENT_H__
#define __SELECT_STATEMENT_H__
#ifndef __SQLPARSER__SELECT_STATEMENT_H__
#define __SQLPARSER__SELECT_STATEMENT_H__
#include "SQLStatement.h"
#include "Expr.h"
@ -13,7 +13,6 @@ namespace hsql {
/**
* Description of the order by clause within a select statement
* TODO: hold multiple expressions to be sorted by
*/
struct OrderDescription {
OrderDescription(OrderType type, Expr* expr);
@ -41,8 +40,7 @@ namespace hsql {
*/
struct GroupByDescription {
GroupByDescription();
// TODO: make virtual
~GroupByDescription();
virtual ~GroupByDescription();
std::vector<Expr*>* columns;
Expr* having;

View File

@ -1,5 +1,5 @@
#ifndef __TABLEREF_H__
#define __TABLEREF_H__
#ifndef __SQLPARSER__TABLEREF_H__
#define __SQLPARSER__TABLEREF_H__
#include "Expr.h"
#include <stdio.h>
@ -35,10 +35,10 @@ namespace hsql {
JoinDefinition* join;
// Returns true if a schema is set.
bool hasSchema();
bool hasSchema() const;
// Returns the alias, if it is set. Otherwise the name.
char* getName();
const char* getName() const;
};
// Possible types of joins.

View File

@ -1,5 +1,5 @@
#ifndef __UPDATE_STATEMENT_H__
#define __UPDATE_STATEMENT_H__
#ifndef __SQLPARSER__UPDATE_STATEMENT_H__
#define __SQLPARSER__UPDATE_STATEMENT_H__
#include "SQLStatement.h"

View File

@ -13,6 +13,10 @@ namespace hsql {
return type_;
}
bool SQLStatement::isType(StatementType type) const {
return (type_ == type);
}
// ColumnDefinition
ColumnDefinition::ColumnDefinition(char* name, DataType type) :
name(name),
@ -268,11 +272,11 @@ namespace hsql {
}
}
bool TableRef::hasSchema() {
bool TableRef::hasSchema() const {
return schema != NULL;
}
char* TableRef::getName() {
const char* TableRef::getName() const {
if (alias != NULL) return alias;
else return name;
}

View File

@ -1,5 +1,5 @@
#ifndef __STATEMENTS_H__
#define __STATEMENTS_H__
#ifndef __SQLPARSER__STATEMENTS_H__
#define __SQLPARSER__STATEMENTS_H__
#include "SelectStatement.h"
#include "ImportStatement.h"
@ -11,4 +11,4 @@
#include "PrepareStatement.h"
#include "ExecuteStatement.h"
#endif // __STATEMENTS_H__
#endif // __SQLPARSER__STATEMENTS_H__

View File

@ -63,16 +63,16 @@ namespace hsql {
}
switch (expr->opType) {
case Expr::SIMPLE_OP:
case kOpSimple:
inprintC(expr->opChar, numIndent);
break;
case Expr::AND:
case kOpAnd:
inprint("AND", numIndent);
break;
case Expr::OR:
case kOpOr:
inprint("OR", numIndent);
break;
case Expr::NOT:
case kOpNot:
inprint("NOT", numIndent);
break;
default:

View File

@ -1,7 +1,7 @@
#ifndef __SQLHELPER_H__
#define __SQLHELPER_H__
#ifndef __SQLPARSER__SQLHELPER_H__
#define __SQLPARSER__SQLHELPER_H__
#include "sql/statements.h"
#include "../sql/statements.h"
namespace hsql {

82
test/prepare_tests.cpp Normal file
View File

@ -0,0 +1,82 @@
#include "thirdparty/microtest/microtest.h"
#include "sql_asserts.h"
#include "SQLParser.h"
using hsql::kExprPlaceholder;
using hsql::kStmtDrop;
using hsql::kStmtExecute;
using hsql::kStmtInsert;
using hsql::kStmtPrepare;
using hsql::kStmtSelect;
using hsql::DropStatement;
using hsql::ExecuteStatement;
using hsql::InsertStatement;
using hsql::PrepareStatement;
using hsql::SelectStatement;
TEST(PrepareSingleStatementTest) {
const std::string query = "PREPARE test: SELECT * FROM students WHERE grade = ?;";
TEST_PARSE_SINGLE_SQL(query, kStmtPrepare, PrepareStatement, result, prepare);
const SelectStatement* select = (const SelectStatement*) prepare->query->getStatement(0);
ASSERT(select->whereClause->isSimpleOp('='));
ASSERT_EQ(select->whereClause->expr2, prepare->placeholders[0])
}
TEST(PrepareMultiStatementTest) {
const std::string query = "PREPARE test {"
"INSERT INTO test VALUES(?);"
"SELECT ?, test FROM test WHERE c1 = ?;"
"};"
"PREPARE stmt: SELECT * FROM data WHERE c1 = ?;"
"DEALLOCATE PREPARE stmt;";
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, DropStatement::kPreparedStatement);
ASSERT_STREQ(drop->name, "stmt");
}
TEST(ExecuteStatementTest) {
TEST_PARSE_SINGLE_SQL("EXECUTE test(1, 2);", kStmtExecute, ExecuteStatement, result, stmt);
ASSERT_STREQ(stmt->name, "test");
ASSERT_EQ(stmt->parameters->size(), 2);
}

View File

@ -16,8 +16,6 @@ TEST(SelectTest) {
ASSERT_NULL(stmt->whereClause);
ASSERT_NULL(stmt->groupBy);
delete result;
}
TEST(SelectExprTest) {
@ -55,8 +53,6 @@ TEST(SelectExprTest) {
ASSERT_EQ(stmt->selectList->at(2)->exprList->at(1)->exprList->size(), 1);
ASSERT(stmt->selectList->at(2)->exprList->at(1)->exprList->at(0)->isType(kExprColumnRef));
ASSERT_STREQ(stmt->selectList->at(2)->exprList->at(1)->exprList->at(0)->getName(), "un");
delete result;
}
@ -76,8 +72,6 @@ TEST(SelectHavingTest) {
ASSERT(group->having->isSimpleOp('<'));
ASSERT(group->having->expr->isType(kExprFunctionRef));
ASSERT(group->having->expr2->isType(kExprLiteralFloat));
delete result;
}
@ -91,8 +85,6 @@ TEST(SelectDistinctTest) {
ASSERT(stmt->selectDistinct);
ASSERT_NULL(stmt->whereClause);
delete result;
}
TEST(SelectGroupDistinctTest) {
@ -107,8 +99,6 @@ TEST(SelectGroupDistinctTest) {
ASSERT_EQ(stmt->selectList->size(), 3);
ASSERT(!stmt->selectList->at(1)->distinct);
ASSERT(stmt->selectList->at(2)->distinct);
delete result;
}
TEST(OrderByTest) {
@ -128,8 +118,6 @@ TEST(OrderByTest) {
ASSERT_EQ(stmt->order->at(1)->type, kOrderDesc);
ASSERT_STREQ(stmt->order->at(1)->expr->name, "city");
delete result;
}
TEST(SelectBetweenTest) {
@ -144,7 +132,7 @@ TEST(SelectBetweenTest) {
Expr* where = stmt->whereClause;
ASSERT_NOTNULL(where);
ASSERT(where->isType(kExprOperator));
ASSERT_EQ(where->opType, Expr::BETWEEN);
ASSERT_EQ(where->opType, kOpBetween);
ASSERT_STREQ(where->expr->getName(), "grade");
ASSERT(where->expr->isType(kExprColumnRef));
@ -154,8 +142,6 @@ TEST(SelectBetweenTest) {
ASSERT_EQ(where->exprList->at(0)->ival, 1);
ASSERT(where->exprList->at(1)->isType(kExprColumnRef));
ASSERT_STREQ(where->exprList->at(1)->getName(), "c");
delete result;
}
TEST(SelectConditionalSelectTest) {
@ -169,7 +155,7 @@ TEST(SelectConditionalSelectTest) {
Expr* where = stmt->whereClause;
ASSERT_NOTNULL(where);
ASSERT(where->isType(kExprOperator));
ASSERT_EQ(where->opType, Expr::AND);
ASSERT_EQ(where->opType, kOpAnd);
// a = (SELECT ...)
Expr* cond1 = where->expr;
@ -189,13 +175,11 @@ TEST(SelectConditionalSelectTest) {
// EXISTS (SELECT ...)
Expr* cond2 = where->expr2;
ASSERT_EQ(cond2->opType, Expr::EXISTS);
ASSERT_EQ(cond2->opType, kOpExists);
ASSERT_NOTNULL(cond2->select);
SelectStatement* ex_select = cond2->select;
ASSERT_STREQ(ex_select->fromTable->getName(), "test");
delete result;
}
TEST(SelectCaseWhen) {
@ -216,10 +200,8 @@ TEST(SelectCaseWhen) {
Expr* caseExpr = func->exprList->at(0);
ASSERT_NOTNULL(caseExpr);
ASSERT(caseExpr->isType(kExprOperator));
ASSERT_EQ(caseExpr->opType, Expr::CASE);
ASSERT_EQ(caseExpr->opType, kOpCase);
ASSERT(caseExpr->expr->isType(kExprOperator));
ASSERT(caseExpr->expr->isSimpleOp('='));
ASSERT_EQ(caseExpr->exprList->size(), 2);
delete result;
}

View File

@ -3,20 +3,21 @@
#define TEST_PARSE_SQL_QUERY(query, result, numStatements) \
const SQLParserResult* result = SQLParser::parseSQLString(query); \
ASSERT(result->isValid()); \
ASSERT_EQ(result->size(), numStatements);
hsql::SQLParserResult result; \
hsql::SQLParser::parseSQLString(query, &result); \
ASSERT(result.isValid()); \
ASSERT_EQ(result.size(), numStatements);
#define TEST_PARSE_SINGLE_SQL(query, stmtType, stmtClass, result, outputVar) \
TEST_PARSE_SQL_QUERY(query, result, 1); \
ASSERT_EQ(result->getStatement(0)->type(), stmtType); \
const stmtClass* outputVar = (const stmtClass*) result->getStatement(0);
ASSERT_EQ(result.getStatement(0)->type(), stmtType); \
const stmtClass* outputVar = (const stmtClass*) result.getStatement(0);
#define TEST_CAST_STMT(result, stmt_index, stmtType, stmtClass, outputVar) \
ASSERT_EQ(result->getStatement(stmt_index)->type(), stmtType); \
const stmtClass* outputVar = (const stmtClass*) result->getStatement(stmt_index);
ASSERT_EQ(result.getStatement(stmt_index)->type(), stmtType); \
const stmtClass* outputVar = (const stmtClass*) result.getStatement(stmt_index);
#endif

View File

@ -79,23 +79,22 @@ TEST(AutoGrammarTest) {
start = std::chrono::system_clock::now();
// Parsing
SQLParserResult* result = SQLParser::parseSQLString(sql.c_str());
SQLParserResult result;
SQLParser::parseSQLString(sql.c_str(), &result);
end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end - start;
double us = elapsed_seconds.count() * 1000 * 1000;
if (expectFalse == result->isValid()) {
if (expectFalse == result.isValid()) {
printf("\033[0;31m{ failed}\033[0m\n");
printf("\t\033[0;31m%s (L%d:%d)\n\033[0m", result->errorMsg(), result->errorLine(), result->errorColumn());
printf("\t\033[0;31m%s (L%d:%d)\n\033[0m", result.errorMsg(), result.errorLine(), result.errorColumn());
printf("\t%s\n", sql.c_str());
numFailed++;
} else {
// TODO: indicate whether expectFalse was set
printf("\033[0;32m{ ok} (%.1fus)\033[0m %s\n", us, sql.c_str());
}
delete result;
}
if (numFailed == 0) {

View File

@ -3,36 +3,40 @@
*/
#include "thirdparty/microtest/microtest.h"
#include "sql_asserts.h"
#include "SQLParser.h"
#include "sqlhelper.h"
#include "util/sqlhelper.h"
#include "sql_asserts.h"
using namespace hsql;
TEST(DeleteStatementTest) {
const SQLParserResult* result = SQLParser::parseSQLString("DELETE FROM students WHERE grade > 2.0;");
ASSERT(result->isValid());
ASSERT_EQ(result->size(), 1);
ASSERT(result->getStatement(0)->type() == kStmtDelete);
SQLParserResult result;
SQLParser::parseSQLString("DELETE FROM students WHERE grade > 2.0;", &result);
const DeleteStatement* stmt = (const DeleteStatement*) result->getStatement(0);
ASSERT(result.isValid());
ASSERT_EQ(result.size(), 1);
ASSERT(result.getStatement(0)->type() == kStmtDelete);
const DeleteStatement* stmt = (const DeleteStatement*) result.getStatement(0);
ASSERT_STREQ(stmt->tableName, "students");
ASSERT_NOTNULL(stmt->expr);
ASSERT(stmt->expr->isType(kExprOperator));
ASSERT_STREQ(stmt->expr->expr->name, "grade");
ASSERT_EQ(stmt->expr->expr2->fval, 2.0);
delete result;
}
TEST(CreateStatementTest) {
const SQLParserResult* result = SQLParser::parseSQLString("CREATE TABLE students (name TEXT, student_number INT, city INTEGER, grade DOUBLE)");
ASSERT(result->isValid());
ASSERT_EQ(result->size(), 1);
ASSERT_EQ(result->getStatement(0)->type(), kStmtCreate);
SQLParserResult result;
SQLParser::parseSQLString("CREATE TABLE students (name TEXT, student_number INT, city INTEGER, grade DOUBLE)", &result);
const CreateStatement* stmt = (const CreateStatement*) result->getStatement(0);
ASSERT(result.isValid());
ASSERT_EQ(result.size(), 1);
ASSERT_EQ(result.getStatement(0)->type(), kStmtCreate);
const CreateStatement* stmt = (const CreateStatement*) result.getStatement(0);
ASSERT_EQ(stmt->type, CreateStatement::kTable);
ASSERT_STREQ(stmt->tableName, "students");
ASSERT_NOTNULL(stmt->columns);
@ -45,18 +49,18 @@ TEST(CreateStatementTest) {
ASSERT_EQ(stmt->columns->at(1)->type, ColumnDefinition::INT);
ASSERT_EQ(stmt->columns->at(2)->type, ColumnDefinition::INT);
ASSERT_EQ(stmt->columns->at(3)->type, ColumnDefinition::DOUBLE);
delete result;
}
TEST(UpdateStatementTest) {
const SQLParserResult* result = SQLParser::parseSQLString("UPDATE students SET grade = 5.0, name = 'test' WHERE name = 'Max Mustermann';");
ASSERT(result->isValid());
ASSERT_EQ(result->size(), 1);
ASSERT_EQ(result->getStatement(0)->type(), kStmtUpdate);
SQLParserResult result;
SQLParser::parseSQLString("UPDATE students SET grade = 5.0, name = 'test' WHERE name = 'Max Mustermann';", &result);
const UpdateStatement* stmt = (const UpdateStatement*) result->getStatement(0);
ASSERT(result.isValid());
ASSERT_EQ(result.size(), 1);
ASSERT_EQ(result.getStatement(0)->type(), kStmtUpdate);
const UpdateStatement* stmt = (const UpdateStatement*) result.getStatement(0);
ASSERT_NOTNULL(stmt->table);
ASSERT_STREQ(stmt->table->name, "students");
@ -74,9 +78,6 @@ TEST(UpdateStatementTest) {
ASSERT(stmt->where->isSimpleOp('='));
ASSERT_STREQ(stmt->where->expr->name, "name");
ASSERT_STREQ(stmt->where->expr2->name, "Max Mustermann");
\
delete result;
}
@ -90,8 +91,6 @@ TEST(InsertStatementTest) {
ASSERT_EQ(stmt->values->size(), 4);
// TODO
delete result;
}
@ -106,66 +105,26 @@ TEST(DropTableStatementTest) {
ASSERT_EQ(stmt->type, DropStatement::kTable);
ASSERT_NOTNULL(stmt->name);
ASSERT_STREQ(stmt->name, "students");
delete result;
}
TEST(ReleaseStatementTest) {
TEST_PARSE_SINGLE_SQL(
"SELECT * FROM students;",
kStmtSelect,
SelectStatement,
result,
stmt);
TEST(PrepareStatementTest) {
std::string query = "PREPARE test {"
"INSERT INTO test VALUES(?);"
"SELECT ?, test FROM test WHERE c1 = ?;"
"};"
"PREPARE stmt: SELECT * FROM data WHERE c1 = ?;"
"DEALLOCATE PREPARE stmt;";
ASSERT_EQ(1, result.size());
ASSERT_NULL(stmt->whereClause);
TEST_PARSE_SQL_QUERY(query, result, 3);
std::vector<SQLStatement*> statements = result.releaseStatements();
TEST_CAST_STMT(result, 0, kStmtPrepare, PrepareStatement, prep1);
TEST_CAST_STMT(result, 1, kStmtPrepare, PrepareStatement, prep2);
TEST_CAST_STMT(result, 2, kStmtDrop, DropStatement, drop);
ASSERT_EQ(0, result.size());
// 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, DropStatement::kPreparedStatement);
ASSERT_STREQ(drop->name, "stmt");
delete result;
}
TEST(ExecuteStatementTest) {
TEST_PARSE_SINGLE_SQL("EXECUTE test(1, 2);", kStmtExecute, ExecuteStatement, result, stmt);
ASSERT_STREQ(stmt->name, "test");
ASSERT_EQ(stmt->parameters->size(), 2);
delete result;
for (SQLStatement* stmt : statements) {
delete stmt;
}
}
TEST_MAIN();

View File

@ -1,7 +1,9 @@
#include "thirdparty/microtest/microtest.h"
#include "sql_asserts.h"
#include "SQLParser.h"
#include "sqlhelper.h"
#include "util/sqlhelper.h"
#include "sql_asserts.h"
#include <string>
#include <fstream>
@ -37,15 +39,15 @@ TEST(TPCHQueryGrammarTests) {
for (const std::string& file_path : files) {
std::string query = readFileContents(file_path);
SQLParserResult* result = SQLParser::parseSQLString(query.c_str());
if (!result->isValid()) {
SQLParserResult result;
SQLParser::parseSQLString(query.c_str(), &result);
if (!result.isValid()) {
mt::printFailed(file_path.c_str());
printf("%s %s (L%d:%d)%s\n", mt::red(), result->errorMsg(), result->errorLine(), result->errorColumn(), mt::def());
printf("%s %s (L%d:%d)%s\n", mt::red(), result.errorMsg(), result.errorLine(), result.errorColumn(), mt::def());
++testsFailed;
} else {
mt::printOk(file_path.c_str());
}
delete result;
}
ASSERT_EQ(testsFailed, 0);
}
@ -53,11 +55,12 @@ TEST(TPCHQueryGrammarTests) {
TEST(TPCHQueryDetailTest) {
std::string query = readFileContents("test/queries/tpc-h-16-22.sql");
SQLParserResult* result = SQLParser::parseSQLString(query.c_str());
ASSERT(result->isValid());
ASSERT_EQ(result->size(), 7);
SQLParserResult result;
SQLParser::parseSQLString(query.c_str(), &result);
ASSERT(result.isValid());
ASSERT_EQ(result.size(), 7);
const SQLStatement* stmt20 = result->getStatement(4);
const SQLStatement* stmt20 = result.getStatement(4);
ASSERT_EQ(stmt20->type(), kStmtSelect);
const SelectStatement* select20 = (const SelectStatement*) stmt20;
@ -69,18 +72,18 @@ TEST(TPCHQueryDetailTest) {
Expr* where = select20->whereClause;
ASSERT_NOTNULL(where);
ASSERT(where->isType(kExprOperator));
ASSERT_EQ(where->opType, Expr::AND);
ASSERT_EQ(where->opType, kOpAnd);
Expr* andExpr2 = where->expr;
ASSERT_NOTNULL(andExpr2);
ASSERT(andExpr2->isType(kExprOperator));
ASSERT_EQ(andExpr2->opType, Expr::AND);
ASSERT_EQ(andExpr2->opType, kOpAnd);
// Test IN expression.
Expr* inExpr = andExpr2->expr;
ASSERT_NOTNULL(inExpr);
ASSERT(inExpr->isType(kExprOperator));
ASSERT_EQ(inExpr->opType, Expr::IN);
ASSERT_EQ(inExpr->opType, kOpIn);
ASSERT_STREQ(inExpr->expr->getName(), "S_SUPPKEY");
ASSERT_NOTNULL(inExpr->select);
@ -93,6 +96,4 @@ TEST(TPCHQueryDetailTest) {
ASSERT_EQ(select20->order->size(), 1);
ASSERT(select20->order->at(0)->expr->isType(kExprColumnRef));
ASSERT_STREQ(select20->order->at(0)->expr->getName(), "S_NAME");
delete result;
}