mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 19:17:41 +00:00
LibSQL: Parse (most of) SELECT statement
This doesn't yet parse join clauses, windowing functions, or compound SELECT statements.
This commit is contained in:
parent
9331293e44
commit
ac0e387beb
5 changed files with 555 additions and 7 deletions
|
@ -154,6 +154,148 @@ private:
|
||||||
Vector<ColumnClause> m_columns;
|
Vector<ColumnClause> m_columns;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class ResultType {
|
||||||
|
All,
|
||||||
|
Table,
|
||||||
|
Expression,
|
||||||
|
};
|
||||||
|
|
||||||
|
class ResultColumn : public ASTNode {
|
||||||
|
public:
|
||||||
|
ResultColumn() = default;
|
||||||
|
|
||||||
|
explicit ResultColumn(String table_name)
|
||||||
|
: m_type(ResultType::Table)
|
||||||
|
, m_table_name(move(table_name))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultColumn(NonnullRefPtr<Expression> expression, String column_alias)
|
||||||
|
: m_type(ResultType::Expression)
|
||||||
|
, m_expression(move(expression))
|
||||||
|
, m_column_alias(move(column_alias))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultType type() const { return m_type; }
|
||||||
|
|
||||||
|
bool select_from_table() const { return !m_table_name.is_null(); }
|
||||||
|
const String& table_name() const { return m_table_name; }
|
||||||
|
|
||||||
|
bool select_from_expression() const { return !m_expression.is_null(); }
|
||||||
|
const RefPtr<Expression>& expression() const { return m_expression; }
|
||||||
|
const String& column_alias() const { return m_column_alias; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ResultType m_type { ResultType::All };
|
||||||
|
|
||||||
|
String m_table_name {};
|
||||||
|
|
||||||
|
RefPtr<Expression> m_expression {};
|
||||||
|
String m_column_alias {};
|
||||||
|
};
|
||||||
|
|
||||||
|
class GroupByClause : public ASTNode {
|
||||||
|
public:
|
||||||
|
GroupByClause(NonnullRefPtrVector<Expression> group_by_list, RefPtr<Expression> having_clause)
|
||||||
|
: m_group_by_list(move(group_by_list))
|
||||||
|
, m_having_clause(move(having_clause))
|
||||||
|
{
|
||||||
|
VERIFY(!m_group_by_list.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonnullRefPtrVector<Expression>& group_by_list() const { return m_group_by_list; }
|
||||||
|
const RefPtr<Expression>& having_clause() const { return m_having_clause; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
NonnullRefPtrVector<Expression> m_group_by_list;
|
||||||
|
RefPtr<Expression> m_having_clause;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TableOrSubquery : public ASTNode {
|
||||||
|
public:
|
||||||
|
TableOrSubquery() = default;
|
||||||
|
|
||||||
|
TableOrSubquery(String schema_name, String table_name, String table_alias)
|
||||||
|
: m_is_table(true)
|
||||||
|
, m_schema_name(move(schema_name))
|
||||||
|
, m_table_name(move(table_name))
|
||||||
|
, m_table_alias(move(table_alias))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit TableOrSubquery(NonnullRefPtrVector<TableOrSubquery> subqueries)
|
||||||
|
: m_is_subquery(!subqueries.is_empty())
|
||||||
|
, m_subqueries(move(subqueries))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_table() const { return m_is_table; }
|
||||||
|
const String& schema_name() const { return m_schema_name; }
|
||||||
|
const String& table_name() const { return m_table_name; }
|
||||||
|
const String& table_alias() const { return m_table_alias; }
|
||||||
|
|
||||||
|
bool is_subquery() const { return m_is_subquery; }
|
||||||
|
const NonnullRefPtrVector<TableOrSubquery>& subqueries() const { return m_subqueries; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_is_table { false };
|
||||||
|
String m_schema_name {};
|
||||||
|
String m_table_name {};
|
||||||
|
String m_table_alias {};
|
||||||
|
|
||||||
|
bool m_is_subquery { false };
|
||||||
|
NonnullRefPtrVector<TableOrSubquery> m_subqueries {};
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Order {
|
||||||
|
Ascending,
|
||||||
|
Descending,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Nulls {
|
||||||
|
First,
|
||||||
|
Last,
|
||||||
|
};
|
||||||
|
|
||||||
|
class OrderingTerm : public ASTNode {
|
||||||
|
public:
|
||||||
|
OrderingTerm(NonnullRefPtr<Expression> expression, String collation_name, Order order, Nulls nulls)
|
||||||
|
: m_expression(move(expression))
|
||||||
|
, m_collation_name(move(collation_name))
|
||||||
|
, m_order(order)
|
||||||
|
, m_nulls(nulls)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonnullRefPtr<Expression>& expression() const { return m_expression; }
|
||||||
|
const String& collation_name() const { return m_collation_name; }
|
||||||
|
Order order() const { return m_order; }
|
||||||
|
Nulls nulls() const { return m_nulls; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
NonnullRefPtr<Expression> m_expression;
|
||||||
|
String m_collation_name;
|
||||||
|
Order m_order;
|
||||||
|
Nulls m_nulls;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LimitClause : public ASTNode {
|
||||||
|
public:
|
||||||
|
LimitClause(NonnullRefPtr<Expression> limit_expression, RefPtr<Expression> offset_expression)
|
||||||
|
: m_limit_expression(move(limit_expression))
|
||||||
|
, m_offset_expression(move(offset_expression))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const NonnullRefPtr<Expression>& limit_expression() const { return m_limit_expression; }
|
||||||
|
const RefPtr<Expression>& offset_expression() const { return m_offset_expression; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
NonnullRefPtr<Expression> m_limit_expression;
|
||||||
|
RefPtr<Expression> m_offset_expression;
|
||||||
|
};
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Expressions
|
// Expressions
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -570,4 +712,38 @@ private:
|
||||||
RefPtr<ReturningClause> m_returning_clause;
|
RefPtr<ReturningClause> m_returning_clause;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Select : public Statement {
|
||||||
|
public:
|
||||||
|
Select(RefPtr<CommonTableExpressionList> common_table_expression_list, bool select_all, NonnullRefPtrVector<ResultColumn> result_column_list, NonnullRefPtrVector<TableOrSubquery> table_or_subquery_list, RefPtr<Expression> where_clause, RefPtr<GroupByClause> group_by_clause, NonnullRefPtrVector<OrderingTerm> ordering_term_list, RefPtr<LimitClause> limit_clause)
|
||||||
|
: m_common_table_expression_list(move(common_table_expression_list))
|
||||||
|
, m_select_all(move(select_all))
|
||||||
|
, m_result_column_list(move(result_column_list))
|
||||||
|
, m_table_or_subquery_list(move(table_or_subquery_list))
|
||||||
|
, m_where_clause(move(where_clause))
|
||||||
|
, m_group_by_clause(move(group_by_clause))
|
||||||
|
, m_ordering_term_list(move(ordering_term_list))
|
||||||
|
, m_limit_clause(move(limit_clause))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const RefPtr<CommonTableExpressionList>& common_table_expression_list() const { return m_common_table_expression_list; }
|
||||||
|
bool select_all() const { return m_select_all; }
|
||||||
|
const NonnullRefPtrVector<ResultColumn>& result_column_list() const { return m_result_column_list; }
|
||||||
|
const NonnullRefPtrVector<TableOrSubquery>& table_or_subquery_list() const { return m_table_or_subquery_list; }
|
||||||
|
const RefPtr<Expression>& where_clause() const { return m_where_clause; }
|
||||||
|
const RefPtr<GroupByClause>& group_by_clause() const { return m_group_by_clause; }
|
||||||
|
const NonnullRefPtrVector<OrderingTerm>& ordering_term_list() const { return m_ordering_term_list; }
|
||||||
|
const RefPtr<LimitClause>& limit_clause() const { return m_limit_clause; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
RefPtr<CommonTableExpressionList> m_common_table_expression_list;
|
||||||
|
bool m_select_all;
|
||||||
|
NonnullRefPtrVector<ResultColumn> m_result_column_list;
|
||||||
|
NonnullRefPtrVector<TableOrSubquery> m_table_or_subquery_list;
|
||||||
|
RefPtr<Expression> m_where_clause;
|
||||||
|
RefPtr<GroupByClause> m_group_by_clause;
|
||||||
|
NonnullRefPtrVector<OrderingTerm> m_ordering_term_list;
|
||||||
|
RefPtr<LimitClause> m_limit_clause;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,24 +25,30 @@ class DropTable;
|
||||||
class ErrorExpression;
|
class ErrorExpression;
|
||||||
class ErrorStatement;
|
class ErrorStatement;
|
||||||
class Expression;
|
class Expression;
|
||||||
|
class GroupByClause;
|
||||||
class InChainedExpression;
|
class InChainedExpression;
|
||||||
class InTableExpression;
|
class InTableExpression;
|
||||||
class InvertibleNestedDoubleExpression;
|
class InvertibleNestedDoubleExpression;
|
||||||
class InvertibleNestedExpression;
|
class InvertibleNestedExpression;
|
||||||
class IsExpression;
|
class IsExpression;
|
||||||
class Lexer;
|
class Lexer;
|
||||||
|
class LimitClause;
|
||||||
class MatchExpression;
|
class MatchExpression;
|
||||||
class NestedDoubleExpression;
|
class NestedDoubleExpression;
|
||||||
class NestedExpression;
|
class NestedExpression;
|
||||||
class NullExpression;
|
class NullExpression;
|
||||||
class NullLiteral;
|
class NullLiteral;
|
||||||
class NumericLiteral;
|
class NumericLiteral;
|
||||||
|
class OrderingTerm;
|
||||||
class Parser;
|
class Parser;
|
||||||
class QualifiedTableName;
|
class QualifiedTableName;
|
||||||
|
class ResultColumn;
|
||||||
class ReturningClause;
|
class ReturningClause;
|
||||||
|
class Select;
|
||||||
class SignedNumber;
|
class SignedNumber;
|
||||||
class Statement;
|
class Statement;
|
||||||
class StringLiteral;
|
class StringLiteral;
|
||||||
|
class TableOrSubquery;
|
||||||
class Token;
|
class Token;
|
||||||
class TypeName;
|
class TypeName;
|
||||||
class UnaryOperatorExpression;
|
class UnaryOperatorExpression;
|
||||||
|
|
|
@ -33,8 +33,10 @@ NonnullRefPtr<Statement> Parser::parse_statement()
|
||||||
return parse_drop_table_statement();
|
return parse_drop_table_statement();
|
||||||
case TokenType::Delete:
|
case TokenType::Delete:
|
||||||
return parse_delete_statement({});
|
return parse_delete_statement({});
|
||||||
|
case TokenType::Select:
|
||||||
|
return parse_select_statement({});
|
||||||
default:
|
default:
|
||||||
expected("CREATE, DROP, or DELETE");
|
expected("CREATE, DROP, DELETE, or SELECT");
|
||||||
return create_ast_node<ErrorStatement>();
|
return create_ast_node<ErrorStatement>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,8 +46,10 @@ NonnullRefPtr<Statement> Parser::parse_statement_with_expression_list(RefPtr<Com
|
||||||
switch (m_parser_state.m_token.type()) {
|
switch (m_parser_state.m_token.type()) {
|
||||||
case TokenType::Delete:
|
case TokenType::Delete:
|
||||||
return parse_delete_statement(move(common_table_expression_list));
|
return parse_delete_statement(move(common_table_expression_list));
|
||||||
|
case TokenType::Select:
|
||||||
|
return parse_select_statement(move(common_table_expression_list));
|
||||||
default:
|
default:
|
||||||
expected("DELETE");
|
expected("DELETE or SELECT");
|
||||||
return create_ast_node<ErrorStatement>();
|
return create_ast_node<ErrorStatement>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,6 +152,94 @@ NonnullRefPtr<Delete> Parser::parse_delete_statement(RefPtr<CommonTableExpressio
|
||||||
return create_ast_node<Delete>(move(common_table_expression_list), move(qualified_table_name), move(where_clause), move(returning_clause));
|
return create_ast_node<Delete>(move(common_table_expression_list), move(qualified_table_name), move(where_clause), move(returning_clause));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NonnullRefPtr<Select> Parser::parse_select_statement(RefPtr<CommonTableExpressionList> common_table_expression_list)
|
||||||
|
{
|
||||||
|
// https://sqlite.org/lang_select.html
|
||||||
|
consume(TokenType::Select);
|
||||||
|
|
||||||
|
bool select_all = !consume_if(TokenType::Distinct);
|
||||||
|
consume_if(TokenType::All); // ALL is the default, so ignore it if specified.
|
||||||
|
|
||||||
|
NonnullRefPtrVector<ResultColumn> result_column_list;
|
||||||
|
do {
|
||||||
|
result_column_list.append(parse_result_column());
|
||||||
|
if (!match(TokenType::Comma))
|
||||||
|
break;
|
||||||
|
consume(TokenType::Comma);
|
||||||
|
} while (!match(TokenType::Eof));
|
||||||
|
|
||||||
|
NonnullRefPtrVector<TableOrSubquery> table_or_subquery_list;
|
||||||
|
if (consume_if(TokenType::From)) {
|
||||||
|
// FIXME: Parse join-clause.
|
||||||
|
|
||||||
|
do {
|
||||||
|
table_or_subquery_list.append(parse_table_or_subquery());
|
||||||
|
if (!match(TokenType::Comma))
|
||||||
|
break;
|
||||||
|
consume(TokenType::Comma);
|
||||||
|
} while (!match(TokenType::Eof));
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<Expression> where_clause;
|
||||||
|
if (consume_if(TokenType::Where))
|
||||||
|
where_clause = parse_expression();
|
||||||
|
|
||||||
|
RefPtr<GroupByClause> group_by_clause;
|
||||||
|
if (consume_if(TokenType::Group)) {
|
||||||
|
consume(TokenType::By);
|
||||||
|
|
||||||
|
NonnullRefPtrVector<Expression> group_by_list;
|
||||||
|
do {
|
||||||
|
group_by_list.append(parse_expression());
|
||||||
|
if (!match(TokenType::Comma))
|
||||||
|
break;
|
||||||
|
consume(TokenType::Comma);
|
||||||
|
} while (!match(TokenType::Eof));
|
||||||
|
|
||||||
|
RefPtr<Expression> having_clause;
|
||||||
|
if (consume_if(TokenType::Having))
|
||||||
|
having_clause = parse_expression();
|
||||||
|
|
||||||
|
group_by_clause = create_ast_node<GroupByClause>(move(group_by_list), move(having_clause));
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Parse 'WINDOW window-name AS window-defn'.
|
||||||
|
// FIXME: Parse 'compound-operator'.
|
||||||
|
|
||||||
|
NonnullRefPtrVector<OrderingTerm> ordering_term_list;
|
||||||
|
if (consume_if(TokenType::Order)) {
|
||||||
|
consume(TokenType::By);
|
||||||
|
|
||||||
|
do {
|
||||||
|
ordering_term_list.append(parse_ordering_term());
|
||||||
|
if (!match(TokenType::Comma))
|
||||||
|
break;
|
||||||
|
consume(TokenType::Comma);
|
||||||
|
} while (!match(TokenType::Eof));
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<LimitClause> limit_clause;
|
||||||
|
if (consume_if(TokenType::Limit)) {
|
||||||
|
auto limit_expression = parse_expression();
|
||||||
|
|
||||||
|
RefPtr<Expression> offset_expression;
|
||||||
|
if (consume_if(TokenType::Offset)) {
|
||||||
|
offset_expression = parse_expression();
|
||||||
|
} else {
|
||||||
|
// Note: The limit clause may instead be definied as "offset-expression, limit-expression", effectively reversing the
|
||||||
|
// order of the expressions. SQLite notes "this is counter-intuitive" and "to avoid confusion, programmers are strongly
|
||||||
|
// encouraged to ... avoid using a LIMIT clause with a comma-separated offset."
|
||||||
|
VERIFY(!consume_if(TokenType::Comma));
|
||||||
|
}
|
||||||
|
|
||||||
|
limit_clause = create_ast_node<LimitClause>(move(limit_expression), move(offset_expression));
|
||||||
|
}
|
||||||
|
|
||||||
|
consume(TokenType::SemiColon);
|
||||||
|
|
||||||
|
return create_ast_node<Select>(move(common_table_expression_list), select_all, move(result_column_list), move(table_or_subquery_list), move(where_clause), move(group_by_clause), move(ordering_term_list), move(limit_clause));
|
||||||
|
}
|
||||||
|
|
||||||
NonnullRefPtr<CommonTableExpressionList> Parser::parse_common_table_expression_list()
|
NonnullRefPtr<CommonTableExpressionList> Parser::parse_common_table_expression_list()
|
||||||
{
|
{
|
||||||
consume(TokenType::With);
|
consume(TokenType::With);
|
||||||
|
@ -297,17 +389,22 @@ Optional<NonnullRefPtr<Expression>> Parser::parse_literal_value_expression()
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<NonnullRefPtr<Expression>> Parser::parse_column_name_expression()
|
Optional<NonnullRefPtr<Expression>> Parser::parse_column_name_expression(String with_parsed_identifier, bool with_parsed_period)
|
||||||
{
|
{
|
||||||
if (!match(TokenType::Identifier))
|
if (with_parsed_identifier.is_null() && !match(TokenType::Identifier))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
String first_identifier = consume(TokenType::Identifier).value();
|
String first_identifier;
|
||||||
|
if (with_parsed_identifier.is_null())
|
||||||
|
first_identifier = consume(TokenType::Identifier).value();
|
||||||
|
else
|
||||||
|
first_identifier = move(with_parsed_identifier);
|
||||||
|
|
||||||
String schema_name;
|
String schema_name;
|
||||||
String table_name;
|
String table_name;
|
||||||
String column_name;
|
String column_name;
|
||||||
|
|
||||||
if (consume_if(TokenType::Period)) {
|
if (with_parsed_period || consume_if(TokenType::Period)) {
|
||||||
String second_identifier = consume(TokenType::Identifier).value();
|
String second_identifier = consume(TokenType::Identifier).value();
|
||||||
|
|
||||||
if (consume_if(TokenType::Period)) {
|
if (consume_if(TokenType::Period)) {
|
||||||
|
@ -741,6 +838,107 @@ NonnullRefPtr<ReturningClause> Parser::parse_returning_clause()
|
||||||
return create_ast_node<ReturningClause>(move(columns));
|
return create_ast_node<ReturningClause>(move(columns));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NonnullRefPtr<ResultColumn> Parser::parse_result_column()
|
||||||
|
{
|
||||||
|
// https://sqlite.org/syntax/result-column.html
|
||||||
|
if (consume_if(TokenType::Asterisk))
|
||||||
|
return create_ast_node<ResultColumn>();
|
||||||
|
|
||||||
|
// If we match an identifier now, we don't know whether it is a table-name of the form "table-name.*", or if it is the start of a
|
||||||
|
// column-name-expression, until we try to parse the asterisk. So if we consume an indentifier and a period, but don't find an
|
||||||
|
// asterisk, hold onto that information to form a column-name-expression later.
|
||||||
|
String table_name;
|
||||||
|
bool parsed_period = false;
|
||||||
|
|
||||||
|
if (match(TokenType::Identifier)) {
|
||||||
|
table_name = consume().value();
|
||||||
|
parsed_period = consume_if(TokenType::Period);
|
||||||
|
if (parsed_period && consume_if(TokenType::Asterisk))
|
||||||
|
return create_ast_node<ResultColumn>(move(table_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto expression = table_name.is_null()
|
||||||
|
? parse_expression()
|
||||||
|
: static_cast<NonnullRefPtr<Expression>>(*parse_column_name_expression(move(table_name), parsed_period));
|
||||||
|
consume_if(TokenType::As); // 'AS' is optional.
|
||||||
|
|
||||||
|
String column_alias;
|
||||||
|
if (match(TokenType::Identifier))
|
||||||
|
column_alias = consume().value();
|
||||||
|
|
||||||
|
return create_ast_node<ResultColumn>(move(expression), move(column_alias));
|
||||||
|
}
|
||||||
|
|
||||||
|
NonnullRefPtr<TableOrSubquery> Parser::parse_table_or_subquery()
|
||||||
|
{
|
||||||
|
// https://sqlite.org/syntax/table-or-subquery.html
|
||||||
|
if (match(TokenType::Identifier)) {
|
||||||
|
String schema_or_table_name = consume().value();
|
||||||
|
String schema_name;
|
||||||
|
String table_name;
|
||||||
|
|
||||||
|
if (consume_if(TokenType::Period)) {
|
||||||
|
schema_name = move(schema_or_table_name);
|
||||||
|
table_name = consume(TokenType::Identifier).value();
|
||||||
|
} else {
|
||||||
|
table_name = move(schema_or_table_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
consume_if(TokenType::As); // 'AS' is optional.
|
||||||
|
|
||||||
|
String table_alias;
|
||||||
|
if (match(TokenType::Identifier))
|
||||||
|
table_alias = consume().value();
|
||||||
|
|
||||||
|
return create_ast_node<TableOrSubquery>(move(schema_name), move(table_name), move(table_alias));
|
||||||
|
}
|
||||||
|
|
||||||
|
consume(TokenType::ParenOpen);
|
||||||
|
// FIXME: Parse join-clause.
|
||||||
|
|
||||||
|
NonnullRefPtrVector<TableOrSubquery> subqueries;
|
||||||
|
while (!has_errors() && !match(TokenType::Eof)) {
|
||||||
|
subqueries.append(parse_table_or_subquery());
|
||||||
|
if (!match(TokenType::Comma))
|
||||||
|
break;
|
||||||
|
consume(TokenType::Comma);
|
||||||
|
}
|
||||||
|
|
||||||
|
consume(TokenType::ParenClose);
|
||||||
|
|
||||||
|
return create_ast_node<TableOrSubquery>(move(subqueries));
|
||||||
|
}
|
||||||
|
|
||||||
|
NonnullRefPtr<OrderingTerm> Parser::parse_ordering_term()
|
||||||
|
{
|
||||||
|
// https://sqlite.org/syntax/ordering-term.html
|
||||||
|
auto expression = parse_expression();
|
||||||
|
|
||||||
|
String collation_name;
|
||||||
|
if (is<CollateExpression>(*expression)) {
|
||||||
|
const auto& collate = static_cast<const CollateExpression&>(*expression);
|
||||||
|
collation_name = collate.collation_name();
|
||||||
|
expression = collate.expression();
|
||||||
|
} else if (consume_if(TokenType::Collate)) {
|
||||||
|
collation_name = consume(TokenType::Identifier).value();
|
||||||
|
}
|
||||||
|
|
||||||
|
Order order = consume_if(TokenType::Desc) ? Order::Descending : Order::Ascending;
|
||||||
|
consume_if(TokenType::Asc); // ASC is the default, so ignore it if specified.
|
||||||
|
|
||||||
|
Nulls nulls = order == Order::Ascending ? Nulls::First : Nulls::Last;
|
||||||
|
if (consume_if(TokenType::Nulls)) {
|
||||||
|
if (consume_if(TokenType::First))
|
||||||
|
nulls = Nulls::First;
|
||||||
|
else if (consume_if(TokenType::Last))
|
||||||
|
nulls = Nulls::Last;
|
||||||
|
else
|
||||||
|
expected("FIRST or LAST");
|
||||||
|
}
|
||||||
|
|
||||||
|
return create_ast_node<OrderingTerm>(move(expression), move(collation_name), order, nulls);
|
||||||
|
}
|
||||||
|
|
||||||
Token Parser::consume()
|
Token Parser::consume()
|
||||||
{
|
{
|
||||||
auto old_token = m_parser_state.m_token;
|
auto old_token = m_parser_state.m_token;
|
||||||
|
|
|
@ -55,13 +55,14 @@ private:
|
||||||
NonnullRefPtr<CreateTable> parse_create_table_statement();
|
NonnullRefPtr<CreateTable> parse_create_table_statement();
|
||||||
NonnullRefPtr<DropTable> parse_drop_table_statement();
|
NonnullRefPtr<DropTable> parse_drop_table_statement();
|
||||||
NonnullRefPtr<Delete> parse_delete_statement(RefPtr<CommonTableExpressionList>);
|
NonnullRefPtr<Delete> parse_delete_statement(RefPtr<CommonTableExpressionList>);
|
||||||
|
NonnullRefPtr<Select> parse_select_statement(RefPtr<CommonTableExpressionList>);
|
||||||
NonnullRefPtr<CommonTableExpressionList> parse_common_table_expression_list();
|
NonnullRefPtr<CommonTableExpressionList> parse_common_table_expression_list();
|
||||||
|
|
||||||
NonnullRefPtr<Expression> parse_primary_expression();
|
NonnullRefPtr<Expression> parse_primary_expression();
|
||||||
NonnullRefPtr<Expression> parse_secondary_expression(NonnullRefPtr<Expression> primary);
|
NonnullRefPtr<Expression> parse_secondary_expression(NonnullRefPtr<Expression> primary);
|
||||||
bool match_secondary_expression() const;
|
bool match_secondary_expression() const;
|
||||||
Optional<NonnullRefPtr<Expression>> parse_literal_value_expression();
|
Optional<NonnullRefPtr<Expression>> parse_literal_value_expression();
|
||||||
Optional<NonnullRefPtr<Expression>> parse_column_name_expression();
|
Optional<NonnullRefPtr<Expression>> parse_column_name_expression(String with_parsed_identifier = {}, bool with_parsed_period = false);
|
||||||
Optional<NonnullRefPtr<Expression>> parse_unary_operator_expression();
|
Optional<NonnullRefPtr<Expression>> parse_unary_operator_expression();
|
||||||
Optional<NonnullRefPtr<Expression>> parse_binary_operator_expression(NonnullRefPtr<Expression> lhs);
|
Optional<NonnullRefPtr<Expression>> parse_binary_operator_expression(NonnullRefPtr<Expression> lhs);
|
||||||
Optional<NonnullRefPtr<Expression>> parse_chained_expression();
|
Optional<NonnullRefPtr<Expression>> parse_chained_expression();
|
||||||
|
@ -80,6 +81,9 @@ private:
|
||||||
NonnullRefPtr<CommonTableExpression> parse_common_table_expression();
|
NonnullRefPtr<CommonTableExpression> parse_common_table_expression();
|
||||||
NonnullRefPtr<QualifiedTableName> parse_qualified_table_name();
|
NonnullRefPtr<QualifiedTableName> parse_qualified_table_name();
|
||||||
NonnullRefPtr<ReturningClause> parse_returning_clause();
|
NonnullRefPtr<ReturningClause> parse_returning_clause();
|
||||||
|
NonnullRefPtr<ResultColumn> parse_result_column();
|
||||||
|
NonnullRefPtr<TableOrSubquery> parse_table_or_subquery();
|
||||||
|
NonnullRefPtr<OrderingTerm> parse_ordering_term();
|
||||||
|
|
||||||
Token consume();
|
Token consume();
|
||||||
Token consume(TokenType type);
|
Token consume(TokenType type);
|
||||||
|
|
|
@ -234,4 +234,168 @@ TEST_CASE(delete_)
|
||||||
validate("WITH RECURSIVE table AS () DELETE FROM table;", { true, { { "table", {} } } }, {}, "table", {}, false, false, {});
|
validate("WITH RECURSIVE table AS () DELETE FROM table;", { true, { { "table", {} } } }, {}, "table", {}, false, false, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE(select)
|
||||||
|
{
|
||||||
|
EXPECT(parse("SELECT").is_error());
|
||||||
|
EXPECT(parse("SELECT;").is_error());
|
||||||
|
EXPECT(parse("SELECT DISTINCT;").is_error());
|
||||||
|
EXPECT(parse("SELECT ALL;").is_error());
|
||||||
|
EXPECT(parse("SELECT *").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM;").is_error());
|
||||||
|
EXPECT(parse("SELECT table. FROM table;").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM (").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM ()").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM ();").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM (table1)").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM (table1, )").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM (table1, table2)").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM table").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM table WHERE;").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM table WHERE 1 ==1").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM table GROUP;").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM table GROUP BY;").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM table GROUP BY column").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM table ORDER:").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM table ORDER BY column").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM table ORDER BY column COLLATE:").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM table ORDER BY column COLLATE collation").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM table ORDER BY column NULLS;").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM table ORDER BY column NULLS SECOND;").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM table LIMIT;").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM table LIMIT 12").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM table LIMIT 12 OFFSET;").is_error());
|
||||||
|
EXPECT(parse("SELECT * FROM table LIMIT 12 OFFSET 15").is_error());
|
||||||
|
|
||||||
|
struct Type {
|
||||||
|
SQL::ResultType type;
|
||||||
|
StringView table_name_or_column_alias {};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct From {
|
||||||
|
StringView schema_name;
|
||||||
|
StringView table_name;
|
||||||
|
StringView table_alias;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Ordering {
|
||||||
|
String collation_name;
|
||||||
|
SQL::Order order;
|
||||||
|
SQL::Nulls nulls;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto validate = [](StringView sql, Vector<Type> expected_columns, Vector<From> expected_from_list, bool expect_where_clause, size_t expected_group_by_size, bool expect_having_clause, Vector<Ordering> expected_ordering, bool expect_limit_clause, bool expect_offset_clause) {
|
||||||
|
auto result = parse(sql);
|
||||||
|
EXPECT(!result.is_error());
|
||||||
|
|
||||||
|
auto statement = result.release_value();
|
||||||
|
EXPECT(is<SQL::Select>(*statement));
|
||||||
|
|
||||||
|
const auto& select = static_cast<const SQL::Select&>(*statement);
|
||||||
|
|
||||||
|
const auto& result_column_list = select.result_column_list();
|
||||||
|
EXPECT_EQ(result_column_list.size(), expected_columns.size());
|
||||||
|
for (size_t i = 0; i < result_column_list.size(); ++i) {
|
||||||
|
const auto& result_column = result_column_list[i];
|
||||||
|
const auto& expected_column = expected_columns[i];
|
||||||
|
EXPECT_EQ(result_column.type(), expected_column.type);
|
||||||
|
|
||||||
|
switch (result_column.type()) {
|
||||||
|
case SQL::ResultType::All:
|
||||||
|
EXPECT(expected_column.table_name_or_column_alias.is_null());
|
||||||
|
break;
|
||||||
|
case SQL::ResultType::Table:
|
||||||
|
EXPECT_EQ(result_column.table_name(), expected_column.table_name_or_column_alias);
|
||||||
|
break;
|
||||||
|
case SQL::ResultType::Expression:
|
||||||
|
EXPECT_EQ(result_column.column_alias(), expected_column.table_name_or_column_alias);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& table_or_subquery_list = select.table_or_subquery_list();
|
||||||
|
EXPECT_EQ(table_or_subquery_list.size(), expected_from_list.size());
|
||||||
|
for (size_t i = 0; i < table_or_subquery_list.size(); ++i) {
|
||||||
|
const auto& result_from = table_or_subquery_list[i];
|
||||||
|
const auto& expected_from = expected_from_list[i];
|
||||||
|
EXPECT_EQ(result_from.schema_name(), expected_from.schema_name);
|
||||||
|
EXPECT_EQ(result_from.table_name(), expected_from.table_name);
|
||||||
|
EXPECT_EQ(result_from.table_alias(), expected_from.table_alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& where_clause = select.where_clause();
|
||||||
|
EXPECT_EQ(where_clause.is_null(), !expect_where_clause);
|
||||||
|
if (where_clause)
|
||||||
|
EXPECT(!is<SQL::ErrorExpression>(*where_clause));
|
||||||
|
|
||||||
|
const auto& group_by_clause = select.group_by_clause();
|
||||||
|
EXPECT_EQ(group_by_clause.is_null(), (expected_group_by_size == 0));
|
||||||
|
if (group_by_clause) {
|
||||||
|
const auto& group_by_list = group_by_clause->group_by_list();
|
||||||
|
EXPECT_EQ(group_by_list.size(), expected_group_by_size);
|
||||||
|
for (size_t i = 0; i < group_by_list.size(); ++i)
|
||||||
|
EXPECT(!is<SQL::ErrorExpression>(group_by_list[i]));
|
||||||
|
|
||||||
|
const auto& having_clause = group_by_clause->having_clause();
|
||||||
|
EXPECT_EQ(having_clause.is_null(), !expect_having_clause);
|
||||||
|
if (having_clause)
|
||||||
|
EXPECT(!is<SQL::ErrorExpression>(*having_clause));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& ordering_term_list = select.ordering_term_list();
|
||||||
|
EXPECT_EQ(ordering_term_list.size(), expected_ordering.size());
|
||||||
|
for (size_t i = 0; i < ordering_term_list.size(); ++i) {
|
||||||
|
const auto& result_order = ordering_term_list[i];
|
||||||
|
const auto& expected_order = expected_ordering[i];
|
||||||
|
EXPECT(!is<SQL::ErrorExpression>(*result_order.expression()));
|
||||||
|
EXPECT_EQ(result_order.collation_name(), expected_order.collation_name);
|
||||||
|
EXPECT_EQ(result_order.order(), expected_order.order);
|
||||||
|
EXPECT_EQ(result_order.nulls(), expected_order.nulls);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& limit_clause = select.limit_clause();
|
||||||
|
EXPECT_EQ(limit_clause.is_null(), !expect_limit_clause);
|
||||||
|
if (limit_clause) {
|
||||||
|
const auto& limit_expression = limit_clause->limit_expression();
|
||||||
|
EXPECT(!is<SQL::ErrorExpression>(*limit_expression));
|
||||||
|
|
||||||
|
const auto& offset_expression = limit_clause->offset_expression();
|
||||||
|
EXPECT_EQ(offset_expression.is_null(), !expect_offset_clause);
|
||||||
|
if (offset_expression)
|
||||||
|
EXPECT(!is<SQL::ErrorExpression>(*offset_expression));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Vector<Type> all { { SQL::ResultType::All } };
|
||||||
|
Vector<From> from { { {}, "table", {} } };
|
||||||
|
|
||||||
|
validate("SELECT * FROM table;", { { SQL::ResultType::All } }, from, false, 0, false, {}, false, false);
|
||||||
|
validate("SELECT table.* FROM table;", { { SQL::ResultType::Table, "table" } }, from, false, 0, false, {}, false, false);
|
||||||
|
validate("SELECT column AS alias FROM table;", { { SQL::ResultType::Expression, "alias" } }, from, false, 0, false, {}, false, false);
|
||||||
|
validate("SELECT table.column AS alias FROM table;", { { SQL::ResultType::Expression, "alias" } }, from, false, 0, false, {}, false, false);
|
||||||
|
validate("SELECT schema.table.column AS alias FROM table;", { { SQL::ResultType::Expression, "alias" } }, from, false, 0, false, {}, false, false);
|
||||||
|
validate("SELECT column AS alias, *, table.* FROM table;", { { SQL::ResultType::Expression, "alias" }, { SQL::ResultType::All }, { SQL::ResultType::Table, "table" } }, from, false, 0, false, {}, false, false);
|
||||||
|
|
||||||
|
validate("SELECT * FROM table;", all, { { {}, "table", {} } }, false, 0, false, {}, false, false);
|
||||||
|
validate("SELECT * FROM schema.table;", all, { { "schema", "table", {} } }, false, 0, false, {}, false, false);
|
||||||
|
validate("SELECT * FROM schema.table AS alias;", all, { { "schema", "table", "alias" } }, false, 0, false, {}, false, false);
|
||||||
|
validate("SELECT * FROM schema.table AS alias, table2, table3 AS table4;", all, { { "schema", "table", "alias" }, { {}, "table2", {} }, { {}, "table3", "table4" } }, false, 0, false, {}, false, false);
|
||||||
|
|
||||||
|
validate("SELECT * FROM table WHERE column IS NOT NULL;", all, from, true, 0, false, {}, false, false);
|
||||||
|
|
||||||
|
validate("SELECT * FROM table GROUP BY column;", all, from, false, 1, false, {}, false, false);
|
||||||
|
validate("SELECT * FROM table GROUP BY column1, column2, column3;", all, from, false, 3, false, {}, false, false);
|
||||||
|
validate("SELECT * FROM table GROUP BY column HAVING 'abc';", all, from, false, 1, true, {}, false, false);
|
||||||
|
|
||||||
|
validate("SELECT * FROM table ORDER BY column;", all, from, false, 0, false, { { {}, SQL::Order::Ascending, SQL::Nulls::First } }, false, false);
|
||||||
|
validate("SELECT * FROM table ORDER BY column COLLATE collation;", all, from, false, 0, false, { { "collation", SQL::Order::Ascending, SQL::Nulls::First } }, false, false);
|
||||||
|
validate("SELECT * FROM table ORDER BY column ASC;", all, from, false, 0, false, { { {}, SQL::Order::Ascending, SQL::Nulls::First } }, false, false);
|
||||||
|
validate("SELECT * FROM table ORDER BY column DESC;", all, from, false, 0, false, { { {}, SQL::Order::Descending, SQL::Nulls::Last } }, false, false);
|
||||||
|
validate("SELECT * FROM table ORDER BY column ASC NULLS LAST;", all, from, false, 0, false, { { {}, SQL::Order::Ascending, SQL::Nulls::Last } }, false, false);
|
||||||
|
validate("SELECT * FROM table ORDER BY column DESC NULLS FIRST;", all, from, false, 0, false, { { {}, SQL::Order::Descending, SQL::Nulls::First } }, false, false);
|
||||||
|
validate("SELECT * FROM table ORDER BY column1, column2 DESC, column3 NULLS LAST;", all, from, false, 0, false, { { {}, SQL::Order::Ascending, SQL::Nulls::First }, { {}, SQL::Order::Descending, SQL::Nulls::Last }, { {}, SQL::Order::Ascending, SQL::Nulls::Last } }, false, false);
|
||||||
|
|
||||||
|
validate("SELECT * FROM table LIMIT 15;", all, from, false, 0, false, {}, true, false);
|
||||||
|
validate("SELECT * FROM table LIMIT 15 OFFSET 16;", all, from, false, 0, false, {}, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_MAIN(SqlStatementParser)
|
TEST_MAIN(SqlStatementParser)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue