diff --git a/src/parser/bison_parser.y b/src/parser/bison_parser.y index 9b180bc..edd8132 100644 --- a/src/parser/bison_parser.y +++ b/src/parser/bison_parser.y @@ -195,7 +195,8 @@ 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 operand scalar_expr unary_expr binary_expr logic_expr function_expr between_expr star_expr expr_alias placeholder_expr +%type expr operand scalar_expr unary_expr binary_expr logic_expr exists_expr +%type 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 @@ -604,6 +605,7 @@ expr: operand | between_expr | logic_expr + | exists_expr ; operand: @@ -643,6 +645,10 @@ logic_expr: | expr OR expr { $$ = Expr::makeOpBinary($1, Expr::OR, $3); } ; +exists_expr: + EXISTS '(' select_no_paren ')' { $$ = Expr::makeExists($3); } + ; + comp_expr: operand '=' operand { $$ = Expr::makeOpBinary($1, '=', $3); } | operand NOTEQUALS operand { $$ = Expr::makeOpBinary($1, Expr::NOT_EQUALS, $3); } diff --git a/src/sql/Expr.cpp b/src/sql/Expr.cpp index 9893384..bd2f265 100644 --- a/src/sql/Expr.cpp +++ b/src/sql/Expr.cpp @@ -50,6 +50,7 @@ namespace hsql { Expr* Expr::makeBetween(Expr* expr, Expr* left, Expr* right) { Expr* e = new Expr(kExprOperator); e->expr = expr; + e->opType = BETWEEN; e->exprList = new std::vector(); e->exprList->push_back(left); e->exprList->push_back(right); @@ -108,6 +109,13 @@ namespace hsql { return e; } + Expr* Expr::makeExists(SelectStatement* select) { + Expr* e = new Expr(kExprOperator); + e->opType = EXISTS; + e->select = select; + return e; + } + bool Expr::isType(ExprType e_type) { return e_type == type; } diff --git a/src/sql/Expr.h b/src/sql/Expr.h index f8ef761..d9dac70 100644 --- a/src/sql/Expr.h +++ b/src/sql/Expr.h @@ -34,6 +34,8 @@ namespace hsql { // + - * / < > = % // Non-trivial are: <> <= >= LIKE ISNULL NOT enum OperatorType { + NONE, + // Ternary operators BETWEEN, @@ -50,7 +52,8 @@ namespace hsql { // Unary operators. NOT, UMINUS, - ISNULL + ISNULL, + EXISTS }; @@ -123,6 +126,8 @@ namespace hsql { static Expr* makePlaceholder(int id); static Expr* makeSelect(SelectStatement* select); + + static Expr* makeExists(SelectStatement* select); }; // Zero initializes an Expr object and assigns it to a space in the heap diff --git a/test/select_tests.cpp b/test/select_tests.cpp index 9fcb0cc..f6a9e10 100644 --- a/test/select_tests.cpp +++ b/test/select_tests.cpp @@ -160,7 +160,7 @@ TEST(SelectBetweenTest) { TEST(SelectConditionalSelectTest) { TEST_PARSE_SINGLE_SQL( - "SELECT * FROM t WHERE a = (SELECT MIN(v) FROM tt);", + "SELECT * FROM t WHERE a = (SELECT MIN(v) FROM tt) AND EXISTS (SELECT * FROM test WHERE x < a);", kStmtSelect, SelectStatement, result, @@ -169,18 +169,31 @@ TEST(SelectConditionalSelectTest) { Expr* where = stmt->whereClause; ASSERT_NOTNULL(where); ASSERT(where->isType(kExprOperator)); - ASSERT(where->isSimpleOp('=')); + ASSERT_EQ(where->opType, Expr::AND); - ASSERT_NOTNULL(where->expr); - ASSERT_STREQ(where->expr->getName(), "a"); - ASSERT(where->expr->isType(kExprColumnRef)); + // a = (SELECT ...) + Expr* cond1 = where->expr; + ASSERT_NOTNULL(cond1); + ASSERT_NOTNULL(cond1->expr); + ASSERT(cond1->isSimpleOp('=')); + ASSERT_STREQ(cond1->expr->getName(), "a"); + ASSERT(cond1->expr->isType(kExprColumnRef)); - ASSERT_NOTNULL(where->expr2); - ASSERT(where->expr2->isType(kExprSelect)); + ASSERT_NOTNULL(cond1->expr2); + ASSERT(cond1->expr2->isType(kExprSelect)); - SelectStatement* select2 = where->expr2->select; + SelectStatement* select2 = cond1->expr2->select; ASSERT_NOTNULL(select2); ASSERT_STREQ(select2->fromTable->getName(), "tt") + + // EXISTS (SELECT ...) + Expr* cond2 = where->expr2; + ASSERT_EQ(cond2->opType, Expr::EXISTS); + ASSERT_NOTNULL(cond2->select); + + SelectStatement* ex_select = cond2->select; + ASSERT_STREQ(ex_select->fromTable->getName(), "test") + delete result; }