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..." @echo "Compiling..."
@make clean -C src/ >/dev/null || exit 1 @make clean -C src/ >/dev/null || exit 1
@make tests -C src/ >/dev/null || exit 1 @make tests -C src/ >/dev/null || exit 1

View File

@ -85,6 +85,7 @@ struct Expr {
* Convenience accessor methods * Convenience accessor methods
*/ */
inline bool isType(ExprType e_type) { return e_type == type; } 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 hasAlias() { return alias != NULL; }
inline bool hasTable() { return table != NULL; } inline bool hasTable() { return table != NULL; }
inline char* getName() { inline char* getName() {

View File

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

View File

@ -49,6 +49,23 @@ struct LimitDescription {
int64_t offset; int64_t offset;
}; };
/**
* @struct GroupByDescription
*/
struct GroupByDescription {
GroupByDescription() :
columns(NULL),
having(NULL) {}
~GroupByDescription() {
delete columns;
delete having;
}
List<Expr*>* columns;
Expr* having;
};
/** /**
* @struct SelectStatement * @struct SelectStatement
* @brief Representation of a full select statement. * @brief Representation of a full select statement.
@ -78,7 +95,7 @@ struct SelectStatement : SQLStatement {
TableRef* from_table; TableRef* from_table;
List<Expr*>* select_list; List<Expr*>* select_list;
Expr* where_clause; Expr* where_clause;
List<Expr*>* group_by; GroupByDescription* group_by;
SelectStatement* union_select; SelectStatement* union_select;
OrderDescription* order; 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::OrderType order_type;
hsql::LimitDescription* limit; hsql::LimitDescription* limit;
hsql::ColumnDefinition* column_t; hsql::ColumnDefinition* column_t;
hsql::GroupByDescription* group_t;
hsql::UpdateClause* update_t; hsql::UpdateClause* update_t;
hsql::SQLStatementList* stmt_list; 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 <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> 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> column_name literal int_literal num_literal string_literal
%type <expr> comp_expr opt_where join_condition %type <expr> comp_expr opt_where join_condition opt_having
%type <expr_list> expr_list opt_group select_list literal_list %type <expr_list> expr_list select_list literal_list
%type <table_list> table_ref_commalist %type <table_list> table_ref_commalist
%type <order> opt_order %type <order> opt_order
%type <limit> opt_limit %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 <column_list_t> column_def_commalist
%type <update_t> update_clause %type <update_t> update_clause
%type <update_list_t> update_clause_commalist %type <update_list_t> update_clause_commalist
%type <group_t> opt_group
/****************************** /******************************
** Token Precedence and Associativity ** Token Precedence and Associativity
@ -480,10 +482,17 @@ opt_where:
// TODO: having // TODO: having
opt_group: opt_group:
GROUP BY expr_list { $$ = $3; } GROUP BY expr_list opt_having {
$$ = new GroupByDescription();
$$->columns = $3;
$$->having = $4;
}
| /* empty */ { $$ = NULL; } | /* empty */ { $$ = NULL; }
; ;
opt_having:
HAVING expr { $$ = $2; }
| /* empty */ { $$ = NULL; }
opt_order: opt_order:
ORDER BY expr opt_order_type { $$ = new OrderDescription($4, $3); } 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; return lines;
} }
#define STREQ(a, b) (std::string(a).compare(std::string(b)) == 0) #define STREQ(a, b) (std::string(a).compare(std::string(b)) == 0)
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {

View File

@ -3,19 +3,12 @@
*/ */
#include "tests/test.h" #include "tests/test.h"
#include "tests/helper.h"
#include "SQLParser.h" #include "SQLParser.h"
#include "sqlhelper.h" #include "sqlhelper.h"
using namespace hsql; 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) { TEST(DeleteStatementTest) {
SQLStatementList* stmt_list = SQLParser::parseSQLString("DELETE FROM students WHERE grade > 2.0;"); SQLStatementList* stmt_list = SQLParser::parseSQLString("DELETE FROM students WHERE grade > 2.0;");
@ -105,7 +98,7 @@ TEST(DropTableStatementTest) {
TEST(PrepareStatementTest) { 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_EQ(prep_stmt->stmt->type(), kStmtSelect);
ASSERT_STREQ(prep_stmt->name, "test"); ASSERT_STREQ(prep_stmt->name, "test");
@ -120,7 +113,7 @@ TEST(PrepareStatementTest) {
TEST(ExecuteStatementTest) { 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_STREQ(stmt->name, "test");
ASSERT_EQ(stmt->parameters->size(), 2); ASSERT_EQ(stmt->parameters->size(), 2);

View File

@ -2,20 +2,14 @@
#define __HELPER_H__ #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 #define TEST_PARSE_SINGLE_SQL(query, stmt_type, stmt_class, output_var) \
if (line[0] != '#') { SQLStatementList* stmt_list = SQLParser::parseSQLString(query); \
lines.push_back(line); ASSERT(stmt_list->isValid); \
} ASSERT_EQ(stmt_list->size(), 1); \
} ASSERT_EQ(stmt_list->at(0)->type(), stmt_type); \
return lines; stmt_class* output_var = (stmt_class*) stmt_list->at(0);
}
#endif #endif

View File

@ -1,15 +1,39 @@
#include "test.h" #include "test.h"
#include "helper.h"
#include "SQLParser.h" #include "SQLParser.h"
using namespace hsql; using namespace hsql;
TEST(Select) { TEST(SelectTest) {
SQLStatementList* stmt_list = SQLParser::parseSQLString("SELECT * FROM students;"); TEST_PARSE_SINGLE_SQL("SELECT * FROM students;", kStmtSelect, SelectStatement, stmt);
ASSERT(stmt_list->isValid);
ASSERT_EQ(stmt_list->size(), 1);
ASSERT_EQ(stmt_list->at(0)->type(), kStmtSelect);
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); 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 # 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 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 * FROM t1 JOIN t2 ON c1 = c2;
SELECT a, SUM(b) FROM t2 GROUP BY a HAVING SUM(b) > 100;
# CREATE statement # CREATE statement
CREATE TABLE "table" FROM TBL FILE 'students.tbl' CREATE TABLE "table" FROM TBL FILE 'students.tbl'
CREATE TABLE IF NOT EXISTS "table" FROM TBL FILE 'students.tbl' CREATE TABLE IF NOT EXISTS "table" FROM TBL FILE 'students.tbl'