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,18 +9,23 @@ 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:**
**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)
@ -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)
* [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);
}
} else {
printf("Invalid SQL!\n");
}
return 0;
} else {
printf("Invalid SQL!\n");
return -1;
}
}

View File

@ -13,8 +13,8 @@ SQLParser::SQLParser() {
}
SQLStatementList* SQLParser::parseSQLString(const char *text) {
SQLStatementList* result;
SQLParserResult* SQLParser::parseSQLString(const char *text) {
SQLParserResult* result = NULL;
yyscan_t scanner;
YY_BUFFER_STATE state;
@ -37,7 +37,8 @@ SQLStatementList* SQLParser::parseSQLString(const char *text) {
return result;
}
SQLStatementList* SQLParser::parseSQLString(const std::string& text) {
SQLParserResult* SQLParser::parseSQLString(const std::string& text) {
return parseSQLString(text.c_str());
}

View File

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

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,10 +4,8 @@
#include "SQLStatement.h"
namespace hsql {
/**
* @struct ColumnDefinition
* @brief Represents definition of a table column
* Represents definition of a table column
*/
struct ColumnDefinition {
enum DataType {
@ -28,41 +26,37 @@ struct ColumnDefinition {
DataType type;
};
/**
* @struct CreateStatement
* @brief Represents "CREATE TABLE students (name TEXT, student_number INTEGER, city TEXT, grade DOUBLE)"
* 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
kTableFromTbl // Hyrise file format
};
CreateStatement(CreateType type) :
SQLStatement(kStmtCreate),
type(type),
if_not_exists(false),
columns(NULL),
file_path(NULL),
table_name(NULL) {};
ifNotExists(false),
filePath(NULL),
tableName(NULL),
columns(NULL) {};
virtual ~CreateStatement() {
delete columns;
delete file_path;
delete table_name;
delete filePath;
delete tableName;
}
CreateType type;
bool if_not_exists;
bool ifNotExists;
const char* filePath;
const char* tableName;
std::vector<ColumnDefinition*>* columns;
const char* file_path;
const char* table_name;
};
} // namespace hsql
#endif

View File

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

View File

@ -4,11 +4,9 @@
#include "SQLStatement.h"
namespace hsql {
/**
* @struct DropStatement
* @brief Represents "DROP TABLE"
* Represents SQL Delete statements.
* Example "DROP TABLE students;"
*/
struct DropStatement : SQLStatement {
enum EntityType {
@ -19,7 +17,6 @@ struct DropStatement : SQLStatement {
kPreparedStatement
};
DropStatement(EntityType type) :
SQLStatement(kStmtDrop),
type(type),
@ -29,14 +26,9 @@ struct DropStatement : SQLStatement {
delete name;
}
EntityType type;
const char* name;
};
} // namespace hsql
#endif

View File

@ -4,11 +4,9 @@
#include "SQLStatement.h"
namespace hsql {
/**
* @struct ExecuteStatement
* @brief Represents "EXECUTE ins_prep(100, "test", 2.3);"
* Represents SQL Execute statements.
* Example: "EXECUTE ins_prep(100, "test", 2.3);"
*/
struct ExecuteStatement : SQLStatement {
ExecuteStatement() :
@ -25,8 +23,5 @@ struct ExecuteStatement : SQLStatement {
std::vector<Expr*>* parameters;
};
} // namsepace hsql
#endif

View File

@ -26,8 +26,7 @@ typedef enum {
typedef struct Expr Expr;
/**
* @class Expr
* @brief Represents SQL expressions (i.e. literals, operators, column_refs)
* Represents SQL expressions (i.e. literals, operators, column_refs)
*
* TODO: When destructing a placeholder expression, we might need to alter the placeholder_list
*/
@ -88,16 +87,28 @@ struct Expr {
/**
* 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 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; }
inline bool isSimpleOp() {
return op_type == SIMPLE_OP;
}
inline bool isSimpleOp(char op) {
return isSimpleOp() && op_char == op;
}
/**

View File

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

View File

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

View File

@ -1,16 +1,15 @@
#ifndef __PREPARE_STATEMENT_H__
#define __PREPARE_STATEMENT_H__
#include "../SQLParserResult.h"
#include "SQLStatement.h"
#include "SelectStatement.h"
#include <algorithm>
namespace hsql {
/**
* @struct PrepareStatement
* @brief Represents "PREPARE ins_prep: SELECT * FROM t1 WHERE c1 = ? AND c2 = ?"
* Represents SQL Prepare statements.
* Example: "PREPARE ins_prep: SELECT * FROM t1 WHERE c1 = ? AND c2 = ?"
*/
struct PrepareStatement : SQLStatement {
PrepareStatement() :
@ -24,10 +23,10 @@ struct PrepareStatement : SQLStatement {
}
/**
* @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.
*
* @param vector of placeholders that the parser found
*/
void setPlaceholders(std::vector<void*> ph) {
for (void* e : ph) {
@ -42,12 +41,9 @@ struct PrepareStatement : SQLStatement {
}
const char* name;
SQLStatementList* query;
SQLParserResult* query;
std::vector<Expr*> placeholders;
};
} // namsepace hsql
#endif

View File

@ -1,18 +1,12 @@
/*
* 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
kStmtError, // unused
kStmtSelect,
kStmtImport,
kStmtInsert,
@ -27,10 +21,8 @@ typedef enum {
kStmtAlter
} StatementType;
/**
* @struct SQLStatement
* @brief Base class for every SQLStatement
* Base struct for every SQL statement
*/
struct SQLStatement {
SQLStatement(StatementType type) :
@ -38,49 +30,13 @@ struct SQLStatement {
virtual ~SQLStatement() {}
virtual StatementType type() { return _type; }
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;
};
} // namespace hsql
#endif // __STATEMENT_H__
#endif // __SQLSTATEMENT_H__

View File

@ -6,20 +6,15 @@
#include "Table.h"
namespace hsql {
/**
* @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;
/**
* 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),
@ -33,12 +28,12 @@ struct OrderDescription {
Expr* expr;
};
/**
* @struct LimitDescription
* @brief Description of the limit clause within a select statement
*/
const int64_t kNoLimit = -1;
const int64_t kNoOffset = -1;
/**
* Description of the limit clause within a select statement
*/
struct LimitDescription {
LimitDescription(int64_t limit, int64_t offset) :
limit(limit),
@ -49,7 +44,7 @@ struct LimitDescription {
};
/**
* @struct GroupByDescription
* Description of the group-by clause within a select statement
*/
struct GroupByDescription {
GroupByDescription() :
@ -66,43 +61,40 @@ struct GroupByDescription {
};
/**
* @struct SelectStatement
* @brief Representation of a full select statement.
*
* Representation of a full SQL 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),
fromTable(NULL),
selectDistinct(false),
selectList(NULL),
whereClause(NULL),
groupBy(NULL),
unionSelect(NULL),
order(NULL),
limit(NULL) {};
virtual ~SelectStatement() {
delete from_table;
delete select_list;
delete where_clause;
delete group_by;
delete fromTable;
delete selectList;
delete whereClause;
delete groupBy;
delete order;
delete limit;
}
TableRef* from_table;
bool select_distinct;
std::vector<Expr*>* select_list;
Expr* where_clause;
GroupByDescription* group_by;
TableRef* fromTable;
bool selectDistinct;
std::vector<Expr*>* selectList;
Expr* whereClause;
GroupByDescription* groupBy;
SelectStatement* union_select;
SelectStatement* unionSelect;
OrderDescription* order;
LimitDescription* limit;
};
} // namespace hsql
#endif

View File

@ -54,7 +54,9 @@ struct TableRef {
/**
* Convenience accessor methods
*/
inline bool hasSchema() { return schema != NULL; }
inline bool hasSchema() {
return schema != NULL;
}
inline char* getName() {
if (alias != NULL) return alias;

View File

@ -4,21 +4,16 @@
#include "SQLStatement.h"
namespace hsql {
/**
* @struct UpdateClause
* @brief Represents "column = value" expressions
* Represents "column = value" expressions
*/
struct UpdateClause {
char* column;
Expr* value;
};
/**
* @struct UpdateStatement
* @brief Represents "UPDATE"
* Represents SQL Update statements.
*/
struct UpdateStatement : SQLStatement {
UpdateStatement() :
@ -39,7 +34,5 @@ struct UpdateStatement : SQLStatement {
Expr* where;
};
} // namsepace hsql
#endif

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,138 +5,183 @@
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); }
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);
}
void printTableRefInfo(TableRef* table, uint num_indent) {
void printTableRefInfo(TableRef* table, uint numIndent) {
switch (table->type) {
case kTableName:
inprint(table->name, num_indent);
inprint(table->name, numIndent);
break;
case kTableSelect:
printSelectStatementInfo(table->select, num_indent);
printSelectStatementInfo(table->select, numIndent);
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);
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, num_indent);
for (TableRef* tbl : *table->list) printTableRefInfo(tbl, numIndent);
break;
}
if (table->alias != NULL) {
inprint("Alias", num_indent+1);
inprint(table->alias, num_indent+2);
inprint("Alias", numIndent+1);
inprint(table->alias, numIndent+2);
}
}
void printOperatorExpression(Expr* expr, uint num_indent) {
if (expr == NULL) { inprint("null", num_indent); return; }
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, 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;
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, num_indent+1);
if (expr->expr2 != NULL) printExpression(expr->expr2, num_indent+1);
printExpression(expr->expr, numIndent+1);
if (expr->expr2 != NULL) printExpression(expr->expr2, numIndent+1);
}
void printExpression(Expr* expr, uint num_indent) {
void printExpression(Expr* expr, uint numIndent) {
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;
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", num_indent+1); inprint(expr->alias, num_indent+2);
inprint("Alias", numIndent+1);
inprint(expr->alias, numIndent+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);
void printSelectStatementInfo(SelectStatement* stmt, uint numIndent) {
inprint("SelectStatement", numIndent);
inprint("Fields:", numIndent+1);
for (Expr* expr : *stmt->selectList) printExpression(expr, numIndent+2);
inprint("Sources:", num_indent+1);
printTableRefInfo(stmt->from_table, num_indent+2);
inprint("Sources:", numIndent+1);
printTableRefInfo(stmt->fromTable, numIndent+2);
if (stmt->where_clause != NULL) {
inprint("Search Conditions:", num_indent+1);
printExpression(stmt->where_clause, num_indent+2);
if (stmt->whereClause != NULL) {
inprint("Search Conditions:", numIndent+1);
printExpression(stmt->whereClause, numIndent+2);
}
if (stmt->union_select != NULL) {
inprint("Union:", num_indent+1);
printSelectStatementInfo(stmt->union_select, num_indent+2);
if (stmt->unionSelect != NULL) {
inprint("Union:", numIndent+1);
printSelectStatementInfo(stmt->unionSelect, numIndent+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);
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:", num_indent+1);
inprint(stmt->limit->limit, num_indent+2);
inprint("Limit:", numIndent+1);
inprint(stmt->limit->limit, numIndent+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 printImportStatementInfo(ImportStatement* stmt, uint numIndent) {
inprint("ImportStatment", numIndent);
inprint(stmt->filePath, numIndent+1);
inprint(stmt->tableName, numIndent+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 printCreateStatementInfo(CreateStatement* stmt, uint numIndent) {
inprint("CreateStatment", numIndent);
inprint(stmt->tableName, numIndent+1);
inprint(stmt->filePath, numIndent+1);
}
void printInsertStatementInfo(InsertStatement* stmt, uint num_indent) {
inprint("InsertStatment", num_indent);
inprint(stmt->table_name, num_indent+1);
void printInsertStatementInfo(InsertStatement* stmt, uint numIndent) {
inprint("InsertStatment", numIndent);
inprint(stmt->tableName, numIndent+1);
if (stmt->columns != NULL) {
inprint("Columns", num_indent+1);
inprint("Columns", numIndent+1);
for (char* col_name : *stmt->columns) {
inprint(col_name, num_indent+2);
inprint(col_name, numIndent+2);
}
}
switch (stmt->type) {
case InsertStatement::kInsertValues:
inprint("Values", num_indent+1);
inprint("Values", numIndent+1);
for (Expr* expr : *stmt->values) {
printExpression(expr, num_indent+2);
printExpression(expr, numIndent+2);
}
break;
case InsertStatement::kInsertSelect:
printSelectStatementInfo(stmt->select, num_indent+1);
printSelectStatementInfo(stmt->select, numIndent+1);
break;
}
}

View File

@ -1,9 +1,7 @@
#ifndef __SQLHELPER_H__
#define __SQLHELPER_H__
#include "sqltypes.h"
#include "sql/statements.h"
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

@ -9,16 +9,16 @@ using namespace hsql;
TEST(SelectTest) {
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);
ASSERT_FALSE(stmt->selectDistinct);
GroupByDescription* group = stmt->group_by;
GroupByDescription* group = stmt->groupBy;
ASSERT_NOTNULL(group);
ASSERT_EQ(group->columns->size(), 1);
ASSERT(group->having->isSimpleOp('<'));
@ -30,17 +30,17 @@ TEST(SelectHavingTest) {
TEST(SelectDistinctTest) {
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);
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

@ -7,9 +7,9 @@ class TestsManager {
// 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() {
@ -22,34 +22,38 @@ public:
int AddTest(void (*foo)(void), std::string name) {
TestsManager::tests().push_back(foo);
TestsManager::test_names().push_back(name);
TestsManager::testNames().push_back(name);
return 0;
}
void RunTests() {
size_t num_failed = 0;
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::test_names()[i].c_str());
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());
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("\033[1;31m{ failed} %s\n", TestsManager::testNames()[i].c_str());
printf("\tAssertion failed: %s\n\033[0m", e.what());
num_failed++;
numFailed++;
}
}
return numFailed;
}
int main() {
RunTests();
size_t numFailed = RunTests();
if (numFailed == 0) {
return 0;
} else {
return -1;
}
}

View File

@ -44,11 +44,6 @@ protected:
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());
} else {
fprintf(stderr, "\033[0;31m{ failed} \033[0mSome grammar tests failed! %d out of %lu tests failed!\n", num_failed, queries.size());
}
return 0;
} else {
fprintf(stderr, "\033[0;31m{ failed} \033[0mSome grammar tests failed! %d out of %lu tests failed!\n", numFailed, queries.size());
return -1;
}
}

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