use crate::keywords::Keyword;
use crate::lexer::Token;
use crate::parser::{ParseError, Parser};
use crate::{Expression, OptSpanned, Span, Spanned};
use alloc::format;
use alloc::vec::Vec;
#[derive(Debug, Clone)]
pub enum FetchDirection {
First(Span),
Next(Span),
}
impl Spanned for FetchDirection {
fn span(&self) -> Span {
match self {
FetchDirection::First(span) => span.clone(),
FetchDirection::Next(span) => span.clone(),
}
}
}
#[derive(Debug, Clone)]
pub struct Fetch<'a> {
pub fetch_span: Span,
pub direction: Option<FetchDirection>,
pub count: Option<Expression<'a>>,
pub row_span: Span,
}
impl<'a> Spanned for Fetch<'a> {
fn span(&self) -> Span {
self.fetch_span
.join_span(&self.direction)
.join_span(&self.count)
.join_span(&self.row_span)
}
}
fn parse_fetch<'a>(parser: &mut Parser<'a, '_>, fetch_span: Span) -> Result<Fetch<'a>, ParseError> {
let direction = match &parser.token {
Token::Ident(_, Keyword::FIRST) => Some(FetchDirection::First(parser.consume())),
Token::Ident(_, Keyword::NEXT) => Some(FetchDirection::Next(parser.consume())),
_ => None,
};
let count = if !matches!(
&parser.token,
Token::Ident(_, Keyword::ROW | Keyword::ROWS | Keyword::ONLY)
) {
Some(crate::expression::parse_expression_unreserved(
parser,
crate::expression::PRIORITY_MAX,
)?)
} else {
None
};
let row_span = match &parser.token {
Token::Ident(_, Keyword::ROW | Keyword::ROWS) => Some(parser.consume()),
_ => None,
};
let row_span = parser.consume_keyword(Keyword::ONLY)?.join_span(&row_span);
Ok(Fetch {
fetch_span,
direction,
count,
row_span,
})
}
#[derive(Debug, Clone)]
pub struct Values<'a> {
pub values_span: Span,
pub rows: Vec<Vec<Expression<'a>>>,
pub order_by: Option<(Span, Vec<(Expression<'a>, crate::select::OrderFlag)>)>, pub limit: Option<(Span, Expression<'a>)>,
pub offset: Option<(Span, Expression<'a>)>,
pub fetch: Option<Fetch<'a>>,
}
impl<'a> Spanned for Values<'a> {
fn span(&self) -> Span {
self.values_span
.join_span(&self.rows)
.join_span(&self.order_by)
.join_span(&self.limit)
.join_span(&self.offset)
.join_span(&self.fetch)
}
}
pub(crate) fn parse_values<'a>(parser: &mut Parser<'a, '_>) -> Result<Values<'a>, ParseError> {
let values_span = parser.consume_keyword(Keyword::VALUES)?;
parser.postgres_only(&values_span);
let mut rows = Vec::new();
loop {
parser.consume_token(Token::LParen)?;
let mut row = Vec::new();
parser.recovered(
"')' or ','",
&|t| matches!(t, Token::RParen | Token::Comma),
|parser| {
loop {
row.push(crate::expression::parse_expression_unreserved(
parser,
crate::expression::PRIORITY_MAX,
)?);
if parser.skip_token(Token::Comma).is_none() {
break;
}
}
Ok(())
},
)?;
parser.consume_token(Token::RParen)?;
rows.push(row);
if parser.skip_token(Token::Comma).is_none() {
break;
}
}
if let Some(first_row) = rows.first() {
let cols = first_row.len();
for row in rows.iter() {
if row.len() != cols {
parser
.err(
format!("This row has {} members", row.len()),
&row.opt_span().unwrap(),
)
.frag(
format!("Expected {} members", cols),
&first_row.opt_span().unwrap(),
);
}
}
}
let order_by = if let Some(order_span) = parser.skip_keyword(Keyword::ORDER) {
let by_span = parser.consume_keyword(Keyword::BY)?;
let span = order_span.join_span(&by_span);
let mut items = Vec::new();
loop {
let expr = crate::expression::parse_expression_unreserved(
parser,
crate::expression::PRIORITY_MAX,
)?;
let order_flag = match &parser.token {
Token::Ident(_, Keyword::ASC) => crate::select::OrderFlag::Asc(parser.consume()),
Token::Ident(_, Keyword::DESC) => crate::select::OrderFlag::Desc(parser.consume()),
_ => crate::select::OrderFlag::None,
};
items.push((expr, order_flag));
if parser.skip_token(Token::Comma).is_none() {
break;
}
}
Some((span, items))
} else {
None
};
let limit = if let Some(limit_span) = parser.skip_keyword(Keyword::LIMIT) {
let expr = crate::expression::parse_expression_unreserved(
parser,
crate::expression::PRIORITY_MAX,
)?;
Some((limit_span, expr))
} else {
None
};
let offset = if let Some(offset_span) = parser.skip_keyword(Keyword::OFFSET) {
let expr = crate::expression::parse_expression_unreserved(
parser,
crate::expression::PRIORITY_MAX,
)?;
if matches!(parser.token, Token::Ident(_, Keyword::ROW | Keyword::ROWS)) {
parser.consume();
}
Some((offset_span, expr))
} else {
None
};
let fetch = if let Some(fetch_span) = parser.skip_keyword(Keyword::FETCH) {
Some(parse_fetch(parser, fetch_span)?)
} else {
None
};
Ok(Values {
values_span,
rows,
order_by,
limit,
offset,
fetch,
})
}