diff options
author | Timothy Flynn <trflynn89@pm.me> | 2021-04-23 14:54:37 -0400 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-04-23 22:36:07 +0200 |
commit | fa59d02692426d2122a2b197ce17ccd01d725299 (patch) | |
tree | cd9b83a68cb6126dc6180d124120d83d3fcbfed0 | |
parent | 004025c3c4bfb3e9998e5c860700e357d70b41a4 (diff) | |
download | serenity-fa59d02692426d2122a2b197ce17ccd01d725299.zip |
LibSQL: Parse IN / NOT IN expressions with a nested SELECT statement
-rw-r--r-- | Userland/Libraries/LibSQL/AST.h | 14 | ||||
-rw-r--r-- | Userland/Libraries/LibSQL/Parser.cpp | 4 | ||||
-rw-r--r-- | Userland/Libraries/LibSQL/Tests/TestSqlExpressionParser.cpp | 23 |
3 files changed, 39 insertions, 2 deletions
diff --git a/Userland/Libraries/LibSQL/AST.h b/Userland/Libraries/LibSQL/AST.h index 1c7a90eb26..44c5134ba3 100644 --- a/Userland/Libraries/LibSQL/AST.h +++ b/Userland/Libraries/LibSQL/AST.h @@ -624,6 +624,20 @@ private: NonnullRefPtr<Expression> m_expression; }; +class InSelectionExpression : public InvertibleNestedExpression { +public: + InSelectionExpression(NonnullRefPtr<Expression> expression, NonnullRefPtr<Select> select_statement, bool invert_expression) + : InvertibleNestedExpression(move(expression), invert_expression) + , m_select_statement(move(select_statement)) + { + } + + const NonnullRefPtr<Select>& select_statement() const { return m_select_statement; } + +private: + NonnullRefPtr<Select> m_select_statement; +}; + class InChainedExpression : public InvertibleNestedExpression { public: InChainedExpression(NonnullRefPtr<Expression> expression, NonnullRefPtr<ChainedExpression> expression_chain, bool invert_expression) diff --git a/Userland/Libraries/LibSQL/Parser.cpp b/Userland/Libraries/LibSQL/Parser.cpp index cd9e46afdb..3ac2527b6f 100644 --- a/Userland/Libraries/LibSQL/Parser.cpp +++ b/Userland/Libraries/LibSQL/Parser.cpp @@ -628,8 +628,8 @@ Optional<NonnullRefPtr<Expression>> Parser::parse_in_expression(NonnullRefPtr<Ex if (consume_if(TokenType::ParenOpen)) { if (match(TokenType::Select)) { - // FIXME: Parse "select-stmt". - return {}; + auto select_statement = parse_select_statement({}); + return create_ast_node<InSelectionExpression>(move(expression), move(select_statement), invert_expression); } // FIXME: Consolidate this with parse_chained_expression(). That method consumes the opening paren as diff --git a/Userland/Libraries/LibSQL/Tests/TestSqlExpressionParser.cpp b/Userland/Libraries/LibSQL/Tests/TestSqlExpressionParser.cpp index 1e7a2ef187..a3d2e6d32f 100644 --- a/Userland/Libraries/LibSQL/Tests/TestSqlExpressionParser.cpp +++ b/Userland/Libraries/LibSQL/Tests/TestSqlExpressionParser.cpp @@ -580,4 +580,27 @@ TEST_CASE(in_chained_expression) validate("15 NOT IN (15, 16)", 2, true); } +TEST_CASE(in_selection_expression) +{ + EXPECT(parse("IN (SELECT)").is_error()); + EXPECT(parse("IN (SELECT * FROM table, SELECT * FROM table);").is_error()); + EXPECT(parse("NOT IN (SELECT)").is_error()); + EXPECT(parse("NOT IN (SELECT * FROM table, SELECT * FROM table);").is_error()); + + auto validate = [](StringView sql, bool expected_invert_expression) { + auto result = parse(sql); + EXPECT(!result.is_error()); + + auto expression = result.release_value(); + EXPECT(is<SQL::InSelectionExpression>(*expression)); + + const auto& in = static_cast<const SQL::InSelectionExpression&>(*expression); + EXPECT(!is<SQL::ErrorExpression>(*in.expression())); + EXPECT_EQ(in.invert_expression(), expected_invert_expression); + }; + + validate("15 IN (SELECT * FROM table)", false); + validate("15 NOT IN (SELECT * FROM table)", true); +} + TEST_MAIN(SqlExpressionParser) |