Skip to content

Commit

Permalink
feat: [sqlparser] Support join multiple Table
Browse files Browse the repository at this point in the history
  • Loading branch information
holicc committed Jan 16, 2025
1 parent 45621e3 commit 89b4390
Showing 1 changed file with 197 additions and 38 deletions.
235 changes: 197 additions & 38 deletions sqlparser/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -659,48 +659,33 @@ impl<'a> Parser<'a> {
}

fn parse_from_statment(&mut self) -> Result<Vec<ast::From>> {
// parse subquery
if self.next_if_token(TokenType::LParen).is_some() {
self.next_except(TokenType::Keyword(Keyword::Select))?;

let subquery = self.parse_select_statement()?;
self.next_except(TokenType::RParen)?;

return Ok(vec![ast::From::SubQuery {
query: Box::new(subquery),
alias: self.parse_alias()?,
}]);
}
let relation = self.parse_table_reference()?;
let mut table_ref = vec![relation];
loop {
if let Some(join_type) = self.parse_join_type()? {
let right = self.parse_table_reference()?;
let on = if join_type == ast::JoinType::Cross {
None
} else {
self.next_except(TokenType::Keyword(Keyword::On))?;
Some(self.parse_expression(0)?)
};

// parse table refereneces
let mut table_ref = vec![];
let left = table_ref.pop().ok_or(Error::ParserError("no left table".to_string()))?;

loop {
table_ref.push(self.parse_table_reference()?);
if self.next_if_token(TokenType::Comma).is_none() {
table_ref.push(ast::From::Join {
left: Box::new(left),
right: Box::new(right),
on,
join_type,
});
} else if self.next_if_token(TokenType::Comma).is_some() {
table_ref.push(self.parse_table_reference()?);
} else {
break;
}
}

// parse join cause
// TODO handle multiple join
if let Some(join_type) = self.parse_join_type()? {
let mut right = self.parse_from_statment()?;
let on = if join_type == ast::JoinType::Cross {
None
} else {
self.next_except(TokenType::Keyword(Keyword::On))?;
Some(self.parse_expression(0)?)
};

return Ok(vec![ast::From::Join {
join_type,
left: Box::new(table_ref.remove(0)),
right: Box::new(right.remove(0)),
on,
}]);
}

Ok(table_ref)
}

Expand All @@ -725,6 +710,17 @@ impl<'a> Parser<'a> {
}

fn parse_table_reference(&mut self) -> Result<ast::From> {
if self.next_if_token(TokenType::LParen).is_some() {
self.next_except(TokenType::Keyword(Keyword::Select))?;
let subquery = self.parse_select_statement()?;
self.next_except(TokenType::RParen)?;

return Ok(ast::From::SubQuery {
query: Box::new(subquery),
alias: self.parse_alias()?,
});
}

let mut table_name = self.next_token().map(|i| i.literal)?;
let mut is_table_function = false;
let mut args = Vec::new();
Expand Down Expand Up @@ -1282,8 +1278,8 @@ mod tests {

use super::Parser;
use crate::ast::{
self, Assignment, CopyOption, CopySource, CopyTarget, DateTimeField, Expression, FunctionArgument, Ident,
Select, SelectItem, Statement,
self, Assignment, BinaryOperator, CopyOption, CopySource, CopyTarget, DateTimeField, Expression,
FunctionArgument, Ident, Select, SelectItem, Statement,
};
use crate::datatype::DataType;
use crate::error::Result;
Expand All @@ -1294,6 +1290,169 @@ mod tests {
assert_eq!(result, stmt, "Runing SQL: {}", sql);
}

#[test]
fn test_join_multiple_table() {
assert_stmt_eq(
"SELECT * FROM a LEFT JOIN b ON a.id = b.id LEFT JOIN c on c.id = b.id",
Statement::Select(Box::new(Select {
with: None,
distinct: None,
columns: vec![SelectItem::Wildcard],
from: vec![ast::From::Join {
left: Box::new(ast::From::Join {
left: Box::new(ast::From::Table {
name: "a".to_owned(),
alias: None,
}),
right: Box::new(ast::From::Table {
name: "b".to_owned(),
alias: None,
}),
on: Some(Expression::BinaryOperator(BinaryOperator::Eq(
Box::new(Expression::CompoundIdentifier(vec![
Ident {
value: "a".to_owned(),
quote_style: None,
},
Ident {
value: "id".to_owned(),
quote_style: None,
},
])),
Box::new(Expression::CompoundIdentifier(vec![
Ident {
value: "b".to_owned(),
quote_style: None,
},
Ident {
value: "id".to_owned(),
quote_style: None,
},
])),
))),
join_type: ast::JoinType::Left,
}),
right: Box::new(ast::From::Table {
name: "c".to_owned(),
alias: None,
}),
on: Some(Expression::BinaryOperator(BinaryOperator::Eq(
Box::new(Expression::CompoundIdentifier(vec![
Ident {
value: "c".to_owned(),
quote_style: None,
},
Ident {
value: "id".to_owned(),
quote_style: None,
},
])),
Box::new(Expression::CompoundIdentifier(vec![
Ident {
value: "b".to_owned(),
quote_style: None,
},
Ident {
value: "id".to_owned(),
quote_style: None,
},
])),
))),
join_type: ast::JoinType::Left,
}],
r#where: None,
group_by: None,
having: None,
order_by: None,
limit: None,
offset: None,
})),
);

assert_stmt_eq(
"SELECT * FROM a,b LEFT JOIN c ON a.id = c.id LEFT JOIN d ON b.id = d.id",
Statement::Select(Box::new(Select {
with: None,
distinct: None,
columns: vec![SelectItem::Wildcard],
from: vec![
ast::From::Table {
name: "a".to_owned(),
alias: None,
},
ast::From::Join {
left: Box::new(ast::From::Join {
left: Box::new(ast::From::Table {
name: "b".to_owned(),
alias: None,
}),
right: Box::new(ast::From::Table {
name: "c".to_owned(),
alias: None,
}),
on: Some(Expression::BinaryOperator(BinaryOperator::Eq(
Box::new(Expression::CompoundIdentifier(vec![
Ident {
value: "a".to_owned(),
quote_style: None,
},
Ident {
value: "id".to_owned(),
quote_style: None,
},
])),
Box::new(Expression::CompoundIdentifier(vec![
Ident {
value: "c".to_owned(),
quote_style: None,
},
Ident {
value: "id".to_owned(),
quote_style: None,
},
])),
))),
join_type: ast::JoinType::Left,
}),
right: Box::new(ast::From::Table {
name: "d".to_owned(),
alias: None,
}),
on: Some(Expression::BinaryOperator(BinaryOperator::Eq(
Box::new(Expression::CompoundIdentifier(vec![
Ident {
value: "b".to_owned(),
quote_style: None,
},
Ident {
value: "id".to_owned(),
quote_style: None,
},
])),
Box::new(Expression::CompoundIdentifier(vec![
Ident {
value: "d".to_owned(),
quote_style: None,
},
Ident {
value: "id".to_owned(),
quote_style: None,
},
])),
))),
join_type: ast::JoinType::Left,
},
],
r#where: None,
group_by: None,
having: None,
order_by: None,
limit: None,
offset: None,
})),
);
}

#[test]
fn test_like() {
assert_stmt_eq(
Expand Down

0 comments on commit 89b4390

Please sign in to comment.