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
*.out
*.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)
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
CC = g++
CFLAGS = -std=c++11 -Wall -fPIC
LIBFLAGS = -shared
TARGET = libsqlparser.so
INSTALL = /usr/local
CTESTFLAGS = -Wall -Isrc/ -Itest/ -L./ -std=c++11
CTESTFLAGS = -Wall -Isrc/ -Itest/ -L./ -std=c++11 -lstdc++
all: library
library: $(TARGET)
$(TARGET): $(LIBOBJ)
$(CC) $(LIBFLAGS) -o $(TARGET) $(LIBOBJ)
$(CXX) $(LIBFLAGS) -o $(TARGET) $(LIBOBJ)
%.o: %.cpp $(PARSERFILES)
$(CC) $(CFLAGS) -c -o $@ $<
$(CXX) $(CFLAGS) -c -o $@ $<
$(SRCPARSER)/bison_parser.cpp: parser
$(SRCPARSER)/flex_lexer.cpp: parser
@ -48,20 +50,26 @@ cleanall: clean cleanparser
install:
cp $(TARGET) $(INSTALL)/lib/$(TARGET)
format:
astyle --options=astyle.options $(ALLLIB)
astyle --options=astyle.options $(ALLTEST)
############
### Test ###
############
test: $(BIN)/sql_tests $(BIN)/sql_grammar_test
LD_LIBRARY_PATH=./ $(BIN)/sql_grammar_test -f "test/lib/valid_queries.sql"
LD_LIBRARY_PATH=./ $(BIN)/sql_tests
bash test/test.sh
# test whete
test_install:
make -C example/
./example/example "SELECT * FROM students WHERE name = 'Max Mustermann';"
$(BIN)/sql_tests: library
@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
@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
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)
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
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
### Development
**Prerequisites:**
* [bison](https://www.gnu.org/software/bison/) (tested with v3.0.2)
* [flex](http://flex.sourceforge.net/) (tested with v2.5.5)
**Requirements for development:**
* gcc 4.8 (or newer)
* [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:
@ -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.
### Documenation
### Resources
* [Working Syntax Examples](docs/syntax.md)
* [Known Issues](docs/issues.md)
* [Developer Documentation](docs/documentation.md)
* [Integration in Hyrise](docs/integration.md)
* [Working Syntax Examples](docs/syntax.md)
* [Developer Documentation](docs/dev-docs.md)
### 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
=======================
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/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.
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`.
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:
* src/parser/bison_parser.y
* src/parser/flex_lexer.l
* src/parser/keywordlist_generator.py
* src/parser/sql_keywords.txt
````
src/parser/bison_parser.y
src/parser/flex_lexer.l
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.
@ -33,7 +35,6 @@ If your defining a new statement, you will need to define your type in the \%uni
## Implementing Tests
Related files:
* src/sql_tests.cpp
All test related files are in `test/`. Take a look to see how tests are implemented.

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 -Wall -I../src/ -L../
CFLAGS = -std=c++11 -lstdc++ -Wall -I../src/ -L../
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];
// parse a given query
hsql::SQLStatementList* result = hsql::SQLParser::parseSQLString(query);
hsql::SQLParserResult* result = hsql::SQLParser::parseSQLString(query);
// check whether the parsing was successful
if (result->isValid) {
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) {
// process the statements...
hsql::printStatementInfo(stmt);
}
return 0;
} else {
printf("Invalid SQL!\n");
return -1;
}
return 0;
}

View File

@ -8,38 +8,39 @@
namespace hsql {
SQLParser::SQLParser() {
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;
SQLParser::SQLParser() {
fprintf(stderr, "SQLParser only has static methods atm! Do not initialize!\n");
}
state = hsql__scan_string(text, scanner);
if (hsql_parse(&result, scanner)) {
// Returns an error stmt object
SQLParserResult* SQLParser::parseSQLString(const char *text) {
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;
}
hsql__delete_buffer(state, scanner);
hsql_lex_destroy(scanner);
return result;
}
SQLParserResult* SQLParser::parseSQLString(const std::string& text) {
return parseSQLString(text.c_str());
}
SQLStatementList* SQLParser::parseSQLString(const std::string& text) {
return parseSQLString(text.c_str());
}
} // namespace hsql

View File

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

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;
#endif
/* "%code requires" blocks. */
#line 43 "bison_parser.y" /* yacc.c:1909 */
#line 42 "bison_parser.y" /* yacc.c:1909 */
// %code requires block
#include "../sql/statements.h"
#include "../SQLParserResult.h"
#include "parser_typedef.h"
// 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. */
#ifndef HSQL_TOKENTYPE
@ -206,7 +209,7 @@ extern int hsql_debug;
typedef union HSQL_STYPE HSQL_STYPE;
union HSQL_STYPE
{
#line 99 "bison_parser.y" /* yacc.c:1909 */
#line 101 "bison_parser.y" /* yacc.c:1909 */
double fval;
int64_t ival;
@ -234,7 +237,7 @@ union HSQL_STYPE
hsql::GroupByDescription* group_t;
hsql::UpdateClause* update_t;
hsql::SQLStatementList* stmt_list;
hsql::SQLParserResult* stmt_list;
std::vector<char*>* str_vec;
std::vector<hsql::TableRef*>* table_vec;
@ -242,7 +245,7 @@ union HSQL_STYPE
std::vector<hsql::UpdateClause*>* update_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_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 */

View File

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

View File

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

View File

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

View File

@ -4,65 +4,59 @@
#include "SQLStatement.h"
namespace hsql {
/**
* Represents definition of a table column
*/
struct ColumnDefinition {
enum DataType {
TEXT,
INT,
DOUBLE
};
/**
* @struct ColumnDefinition
* @brief Represents definition of a table column
*/
struct ColumnDefinition {
enum DataType {
TEXT,
INT,
DOUBLE
};
ColumnDefinition(char* name, DataType type) :
name(name),
type(type) {}
ColumnDefinition(char* name, DataType type) :
name(name),
type(type) {}
virtual ~ColumnDefinition() {
delete name;
}
virtual ~ColumnDefinition() {
delete name;
}
char* 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) {};
/**
* @struct CreateStatement
* @brief Represents "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),
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;
};
virtual ~CreateStatement() {
delete columns;
delete filePath;
delete tableName;
}
CreateType type;
bool ifNotExists;
const char* filePath;
const char* tableName;
std::vector<ColumnDefinition*>* columns;
};
} // namespace hsql
#endif

View File

@ -4,31 +4,26 @@
#include "SQLStatement.h"
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;
}
/**
* @struct DeleteStatement
* @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;
};
char* tableName;
Expr* expr;
};
} // namespace hsql
#endif

View File

@ -4,39 +4,31 @@
#include "SQLStatement.h"
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) {}
/**
* @struct DropStatement
* @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;
};
virtual ~DropStatement() {
delete name;
}
EntityType type;
const char* name;
};
} // namespace hsql
#endif

View File

@ -4,29 +4,24 @@
#include "SQLStatement.h"
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;
}
/**
* @struct ExecuteStatement
* @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;
};
const char* name;
std::vector<Expr*>* parameters;
};
} // namsepace hsql
#endif

View File

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

View File

@ -7,116 +7,127 @@
namespace hsql {
// Helper function
char* substr(const char* source, int from, int to);
char* substr(const char* source, int from, int to);
typedef enum {
kExprLiteralFloat,
kExprLiteralString,
kExprLiteralInt,
kExprStar,
kExprPlaceholder,
kExprColumnRef,
kExprFunctionRef,
kExprOperator
} ExprType;
typedef enum {
kExprLiteralFloat,
kExprLiteralString,
kExprLiteralInt,
kExprStar,
kExprPlaceholder,
kExprColumnRef,
kExprFunctionRef,
kExprOperator
} ExprType;
typedef struct Expr Expr;
typedef struct Expr Expr;
/**
* @class Expr
* @brief 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
*/
typedef enum {
SIMPLE_OP,
// Binary
NOT_EQUALS,
LESS_EQ,
GREATER_EQ,
LIKE,
NOT_LIKE,
AND,
OR,
// Unary
NOT,
UMINUS,
ISNULL
} OperatorType;
/**
* 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
*/
typedef enum {
SIMPLE_OP,
// Binary
NOT_EQUALS,
LESS_EQ,
GREATER_EQ,
LIKE,
NOT_LIKE,
AND,
OR,
// Unary
NOT,
UMINUS,
ISNULL
} OperatorType;
Expr(ExprType type) :
type(type),
expr(NULL),
expr2(NULL),
name(NULL),
table(NULL),
alias(NULL) {};
Expr(ExprType type) :
type(type),
expr(NULL),
expr2(NULL),
name(NULL),
table(NULL),
alias(NULL) {};
// Interesting side-effect:
// Making the destructor virtual used to cause segmentation faults
~Expr();
ExprType type;
// Interesting side-effect:
// Making the destructor virtual used to cause segmentation faults
~Expr();
Expr* expr;
Expr* expr2;
char* name;
char* table;
char* alias;
float fval;
int64_t ival;
int64_t ival2;
ExprType type;
OperatorType op_type;
char op_char;
bool distinct;
Expr* expr;
Expr* expr2;
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
*/
inline bool isType(ExprType e_type) { return e_type == type; }
inline bool isLiteral() { return isType(kExprLiteralInt) || isType(kExprLiteralFloat) || isType(kExprLiteralString) || isType(kExprPlaceholder); }
inline bool hasAlias() { return alias != NULL; }
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; }
/**
* Convenience accessor methods
*/
inline bool isType(ExprType e_type) {
return e_type == type;
}
inline bool isLiteral() {
return isType(kExprLiteralInt) || isType(kExprLiteralFloat) || isType(kExprLiteralString) || isType(kExprPlaceholder);
}
inline bool hasAlias() {
return alias != NULL;
}
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 Expr* makeOpUnary(OperatorType op, Expr* expr);
static Expr* makeOpBinary(Expr* expr1, char op, Expr* expr2);
static Expr* makeOpBinary(Expr* expr1, OperatorType op, Expr* expr2);
/**
* Static expression constructors
*/
static Expr* makeOpUnary(OperatorType op, Expr* expr);
static Expr* makeOpBinary(Expr* expr1, char op, Expr* expr2);
static Expr* makeOpBinary(Expr* expr1, OperatorType op, Expr* expr2);
static Expr* makeLiteral(int64_t val);
static Expr* makeLiteral(double val);
static Expr* makeLiteral(char* val);
static Expr* makeLiteral(int64_t val);
static Expr* makeLiteral(double val);
static Expr* makeLiteral(char* val);
static Expr* makeColumnRef(char* name);
static Expr* makeColumnRef(char* table, char* name);
static Expr* makeFunctionRef(char* func_name, Expr* expr, bool distinct);
static Expr* makeColumnRef(char* name);
static Expr* makeColumnRef(char* table, char* name);
static Expr* makeFunctionRef(char* func_name, Expr* expr, bool distinct);
static Expr* makePlaceholder(int id);
};
static Expr* makePlaceholder(int id);
};
// 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

View File

@ -4,39 +4,30 @@
#include "SQLStatement.h"
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;
}
/**
* @struct ImportStatement
* @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;
};
ImportType type;
const char* filePath;
const char* tableName;
};
} // namespace hsql

View File

@ -5,42 +5,37 @@
#include "SelectStatement.h"
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) {}
/**
* @struct InsertStatement
* @brief Represents "INSERT INTO students VALUES ('Max', 1112233, 'Musterhausen', 2.3)"
*/
struct InsertStatement : SQLStatement {
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;
};
virtual ~InsertStatement() {
delete tableName;
delete columns;
delete values;
delete select;
}
InsertType type;
const char* tableName;
std::vector<char*>* columns;
std::vector<Expr*>* values;
SelectStatement* select;
};
} // namsepace hsql
#endif

View File

@ -1,53 +1,49 @@
#ifndef __PREPARE_STATEMENT_H__
#define __PREPARE_STATEMENT_H__
#include "../SQLParserResult.h"
#include "SQLStatement.h"
#include "SelectStatement.h"
#include <algorithm>
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
* @brief Represents "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;
}
/**
* @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;
};
/**
* 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.
*
* @param vector of placeholders that the parser found
*/
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;
SQLParserResult* query;
std::vector<Expr*> placeholders;
};
} // namsepace hsql
#endif

View File

@ -1,86 +1,42 @@
/*
* SQLStatement.h
* Definition of the structure used to build the syntax tree.
*/
#ifndef __STATEMENT_H__
#define __STATEMENT_H__
#ifndef __SQLSTATEMENT_H__
#define __SQLSTATEMENT_H__
#include "Expr.h"
#include <vector>
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 {
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 ~SQLStatement() {}
virtual StatementType type() {
return _type;
}
private:
StatementType _type;
};
} // namespace hsql
#endif // __STATEMENT_H__
#endif // __SQLSTATEMENT_H__

View File

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

View File

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

View File

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

View File

@ -5,12 +5,12 @@
namespace hsql {
TableRef::~TableRef() {
delete name;
delete alias;
delete select;
delete list;
}
TableRef::~TableRef() {
delete name;
delete alias;
delete select;
delete list;
}
} // 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 {
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'); }
void inprint(int64_t val, uint num_indent) { printf("%s%lld \n", indent(num_indent).c_str(), val); }
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(const char* val, const char* val2, uint num_indent) { printf("%s%s->%s\n", indent(num_indent).c_str(), val, val2); }
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 printTableRefInfo(TableRef* table, uint num_indent) {
switch (table->type) {
case kTableName:
inprint(table->name, num_indent);
break;
case kTableSelect:
printSelectStatementInfo(table->select, num_indent);
break;
case kTableJoin:
inprint("Join Table", num_indent);
inprint("Left", num_indent+1);
printTableRefInfo(table->join->left, num_indent+2);
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);
std::string indent(uint numIndent) {
return std::string(numIndent, '\t');
}
void inprint(int64_t val, uint numIndent) {
printf("%s%ld \n", indent(numIndent).c_str(), val);
}
void inprint(float val, uint numIndent) {
printf("%s%f\n", indent(numIndent).c_str(), val);
}
void inprint(const char* val, uint numIndent) {
printf("%s%s\n", indent(numIndent).c_str(), val);
}
void inprint(const char* val, const char* val2, uint numIndent) {
printf("%s%s->%s\n", indent(numIndent).c_str(), val, val2);
}
void inprintC(char val, uint numIndent) {
printf("%s%c\n", indent(numIndent).c_str(), val);
}
void inprintU(uint64_t val, uint numIndent) {
printf("%s%lu\n", indent(numIndent).c_str(), val);
}
}
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) {
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;
}
}
void printTableRefInfo(TableRef* table, uint numIndent) {
switch (table->type) {
case kTableName:
inprint(table->name, numIndent);
break;
case kTableSelect:
printSelectStatementInfo(table->select, numIndent);
break;
case kTableJoin:
inprint("Join Table", numIndent);
inprint("Left", numIndent+1);
printTableRefInfo(table->join->left, numIndent+2);
inprint("Right", numIndent+1);
printTableRefInfo(table->join->right, numIndent+2);
inprint("Join Condition", numIndent+1);
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

View File

@ -1,18 +1,16 @@
#ifndef __SQLHELPER_H__
#define __SQLHELPER_H__
#include "sqltypes.h"
#include "sql/statements.h"
namespace hsql {
void printStatementInfo(SQLStatement* stmt);
void printSelectStatementInfo(SelectStatement* stmt, uint num_indent);
void printImportStatementInfo(ImportStatement* stmt, uint num_indent);
void printInsertStatementInfo(InsertStatement* stmt, uint num_indent);
void printCreateStatementInfo(CreateStatement* stmt, uint num_indent);
void printExpression(Expr* expr, uint num_indent);
void printStatementInfo(SQLStatement* stmt);
void printSelectStatementInfo(SelectStatement* stmt, uint num_indent);
void printImportStatementInfo(ImportStatement* stmt, uint num_indent);
void printInsertStatementInfo(InsertStatement* stmt, uint num_indent);
void printCreateStatementInfo(CreateStatement* stmt, uint num_indent);
void printExpression(Expr* expr, uint num_indent);
} // 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 TEST_PARSE_SQL_QUERY(query, output_var, num_statements) \
SQLStatementList* output_var = SQLParser::parseSQLString(query); \
ASSERT(output_var->isValid); \
ASSERT_EQ(output_var->numStatements(), num_statements);
#define TEST_PARSE_SQL_QUERY(query, outputVar, numStatements) \
SQLParserResult* outputVar = SQLParser::parseSQLString(query); \
ASSERT(outputVar->isValid); \
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); \
ASSERT_EQ(stmt_list->getStatement(0)->type(), stmt_type); \
stmt_class* output_var = (stmt_class*) stmt_list->getStatement(0);
ASSERT_EQ(stmt_list->getStatement(0)->type(), stmtType); \
stmtClass* outputVar = (stmtClass*) stmt_list->getStatement(0);
#define TEST_CAST_STMT(stmt_list, stmt_index, stmt_type, stmt_class, output_var) \
ASSERT_EQ(stmt_list->getStatement(stmt_index)->type(), stmt_type); \
stmt_class* output_var = (stmt_class*) stmt_list->getStatement(stmt_index);
#define TEST_CAST_STMT(stmt_list, stmt_index, stmtType, stmtClass, outputVar) \
ASSERT_EQ(stmt_list->getStatement(stmt_index)->type(), stmtType); \
stmtClass* outputVar = (stmtClass*) stmt_list->getStatement(stmt_index);
#endif

View File

@ -7,40 +7,40 @@
using namespace hsql;
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->group_by);
ASSERT_NULL(stmt->whereClause);
ASSERT_NULL(stmt->groupBy);
}
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);
ASSERT_FALSE(stmt->select_distinct);
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->selectDistinct);
GroupByDescription* group = stmt->group_by;
ASSERT_NOTNULL(group);
ASSERT_EQ(group->columns->size(), 1);
ASSERT(group->having->isSimpleOp('<'));
ASSERT(group->having->expr->isType(kExprFunctionRef));
ASSERT(group->having->expr2->isType(kExprLiteralFloat));
GroupByDescription* group = stmt->groupBy;
ASSERT_NOTNULL(group);
ASSERT_EQ(group->columns->size(), 1);
ASSERT(group->having->isSimpleOp('<'));
ASSERT(group->having->expr->isType(kExprFunctionRef));
ASSERT(group->having->expr2->isType(kExprLiteralFloat));
}
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_NULL(stmt->where_clause);
ASSERT(stmt->selectDistinct);
ASSERT_NULL(stmt->whereClause);
}
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_EQ(stmt->select_list->size(), 3);
ASSERT(!stmt->select_list->at(1)->distinct);
ASSERT(stmt->select_list->at(2)->distinct);
ASSERT_FALSE(stmt->selectDistinct);
ASSERT_EQ(stmt->selectList->size(), 3);
ASSERT(!stmt->selectList->at(1)->distinct);
ASSERT(stmt->selectList->at(2)->distinct);
}

View File

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

View File

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

View File

@ -33,17 +33,17 @@ int main(int argc, char *argv[]) {
return -1;
}
bool expect_false = false;
bool use_file = false;
std::string file_path = "";
bool expectFalse = false;
bool useFile = false;
std::string filePath = "";
// Parse command line arguments
int i = 1;
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")) {
use_file = true;
file_path = argv[++i];
useFile = true;
filePath = argv[++i];
} else {
break;
}
@ -52,44 +52,43 @@ int main(int argc, char *argv[]) {
// Read list of queries for this rest
std::vector<std::string> queries;
if (use_file) {
queries = readlines(file_path);
if (useFile) {
queries = readlines(filePath);
} else {
for (; i < argc; ++i) queries.push_back(argv[i]);
}
// Execute queries
int num_failed = 0;
int numFailed = 0;
for (std::string sql : queries) {
// Measuring the parsing time
std::chrono::time_point<std::chrono::system_clock> start, end;
start = std::chrono::system_clock::now();
// Parsing
SQLStatementList* stmt_list = SQLParser::parseSQLString(sql.c_str());
SQLParserResult* stmt_list = SQLParser::parseSQLString(sql.c_str());
end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end-start;
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("\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());
num_failed++;
numFailed++;
} 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());
}
}
if (num_failed == 0) {
if (numFailed == 0) {
printf("\033[0;32m{ ok} \033[0mAll %lu grammar tests completed successfully!\n", queries.size());
return 0;
} 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) {
SQLStatementList* stmt_list = SQLParser::parseSQLString("DELETE FROM students WHERE grade > 2.0;");
ASSERT(stmt_list->isValid);
ASSERT_EQ(stmt_list->numStatements(), 1);
ASSERT(stmt_list->getStatement(0)->type() == kStmtDelete);
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);
DeleteStatement* stmt = (DeleteStatement*) stmt_list->getStatement(0);
ASSERT_STREQ(stmt->table_name, "students");
DeleteStatement* stmt = (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");
@ -25,14 +25,14 @@ TEST(DeleteStatementTest) {
}
TEST(CreateStatementTest) {
SQLStatementList* stmt_list = SQLParser::parseSQLString("CREATE TABLE students (name TEXT, student_number INT, city INTEGER, grade DOUBLE)");
ASSERT(stmt_list->isValid);
ASSERT_EQ(stmt_list->numStatements(), 1);
ASSERT_EQ(stmt_list->getStatement(0)->type(), kStmtCreate);
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);
CreateStatement* stmt = (CreateStatement*) stmt_list->getStatement(0);
CreateStatement* stmt = (CreateStatement*) result->getStatement(0);
ASSERT_EQ(stmt->type, CreateStatement::kTable);
ASSERT_STREQ(stmt->table_name, "students");
ASSERT_STREQ(stmt->tableName, "students");
ASSERT_NOTNULL(stmt->columns);
ASSERT_EQ(stmt->columns->size(), 4);
ASSERT_STREQ(stmt->columns->at(0)->name, "name");
@ -47,12 +47,12 @@ TEST(CreateStatementTest) {
TEST(UpdateStatementTest) {
SQLStatementList* stmt_list = SQLParser::parseSQLString("UPDATE students SET grade = 5.0, name = 'test' WHERE name = 'Max Mustermann';");
ASSERT(stmt_list->isValid);
ASSERT_EQ(stmt_list->numStatements(), 1);
ASSERT_EQ(stmt_list->getStatement(0)->type(), kStmtUpdate);
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);
UpdateStatement* stmt = (UpdateStatement*) stmt_list->getStatement(0);
UpdateStatement* stmt = (UpdateStatement*) result->getStatement(0);
ASSERT_NOTNULL(stmt->table);
ASSERT_STREQ(stmt->table->name, "students");
@ -99,33 +99,33 @@ TEST(PrepareStatementTest) {
"PREPARE stmt: SELECT * FROM data WHERE c1 = ?;"
"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(stmt_list, 1, kStmtPrepare, PrepareStatement, prep2);
TEST_CAST_STMT(stmt_list, 2, kStmtDrop, DropStatement, drop);
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->numStatements(), 2);
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->select_list->at(0)->isType(kExprPlaceholder));
ASSERT(select->where_clause->expr2->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->select_list->at(0)->ival, 1);
ASSERT_EQ(select->select_list->at(0), prep1->placeholders[1]);
ASSERT_EQ(select->selectList->at(0)->ival, 1);
ASSERT_EQ(select->selectList->at(0), prep1->placeholders[1]);
ASSERT_EQ(select->where_clause->expr2->ival, 2);
ASSERT_EQ(select->where_clause->expr2, prep1->placeholders[2]);
ASSERT_EQ(select->whereClause->expr2->ival, 2);
ASSERT_EQ(select->whereClause->expr2, prep1->placeholders[2]);
// Prepare Statement #2
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