use super::{Error, MetaTracker, ParserInner, Result};
use crate::{
ast::{
AliasedQueryTable, ApplyJoinTable, ApplyTableJoin, AsAlias, ComposeOperation,
ComposeOperator, ContainersTable, CrossInnerJoinedTable, CteSubQuery, CteTable, Duplicates,
Expr, GroupBy, GroupByExprs, GroupByType, HavingCondition, Ident, Identifier,
InnerJoinedTable, InnerTableJoin, NaturalInnerJoinedTable, Node, OuterJoinCondition,
OuterTableJoin, ProjectionItem, ProjectionWildcard, QueryBlock, QueryBody, QuerySelect,
QueryTable, QueryTableAlias, QueryTableExpression, QueryTableType, QueryTableWithJoins,
QueryTables, QueryWindow, QueryWindows, ShardsTable, SubQuery, TableCollection, TableJoin,
WhereCondition, With,
},
parser::{
OpenedParen,
condition::ParseConditionContext,
expression::{ParseCaseIdent, ParseExprContext},
parse_comma_separated, parse_opened_parens, parse_parens,
window::WindowSpecMode,
},
scanner::{Keyword, Reserved, Token, TokenType},
};
impl<'s, M> ParserInner<'s, M>
where
M: MetaTracker<'s>,
{
pub(super) fn parse_query_body(&mut self) -> Result<QueryBody<'s, M::NodeId>> {
let mut block = self.parse_query_block()?;
while let Some(Token {
ttype: TokenType::Keyword(kw),
loc: kw_loc,
}) = self.peek_token()?
{
let op = match kw {
Keyword::UNION => ComposeOperation::Union,
Keyword::INTERSECT => ComposeOperation::Intersect,
Keyword::MINUS => ComposeOperation::Minus,
Keyword::EXCEPT => ComposeOperation::Except,
_ => break,
};
let operation = {
let loc = *kw_loc;
self.next_token()?; Node(op, self.meta_tracker.on_node_start(loc))
};
let all_token = if matches!(
self.peek_token()?,
Some(Token {
ttype: TokenType::Keyword(Keyword::ALL),
..
})
) {
let_next_token!(self, Token { ttype: _, loc });
Some(Node((), self.meta_tracker.on_node_start(loc)))
} else {
None
};
let right = self.parse_query_block()?;
block = QueryBlock::Composed(
block.into(),
ComposeOperator {
operation,
all_token,
},
right.into(),
);
}
let order_by = self.parse_order_by()?;
let row_limit = self.parse_row_limit(if order_by.is_some() {
None
} else {
block_table_alias_mut_if_last_token(&mut block)
})?;
Ok(QueryBody {
block,
order_by,
row_limit,
})
}
pub(super) fn parse_query_with(&mut self) -> Result<Option<With<'s, M::NodeId>>> {
if let Some(Token {
ttype: TokenType::Keyword(Keyword::WITH),
loc,
}) = self.peek_token()?
{
let t_loc = *loc;
self.consume_token()?;
Ok(Some(With {
with_keyword: Node((), self.meta_tracker.on_node_start(t_loc)),
ctes: parse_comma_separated(self, |parser| parser.parse_query_with_cte())?,
}))
} else {
Ok(None)
}
}
fn parse_query_with_cte(&mut self) -> Result<CteTable<'s, M::NodeId>> {
let name = expect_token!(|t = self.next_token() | "an identifier" match {
TokenType::Identifier(ident, _) => Node(ident, self.meta_tracker.on_node_start(t.loc)),
});
let column_aliases = if let Some(Token {
ttype: TokenType::LeftParen,
..
}) = self.peek_token()?
{
let_next_token!(self, opened_paren);
parse_opened_parens(self, opened_paren.into(), |parser, node_id| {
let aliases = parse_comma_separated(parser, |parser| {
expect_token!(|t = parser.next_token()| "an identifier" match {
TokenType::Identifier(ident, _) => {
Ok(Node(ident, parser.meta_tracker.on_node_start(t.loc)))
}
})
})?;
Ok(Some(Node(aliases, node_id)))
})?
} else {
None
};
let as_token = expect_token!(|t = self.next_token()| "the AS keyword" match {
TokenType::Keyword(Keyword::AS) => Node((), self.meta_tracker.on_node_start(t.loc)),
});
let query = expect_token!(|t = self.next_token()| "an opening parenthesis" match {
TokenType::LeftParen => {
parse_opened_parens(self, t.into(), |parser, node_id| {
Ok(Node(parser.parse_query_body()?.into(), node_id))
})
}
})?;
Ok(CteTable::SubQuery(CteSubQuery {
name,
column_aliases,
as_token,
query,
}))
}
fn parse_query_block(&mut self) -> Result<QueryBlock<'s, M::NodeId>> {
expect_token!(
|t = self.next_token()| "a query block" match {
TokenType::LeftParen => parse_opened_parens(self, t.into(), |parser, node_id| {
Ok(QueryBlock::Nested(Node(parser.parse_query_body()?.into(), node_id)))
}),
TokenType::Keyword(Keyword::SELECT) => {
Ok(QueryBlock::Select(self.parse_query_select(t)?))
}
})
}
fn parse_query_select(
&mut self,
select_token: Token<'s>,
) -> Result<QuerySelect<'s, M::NodeId>> {
let select_token_id = self.meta_tracker.on_node_start(select_token.loc);
let hint = self.parse_hint()?;
let filter_token = self.parse_query_duplicates()?;
let projection = self.parse_query_projection()?;
let from = self.parse_query_tables()?;
let mut query_select = QuerySelect {
select_token: Node((), select_token_id),
hint,
duplicates: filter_token,
projection,
from,
selection: None,
group_by: None,
windows: None,
};
query_select.selection = self.parse_where_condition()?;
query_select.group_by = self.parse_group_by()?;
query_select.windows =
self.parse_query_windows(query_select_alias_mut_if_last_token(&mut query_select))?;
Ok(query_select)
}
fn parse_query_projection(&mut self) -> Result<Vec<ProjectionItem<'s, M::NodeId>>> {
let mut items = Vec::new();
items.push(self.parse_query_projection_item()?);
while let Some(Token {
ttype: TokenType::Comma,
..
}) = self.peek_token()?
{
self.consume_token()?;
self.meta_tracker.on_node_end();
items.push(self.parse_query_projection_item()?);
}
if items.is_empty() {
if let Some(t) = self.peek_token()? {
Err(Error::unexpected_token(t, "at least one projection item"))
} else {
unexpected_eof_err!(self, "at least one projection item")
}
} else {
Ok(items)
}
}
fn parse_query_projection_item(&mut self) -> Result<ProjectionItem<'s, M::NodeId>> {
let (expr, mut ctx) = expect_token!(|t = self.peek_token()| "a projection item" match {
TokenType::Star => {
let loc = t.loc;
self.consume_token()?;
let wildcard = Node((), self.meta_tracker.on_node_start(loc));
return Ok(ProjectionItem::Wildcard(ProjectionWildcard::Simple(wildcard)));
}
TokenType::Identifier(_, _) => {
let_next_token!(self, Token { ttype: TokenType::Identifier(ident, reserved), loc });
match reserved {
Some(Reserved::CASE) => {
let case_ident = Node(ident, self.meta_tracker.on_node_start(loc));
let mut parser = self.expr_parser().with_context(ParseExprContext::for_projection_item());
match parser.parse_case_ident(case_ident)? {
ParseCaseIdent::Ident(ident) => {
if parser.context().has_left_over_ident() {
(
Expr::Identifier(Identifier::Simple(ident)),
parser.into_context()
)
} else {
match self.parse_identifier_or_wildcard(ident)? {
IdentifierOrWildcard::Wildcard(wildcard) => {
return Ok(ProjectionItem::Wildcard(wildcard));
}
IdentifierOrWildcard::Identifier(ident) => {
let mut parser = self.expr_parser().with_context(ParseExprContext::for_projection_item());
(parser.parse_with_identifier(ident, loc)?, parser.into_context())
}
}
}
}
ParseCaseIdent::Expr(expr) => {
(expr, parser.into_context())
}
}
}
_ => {
let ident = Node(ident, self.meta_tracker.on_node_start(loc));
match self.parse_identifier_or_wildcard(ident)? {
IdentifierOrWildcard::Wildcard(wildcard) => {
return Ok(ProjectionItem::Wildcard(wildcard));
}
IdentifierOrWildcard::Identifier(ident) => {
let mut parser = self.expr_parser().with_context(ParseExprContext::for_projection_item());
(parser.parse_with_identifier(ident, loc)?, parser.into_context())
}
}
}
}
}
_ => {
let mut parser = self.expr_parser().with_context(ParseExprContext::for_projection_item());
let expr = parser.parse()?;
(expr, parser.into_context())
}
});
let alias = match ctx.take_left_over_ident().map(|ident| AsAlias {
as_token: None,
name: Some(ident),
}) {
Some(alias) => Some(alias),
None => self.parse_projection_item_alias()?,
};
Ok(ProjectionItem::Expr(expr, alias))
}
fn parse_projection_item_alias(&mut self) -> Result<Option<AsAlias<'s, M::NodeId>>> {
let as_token = if let Some(Token {
ttype: TokenType::Keyword(Keyword::AS),
loc,
}) = self.peek_token()?
{
let loc = *loc;
self.consume_token()?; Some(Node((), self.meta_tracker.on_node_start(loc)))
} else {
None
};
let name = if matches!(
self.peek_token()?,
Some(Token {
ttype: TokenType::Identifier(_, _),
..
})
) {
let_next_token!(
self,
Token {
ttype: TokenType::Identifier(ident, _),
loc,
}
);
Some(Node(ident, self.meta_tracker.on_node_start(loc)))
} else {
None
};
Ok(match (as_token, name) {
(None, None) => None,
(as_token, name) => Some(AsAlias { as_token, name }),
})
}
fn parse_identifier_or_wildcard(
&mut self,
init_ident: Node<Ident<'s>, M::NodeId>,
) -> Result<IdentifierOrWildcard<'s, M::NodeId>> {
if !matches!(
self.peek_token()?,
Some(Token {
ttype: TokenType::Dot,
..
})
) {
return Ok(IdentifierOrWildcard::Identifier(Identifier::Simple(
init_ident,
)));
}
self.consume_token()?;
self.meta_tracker.on_node_end();
let (next_ident, next_ident_id) = expect_token! {
|t = self.peek_token()| "an asterisk or identifier" match {
TokenType::Star => {
let loc = t.loc;
self.consume_token()?;
let star_id = self.meta_tracker.on_node_start(loc);
return Ok(IdentifierOrWildcard::Wildcard(
ProjectionWildcard::Qualified(
Identifier::Simple(init_ident),
Node((), star_id))));
}
TokenType::Identifier(_, _) => {
let_next_token!(self, Token { ttype: TokenType::Identifier(ident, _), loc });
(ident, self.meta_tracker.on_node_start(loc))
}
}
};
let mut idents = Vec::with_capacity(3);
idents.push(init_ident);
idents.push(Node(next_ident, next_ident_id));
while let Some(Token {
ttype: TokenType::Dot,
..
}) = self.peek_token()?
{
self.consume_token()?;
self.meta_tracker.on_node_end();
expect_token! {
|t = self.next_token()| "an asterisk or identifier" match {
TokenType::Star => return Ok(
IdentifierOrWildcard::Wildcard(
ProjectionWildcard::Qualified(
Identifier::Qualified(idents),
Node((), self.meta_tracker.on_node_start(t.loc))))),
TokenType::Identifier(ident, _) => {
idents.push(Node(ident, self.meta_tracker.on_node_start(t.loc)));
}
}
};
}
Ok(IdentifierOrWildcard::Identifier(Identifier::Qualified(
idents,
)))
}
fn parse_query_duplicates(&mut self) -> Result<Option<Node<Duplicates, M::NodeId>>> {
if let Some(t) = self.peek_token()? {
let filter = match t.ttype {
TokenType::Keyword(Keyword::DISTINCT) => Duplicates::Distinct,
TokenType::Keyword(Keyword::UNIQUE) => Duplicates::Unique,
TokenType::Keyword(Keyword::ALL) => Duplicates::All,
_ => return Ok(None),
};
let loc = t.loc;
self.consume_token()?;
Ok(Some(Node(filter, self.meta_tracker.on_node_start(loc))))
} else {
Ok(None)
}
}
fn parse_query_tables(&mut self) -> Result<QueryTables<'s, M::NodeId>> {
let from_token = expect_token! {
|t = self.next_token()| "FROM" match {
TokenType::Keyword(Keyword::FROM) => Node((), self.meta_tracker.on_node_start(t.loc)),
}
};
let mut tables = Vec::new();
tables.push(self.parse_query_table()?);
while let Some(Token {
ttype: TokenType::Comma,
..
}) = self.peek_token()?
{
self.consume_token()?;
self.meta_tracker.on_node_end();
tables.push(self.parse_query_table()?);
}
Ok(QueryTables { from_token, tables })
}
fn parse_query_table(&mut self) -> Result<QueryTable<'s, M::NodeId>> {
let mut table = self.parse_aliased_query_table()?;
let mut joins = Vec::<TableJoin<_>>::new();
let mut last_alias = Some(&mut table.alias);
while let Some(join) = self.parse_query_join(last_alias)? {
joins.push(join);
last_alias = joins
.last_mut()
.and_then(join_table_alias_mut_if_last_token);
}
Ok(QueryTable::Table(QueryTableWithJoins { table, joins }))
}
pub(super) fn parse_aliased_query_table(&mut self) -> Result<AliasedQueryTable<'s, M::NodeId>> {
let first_token =
expect_token! { |t = self.next_token()| "a QUERY table" match { _ => t } };
let first_token_id = self.meta_tracker.on_node_start(first_token.loc);
let table = match first_token.ttype {
TokenType::LeftParen => {
parse_opened_parens(
self,
OpenedParen::NodeId(first_token_id),
|parser, node_id| {
if parser.can_parse_query()? {
Ok(QueryTableType::Expr(QueryTableExpression::SubQuery(
SubQuery {
lateral_token: None,
query: Node(parser.parse_query()?.into(), node_id),
},
)))
} else {
let nested = parser.parse_query_table()?;
Ok(QueryTableType::Nested(Node(nested.into(), node_id)))
}
},
)?
}
TokenType::Identifier(_, reserved) => {
let next_is_paren = matches!(
self.peek_token()?,
Some(Token {
ttype: TokenType::LeftParen,
..
})
);
if next_is_paren && let Some(reserved) = reserved {
match reserved {
Reserved::ONLY => parse_parens(self, |parser, node_id| {
Ok(QueryTableType::Only {
only_token: Node((), first_token_id),
expression: Node(
parser.parse_query_table_expression(None)?,
node_id,
),
})
})?,
Reserved::CONTAINERS => parse_parens(self, |parser, node_id| {
let ident = parser.parse_identifier()?;
Ok(QueryTableType::Containers(ContainersTable {
containers_token: Node((), first_token_id),
table: Node(ident, node_id),
}))
})?,
Reserved::SHARDS => parse_parens(self, |parser, node_id| {
let ident = parser.parse_identifier()?;
Ok(QueryTableType::Shards(ShardsTable {
shards_token: Node((), first_token_id),
table: Node(ident, node_id),
}))
})?,
_ => {
QueryTableType::Expr(self.parse_query_table_expression(Some(Node(
first_token,
first_token_id,
)))?)
}
}
} else {
QueryTableType::Expr(
self.parse_query_table_expression(Some(Node(first_token, first_token_id)))?,
)
}
}
TokenType::Keyword(Keyword::TABLE) => QueryTableType::Expr(
self.parse_query_table_expression(Some(Node(first_token, first_token_id)))?,
),
_ => {
return Err(Error::unexpected_token(
first_token,
"an opening parenthesis or an identifier",
));
}
};
let alias = if let Some(Token {
ttype: TokenType::Identifier(_, _),
..
}) = self.peek_token()?
{
Some(self.parse_ident()?)
} else {
None
};
Ok(AliasedQueryTable { table, alias })
}
fn parse_query_table_expression(
&mut self,
token: Option<Node<Token<'s>, M::NodeId>>,
) -> Result<QueryTableExpression<'s, M::NodeId>> {
let Node(token, token_id) = if let Some(token_node) = token {
token_node
} else {
let t = self
.next_token()
.transpose()
.unwrap_or_else(|| unexpected_eof_err!(self, "a query table expression"))?;
let t_id = self.meta_tracker.on_node_start(t.loc);
Node(t, t_id)
};
match token.ttype {
TokenType::Keyword(Keyword::TABLE) => {
let collection = parse_parens(self, |parser, node_id| {
Ok(Node(parser.parse_expr()?, node_id))
})?;
let as_outer_join = if let Some(Token {
ttype: TokenType::LeftParen,
..
}) = self.peek_token()?
{
Some(parse_parens(self, |parser, node_id| {
expect_token!(|t = parser.next_token()| "a PLUS symbol" match {
TokenType::Plus => Ok(Node(Node((), parser.meta_tracker.on_node_start(t.loc)), node_id))
})
})?)
} else {
None
};
Ok(QueryTableExpression::Table(TableCollection {
table_token: Node((), token_id),
expr: collection,
as_outer_join,
}))
}
TokenType::Identifier(ident, reserved) => {
if let Some(Reserved::LATERAL) = reserved {
parse_parens(self, |parser, node_id| {
Ok(QueryTableExpression::SubQuery(SubQuery {
lateral_token: Some(Node((), token_id)),
query: Node(parser.parse_query()?.into(), node_id),
}))
})
} else {
let name = self.parse_identifier_(Node(ident, token_id))?;
Ok(QueryTableExpression::Name(name))
}
}
TokenType::LeftParen => {
parse_opened_parens(self, OpenedParen::NodeId(token_id), |parser, node_id| {
Ok(QueryTableExpression::SubQuery(SubQuery {
lateral_token: None,
query: Node(parser.parse_query()?.into(), node_id),
}))
})
}
_ => Err(Error::unexpected_token(
token,
"an identifier or a sub-query",
)),
}
}
fn parse_where_condition(&mut self) -> Result<Option<WhereCondition<'s, M::NodeId>>> {
if let Some(Token {
ttype: TokenType::Keyword(Keyword::WHERE),
loc,
}) = self.peek_token()?
{
let loc = *loc;
let where_id = self.meta_tracker.on_node_start(loc);
self.consume_token()?;
let condition = self.condition_parser().parse_condition()?;
Ok(Some(WhereCondition {
where_token: Node((), where_id),
condition,
}))
} else {
Ok(None)
}
}
fn parse_group_by(&mut self) -> Result<Option<GroupBy<'s, M::NodeId>>> {
if let Some(Token {
ttype: TokenType::Keyword(Keyword::GROUP),
loc,
}) = self.peek_token()?
{
let group_token = {
let loc = *loc;
self.consume_token()?;
Node((), self.meta_tracker.on_node_start(loc))
};
let by_token = expect_token!(|t = self.next_token()| "the BY keyword" match {
TokenType::Keyword(Keyword::BY) => Node((), self.meta_tracker.on_node_start(t.loc)),
});
let group_type = GroupByType::Exprs(
if let Some(Token {
ttype: TokenType::LeftParen,
..
}) = self.peek_token()?
{
let paren_token = self.next_token()?.expect("missing token");
parse_opened_parens(self, paren_token.into(), |parser, node_id| {
if let Some(Token {
ttype: TokenType::RightParen,
loc: right_paren_loc,
}) = parser.peek_token()?
{
let right_paren_loc = *right_paren_loc;
let right_paren_id = parser.meta_tracker.on_node_start(right_paren_loc);
parser.meta_tracker.on_node_end();
Ok(GroupByExprs::All(Node(Node((), right_paren_id), node_id)))
} else {
Ok(GroupByExprs::Nested(Node(
parse_comma_separated(parser, |parser| parser.parse_expr())?,
node_id,
)))
}
})?
} else {
GroupByExprs::Exprs(parse_comma_separated(self, |parser| parser.parse_expr())?)
},
);
let having = if let Some(Token {
ttype: TokenType::Keyword(Keyword::HAVING),
..
}) = self.peek_token()?
{
let having_token = self.next_token()?.expect("missing token");
let having_token = Node((), self.meta_tracker.on_node_start(having_token.loc));
let condition = self
.condition_parser()
.with_context(ParseConditionContext::ForHaving)
.parse_condition()?;
Some(HavingCondition {
having_token,
condition,
})
} else {
None
};
Ok(Some(GroupBy {
group_token,
by_token,
group_type,
having,
}))
} else {
Ok(None)
}
}
fn parse_query_windows<'a>(
&mut self,
steal_alias_if_needed: Option<&'a mut QueryTableAlias<'s, M::NodeId>>,
) -> Result<Option<QueryWindows<'s, M::NodeId>>> {
let window_token = if let Some(Token {
ttype: TokenType::Identifier(_, maybe_reserved),
loc,
}) = self.peek_token()?
{
if let Some(Reserved::WINDOW) = *maybe_reserved {
let loc = *loc;
self.consume_token()?;
Node((), self.meta_tracker.on_node_start(loc))
} else if let Some(steal_alias_if_needed) = steal_alias_if_needed
&& let Some(Node(alias, _)) = steal_alias_if_needed
&& Reserved::WINDOW.matches(alias)
{
let Node(_, alias_id) = steal_alias_if_needed.take().expect("missing alias");
Node((), alias_id)
} else {
return Ok(None);
}
} else {
return Ok(None);
};
let mut windows = Vec::new();
loop {
let name = self.parse_ident()?;
let as_token = expect_token!(|t = self.next_token()| "the AS keyword" match {
TokenType::Keyword(Keyword::AS) => Node((), self.meta_tracker.on_node_start(t.loc)),
});
let window = self.parse_window_spec(WindowSpecMode::Query)?;
windows.push(QueryWindow {
name,
as_token,
window,
});
if let Some(Token {
ttype: TokenType::Comma,
..
}) = self.peek_token()?
{
self.consume_token()?;
self.meta_tracker.on_node_end();
} else {
break;
}
}
Ok(Some(QueryWindows {
window_token,
windows,
}))
}
}
enum IdentifierOrWildcard<'s, ID> {
Identifier(Identifier<'s, ID>),
Wildcard(ProjectionWildcard<'s, ID>),
}
fn join_table_alias_mut_if_last_token<'j, 's, ID>(
join: &'j mut TableJoin<'s, ID>,
) -> Option<&'j mut QueryTableAlias<'s, ID>> {
match join {
TableJoin::Inner(InnerTableJoin::Inner(InnerJoinedTable { .. })) => None,
TableJoin::Inner(InnerTableJoin::Cross(CrossInnerJoinedTable {
cross_token: _,
join_token: _,
table,
})) => Some(&mut table.alias),
TableJoin::Inner(InnerTableJoin::Natural(NaturalInnerJoinedTable {
natural_token: _,
inner_token: _,
join_token: _,
table,
})) => Some(&mut table.alias),
TableJoin::Outer(OuterTableJoin {
type_token: _,
outer_token: _,
join_token: _,
table,
partition_by,
condition,
}) => {
if partition_by.is_some() {
None
} else {
match condition {
OuterJoinCondition::Natural { .. } => Some(&mut table.alias),
OuterJoinCondition::Regular(None) => Some(&mut table.alias),
OuterJoinCondition::Regular(Some(_)) => None,
}
}
}
TableJoin::Apply(ApplyTableJoin { table, .. }) => match table {
ApplyJoinTable::Table(table) => Some(&mut table.alias),
ApplyJoinTable::Expr(_) => None,
},
}
}
fn block_table_alias_mut_if_last_token<'b, 's, ID>(
mut block: &'b mut QueryBlock<'s, ID>,
) -> Option<&'b mut QueryTableAlias<'s, ID>> {
let last_block = loop {
match block {
QueryBlock::Nested(_) => {
return None;
}
QueryBlock::Composed(_, _, last) => {
block = &mut **last;
}
QueryBlock::Select(_) => break block,
}
};
if let QueryBlock::Select(query_select) = last_block {
query_select_alias_mut_if_last_token(query_select)
} else {
None
}
}
fn query_select_alias_mut_if_last_token<'b, 's, ID>(
query_select: &'b mut QuerySelect<'s, ID>,
) -> Option<&'b mut QueryTableAlias<'s, ID>> {
let QuerySelect {
select_token: _,
hint: _,
duplicates: _,
projection: _,
from,
selection,
group_by,
windows,
} = query_select;
if selection.is_none()
&& group_by.is_none()
&& windows.is_none()
&& let Some(t) = from.tables.last_mut()
{
match t {
QueryTable::Table(table_with_joins) => {
if let Some(join) = table_with_joins.joins.last_mut() {
join_table_alias_mut_if_last_token(join)
} else {
Some(&mut table_with_joins.table.alias)
}
}
}
} else {
None
}
}