1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 01:48:11 +00:00

LibSQL: Parse EXISTS expressions

The EXISTS expression is a bit of an odd-man-out because it can appear
as any of the following forms:

    EXISTS (select-stmt)
    NOT EXISTS (select-stmt)
    (select-stmt)

Which makes it the only keyword expression that doesn't require its
keyword to actually be used. The consequence is that we might come
across an EXISTS expression while parsing another expression type;
NOT would have triggered a unary operator expression, and an opening
parentheses would have triggered an expression chain.
This commit is contained in:
Timothy Flynn 2021-04-23 15:29:28 -04:00 committed by Andreas Kling
parent e62e76ca1a
commit 99b38aa3fa
4 changed files with 80 additions and 5 deletions

View file

@ -537,6 +537,22 @@ private:
RefPtr<Expression> m_else_expression; RefPtr<Expression> m_else_expression;
}; };
class ExistsExpression : public Expression {
public:
ExistsExpression(NonnullRefPtr<Select> select_statement, bool invert_expression)
: m_select_statement(move(select_statement))
, m_invert_expression(invert_expression)
{
}
const NonnullRefPtr<Select>& select_statement() const { return m_select_statement; }
bool invert_expression() const { return m_invert_expression; }
private:
NonnullRefPtr<Select> m_select_statement;
bool m_invert_expression;
};
class CollateExpression : public NestedExpression { class CollateExpression : public NestedExpression {
public: public:
CollateExpression(NonnullRefPtr<Expression> expression, String collation_name) CollateExpression(NonnullRefPtr<Expression> expression, String collation_name)

View file

@ -215,7 +215,6 @@ NonnullRefPtr<Expression> Parser::parse_expression()
// FIXME: Parse 'bind-parameter'. // FIXME: Parse 'bind-parameter'.
// FIXME: Parse 'function-name'. // FIXME: Parse 'function-name'.
// FIXME: Parse 'exists'.
// FIXME: Parse 'raise-function'. // FIXME: Parse 'raise-function'.
return expression; return expression;
@ -241,6 +240,9 @@ NonnullRefPtr<Expression> Parser::parse_primary_expression()
if (auto expression = parse_case_expression(); expression.has_value()) if (auto expression = parse_case_expression(); expression.has_value())
return move(expression.value()); return move(expression.value());
if (auto expression = parse_exists_expression(false); expression.has_value())
return move(expression.value());
expected("Primary Expression"); expected("Primary Expression");
consume(); consume();
@ -381,8 +383,12 @@ Optional<NonnullRefPtr<Expression>> Parser::parse_unary_operator_expression()
if (consume_if(TokenType::Tilde)) if (consume_if(TokenType::Tilde))
return create_ast_node<UnaryOperatorExpression>(UnaryOperator::BitwiseNot, parse_expression()); return create_ast_node<UnaryOperatorExpression>(UnaryOperator::BitwiseNot, parse_expression());
if (consume_if(TokenType::Not)) if (consume_if(TokenType::Not)) {
return create_ast_node<UnaryOperatorExpression>(UnaryOperator::Not, parse_expression()); if (match(TokenType::Exists))
return parse_exists_expression(true);
else
return create_ast_node<UnaryOperatorExpression>(UnaryOperator::Not, parse_expression());
}
return {}; return {};
} }
@ -448,11 +454,15 @@ Optional<NonnullRefPtr<Expression>> Parser::parse_binary_operator_expression(Non
Optional<NonnullRefPtr<Expression>> Parser::parse_chained_expression() Optional<NonnullRefPtr<Expression>> Parser::parse_chained_expression()
{ {
if (!match(TokenType::ParenOpen)) if (!consume_if(TokenType::ParenOpen))
return {}; return {};
if (match(TokenType::Select))
return parse_exists_expression(false, TokenType::Select);
NonnullRefPtrVector<Expression> expressions; NonnullRefPtrVector<Expression> expressions;
parse_comma_separated_list(true, [&]() { expressions.append(parse_expression()); }); parse_comma_separated_list(false, [&]() { expressions.append(parse_expression()); });
consume(TokenType::ParenClose);
return create_ast_node<ChainedExpression>(move(expressions)); return create_ast_node<ChainedExpression>(move(expressions));
} }
@ -506,6 +516,21 @@ Optional<NonnullRefPtr<Expression>> Parser::parse_case_expression()
return create_ast_node<CaseExpression>(move(case_expression), move(when_then_clauses), move(else_expression)); return create_ast_node<CaseExpression>(move(case_expression), move(when_then_clauses), move(else_expression));
} }
Optional<NonnullRefPtr<Expression>> Parser::parse_exists_expression(bool invert_expression, TokenType opening_token)
{
VERIFY((opening_token == TokenType::Exists) || (opening_token == TokenType::Select));
if ((opening_token == TokenType::Exists) && !consume_if(TokenType::Exists))
return {};
if (opening_token == TokenType::Exists)
consume(TokenType::ParenOpen);
auto select_statement = parse_select_statement({});
consume(TokenType::ParenClose);
return create_ast_node<ExistsExpression>(move(select_statement), invert_expression);
}
Optional<NonnullRefPtr<Expression>> Parser::parse_collate_expression(NonnullRefPtr<Expression> expression) Optional<NonnullRefPtr<Expression>> Parser::parse_collate_expression(NonnullRefPtr<Expression> expression)
{ {
if (!match(TokenType::Collate)) if (!match(TokenType::Collate))

View file

@ -68,6 +68,7 @@ private:
Optional<NonnullRefPtr<Expression>> parse_chained_expression(); Optional<NonnullRefPtr<Expression>> parse_chained_expression();
Optional<NonnullRefPtr<Expression>> parse_cast_expression(); Optional<NonnullRefPtr<Expression>> parse_cast_expression();
Optional<NonnullRefPtr<Expression>> parse_case_expression(); Optional<NonnullRefPtr<Expression>> parse_case_expression();
Optional<NonnullRefPtr<Expression>> parse_exists_expression(bool invert_expression, TokenType opening_token = TokenType::Exists);
Optional<NonnullRefPtr<Expression>> parse_collate_expression(NonnullRefPtr<Expression> expression); Optional<NonnullRefPtr<Expression>> parse_collate_expression(NonnullRefPtr<Expression> expression);
Optional<NonnullRefPtr<Expression>> parse_is_expression(NonnullRefPtr<Expression> expression); Optional<NonnullRefPtr<Expression>> parse_is_expression(NonnullRefPtr<Expression> expression);
Optional<NonnullRefPtr<Expression>> parse_match_expression(NonnullRefPtr<Expression> lhs, bool invert_expression); Optional<NonnullRefPtr<Expression>> parse_match_expression(NonnullRefPtr<Expression> lhs, bool invert_expression);

View file

@ -338,6 +338,39 @@ TEST_CASE(case_expression)
validate("CASE 15 WHEN 16 THEN 17 WHEN 18 THEN 19 ELSE 20 END", true, 2, true); validate("CASE 15 WHEN 16 THEN 17 WHEN 18 THEN 19 ELSE 20 END", true, 2, true);
} }
TEST_CASE(exists_expression)
{
EXPECT(parse("EXISTS").is_error());
EXPECT(parse("EXISTS (").is_error());
EXPECT(parse("EXISTS (SELECT").is_error());
EXPECT(parse("EXISTS (SELECT)").is_error());
EXPECT(parse("EXISTS (SELECT * FROM table").is_error());
EXPECT(parse("NOT EXISTS").is_error());
EXPECT(parse("NOT EXISTS (").is_error());
EXPECT(parse("NOT EXISTS (SELECT").is_error());
EXPECT(parse("NOT EXISTS (SELECT)").is_error());
EXPECT(parse("NOT EXISTS (SELECT * FROM table").is_error());
EXPECT(parse("(").is_error());
EXPECT(parse("(SELECT").is_error());
EXPECT(parse("(SELECT)").is_error());
EXPECT(parse("(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::ExistsExpression>(*expression));
const auto& exists = static_cast<const SQL::ExistsExpression&>(*expression);
EXPECT_EQ(exists.invert_expression(), expected_invert_expression);
};
validate("EXISTS (SELECT * FROM table)", false);
validate("NOT EXISTS (SELECT * FROM table)", true);
validate("(SELECT * FROM table)", false);
}
TEST_CASE(collate_expression) TEST_CASE(collate_expression)
{ {
EXPECT(parse("COLLATE").is_error()); EXPECT(parse("COLLATE").is_error());