mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-30 18:57:21 +00:00
Merge pull request #120 from nickolay/pr/outer-apply
[mssql/oracle] Support CROSS/OUTER APPLY
This commit is contained in:
commit
3c401d5e4f
5 changed files with 45 additions and 11 deletions
|
@ -58,6 +58,7 @@ define_keywords!(
|
|||
ALTER,
|
||||
AND,
|
||||
ANY,
|
||||
APPLY,
|
||||
ARE,
|
||||
ARRAY,
|
||||
ARRAY_AGG,
|
||||
|
@ -422,11 +423,8 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[&str] = &[
|
|||
// Reserved as both a table and a column alias:
|
||||
WITH, SELECT, WHERE, GROUP, HAVING, ORDER, LIMIT, OFFSET, FETCH, UNION, EXCEPT, INTERSECT,
|
||||
// Reserved only as a table alias in the `FROM`/`JOIN` clauses:
|
||||
ON, JOIN, INNER, CROSS, FULL, LEFT, RIGHT, NATURAL, USING, LIMIT, OFFSET, FETCH,
|
||||
// Reserved not because of ambiguity, but so that parsing `SELECT * FROM a
|
||||
// OUTER JOIN b` causes a syntax error, rather than silently parsing to an
|
||||
// inner join where table `a` is aliased as `OUTER`, which is certainly not
|
||||
// what the user intended and also not valid according to the SQL standard.
|
||||
ON, JOIN, INNER, CROSS, FULL, LEFT, RIGHT, NATURAL, USING,
|
||||
// for MSSQL-specific OUTER APPLY (seems reserved in most dialects)
|
||||
OUTER,
|
||||
];
|
||||
|
||||
|
|
|
@ -327,7 +327,6 @@ impl ToString for Join {
|
|||
self.relation.to_string(),
|
||||
suffix(constraint)
|
||||
),
|
||||
JoinOperator::Cross => format!(" CROSS JOIN {}", self.relation.to_string()),
|
||||
JoinOperator::LeftOuter(constraint) => format!(
|
||||
" {}LEFT JOIN {}{}",
|
||||
prefix(constraint),
|
||||
|
@ -346,6 +345,9 @@ impl ToString for Join {
|
|||
self.relation.to_string(),
|
||||
suffix(constraint)
|
||||
),
|
||||
JoinOperator::CrossJoin => format!(" CROSS JOIN {}", self.relation.to_string()),
|
||||
JoinOperator::CrossApply => format!(" CROSS APPLY {}", self.relation.to_string()),
|
||||
JoinOperator::OuterApply => format!(" OUTER APPLY {}", self.relation.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -356,7 +358,11 @@ pub enum JoinOperator {
|
|||
LeftOuter(JoinConstraint),
|
||||
RightOuter(JoinConstraint),
|
||||
FullOuter(JoinConstraint),
|
||||
Cross,
|
||||
CrossJoin,
|
||||
/// CROSS APPLY (non-standard)
|
||||
CrossApply,
|
||||
/// OUTER APPLY (non-standard)
|
||||
OuterApply,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||
|
|
|
@ -1618,10 +1618,24 @@ impl Parser {
|
|||
let mut joins = vec![];
|
||||
loop {
|
||||
let join = if self.parse_keyword("CROSS") {
|
||||
self.expect_keyword("JOIN")?;
|
||||
let join_operator = if self.parse_keyword("JOIN") {
|
||||
JoinOperator::CrossJoin
|
||||
} else if self.parse_keyword("APPLY") {
|
||||
// MSSQL extension, similar to CROSS JOIN LATERAL
|
||||
JoinOperator::CrossApply
|
||||
} else {
|
||||
return self.expected("JOIN or APPLY after CROSS", self.peek_token());
|
||||
};
|
||||
Join {
|
||||
relation: self.parse_table_factor()?,
|
||||
join_operator: JoinOperator::Cross,
|
||||
join_operator,
|
||||
}
|
||||
} else if self.parse_keyword("OUTER") {
|
||||
// MSSQL extension, similar to LEFT JOIN LATERAL .. ON 1=1
|
||||
self.expect_keyword("APPLY")?;
|
||||
Join {
|
||||
relation: self.parse_table_factor()?,
|
||||
join_operator: JoinOperator::OuterApply,
|
||||
}
|
||||
} else {
|
||||
let natural = self.parse_keyword("NATURAL");
|
||||
|
|
|
@ -1560,7 +1560,7 @@ fn parse_cross_join() {
|
|||
args: vec![],
|
||||
with_hints: vec![],
|
||||
},
|
||||
join_operator: JoinOperator::Cross
|
||||
join_operator: JoinOperator::CrossJoin
|
||||
},
|
||||
only(only(select.from).joins),
|
||||
);
|
||||
|
@ -1804,7 +1804,7 @@ fn parse_join_syntax_variants() {
|
|||
|
||||
let res = parse_sql_statements("SELECT * FROM a OUTER JOIN b ON 1");
|
||||
assert_eq!(
|
||||
ParserError::ParserError("Expected LEFT, RIGHT, or FULL, found: OUTER".to_string()),
|
||||
ParserError::ParserError("Expected APPLY, found: JOIN".to_string()),
|
||||
res.unwrap_err()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -52,6 +52,22 @@ fn parse_mssql_delimited_identifiers() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_mssql_apply_join() {
|
||||
let _ = ms_and_generic().verified_only_select(
|
||||
"SELECT * FROM sys.dm_exec_query_stats AS deqs \
|
||||
CROSS APPLY sys.dm_exec_query_plan(deqs.plan_handle)",
|
||||
);
|
||||
let _ = ms_and_generic().verified_only_select(
|
||||
"SELECT * FROM sys.dm_exec_query_stats AS deqs \
|
||||
OUTER APPLY sys.dm_exec_query_plan(deqs.plan_handle)",
|
||||
);
|
||||
let _ = ms_and_generic().verified_only_select(
|
||||
"SELECT * FROM foo \
|
||||
OUTER APPLY (SELECT foo.x + 1) AS bar",
|
||||
);
|
||||
}
|
||||
|
||||
fn ms() -> TestedDialects {
|
||||
TestedDialects {
|
||||
dialects: vec![Box::new(MsSqlDialect {})],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue