use alloc::{boxed::Box, vec::Vec};
use crate::{
keywords::Keyword,
lexer::Token,
parser::{ParseError, Parser},
statement::parse_statement,
Identifier, Span, Spanned, Statement,
};
#[derive(Clone, Debug)]
pub struct WithBlock<'a> {
pub identifier: Identifier<'a>,
pub as_span: Span,
pub lparen_span: Span,
pub statement: Statement<'a>,
pub rparen_span: Span,
}
impl<'a> Spanned for WithBlock<'a> {
fn span(&self) -> Span {
self.identifier
.span()
.join_span(&self.as_span)
.join_span(&self.lparen_span)
.join_span(&self.statement)
.join_span(&self.rparen_span)
}
}
#[derive(Clone, Debug)]
pub struct WithQuery<'a> {
pub with_span: Span,
pub with_blocks: Vec<WithBlock<'a>>,
pub statement: Box<Statement<'a>>,
}
impl<'a> Spanned for WithQuery<'a> {
fn span(&self) -> Span {
self.with_span
.join_span(&self.with_blocks)
.join_span(&self.statement)
}
}
pub(crate) fn parse_with_query<'a>(
parser: &mut Parser<'a, '_>,
) -> Result<WithQuery<'a>, ParseError> {
let with_span = parser.consume_keyword(Keyword::WITH)?;
let mut with_blocks = Vec::new();
loop {
let identifier = parser.consume_plain_identifier()?;
let as_span = parser.consume_keyword(Keyword::AS)?;
let lparen_span = parser.consume_token(Token::LParen)?;
let statement =
parser.recovered(
"')'",
&|t| t == &Token::RParen,
|parser| match parse_statement(parser)? {
Some(v) => Ok(Some(v)),
None => {
parser.expected_error("Statement");
Ok(None)
}
},
)?;
let rparen_span = parser.consume_token(Token::RParen)?;
let statement = match statement {
Some(v) => {
if !matches!(
&v,
Statement::Select(_)
| Statement::InsertReplace(_)
| Statement::Update(_)
| Statement::Delete(_)
) {
parser.err(
"Only SELECT, INSERT, UPDATE or DELETE allowed within WITH query",
&v.span(),
);
}
v
}
None => Statement::Begin(lparen_span.clone()),
};
with_blocks.push(WithBlock {
identifier,
as_span,
lparen_span,
statement,
rparen_span,
});
if parser.skip_token(Token::Comma).is_none() {
break;
}
}
let statement = match parse_statement(parser)? {
Some(v) => {
if !matches!(
&v,
Statement::Select(_)
| Statement::InsertReplace(_)
| Statement::Update(_)
| Statement::Delete(_)
) {
parser.err(
"Only SELECT, INSERT, UPDATE or DELETE allowed as WITH query",
&v.span(),
);
}
Box::new(v)
}
None => parser.expected_failure("Statement")?,
};
let res = WithQuery {
with_span,
with_blocks,
statement,
};
if !parser.options.dialect.is_postgresql() {
parser.err("WITH statements only supported by postgresql", &res.span());
}
Ok(res)
}