mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-26 15:39:12 +00:00
Move TableFactor to be a separate enum
ASTNode can now be renamed SQLExpression, as it represents a node in the "expression" part of the AST -- other nodes have their own types.
This commit is contained in:
parent
e0ceacd1ad
commit
9967031cba
4 changed files with 82 additions and 67 deletions
|
@ -20,7 +20,7 @@ mod sqltype;
|
||||||
mod table_key;
|
mod table_key;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
pub use self::query::{Join, JoinConstraint, JoinOperator, SQLOrderByExpr, SQLSelect};
|
pub use self::query::{Join, JoinConstraint, JoinOperator, SQLOrderByExpr, SQLSelect, TableFactor};
|
||||||
pub use self::sqltype::SQLType;
|
pub use self::sqltype::SQLType;
|
||||||
pub use self::table_key::{AlterOperation, Key, TableKey};
|
pub use self::table_key::{AlterOperation, Key, TableKey};
|
||||||
pub use self::value::Value;
|
pub use self::value::Value;
|
||||||
|
@ -30,7 +30,8 @@ pub use self::sql_operator::SQLOperator;
|
||||||
/// Identifier name, in the originally quoted form (e.g. `"id"`)
|
/// Identifier name, in the originally quoted form (e.g. `"id"`)
|
||||||
pub type SQLIdent = String;
|
pub type SQLIdent = String;
|
||||||
|
|
||||||
/// SQL Abstract Syntax Tree (AST)
|
/// Represents a parsed SQL expression, which is a common building
|
||||||
|
/// block of SQL statements (the part after SELECT, WHERE, etc.)
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum ASTNode {
|
pub enum ASTNode {
|
||||||
/// Identifier e.g. table name or column name
|
/// Identifier e.g. table name or column name
|
||||||
|
@ -73,11 +74,6 @@ 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>,
|
|
||||||
},
|
|
||||||
/// A parenthesized subquery `(SELECT ...)`, used in expression like
|
/// A parenthesized subquery `(SELECT ...)`, used in expression like
|
||||||
/// `SELECT (subquery) AS x` or `WHERE (subquery) = x`
|
/// `SELECT (subquery) AS x` or `WHERE (subquery) = x`
|
||||||
SQLSubquery(SQLSelect),
|
SQLSubquery(SQLSelect),
|
||||||
|
@ -134,13 +130,6 @@ 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::SQLSubquery(s) => format!("({})", s.to_string()),
|
ASTNode::SQLSubquery(s) => format!("({})", s.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ pub struct SQLSelect {
|
||||||
/// projection expressions
|
/// projection expressions
|
||||||
pub projection: Vec<ASTNode>,
|
pub projection: Vec<ASTNode>,
|
||||||
/// FROM
|
/// FROM
|
||||||
pub relation: Option<Box<ASTNode>>, // TableFactor
|
pub relation: Option<TableFactor>,
|
||||||
// JOIN
|
// JOIN
|
||||||
pub joins: Vec<Join>,
|
pub joins: Vec<Join>,
|
||||||
/// WHERE
|
/// WHERE
|
||||||
|
@ -31,7 +31,7 @@ impl ToString for SQLSelect {
|
||||||
.join(", ")
|
.join(", ")
|
||||||
);
|
);
|
||||||
if let Some(ref relation) = self.relation {
|
if let Some(ref relation) = self.relation {
|
||||||
s += &format!(" FROM {}", relation.as_ref().to_string());
|
s += &format!(" FROM {}", relation.to_string());
|
||||||
}
|
}
|
||||||
for join in &self.joins {
|
for join in &self.joins {
|
||||||
s += &join.to_string();
|
s += &join.to_string();
|
||||||
|
@ -69,9 +69,38 @@ impl ToString for SQLSelect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A table name or a parenthesized subquery with an optional alias
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum TableFactor {
|
||||||
|
Table {
|
||||||
|
name: SQLObjectName,
|
||||||
|
alias: Option<SQLIdent>,
|
||||||
|
},
|
||||||
|
Derived {
|
||||||
|
subquery: Box<SQLSelect>,
|
||||||
|
alias: Option<SQLIdent>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for TableFactor {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
let (base, alias) = match self {
|
||||||
|
TableFactor::Table { name, alias } => (name.to_string(), alias),
|
||||||
|
TableFactor::Derived { subquery, alias } => {
|
||||||
|
(format!("({})", subquery.to_string()), alias)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Some(alias) = alias {
|
||||||
|
format!("{} AS {}", base, alias)
|
||||||
|
} else {
|
||||||
|
base
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Join {
|
pub struct Join {
|
||||||
pub relation: ASTNode, // TableFactor
|
pub relation: TableFactor,
|
||||||
pub join_operator: JoinOperator,
|
pub join_operator: JoinOperator,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1119,8 +1119,8 @@ impl Parser {
|
||||||
pub fn parse_select(&mut self) -> Result<SQLSelect, ParserError> {
|
pub fn parse_select(&mut self) -> Result<SQLSelect, ParserError> {
|
||||||
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) = if self.parse_keyword("FROM") {
|
||||||
let relation = Some(Box::new(self.parse_table_factor()?));
|
let relation = Some(self.parse_table_factor()?);
|
||||||
let joins = self.parse_joins()?;
|
let joins = self.parse_joins()?;
|
||||||
(relation, joins)
|
(relation, joins)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1171,20 +1171,21 @@ impl Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A table name or a parenthesized subquery, followed by optional `[AS] alias`
|
/// A table name or a parenthesized subquery, followed by optional `[AS] alias`
|
||||||
pub fn parse_table_factor(&mut self) -> Result<ASTNode, ParserError> {
|
pub fn parse_table_factor(&mut self) -> Result<TableFactor, ParserError> {
|
||||||
let relation = if self.consume_token(&Token::LParen) {
|
if self.consume_token(&Token::LParen) {
|
||||||
self.expect_keyword("SELECT")?;
|
self.expect_keyword("SELECT")?;
|
||||||
let subquery = self.parse_select()?;
|
let subquery = self.parse_select()?;
|
||||||
self.expect_token(&Token::RParen)?;
|
self.expect_token(&Token::RParen)?;
|
||||||
ASTNode::SQLSubquery(subquery)
|
Ok(TableFactor::Derived {
|
||||||
|
subquery: Box::new(subquery),
|
||||||
|
alias: self.parse_optional_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
ASTNode::SQLCompoundIdentifier(self.parse_object_name()?.0)
|
Ok(TableFactor::Table {
|
||||||
};
|
name: self.parse_object_name()?,
|
||||||
let alias = self.parse_optional_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?;
|
alias: self.parse_optional_alias(keywords::RESERVED_FOR_TABLE_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> {
|
||||||
|
|
|
@ -432,6 +432,14 @@ fn parse_delimited_identifiers() {
|
||||||
// check that quoted identifiers in any position remain quoted after serialization
|
// check that quoted identifiers in any position remain quoted after serialization
|
||||||
let sql = r#"SELECT "alias"."bar baz", "myfun"(), "simple id" FROM "a table" AS "alias""#;
|
let sql = r#"SELECT "alias"."bar baz", "myfun"(), "simple id" FROM "a table" AS "alias""#;
|
||||||
let select = verified_only_select(sql);
|
let select = verified_only_select(sql);
|
||||||
|
// check FROM
|
||||||
|
match select.relation.unwrap() {
|
||||||
|
TableFactor::Table { name, alias } => {
|
||||||
|
assert_eq!(vec![r#""a table""#.to_string()], name.0);
|
||||||
|
assert_eq!(r#""alias""#, alias.unwrap());
|
||||||
|
}
|
||||||
|
_ => panic!("Expecting TableFactor::Table"),
|
||||||
|
}
|
||||||
// check SELECT
|
// check SELECT
|
||||||
assert_eq!(3, select.projection.len());
|
assert_eq!(3, select.projection.len());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -515,45 +523,33 @@ fn parse_case_expression() {
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_implicit_join() {
|
fn parse_implicit_join() {
|
||||||
let sql = "SELECT * FROM t1, t2";
|
let sql = "SELECT * FROM t1, t2";
|
||||||
|
let select = verified_only_select(sql);
|
||||||
match verified_stmt(sql) {
|
assert_eq!(
|
||||||
SQLStatement::SQLSelect(SQLSelect { joins, .. }) => {
|
&Join {
|
||||||
assert_eq!(joins.len(), 1);
|
relation: TableFactor::Table {
|
||||||
assert_eq!(
|
name: SQLObjectName(vec!["t2".to_string()]),
|
||||||
joins[0],
|
alias: None,
|
||||||
Join {
|
},
|
||||||
relation: ASTNode::TableFactor {
|
join_operator: JoinOperator::Implicit
|
||||||
relation: Box::new(ASTNode::SQLCompoundIdentifier(vec!["t2".to_string()])),
|
},
|
||||||
alias: None,
|
only(&select.joins),
|
||||||
},
|
);
|
||||||
join_operator: JoinOperator::Implicit
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => assert!(false),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_cross_join() {
|
fn parse_cross_join() {
|
||||||
let sql = "SELECT * FROM t1 CROSS JOIN t2";
|
let sql = "SELECT * FROM t1 CROSS JOIN t2";
|
||||||
|
let select = verified_only_select(sql);
|
||||||
match verified_stmt(sql) {
|
assert_eq!(
|
||||||
SQLStatement::SQLSelect(SQLSelect { joins, .. }) => {
|
&Join {
|
||||||
assert_eq!(joins.len(), 1);
|
relation: TableFactor::Table {
|
||||||
assert_eq!(
|
name: SQLObjectName(vec!["t2".to_string()]),
|
||||||
joins[0],
|
alias: None,
|
||||||
Join {
|
},
|
||||||
relation: ASTNode::TableFactor {
|
join_operator: JoinOperator::Cross
|
||||||
relation: Box::new(ASTNode::SQLCompoundIdentifier(vec!["t2".to_string()])),
|
},
|
||||||
alias: None,
|
only(&select.joins),
|
||||||
},
|
);
|
||||||
join_operator: JoinOperator::Cross
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => assert!(false),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -564,8 +560,8 @@ fn parse_joins_on() {
|
||||||
f: impl Fn(JoinConstraint) -> JoinOperator,
|
f: impl Fn(JoinConstraint) -> JoinOperator,
|
||||||
) -> Join {
|
) -> Join {
|
||||||
Join {
|
Join {
|
||||||
relation: ASTNode::TableFactor {
|
relation: TableFactor::Table {
|
||||||
relation: Box::new(ASTNode::SQLCompoundIdentifier(vec![relation.into()])),
|
name: SQLObjectName(vec![relation.into()]),
|
||||||
alias,
|
alias,
|
||||||
},
|
},
|
||||||
join_operator: f(JoinConstraint::On(ASTNode::SQLBinaryExpr {
|
join_operator: f(JoinConstraint::On(ASTNode::SQLBinaryExpr {
|
||||||
|
@ -615,8 +611,8 @@ fn parse_joins_using() {
|
||||||
f: impl Fn(JoinConstraint) -> JoinOperator,
|
f: impl Fn(JoinConstraint) -> JoinOperator,
|
||||||
) -> Join {
|
) -> Join {
|
||||||
Join {
|
Join {
|
||||||
relation: ASTNode::TableFactor {
|
relation: TableFactor::Table {
|
||||||
relation: Box::new(ASTNode::SQLCompoundIdentifier(vec![relation.into()])),
|
name: SQLObjectName(vec![relation.into()]),
|
||||||
alias,
|
alias,
|
||||||
},
|
},
|
||||||
join_operator: f(JoinConstraint::Using(vec!["c1".into()])),
|
join_operator: f(JoinConstraint::Using(vec!["c1".into()])),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue