added having

This commit is contained in:
Pedro 2014-12-18 12:11:26 +01:00
parent 38e480ec0e
commit 3c98fe1bae
11 changed files with 93 additions and 42 deletions

View File

@ -1,6 +1,6 @@
test:
test: FORCE
@echo "Compiling..."
@make clean -C src/ >/dev/null || exit 1
@make tests -C src/ >/dev/null || exit 1
@ -14,4 +14,4 @@ docs: FORCE
doxygen docs/doxy.conf
FORCE:
FORCE:

View File

@ -85,6 +85,7 @@ 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 char* getName() {

View File

@ -1,6 +1,8 @@
#ifndef __TABLEREF_H__
#define __TABLEREF_H__
#include "List.h"
#include "Expr.h"
#include <stdio.h>
namespace hsql {
@ -36,12 +38,7 @@ struct TableRef {
list(NULL),
join(NULL) {}
virtual ~TableRef() {
delete name;
delete alias;
delete select;
delete list;
}
virtual ~TableRef();
TableRefType type;

View File

@ -49,6 +49,23 @@ struct LimitDescription {
int64_t offset;
};
/**
* @struct GroupByDescription
*/
struct GroupByDescription {
GroupByDescription() :
columns(NULL),
having(NULL) {}
~GroupByDescription() {
delete columns;
delete having;
}
List<Expr*>* columns;
Expr* having;
};
/**
* @struct SelectStatement
* @brief Representation of a full select statement.
@ -78,7 +95,7 @@ struct SelectStatement : SQLStatement {
TableRef* from_table;
List<Expr*>* select_list;
Expr* where_clause;
List<Expr*>* group_by;
GroupByDescription* group_by;
SelectStatement* union_select;
OrderDescription* order;

View File

@ -0,0 +1,16 @@
#include "Table.h"
#include "SelectStatement.h"
namespace hsql {
TableRef::~TableRef() {
delete name;
delete alias;
delete select;
delete list;
}
} // namespace hsql

View File

@ -118,6 +118,7 @@ int yyerror(YYLTYPE* llocp, SQLStatementList** result, yyscan_t scanner, const c
hsql::OrderType order_type;
hsql::LimitDescription* limit;
hsql::ColumnDefinition* column_t;
hsql::GroupByDescription* group_t;
hsql::UpdateClause* update_t;
hsql::SQLStatementList* stmt_list;
@ -176,8 +177,8 @@ int yyerror(YYLTYPE* llocp, SQLStatementList** result, yyscan_t scanner, const c
%type <table> join_clause join_table table_ref_name_no_alias
%type <expr> expr scalar_expr unary_expr binary_expr function_expr star_expr expr_alias placeholder_expr
%type <expr> column_name literal int_literal num_literal string_literal
%type <expr> comp_expr opt_where join_condition
%type <expr_list> expr_list opt_group select_list literal_list
%type <expr> comp_expr opt_where join_condition opt_having
%type <expr_list> expr_list select_list literal_list
%type <table_list> table_ref_commalist
%type <order> opt_order
%type <limit> opt_limit
@ -187,6 +188,7 @@ int yyerror(YYLTYPE* llocp, SQLStatementList** result, yyscan_t scanner, const c
%type <column_list_t> column_def_commalist
%type <update_t> update_clause
%type <update_list_t> update_clause_commalist
%type <group_t> opt_group
/******************************
** Token Precedence and Associativity
@ -480,10 +482,17 @@ opt_where:
// TODO: having
opt_group:
GROUP BY expr_list { $$ = $3; }
GROUP BY expr_list opt_having {
$$ = new GroupByDescription();
$$->columns = $3;
$$->having = $4;
}
| /* empty */ { $$ = NULL; }
;
opt_having:
HAVING expr { $$ = $2; }
| /* empty */ { $$ = NULL; }
opt_order:
ORDER BY expr opt_order_type { $$ = new OrderDescription($4, $3); }

View File

@ -25,7 +25,6 @@ std::vector<std::string> readlines(std::string path) {
return lines;
}
#define STREQ(a, b) (std::string(a).compare(std::string(b)) == 0)
int main(int argc, char *argv[]) {

View File

@ -3,19 +3,12 @@
*/
#include "tests/test.h"
#include "tests/helper.h"
#include "SQLParser.h"
#include "sqlhelper.h"
using namespace hsql;
#define PARSE_SINGLE_SQL(query, stmt_type, stmt_class, output_var) \
SQLStatementList* stmt_list = SQLParser::parseSQLString(query); \
ASSERT(stmt_list->isValid); \
ASSERT_EQ(stmt_list->size(), 1); \
ASSERT_EQ(stmt_list->at(0)->type(), stmt_type); \
stmt_class* output_var = (stmt_class*) stmt_list->at(0);
TEST(DeleteStatementTest) {
SQLStatementList* stmt_list = SQLParser::parseSQLString("DELETE FROM students WHERE grade > 2.0;");
@ -105,7 +98,7 @@ TEST(DropTableStatementTest) {
TEST(PrepareStatementTest) {
PARSE_SINGLE_SQL("PREPARE test: SELECT ?, test FROM t2 WHERE c1 = ?;", kStmtPrepare, PrepareStatement, prep_stmt);
TEST_PARSE_SINGLE_SQL("PREPARE test: SELECT ?, test FROM t2 WHERE c1 = ?;", kStmtPrepare, PrepareStatement, prep_stmt);
ASSERT_EQ(prep_stmt->stmt->type(), kStmtSelect);
ASSERT_STREQ(prep_stmt->name, "test");
@ -120,7 +113,7 @@ TEST(PrepareStatementTest) {
TEST(ExecuteStatementTest) {
PARSE_SINGLE_SQL("EXECUTE test(1, 2);", kStmtExecute, ExecuteStatement, stmt);
TEST_PARSE_SINGLE_SQL("EXECUTE test(1, 2);", kStmtExecute, ExecuteStatement, stmt);
ASSERT_STREQ(stmt->name, "test");
ASSERT_EQ(stmt->parameters->size(), 2);

View File

@ -2,20 +2,14 @@
#define __HELPER_H__
std::vector<std::string> readlines(std::string path) {
std::ifstream infile(path);
std::vector<std::string> lines;
std::string line;
while (std::getline(infile, line)) {
std::istringstream iss(line);
// Skip comments
if (line[0] != '#') {
lines.push_back(line);
}
}
return lines;
}
#define TEST_PARSE_SINGLE_SQL(query, stmt_type, stmt_class, output_var) \
SQLStatementList* stmt_list = SQLParser::parseSQLString(query); \
ASSERT(stmt_list->isValid); \
ASSERT_EQ(stmt_list->size(), 1); \
ASSERT_EQ(stmt_list->at(0)->type(), stmt_type); \
stmt_class* output_var = (stmt_class*) stmt_list->at(0);
#endif

View File

@ -1,15 +1,39 @@
#include "test.h"
#include "helper.h"
#include "SQLParser.h"
using namespace hsql;
TEST(Select) {
SQLStatementList* stmt_list = SQLParser::parseSQLString("SELECT * FROM students;");
ASSERT(stmt_list->isValid);
ASSERT_EQ(stmt_list->size(), 1);
ASSERT_EQ(stmt_list->at(0)->type(), kStmtSelect);
TEST(SelectTest) {
TEST_PARSE_SINGLE_SQL("SELECT * FROM students;", kStmtSelect, SelectStatement, stmt);
SelectStatement* stmt = (SelectStatement*) stmt_list->at(0);
ASSERT_NULL(stmt->where_clause);
ASSERT_NULL(stmt->group_by);
}
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);
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));
}
TEST(SelectDistinctTest) {
TEST_PARSE_SINGLE_SQL("SELECT DISTINCT grade, city FROM students;", kStmtSelect, SelectStatement, stmt);
ASSERT_NULL(stmt->where_clause);
}
TEST(SelectGroupDistinctTest) {
TEST_PARSE_SINGLE_SQL("SELECT city, COUNT(DISTINCT name), SUM(DISTINCT grade) FROM students GROUP BY city;", kStmtSelect, SelectStatement, stmt);
ASSERT_NULL(stmt->where_clause);
}

View File

@ -10,6 +10,7 @@ SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY col1;
# JOIN
SELECT t1.a, t1.b, t2.c FROM "table" AS t1 JOIN (SELECT * FROM foo JOIN bar ON foo.id = bar.id) t2 ON t1.a = t2.b WHERE (t1.b OR NOT t1.a) AND t2.c = 12.5
SELECT * FROM t1 JOIN t2 ON c1 = c2;
SELECT a, SUM(b) FROM t2 GROUP BY a HAVING SUM(b) > 100;
# CREATE statement
CREATE TABLE "table" FROM TBL FILE 'students.tbl'
CREATE TABLE IF NOT EXISTS "table" FROM TBL FILE 'students.tbl'