use crate::db::{
reduced_sql::{Keyword, SqlParseError},
sql::parser::{
Parser, SqlDeleteStatement, SqlDescribeStatement, SqlExplainMode, SqlExplainStatement,
SqlExplainTarget, SqlSelectStatement, SqlShowColumnsStatement, SqlShowEntitiesStatement,
SqlShowIndexesStatement, SqlStatement,
},
};
impl Parser {
pub(super) fn parse_statement(&mut self) -> Result<SqlStatement, SqlParseError> {
if self.eat_keyword(Keyword::Select) {
return Ok(SqlStatement::Select(self.parse_select_statement()?));
}
if self.eat_keyword(Keyword::Delete) {
return Ok(SqlStatement::Delete(self.parse_delete_statement()?));
}
if self.eat_keyword(Keyword::Explain) {
return Ok(SqlStatement::Explain(self.parse_explain_statement()?));
}
if self.eat_keyword(Keyword::Describe) {
return Ok(SqlStatement::Describe(self.parse_describe_statement()?));
}
if self.eat_keyword(Keyword::Show) {
return self.parse_show_statement();
}
if let Some(feature) = self.peek_unsupported_feature() {
return Err(SqlParseError::unsupported_feature(feature));
}
Err(SqlParseError::expected(
"one of SELECT, DELETE, EXPLAIN, DESCRIBE, SHOW",
self.peek_kind(),
))
}
pub(super) fn trailing_clause_order_error(
&self,
statement: &SqlStatement,
) -> Option<SqlParseError> {
match statement {
SqlStatement::Select(select) => self.select_clause_order_error(select),
SqlStatement::Delete(delete) => self.delete_clause_order_error(delete),
SqlStatement::Explain(explain) => match &explain.statement {
SqlExplainTarget::Select(select) => self.select_clause_order_error(select),
SqlExplainTarget::Delete(delete) => self.delete_clause_order_error(delete),
},
SqlStatement::Describe(_) => {
Some(SqlParseError::unsupported_feature("DESCRIBE modifiers"))
}
SqlStatement::ShowIndexes(_) => {
Some(SqlParseError::unsupported_feature("SHOW INDEXES modifiers"))
}
SqlStatement::ShowColumns(_) => {
Some(SqlParseError::unsupported_feature("SHOW COLUMNS modifiers"))
}
SqlStatement::ShowEntities(_) => Some(SqlParseError::unsupported_feature(
"SHOW ENTITIES modifiers",
)),
}
}
fn parse_show_statement(&mut self) -> Result<SqlStatement, SqlParseError> {
if self.eat_keyword(Keyword::Indexes) {
return Ok(SqlStatement::ShowIndexes(
self.parse_show_indexes_statement()?,
));
}
if self.eat_keyword(Keyword::Columns) {
return Ok(SqlStatement::ShowColumns(
self.parse_show_columns_statement()?,
));
}
if self.eat_keyword(Keyword::Entities) {
return Ok(SqlStatement::ShowEntities(SqlShowEntitiesStatement));
}
Err(SqlParseError::unsupported_feature(
"SHOW commands beyond SHOW INDEXES/SHOW COLUMNS/SHOW ENTITIES",
))
}
fn parse_explain_statement(&mut self) -> Result<SqlExplainStatement, SqlParseError> {
let mode = if self.eat_keyword(Keyword::Execution) {
SqlExplainMode::Execution
} else if self.eat_keyword(Keyword::Json) {
SqlExplainMode::Json
} else {
SqlExplainMode::Plan
};
let statement = if self.eat_keyword(Keyword::Select) {
SqlExplainTarget::Select(self.parse_select_statement()?)
} else if self.eat_keyword(Keyword::Delete) {
SqlExplainTarget::Delete(self.parse_delete_statement()?)
} else if let Some(feature) = self.peek_unsupported_feature() {
return Err(SqlParseError::unsupported_feature(feature));
} else {
return Err(SqlParseError::expected(
"one of SELECT, DELETE",
self.peek_kind(),
));
};
Ok(SqlExplainStatement { mode, statement })
}
fn select_clause_order_error(&self, statement: &SqlSelectStatement) -> Option<SqlParseError> {
if self.peek_keyword(Keyword::Order)
&& (statement.limit.is_some() || statement.offset.is_some())
{
return Some(SqlParseError::invalid_syntax(
"ORDER BY must appear before LIMIT/OFFSET",
));
}
None
}
fn delete_clause_order_error(&self, statement: &SqlDeleteStatement) -> Option<SqlParseError> {
if self.peek_keyword(Keyword::Order) && statement.limit.is_some() {
return Some(SqlParseError::invalid_syntax(
"ORDER BY must appear before LIMIT in DELETE",
));
}
None
}
fn parse_select_statement(&mut self) -> Result<SqlSelectStatement, SqlParseError> {
let distinct = self.eat_keyword(Keyword::Distinct);
let projection = self.parse_projection()?;
self.expect_keyword(Keyword::From)?;
let entity = self.expect_identifier()?;
self.reject_table_alias_if_present()?;
let predicate = if self.eat_keyword(Keyword::Where) {
Some(self.parse_predicate()?)
} else {
None
};
let group_by = if self.eat_keyword(Keyword::Group) {
self.expect_keyword(Keyword::By)?;
self.parse_identifier_list()?
} else {
Vec::new()
};
let having = if self.eat_keyword(Keyword::Having) {
self.parse_having_clauses()?
} else {
Vec::new()
};
let order_by = if self.eat_keyword(Keyword::Order) {
self.expect_keyword(Keyword::By)?;
self.parse_order_terms()?
} else {
Vec::new()
};
let limit = if self.eat_keyword(Keyword::Limit) {
Some(self.parse_u32_literal("LIMIT")?)
} else {
None
};
let offset = if self.eat_keyword(Keyword::Offset) {
Some(self.parse_u32_literal("OFFSET")?)
} else {
None
};
Ok(SqlSelectStatement {
entity,
projection,
predicate,
distinct,
group_by,
having,
order_by,
limit,
offset,
})
}
fn parse_delete_statement(&mut self) -> Result<SqlDeleteStatement, SqlParseError> {
self.expect_keyword(Keyword::From)?;
let entity = self.expect_identifier()?;
self.reject_table_alias_if_present()?;
let predicate = if self.eat_keyword(Keyword::Where) {
Some(self.parse_predicate()?)
} else {
None
};
let order_by = if self.eat_keyword(Keyword::Order) {
self.expect_keyword(Keyword::By)?;
self.parse_order_terms()?
} else {
Vec::new()
};
let limit = if self.eat_keyword(Keyword::Limit) {
Some(self.parse_u32_literal("LIMIT")?)
} else {
None
};
if self.eat_keyword(Keyword::Offset) {
return Err(SqlParseError::unsupported_feature("DELETE ... OFFSET"));
}
Ok(SqlDeleteStatement {
entity,
predicate,
order_by,
limit,
})
}
fn parse_describe_statement(&mut self) -> Result<SqlDescribeStatement, SqlParseError> {
let entity = self.expect_identifier()?;
Ok(SqlDescribeStatement { entity })
}
fn parse_show_indexes_statement(&mut self) -> Result<SqlShowIndexesStatement, SqlParseError> {
let entity = self.expect_identifier()?;
Ok(SqlShowIndexesStatement { entity })
}
fn parse_show_columns_statement(&mut self) -> Result<SqlShowColumnsStatement, SqlParseError> {
let entity = self.expect_identifier()?;
Ok(SqlShowColumnsStatement { entity })
}
}