use alloc::vec::Vec;
use crate::{
Identifier, QualifiedName, Span, Spanned,
keywords::Keyword,
lexer::Token,
parser::{ParseError, Parser},
qualified_name::parse_qualified_name_unreserved,
};
#[derive(Clone, Debug)]
pub enum LockType {
Read(Span),
ReadLocal(Span),
Write(Span),
LowPriorityWrite(Span),
}
impl Spanned for LockType {
fn span(&self) -> Span {
match self {
LockType::Read(s) => s.clone(),
LockType::ReadLocal(s) => s.clone(),
LockType::Write(s) => s.clone(),
LockType::LowPriorityWrite(s) => s.clone(),
}
}
}
#[derive(Clone, Debug)]
pub struct LockMember<'a> {
pub table_name: QualifiedName<'a>,
pub alias: Option<Identifier<'a>>,
pub lock_type: LockType,
}
impl Spanned for LockMember<'_> {
fn span(&self) -> Span {
self.table_name
.span()
.join_span(&self.alias)
.join_span(&self.lock_type)
}
}
#[derive(Clone, Debug)]
pub struct Lock<'a> {
pub lock_span: Span,
pub tables_span: Span,
pub members: Vec<LockMember<'a>>,
}
impl Spanned for Lock<'_> {
fn span(&self) -> Span {
self.lock_span
.join_span(&self.tables_span)
.join_span(&self.members)
}
}
pub(crate) fn parse_lock<'a>(parser: &mut Parser<'a, '_>) -> Result<Lock<'a>, ParseError> {
let lock_span = parser.consume_keyword(Keyword::LOCK)?;
let tables_span = match parser.token {
Token::Ident(_, Keyword::TABLE) => parser.consume_keyword(Keyword::TABLE)?,
Token::Ident(_, Keyword::TABLES) => parser.consume_keyword(Keyword::TABLES)?,
_ => return parser.expected_failure("'TABLE' | 'TABLES'"),
};
let mut members = Vec::new();
loop {
let table_name = parse_qualified_name_unreserved(parser)?;
let alias = if parser.skip_keyword(Keyword::AS).is_some() {
Some(parser.consume_plain_identifier_unreserved()?)
} else if matches!(
parser.token,
Token::Ident(_, kw) if !matches!(kw, Keyword::READ | Keyword::WRITE | Keyword::LOW_PRIORITY)
) {
Some(parser.consume_plain_identifier_unreserved()?)
} else {
None
};
let lock_type = match &parser.token {
Token::Ident(_, Keyword::READ) => {
let read_span = parser.consume_keyword(Keyword::READ)?;
if let Some(local_span) = parser.skip_keyword(Keyword::LOCAL) {
LockType::ReadLocal(read_span.join_span(&local_span))
} else {
LockType::Read(read_span)
}
}
Token::Ident(_, Keyword::LOW_PRIORITY) => {
let span = parser.consume_keywords(&[Keyword::LOW_PRIORITY, Keyword::WRITE])?;
LockType::LowPriorityWrite(span)
}
Token::Ident(_, Keyword::WRITE) => {
let write_span = parser.consume_keyword(Keyword::WRITE)?;
LockType::Write(write_span)
}
_ => return parser.expected_failure("'READ' | 'WRITE'"),
};
members.push(LockMember {
table_name,
alias,
lock_type,
});
if parser.skip_token(Token::Comma).is_none() {
break;
}
}
Ok(Lock {
lock_span,
tables_span,
members,
})
}
#[derive(Clone, Debug)]
pub struct Unlock {
pub unlock_span: Span,
pub tables_span: Span,
}
impl Spanned for Unlock {
fn span(&self) -> Span {
self.unlock_span.join_span(&self.tables_span)
}
}
pub(crate) fn parse_unlock<'a>(parser: &mut Parser<'a, '_>) -> Result<Unlock, ParseError> {
let unlock_span = parser.consume_keyword(Keyword::UNLOCK)?;
let tables_span = match parser.token {
Token::Ident(_, Keyword::TABLE) => parser.consume_keyword(Keyword::TABLE)?,
Token::Ident(_, Keyword::TABLES) => parser.consume_keyword(Keyword::TABLES)?,
_ => return parser.expected_failure("'TABLE' | 'TABLES'"),
};
Ok(Unlock {
unlock_span,
tables_span,
})
}