summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorTimothy Flynn <trflynn89@pm.me>2021-04-23 14:54:37 -0400
committerAndreas Kling <kling@serenityos.org>2021-04-23 22:36:07 +0200
commitfa59d02692426d2122a2b197ce17ccd01d725299 (patch)
treecd9b83a68cb6126dc6180d124120d83d3fcbfed0 /Userland
parent004025c3c4bfb3e9998e5c860700e357d70b41a4 (diff)
downloadserenity-fa59d02692426d2122a2b197ce17ccd01d725299.zip
LibSQL: Parse IN / NOT IN expressions with a nested SELECT statement
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibSQL/AST.h14
-rw-r--r--Userland/Libraries/LibSQL/Parser.cpp4
-rw-r--r--Userland/Libraries/LibSQL/Tests/TestSqlExpressionParser.cpp23
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)