use alloc::vec;
use alloc::vec::Vec;
use crate::{
Identifier, SelectExpr, Span, Spanned,
expression::{
Expression, PRIORITY_MAX, parse_expression_or_default, parse_expression_unreserved,
},
keywords::{Keyword, Restrict},
lexer::Token,
parser::{ParseError, Parser},
select::{TableReference, parse_select_expr, parse_table_reference},
span::OptSpanned,
};
#[derive(Clone, Debug)]
pub enum UpdateFlag {
LowPriority(Span),
Ignore(Span),
}
impl Spanned for UpdateFlag {
fn span(&self) -> Span {
match &self {
UpdateFlag::LowPriority(v) => v.span(),
UpdateFlag::Ignore(v) => v.span(),
}
}
}
#[derive(Clone, Debug)]
pub struct Update<'a> {
pub update_span: Span,
pub flags: Vec<UpdateFlag>,
pub tables: Vec<TableReference<'a>>,
pub set_span: Span,
pub set: Vec<(Vec<Identifier<'a>>, Expression<'a>)>,
pub where_: Option<(Expression<'a>, Span)>,
pub from: Option<(Span, Vec<TableReference<'a>>)>,
pub returning: Option<(Span, Vec<SelectExpr<'a>>)>,
}
impl<'a> Spanned for Update<'a> {
fn span(&self) -> Span {
let mut set_span = None;
for (a, b) in &self.set {
set_span = set_span.opt_join_span(a).opt_join_span(b)
}
self.update_span
.join_span(&self.flags)
.join_span(&self.tables)
.join_span(&self.set_span)
.join_span(&set_span)
.join_span(&self.from)
.join_span(&self.where_)
.join_span(&self.returning)
}
}
pub(crate) fn parse_update<'a>(parser: &mut Parser<'a, '_>) -> Result<Update<'a>, ParseError> {
let update_span = parser.consume_keyword(Keyword::UPDATE)?;
let mut flags = Vec::new();
loop {
match &parser.token {
Token::Ident(_, Keyword::LOW_PRIORITY) => flags.push(UpdateFlag::LowPriority(
parser.consume_keyword(Keyword::LOW_PRIORITY)?,
)),
Token::Ident(_, Keyword::IGNORE) => {
flags.push(UpdateFlag::Ignore(parser.consume_keyword(Keyword::IGNORE)?))
}
_ => break,
}
}
let mut tables = Vec::new();
loop {
tables.push(parse_table_reference(parser, Restrict::UPDATE_TABLE)?);
if matches!(parser.token, Token::Ident(_, Keyword::SET)) {
break;
}
if parser.skip_token(Token::Comma).is_none() {
break;
}
}
let set_span = parser.consume_keyword(Keyword::SET)?;
let mut set = Vec::new();
loop {
let mut col = vec![parser.consume_plain_identifier_unreserved()?];
while parser.skip_token(Token::Period).is_some() {
col.push(parser.consume_plain_identifier_unreserved()?);
}
parser.consume_token(Token::Eq)?;
let val = parse_expression_or_default(parser, PRIORITY_MAX)?;
set.push((col, val));
if parser.skip_token(Token::Comma).is_none() {
break;
}
}
let where_ = if let Some(span) = parser.skip_keyword(Keyword::WHERE) {
Some((parse_expression_unreserved(parser, PRIORITY_MAX)?, span))
} else {
None
};
let from = if where_.is_none() {
if let Some(from_span) = parser.skip_keyword(Keyword::FROM) {
parser.postgres_only(&from_span);
let mut from_tables = Vec::new();
loop {
from_tables.push(parse_table_reference(parser, Restrict::EMPTY)?);
if parser.skip_token(Token::Comma).is_none() {
break;
}
}
let where_inner = if let Some(span) = parser.skip_keyword(Keyword::WHERE) {
Some((parse_expression_unreserved(parser, PRIORITY_MAX)?, span))
} else {
None
};
Some((from_span, from_tables, where_inner))
} else {
None
}
} else {
None
};
let (from, where_) = if let Some((from_span, from_tables, where_inner)) = from {
(Some((from_span, from_tables)), where_inner)
} else {
(None, where_)
};
let returning = if let Some(returning_span) = parser.skip_keyword(Keyword::RETURNING) {
let mut returning_exprs = Vec::new();
loop {
returning_exprs.push(parse_select_expr(parser)?);
if parser.skip_token(Token::Comma).is_none() {
break;
}
}
parser.postgres_only(&returning_span);
Some((returning_span, returning_exprs))
} else {
None
};
Ok(Update {
flags,
update_span,
tables,
set_span,
set,
from,
where_,
returning,
})
}