use super::*;
#[derive(Default)]
struct ConflictClause {
on_duplicate: bool,
update_columns: Vec<Identifier>,
update_expressions: Vec<Expression>,
do_nothing: bool,
conflict_target: Vec<Identifier>,
}
impl Parser {
pub(super) fn parse_insert_statement(&mut self) -> Option<InsertStatement> {
let token = self.cur_token.clone();
if !self.expect_keyword("INTO") {
return None;
}
if !self.expect_peek_identifier_like() {
return None;
}
let table_name = self.parse_relation_identifier_current()?;
let mut columns = Vec::new();
if self.peek_token_is_punctuator("(") {
self.next_token(); columns = self.parse_identifier_list();
if self.peek_token_is_punctuator(".") {
self.add_error(format!(
"{}: navigation paths cannot be INSERT target columns",
NavigationErrorCode::ReadOnly
));
return None;
}
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!("expected ')' at {}", self.cur_token.position));
return None;
}
}
if self.peek_token_is_keyword("SELECT") {
self.next_token(); let select_stmt = self.parse_select_statement()?;
let conflict = self.parse_conflict_clause();
let returning = self.parse_returning_clause();
return Some(InsertStatement {
token,
table_name,
columns,
values: Vec::new(),
select: Some(Box::new(select_stmt)),
on_duplicate: conflict.on_duplicate,
update_columns: conflict.update_columns,
update_expressions: conflict.update_expressions,
do_nothing: conflict.do_nothing,
conflict_target: conflict.conflict_target,
returning,
});
}
if self.peek_token_is_keyword("WITH") {
self.next_token(); let with_clause = self.parse_with_clause()?;
if !self.expect_keyword("SELECT") {
return None;
}
let mut select_stmt = self.parse_select_statement()?;
select_stmt.with = Some(with_clause);
let conflict = self.parse_conflict_clause();
let returning = self.parse_returning_clause();
return Some(InsertStatement {
token,
table_name,
columns,
values: Vec::new(),
select: Some(Box::new(select_stmt)),
on_duplicate: conflict.on_duplicate,
update_columns: conflict.update_columns,
update_expressions: conflict.update_expressions,
do_nothing: conflict.do_nothing,
conflict_target: conflict.conflict_target,
returning,
});
}
if !self.expect_keyword("VALUES") {
return None;
}
let values = self.parse_value_lists()?;
let conflict = self.parse_conflict_clause();
let returning = self.parse_returning_clause();
Some(InsertStatement {
token,
table_name,
columns,
values,
select: None,
on_duplicate: conflict.on_duplicate,
update_columns: conflict.update_columns,
update_expressions: conflict.update_expressions,
do_nothing: conflict.do_nothing,
conflict_target: conflict.conflict_target,
returning,
})
}
fn parse_conflict_clause(&mut self) -> ConflictClause {
if !self.peek_token_is_keyword("ON") {
return ConflictClause::default();
}
self.next_token();
if self.peek_token_is_keyword("DUPLICATE") {
self.next_token(); if !self.expect_keyword("KEY") {
return ConflictClause::default();
}
if !self.expect_keyword("UPDATE") {
return ConflictClause::default();
}
let (update_columns, update_expressions) = self.parse_update_assignments();
ConflictClause {
on_duplicate: true,
update_columns,
update_expressions,
..Default::default()
}
} else if self.peek_token_is_keyword("CONFLICT") {
self.next_token();
let conflict_target = if self.peek_token_is_punctuator("(") {
self.next_token(); let cols = self.parse_identifier_list();
if self.peek_token_is_punctuator(".") {
self.add_error(format!(
"{}: navigation paths cannot be conflict target columns",
NavigationErrorCode::ReadOnly
));
return ConflictClause::default();
}
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!(
"expected ')' after conflict target at {}",
self.cur_token.position
));
return ConflictClause::default();
}
cols
} else {
Vec::new()
};
if !self.expect_keyword("DO") {
return ConflictClause::default();
}
if self.peek_token_is_keyword("NOTHING") {
self.next_token(); ConflictClause {
do_nothing: true,
conflict_target,
..Default::default()
}
} else if self.peek_token_is_keyword("UPDATE") {
self.next_token(); if !self.expect_keyword("SET") {
return ConflictClause::default();
}
let (update_columns, update_expressions) = self.parse_update_assignments();
ConflictClause {
on_duplicate: true,
update_columns,
update_expressions,
conflict_target,
do_nothing: false,
}
} else {
self.add_error(format!(
"expected NOTHING or UPDATE after DO, got {}",
Self::format_token_for_error(&self.peek_token)
));
ConflictClause::default()
}
} else {
self.add_error(format!(
"expected DUPLICATE or CONFLICT after ON, got {}",
Self::format_token_for_error(&self.peek_token)
));
ConflictClause::default()
}
}
pub(super) fn parse_update_assignments(&mut self) -> (Vec<Identifier>, Vec<Expression>) {
let mut update_columns = Vec::new();
let mut update_expressions = Vec::new();
loop {
if !self.peek_token_is(TokenType::Identifier) && !self.peek_token_is(TokenType::Keyword)
{
self.add_error(format!(
"expected column name, got {}",
Self::format_token_for_error(&self.peek_token)
));
return (update_columns, update_expressions);
}
self.next_token();
update_columns.push(self.cur_token_as_column_identifier());
if self.peek_token_is_punctuator(".") {
self.add_error(format!(
"{}: navigation paths cannot be assignment targets",
NavigationErrorCode::ReadOnly
));
return (update_columns, update_expressions);
}
if !self.expect_peek(TokenType::Operator) || self.cur_token.literal != "=" {
self.add_error(format!("expected '=' at {}", self.cur_token.position));
return (update_columns, update_expressions);
}
self.next_token();
if let Some(expr) = self.parse_expression(Precedence::Lowest) {
update_expressions.push(expr);
} else {
return (update_columns, update_expressions);
}
if !self.peek_token_is_punctuator(",") {
break;
}
self.next_token(); }
(update_columns, update_expressions)
}
pub(super) fn parse_returning_clause(&mut self) -> Vec<Expression> {
if !self.peek_token_is_keyword("RETURNING") {
return Vec::new();
}
self.next_token();
self.parse_expression_list()
}
pub(super) fn parse_value_lists(&mut self) -> Option<Vec<Vec<Expression>>> {
let mut value_lists = Vec::with_capacity(1);
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
self.add_error(format!("expected '(' at {}", self.cur_token.position));
return None;
}
let values = self.parse_expression_list();
value_lists.push(values);
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!("expected ')' at {}", self.cur_token.position));
return None;
}
while self.peek_token_is_punctuator(",") {
self.next_token();
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
self.add_error(format!("expected '(' at {}", self.cur_token.position));
return None;
}
let values = self.parse_expression_list();
value_lists.push(values);
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!("expected ')' at {}", self.cur_token.position));
return None;
}
}
Some(value_lists)
}
pub(super) fn parse_update_statement(&mut self) -> Option<UpdateStatement> {
let token = self.cur_token.clone();
if !self.expect_peek_identifier_like() {
return None;
}
let table_name = self.parse_relation_identifier_current()?;
if !self.expect_keyword("SET") {
return None;
}
let mut updates = FxHashMap::default();
loop {
if !self.peek_token_is(TokenType::Identifier) && !self.peek_token_is(TokenType::Keyword)
{
self.add_error(format!(
"expected column name in SET clause, got {}",
Self::format_token_for_error(&self.peek_token)
));
return None;
}
self.next_token();
let column_name = self.cur_token_as_column_identifier();
let column_name = column_name.value;
if self.peek_token_is_punctuator(".") {
self.add_error(format!(
"{}: navigation paths cannot be assignment targets",
NavigationErrorCode::ReadOnly
));
return None;
}
if !self.expect_peek(TokenType::Operator) || self.cur_token.literal != "=" {
self.add_error(format!("expected '=' at {}", self.cur_token.position));
return None;
}
self.next_token();
let value_expr = self.parse_expression(Precedence::Lowest)?;
if updates
.keys()
.any(|existing: &SmartString| existing.eq_ignore_ascii_case(&column_name))
{
self.add_error(format!(
"duplicate column '{}' in UPDATE SET clause",
column_name
));
return None;
}
updates.insert(column_name, value_expr);
if !self.peek_token_is_punctuator(",") {
break;
}
self.next_token(); }
let where_clause = if self.peek_token_is_keyword("WHERE") {
self.next_token(); self.current_clause = "WHERE".to_string();
self.next_token();
Some(Box::new(self.parse_expression(Precedence::Lowest)?))
} else {
None
};
self.current_clause.clear();
let returning = self.parse_returning_clause();
Some(UpdateStatement {
token,
table_name,
updates,
where_clause,
returning,
})
}
pub(super) fn parse_delete_statement(&mut self) -> Option<DeleteStatement> {
let token = self.cur_token.clone();
if !self.expect_keyword("FROM") {
return None;
}
if !self.peek_token_is_identifier_like() {
self.add_error(format!(
"expected table name after DELETE FROM, got {}",
Self::format_token_for_error(&self.peek_token)
));
return None;
}
self.next_token();
let table_name = self.parse_relation_identifier_current()?;
let alias = if self.peek_token_is_keyword("AS") {
self.next_token(); if !self.expect_peek_identifier_like() {
return None;
}
Some(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
))
} else if self.peek_token_is_identifier_like()
&& !self.peek_token_is_keyword("WHERE")
&& !self.peek_token_is_keyword("RETURNING")
{
self.next_token();
Some(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
))
} else {
None
};
let where_clause = if self.peek_token_is_keyword("WHERE") {
self.next_token(); self.current_clause = "WHERE".to_string();
self.next_token();
Some(Box::new(self.parse_expression(Precedence::Lowest)?))
} else {
None
};
self.current_clause.clear();
let returning = self.parse_returning_clause();
Some(DeleteStatement {
token,
table_name,
alias,
where_clause,
returning,
})
}
pub(super) fn parse_truncate_statement(&mut self) -> Option<TruncateStatement> {
let token = self.cur_token.clone();
if self.peek_token_is_keyword("TABLE") {
self.next_token(); }
if !self.expect_peek_identifier_like() {
return None;
}
let table_name = self.parse_relation_identifier_current()?;
Some(TruncateStatement { token, table_name })
}
pub(super) fn parse_vacuum_statement(&mut self) -> Option<VacuumStatement> {
let token = self.cur_token.clone();
let table_name = if self.peek_token_is_identifier_like() {
self.next_token();
Some(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
))
} else {
None
};
Some(VacuumStatement { token, table_name })
}
}