added having
This commit is contained in:
parent
38e480ec0e
commit
3c98fe1bae
2
Makefile
2
Makefile
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
#include "Table.h"
|
||||
#include "SelectStatement.h"
|
||||
|
||||
namespace hsql {
|
||||
|
||||
|
||||
TableRef::~TableRef() {
|
||||
delete name;
|
||||
delete alias;
|
||||
delete select;
|
||||
delete list;
|
||||
}
|
||||
|
||||
|
||||
} // namespace hsql
|
|
@ -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); }
|
||||
|
|
|
@ -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[]) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Reference in New Issue