mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-07 17:04:59 +00:00
Snowflake: support nested join without parentheses (#1799)
This commit is contained in:
parent
6566c47593
commit
514d2ecdaf
2 changed files with 440 additions and 2 deletions
|
@ -11712,6 +11712,11 @@ impl<'a> Parser<'a> {
|
|||
// Note that for keywords to be properly handled here, they need to be
|
||||
// added to `RESERVED_FOR_TABLE_ALIAS`, otherwise they may be parsed as
|
||||
// a table alias.
|
||||
let joins = self.parse_joins()?;
|
||||
Ok(TableWithJoins { relation, joins })
|
||||
}
|
||||
|
||||
fn parse_joins(&mut self) -> Result<Vec<Join>, ParserError> {
|
||||
let mut joins = vec![];
|
||||
loop {
|
||||
let global = self.parse_keyword(Keyword::GLOBAL);
|
||||
|
@ -11844,7 +11849,16 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
_ => break,
|
||||
};
|
||||
let relation = self.parse_table_factor()?;
|
||||
let mut relation = self.parse_table_factor()?;
|
||||
|
||||
if self.peek_parens_less_nested_join() {
|
||||
let joins = self.parse_joins()?;
|
||||
relation = TableFactor::NestedJoin {
|
||||
table_with_joins: Box::new(TableWithJoins { relation, joins }),
|
||||
alias: None,
|
||||
};
|
||||
}
|
||||
|
||||
let join_constraint = self.parse_join_constraint(natural)?;
|
||||
Join {
|
||||
relation,
|
||||
|
@ -11854,7 +11868,21 @@ impl<'a> Parser<'a> {
|
|||
};
|
||||
joins.push(join);
|
||||
}
|
||||
Ok(TableWithJoins { relation, joins })
|
||||
Ok(joins)
|
||||
}
|
||||
|
||||
fn peek_parens_less_nested_join(&self) -> bool {
|
||||
matches!(
|
||||
self.peek_token_ref().token,
|
||||
Token::Word(Word {
|
||||
keyword: Keyword::JOIN
|
||||
| Keyword::INNER
|
||||
| Keyword::LEFT
|
||||
| Keyword::RIGHT
|
||||
| Keyword::FULL,
|
||||
..
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/// A table name or a parenthesized subquery, followed by optional `[AS] alias`
|
||||
|
|
|
@ -3573,3 +3573,413 @@ fn test_alter_session_followed_by_statement() {
|
|||
_ => panic!("Unexpected statements: {:?}", stmts),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_join_without_parentheses() {
|
||||
let query = "SELECT DISTINCT p.product_id FROM orders AS o INNER JOIN customers AS c INNER JOIN products AS p ON p.customer_id = c.customer_id ON c.order_id = o.order_id";
|
||||
assert_eq!(
|
||||
only(
|
||||
snowflake()
|
||||
.verified_only_select_with_canonical(query, "SELECT DISTINCT p.product_id FROM orders AS o INNER JOIN (customers AS c INNER JOIN products AS p ON p.customer_id = c.customer_id) ON c.order_id = o.order_id")
|
||||
.from
|
||||
)
|
||||
.joins,
|
||||
vec![Join {
|
||||
relation: TableFactor::NestedJoin {
|
||||
table_with_joins: Box::new(TableWithJoins {
|
||||
relation: TableFactor::Table {
|
||||
name: ObjectName::from(vec![Ident::new("customers".to_string())]),
|
||||
alias: Some(TableAlias {
|
||||
name: Ident {
|
||||
value: "c".to_string(),
|
||||
quote_style: None,
|
||||
span: Span::empty(),
|
||||
},
|
||||
columns: vec![],
|
||||
}),
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
partitions: vec![],
|
||||
with_ordinality: false,
|
||||
json_path: None,
|
||||
sample: None,
|
||||
index_hints: vec![],
|
||||
},
|
||||
joins: vec![Join {
|
||||
relation: TableFactor::Table {
|
||||
name: ObjectName::from(vec![Ident::new("products".to_string())]),
|
||||
alias: Some(TableAlias {
|
||||
name: Ident {
|
||||
value: "p".to_string(),
|
||||
quote_style: None,
|
||||
span: Span::empty(),
|
||||
},
|
||||
columns: vec![],
|
||||
}),
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
partitions: vec![],
|
||||
with_ordinality: false,
|
||||
json_path: None,
|
||||
sample: None,
|
||||
index_hints: vec![],
|
||||
},
|
||||
global: false,
|
||||
join_operator: JoinOperator::Inner(JoinConstraint::On(Expr::BinaryOp {
|
||||
left: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("p".to_string()),
|
||||
Ident::new("customer_id".to_string())
|
||||
])),
|
||||
op: BinaryOperator::Eq,
|
||||
right: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("c".to_string()),
|
||||
Ident::new("customer_id".to_string())
|
||||
])),
|
||||
})),
|
||||
}]
|
||||
}),
|
||||
alias: None
|
||||
},
|
||||
global: false,
|
||||
join_operator: JoinOperator::Inner(JoinConstraint::On(Expr::BinaryOp {
|
||||
left: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("c".to_string()),
|
||||
Ident::new("order_id".to_string())
|
||||
])),
|
||||
op: BinaryOperator::Eq,
|
||||
right: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("o".to_string()),
|
||||
Ident::new("order_id".to_string())
|
||||
])),
|
||||
}))
|
||||
}],
|
||||
);
|
||||
|
||||
let query = "SELECT DISTINCT p.product_id FROM orders AS o JOIN customers AS c JOIN products AS p ON p.customer_id = c.customer_id ON c.order_id = o.order_id";
|
||||
assert_eq!(
|
||||
only(
|
||||
snowflake()
|
||||
.verified_only_select_with_canonical(query, "SELECT DISTINCT p.product_id FROM orders AS o JOIN (customers AS c JOIN products AS p ON p.customer_id = c.customer_id) ON c.order_id = o.order_id")
|
||||
.from
|
||||
)
|
||||
.joins,
|
||||
vec![Join {
|
||||
relation: TableFactor::NestedJoin {
|
||||
table_with_joins: Box::new(TableWithJoins {
|
||||
relation: TableFactor::Table {
|
||||
name: ObjectName::from(vec![Ident::new("customers".to_string())]),
|
||||
alias: Some(TableAlias {
|
||||
name: Ident {
|
||||
value: "c".to_string(),
|
||||
quote_style: None,
|
||||
span: Span::empty(),
|
||||
},
|
||||
columns: vec![],
|
||||
}),
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
partitions: vec![],
|
||||
with_ordinality: false,
|
||||
json_path: None,
|
||||
sample: None,
|
||||
index_hints: vec![],
|
||||
},
|
||||
joins: vec![Join {
|
||||
relation: TableFactor::Table {
|
||||
name: ObjectName::from(vec![Ident::new("products".to_string())]),
|
||||
alias: Some(TableAlias {
|
||||
name: Ident {
|
||||
value: "p".to_string(),
|
||||
quote_style: None,
|
||||
span: Span::empty(),
|
||||
},
|
||||
columns: vec![],
|
||||
}),
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
partitions: vec![],
|
||||
with_ordinality: false,
|
||||
json_path: None,
|
||||
sample: None,
|
||||
index_hints: vec![],
|
||||
},
|
||||
global: false,
|
||||
join_operator: JoinOperator::Join(JoinConstraint::On(Expr::BinaryOp {
|
||||
left: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("p".to_string()),
|
||||
Ident::new("customer_id".to_string())
|
||||
])),
|
||||
op: BinaryOperator::Eq,
|
||||
right: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("c".to_string()),
|
||||
Ident::new("customer_id".to_string())
|
||||
])),
|
||||
})),
|
||||
}]
|
||||
}),
|
||||
alias: None
|
||||
},
|
||||
global: false,
|
||||
join_operator: JoinOperator::Join(JoinConstraint::On(Expr::BinaryOp {
|
||||
left: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("c".to_string()),
|
||||
Ident::new("order_id".to_string())
|
||||
])),
|
||||
op: BinaryOperator::Eq,
|
||||
right: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("o".to_string()),
|
||||
Ident::new("order_id".to_string())
|
||||
])),
|
||||
}))
|
||||
}],
|
||||
);
|
||||
|
||||
let query = "SELECT DISTINCT p.product_id FROM orders AS o LEFT JOIN customers AS c LEFT JOIN products AS p ON p.customer_id = c.customer_id ON c.order_id = o.order_id";
|
||||
assert_eq!(
|
||||
only(
|
||||
snowflake()
|
||||
.verified_only_select_with_canonical(query, "SELECT DISTINCT p.product_id FROM orders AS o LEFT JOIN (customers AS c LEFT JOIN products AS p ON p.customer_id = c.customer_id) ON c.order_id = o.order_id")
|
||||
.from
|
||||
)
|
||||
.joins,
|
||||
vec![Join {
|
||||
relation: TableFactor::NestedJoin {
|
||||
table_with_joins: Box::new(TableWithJoins {
|
||||
relation: TableFactor::Table {
|
||||
name: ObjectName::from(vec![Ident::new("customers".to_string())]),
|
||||
alias: Some(TableAlias {
|
||||
name: Ident {
|
||||
value: "c".to_string(),
|
||||
quote_style: None,
|
||||
span: Span::empty(),
|
||||
},
|
||||
columns: vec![],
|
||||
}),
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
partitions: vec![],
|
||||
with_ordinality: false,
|
||||
json_path: None,
|
||||
sample: None,
|
||||
index_hints: vec![],
|
||||
},
|
||||
joins: vec![Join {
|
||||
relation: TableFactor::Table {
|
||||
name: ObjectName::from(vec![Ident::new("products".to_string())]),
|
||||
alias: Some(TableAlias {
|
||||
name: Ident {
|
||||
value: "p".to_string(),
|
||||
quote_style: None,
|
||||
span: Span::empty(),
|
||||
},
|
||||
columns: vec![],
|
||||
}),
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
partitions: vec![],
|
||||
with_ordinality: false,
|
||||
json_path: None,
|
||||
sample: None,
|
||||
index_hints: vec![],
|
||||
},
|
||||
global: false,
|
||||
join_operator: JoinOperator::Left(JoinConstraint::On(Expr::BinaryOp {
|
||||
left: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("p".to_string()),
|
||||
Ident::new("customer_id".to_string())
|
||||
])),
|
||||
op: BinaryOperator::Eq,
|
||||
right: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("c".to_string()),
|
||||
Ident::new("customer_id".to_string())
|
||||
])),
|
||||
})),
|
||||
}]
|
||||
}),
|
||||
alias: None
|
||||
},
|
||||
global: false,
|
||||
join_operator: JoinOperator::Left(JoinConstraint::On(Expr::BinaryOp {
|
||||
left: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("c".to_string()),
|
||||
Ident::new("order_id".to_string())
|
||||
])),
|
||||
op: BinaryOperator::Eq,
|
||||
right: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("o".to_string()),
|
||||
Ident::new("order_id".to_string())
|
||||
])),
|
||||
}))
|
||||
}],
|
||||
);
|
||||
|
||||
let query = "SELECT DISTINCT p.product_id FROM orders AS o RIGHT JOIN customers AS c RIGHT JOIN products AS p ON p.customer_id = c.customer_id ON c.order_id = o.order_id";
|
||||
assert_eq!(
|
||||
only(
|
||||
snowflake()
|
||||
.verified_only_select_with_canonical(query, "SELECT DISTINCT p.product_id FROM orders AS o RIGHT JOIN (customers AS c RIGHT JOIN products AS p ON p.customer_id = c.customer_id) ON c.order_id = o.order_id")
|
||||
.from
|
||||
)
|
||||
.joins,
|
||||
vec![Join {
|
||||
relation: TableFactor::NestedJoin {
|
||||
table_with_joins: Box::new(TableWithJoins {
|
||||
relation: TableFactor::Table {
|
||||
name: ObjectName::from(vec![Ident::new("customers".to_string())]),
|
||||
alias: Some(TableAlias {
|
||||
name: Ident {
|
||||
value: "c".to_string(),
|
||||
quote_style: None,
|
||||
span: Span::empty(),
|
||||
},
|
||||
columns: vec![],
|
||||
}),
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
partitions: vec![],
|
||||
with_ordinality: false,
|
||||
json_path: None,
|
||||
sample: None,
|
||||
index_hints: vec![],
|
||||
},
|
||||
joins: vec![Join {
|
||||
relation: TableFactor::Table {
|
||||
name: ObjectName::from(vec![Ident::new("products".to_string())]),
|
||||
alias: Some(TableAlias {
|
||||
name: Ident {
|
||||
value: "p".to_string(),
|
||||
quote_style: None,
|
||||
span: Span::empty(),
|
||||
},
|
||||
columns: vec![],
|
||||
}),
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
partitions: vec![],
|
||||
with_ordinality: false,
|
||||
json_path: None,
|
||||
sample: None,
|
||||
index_hints: vec![],
|
||||
},
|
||||
global: false,
|
||||
join_operator: JoinOperator::Right(JoinConstraint::On(Expr::BinaryOp {
|
||||
left: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("p".to_string()),
|
||||
Ident::new("customer_id".to_string())
|
||||
])),
|
||||
op: BinaryOperator::Eq,
|
||||
right: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("c".to_string()),
|
||||
Ident::new("customer_id".to_string())
|
||||
])),
|
||||
})),
|
||||
}]
|
||||
}),
|
||||
alias: None
|
||||
},
|
||||
global: false,
|
||||
join_operator: JoinOperator::Right(JoinConstraint::On(Expr::BinaryOp {
|
||||
left: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("c".to_string()),
|
||||
Ident::new("order_id".to_string())
|
||||
])),
|
||||
op: BinaryOperator::Eq,
|
||||
right: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("o".to_string()),
|
||||
Ident::new("order_id".to_string())
|
||||
])),
|
||||
}))
|
||||
}],
|
||||
);
|
||||
|
||||
let query = "SELECT DISTINCT p.product_id FROM orders AS o FULL JOIN customers AS c FULL JOIN products AS p ON p.customer_id = c.customer_id ON c.order_id = o.order_id";
|
||||
assert_eq!(
|
||||
only(
|
||||
snowflake()
|
||||
.verified_only_select_with_canonical(query, "SELECT DISTINCT p.product_id FROM orders AS o FULL JOIN (customers AS c FULL JOIN products AS p ON p.customer_id = c.customer_id) ON c.order_id = o.order_id")
|
||||
.from
|
||||
)
|
||||
.joins,
|
||||
vec![Join {
|
||||
relation: TableFactor::NestedJoin {
|
||||
table_with_joins: Box::new(TableWithJoins {
|
||||
relation: TableFactor::Table {
|
||||
name: ObjectName::from(vec![Ident::new("customers".to_string())]),
|
||||
alias: Some(TableAlias {
|
||||
name: Ident {
|
||||
value: "c".to_string(),
|
||||
quote_style: None,
|
||||
span: Span::empty(),
|
||||
},
|
||||
columns: vec![],
|
||||
}),
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
partitions: vec![],
|
||||
with_ordinality: false,
|
||||
json_path: None,
|
||||
sample: None,
|
||||
index_hints: vec![],
|
||||
},
|
||||
joins: vec![Join {
|
||||
relation: TableFactor::Table {
|
||||
name: ObjectName::from(vec![Ident::new("products".to_string())]),
|
||||
alias: Some(TableAlias {
|
||||
name: Ident {
|
||||
value: "p".to_string(),
|
||||
quote_style: None,
|
||||
span: Span::empty(),
|
||||
},
|
||||
columns: vec![],
|
||||
}),
|
||||
args: None,
|
||||
with_hints: vec![],
|
||||
version: None,
|
||||
partitions: vec![],
|
||||
with_ordinality: false,
|
||||
json_path: None,
|
||||
sample: None,
|
||||
index_hints: vec![],
|
||||
},
|
||||
global: false,
|
||||
join_operator: JoinOperator::FullOuter(JoinConstraint::On(
|
||||
Expr::BinaryOp {
|
||||
left: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("p".to_string()),
|
||||
Ident::new("customer_id".to_string())
|
||||
])),
|
||||
op: BinaryOperator::Eq,
|
||||
right: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("c".to_string()),
|
||||
Ident::new("customer_id".to_string())
|
||||
])),
|
||||
}
|
||||
)),
|
||||
}]
|
||||
}),
|
||||
alias: None
|
||||
},
|
||||
global: false,
|
||||
join_operator: JoinOperator::FullOuter(JoinConstraint::On(Expr::BinaryOp {
|
||||
left: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("c".to_string()),
|
||||
Ident::new("order_id".to_string())
|
||||
])),
|
||||
op: BinaryOperator::Eq,
|
||||
right: Box::new(Expr::CompoundIdentifier(vec![
|
||||
Ident::new("o".to_string()),
|
||||
Ident::new("order_id".to_string())
|
||||
])),
|
||||
}))
|
||||
}],
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue