mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-31 11:17:23 +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,
|
ALTER,
|
||||||
AND,
|
AND,
|
||||||
ANY,
|
ANY,
|
||||||
|
APPLY,
|
||||||
ARE,
|
ARE,
|
||||||
ARRAY,
|
ARRAY,
|
||||||
ARRAY_AGG,
|
ARRAY_AGG,
|
||||||
|
@ -422,11 +423,8 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[&str] = &[
|
||||||
// Reserved as both a table and a column alias:
|
// Reserved as both a table and a column alias:
|
||||||
WITH, SELECT, WHERE, GROUP, HAVING, ORDER, LIMIT, OFFSET, FETCH, UNION, EXCEPT, INTERSECT,
|
WITH, SELECT, WHERE, GROUP, HAVING, ORDER, LIMIT, OFFSET, FETCH, UNION, EXCEPT, INTERSECT,
|
||||||
// Reserved only as a table alias in the `FROM`/`JOIN` clauses:
|
// Reserved only as a table alias in the `FROM`/`JOIN` clauses:
|
||||||
ON, JOIN, INNER, CROSS, FULL, LEFT, RIGHT, NATURAL, USING, LIMIT, OFFSET, FETCH,
|
ON, JOIN, INNER, CROSS, FULL, LEFT, RIGHT, NATURAL, USING,
|
||||||
// Reserved not because of ambiguity, but so that parsing `SELECT * FROM a
|
// for MSSQL-specific OUTER APPLY (seems reserved in most dialects)
|
||||||
// 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.
|
|
||||||
OUTER,
|
OUTER,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -327,7 +327,6 @@ impl ToString for Join {
|
||||||
self.relation.to_string(),
|
self.relation.to_string(),
|
||||||
suffix(constraint)
|
suffix(constraint)
|
||||||
),
|
),
|
||||||
JoinOperator::Cross => format!(" CROSS JOIN {}", self.relation.to_string()),
|
|
||||||
JoinOperator::LeftOuter(constraint) => format!(
|
JoinOperator::LeftOuter(constraint) => format!(
|
||||||
" {}LEFT JOIN {}{}",
|
" {}LEFT JOIN {}{}",
|
||||||
prefix(constraint),
|
prefix(constraint),
|
||||||
|
@ -346,6 +345,9 @@ impl ToString for Join {
|
||||||
self.relation.to_string(),
|
self.relation.to_string(),
|
||||||
suffix(constraint)
|
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),
|
LeftOuter(JoinConstraint),
|
||||||
RightOuter(JoinConstraint),
|
RightOuter(JoinConstraint),
|
||||||
FullOuter(JoinConstraint),
|
FullOuter(JoinConstraint),
|
||||||
Cross,
|
CrossJoin,
|
||||||
|
/// CROSS APPLY (non-standard)
|
||||||
|
CrossApply,
|
||||||
|
/// OUTER APPLY (non-standard)
|
||||||
|
OuterApply,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||||
|
|
|
@ -1618,10 +1618,24 @@ impl Parser {
|
||||||
let mut joins = vec![];
|
let mut joins = vec![];
|
||||||
loop {
|
loop {
|
||||||
let join = if self.parse_keyword("CROSS") {
|
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 {
|
Join {
|
||||||
relation: self.parse_table_factor()?,
|
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 {
|
} else {
|
||||||
let natural = self.parse_keyword("NATURAL");
|
let natural = self.parse_keyword("NATURAL");
|
||||||
|
|
|
@ -1560,7 +1560,7 @@ fn parse_cross_join() {
|
||||||
args: vec![],
|
args: vec![],
|
||||||
with_hints: vec![],
|
with_hints: vec![],
|
||||||
},
|
},
|
||||||
join_operator: JoinOperator::Cross
|
join_operator: JoinOperator::CrossJoin
|
||||||
},
|
},
|
||||||
only(only(select.from).joins),
|
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");
|
let res = parse_sql_statements("SELECT * FROM a OUTER JOIN b ON 1");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ParserError::ParserError("Expected LEFT, RIGHT, or FULL, found: OUTER".to_string()),
|
ParserError::ParserError("Expected APPLY, found: JOIN".to_string()),
|
||||||
res.unwrap_err()
|
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 {
|
fn ms() -> TestedDialects {
|
||||||
TestedDialects {
|
TestedDialects {
|
||||||
dialects: vec![Box::new(MsSqlDialect {})],
|
dialects: vec![Box::new(MsSqlDialect {})],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue