added having
This commit is contained in:
parent
38e480ec0e
commit
3c98fe1bae
2
Makefile
2
Makefile
|
@ -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
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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::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); }
|
||||||
|
|
|
@ -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[]) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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'
|
||||||
|
|
Loading…
Reference in New Issue