Implement BETWEEN operator

This commit is contained in:
Pedro 2017-03-07 13:49:56 +01:00
parent 9ecfa8e8f6
commit 35b8b569eb
6 changed files with 76 additions and 24 deletions

View File

@ -195,7 +195,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult** result, yyscan_t scanner, const ch
%type <uval> import_file_type opt_join_type column_type %type <uval> import_file_type opt_join_type column_type
%type <table> from_clause table_ref table_ref_atomic table_ref_name %type <table> from_clause table_ref table_ref_atomic table_ref_name
%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 operand scalar_expr unary_expr binary_expr logic_expr function_expr between_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 opt_having %type <expr> comp_expr opt_where join_condition opt_having
%type <limit> opt_limit opt_top %type <limit> opt_limit opt_top
@ -601,6 +601,12 @@ expr_alias:
; ;
expr: expr:
operand
| between_expr
| logic_expr
;
operand:
'(' expr ')' { $$ = $2; } '(' expr ')' { $$ = $2; }
| scalar_expr | scalar_expr
| unary_expr | unary_expr
@ -615,38 +621,44 @@ scalar_expr:
; ;
unary_expr: unary_expr:
'-' expr { $$ = Expr::makeOpUnary(Expr::UMINUS, $2); } '-' operand { $$ = Expr::makeOpUnary(Expr::UMINUS, $2); }
| NOT expr { $$ = Expr::makeOpUnary(Expr::NOT, $2); } | NOT operand { $$ = Expr::makeOpUnary(Expr::NOT, $2); }
; ;
binary_expr: binary_expr:
comp_expr comp_expr
| expr '-' expr { $$ = Expr::makeOpBinary($1, '-', $3); } | operand '-' operand { $$ = Expr::makeOpBinary($1, '-', $3); }
| expr '+' expr { $$ = Expr::makeOpBinary($1, '+', $3); } | operand '+' operand { $$ = Expr::makeOpBinary($1, '+', $3); }
| expr '/' expr { $$ = Expr::makeOpBinary($1, '/', $3); } | operand '/' operand { $$ = Expr::makeOpBinary($1, '/', $3); }
| expr '*' expr { $$ = Expr::makeOpBinary($1, '*', $3); } | operand '*' operand { $$ = Expr::makeOpBinary($1, '*', $3); }
| expr '%' expr { $$ = Expr::makeOpBinary($1, '%', $3); } | operand '%' operand { $$ = Expr::makeOpBinary($1, '%', $3); }
| expr '^' expr { $$ = Expr::makeOpBinary($1, '^', $3); } | operand '^' operand { $$ = Expr::makeOpBinary($1, '^', $3); }
| expr AND expr { $$ = Expr::makeOpBinary($1, Expr::AND, $3); } | operand LIKE operand { $$ = Expr::makeOpBinary($1, Expr::LIKE, $3); }
| expr OR expr { $$ = Expr::makeOpBinary($1, Expr::OR, $3); } | operand NOT LIKE operand { $$ = Expr::makeOpBinary($1, Expr::NOT_LIKE, $4); }
| expr LIKE expr { $$ = Expr::makeOpBinary($1, Expr::LIKE, $3); }
| expr NOT LIKE expr { $$ = Expr::makeOpBinary($1, Expr::NOT_LIKE, $4); }
; ;
logic_expr:
expr AND expr { $$ = Expr::makeOpBinary($1, Expr::AND, $3); }
| expr OR expr { $$ = Expr::makeOpBinary($1, Expr::OR, $3); }
;
comp_expr: comp_expr:
expr '=' expr { $$ = Expr::makeOpBinary($1, '=', $3); } operand '=' operand { $$ = Expr::makeOpBinary($1, '=', $3); }
| expr NOTEQUALS expr { $$ = Expr::makeOpBinary($1, Expr::NOT_EQUALS, $3); } | operand NOTEQUALS operand { $$ = Expr::makeOpBinary($1, Expr::NOT_EQUALS, $3); }
| expr '<' expr { $$ = Expr::makeOpBinary($1, '<', $3); } | operand '<' operand { $$ = Expr::makeOpBinary($1, '<', $3); }
| expr '>' expr { $$ = Expr::makeOpBinary($1, '>', $3); } | operand '>' operand { $$ = Expr::makeOpBinary($1, '>', $3); }
| expr LESSEQ expr { $$ = Expr::makeOpBinary($1, Expr::LESS_EQ, $3); } | operand LESSEQ operand { $$ = Expr::makeOpBinary($1, Expr::LESS_EQ, $3); }
| expr GREATEREQ expr { $$ = Expr::makeOpBinary($1, Expr::GREATER_EQ, $3); } | operand GREATEREQ operand { $$ = Expr::makeOpBinary($1, Expr::GREATER_EQ, $3); }
; ;
function_expr: function_expr:
IDENTIFIER '(' opt_distinct expr_list ')' { $$ = Expr::makeFunctionRef($1, $4, $3); } IDENTIFIER '(' opt_distinct expr_list ')' { $$ = Expr::makeFunctionRef($1, $4, $3); }
; ;
between_expr:
operand BETWEEN operand AND operand { $$ = Expr::makeBetween($1, $3, $5); }
;
column_name: column_name:
IDENTIFIER { $$ = Expr::makeColumnRef($1); } IDENTIFIER { $$ = Expr::makeColumnRef($1); }
| IDENTIFIER '.' IDENTIFIER { $$ = Expr::makeColumnRef($1, $3); } | IDENTIFIER '.' IDENTIFIER { $$ = Expr::makeColumnRef($1, $3); }

