use super::{MetaTracker, ParserInner, Result};
use crate::{
ast::{
FetchAmount, FetchAmountSpec, FetchFirstKeyword, Node, QueryTableAlias, RowKeyword,
RowLimit, RowLimitFetch, RowLimitOffset,
},
parser::expression::ExprParser,
scanner::{Keyword, Reserved, Token, TokenType},
};
impl<'s, M> ParserInner<'s, M>
where
M: MetaTracker<'s>,
{
pub(super) fn parse_row_limit<'a>(
&mut self,
mut steal_alias_if_needed: Option<&'a mut QueryTableAlias<'s, M::NodeId>>,
) -> Result<RowLimit<'s, M::NodeId>> {
let offset = if let Some(offset_token) =
self.next_token_if_reserved_offset(&mut steal_alias_if_needed)?
{
let offset_value = self.parse_expr()?;
let row_token = {
let (kw, loc) = expect_reserved!(|t = self.next_token()| "the ROW or ROWS keyword" match {
Reserved::ROW => (RowKeyword::Row, t.loc),
Reserved::ROWS => (RowKeyword::Rows, t.loc),
});
Node(kw, self.meta_tracker.on_node_start(loc))
};
steal_alias_if_needed = None;
Some(RowLimitOffset {
offset_token,
offset_value,
row_token,
})
} else {
None
};
let fetch = if let Some(fetch_token) =
self.next_token_if_reserved_fetch(&mut steal_alias_if_needed)?
{
let first_token = {
let (kw, loc) = expect_reserved!(|t = self.next_token()| "the FIRST or NEXT keyword" match {
Reserved::FIRST => (FetchFirstKeyword::First, t.loc),
Reserved::NEXT => (FetchFirstKeyword::Next, t.loc),
});
Node(kw, self.meta_tracker.on_node_start(loc))
};
let fetch_amount = {
let amount = self.parse_expr()?;
if let Some(Token {
ttype: TokenType::Identifier(_, Some(Reserved::PERCENT)),
loc,
}) = self.peek_token()?
{
let loc = *loc;
self.consume_token()?;
FetchAmount::Percent {
amount,
percent_token: Node((), self.meta_tracker.on_node_start(loc)),
}
} else {
FetchAmount::Count(amount)
}
};
let row_token = {
let (kw, loc) = expect_reserved!(|t = self.next_token()| "the ROW or ROWS keyword" match {
Reserved::ROW => (RowKeyword::Row, t.loc),
Reserved::ROWS => (RowKeyword::Rows, t.loc),
});
Node(kw, self.meta_tracker.on_node_start(loc))
};
let fetch_amount_spec = expect_token!(|t = self.next_token()| "the ONLY or WITH TIES keywords" match {
TokenType::Identifier(_, Some(Reserved::ONLY)) => FetchAmountSpec::Only { only_token: Node((), self.meta_tracker.on_node_start(t.loc) ) },
TokenType::Keyword(Keyword::WITH) => {
let with_token = Node((), self.meta_tracker.on_node_start(t.loc));
let ties_token = expect_reserved!(|t = self.next_token()| "the TIES keyword" match {
Reserved::TIES => Node((), self.meta_tracker.on_node_start(t.loc)),
});
FetchAmountSpec::WithTies { with_token, ties_token }
}
});
Some(RowLimitFetch {
fetch_token,
first_token,
row_token,
fetch_amount,
fetch_amount_spec,
})
} else {
None
};
Ok(RowLimit { offset, fetch })
}
fn next_token_if_reserved_offset<'a>(
&mut self,
steal_alias_if_needed: &mut Option<&'a mut QueryTableAlias<'s, M::NodeId>>,
) -> Result<Option<Node<(), M::NodeId>>> {
let try_stealing_alias = if let Some(t) = self.peek_token()? {
match &t.ttype {
TokenType::Identifier(_, Some(Reserved::OFFSET)) => {
let loc = t.loc;
self.consume_token()?;
return Ok(Some(Node((), self.meta_tracker.on_node_start(loc))));
}
_ => ExprParser::<M>::is_start_token(t),
}
} else {
false
};
if try_stealing_alias
&& let Some(steal_alias_if_needed) = steal_alias_if_needed
&& let Some(Node(ident, ident_id)) = steal_alias_if_needed
&& Reserved::OFFSET.matches(ident)
{
let node_id = *ident_id;
steal_alias_if_needed.take();
return Ok(Some(Node((), node_id)));
}
Ok(None)
}
fn next_token_if_reserved_fetch<'a>(
&mut self,
steal_alias_if_needed: &mut Option<&'a mut QueryTableAlias<'s, M::NodeId>>,
) -> Result<Option<Node<(), M::NodeId>>> {
let try_stealing_alias = if let Some(t) = self.peek_token()? {
match &t.ttype {
TokenType::Identifier(_, Some(Reserved::FETCH)) => {
let loc = t.loc;
self.consume_token()?;
return Ok(Some(Node((), self.meta_tracker.on_node_start(loc))));
}
TokenType::Identifier(_, Some(Reserved::FIRST | Reserved::NEXT)) => true,
_ => false,
}
} else {
false
};
if try_stealing_alias
&& let Some(steal_alias_if_needed) = steal_alias_if_needed
&& let Some(Node(ident, ident_id)) = steal_alias_if_needed
&& Reserved::FETCH.matches(ident)
{
let node_id = *ident_id;
steal_alias_if_needed.take();
return Ok(Some(Node((), node_id)));
}
Ok(None)
}
}