mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-18 19:59:48 +00:00
Merge pull request #98 from benesch/col-names
Support column names in more places
This commit is contained in:
commit
4c2722280d
4 changed files with 97 additions and 17 deletions
|
@ -25,7 +25,7 @@ use std::ops::Deref;
|
||||||
pub use self::ddl::{AlterTableOperation, TableConstraint};
|
pub use self::ddl::{AlterTableOperation, TableConstraint};
|
||||||
pub use self::query::{
|
pub use self::query::{
|
||||||
Cte, Fetch, Join, JoinConstraint, JoinOperator, SQLOrderByExpr, SQLQuery, SQLSelect,
|
Cte, Fetch, Join, JoinConstraint, JoinOperator, SQLOrderByExpr, SQLQuery, SQLSelect,
|
||||||
SQLSelectItem, SQLSetExpr, SQLSetOperator, SQLValues, TableFactor,
|
SQLSelectItem, SQLSetExpr, SQLSetOperator, SQLValues, TableAlias, TableFactor,
|
||||||
};
|
};
|
||||||
pub use self::sqltype::SQLType;
|
pub use self::sqltype::SQLType;
|
||||||
pub use self::value::Value;
|
pub use self::value::Value;
|
||||||
|
@ -377,6 +377,7 @@ pub enum SQLStatement {
|
||||||
SQLCreateView {
|
SQLCreateView {
|
||||||
/// View name
|
/// View name
|
||||||
name: SQLObjectName,
|
name: SQLObjectName,
|
||||||
|
columns: Vec<SQLIdent>,
|
||||||
query: Box<SQLQuery>,
|
query: Box<SQLQuery>,
|
||||||
materialized: bool,
|
materialized: bool,
|
||||||
with_options: Vec<SQLOption>,
|
with_options: Vec<SQLOption>,
|
||||||
|
@ -474,6 +475,7 @@ impl ToString for SQLStatement {
|
||||||
}
|
}
|
||||||
SQLStatement::SQLCreateView {
|
SQLStatement::SQLCreateView {
|
||||||
name,
|
name,
|
||||||
|
columns,
|
||||||
query,
|
query,
|
||||||
materialized,
|
materialized,
|
||||||
with_options,
|
with_options,
|
||||||
|
@ -484,12 +486,18 @@ impl ToString for SQLStatement {
|
||||||
} else {
|
} else {
|
||||||
"".into()
|
"".into()
|
||||||
};
|
};
|
||||||
|
let columns = if !columns.is_empty() {
|
||||||
|
format!(" ({})", comma_separated_string(columns))
|
||||||
|
} else {
|
||||||
|
"".into()
|
||||||
|
};
|
||||||
format!(
|
format!(
|
||||||
"CREATE{} VIEW {}{} AS {}",
|
"CREATE{} VIEW {}{}{} AS {}",
|
||||||
modifier,
|
modifier,
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
with_options,
|
with_options,
|
||||||
query.to_string()
|
columns,
|
||||||
|
query.to_string(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SQLStatement::SQLCreateTable {
|
SQLStatement::SQLCreateTable {
|
||||||
|
|
|
@ -202,7 +202,7 @@ impl ToString for SQLSelectItem {
|
||||||
pub enum TableFactor {
|
pub enum TableFactor {
|
||||||
Table {
|
Table {
|
||||||
name: SQLObjectName,
|
name: SQLObjectName,
|
||||||
alias: Option<SQLIdent>,
|
alias: Option<TableAlias>,
|
||||||
/// Arguments of a table-valued function, as supported by Postgres
|
/// Arguments of a table-valued function, as supported by Postgres
|
||||||
/// and MSSQL. Note that deprecated MSSQL `FROM foo (NOLOCK)` syntax
|
/// and MSSQL. Note that deprecated MSSQL `FROM foo (NOLOCK)` syntax
|
||||||
/// will also be parsed as `args`.
|
/// will also be parsed as `args`.
|
||||||
|
@ -213,7 +213,7 @@ pub enum TableFactor {
|
||||||
Derived {
|
Derived {
|
||||||
lateral: bool,
|
lateral: bool,
|
||||||
subquery: Box<SQLQuery>,
|
subquery: Box<SQLQuery>,
|
||||||
alias: Option<SQLIdent>,
|
alias: Option<TableAlias>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ impl ToString for TableFactor {
|
||||||
s += &format!("({})", comma_separated_string(args))
|
s += &format!("({})", comma_separated_string(args))
|
||||||
};
|
};
|
||||||
if let Some(alias) = alias {
|
if let Some(alias) = alias {
|
||||||
s += &format!(" AS {}", alias);
|
s += &format!(" AS {}", alias.to_string());
|
||||||
}
|
}
|
||||||
if !with_hints.is_empty() {
|
if !with_hints.is_empty() {
|
||||||
s += &format!(" WITH ({})", comma_separated_string(with_hints));
|
s += &format!(" WITH ({})", comma_separated_string(with_hints));
|
||||||
|
@ -249,7 +249,7 @@ impl ToString for TableFactor {
|
||||||
}
|
}
|
||||||
s += &format!("({})", subquery.to_string());
|
s += &format!("({})", subquery.to_string());
|
||||||
if let Some(alias) = alias {
|
if let Some(alias) = alias {
|
||||||
s += &format!(" AS {}", alias);
|
s += &format!(" AS {}", alias.to_string());
|
||||||
}
|
}
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
@ -257,6 +257,22 @@ impl ToString for TableFactor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||||
|
pub struct TableAlias {
|
||||||
|
pub name: SQLIdent,
|
||||||
|
pub columns: Vec<SQLIdent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for TableAlias {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
let mut s = self.name.clone();
|
||||||
|
if !self.columns.is_empty() {
|
||||||
|
s += &format!(" ({})", comma_separated_string(&self.columns));
|
||||||
|
}
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||||
pub struct Join {
|
pub struct Join {
|
||||||
pub relation: TableFactor,
|
pub relation: TableFactor,
|
||||||
|
|
|
@ -775,7 +775,7 @@ impl Parser {
|
||||||
// Many dialects support `OR REPLACE` | `OR ALTER` right after `CREATE`, but we don't (yet).
|
// Many dialects support `OR REPLACE` | `OR ALTER` right after `CREATE`, but we don't (yet).
|
||||||
// ANSI SQL and Postgres support RECURSIVE here, but we don't support it either.
|
// ANSI SQL and Postgres support RECURSIVE here, but we don't support it either.
|
||||||
let name = self.parse_object_name()?;
|
let name = self.parse_object_name()?;
|
||||||
// Parenthesized "output" columns list could be handled here.
|
let columns = self.parse_parenthesized_column_list(Optional)?;
|
||||||
let with_options = if self.parse_keyword("WITH") {
|
let with_options = if self.parse_keyword("WITH") {
|
||||||
self.parse_with_options()?
|
self.parse_with_options()?
|
||||||
} else {
|
} else {
|
||||||
|
@ -786,6 +786,7 @@ impl Parser {
|
||||||
// Optional `WITH [ CASCADED | LOCAL ] CHECK OPTION` is widely supported here.
|
// Optional `WITH [ CASCADED | LOCAL ] CHECK OPTION` is widely supported here.
|
||||||
Ok(SQLStatement::SQLCreateView {
|
Ok(SQLStatement::SQLCreateView {
|
||||||
name,
|
name,
|
||||||
|
columns,
|
||||||
query,
|
query,
|
||||||
materialized,
|
materialized,
|
||||||
with_options,
|
with_options,
|
||||||
|
@ -1207,6 +1208,23 @@ impl Parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse `AS identifier` when the AS is describing a table-valued object,
|
||||||
|
/// like in `... FROM generate_series(1, 10) AS t (col)`. In this case
|
||||||
|
/// the alias is allowed to optionally name the columns in the table, in
|
||||||
|
/// addition to the table itself.
|
||||||
|
pub fn parse_optional_table_alias(
|
||||||
|
&mut self,
|
||||||
|
reserved_kwds: &[&str],
|
||||||
|
) -> Result<Option<TableAlias>, ParserError> {
|
||||||
|
match self.parse_optional_alias(reserved_kwds)? {
|
||||||
|
Some(name) => {
|
||||||
|
let columns = self.parse_parenthesized_column_list(Optional)?;
|
||||||
|
Ok(Some(TableAlias { name, columns }))
|
||||||
|
}
|
||||||
|
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_list_of_ids(&mut self, separator: &Token) -> Result<Vec<SQLIdent>, ParserError> {
|
pub fn parse_list_of_ids(&mut self, separator: &Token) -> Result<Vec<SQLIdent>, ParserError> {
|
||||||
let mut idents = vec![];
|
let mut idents = vec![];
|
||||||
|
@ -1490,7 +1508,7 @@ impl Parser {
|
||||||
if self.consume_token(&Token::LParen) {
|
if self.consume_token(&Token::LParen) {
|
||||||
let subquery = Box::new(self.parse_query()?);
|
let subquery = Box::new(self.parse_query()?);
|
||||||
self.expect_token(&Token::RParen)?;
|
self.expect_token(&Token::RParen)?;
|
||||||
let alias = self.parse_optional_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?;
|
let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?;
|
||||||
Ok(TableFactor::Derived {
|
Ok(TableFactor::Derived {
|
||||||
lateral,
|
lateral,
|
||||||
subquery,
|
subquery,
|
||||||
|
@ -1506,7 +1524,7 @@ impl Parser {
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
let alias = self.parse_optional_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?;
|
let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?;
|
||||||
// MSSQL-specific table hints:
|
// MSSQL-specific table hints:
|
||||||
let mut with_hints = vec![];
|
let mut with_hints = vec![];
|
||||||
if self.parse_keyword("WITH") {
|
if self.parse_keyword("WITH") {
|
||||||
|
|
|
@ -1083,7 +1083,7 @@ fn parse_delimited_identifiers() {
|
||||||
with_hints,
|
with_hints,
|
||||||
} => {
|
} => {
|
||||||
assert_eq!(vec![r#""a table""#.to_string()], name.0);
|
assert_eq!(vec![r#""a table""#.to_string()], name.0);
|
||||||
assert_eq!(r#""alias""#, alias.unwrap());
|
assert_eq!(r#""alias""#, alias.unwrap().name);
|
||||||
assert!(args.is_empty());
|
assert!(args.is_empty());
|
||||||
assert!(with_hints.is_empty());
|
assert!(with_hints.is_empty());
|
||||||
}
|
}
|
||||||
|
@ -1236,11 +1236,18 @@ fn parse_cross_join() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn table_alias(name: impl Into<String>) -> Option<TableAlias> {
|
||||||
|
Some(TableAlias {
|
||||||
|
name: name.into(),
|
||||||
|
columns: vec![],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
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>,
|
alias: Option<TableAlias>,
|
||||||
f: impl Fn(JoinConstraint) -> JoinOperator,
|
f: impl Fn(JoinConstraint) -> JoinOperator,
|
||||||
) -> Join {
|
) -> Join {
|
||||||
Join {
|
Join {
|
||||||
|
@ -1262,7 +1269,7 @@ fn parse_joins_on() {
|
||||||
verified_only_select("SELECT * FROM t1 JOIN t2 AS foo ON c1 = c2").joins,
|
verified_only_select("SELECT * FROM t1 JOIN t2 AS foo ON c1 = c2").joins,
|
||||||
vec![join_with_constraint(
|
vec![join_with_constraint(
|
||||||
"t2",
|
"t2",
|
||||||
Some("foo".to_string()),
|
table_alias("foo"),
|
||||||
JoinOperator::Inner
|
JoinOperator::Inner
|
||||||
)]
|
)]
|
||||||
);
|
);
|
||||||
|
@ -1293,7 +1300,7 @@ 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>,
|
alias: Option<TableAlias>,
|
||||||
f: impl Fn(JoinConstraint) -> JoinOperator,
|
f: impl Fn(JoinConstraint) -> JoinOperator,
|
||||||
) -> Join {
|
) -> Join {
|
||||||
Join {
|
Join {
|
||||||
|
@ -1311,7 +1318,7 @@ fn parse_joins_using() {
|
||||||
verified_only_select("SELECT * FROM t1 JOIN t2 AS foo USING(c1)").joins,
|
verified_only_select("SELECT * FROM t1 JOIN t2 AS foo USING(c1)").joins,
|
||||||
vec![join_with_constraint(
|
vec![join_with_constraint(
|
||||||
"t2",
|
"t2",
|
||||||
Some("foo".to_string()),
|
table_alias("foo"),
|
||||||
JoinOperator::Inner
|
JoinOperator::Inner
|
||||||
)]
|
)]
|
||||||
);
|
);
|
||||||
|
@ -1471,6 +1478,12 @@ fn parse_derived_tables() {
|
||||||
let sql = "SELECT a.x, b.y FROM (SELECT x FROM foo) AS a CROSS JOIN (SELECT y FROM bar) AS b";
|
let sql = "SELECT a.x, b.y FROM (SELECT x FROM foo) AS a CROSS JOIN (SELECT y FROM bar) AS b";
|
||||||
let _ = verified_only_select(sql);
|
let _ = verified_only_select(sql);
|
||||||
//TODO: add assertions
|
//TODO: add assertions
|
||||||
|
|
||||||
|
let sql = "SELECT a.x, b.y \
|
||||||
|
FROM (SELECT x FROM foo) AS a (x) \
|
||||||
|
CROSS JOIN (SELECT y FROM bar) AS b (y)";
|
||||||
|
let _ = verified_only_select(sql);
|
||||||
|
//TODO: add assertions
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1595,11 +1608,13 @@ fn parse_create_view() {
|
||||||
match verified_stmt(sql) {
|
match verified_stmt(sql) {
|
||||||
SQLStatement::SQLCreateView {
|
SQLStatement::SQLCreateView {
|
||||||
name,
|
name,
|
||||||
|
columns,
|
||||||
query,
|
query,
|
||||||
materialized,
|
materialized,
|
||||||
with_options,
|
with_options,
|
||||||
} => {
|
} => {
|
||||||
assert_eq!("myschema.myview", name.to_string());
|
assert_eq!("myschema.myview", name.to_string());
|
||||||
|
assert_eq!(Vec::<SQLIdent>::new(), columns);
|
||||||
assert_eq!("SELECT foo FROM bar", query.to_string());
|
assert_eq!("SELECT foo FROM bar", query.to_string());
|
||||||
assert!(!materialized);
|
assert!(!materialized);
|
||||||
assert_eq!(with_options, vec![]);
|
assert_eq!(with_options, vec![]);
|
||||||
|
@ -1631,17 +1646,40 @@ fn parse_create_view_with_options() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_create_view_with_columns() {
|
||||||
|
let sql = "CREATE VIEW v (has, cols) AS SELECT 1, 2";
|
||||||
|
match verified_stmt(sql) {
|
||||||
|
SQLStatement::SQLCreateView {
|
||||||
|
name,
|
||||||
|
columns,
|
||||||
|
with_options,
|
||||||
|
query,
|
||||||
|
materialized,
|
||||||
|
} => {
|
||||||
|
assert_eq!("v", name.to_string());
|
||||||
|
assert_eq!(columns, vec!["has".to_string(), "cols".to_string()]);
|
||||||
|
assert_eq!(with_options, vec![]);
|
||||||
|
assert_eq!("SELECT 1, 2", query.to_string());
|
||||||
|
assert!(!materialized);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_create_materialized_view() {
|
fn parse_create_materialized_view() {
|
||||||
let sql = "CREATE MATERIALIZED VIEW myschema.myview AS SELECT foo FROM bar";
|
let sql = "CREATE MATERIALIZED VIEW myschema.myview AS SELECT foo FROM bar";
|
||||||
match verified_stmt(sql) {
|
match verified_stmt(sql) {
|
||||||
SQLStatement::SQLCreateView {
|
SQLStatement::SQLCreateView {
|
||||||
name,
|
name,
|
||||||
|
columns,
|
||||||
query,
|
query,
|
||||||
materialized,
|
materialized,
|
||||||
with_options,
|
with_options,
|
||||||
} => {
|
} => {
|
||||||
assert_eq!("myschema.myview", name.to_string());
|
assert_eq!("myschema.myview", name.to_string());
|
||||||
|
assert_eq!(Vec::<SQLIdent>::new(), columns);
|
||||||
assert_eq!("SELECT foo FROM bar", query.to_string());
|
assert_eq!("SELECT foo FROM bar", query.to_string());
|
||||||
assert!(materialized);
|
assert!(materialized);
|
||||||
assert_eq!(with_options, vec![]);
|
assert_eq!(with_options, vec![]);
|
||||||
|
@ -1928,11 +1966,11 @@ fn lateral_derived() {
|
||||||
if let TableFactor::Derived {
|
if let TableFactor::Derived {
|
||||||
lateral,
|
lateral,
|
||||||
ref subquery,
|
ref subquery,
|
||||||
ref alias,
|
alias: Some(ref alias),
|
||||||
} = select.joins[0].relation
|
} = select.joins[0].relation
|
||||||
{
|
{
|
||||||
assert_eq!(lateral_in, lateral);
|
assert_eq!(lateral_in, lateral);
|
||||||
assert_eq!(Some("order".to_string()), *alias);
|
assert_eq!("order".to_string(), alias.name);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
subquery.to_string(),
|
subquery.to_string(),
|
||||||
"SELECT * FROM order WHERE order.customer = customer.id LIMIT 3"
|
"SELECT * FROM order WHERE order.customer = customer.id LIMIT 3"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue