From cf1c84d46d8e4ec786df5b8d51a81644c907072f Mon Sep 17 00:00:00 2001 From: Pedro Date: Tue, 7 Mar 2017 14:21:45 +0100 Subject: [PATCH] Add select statement as possible operand in expressions --- src/parser/bison_parser.y | 11 ++++++----- src/sql/Expr.cpp | 6 ++++++ src/sql/Expr.h | 8 +++++++- test/select_tests.cpp | 27 +++++++++++++++++++++++++++ 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/parser/bison_parser.y b/src/parser/bison_parser.y index a176f40..9b180bc 100644 --- a/src/parser/bison_parser.y +++ b/src/parser/bison_parser.y @@ -612,6 +612,7 @@ operand: | unary_expr | binary_expr | function_expr + | '(' select_no_paren ')' { $$ = Expr::makeSelect($2); } ; scalar_expr: @@ -633,7 +634,7 @@ binary_expr: | 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 LIKE operand { $$ = Expr::makeOpBinary($1, Expr::LIKE, $3); } | operand NOT LIKE operand { $$ = Expr::makeOpBinary($1, Expr::NOT_LIKE, $4); } ; @@ -643,11 +644,11 @@ logic_expr: ; comp_expr: - operand '=' operand { $$ = Expr::makeOpBinary($1, '=', $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 '<' 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); } ; diff --git a/src/sql/Expr.cpp b/src/sql/Expr.cpp index cb89d67..9893384 100644 --- a/src/sql/Expr.cpp +++ b/src/sql/Expr.cpp @@ -102,6 +102,12 @@ namespace hsql { return e; } + Expr* Expr::makeSelect(SelectStatement* select) { + Expr* e = new Expr(kExprSelect); + 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 eef1bf8..f8ef761 100644 --- a/src/sql/Expr.h +++ b/src/sql/Expr.h @@ -6,6 +6,7 @@ #include namespace hsql { + class SelectStatement; // Helper function used by the lexer. // TODO: move to more appropriate place. @@ -19,7 +20,8 @@ namespace hsql { kExprPlaceholder, kExprColumnRef, kExprFunctionRef, - kExprOperator + kExprOperator, + kExprSelect }; typedef struct Expr Expr; @@ -66,6 +68,7 @@ namespace hsql { Expr* expr; Expr* expr2; std::vector* exprList; + SelectStatement* select; char* name; char* table; char* alias; @@ -77,6 +80,7 @@ namespace hsql { char opChar; bool distinct; + // Convenience accessor methods. bool isType(ExprType e_type); @@ -117,6 +121,8 @@ namespace hsql { static Expr* makeFunctionRef(char* func_name, std::vector* exprList, bool distinct); static Expr* makePlaceholder(int id); + + static Expr* makeSelect(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 c9cc78c..9fcb0cc 100644 --- a/test/select_tests.cpp +++ b/test/select_tests.cpp @@ -157,3 +157,30 @@ TEST(SelectBetweenTest) { delete result; } + +TEST(SelectConditionalSelectTest) { + TEST_PARSE_SINGLE_SQL( + "SELECT * FROM t WHERE a = (SELECT MIN(v) FROM tt);", + kStmtSelect, + SelectStatement, + result, + stmt); + + Expr* where = stmt->whereClause; + ASSERT_NOTNULL(where); + ASSERT(where->isType(kExprOperator)); + ASSERT(where->isSimpleOp('=')); + + ASSERT_NOTNULL(where->expr); + ASSERT_STREQ(where->expr->getName(), "a"); + ASSERT(where->expr->isType(kExprColumnRef)); + + ASSERT_NOTNULL(where->expr2); + ASSERT(where->expr2->isType(kExprSelect)); + + SelectStatement* select2 = where->expr2->select; + ASSERT_NOTNULL(select2); + ASSERT_STREQ(select2->fromTable->getName(), "tt") + + delete result; +}