Merge pull request #12 from torpedro/api-cleanup

Improve API & formatting. Adds clang support & travis ci
This commit is contained in:
Pedro Flemming 2016-02-27 17:49:32 +01:00
commit 7cfae24503
46 changed files with 1451 additions and 1400 deletions

3
.gitignore vendored
View File

@ -31,3 +31,6 @@ lib-test/
*.exe *.exe
*.out *.out
*.app *.app
*.cpp.orig
*.h.orig

20
.travis.yml Normal file
View File

@ -0,0 +1,20 @@
language: cpp
install:
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
- sudo apt-get -qq update
- sudo apt-get install -y bison flex
- sudo apt-get install -y g++-4.8 libstdc++-4.8-dev
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 90
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 90
- which g++
- g++ -v
compiler:
- gcc
- clang
script:
- make
- make test

View File

@ -9,25 +9,27 @@ LIBCPP = $(shell find $(SRC) -name '*.cpp' -not -path "$(SRCPARSER)/*") $(S
LIBOBJ = $(LIBCPP:%.cpp=%.o) LIBOBJ = $(LIBCPP:%.cpp=%.o)
TESTCPP = $(shell find test/lib/ -name '*.cpp') TESTCPP = $(shell find test/lib/ -name '*.cpp')
ALLLIB = $(shell find $(SRC) -name '*.cpp' -not -path "$(SRCPARSER)/*") $(shell find $(SRC) -name '*.h' -not -path "$(SRCPARSER)/*")
ALLTEST = $(shell find test/lib/ -name '*.cpp') $(shell find test/lib/ -name '*.h')
# compile & link flages # compile & link flages
CC = g++
CFLAGS = -std=c++11 -Wall -fPIC CFLAGS = -std=c++11 -Wall -fPIC
LIBFLAGS = -shared LIBFLAGS = -shared
TARGET = libsqlparser.so TARGET = libsqlparser.so
INSTALL = /usr/local INSTALL = /usr/local
CTESTFLAGS = -Wall -Isrc/ -Itest/ -L./ -std=c++11 CTESTFLAGS = -Wall -Isrc/ -Itest/ -L./ -std=c++11 -lstdc++
all: library all: library
library: $(TARGET) library: $(TARGET)
$(TARGET): $(LIBOBJ) $(TARGET): $(LIBOBJ)
$(CC) $(LIBFLAGS) -o $(TARGET) $(LIBOBJ) $(CXX) $(LIBFLAGS) -o $(TARGET) $(LIBOBJ)
%.o: %.cpp $(PARSERFILES) %.o: %.cpp $(PARSERFILES)
$(CC) $(CFLAGS) -c -o $@ $< $(CXX) $(CFLAGS) -c -o $@ $<
$(SRCPARSER)/bison_parser.cpp: parser $(SRCPARSER)/bison_parser.cpp: parser
$(SRCPARSER)/flex_lexer.cpp: parser $(SRCPARSER)/flex_lexer.cpp: parser
@ -48,20 +50,26 @@ cleanall: clean cleanparser
install: install:
cp $(TARGET) $(INSTALL)/lib/$(TARGET) cp $(TARGET) $(INSTALL)/lib/$(TARGET)
format:
astyle --options=astyle.options $(ALLLIB)
astyle --options=astyle.options $(ALLTEST)
############ ############
### Test ### ### Test ###
############ ############
test: $(BIN)/sql_tests $(BIN)/sql_grammar_test test: $(BIN)/sql_tests $(BIN)/sql_grammar_test
LD_LIBRARY_PATH=./ $(BIN)/sql_grammar_test -f "test/lib/valid_queries.sql" bash test/test.sh
LD_LIBRARY_PATH=./ $(BIN)/sql_tests
# test whete
test_install:
make -C example/
./example/example "SELECT * FROM students WHERE name = 'Max Mustermann';"
$(BIN)/sql_tests: library $(BIN)/sql_tests: library
@mkdir -p $(BIN)/ @mkdir -p $(BIN)/
$(CC) $(CTESTFLAGS) $(TESTCPP) test/sql_tests.cpp -o $(BIN)/sql_tests -lsqlparser $(CXX) $(CTESTFLAGS) $(TESTCPP) test/sql_tests.cpp -o $(BIN)/sql_tests -lsqlparser
$(BIN)/sql_grammar_test: library $(BIN)/sql_grammar_test: library
@mkdir -p $(BIN)/ @mkdir -p $(BIN)/
$(CC) $(CTESTFLAGS) test/sql_grammar_test.cpp -o $(BIN)/sql_grammar_test -lsqlparser $(CXX) $(CTESTFLAGS) test/sql_grammar_test.cpp -o $(BIN)/sql_grammar_test -lsqlparser

View File

@ -9,20 +9,25 @@ In March 2015 we've also written a short paper outlining discussing some develop
### Usage ### Usage
To use the SQL parser in your own projects you simply have to follow these few steps. The only requirement for is GCC 4.8+. Older versions of GCC probably also work, but are untested. **Requirements:**
* gcc 4.8+
To use the SQL parser in your own projects you simply have to follow these few steps. The only requirement for is gcc 4.8+. Older versions of gcc might also work, but are untested.
1. Download the [latest release here](https://github.com/hyrise/sql-parser/releases) 1. Download the [latest release here](https://github.com/hyrise/sql-parser/releases)
2. Compile the library `make` to create `libsqlparser.so` 2. Compile the library `make` to create `libsqlparser.so`
3. *(Optional)* Run `make install` to copy the library to `/usr/local/lib/`
3. Run the tests `make test` to make sure everything worked 3. Run the tests `make test` to make sure everything worked
4. Take a look at the [example project here](https://github.com/hyrise/sql-parser/tree/dynamic-library/example) 4. Take a look at the [example project here](https://github.com/hyrise/sql-parser/tree/master/example)
5. Include the `SQLParser.h` from `src/` and link the library in your project 5. Include the `SQLParser.h` from `src/` and link the library in your project
### Development ### Development
**Prerequisites:** **Requirements for development:**
* [bison](https://www.gnu.org/software/bison/) (tested with v3.0.2) * gcc 4.8 (or newer)
* [flex](http://flex.sourceforge.net/) (tested with v2.5.5) * [bison](https://www.gnu.org/software/bison/) (tested with v3.0.2)
* [flex](http://flex.sourceforge.net/) (tested with v2.5.5)
First step to extending this parser is cloning the repository `git clone git@github.com:hyrise/sql-parser.git` and making sure everything works by running the following steps: First step to extending this parser is cloning the repository `git clone git@github.com:hyrise/sql-parser.git` and making sure everything works by running the following steps:
@ -45,12 +50,10 @@ make test # build parser, library and runs the tests
We strongly encourage you to contribute to this project! If you want to contribute to this project there are several options. If you've noticed a bug or would like an improvement let us know by creating a [new issue](https://github.com/hyrise/sql-parser/issues). If you want to develop a new feature yourself or just improve the quality of the system, feel free to fork the reposistory and implement your changes. Open a pull request as soon as your done and we will look over it. If we think it's good then your pull request will be merged into this repository. We strongly encourage you to contribute to this project! If you want to contribute to this project there are several options. If you've noticed a bug or would like an improvement let us know by creating a [new issue](https://github.com/hyrise/sql-parser/issues). If you want to develop a new feature yourself or just improve the quality of the system, feel free to fork the reposistory and implement your changes. Open a pull request as soon as your done and we will look over it. If we think it's good then your pull request will be merged into this repository.
### Documenation ### Resources
* [Working Syntax Examples](docs/syntax.md) * [Working Syntax Examples](docs/syntax.md)
* [Known Issues](docs/issues.md) * [Developer Documentation](docs/dev-docs.md)
* [Developer Documentation](docs/documentation.md)
* [Integration in Hyrise](docs/integration.md)
### License ### License

8
astyle.options Normal file
View File

@ -0,0 +1,8 @@
# indentation
--indent=spaces=4
--indent-namespaces
--style=java
--style=attach
-A2

View File

@ -1,29 +1,31 @@
Developer Documentation Developer Documentation
======================= =======================
This page contains information about how to extend this parser with new functionalities. ## Developing New Functionality
This section contains information about how to extend this parser with new functionalities.
### Implementing a new Statement
## Implementing Statement Class Create a new file and class in `src/sql/` or extend any of the existing Statements. Every statement needs to have the base class SQLStatement and needs to call its super constructor with its type. If your defining a new statement type, you need to define a new StatementType in `SQLStatement.h`.
Create a new file and class in src/lib/statements/ or extend any of the existing Statements. Every statement needs to have the base class SQLStatement and needs to call its super constructor with its type. If your defining a new statement type, you need to define a new StatementType in SQLStatement.h.
It is important that you create an appropriate constructor for your statement that zero-initializes all its pointer variables and that your create an appropriate destructor. It is important that you create an appropriate constructor for your statement that zero-initializes all its pointer variables and that your create an appropriate destructor.
Lastly you need to include your new file in src/lib/sqllib.h Finally you will need to include your new file in `src/sql/statements.h`.
### Extending the Grammar
## Extending the Grammar
Related files: Related files:
* src/parser/bison_parser.y ````
* src/parser/flex_lexer.l src/parser/bison_parser.y
* src/parser/keywordlist_generator.py src/parser/flex_lexer.l
* src/parser/sql_keywords.txt src/parser/keywordlist_generator.py
src/parser/sql_keywords.txt
```
To extend the grammar the file you will mostly have to deal with is the bison grammar definition in src/parser/bison_parser.y. To extend the grammar the file you will mostly have to deal with is the bison grammar definition in `src/parser/bison_parser.y`.
If your extending an existing statement, skip to the non-terminal definition for that statement. I.e. for an InsertStatement the non-terminal insert_statement. If your extending an existing statement, skip to the non-terminal definition for that statement. I.e. for an InsertStatement the non-terminal insert_statement.
@ -33,7 +35,6 @@ If your defining a new statement, you will need to define your type in the \%uni
## Implementing Tests ## Implementing Tests
Related files: All test related files are in `test/`. Take a look to see how tests are implemented.
* src/sql_tests.cpp

View File

@ -1,20 +0,0 @@
@PROJECT_NAME = "SQL Parser for Hyrise (C++)"
@OUTPUT_DIRECTORY = docs/__doxygen__/
@GENERATE_LATEX = NO
@GENERATE_HTML = YES
@INCLUDE_FILE_PATTERNS = *.y *.l
@FILE_PATTERNS = *.y *.l *.h *.cpp *.md
@INPUT = README.md \
docs/ \
src/parser/SQLParser.h \
src/parser/SQLParser.cpp \
src/parser/bison_parser.y \
src/parser/flex_lexer.l \
src/lib/ \
@RECURSIVE = YES

View File

@ -1,8 +0,0 @@
Integration in Hyrise
=====================
On this page we describe how to integrate changes to the parser into Hyrise.
## Update the Parser code in Hyrise
Run `./deploy_to_hyrise.sh path/to/hyrise` to update the SQL parser code within Hyrise.

View File

@ -1,10 +0,0 @@
Known Issues
============
Here we will keep track of issues with the parser and the integration in Hyrise.
## Missing Functionality
* Union clauses
* Create anything other than tables
* Alter/Rename statements

View File

@ -1,7 +1,6 @@
CC = g++ CFLAGS = -std=c++11 -lstdc++ -Wall -I../src/ -L../
CFLAGS = -std=c++11 -Wall -I../src/ -L../
all: all:
$(CC) $(CFLAGS) example.cpp -o example -lsqlparser $(CXX) $(CFLAGS) example.cpp -o example -lsqlparser

View File

@ -16,20 +16,21 @@ int main(int argc, char *argv[]) {
std::string query = argv[1]; std::string query = argv[1];
// parse a given query // parse a given query
hsql::SQLStatementList* result = hsql::SQLParser::parseSQLString(query); hsql::SQLParserResult* result = hsql::SQLParser::parseSQLString(query);
// check whether the parsing was successful // check whether the parsing was successful
if (result->isValid) { if (result->isValid) {
printf("Parsed successfully!\n"); printf("Parsed successfully!\n");
printf("Number of statements: %lu\n", result->numStatements()); printf("Number of statements: %lu\n", result->size());
for (hsql::SQLStatement* stmt : result->statements) { for (hsql::SQLStatement* stmt : result->statements) {
// process the statements... // process the statements...
hsql::printStatementInfo(stmt); hsql::printStatementInfo(stmt);
} }
return 0;
} else { } else {
printf("Invalid SQL!\n"); printf("Invalid SQL!\n");
return -1;
} }
return 0;
} }

View File

@ -8,38 +8,39 @@
namespace hsql { namespace hsql {
SQLParser::SQLParser() { SQLParser::SQLParser() {
fprintf(stderr, "SQLParser only has static methods atm! Do not initialize!\n"); fprintf(stderr, "SQLParser only has static methods atm! Do not initialize!\n");
}
SQLStatementList* SQLParser::parseSQLString(const char *text) {
SQLStatementList* result;
yyscan_t scanner;
YY_BUFFER_STATE state;
if (hsql_lex_init(&scanner)) {
// couldn't initialize
fprintf(stderr, "[Error] SQLParser: Error when initializing lexer!\n");
return NULL;
} }
state = hsql__scan_string(text, scanner);
if (hsql_parse(&result, scanner)) { SQLParserResult* SQLParser::parseSQLString(const char *text) {
// Returns an error stmt object SQLParserResult* result = NULL;
yyscan_t scanner;
YY_BUFFER_STATE state;
if (hsql_lex_init(&scanner)) {
// couldn't initialize
fprintf(stderr, "[Error] SQLParser: Error when initializing lexer!\n");
return NULL;
}
state = hsql__scan_string(text, scanner);
if (hsql_parse(&result, scanner)) {
// Returns an error stmt object
return result;
}
hsql__delete_buffer(state, scanner);
hsql_lex_destroy(scanner);
return result; return result;
} }
hsql__delete_buffer(state, scanner);
hsql_lex_destroy(scanner); SQLParserResult* SQLParser::parseSQLString(const std::string& text) {
return result; return parseSQLString(text.c_str());
} }
SQLStatementList* SQLParser::parseSQLString(const std::string& text) {
return parseSQLString(text.c_str());
}
} // namespace hsql } // namespace hsql

View File

@ -1,27 +1,23 @@
#ifndef __SQLPARSER_H_ #ifndef __SQLPARSER_H_
#define __SQLPARSER_H_ #define __SQLPARSER_H_
#include "sqltypes.h" #include "SQLParserResult.h"
#include "sql/statements.h"
namespace hsql { namespace hsql {
/**
* Main class for parsing SQL strings
*/
class SQLParser {
public:
static SQLParserResult* parseSQLString(const char* sql);
static SQLParserResult* parseSQLString(const std::string& sql);
/*! private:
* \mainpage SQLParser (C++) SQLParser();
*/ };
/*!
* @brief Main class for parsing SQL strings
*/
class SQLParser {
public:
static SQLStatementList* parseSQLString(const char* sql);
static SQLStatementList* parseSQLString(const std::string& sql);
private:
SQLParser();
};
} // namespace hsql } // namespace hsql

41
src/SQLParserResult.cpp Normal file
View File

@ -0,0 +1,41 @@
#include "SQLParserResult.h"
namespace hsql {
SQLParserResult::SQLParserResult() :
isValid(true),
errorMsg(NULL) {};
SQLParserResult::SQLParserResult(SQLStatement* stmt) :
isValid(true),
errorMsg(NULL) {
addStatement(stmt);
};
SQLParserResult::~SQLParserResult() {
for (std::vector<SQLStatement*>::iterator it = statements.begin(); it != statements.end(); ++it) {
delete *it;
}
delete errorMsg;
}
void SQLParserResult::addStatement(SQLStatement* stmt) {
statements.push_back(stmt);
}
SQLStatement* SQLParserResult::getStatement(int id) {
return statements[id];
}
size_t SQLParserResult::size() {
return statements.size();
}
} // namespace hsql

35
src/SQLParserResult.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef __SQLPARSERRESULT__
#define __SQLPARSERRESULT__
#include "sql/SQLStatement.h"
namespace hsql {
/**
* Represents the result of the SQLParser.
* If parsing was successful it contains a list of SQLStatement.
*/
class SQLParserResult {
public:
SQLParserResult();
SQLParserResult(SQLStatement* stmt);
virtual ~SQLParserResult();
void addStatement(SQLStatement* stmt);
SQLStatement* getStatement(int id);
size_t size();
// public properties
std::vector<SQLStatement*> statements;
bool isValid;
const char* errorMsg;
int errorLine;
int errorColumn;
};
} // namespace hsql
#endif // __SQLPARSERRESULT__

File diff suppressed because it is too large Load Diff

View File

@ -48,9 +48,12 @@
extern int hsql_debug; extern int hsql_debug;
#endif #endif
/* "%code requires" blocks. */ /* "%code requires" blocks. */
#line 43 "bison_parser.y" /* yacc.c:1909 */ #line 42 "bison_parser.y" /* yacc.c:1909 */
// %code requires block // %code requires block
#include "../sql/statements.h"
#include "../SQLParserResult.h"
#include "parser_typedef.h" #include "parser_typedef.h"
// Auto update column and line number // Auto update column and line number
@ -68,7 +71,7 @@ extern int hsql_debug;
} \ } \
} }
#line 72 "bison_parser.h" /* yacc.c:1909 */ #line 75 "bison_parser.h" /* yacc.c:1909 */
/* Token type. */ /* Token type. */
#ifndef HSQL_TOKENTYPE #ifndef HSQL_TOKENTYPE
@ -206,7 +209,7 @@ extern int hsql_debug;
typedef union HSQL_STYPE HSQL_STYPE; typedef union HSQL_STYPE HSQL_STYPE;
union HSQL_STYPE union HSQL_STYPE
{ {
#line 99 "bison_parser.y" /* yacc.c:1909 */ #line 101 "bison_parser.y" /* yacc.c:1909 */
double fval; double fval;
int64_t ival; int64_t ival;
@ -234,7 +237,7 @@ union HSQL_STYPE
hsql::GroupByDescription* group_t; hsql::GroupByDescription* group_t;
hsql::UpdateClause* update_t; hsql::UpdateClause* update_t;
hsql::SQLStatementList* stmt_list; hsql::SQLParserResult* stmt_list;
std::vector<char*>* str_vec; std::vector<char*>* str_vec;
std::vector<hsql::TableRef*>* table_vec; std::vector<hsql::TableRef*>* table_vec;
@ -242,7 +245,7 @@ union HSQL_STYPE
std::vector<hsql::UpdateClause*>* update_vec; std::vector<hsql::UpdateClause*>* update_vec;
std::vector<hsql::Expr*>* expr_vec; std::vector<hsql::Expr*>* expr_vec;
#line 246 "bison_parser.h" /* yacc.c:1909 */ #line 249 "bison_parser.h" /* yacc.c:1909 */
}; };
# define HSQL_STYPE_IS_TRIVIAL 1 # define HSQL_STYPE_IS_TRIVIAL 1
# define HSQL_STYPE_IS_DECLARED 1 # define HSQL_STYPE_IS_DECLARED 1
@ -264,6 +267,6 @@ struct HSQL_LTYPE
int hsql_parse (hsql::SQLStatementList** result, yyscan_t scanner); int hsql_parse (hsql::SQLParserResult** result, yyscan_t scanner);
#endif /* !YY_HSQL_BISON_PARSER_H_INCLUDED */ #endif /* !YY_HSQL_BISON_PARSER_H_INCLUDED */

View File

@ -11,7 +11,6 @@
** Section 1: C Declarations ** Section 1: C Declarations
*********************************/ *********************************/
#include "../sqltypes.h"
#include "bison_parser.h" #include "bison_parser.h"
#include "flex_lexer.h" #include "flex_lexer.h"
@ -19,13 +18,13 @@
using namespace hsql; using namespace hsql;
int yyerror(YYLTYPE* llocp, SQLStatementList** result, yyscan_t scanner, const char *msg) { int yyerror(YYLTYPE* llocp, SQLParserResult** result, yyscan_t scanner, const char *msg) {
SQLStatementList* list = new SQLStatementList(); SQLParserResult* list = new SQLParserResult();
list->isValid = false; list->isValid = false;
list->parser_msg = strdup(msg); list->errorMsg = strdup(msg);
list->error_line = llocp->first_line; list->errorLine = llocp->first_line;
list->error_col = llocp->first_column; list->errorColumn = llocp->first_column;
*result = list; *result = list;
return 0; return 0;
@ -42,6 +41,9 @@ int yyerror(YYLTYPE* llocp, SQLStatementList** result, yyscan_t scanner, const c
// Specify code that is included in the generated .h and .c files // Specify code that is included in the generated .h and .c files
%code requires { %code requires {
// %code requires block // %code requires block
#include "../sql/statements.h"
#include "../SQLParserResult.h"
#include "parser_typedef.h" #include "parser_typedef.h"
// Auto update column and line number // Auto update column and line number
@ -89,7 +91,7 @@ int yyerror(YYLTYPE* llocp, SQLStatementList** result, yyscan_t scanner, const c
%lex-param { yyscan_t scanner } %lex-param { yyscan_t scanner }
// Define additional parameters for yyparse // Define additional parameters for yyparse
%parse-param { hsql::SQLStatementList** result } %parse-param { hsql::SQLParserResult** result }
%parse-param { yyscan_t scanner } %parse-param { yyscan_t scanner }
@ -123,7 +125,7 @@ int yyerror(YYLTYPE* llocp, SQLStatementList** result, yyscan_t scanner, const c
hsql::GroupByDescription* group_t; hsql::GroupByDescription* group_t;
hsql::UpdateClause* update_t; hsql::UpdateClause* update_t;
hsql::SQLStatementList* stmt_list; hsql::SQLParserResult* stmt_list;
std::vector<char*>* str_vec; std::vector<char*>* str_vec;
std::vector<hsql::TableRef*>* table_vec; std::vector<hsql::TableRef*>* table_vec;
@ -231,7 +233,7 @@ input:
statement_list: statement_list:
statement { $$ = new SQLStatementList($1); } statement { $$ = new SQLParserResult($1); }
| statement_list ';' statement { $1->addStatement($3); $$ = $1; } | statement_list ';' statement { $1->addStatement($3); $$ = $1; }
; ;
@ -265,7 +267,7 @@ prepare_statement:
PREPARE IDENTIFIER ':' preparable_statement { PREPARE IDENTIFIER ':' preparable_statement {
$$ = new PrepareStatement(); $$ = new PrepareStatement();
$$->name = $2; $$->name = $2;
$$->query = new SQLStatementList($4); $$->query = new SQLParserResult($4);
} }
| PREPARE IDENTIFIER '{' statement_list opt_semicolon '}' { | PREPARE IDENTIFIER '{' statement_list opt_semicolon '}' {
$$ = new PrepareStatement(); $$ = new PrepareStatement();
@ -293,8 +295,8 @@ execute_statement:
import_statement: import_statement:
IMPORT FROM import_file_type FILE file_path INTO table_name { IMPORT FROM import_file_type FILE file_path INTO table_name {
$$ = new ImportStatement((ImportStatement::ImportType) $3); $$ = new ImportStatement((ImportStatement::ImportType) $3);
$$->file_path = $5; $$->filePath = $5;
$$->table_name = $7; $$->tableName = $7;
} }
; ;
@ -315,14 +317,14 @@ file_path:
create_statement: create_statement:
CREATE TABLE opt_not_exists table_name FROM TBL FILE file_path { CREATE TABLE opt_not_exists table_name FROM TBL FILE file_path {
$$ = new CreateStatement(CreateStatement::kTableFromTbl); $$ = new CreateStatement(CreateStatement::kTableFromTbl);
$$->if_not_exists = $3; $$->ifNotExists = $3;
$$->table_name = $4; $$->tableName = $4;
$$->file_path = $8; $$->filePath = $8;
} }
| CREATE TABLE opt_not_exists table_name '(' column_def_commalist ')' { | CREATE TABLE opt_not_exists table_name '(' column_def_commalist ')' {
$$ = new CreateStatement(CreateStatement::kTable); $$ = new CreateStatement(CreateStatement::kTable);
$$->if_not_exists = $3; $$->ifNotExists = $3;
$$->table_name = $4; $$->tableName = $4;
$$->columns = $6; $$->columns = $6;
} }
; ;
@ -376,7 +378,7 @@ drop_statement:
delete_statement: delete_statement:
DELETE FROM table_name opt_where { DELETE FROM table_name opt_where {
$$ = new DeleteStatement(); $$ = new DeleteStatement();
$$->table_name = $3; $$->tableName = $3;
$$->expr = $4; $$->expr = $4;
} }
; ;
@ -384,7 +386,7 @@ delete_statement:
truncate_statement: truncate_statement:
TRUNCATE table_name { TRUNCATE table_name {
$$ = new DeleteStatement(); $$ = new DeleteStatement();
$$->table_name = $2; $$->tableName = $2;
} }
; ;
@ -396,13 +398,13 @@ truncate_statement:
insert_statement: insert_statement:
INSERT INTO table_name opt_column_list VALUES '(' literal_list ')' { INSERT INTO table_name opt_column_list VALUES '(' literal_list ')' {
$$ = new InsertStatement(InsertStatement::kInsertValues); $$ = new InsertStatement(InsertStatement::kInsertValues);
$$->table_name = $3; $$->tableName = $3;
$$->columns = $4; $$->columns = $4;
$$->values = $7; $$->values = $7;
} }
| INSERT INTO table_name opt_column_list select_no_paren { | INSERT INTO table_name opt_column_list select_no_paren {
$$ = new InsertStatement(InsertStatement::kInsertSelect); $$ = new InsertStatement(InsertStatement::kInsertSelect);
$$->table_name = $3; $$->tableName = $3;
$$->columns = $4; $$->columns = $4;
$$->select = $5; $$->select = $5;
} }
@ -467,7 +469,7 @@ select_no_paren:
// TODO: capture type of set_operator // TODO: capture type of set_operator
// TODO: might overwrite order and limit of first select here // TODO: might overwrite order and limit of first select here
$$ = $1; $$ = $1;
$$->union_select = $3; $$->unionSelect = $3;
$$->order = $4; $$->order = $4;
$$->limit = $5; $$->limit = $5;
} }
@ -482,11 +484,11 @@ set_operator:
select_clause: select_clause:
SELECT opt_distinct select_list from_clause opt_where opt_group { SELECT opt_distinct select_list from_clause opt_where opt_group {
$$ = new SelectStatement(); $$ = new SelectStatement();
$$->select_distinct = $2; $$->selectDistinct = $2;
$$->select_list = $3; $$->selectList = $3;
$$->from_table = $4; $$->fromTable = $4;
$$->where_clause = $5; $$->whereClause = $5;
$$->group_by = $6; $$->groupBy = $6;
} }
; ;

View File

@ -1545,7 +1545,7 @@ static yyconst flex_int16_t yy_chk[3639] =
***************************/ ***************************/
#line 12 "flex_lexer.l" #line 12 "flex_lexer.l"
#include "../sqltypes.h" #include "../sql/Expr.h"
#include "bison_parser.h" #include "bison_parser.h"
#include <stdio.h> #include <stdio.h>

View File

@ -10,7 +10,7 @@
***************************/ ***************************/
%{ %{
#include "../sqltypes.h" #include "../sql/Expr.h"
#include "bison_parser.h" #include "bison_parser.h"
#include <stdio.h> #include <stdio.h>

View File

@ -4,65 +4,59 @@
#include "SQLStatement.h" #include "SQLStatement.h"
namespace hsql { namespace hsql {
/**
* Represents definition of a table column
*/
struct ColumnDefinition {
enum DataType {
TEXT,
INT,
DOUBLE
};
/** ColumnDefinition(char* name, DataType type) :
* @struct ColumnDefinition name(name),
* @brief Represents definition of a table column type(type) {}
*/
struct ColumnDefinition {
enum DataType {
TEXT,
INT,
DOUBLE
};
ColumnDefinition(char* name, DataType type) : virtual ~ColumnDefinition() {
name(name), delete name;
type(type) {} }
virtual ~ColumnDefinition() { char* name;
delete name; DataType type;
} };
char* name; /**
DataType type; * Represents SQL Create statements.
}; * Example: "CREATE TABLE students (name TEXT, student_number INTEGER, city TEXT, grade DOUBLE)"
*/
struct CreateStatement : SQLStatement {
enum CreateType {
kTable,
kTableFromTbl // Hyrise file format
};
CreateStatement(CreateType type) :
SQLStatement(kStmtCreate),
type(type),
ifNotExists(false),
filePath(NULL),
tableName(NULL),
columns(NULL) {};
/** virtual ~CreateStatement() {
* @struct CreateStatement delete columns;
* @brief Represents "CREATE TABLE students (name TEXT, student_number INTEGER, city TEXT, grade DOUBLE)" delete filePath;
*/ delete tableName;
struct CreateStatement : SQLStatement { }
enum CreateType {
kTable,
kTableFromTbl, // Hyrise file format
};
CreateStatement(CreateType type) :
SQLStatement(kStmtCreate),
type(type),
if_not_exists(false),
columns(NULL),
file_path(NULL),
table_name(NULL) {};
virtual ~CreateStatement() {
delete columns;
delete file_path;
delete table_name;
}
CreateType type;
bool if_not_exists;
std::vector<ColumnDefinition*>* columns;
const char* file_path;
const char* table_name;
};
CreateType type;
bool ifNotExists;
const char* filePath;
const char* tableName;
std::vector<ColumnDefinition*>* columns;
};
} // namespace hsql } // namespace hsql
#endif #endif

View File

@ -4,31 +4,26 @@
#include "SQLStatement.h" #include "SQLStatement.h"
namespace hsql { namespace hsql {
/**
* Represents SQL Delete statements.
* Example: "DELETE FROM students WHERE grade > 3.0"
*
* Note: if (expr == NULL) => delete all rows (truncate)
*/
struct DeleteStatement : SQLStatement {
DeleteStatement() :
SQLStatement(kStmtDelete),
tableName(NULL),
expr(NULL) {};
virtual ~DeleteStatement() {
delete tableName;
delete expr;
}
/** char* tableName;
* @struct DeleteStatement Expr* expr;
* @brief Represents "DELETE FROM students WHERE grade > 3.0" };
*
* If expr == NULL => delete all rows (truncate)
*/
struct DeleteStatement : SQLStatement {
DeleteStatement() :
SQLStatement(kStmtDelete),
table_name(NULL),
expr(NULL) {};
virtual ~DeleteStatement() {
delete table_name;
delete expr;
}
char* table_name;
Expr* expr;
};
} // namespace hsql } // namespace hsql
#endif #endif

View File

@ -4,39 +4,31 @@
#include "SQLStatement.h" #include "SQLStatement.h"
namespace hsql { namespace hsql {
/**
* Represents SQL Delete statements.
* Example "DROP TABLE students;"
*/
struct DropStatement : SQLStatement {
enum EntityType {
kTable,
kSchema,
kIndex,
kView,
kPreparedStatement
};
DropStatement(EntityType type) :
SQLStatement(kStmtDrop),
type(type),
name(NULL) {}
/** virtual ~DropStatement() {
* @struct DropStatement delete name;
* @brief Represents "DROP TABLE" }
*/
struct DropStatement : SQLStatement {
enum EntityType {
kTable,
kSchema,
kIndex,
kView,
kPreparedStatement
};
DropStatement(EntityType type) :
SQLStatement(kStmtDrop),
type(type),
name(NULL) {}
virtual ~DropStatement() {
delete name;
}
EntityType type;
const char* name;
};
EntityType type;
const char* name;
};
} // namespace hsql } // namespace hsql
#endif #endif

View File

@ -4,29 +4,24 @@
#include "SQLStatement.h" #include "SQLStatement.h"
namespace hsql { namespace hsql {
/**
* Represents SQL Execute statements.
* Example: "EXECUTE ins_prep(100, "test", 2.3);"
*/
struct ExecuteStatement : SQLStatement {
ExecuteStatement() :
SQLStatement(kStmtExecute),
name(NULL),
parameters(NULL) {}
virtual ~ExecuteStatement() {
delete name;
delete parameters;
}
/** const char* name;
* @struct ExecuteStatement std::vector<Expr*>* parameters;
* @brief Represents "EXECUTE ins_prep(100, "test", 2.3);" };
*/
struct ExecuteStatement : SQLStatement {
ExecuteStatement() :
SQLStatement(kStmtExecute),
name(NULL),
parameters(NULL) {}
virtual ~ExecuteStatement() {
delete name;
delete parameters;
}
const char* name;
std::vector<Expr*>* parameters;
};
} // namsepace hsql } // namsepace hsql
#endif #endif

View File

@ -5,98 +5,98 @@
namespace hsql { namespace hsql {
char* substr(const char* source, int from, int to) { char* substr(const char* source, int from, int to) {
int len = to-from; int len = to-from;
char* copy = new char[len+1]; char* copy = new char[len+1];
strncpy(copy, source+from, len); strncpy(copy, source+from, len);
copy[len] = '\0'; copy[len] = '\0';
return copy; return copy;
} }
Expr* Expr::makeOpUnary(OperatorType op, Expr* expr) { Expr* Expr::makeOpUnary(OperatorType op, Expr* expr) {
Expr* e = new Expr(kExprOperator); Expr* e = new Expr(kExprOperator);
e->op_type = op; e->op_type = op;
e->expr = expr; e->expr = expr;
e->expr2 = NULL; e->expr2 = NULL;
return e; return e;
} }
Expr* Expr::makeOpBinary(Expr* expr1, OperatorType op, Expr* expr2) { Expr* Expr::makeOpBinary(Expr* expr1, OperatorType op, Expr* expr2) {
Expr* e = new Expr(kExprOperator); Expr* e = new Expr(kExprOperator);
e->op_type = op; e->op_type = op;
e->op_char = 0; e->op_char = 0;
e->expr = expr1; e->expr = expr1;
e->expr2 = expr2; e->expr2 = expr2;
return e; return e;
} }
Expr* Expr::makeOpBinary(Expr* expr1, char op, Expr* expr2) { Expr* Expr::makeOpBinary(Expr* expr1, char op, Expr* expr2) {
Expr* e = new Expr(kExprOperator); Expr* e = new Expr(kExprOperator);
e->op_type = SIMPLE_OP; e->op_type = SIMPLE_OP;
e->op_char = op; e->op_char = op;
e->expr = expr1; e->expr = expr1;
e->expr2 = expr2; e->expr2 = expr2;
return e; return e;
} }
Expr* Expr::makeLiteral(int64_t val) { Expr* Expr::makeLiteral(int64_t val) {
Expr* e = new Expr(kExprLiteralInt); Expr* e = new Expr(kExprLiteralInt);
e->ival = val; e->ival = val;
return e; return e;
} }
Expr* Expr::makeLiteral(double value) { Expr* Expr::makeLiteral(double value) {
Expr* e = new Expr(kExprLiteralFloat); Expr* e = new Expr(kExprLiteralFloat);
e->fval = value; e->fval = value;
return e; return e;
} }
Expr* Expr::makeLiteral(char* string) { Expr* Expr::makeLiteral(char* string) {
Expr* e = new Expr(kExprLiteralString); Expr* e = new Expr(kExprLiteralString);
e->name = string; e->name = string;
return e; return e;
} }
Expr* Expr::makeColumnRef(char* name) { Expr* Expr::makeColumnRef(char* name) {
Expr* e = new Expr(kExprColumnRef); Expr* e = new Expr(kExprColumnRef);
e->name = name; e->name = name;
return e; return e;
} }
Expr* Expr::makeColumnRef(char* table, char* name) { Expr* Expr::makeColumnRef(char* table, char* name) {
Expr* e = new Expr(kExprColumnRef); Expr* e = new Expr(kExprColumnRef);
e->name = name; e->name = name;
e->table = table; e->table = table;
return e; return e;
} }
Expr* Expr::makeFunctionRef(char* func_name, Expr* expr, bool distinct) { Expr* Expr::makeFunctionRef(char* func_name, Expr* expr, bool distinct) {
Expr* e = new Expr(kExprFunctionRef); Expr* e = new Expr(kExprFunctionRef);
e->name = func_name; e->name = func_name;
e->expr = expr; e->expr = expr;
e->distinct = distinct; e->distinct = distinct;
return e; return e;
} }
Expr* Expr::makePlaceholder(int id) { Expr* Expr::makePlaceholder(int id) {
Expr* e = new Expr(kExprPlaceholder); Expr* e = new Expr(kExprPlaceholder);
e->ival = id; e->ival = id;
return e; return e;
} }
Expr::~Expr() { Expr::~Expr() {
delete expr; delete expr;
delete expr2; delete expr2;
delete name; delete name;
delete table; delete table;
} }
} // namespace hsql } // namespace hsql

View File

@ -7,116 +7,127 @@
namespace hsql { namespace hsql {
// Helper function // Helper function
char* substr(const char* source, int from, int to); char* substr(const char* source, int from, int to);
typedef enum { typedef enum {
kExprLiteralFloat, kExprLiteralFloat,
kExprLiteralString, kExprLiteralString,
kExprLiteralInt, kExprLiteralInt,
kExprStar, kExprStar,
kExprPlaceholder, kExprPlaceholder,
kExprColumnRef, kExprColumnRef,
kExprFunctionRef, kExprFunctionRef,
kExprOperator kExprOperator
} ExprType; } ExprType;
typedef struct Expr Expr; typedef struct Expr Expr;
/** /**
* @class Expr * Represents SQL expressions (i.e. literals, operators, column_refs)
* @brief Represents SQL expressions (i.e. literals, operators, column_refs) *
* * TODO: When destructing a placeholder expression, we might need to alter the placeholder_list
* TODO: When destructing a placeholder expression, we might need to alter the placeholder_list */
*/ struct Expr {
struct Expr { /**
/** * Operator types. These are important for expressions of type kExprOperator
* Operator types. These are important for expressions of type kExprOperator * Trivial types are those that can be described by a single character e.g:
* Trivial types are those that can be described by a single character e.g: * + - * / < > = %
* + - * / < > = % * Non-trivial are:
* Non-trivial are: * <> <= >= LIKE ISNULL NOT
* <> <= >= LIKE ISNULL NOT */
*/ typedef enum {
typedef enum { SIMPLE_OP,
SIMPLE_OP, // Binary
// Binary NOT_EQUALS,
NOT_EQUALS, LESS_EQ,
LESS_EQ, GREATER_EQ,
GREATER_EQ, LIKE,
LIKE, NOT_LIKE,
NOT_LIKE, AND,
AND, OR,
OR, // Unary
// Unary NOT,
NOT, UMINUS,
UMINUS, ISNULL
ISNULL } OperatorType;
} OperatorType;
Expr(ExprType type) : Expr(ExprType type) :
type(type), type(type),
expr(NULL), expr(NULL),
expr2(NULL), expr2(NULL),
name(NULL), name(NULL),
table(NULL), table(NULL),
alias(NULL) {}; alias(NULL) {};
// Interesting side-effect: // Interesting side-effect:
// Making the destructor virtual used to cause segmentation faults // Making the destructor virtual used to cause segmentation faults
~Expr(); ~Expr();
ExprType type;
Expr* expr; ExprType type;
Expr* expr2;
char* name;
char* table;
char* alias;
float fval;
int64_t ival;
int64_t ival2;
OperatorType op_type; Expr* expr;
char op_char; Expr* expr2;
bool distinct; char* name;
char* table;
char* alias;
float fval;
int64_t ival;
int64_t ival2;
OperatorType op_type;
char op_char;
bool distinct;
/** /**
* Convenience accessor methods * Convenience accessor methods
*/ */
inline bool isType(ExprType e_type) { return e_type == type; } inline bool isType(ExprType e_type) {
inline bool isLiteral() { return isType(kExprLiteralInt) || isType(kExprLiteralFloat) || isType(kExprLiteralString) || isType(kExprPlaceholder); } return e_type == type;
inline bool hasAlias() { return alias != NULL; } }
inline bool hasTable() { return table != NULL; } inline bool isLiteral() {
inline char* getName() { return isType(kExprLiteralInt) || isType(kExprLiteralFloat) || isType(kExprLiteralString) || isType(kExprPlaceholder);
if (alias != NULL) return alias; }
else return name; inline bool hasAlias() {
} return alias != NULL;
inline bool isSimpleOp() { return op_type == SIMPLE_OP; } }
inline bool isSimpleOp(char op) { return isSimpleOp() && op_char == op; } inline bool hasTable() {
return table != NULL;
}
inline char* getName() {
if (alias != NULL) return alias;
else return name;
}
inline bool isSimpleOp() {
return op_type == SIMPLE_OP;
}
inline bool isSimpleOp(char op) {
return isSimpleOp() && op_char == op;
}
/** /**
* Static expression constructors * Static expression constructors
*/ */
static Expr* makeOpUnary(OperatorType op, Expr* expr); static Expr* makeOpUnary(OperatorType op, Expr* expr);
static Expr* makeOpBinary(Expr* expr1, char op, Expr* expr2); static Expr* makeOpBinary(Expr* expr1, char op, Expr* expr2);
static Expr* makeOpBinary(Expr* expr1, OperatorType op, Expr* expr2); static Expr* makeOpBinary(Expr* expr1, OperatorType op, Expr* expr2);
static Expr* makeLiteral(int64_t val); static Expr* makeLiteral(int64_t val);
static Expr* makeLiteral(double val); static Expr* makeLiteral(double val);
static Expr* makeLiteral(char* val); static Expr* makeLiteral(char* val);
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); 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
// For Hyrise we still had to put in the explicit NULL constructor // For Hyrise we still had to put in the explicit NULL constructor

View File

@ -4,39 +4,30 @@
#include "SQLStatement.h" #include "SQLStatement.h"
namespace hsql { namespace hsql {
/**
* Represents SQL Import statements.
*/
struct ImportStatement : SQLStatement {
enum ImportType {
kImportCSV,
kImportTbl, // Hyrise file format
};
ImportStatement(ImportType type) :
SQLStatement(kStmtImport),
type(type),
filePath(NULL),
tableName(NULL) {};
virtual ~ImportStatement() {
delete filePath;
delete tableName;
}
ImportType type;
/** const char* filePath;
* @struct ImportStatement const char* tableName;
* @brief Represents "IMPORT" };
*/
struct ImportStatement : SQLStatement {
enum ImportType {
kImportCSV,
kImportTbl, // Hyrise file format
};
ImportStatement(ImportType type) :
SQLStatement(kStmtImport),
type(type),
file_path(NULL),
table_name(NULL) {};
virtual ~ImportStatement() {
delete file_path;
delete table_name;
}
ImportType type;
const char* file_path;
const char* table_name;
};
} // namespace hsql } // namespace hsql

View File

@ -5,42 +5,37 @@
#include "SelectStatement.h" #include "SelectStatement.h"
namespace hsql { namespace hsql {
/**
* Represents SQL Insert statements.
* Example: "INSERT INTO students VALUES ('Max', 1112233, 'Musterhausen', 2.3)"
*/
struct InsertStatement : SQLStatement {
enum InsertType {
kInsertValues,
kInsertSelect
};
InsertStatement(InsertType type) :
SQLStatement(kStmtInsert),
type(type),
tableName(NULL),
columns(NULL),
values(NULL),
select(NULL) {}
/** virtual ~InsertStatement() {
* @struct InsertStatement delete tableName;
* @brief Represents "INSERT INTO students VALUES ('Max', 1112233, 'Musterhausen', 2.3)" delete columns;
*/ delete values;
struct InsertStatement : SQLStatement { delete select;
enum InsertType { }
kInsertValues,
kInsertSelect
};
InsertStatement(InsertType type) :
SQLStatement(kStmtInsert),
type(type),
table_name(NULL),
columns(NULL),
values(NULL),
select(NULL) {}
virtual ~InsertStatement() {
delete table_name;
delete columns;
delete values;
delete select;
}
InsertType type;
const char* table_name;
std::vector<char*>* columns;
std::vector<Expr*>* values;
SelectStatement* select;
};
InsertType type;
const char* tableName;
std::vector<char*>* columns;
std::vector<Expr*>* values;
SelectStatement* select;
};
} // namsepace hsql } // namsepace hsql
#endif #endif

View File

@ -1,53 +1,49 @@
#ifndef __PREPARE_STATEMENT_H__ #ifndef __PREPARE_STATEMENT_H__
#define __PREPARE_STATEMENT_H__ #define __PREPARE_STATEMENT_H__
#include "../SQLParserResult.h"
#include "SQLStatement.h" #include "SQLStatement.h"
#include "SelectStatement.h" #include "SelectStatement.h"
#include <algorithm> #include <algorithm>
namespace hsql { namespace hsql {
/**
* Represents SQL Prepare statements.
* Example: "PREPARE ins_prep: SELECT * FROM t1 WHERE c1 = ? AND c2 = ?"
*/
struct PrepareStatement : SQLStatement {
PrepareStatement() :
SQLStatement(kStmtPrepare),
name(NULL),
query(NULL) {}
virtual ~PrepareStatement() {
delete query;
delete name;
}
/** /**
* @struct PrepareStatement * When setting the placeholders we need to make sure that they are in the correct order.
* @brief Represents "PREPARE ins_prep: SELECT * FROM t1 WHERE c1 = ? AND c2 = ?" * To ensure that, during parsing we store the character position use that to sort the list here.
*/ *
struct PrepareStatement : SQLStatement { * @param vector of placeholders that the parser found
PrepareStatement() : */
SQLStatement(kStmtPrepare), void setPlaceholders(std::vector<void*> ph) {
name(NULL), for (void* e : ph) {
query(NULL) {} if (e != NULL)
placeholders.push_back((Expr*) e);
virtual ~PrepareStatement() { }
delete query; // Sort by col-id
delete name; std::sort(placeholders.begin(), placeholders.end(), [](Expr* i, Expr* j) -> bool { return (i->ival < j->ival); });
}
/**
* @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;
SQLStatementList* query;
std::vector<Expr*> placeholders;
};
// 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;
SQLParserResult* query;
std::vector<Expr*> placeholders;
};
} // namsepace hsql } // namsepace hsql
#endif #endif

View File

@ -1,86 +1,42 @@
/* #ifndef __SQLSTATEMENT_H__
* SQLStatement.h #define __SQLSTATEMENT_H__
* Definition of the structure used to build the syntax tree.
*/
#ifndef __STATEMENT_H__
#define __STATEMENT_H__
#include "Expr.h" #include "Expr.h"
#include <vector> #include <vector>
namespace hsql { namespace hsql {
typedef enum {
kStmtError, // unused
kStmtSelect,
kStmtImport,
kStmtInsert,
kStmtUpdate,
kStmtDelete,
kStmtCreate,
kStmtDrop,
kStmtPrepare,
kStmtExecute,
kStmtExport,
kStmtRename,
kStmtAlter
} StatementType;
/**
* Base struct for every SQL statement
*/
struct SQLStatement {
SQLStatement(StatementType type) :
_type(type) {};
typedef enum { virtual ~SQLStatement() {}
kStmtError, // Unused
kStmtSelect,
kStmtImport,
kStmtInsert,
kStmtUpdate,
kStmtDelete,
kStmtCreate,
kStmtDrop,
kStmtPrepare,
kStmtExecute,
kStmtExport,
kStmtRename,
kStmtAlter
} StatementType;
/**
* @struct SQLStatement
* @brief Base class for every SQLStatement
*/
struct SQLStatement {
SQLStatement(StatementType type) :
_type(type) {};
virtual ~SQLStatement() {}
virtual StatementType type() { return _type; }
private:
StatementType _type;
};
/**
* @struct SQLStatementList
* @brief Represents the result of the SQLParser. If parsing was successful it is a list of SQLStatement.
*/
struct SQLStatementList {
public:
SQLStatementList() :
isValid(true),
parser_msg(NULL) {};
SQLStatementList(SQLStatement* stmt) :
isValid(true),
parser_msg(NULL) {
addStatement(stmt);
};
virtual ~SQLStatementList() {
for (std::vector<SQLStatement*>::iterator it = statements.begin(); it != statements.end(); ++it) {
delete *it;
}
delete parser_msg;
}
void addStatement(SQLStatement* stmt) { statements.push_back(stmt); }
SQLStatement* getStatement(int id) { return statements[id]; }
size_t numStatements() { return statements.size(); }
std::vector<SQLStatement*> statements;
bool isValid;
const char* parser_msg;
int error_line;
int error_col;
};
virtual StatementType type() {
return _type;
}
private:
StatementType _type;
};
} // namespace hsql } // namespace hsql
#endif // __SQLSTATEMENT_H__
#endif // __STATEMENT_H__

View File

@ -6,103 +6,95 @@
#include "Table.h" #include "Table.h"
namespace hsql { namespace hsql {
typedef enum {
kOrderAsc,
kOrderDesc
} OrderType;
/**
* 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) :
type(type),
expr(expr) {}
virtual ~OrderDescription() {
delete expr;
}
/** OrderType type;
* @struct OrderDescription Expr* expr;
* @brief Description of the order by clause within a select statement };
*
* TODO: hold multiple expressions to be sorted by
*/
typedef enum {
kOrderAsc,
kOrderDesc
} OrderType;
struct OrderDescription { const int64_t kNoLimit = -1;
OrderDescription(OrderType type, Expr* expr) : const int64_t kNoOffset = -1;
type(type),
expr(expr) {}
virtual ~OrderDescription() {
delete expr;
}
OrderType type; /**
Expr* expr; * Description of the limit clause within a select statement
}; */
struct LimitDescription {
LimitDescription(int64_t limit, int64_t offset) :
limit(limit),
offset(offset) {}
/** int64_t limit;
* @struct LimitDescription int64_t offset;
* @brief Description of the limit clause within a select statement };
*/
const int64_t kNoLimit = -1;
const int64_t kNoOffset = -1;
struct LimitDescription {
LimitDescription(int64_t limit, int64_t offset) :
limit(limit),
offset(offset) {}
int64_t limit; /**
int64_t offset; * Description of the group-by clause within a select statement
}; */
struct GroupByDescription {
GroupByDescription() :
columns(NULL),
having(NULL) {}
/** ~GroupByDescription() {
* @struct GroupByDescription delete columns;
*/ delete having;
struct GroupByDescription { }
GroupByDescription() :
columns(NULL),
having(NULL) {}
~GroupByDescription() { std::vector<Expr*>* columns;
delete columns; Expr* having;
delete having; };
}
std::vector<Expr*>* columns; /**
Expr* having; * Representation of a full SQL select statement.
}; * TODO: add union_order and union_limit
*/
struct SelectStatement : SQLStatement {
SelectStatement() :
SQLStatement(kStmtSelect),
fromTable(NULL),
selectDistinct(false),
selectList(NULL),
whereClause(NULL),
groupBy(NULL),
unionSelect(NULL),
order(NULL),
limit(NULL) {};
/** virtual ~SelectStatement() {
* @struct SelectStatement delete fromTable;
* @brief Representation of a full select statement. delete selectList;
* delete whereClause;
* TODO: add union_order and union_limit delete groupBy;
*/ delete order;
struct SelectStatement : SQLStatement { delete limit;
SelectStatement() : }
SQLStatement(kStmtSelect),
from_table(NULL),
select_list(NULL),
where_clause(NULL),
group_by(NULL),
union_select(NULL),
order(NULL),
limit(NULL) {};
virtual ~SelectStatement() { TableRef* fromTable;
delete from_table; bool selectDistinct;
delete select_list; std::vector<Expr*>* selectList;
delete where_clause; Expr* whereClause;
delete group_by; GroupByDescription* groupBy;
delete order;
delete limit;
}
TableRef* from_table;
bool select_distinct;
std::vector<Expr*>* select_list;
Expr* where_clause;
GroupByDescription* group_by;
SelectStatement* union_select;
OrderDescription* order;
LimitDescription* limit;
};
SelectStatement* unionSelect;
OrderDescription* order;
LimitDescription* limit;
};
} // namespace hsql } // namespace hsql
#endif #endif

View File

@ -7,97 +7,99 @@
namespace hsql { namespace hsql {
struct SelectStatement; struct SelectStatement;
struct JoinDefinition; struct JoinDefinition;
struct TableRef; struct TableRef;
/** /**
* @enum TableRefType * @enum TableRefType
* Types table references * Types table references
*/ */
typedef enum { typedef enum {
kTableName, kTableName,
kTableSelect, kTableSelect,
kTableJoin, kTableJoin,
kTableCrossProduct kTableCrossProduct
} TableRefType; } TableRefType;
/** /**
* @struct TableRef * @struct TableRef
* @brief Holds reference to tables. Can be either table names or a select statement. * @brief Holds reference to tables. Can be either table names or a select statement.
*/ */
struct TableRef { struct TableRef {
TableRef(TableRefType type) : TableRef(TableRefType type) :
type(type), type(type),
schema(NULL), schema(NULL),
name(NULL), name(NULL),
alias(NULL), alias(NULL),
select(NULL), select(NULL),
list(NULL), list(NULL),
join(NULL) {} join(NULL) {}
virtual ~TableRef();
TableRefType type; virtual ~TableRef();
char* schema; TableRefType type;
char* name;
char* alias;
SelectStatement* select; char* schema;
std::vector<TableRef*>* list; char* name;
JoinDefinition* join; char* alias;
SelectStatement* select;
std::vector<TableRef*>* list;
JoinDefinition* join;
/** /**
* Convenience accessor methods * Convenience accessor methods
*/ */
inline bool hasSchema() { return schema != NULL; } inline bool hasSchema() {
return schema != NULL;
}
inline char* getName() { inline char* getName() {
if (alias != NULL) return alias; if (alias != NULL) return alias;
else return name; else return name;
} }
}; };
/** /**
* @enum JoinType * @enum JoinType
* Types of joins * Types of joins
*/ */
typedef enum { typedef enum {
kJoinInner, kJoinInner,
kJoinOuter, kJoinOuter,
kJoinLeft, kJoinLeft,
kJoinRight, kJoinRight,
} JoinType; } JoinType;
/** /**
* @struct JoinDefinition * @struct JoinDefinition
* @brief Definition of a join table * @brief Definition of a join table
*/ */
struct JoinDefinition { struct JoinDefinition {
JoinDefinition() : JoinDefinition() :
left(NULL), left(NULL),
right(NULL), right(NULL),
condition(NULL), condition(NULL),
type(kJoinInner) {} type(kJoinInner) {}
virtual ~JoinDefinition() { virtual ~JoinDefinition() {
delete left; delete left;
delete right; delete right;
delete condition; delete condition;
} }
TableRef* left; TableRef* left;
TableRef* right; TableRef* right;
Expr* condition; Expr* condition;
JoinType type; JoinType type;
}; };

View File

@ -4,42 +4,35 @@
#include "SQLStatement.h" #include "SQLStatement.h"
namespace hsql { namespace hsql {
/**
* Represents "column = value" expressions
*/
struct UpdateClause {
char* column;
Expr* value;
};
/**
* Represents SQL Update statements.
*/
struct UpdateStatement : SQLStatement {
UpdateStatement() :
SQLStatement(kStmtUpdate),
table(NULL),
updates(NULL),
where(NULL) {}
/** virtual ~UpdateStatement() {
* @struct UpdateClause delete table;
* @brief Represents "column = value" expressions delete updates;
*/ delete where;
struct UpdateClause { }
char* column;
Expr* value;
};
/**
* @struct UpdateStatement
* @brief Represents "UPDATE"
*/
struct UpdateStatement : SQLStatement {
UpdateStatement() :
SQLStatement(kStmtUpdate),
table(NULL),
updates(NULL),
where(NULL) {}
virtual ~UpdateStatement() {
delete table;
delete updates;
delete where;
}
// TODO: switch to char* instead of TableRef
TableRef* table;
std::vector<UpdateClause*>* updates;
Expr* where;
};
// TODO: switch to char* instead of TableRef
TableRef* table;
std::vector<UpdateClause*>* updates;
Expr* where;
};
} // namsepace hsql } // namsepace hsql
#endif #endif

View File

@ -5,12 +5,12 @@
namespace hsql { namespace hsql {
TableRef::~TableRef() { TableRef::~TableRef() {
delete name; delete name;
delete alias; delete alias;
delete select; delete select;
delete list; delete list;
} }
} // namespace hsql } // namespace hsql

14
src/sql/statements.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef __STATEMENTS_H__
#define __STATEMENTS_H__
#include "SelectStatement.h"
#include "ImportStatement.h"
#include "CreateStatement.h"
#include "InsertStatement.h"
#include "UpdateStatement.h"
#include "DeleteStatement.h"
#include "DropStatement.h"
#include "PrepareStatement.h"
#include "ExecuteStatement.h"
#endif // __STATEMENTS_H__

View File

@ -5,159 +5,204 @@
namespace hsql { namespace hsql {
void printOperatorExpression(Expr* expr, uint num_indent); void printOperatorExpression(Expr* expr, uint numIndent);
std::string indent(uint num_indent) { return std::string(num_indent, '\t'); } std::string indent(uint numIndent) {
void inprint(int64_t val, uint num_indent) { printf("%s%lld \n", indent(num_indent).c_str(), val); } return std::string(numIndent, '\t');
void inprint(float val, uint num_indent) { printf("%s%f\n", indent(num_indent).c_str(), val); } }
void inprint(const char* val, uint num_indent) { printf("%s%s\n", indent(num_indent).c_str(), val); } void inprint(int64_t val, uint numIndent) {
void inprint(const char* val, const char* val2, uint num_indent) { printf("%s%s->%s\n", indent(num_indent).c_str(), val, val2); } printf("%s%ld \n", indent(numIndent).c_str(), val);
void inprintC(char val, uint num_indent) { printf("%s%c\n", indent(num_indent).c_str(), val); } }
void inprintU(uint64_t val, uint num_indent) { printf("%s%llu\n", indent(num_indent).c_str(), val); } void inprint(float val, uint numIndent) {
printf("%s%f\n", indent(numIndent).c_str(), val);
void printTableRefInfo(TableRef* table, uint num_indent) { }
switch (table->type) { void inprint(const char* val, uint numIndent) {
case kTableName: printf("%s%s\n", indent(numIndent).c_str(), val);
inprint(table->name, num_indent); }
break; void inprint(const char* val, const char* val2, uint numIndent) {
case kTableSelect: printf("%s%s->%s\n", indent(numIndent).c_str(), val, val2);
printSelectStatementInfo(table->select, num_indent); }
break; void inprintC(char val, uint numIndent) {
case kTableJoin: printf("%s%c\n", indent(numIndent).c_str(), val);
inprint("Join Table", num_indent); }
inprint("Left", num_indent+1); void inprintU(uint64_t val, uint numIndent) {
printTableRefInfo(table->join->left, num_indent+2); printf("%s%lu\n", indent(numIndent).c_str(), val);
inprint("Right", num_indent+1);
printTableRefInfo(table->join->right, num_indent+2);
inprint("Join Condition", num_indent+1);
printExpression(table->join->condition, num_indent+2);
break;
case kTableCrossProduct:
for (TableRef* tbl : *table->list) printTableRefInfo(tbl, num_indent);
break;
}
if (table->alias != NULL) {
inprint("Alias", num_indent+1);
inprint(table->alias, num_indent+2);
}
}
void printOperatorExpression(Expr* expr, uint num_indent) {
if (expr == NULL) { inprint("null", num_indent); return; }
switch (expr->op_type) {
case Expr::SIMPLE_OP: inprintC(expr->op_char, num_indent); break;
case Expr::AND: inprint("AND", num_indent); break;
case Expr::OR: inprint("OR", num_indent); break;
case Expr::NOT: inprint("NOT", num_indent); break;
default: inprintU(expr->op_type, num_indent); break;
}
printExpression(expr->expr, num_indent+1);
if (expr->expr2 != NULL) printExpression(expr->expr2, num_indent+1);
}
void printExpression(Expr* expr, uint num_indent) {
switch (expr->type) {
case kExprStar: inprint("*", num_indent); break;
case kExprColumnRef: inprint(expr->name, num_indent); break;
// case kExprTableColumnRef: inprint(expr->table, expr->name, num_indent); break;
case kExprLiteralFloat: inprint(expr->fval, num_indent); break;
case kExprLiteralInt: inprint(expr->ival, num_indent); break;
case kExprLiteralString: inprint(expr->name, num_indent); break;
case kExprFunctionRef: inprint(expr->name, num_indent); inprint(expr->expr->name, num_indent+1); break;
case kExprOperator: printOperatorExpression(expr, num_indent); break;
default: fprintf(stderr, "Unrecognized expression type %d\n", expr->type); return;
}
if (expr->alias != NULL) {
inprint("Alias", num_indent+1); inprint(expr->alias, num_indent+2);
}
}
void printSelectStatementInfo(SelectStatement* stmt, uint num_indent) {
inprint("SelectStatement", num_indent);
inprint("Fields:", num_indent+1);
for (Expr* expr : *stmt->select_list) printExpression(expr, num_indent+2);
inprint("Sources:", num_indent+1);
printTableRefInfo(stmt->from_table, num_indent+2);
if (stmt->where_clause != NULL) {
inprint("Search Conditions:", num_indent+1);
printExpression(stmt->where_clause, num_indent+2);
}
if (stmt->union_select != NULL) {
inprint("Union:", num_indent+1);
printSelectStatementInfo(stmt->union_select, num_indent+2);
}
if (stmt->order != NULL) {
inprint("OrderBy:", num_indent+1);
printExpression(stmt->order->expr, num_indent+2);
if (stmt->order->type == kOrderAsc) inprint("ascending", num_indent+2);
else inprint("descending", num_indent+2);
}
if (stmt->limit != NULL) {
inprint("Limit:", num_indent+1);
inprint(stmt->limit->limit, num_indent+2);
}
}
void printImportStatementInfo(ImportStatement* stmt, uint num_indent) {
inprint("ImportStatment", num_indent);
inprint(stmt->file_path, num_indent+1);
inprint(stmt->table_name, num_indent+1);
}
void printCreateStatementInfo(CreateStatement* stmt, uint num_indent) {
inprint("CreateStatment", num_indent);
inprint(stmt->table_name, num_indent+1);
inprint(stmt->file_path, num_indent+1);
}
void printInsertStatementInfo(InsertStatement* stmt, uint num_indent) {
inprint("InsertStatment", num_indent);
inprint(stmt->table_name, num_indent+1);
if (stmt->columns != NULL) {
inprint("Columns", num_indent+1);
for (char* col_name : *stmt->columns) {
inprint(col_name, num_indent+2);
} }
}
switch (stmt->type) {
case InsertStatement::kInsertValues:
inprint("Values", num_indent+1);
for (Expr* expr : *stmt->values) {
printExpression(expr, num_indent+2);
}
break;
case InsertStatement::kInsertSelect:
printSelectStatementInfo(stmt->select, num_indent+1);
break;
}
}
void printStatementInfo(SQLStatement* stmt) { void printTableRefInfo(TableRef* table, uint numIndent) {
switch (stmt->type()) { switch (table->type) {
case kStmtSelect: case kTableName:
printSelectStatementInfo((SelectStatement*) stmt, 0); inprint(table->name, numIndent);
break; break;
case kStmtInsert: case kTableSelect:
printInsertStatementInfo((InsertStatement*) stmt, 0); printSelectStatementInfo(table->select, numIndent);
break; break;
case kStmtCreate: case kTableJoin:
printCreateStatementInfo((CreateStatement*) stmt, 0); inprint("Join Table", numIndent);
break; inprint("Left", numIndent+1);
case kStmtImport: printTableRefInfo(table->join->left, numIndent+2);
printImportStatementInfo((ImportStatement*) stmt, 0); inprint("Right", numIndent+1);
break; printTableRefInfo(table->join->right, numIndent+2);
default: inprint("Join Condition", numIndent+1);
break; printExpression(table->join->condition, numIndent+2);
} break;
} case kTableCrossProduct:
for (TableRef* tbl : *table->list) printTableRefInfo(tbl, numIndent);
break;
}
if (table->alias != NULL) {
inprint("Alias", numIndent+1);
inprint(table->alias, numIndent+2);
}
}
void printOperatorExpression(Expr* expr, uint numIndent) {
if (expr == NULL) {
inprint("null", numIndent);
return;
}
switch (expr->op_type) {
case Expr::SIMPLE_OP:
inprintC(expr->op_char, numIndent);
break;
case Expr::AND:
inprint("AND", numIndent);
break;
case Expr::OR:
inprint("OR", numIndent);
break;
case Expr::NOT:
inprint("NOT", numIndent);
break;
default:
inprintU(expr->op_type, numIndent);
break;
}
printExpression(expr->expr, numIndent+1);
if (expr->expr2 != NULL) printExpression(expr->expr2, numIndent+1);
}
void printExpression(Expr* expr, uint numIndent) {
switch (expr->type) {
case kExprStar:
inprint("*", numIndent);
break;
case kExprColumnRef:
inprint(expr->name, numIndent);
break;
// case kExprTableColumnRef: inprint(expr->table, expr->name, numIndent); break;
case kExprLiteralFloat:
inprint(expr->fval, numIndent);
break;
case kExprLiteralInt:
inprint(expr->ival, numIndent);
break;
case kExprLiteralString:
inprint(expr->name, numIndent);
break;
case kExprFunctionRef:
inprint(expr->name, numIndent);
inprint(expr->expr->name, numIndent+1);
break;
case kExprOperator:
printOperatorExpression(expr, numIndent);
break;
default:
fprintf(stderr, "Unrecognized expression type %d\n", expr->type);
return;
}
if (expr->alias != NULL) {
inprint("Alias", numIndent+1);
inprint(expr->alias, numIndent+2);
}
}
void printSelectStatementInfo(SelectStatement* stmt, uint numIndent) {
inprint("SelectStatement", numIndent);
inprint("Fields:", numIndent+1);
for (Expr* expr : *stmt->selectList) printExpression(expr, numIndent+2);
inprint("Sources:", numIndent+1);
printTableRefInfo(stmt->fromTable, numIndent+2);
if (stmt->whereClause != NULL) {
inprint("Search Conditions:", numIndent+1);
printExpression(stmt->whereClause, numIndent+2);
}
if (stmt->unionSelect != NULL) {
inprint("Union:", numIndent+1);
printSelectStatementInfo(stmt->unionSelect, numIndent+2);
}
if (stmt->order != NULL) {
inprint("OrderBy:", numIndent+1);
printExpression(stmt->order->expr, numIndent+2);
if (stmt->order->type == kOrderAsc) inprint("ascending", numIndent+2);
else inprint("descending", numIndent+2);
}
if (stmt->limit != NULL) {
inprint("Limit:", numIndent+1);
inprint(stmt->limit->limit, numIndent+2);
}
}
void printImportStatementInfo(ImportStatement* stmt, uint numIndent) {
inprint("ImportStatment", numIndent);
inprint(stmt->filePath, numIndent+1);
inprint(stmt->tableName, numIndent+1);
}
void printCreateStatementInfo(CreateStatement* stmt, uint numIndent) {
inprint("CreateStatment", numIndent);
inprint(stmt->tableName, numIndent+1);
inprint(stmt->filePath, numIndent+1);
}
void printInsertStatementInfo(InsertStatement* stmt, uint numIndent) {
inprint("InsertStatment", numIndent);
inprint(stmt->tableName, numIndent+1);
if (stmt->columns != NULL) {
inprint("Columns", numIndent+1);
for (char* col_name : *stmt->columns) {
inprint(col_name, numIndent+2);
}
}
switch (stmt->type) {
case InsertStatement::kInsertValues:
inprint("Values", numIndent+1);
for (Expr* expr : *stmt->values) {
printExpression(expr, numIndent+2);
}
break;
case InsertStatement::kInsertSelect:
printSelectStatementInfo(stmt->select, numIndent+1);
break;
}
}
void printStatementInfo(SQLStatement* stmt) {
switch (stmt->type()) {
case kStmtSelect:
printSelectStatementInfo((SelectStatement*) stmt, 0);
break;
case kStmtInsert:
printInsertStatementInfo((InsertStatement*) stmt, 0);
break;
case kStmtCreate:
printCreateStatementInfo((CreateStatement*) stmt, 0);
break;
case kStmtImport:
printImportStatementInfo((ImportStatement*) stmt, 0);
break;
default:
break;
}
}
} // namespace hsql } // namespace hsql

View File

@ -1,18 +1,16 @@
#ifndef __SQLHELPER_H__ #ifndef __SQLHELPER_H__
#define __SQLHELPER_H__ #define __SQLHELPER_H__
#include "sql/statements.h"
#include "sqltypes.h"
namespace hsql { namespace hsql {
void printStatementInfo(SQLStatement* stmt); void printStatementInfo(SQLStatement* stmt);
void printSelectStatementInfo(SelectStatement* stmt, uint num_indent); void printSelectStatementInfo(SelectStatement* stmt, uint num_indent);
void printImportStatementInfo(ImportStatement* stmt, uint num_indent); void printImportStatementInfo(ImportStatement* stmt, uint num_indent);
void printInsertStatementInfo(InsertStatement* stmt, uint num_indent); void printInsertStatementInfo(InsertStatement* stmt, uint num_indent);
void printCreateStatementInfo(CreateStatement* stmt, uint num_indent); void printCreateStatementInfo(CreateStatement* stmt, uint num_indent);
void printExpression(Expr* expr, uint num_indent); void printExpression(Expr* expr, uint num_indent);
} // namespace hsql } // namespace hsql

View File

@ -1,16 +0,0 @@
#ifndef __SQLLIB_H__
#define __SQLLIB_H__
typedef unsigned int uint;
#include "sql/SelectStatement.h"
#include "sql/ImportStatement.h"
#include "sql/CreateStatement.h"
#include "sql/InsertStatement.h"
#include "sql/UpdateStatement.h"
#include "sql/DeleteStatement.h"
#include "sql/DropStatement.h"
#include "sql/PrepareStatement.h"
#include "sql/ExecuteStatement.h"
#endif

View File

@ -2,21 +2,21 @@
#define __HELPER_H__ #define __HELPER_H__
#define TEST_PARSE_SQL_QUERY(query, output_var, num_statements) \ #define TEST_PARSE_SQL_QUERY(query, outputVar, numStatements) \
SQLStatementList* output_var = SQLParser::parseSQLString(query); \ SQLParserResult* outputVar = SQLParser::parseSQLString(query); \
ASSERT(output_var->isValid); \ ASSERT(outputVar->isValid); \
ASSERT_EQ(output_var->numStatements(), num_statements); ASSERT_EQ(outputVar->size(), numStatements);
#define TEST_PARSE_SINGLE_SQL(query, stmt_type, stmt_class, output_var) \ #define TEST_PARSE_SINGLE_SQL(query, stmtType, stmtClass, outputVar) \
TEST_PARSE_SQL_QUERY(query, stmt_list, 1); \ TEST_PARSE_SQL_QUERY(query, stmt_list, 1); \
ASSERT_EQ(stmt_list->getStatement(0)->type(), stmt_type); \ ASSERT_EQ(stmt_list->getStatement(0)->type(), stmtType); \
stmt_class* output_var = (stmt_class*) stmt_list->getStatement(0); stmtClass* outputVar = (stmtClass*) stmt_list->getStatement(0);
#define TEST_CAST_STMT(stmt_list, stmt_index, stmt_type, stmt_class, output_var) \ #define TEST_CAST_STMT(stmt_list, stmt_index, stmtType, stmtClass, outputVar) \
ASSERT_EQ(stmt_list->getStatement(stmt_index)->type(), stmt_type); \ ASSERT_EQ(stmt_list->getStatement(stmt_index)->type(), stmtType); \
stmt_class* output_var = (stmt_class*) stmt_list->getStatement(stmt_index); stmtClass* outputVar = (stmtClass*) stmt_list->getStatement(stmt_index);
#endif #endif

View File

@ -7,40 +7,40 @@
using namespace hsql; using namespace hsql;
TEST(SelectTest) { TEST(SelectTest) {
TEST_PARSE_SINGLE_SQL("SELECT * FROM students;", kStmtSelect, SelectStatement, stmt); TEST_PARSE_SINGLE_SQL("SELECT * FROM students;", kStmtSelect, SelectStatement, stmt);
ASSERT_NULL(stmt->where_clause); ASSERT_NULL(stmt->whereClause);
ASSERT_NULL(stmt->group_by); ASSERT_NULL(stmt->groupBy);
} }
TEST(SelectHavingTest) { TEST(SelectHavingTest) {
TEST_PARSE_SINGLE_SQL("SELECT city, AVG(grade) AS avg_grade FROM students GROUP BY city HAVING AVG(grade) < 2.0", kStmtSelect, SelectStatement, stmt); TEST_PARSE_SINGLE_SQL("SELECT city, AVG(grade) AS avg_grade FROM students GROUP BY city HAVING AVG(grade) < 2.0", kStmtSelect, SelectStatement, stmt);
ASSERT_FALSE(stmt->select_distinct); ASSERT_FALSE(stmt->selectDistinct);
GroupByDescription* group = stmt->group_by; GroupByDescription* group = stmt->groupBy;
ASSERT_NOTNULL(group); ASSERT_NOTNULL(group);
ASSERT_EQ(group->columns->size(), 1); ASSERT_EQ(group->columns->size(), 1);
ASSERT(group->having->isSimpleOp('<')); ASSERT(group->having->isSimpleOp('<'));
ASSERT(group->having->expr->isType(kExprFunctionRef)); ASSERT(group->having->expr->isType(kExprFunctionRef));
ASSERT(group->having->expr2->isType(kExprLiteralFloat)); ASSERT(group->having->expr2->isType(kExprLiteralFloat));
} }
TEST(SelectDistinctTest) { TEST(SelectDistinctTest) {
TEST_PARSE_SINGLE_SQL("SELECT DISTINCT grade, city FROM students;", kStmtSelect, SelectStatement, stmt); TEST_PARSE_SINGLE_SQL("SELECT DISTINCT grade, city FROM students;", kStmtSelect, SelectStatement, stmt);
ASSERT(stmt->select_distinct); ASSERT(stmt->selectDistinct);
ASSERT_NULL(stmt->where_clause); ASSERT_NULL(stmt->whereClause);
} }
TEST(SelectGroupDistinctTest) { TEST(SelectGroupDistinctTest) {
TEST_PARSE_SINGLE_SQL("SELECT city, COUNT(name), COUNT(DISTINCT grade) FROM students GROUP BY city;", kStmtSelect, SelectStatement, stmt); TEST_PARSE_SINGLE_SQL("SELECT city, COUNT(name), COUNT(DISTINCT grade) FROM students GROUP BY city;", kStmtSelect, SelectStatement, stmt);
ASSERT_FALSE(stmt->select_distinct); ASSERT_FALSE(stmt->selectDistinct);
ASSERT_EQ(stmt->select_list->size(), 3); ASSERT_EQ(stmt->selectList->size(), 3);
ASSERT(!stmt->select_list->at(1)->distinct); ASSERT(!stmt->selectList->at(1)->distinct);
ASSERT(stmt->select_list->at(2)->distinct); ASSERT(stmt->selectList->at(2)->distinct);
} }

View File

@ -3,53 +3,57 @@
class TestsManager { class TestsManager {
// Note: static initialization fiasco // Note: static initialization fiasco
// http://www.parashift.com/c++-faq-lite/static-init-order.html // http://www.parashift.com/c++-faq-lite/static-init-order.html
// http://www.parashift.com/c++-faq-lite/static-init-order-on-first-use.html // http://www.parashift.com/c++-faq-lite/static-init-order-on-first-use.html
public: public:
static std::vector<std::string>& test_names() { static std::vector<std::string>& testNames() {
static std::vector<std::string>* test_names = new std::vector<std::string>; static std::vector<std::string>* _testNames = new std::vector<std::string>;
return *test_names; return *_testNames;
} }
static std::vector<void (*)(void)>& tests() { static std::vector<void (*)(void)>& tests() {
static std::vector<void (*)(void)>* tests = new std::vector<void (*)(void)>; static std::vector<void (*)(void)>* tests = new std::vector<void (*)(void)>;
return *tests; return *tests;
} }
}; };
int AddTest(void (*foo)(void), std::string name) { int AddTest(void (*foo)(void), std::string name) {
TestsManager::tests().push_back(foo); TestsManager::tests().push_back(foo);
TestsManager::test_names().push_back(name); TestsManager::testNames().push_back(name);
return 0; return 0;
} }
void RunTests() { size_t RunTests() {
size_t num_failed = 0; size_t numFailed = 0;
for (size_t i = 0; i < TestsManager::tests().size(); ++i) { for (size_t i = 0; i < TestsManager::tests().size(); ++i) {
printf("\033[0;32m{ running}\033[0m %s\n", TestsManager::test_names()[i].c_str()); printf("\033[0;32m{ running}\033[0m %s\n", TestsManager::testNames()[i].c_str());
try { try {
// Run test // Run test
(*TestsManager::tests()[i])(); (*TestsManager::tests()[i])();
printf("\033[0;32m{ ok}\033[0m %s\n", TestsManager::test_names()[i].c_str()); printf("\033[0;32m{ ok}\033[0m %s\n", TestsManager::testNames()[i].c_str());
} catch (AssertionFailedException& e) { } catch (AssertionFailedException& e) {
printf("\033[1;31m{ failed} %s\n", TestsManager::test_names()[i].c_str()); printf("\033[1;31m{ failed} %s\n", TestsManager::testNames()[i].c_str());
printf("\tAssertion failed: %s\n\033[0m", e.what()); printf("\tAssertion failed: %s\n\033[0m", e.what());
num_failed++; numFailed++;
} }
}
} return numFailed;
} }
int main() { int main() {
RunTests(); size_t numFailed = RunTests();
return 0; if (numFailed == 0) {
return 0;
} else {
return -1;
}
} }

View File

@ -13,7 +13,7 @@
#define ASSERT(cond) if (!(cond)) throw AssertionFailedException(#cond); #define ASSERT(cond) if (!(cond)) throw AssertionFailedException(#cond);
#define ASSERT_TRUE(cond) ASSERT(cond); #define ASSERT_TRUE(cond) ASSERT(cond);
#define ASSERT_FALSE(cond) if (cond) throw AssertionFailedException(#cond); #define ASSERT_FALSE(cond) if (cond) throw AssertionFailedException(#cond);
@ -27,28 +27,23 @@
std::cout << "Actual values: " << a << " != " << b << std::endl; \ std::cout << "Actual values: " << a << " != " << b << std::endl; \
} \ } \
ASSERT(a == b); ASSERT(a == b);
class AssertionFailedException: public std::exception { class AssertionFailedException: public std::exception {
public: public:
AssertionFailedException(std::string msg) : AssertionFailedException(std::string msg) :
std::exception(), std::exception(),
_msg(msg) {}; _msg(msg) {};
virtual const char* what() const throw() { virtual const char* what() const throw() {
return _msg.c_str(); return _msg.c_str();
} }
protected: protected:
std::string _msg; std::string _msg;
}; };
int AddTest(void (*foo)(void), std::string name); int AddTest(void (*foo)(void), std::string name);
#endif #endif

View File

@ -33,17 +33,17 @@ int main(int argc, char *argv[]) {
return -1; return -1;
} }
bool expect_false = false; bool expectFalse = false;
bool use_file = false; bool useFile = false;
std::string file_path = ""; std::string filePath = "";
// Parse command line arguments // Parse command line arguments
int i = 1; int i = 1;
for (; i < argc; ++i) { for (; i < argc; ++i) {
if (STREQ(argv[i], "--false")) expect_false = true; if (STREQ(argv[i], "--false")) expectFalse = true;
else if (STREQ(argv[i], "-f")) { else if (STREQ(argv[i], "-f")) {
use_file = true; useFile = true;
file_path = argv[++i]; filePath = argv[++i];
} else { } else {
break; break;
} }
@ -52,44 +52,43 @@ int main(int argc, char *argv[]) {
// Read list of queries for this rest // Read list of queries for this rest
std::vector<std::string> queries; std::vector<std::string> queries;
if (use_file) { if (useFile) {
queries = readlines(file_path); queries = readlines(filePath);
} else { } else {
for (; i < argc; ++i) queries.push_back(argv[i]); for (; i < argc; ++i) queries.push_back(argv[i]);
} }
// Execute queries // Execute queries
int num_failed = 0; int numFailed = 0;
for (std::string sql : queries) { for (std::string sql : queries) {
// Measuring the parsing time // Measuring the parsing time
std::chrono::time_point<std::chrono::system_clock> start, end; std::chrono::time_point<std::chrono::system_clock> start, end;
start = std::chrono::system_clock::now(); start = std::chrono::system_clock::now();
// Parsing // Parsing
SQLStatementList* stmt_list = SQLParser::parseSQLString(sql.c_str()); SQLParserResult* stmt_list = SQLParser::parseSQLString(sql.c_str());
end = std::chrono::system_clock::now(); end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end-start; std::chrono::duration<double> elapsed_seconds = end-start;
double us = elapsed_seconds.count() * 1000 * 1000; double us = elapsed_seconds.count() * 1000 * 1000;
if (expect_false == stmt_list->isValid) { if (expectFalse == stmt_list->isValid) {
printf("\033[0;31m{ failed}\033[0m\n"); printf("\033[0;31m{ failed}\033[0m\n");
printf("\t\033[0;31m%s (L%d:%d)\n\033[0m", stmt_list->parser_msg, stmt_list->error_line, stmt_list->error_col); printf("\t\033[0;31m%s (L%d:%d)\n\033[0m", stmt_list->errorMsg, stmt_list->errorLine, stmt_list->errorColumn);
printf("\t%s\n", sql.c_str()); printf("\t%s\n", sql.c_str());
num_failed++; numFailed++;
} else { } else {
// TODO: indicate whether expect_false was set // TODO: indicate whether expectFalse was set
printf("\033[0;32m{ ok} (%.1fus)\033[0m %s\n", us, sql.c_str()); printf("\033[0;32m{ ok} (%.1fus)\033[0m %s\n", us, sql.c_str());
} }
} }
if (num_failed == 0) { if (numFailed == 0) {
printf("\033[0;32m{ ok} \033[0mAll %lu grammar tests completed successfully!\n", queries.size()); printf("\033[0;32m{ ok} \033[0mAll %lu grammar tests completed successfully!\n", queries.size());
return 0;
} else { } else {
fprintf(stderr, "\033[0;31m{ failed} \033[0mSome grammar tests failed! %d out of %lu tests failed!\n", num_failed, queries.size()); fprintf(stderr, "\033[0;31m{ failed} \033[0mSome grammar tests failed! %d out of %lu tests failed!\n", numFailed, queries.size());
return -1;
} }
return 0;
} }

View File

@ -11,13 +11,13 @@ using namespace hsql;
TEST(DeleteStatementTest) { TEST(DeleteStatementTest) {
SQLStatementList* stmt_list = SQLParser::parseSQLString("DELETE FROM students WHERE grade > 2.0;"); SQLParserResult* result = SQLParser::parseSQLString("DELETE FROM students WHERE grade > 2.0;");
ASSERT(stmt_list->isValid); ASSERT(result->isValid);
ASSERT_EQ(stmt_list->numStatements(), 1); ASSERT_EQ(result->size(), 1);
ASSERT(stmt_list->getStatement(0)->type() == kStmtDelete); ASSERT(result->getStatement(0)->type() == kStmtDelete);
DeleteStatement* stmt = (DeleteStatement*) stmt_list->getStatement(0); DeleteStatement* stmt = (DeleteStatement*) result->getStatement(0);
ASSERT_STREQ(stmt->table_name, "students"); ASSERT_STREQ(stmt->tableName, "students");
ASSERT_NOTNULL(stmt->expr); ASSERT_NOTNULL(stmt->expr);
ASSERT(stmt->expr->isType(kExprOperator)); ASSERT(stmt->expr->isType(kExprOperator));
ASSERT_STREQ(stmt->expr->expr->name, "grade"); ASSERT_STREQ(stmt->expr->expr->name, "grade");
@ -25,14 +25,14 @@ TEST(DeleteStatementTest) {
} }
TEST(CreateStatementTest) { TEST(CreateStatementTest) {
SQLStatementList* stmt_list = SQLParser::parseSQLString("CREATE TABLE students (name TEXT, student_number INT, city INTEGER, grade DOUBLE)"); SQLParserResult* result = SQLParser::parseSQLString("CREATE TABLE students (name TEXT, student_number INT, city INTEGER, grade DOUBLE)");
ASSERT(stmt_list->isValid); ASSERT(result->isValid);
ASSERT_EQ(stmt_list->numStatements(), 1); ASSERT_EQ(result->size(), 1);
ASSERT_EQ(stmt_list->getStatement(0)->type(), kStmtCreate); ASSERT_EQ(result->getStatement(0)->type(), kStmtCreate);
CreateStatement* stmt = (CreateStatement*) stmt_list->getStatement(0); CreateStatement* stmt = (CreateStatement*) result->getStatement(0);
ASSERT_EQ(stmt->type, CreateStatement::kTable); ASSERT_EQ(stmt->type, CreateStatement::kTable);
ASSERT_STREQ(stmt->table_name, "students"); ASSERT_STREQ(stmt->tableName, "students");
ASSERT_NOTNULL(stmt->columns); ASSERT_NOTNULL(stmt->columns);
ASSERT_EQ(stmt->columns->size(), 4); ASSERT_EQ(stmt->columns->size(), 4);
ASSERT_STREQ(stmt->columns->at(0)->name, "name"); ASSERT_STREQ(stmt->columns->at(0)->name, "name");
@ -47,12 +47,12 @@ TEST(CreateStatementTest) {
TEST(UpdateStatementTest) { TEST(UpdateStatementTest) {
SQLStatementList* stmt_list = SQLParser::parseSQLString("UPDATE students SET grade = 5.0, name = 'test' WHERE name = 'Max Mustermann';"); SQLParserResult* result = SQLParser::parseSQLString("UPDATE students SET grade = 5.0, name = 'test' WHERE name = 'Max Mustermann';");
ASSERT(stmt_list->isValid); ASSERT(result->isValid);
ASSERT_EQ(stmt_list->numStatements(), 1); ASSERT_EQ(result->size(), 1);
ASSERT_EQ(stmt_list->getStatement(0)->type(), kStmtUpdate); ASSERT_EQ(result->getStatement(0)->type(), kStmtUpdate);
UpdateStatement* stmt = (UpdateStatement*) stmt_list->getStatement(0); UpdateStatement* stmt = (UpdateStatement*) result->getStatement(0);
ASSERT_NOTNULL(stmt->table); ASSERT_NOTNULL(stmt->table);
ASSERT_STREQ(stmt->table->name, "students"); ASSERT_STREQ(stmt->table->name, "students");
@ -99,33 +99,33 @@ TEST(PrepareStatementTest) {
"PREPARE stmt: SELECT * FROM data WHERE c1 = ?;" "PREPARE stmt: SELECT * FROM data WHERE c1 = ?;"
"DEALLOCATE PREPARE stmt;"; "DEALLOCATE PREPARE stmt;";
TEST_PARSE_SQL_QUERY(query, stmt_list, 3); TEST_PARSE_SQL_QUERY(query, result, 3);
TEST_CAST_STMT(stmt_list, 0, kStmtPrepare, PrepareStatement, prep1); TEST_CAST_STMT(result, 0, kStmtPrepare, PrepareStatement, prep1);
TEST_CAST_STMT(stmt_list, 1, kStmtPrepare, PrepareStatement, prep2); TEST_CAST_STMT(result, 1, kStmtPrepare, PrepareStatement, prep2);
TEST_CAST_STMT(stmt_list, 2, kStmtDrop, DropStatement, drop); TEST_CAST_STMT(result, 2, kStmtDrop, DropStatement, drop);
// Prepare Statement #1 // Prepare Statement #1
ASSERT_STREQ(prep1->name, "test"); ASSERT_STREQ(prep1->name, "test");
ASSERT_EQ(prep1->placeholders.size(), 3); ASSERT_EQ(prep1->placeholders.size(), 3);
ASSERT_EQ(prep1->query->numStatements(), 2); ASSERT_EQ(prep1->query->size(), 2);
TEST_CAST_STMT(prep1->query, 0, kStmtInsert, InsertStatement, insert); TEST_CAST_STMT(prep1->query, 0, kStmtInsert, InsertStatement, insert);
TEST_CAST_STMT(prep1->query, 1, kStmtSelect, SelectStatement, select); TEST_CAST_STMT(prep1->query, 1, kStmtSelect, SelectStatement, select);
ASSERT(insert->values->at(0)->isType(kExprPlaceholder)); ASSERT(insert->values->at(0)->isType(kExprPlaceholder));
ASSERT(select->select_list->at(0)->isType(kExprPlaceholder)); ASSERT(select->selectList->at(0)->isType(kExprPlaceholder));
ASSERT(select->where_clause->expr2->isType(kExprPlaceholder)); ASSERT(select->whereClause->expr2->isType(kExprPlaceholder));
// Check IDs of placeholders // Check IDs of placeholders
ASSERT_EQ(insert->values->at(0)->ival, 0); ASSERT_EQ(insert->values->at(0)->ival, 0);
ASSERT_EQ(insert->values->at(0), prep1->placeholders[0]); ASSERT_EQ(insert->values->at(0), prep1->placeholders[0]);
ASSERT_EQ(select->select_list->at(0)->ival, 1); ASSERT_EQ(select->selectList->at(0)->ival, 1);
ASSERT_EQ(select->select_list->at(0), prep1->placeholders[1]); ASSERT_EQ(select->selectList->at(0), prep1->placeholders[1]);
ASSERT_EQ(select->where_clause->expr2->ival, 2); ASSERT_EQ(select->whereClause->expr2->ival, 2);
ASSERT_EQ(select->where_clause->expr2, prep1->placeholders[2]); ASSERT_EQ(select->whereClause->expr2, prep1->placeholders[2]);
// Prepare Statement #2 // Prepare Statement #2
ASSERT_STREQ(prep2->name, "stmt"); ASSERT_STREQ(prep2->name, "stmt");

15
test/test.sh Executable file
View File

@ -0,0 +1,15 @@
#!/bin/bash
# has to be executed from the root of the repository
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./
bin/sql_grammar_test -f "test/lib/valid_queries.sql"
RET1=$?
bin/sql_tests
RET2=$?
if [[ $RET1 != 0 ]]; then exit $RET1; fi
if [[ $RET2 != 0 ]]; then exit $RET2; fi
exit 0