View File

@ -47,6 +47,15 @@ namespace hsql {
return e; return e;
} }
Expr* Expr::makeBetween(Expr* expr, Expr* left, Expr* right) {
Expr* e = new Expr(kExprOperator);
e->expr = expr;
e->exprList = new std::vector<Expr*>();
e->exprList->push_back(left);
e->exprList->push_back(right);
return e;
}
Expr* Expr::makeLiteral(int64_t val) { Expr* Expr::makeLiteral(int64_t val) {
Expr* e = new Expr(kExprLiteralInt); Expr* e = new Expr(kExprLiteralInt);
e->ival = val; e->ival = val;

View File

@ -32,9 +32,11 @@ namespace hsql {
// + - * / < > = % // + - * / < > = %
// Non-trivial are: <> <= >= LIKE ISNULL NOT // Non-trivial are: <> <= >= LIKE ISNULL NOT
enum OperatorType { enum OperatorType {
SIMPLE_OP, // Ternary operators
BETWEEN,
// Binary operators. // Binary operators.
SIMPLE_OP,
NOT_EQUALS, NOT_EQUALS,
LESS_EQ, LESS_EQ,
GREATER_EQ, GREATER_EQ,
@ -60,6 +62,7 @@ namespace hsql {
ExprType type; ExprType type;
// TODO: Replace expressions by list.
Expr* expr; Expr* expr;
Expr* expr2; Expr* expr2;
std::vector<Expr*>* exprList; std::vector<Expr*>* exprList;
@ -99,6 +102,8 @@ namespace hsql {
static Expr* makeOpBinary(Expr* expr1, OperatorType op, Expr* expr2); static Expr* makeOpBinary(Expr* expr1, OperatorType op, Expr* expr2);
static Expr* makeBetween(Expr* expr, Expr* left, Expr* right);
static Expr* makeLiteral(int64_t val); static Expr* makeLiteral(int64_t val);
static Expr* makeLiteral(double val); static Expr* makeLiteral(double val);

View File

@ -111,7 +111,6 @@ TEST(SelectGroupDistinctTest) {
delete result; delete result;
} }
TEST(OrderByTest) { TEST(OrderByTest) {
TEST_PARSE_SINGLE_SQL( TEST_PARSE_SINGLE_SQL(
"SELECT grade, city FROM students ORDER BY grade, city DESC;", "SELECT grade, city FROM students ORDER BY grade, city DESC;",
@ -132,3 +131,29 @@ TEST(OrderByTest) {
delete result; delete result;
} }
TEST(SelectBetweenTest) {
TEST_PARSE_SINGLE_SQL(
"SELECT grade, city FROM students WHERE grade BETWEEN 1 and c;",
kStmtSelect,
SelectStatement,
result,
stmt);
Expr* where = stmt->whereClause;
ASSERT_NOTNULL(where);
ASSERT(where->isType(kExprOperator));
ASSERT_EQ(where->opType, Expr::BETWEEN);
ASSERT_STREQ(where->expr->getName(), "grade");
ASSERT(where->expr->isType(kExprColumnRef));
ASSERT_EQ(where->exprList->size(), 2);
ASSERT(where->exprList->at(0)->isType(kExprLiteralInt));
ASSERT_EQ(where->exprList->at(0)->ival, 1);
ASSERT(where->exprList->at(1)->isType(kExprColumnRef));
ASSERT_STREQ(where->exprList->at(1)->getName(), "c");
delete result;
}

View File

@ -28,7 +28,7 @@ TEST(TPCHQueryTests) {
for (const std::string& file_path : files) { for (const std::string& file_path : files) {
std::ifstream t(file_path.c_str()); std::ifstream t(file_path.c_str());
std::string query((std::istreambuf_iterator<char>(t)), std::string query((std::istreambuf_iterator<char>(t)),
std::istreambuf_iterator<char>()); std::istreambuf_iterator<char>());
SQLParserResult* result = SQLParser::parseSQLString(query.c_str()); SQLParserResult* result = SQLParser::parseSQLString(query.c_str());
if (!result->isValid()) { if (!result->isValid()) {

View File

@ -10,6 +10,7 @@ SELECT * FROM (SELECT * FROM t1);
SELECT * FROM t1 UNION (SELECT * FROM t2 UNION SELECT * FROM t3) ORDER BY col1; SELECT * FROM t1 UNION (SELECT * FROM t2 UNION SELECT * FROM t3) ORDER BY col1;
SELECT TOP 10 * FROM t1 ORDER BY col1, col2; SELECT TOP 10 * FROM t1 ORDER BY col1, col2;
SELECT a, MAX(b), MAX(c, d), CUSTOM(q, UP(r)) AS f FROM t1; SELECT a, MAX(b), MAX(c, d), CUSTOM(q, UP(r)) AS f FROM t1;
SELECT * FROM t WHERE a BETWEEN 1 and c;
# 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;