mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-01 11:47:20 +00:00
Support AS
table aliases (6/8)
A "table factor" (name borrowed from the ANSI SQL grammar) is a table name or a derived table (subquery), followed by an optional `AS` and an optional alias. (The alias is *not* optional for subqueries, but we don't enforce that.) It can appear in the FROM/JOIN part of the query. This commit: - introduces ASTNode::TableFactor - changes the parser to populate SQLSelect::relation and Join::relation with ASTNode::TableFactor instead of the table name - changes the parser to only accept subqueries or identifiers, not arbitrary expressions in the "table factor" context - always parses the table name as SQLCompoundIdentifier (whether or not it was actually compound).
This commit is contained in:
parent
7bbf69f513
commit
536fa6e428
3 changed files with 111 additions and 23 deletions
|
@ -72,12 +72,17 @@ pub enum ASTNode {
|
||||||
results: Vec<ASTNode>,
|
results: Vec<ASTNode>,
|
||||||
else_result: Option<Box<ASTNode>>,
|
else_result: Option<Box<ASTNode>>,
|
||||||
},
|
},
|
||||||
|
/// A table name or a parenthesized subquery with an optional alias
|
||||||
|
TableFactor {
|
||||||
|
relation: Box<ASTNode>, // SQLNested or SQLCompoundIdentifier
|
||||||
|
alias: Option<SQLIdent>,
|
||||||
|
},
|
||||||
/// SELECT
|
/// SELECT
|
||||||
SQLSelect {
|
SQLSelect {
|
||||||
/// projection expressions
|
/// projection expressions
|
||||||
projection: Vec<ASTNode>,
|
projection: Vec<ASTNode>,
|
||||||
/// FROM
|
/// FROM
|
||||||
relation: Option<Box<ASTNode>>,
|
relation: Option<Box<ASTNode>>, // TableFactor
|
||||||
// JOIN
|
// JOIN
|
||||||
joins: Vec<Join>,
|
joins: Vec<Join>,
|
||||||
/// WHERE
|
/// WHERE
|
||||||
|
@ -191,6 +196,13 @@ impl ToString for ASTNode {
|
||||||
}
|
}
|
||||||
s + " END"
|
s + " END"
|
||||||
}
|
}
|
||||||
|
ASTNode::TableFactor { relation, alias } => {
|
||||||
|
if let Some(alias) = alias {
|
||||||
|
format!("{} AS {}", relation.to_string(), alias)
|
||||||
|
} else {
|
||||||
|
relation.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
ASTNode::SQLSelect {
|
ASTNode::SQLSelect {
|
||||||
projection,
|
projection,
|
||||||
relation,
|
relation,
|
||||||
|
@ -420,7 +432,7 @@ impl ToString for SQLColumnDef {
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Join {
|
pub struct Join {
|
||||||
pub relation: ASTNode,
|
pub relation: ASTNode, // TableFactor
|
||||||
pub join_operator: JoinOperator,
|
pub join_operator: JoinOperator,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -945,6 +945,34 @@ impl Parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse `AS identifier` (or simply `identifier` if it's not a reserved keyword)
|
||||||
|
/// Some examples with aliases: `SELECT 1 foo`, `SELECT COUNT(*) AS cnt`,
|
||||||
|
/// `SELECT ... FROM t1 foo, t2 bar`, `SELECT ... FROM (...) AS bar`
|
||||||
|
pub fn parse_optional_alias(
|
||||||
|
&mut self,
|
||||||
|
) -> Result<Option<SQLIdent>, ParserError> {
|
||||||
|
let after_as = self.parse_keyword("AS");
|
||||||
|
let maybe_alias = self.next_token();
|
||||||
|
match maybe_alias {
|
||||||
|
// Accept any identifier after `AS` (though many dialects have restrictions on
|
||||||
|
// keywords that may appear here).
|
||||||
|
Some(Token::SQLWord(ref w)) if after_as =>
|
||||||
|
{
|
||||||
|
// have to clone here until #![feature(bind_by_move_pattern_guards)] is enabled by default
|
||||||
|
Ok(Some(w.value.clone()))
|
||||||
|
}
|
||||||
|
ref not_an_ident if after_as => parser_err!(format!(
|
||||||
|
"Expected an identifier after AS, got {:?}",
|
||||||
|
not_an_ident
|
||||||
|
)),
|
||||||
|
Some(_not_an_ident) => {
|
||||||
|
self.prev_token();
|
||||||
|
Ok(None) // no alias found
|
||||||
|
}
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse one or more identifiers with the specified separator between them
|
/// Parse one or more identifiers with the specified separator between them
|
||||||
pub fn parse_compound_identifier(&mut self, separator: &Token) -> Result<ASTNode, ParserError> {
|
pub fn parse_compound_identifier(&mut self, separator: &Token) -> Result<ASTNode, ParserError> {
|
||||||
let mut idents = vec![];
|
let mut idents = vec![];
|
||||||
|
@ -1062,7 +1090,7 @@ impl Parser {
|
||||||
let projection = self.parse_expr_list()?;
|
let projection = self.parse_expr_list()?;
|
||||||
|
|
||||||
let (relation, joins): (Option<Box<ASTNode>>, Vec<Join>) = if self.parse_keyword("FROM") {
|
let (relation, joins): (Option<Box<ASTNode>>, Vec<Join>) = if self.parse_keyword("FROM") {
|
||||||
let relation = Some(Box::new(self.parse_expr(0)?));
|
let relation = Some(Box::new(self.parse_table_factor()?));
|
||||||
let joins = self.parse_joins()?;
|
let joins = self.parse_joins()?;
|
||||||
(relation, joins)
|
(relation, joins)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1121,6 +1149,21 @@ impl Parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A table name or a parenthesized subquery, followed by optional `[AS] alias`
|
||||||
|
pub fn parse_table_factor(&mut self) -> Result<ASTNode, ParserError> {
|
||||||
|
let relation = if self.consume_token(&Token::LParen) {
|
||||||
|
self.prev_token();
|
||||||
|
self.parse_expr(0)?
|
||||||
|
} else {
|
||||||
|
self.parse_compound_identifier(&Token::Period)?
|
||||||
|
};
|
||||||
|
let alias = self.parse_optional_alias()?;
|
||||||
|
Ok(ASTNode::TableFactor {
|
||||||
|
relation: Box::new(relation),
|
||||||
|
alias,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_join_constraint(&mut self, natural: bool) -> Result<JoinConstraint, ParserError> {
|
fn parse_join_constraint(&mut self, natural: bool) -> Result<JoinConstraint, ParserError> {
|
||||||
if natural {
|
if natural {
|
||||||
Ok(JoinConstraint::Natural)
|
Ok(JoinConstraint::Natural)
|
||||||
|
@ -1156,7 +1199,7 @@ impl Parser {
|
||||||
let natural = match &self.peek_token() {
|
let natural = match &self.peek_token() {
|
||||||
Some(Token::Comma) => {
|
Some(Token::Comma) => {
|
||||||
self.next_token();
|
self.next_token();
|
||||||
let relation = self.parse_expr(0)?;
|
let relation = self.parse_table_factor()?;
|
||||||
let join = Join {
|
let join = Join {
|
||||||
relation,
|
relation,
|
||||||
join_operator: JoinOperator::Implicit,
|
join_operator: JoinOperator::Implicit,
|
||||||
|
@ -1167,7 +1210,7 @@ impl Parser {
|
||||||
Some(Token::SQLWord(kw)) if kw.keyword == "CROSS" => {
|
Some(Token::SQLWord(kw)) if kw.keyword == "CROSS" => {
|
||||||
self.next_token();
|
self.next_token();
|
||||||
self.expect_keyword("JOIN")?;
|
self.expect_keyword("JOIN")?;
|
||||||
let relation = self.parse_expr(0)?;
|
let relation = self.parse_table_factor()?;
|
||||||
let join = Join {
|
let join = Join {
|
||||||
relation,
|
relation,
|
||||||
join_operator: JoinOperator::Cross,
|
join_operator: JoinOperator::Cross,
|
||||||
|
@ -1188,14 +1231,14 @@ impl Parser {
|
||||||
self.next_token();
|
self.next_token();
|
||||||
self.expect_keyword("JOIN")?;
|
self.expect_keyword("JOIN")?;
|
||||||
Join {
|
Join {
|
||||||
relation: self.parse_expr(0)?,
|
relation: self.parse_table_factor()?,
|
||||||
join_operator: JoinOperator::Inner(self.parse_join_constraint(natural)?),
|
join_operator: JoinOperator::Inner(self.parse_join_constraint(natural)?),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Token::SQLWord(kw)) if kw.keyword == "JOIN" => {
|
Some(Token::SQLWord(kw)) if kw.keyword == "JOIN" => {
|
||||||
self.next_token();
|
self.next_token();
|
||||||
Join {
|
Join {
|
||||||
relation: self.parse_expr(0)?,
|
relation: self.parse_table_factor()?,
|
||||||
join_operator: JoinOperator::Inner(self.parse_join_constraint(natural)?),
|
join_operator: JoinOperator::Inner(self.parse_join_constraint(natural)?),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1204,7 +1247,7 @@ impl Parser {
|
||||||
let _ = self.parse_keyword("OUTER");
|
let _ = self.parse_keyword("OUTER");
|
||||||
self.expect_keyword("JOIN")?;
|
self.expect_keyword("JOIN")?;
|
||||||
Join {
|
Join {
|
||||||
relation: self.parse_expr(0)?,
|
relation: self.parse_table_factor()?,
|
||||||
join_operator: JoinOperator::LeftOuter(
|
join_operator: JoinOperator::LeftOuter(
|
||||||
self.parse_join_constraint(natural)?,
|
self.parse_join_constraint(natural)?,
|
||||||
),
|
),
|
||||||
|
@ -1215,7 +1258,7 @@ impl Parser {
|
||||||
let _ = self.parse_keyword("OUTER");
|
let _ = self.parse_keyword("OUTER");
|
||||||
self.expect_keyword("JOIN")?;
|
self.expect_keyword("JOIN")?;
|
||||||
Join {
|
Join {
|
||||||
relation: self.parse_expr(0)?,
|
relation: self.parse_table_factor()?,
|
||||||
join_operator: JoinOperator::RightOuter(
|
join_operator: JoinOperator::RightOuter(
|
||||||
self.parse_join_constraint(natural)?,
|
self.parse_join_constraint(natural)?,
|
||||||
),
|
),
|
||||||
|
@ -1226,7 +1269,7 @@ impl Parser {
|
||||||
let _ = self.parse_keyword("OUTER");
|
let _ = self.parse_keyword("OUTER");
|
||||||
self.expect_keyword("JOIN")?;
|
self.expect_keyword("JOIN")?;
|
||||||
Join {
|
Join {
|
||||||
relation: self.parse_expr(0)?,
|
relation: self.parse_table_factor()?,
|
||||||
join_operator: JoinOperator::FullOuter(
|
join_operator: JoinOperator::FullOuter(
|
||||||
self.parse_join_constraint(natural)?,
|
self.parse_join_constraint(natural)?,
|
||||||
),
|
),
|
||||||
|
|
|
@ -539,7 +539,10 @@ fn parse_implicit_join() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
joins[0],
|
joins[0],
|
||||||
Join {
|
Join {
|
||||||
relation: ASTNode::SQLIdentifier("t2".to_string()),
|
relation: ASTNode::TableFactor {
|
||||||
|
relation: Box::new(ASTNode::SQLCompoundIdentifier(vec!["t2".to_string()])),
|
||||||
|
alias: None,
|
||||||
|
},
|
||||||
join_operator: JoinOperator::Implicit
|
join_operator: JoinOperator::Implicit
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -558,7 +561,10 @@ fn parse_cross_join() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
joins[0],
|
joins[0],
|
||||||
Join {
|
Join {
|
||||||
relation: ASTNode::SQLIdentifier("t2".to_string()),
|
relation: ASTNode::TableFactor {
|
||||||
|
relation: Box::new(ASTNode::SQLCompoundIdentifier(vec!["t2".to_string()])),
|
||||||
|
alias: None,
|
||||||
|
},
|
||||||
join_operator: JoinOperator::Cross
|
join_operator: JoinOperator::Cross
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -571,10 +577,14 @@ fn parse_cross_join() {
|
||||||
fn parse_joins_on() {
|
fn parse_joins_on() {
|
||||||
fn join_with_constraint(
|
fn join_with_constraint(
|
||||||
relation: impl Into<String>,
|
relation: impl Into<String>,
|
||||||
|
alias: Option<SQLIdent>,
|
||||||
f: impl Fn(JoinConstraint) -> JoinOperator,
|
f: impl Fn(JoinConstraint) -> JoinOperator,
|
||||||
) -> Join {
|
) -> Join {
|
||||||
Join {
|
Join {
|
||||||
relation: ASTNode::SQLIdentifier(relation.into()),
|
relation: ASTNode::TableFactor {
|
||||||
|
relation: Box::new(ASTNode::SQLCompoundIdentifier(vec![relation.into()])),
|
||||||
|
alias,
|
||||||
|
},
|
||||||
join_operator: f(JoinConstraint::On(ASTNode::SQLBinaryExpr {
|
join_operator: f(JoinConstraint::On(ASTNode::SQLBinaryExpr {
|
||||||
left: Box::new(ASTNode::SQLIdentifier("c1".into())),
|
left: Box::new(ASTNode::SQLIdentifier("c1".into())),
|
||||||
op: SQLOperator::Eq,
|
op: SQLOperator::Eq,
|
||||||
|
@ -582,21 +592,31 @@ fn parse_joins_on() {
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Test parsing of aliases
|
||||||
|
assert_eq!(
|
||||||
|
joins_from(verified("SELECT * FROM t1 JOIN t2 AS foo ON c1 = c2")),
|
||||||
|
vec![join_with_constraint(
|
||||||
|
"t2",
|
||||||
|
Some("foo".to_string()),
|
||||||
|
JoinOperator::Inner
|
||||||
|
)]
|
||||||
|
);
|
||||||
|
// Test parsing of different join operators
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
joins_from(verified("SELECT * FROM t1 JOIN t2 ON c1 = c2")),
|
joins_from(verified("SELECT * FROM t1 JOIN t2 ON c1 = c2")),
|
||||||
vec![join_with_constraint("t2", JoinOperator::Inner)]
|
vec![join_with_constraint("t2", None, JoinOperator::Inner)]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
joins_from(verified("SELECT * FROM t1 LEFT JOIN t2 ON c1 = c2")),
|
joins_from(verified("SELECT * FROM t1 LEFT JOIN t2 ON c1 = c2")),
|
||||||
vec![join_with_constraint("t2", JoinOperator::LeftOuter)]
|
vec![join_with_constraint("t2", None, JoinOperator::LeftOuter)]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
joins_from(verified("SELECT * FROM t1 RIGHT JOIN t2 ON c1 = c2")),
|
joins_from(verified("SELECT * FROM t1 RIGHT JOIN t2 ON c1 = c2")),
|
||||||
vec![join_with_constraint("t2", JoinOperator::RightOuter)]
|
vec![join_with_constraint("t2", None, JoinOperator::RightOuter)]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
joins_from(verified("SELECT * FROM t1 FULL JOIN t2 ON c1 = c2")),
|
joins_from(verified("SELECT * FROM t1 FULL JOIN t2 ON c1 = c2")),
|
||||||
vec![join_with_constraint("t2", JoinOperator::FullOuter)]
|
vec![join_with_constraint("t2", None, JoinOperator::FullOuter)]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -604,29 +624,42 @@ fn parse_joins_on() {
|
||||||
fn parse_joins_using() {
|
fn parse_joins_using() {
|
||||||
fn join_with_constraint(
|
fn join_with_constraint(
|
||||||
relation: impl Into<String>,
|
relation: impl Into<String>,
|
||||||
|
alias: Option<SQLIdent>,
|
||||||
f: impl Fn(JoinConstraint) -> JoinOperator,
|
f: impl Fn(JoinConstraint) -> JoinOperator,
|
||||||
) -> Join {
|
) -> Join {
|
||||||
Join {
|
Join {
|
||||||
relation: ASTNode::SQLIdentifier(relation.into()),
|
relation: ASTNode::TableFactor {
|
||||||
|
relation: Box::new(ASTNode::SQLCompoundIdentifier(vec![relation.into()])),
|
||||||
|
alias,
|
||||||
|
},
|
||||||
join_operator: f(JoinConstraint::Using(vec!["c1".into()])),
|
join_operator: f(JoinConstraint::Using(vec!["c1".into()])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Test parsing of aliases
|
||||||
|
assert_eq!(
|
||||||
|
joins_from(verified("SELECT * FROM t1 JOIN t2 AS foo USING(c1)")),
|
||||||
|
vec![join_with_constraint(
|
||||||
|
"t2",
|
||||||
|
Some("foo".to_string()),
|
||||||
|
JoinOperator::Inner
|
||||||
|
)]
|
||||||
|
);
|
||||||
|
// Test parsing of different join operators
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
joins_from(verified("SELECT * FROM t1 JOIN t2 USING(c1)")),
|
joins_from(verified("SELECT * FROM t1 JOIN t2 USING(c1)")),
|
||||||
vec![join_with_constraint("t2", JoinOperator::Inner)]
|
vec![join_with_constraint("t2", None, JoinOperator::Inner)]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
joins_from(verified("SELECT * FROM t1 LEFT JOIN t2 USING(c1)")),
|
joins_from(verified("SELECT * FROM t1 LEFT JOIN t2 USING(c1)")),
|
||||||
vec![join_with_constraint("t2", JoinOperator::LeftOuter)]
|
vec![join_with_constraint("t2", None, JoinOperator::LeftOuter)]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
joins_from(verified("SELECT * FROM t1 RIGHT JOIN t2 USING(c1)")),
|
joins_from(verified("SELECT * FROM t1 RIGHT JOIN t2 USING(c1)")),
|
||||||
vec![join_with_constraint("t2", JoinOperator::RightOuter)]
|
vec![join_with_constraint("t2", None, JoinOperator::RightOuter)]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
joins_from(verified("SELECT * FROM t1 FULL JOIN t2 USING(c1)")),
|
joins_from(verified("SELECT * FROM t1 FULL JOIN t2 USING(c1)")),
|
||||||
vec![join_with_constraint("t2", JoinOperator::FullOuter)]
|
vec![join_with_constraint("t2", None, JoinOperator::FullOuter)]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue