From 35b8b569eb61401dae38929b89b711d07fb43ff1 Mon Sep 17 00:00:00 2001 From: Pedro Date: Tue, 7 Mar 2017 13:49:56 +0100 Subject: [PATCH] Implement BETWEEN operator --- src/parser/bison_parser.y | 50 ++++++++++++++++++++++++--------------- src/sql/Expr.cpp | 9 +++++++ src/sql/Expr.h | 7 +++++- test/select_tests.cpp | 29 +++++++++++++++++++++-- test/tpc_h_tests.cpp | 4 ++-- test/valid_queries.sql | 1 + 6 files changed, 76 insertions(+), 24 deletions(-) diff --git a/src/parser/bison_parser.y b/src/parser/bison_parser.y index 27c12c0..a176f40 100644 --- a/src/parser/bison_parser.y +++ b/src/parser/bison_parser.y @@ -195,7 +195,7 @@ int yyerror(YYLTYPE* llocp, SQLParserResult** result, yyscan_t scanner, const ch %type import_file_type opt_join_type column_type %type from_clause table_ref table_ref_atomic table_ref_name %type
join_clause join_table table_ref_name_no_alias -%type expr scalar_expr unary_expr binary_expr function_expr star_expr expr_alias placeholder_expr +%type expr operand scalar_expr unary_expr binary_expr logic_expr function_expr between_expr star_expr expr_alias placeholder_expr %type column_name literal int_literal num_literal string_literal %type comp_expr opt_where join_condition opt_having %type opt_limit opt_top @@ -601,6 +601,12 @@ expr_alias: ; expr: + operand + | between_expr + | logic_expr + ; + +operand: '(' expr ')' { $$ = $2; } | scalar_expr | unary_expr @@ -615,38 +621,44 @@ scalar_expr: ; unary_expr: - '-' expr { $$ = Expr::makeOpUnary(Expr::UMINUS, $2); } - | NOT expr { $$ = Expr::makeOpUnary(Expr::NOT, $2); } + '-' operand { $$ = Expr::makeOpUnary(Expr::UMINUS, $2); } + | NOT operand { $$ = Expr::makeOpUnary(Expr::NOT, $2); } ; binary_expr: comp_expr - | expr '-' expr { $$ = Expr::makeOpBinary($1, '-', $3); } - | expr '+' expr { $$ = Expr::makeOpBinary($1, '+', $3); } - | expr '/' expr { $$ = Expr::makeOpBinary($1, '/', $3); } - | expr '*' expr { $$ = Expr::makeOpBinary($1, '*', $3); } - | expr '%' expr { $$ = Expr::makeOpBinary($1, '%', $3); } - | expr '^' expr { $$ = Expr::makeOpBinary($1, '^', $3); } - | expr AND expr { $$ = Expr::makeOpBinary($1, Expr::AND, $3); } - | expr OR expr { $$ = Expr::makeOpBinary($1, Expr::OR, $3); } - | expr LIKE expr { $$ = Expr::makeOpBinary($1, Expr::LIKE, $3); } - | expr NOT LIKE expr { $$ = Expr::makeOpBinary($1, Expr::NOT_LIKE, $4); } + | operand '-' operand { $$ = Expr::makeOpBinary($1, '-', $3); } + | operand '+' operand { $$ = Expr::makeOpBinary($1, '+', $3); } + | operand '/' operand { $$ = Expr::makeOpBinary($1, '/', $3); } + | operand '*' operand { $$ = Expr::makeOpBinary($1, '*', $3); } + | operand '%' operand { $$ = Expr::makeOpBinary($1, '%', $3); } + | operand '^' operand { $$ = Expr::makeOpBinary($1, '^', $3); } + | operand LIKE operand { $$ = Expr::makeOpBinary($1, Expr::LIKE, $3); } + | operand NOT LIKE operand { $$ = 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: - expr '=' expr { $$ = Expr::makeOpBinary($1, '=', $3); } - | expr NOTEQUALS expr { $$ = Expr::makeOpBinary($1, Expr::NOT_EQUALS, $3); } - | expr '<' expr { $$ = Expr::makeOpBinary($1, '<', $3); } - | expr '>' expr { $$ = Expr::makeOpBinary($1, '>', $3); } - | expr LESSEQ expr { $$ = Expr::makeOpBinary($1, Expr::LESS_EQ, $3); } - | expr GREATEREQ expr { $$ = Expr::makeOpBinary($1, Expr::GREATER_EQ, $3); } + operand '=' operand { $$ = Expr::makeOpBinary($1, '=', $3); } + | operand NOTEQUALS operand { $$ = Expr::makeOpBinary($1, Expr::NOT_EQUALS, $3); } + | operand '<' operand { $$ = Expr::makeOpBinary($1, '<', $3); } + | operand '>' operand { $$ = Expr::makeOpBinary($1, '>', $3); } + | operand LESSEQ operand { $$ = Expr::makeOpBinary($1, Expr::LESS_EQ, $3); } + | operand GREATEREQ operand { $$ = Expr::makeOpBinary($1, Expr::GREATER_EQ, $3); } ; function_expr: IDENTIFIER '(' opt_distinct expr_list ')' { $$ = Expr::makeFunctionRef($1, $4, $3); } ; +between_expr: + operand BETWEEN operand AND operand { $$ = Expr::makeBetween($1, $3, $5); } + ; + column_name: IDENTIFIER { $$ = Expr::makeColumnRef($1); } | IDENTIFIER '.' IDENTIFIER { $$ = Expr::makeColumnRef($1, $3); } diff --git a/src/sql/Expr.cpp b/src/sql/Expr.cpp index 9035884..cb89d67 100644 --- a/src/sql/Expr.cpp +++ b/src/sql/Expr.cpp @@ -47,6 +47,15 @@ namespace hsql { return e; } + Expr* Expr::makeBetween(Expr* expr, Expr* left, Expr* right) { + Expr* e = new Expr(kExprOperator); + e->expr = expr; + e->exprList = new std::vector(); + e->exprList->push_back(left); + e->exprList->push_back(right); + return e; + } + Expr* Expr::makeLiteral(int64_t val) { Expr* e = new Expr(kExprLiteralInt); e->ival = val; diff --git a/src/sql/Expr.h b/src/sql/Expr.h index bd2b8d0..eef1bf8 100644 --- a/src/sql/Expr.h +++ b/src/sql/Expr.h @@ -32,9 +32,11 @@ namespace hsql { // + - * / < > = % // Non-trivial are: <> <= >= LIKE ISNULL NOT enum OperatorType { - SIMPLE_OP, + // Ternary operators + BETWEEN, // Binary operators. + SIMPLE_OP, NOT_EQUALS, LESS_EQ, GREATER_EQ, @@ -60,6 +62,7 @@ namespace hsql { ExprType type; + // TODO: Replace expressions by list. Expr* expr; Expr* expr2; std::vector* exprList; @@ -99,6 +102,8 @@ namespace hsql { 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(double val); diff --git a/test/select_tests.cpp b/test/select_tests.cpp index 62e5d55..c9cc78c 100644 --- a/test/select_tests.cpp +++ b/test/select_tests.cpp @@ -32,7 +32,7 @@ TEST(SelectExprTest) { ASSERT_NULL(stmt->groupBy); ASSERT_EQ(stmt->selectList->size(), 3); - + ASSERT(stmt->selectList->at(0)->isType(kExprColumnRef)); ASSERT_STREQ(stmt->selectList->at(0)->getName(), "a"); @@ -111,7 +111,6 @@ TEST(SelectGroupDistinctTest) { delete result; } - TEST(OrderByTest) { TEST_PARSE_SINGLE_SQL( "SELECT grade, city FROM students ORDER BY grade, city DESC;", @@ -132,3 +131,29 @@ TEST(OrderByTest) { 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; +} diff --git a/test/tpc_h_tests.cpp b/test/tpc_h_tests.cpp index f85f644..93c5659 100644 --- a/test/tpc_h_tests.cpp +++ b/test/tpc_h_tests.cpp @@ -28,8 +28,8 @@ TEST(TPCHQueryTests) { for (const std::string& file_path : files) { std::ifstream t(file_path.c_str()); std::string query((std::istreambuf_iterator(t)), - std::istreambuf_iterator()); - + std::istreambuf_iterator()); + SQLParserResult* result = SQLParser::parseSQLString(query.c_str()); if (!result->isValid()) { mt::printFailed(file_path.c_str()); diff --git a/test/valid_queries.sql b/test/valid_queries.sql index cbd9584..9e3d095 100644 --- a/test/valid_queries.sql +++ b/test/valid_queries.sql @@ -10,6 +10,7 @@ SELECT * FROM (SELECT * FROM t1); SELECT * FROM t1 UNION (SELECT * FROM t2 UNION SELECT * FROM t3) ORDER BY col1; 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 * FROM t WHERE a BETWEEN 1 and c; # 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;