use alloc::vec::Vec;
use crate::{
QualifiedName, SelectExpr, Span, Spanned, TableReference,
expression::{Expression, PRIORITY_MAX, parse_expression_unreserved},
keywords::{Keyword, Restrict},
lexer::Token,
parser::{ParseError, Parser},
qualified_name::parse_qualified_name_unreserved,
select::{OrderFlag, parse_select_expr, parse_table_reference},
};
#[derive(Clone, Debug)]
pub enum DeleteFlag {
LowPriority(Span),
Quick(Span),
Ignore(Span),
}
impl Spanned for DeleteFlag {
fn span(&self) -> Span {
match &self {
DeleteFlag::LowPriority(v) => v.span(),
DeleteFlag::Quick(v) => v.span(),
DeleteFlag::Ignore(v) => v.span(),
}
}
}
#[derive(Clone, Debug)]
pub struct Delete<'a> {
pub delete_span: Span,
pub flags: Vec<DeleteFlag>,
pub from_span: Span,
pub tables: Vec<QualifiedName<'a>>,
pub using: Vec<TableReference<'a>>,
pub where_: Option<(Expression<'a>, Span)>,
pub order_by: Option<(Span, Vec<(Expression<'a>, OrderFlag)>)>,
pub limit: Option<(Span, Option<Expression<'a>>, Expression<'a>)>,
pub returning: Option<(Span, Vec<SelectExpr<'a>>)>,
}
impl<'a> Spanned for Delete<'a> {
fn span(&self) -> Span {
self.delete_span
.join_span(&self.flags)
.join_span(&self.from_span)
.join_span(&self.tables)
.join_span(&self.using)
.join_span(&self.where_)
.join_span(&self.order_by)
.join_span(&self.limit)
.join_span(&self.returning)
}
}
pub(crate) fn parse_delete<'a>(parser: &mut Parser<'a, '_>) -> Result<Delete<'a>, ParseError> {
let delete_span = parser.consume_keyword(Keyword::DELETE)?;
let mut flags = Vec::new();
loop {
match &parser.token {
Token::Ident(_, Keyword::LOW_PRIORITY) => flags.push(DeleteFlag::LowPriority(
parser.consume_keyword(Keyword::LOW_PRIORITY)?,
)),
Token::Ident(_, Keyword::QUICK) => {
flags.push(DeleteFlag::Quick(parser.consume_keyword(Keyword::QUICK)?))
}
Token::Ident(_, Keyword::IGNORE) => {
flags.push(DeleteFlag::Ignore(parser.consume_keyword(Keyword::IGNORE)?))
}
_ => break,
}
}
let mut tables = Vec::new();
let mut using = Vec::new();
let from_span = if let Some(from_span) = parser.skip_keyword(Keyword::FROM) {
loop {
tables.push(parse_qualified_name_unreserved(parser)?);
if matches!(&parser.token, Token::Ident(_, k) if !k.restricted(parser.reserved() | Restrict::EMPTY))
{
parser.consume();
}
if parser.skip_token(Token::Comma).is_none() {
break;
}
}
from_span
} else {
loop {
tables.push(parse_qualified_name_unreserved(parser)?);
if parser.skip_token(Token::Comma).is_none() {
break;
}
}
let from_span = parser.consume_keyword(Keyword::FROM)?;
loop {
using.push(parse_table_reference(parser, Restrict::EMPTY)?);
if parser.skip_token(Token::Comma).is_none() {
break;
}
}
from_span
};
if let Some(using_span) = parser.skip_keyword(Keyword::USING) {
if !using.is_empty() {
parser.err(
"Using not allowed in delete with table names before FROM",
&using_span,
);
}
loop {
using.push(parse_table_reference(parser, Restrict::EMPTY)?);
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 order_by = if let Some(span) = parser.skip_keyword(Keyword::ORDER) {
let span = parser.consume_keyword(Keyword::BY)?.join_span(&span);
let mut order = Vec::new();
loop {
let e = parse_expression_unreserved(parser, PRIORITY_MAX)?;
let f = match &parser.token {
Token::Ident(_, Keyword::ASC) => OrderFlag::Asc(parser.consume()),
Token::Ident(_, Keyword::DESC) => OrderFlag::Desc(parser.consume()),
_ => OrderFlag::None,
};
order.push((e, f));
if parser.skip_token(Token::Comma).is_none() {
break;
}
}
Some((span, order))
} else {
None
};
let limit = if let Some(span) = parser.skip_keyword(Keyword::LIMIT) {
let n = parse_expression_unreserved(parser, PRIORITY_MAX)?;
match parser.token {
Token::Comma => {
parser.consume();
Some((
span,
Some(n),
parse_expression_unreserved(parser, PRIORITY_MAX)?,
))
}
Token::Ident(_, Keyword::OFFSET) => {
parser.consume();
Some((
span,
Some(parse_expression_unreserved(parser, PRIORITY_MAX)?),
n,
))
}
_ => Some((span, None, n)),
}
} else {
None
};
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;
}
}
Some((returning_span, returning_exprs))
} else {
None
};
Ok(Delete {
flags,
delete_span,
tables,
using,
from_span,
where_,
order_by,
limit,
returning,
})
}