mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-25 06:59:15 +00:00
Support RENAME for wildcard SELECTs (#784)
This commit is contained in:
parent
98403c07b1
commit
17f604f757
5 changed files with 214 additions and 117 deletions
|
@ -34,10 +34,10 @@ pub use self::ddl::{
|
||||||
};
|
};
|
||||||
pub use self::operator::{BinaryOperator, UnaryOperator};
|
pub use self::operator::{BinaryOperator, UnaryOperator};
|
||||||
pub use self::query::{
|
pub use self::query::{
|
||||||
Cte, ExceptSelectItem, ExcludeSelectItem, Fetch, Join, JoinConstraint, JoinOperator,
|
Cte, ExceptSelectItem, ExcludeSelectItem, Fetch, IdentWithAlias, Join, JoinConstraint,
|
||||||
LateralView, LockClause, LockType, NonBlock, Offset, OffsetRows, OrderByExpr, Query, Select,
|
JoinOperator, LateralView, LockClause, LockType, NonBlock, Offset, OffsetRows, OrderByExpr,
|
||||||
SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, Table, TableAlias, TableFactor,
|
Query, RenameSelectItem, Select, SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier,
|
||||||
TableWithJoins, Top, Values, WildcardAdditionalOptions, With,
|
Table, TableAlias, TableFactor, TableWithJoins, Top, Values, WildcardAdditionalOptions, With,
|
||||||
};
|
};
|
||||||
pub use self::value::{
|
pub use self::value::{
|
||||||
escape_quoted_string, DateTimeField, DollarQuotedString, TrimWhereField, Value,
|
escape_quoted_string, DateTimeField, DollarQuotedString, TrimWhereField, Value,
|
||||||
|
|
|
@ -365,7 +365,27 @@ pub enum SelectItem {
|
||||||
Wildcard(WildcardAdditionalOptions),
|
Wildcard(WildcardAdditionalOptions),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Additional options for wildcards, e.g. Snowflake `EXCLUDE` and Bigquery `EXCEPT`.
|
/// Single aliased identifier
|
||||||
|
///
|
||||||
|
/// # Syntax
|
||||||
|
/// ```plaintext
|
||||||
|
/// <ident> AS <alias>
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "visitor", derive(Visit))]
|
||||||
|
pub struct IdentWithAlias {
|
||||||
|
pub ident: Ident,
|
||||||
|
pub alias: Ident,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for IdentWithAlias {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{} AS {}", self.ident, self.alias)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Additional options for wildcards, e.g. Snowflake `EXCLUDE`/`RENAME` and Bigquery `EXCEPT`.
|
||||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||||
|
@ -374,6 +394,8 @@ pub struct WildcardAdditionalOptions {
|
||||||
pub opt_exclude: Option<ExcludeSelectItem>,
|
pub opt_exclude: Option<ExcludeSelectItem>,
|
||||||
/// `[EXCEPT...]`.
|
/// `[EXCEPT...]`.
|
||||||
pub opt_except: Option<ExceptSelectItem>,
|
pub opt_except: Option<ExceptSelectItem>,
|
||||||
|
/// `[RENAME ...]`.
|
||||||
|
pub opt_rename: Option<RenameSelectItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for WildcardAdditionalOptions {
|
impl fmt::Display for WildcardAdditionalOptions {
|
||||||
|
@ -384,6 +406,9 @@ impl fmt::Display for WildcardAdditionalOptions {
|
||||||
if let Some(except) = &self.opt_except {
|
if let Some(except) = &self.opt_except {
|
||||||
write!(f, " {except}")?;
|
write!(f, " {except}")?;
|
||||||
}
|
}
|
||||||
|
if let Some(rename) = &self.opt_rename {
|
||||||
|
write!(f, " {rename}")?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -429,6 +454,47 @@ impl fmt::Display for ExcludeSelectItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Snowflake `RENAME` information.
|
||||||
|
///
|
||||||
|
/// # Syntax
|
||||||
|
/// ```plaintext
|
||||||
|
/// <col_name> AS <col_alias>
|
||||||
|
/// | (<col_name> AS <col_alias>, <col_name> AS <col_alias>, ...)
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "visitor", derive(Visit))]
|
||||||
|
pub enum RenameSelectItem {
|
||||||
|
/// Single column name with alias without parenthesis.
|
||||||
|
///
|
||||||
|
/// # Syntax
|
||||||
|
/// ```plaintext
|
||||||
|
/// <col_name> AS <col_alias>
|
||||||
|
/// ```
|
||||||
|
Single(IdentWithAlias),
|
||||||
|
/// Multiple column names with aliases inside parenthesis.
|
||||||
|
/// # Syntax
|
||||||
|
/// ```plaintext
|
||||||
|
/// (<col_name> AS <col_alias>, <col_name> AS <col_alias>, ...)
|
||||||
|
/// ```
|
||||||
|
Multiple(Vec<IdentWithAlias>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for RenameSelectItem {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "RENAME")?;
|
||||||
|
match self {
|
||||||
|
Self::Single(column) => {
|
||||||
|
write!(f, " {column}")?;
|
||||||
|
}
|
||||||
|
Self::Multiple(columns) => {
|
||||||
|
write!(f, " ({})", display_comma_separated(columns))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Bigquery `EXCEPT` information, with at least one column.
|
/// Bigquery `EXCEPT` information, with at least one column.
|
||||||
///
|
///
|
||||||
/// # Syntax
|
/// # Syntax
|
||||||
|
@ -440,7 +506,7 @@ impl fmt::Display for ExcludeSelectItem {
|
||||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||||
pub struct ExceptSelectItem {
|
pub struct ExceptSelectItem {
|
||||||
/// First guaranteed column.
|
/// First guaranteed column.
|
||||||
pub fist_elemnt: Ident,
|
pub first_element: Ident,
|
||||||
/// Additional columns. This list can be empty.
|
/// Additional columns. This list can be empty.
|
||||||
pub additional_elements: Vec<Ident>,
|
pub additional_elements: Vec<Ident>,
|
||||||
}
|
}
|
||||||
|
@ -449,12 +515,12 @@ impl fmt::Display for ExceptSelectItem {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "EXCEPT ")?;
|
write!(f, "EXCEPT ")?;
|
||||||
if self.additional_elements.is_empty() {
|
if self.additional_elements.is_empty() {
|
||||||
write!(f, "({})", self.fist_elemnt)?;
|
write!(f, "({})", self.first_element)?;
|
||||||
} else {
|
} else {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"({}, {})",
|
"({}, {})",
|
||||||
self.fist_elemnt,
|
self.first_element,
|
||||||
display_comma_separated(&self.additional_elements)
|
display_comma_separated(&self.additional_elements)
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4401,6 +4401,14 @@ impl<'a> Parser<'a> {
|
||||||
Ok(values)
|
Ok(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Strictly parse `identifier AS identifier`
|
||||||
|
pub fn parse_identifier_with_alias(&mut self) -> Result<IdentWithAlias, ParserError> {
|
||||||
|
let ident = self.parse_identifier()?;
|
||||||
|
self.expect_keyword(Keyword::AS)?;
|
||||||
|
let alias = self.parse_identifier()?;
|
||||||
|
Ok(IdentWithAlias { ident, alias })
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse `AS identifier` (or simply `identifier` if it's not a reserved keyword)
|
/// Parse `AS identifier` (or simply `identifier` if it's not a reserved keyword)
|
||||||
/// Some examples with aliases: `SELECT 1 foo`, `SELECT COUNT(*) AS cnt`,
|
/// Some examples with aliases: `SELECT 1 foo`, `SELECT COUNT(*) AS cnt`,
|
||||||
/// `SELECT ... FROM t1 foo, t2 bar`, `SELECT ... FROM (...) AS bar`
|
/// `SELECT ... FROM t1 foo, t2 bar`, `SELECT ... FROM (...) AS bar`
|
||||||
|
@ -4432,7 +4440,7 @@ impl<'a> Parser<'a> {
|
||||||
// ignore the <separator> and treat the multiple strings as
|
// ignore the <separator> and treat the multiple strings as
|
||||||
// a single <literal>."
|
// a single <literal>."
|
||||||
Token::SingleQuotedString(s) => Ok(Some(Ident::with_quote('\'', s))),
|
Token::SingleQuotedString(s) => Ok(Some(Ident::with_quote('\'', s))),
|
||||||
// Support for MySql dialect double qouted string, `AS "HOUR"` for example
|
// Support for MySql dialect double quoted string, `AS "HOUR"` for example
|
||||||
Token::DoubleQuotedString(s) => Ok(Some(Ident::with_quote('\"', s))),
|
Token::DoubleQuotedString(s) => Ok(Some(Ident::with_quote('\"', s))),
|
||||||
_ => {
|
_ => {
|
||||||
if after_as {
|
if after_as {
|
||||||
|
@ -6063,10 +6071,16 @@ impl<'a> Parser<'a> {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
let opt_rename = if dialect_of!(self is GenericDialect | SnowflakeDialect) {
|
||||||
|
self.parse_optional_select_item_rename()?
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
Ok(WildcardAdditionalOptions {
|
Ok(WildcardAdditionalOptions {
|
||||||
opt_exclude,
|
opt_exclude,
|
||||||
opt_except,
|
opt_except,
|
||||||
|
opt_rename,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6108,7 +6122,7 @@ impl<'a> Parser<'a> {
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
[first, idents @ ..] => Some(ExceptSelectItem {
|
[first, idents @ ..] => Some(ExceptSelectItem {
|
||||||
fist_elemnt: first.clone(),
|
first_element: first.clone(),
|
||||||
additional_elements: idents.to_vec(),
|
additional_elements: idents.to_vec(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
@ -6119,6 +6133,27 @@ impl<'a> Parser<'a> {
|
||||||
Ok(opt_except)
|
Ok(opt_except)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a [`Rename`](RenameSelectItem) information for wildcard select items.
|
||||||
|
pub fn parse_optional_select_item_rename(
|
||||||
|
&mut self,
|
||||||
|
) -> Result<Option<RenameSelectItem>, ParserError> {
|
||||||
|
let opt_rename = if self.parse_keyword(Keyword::RENAME) {
|
||||||
|
if self.consume_token(&Token::LParen) {
|
||||||
|
let idents =
|
||||||
|
self.parse_comma_separated(|parser| parser.parse_identifier_with_alias())?;
|
||||||
|
self.expect_token(&Token::RParen)?;
|
||||||
|
Some(RenameSelectItem::Multiple(idents))
|
||||||
|
} else {
|
||||||
|
let ident = self.parse_identifier_with_alias()?;
|
||||||
|
Some(RenameSelectItem::Single(ident))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(opt_rename)
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse an expression, optionally followed by ASC or DESC (used in ORDER BY)
|
/// Parse an expression, optionally followed by ASC or DESC (used in ORDER BY)
|
||||||
pub fn parse_order_by_expr(&mut self) -> Result<OrderByExpr, ParserError> {
|
pub fn parse_order_by_expr(&mut self) -> Result<OrderByExpr, ParserError> {
|
||||||
let expr = self.parse_expr()?;
|
let expr = self.parse_expr()?;
|
||||||
|
|
|
@ -265,51 +265,26 @@ fn parse_array_agg_func() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_select_wildcard_with_except() {
|
fn test_select_wildcard_with_except() {
|
||||||
match bigquery_and_generic().verified_stmt("SELECT * EXCEPT (col_a) FROM data") {
|
let select = bigquery_and_generic().verified_only_select("SELECT * EXCEPT (col_a) FROM data");
|
||||||
Statement::Query(query) => match *query.body {
|
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
|
||||||
SetExpr::Select(select) => match &select.projection[0] {
|
opt_except: Some(ExceptSelectItem {
|
||||||
SelectItem::Wildcard(WildcardAdditionalOptions {
|
first_element: Ident::new("col_a"),
|
||||||
opt_except: Some(except),
|
additional_elements: vec![],
|
||||||
..
|
}),
|
||||||
}) => {
|
..Default::default()
|
||||||
assert_eq!(
|
});
|
||||||
*except,
|
assert_eq!(expected, select.projection[0]);
|
||||||
ExceptSelectItem {
|
|
||||||
fist_elemnt: Ident::new("col_a"),
|
|
||||||
additional_elements: vec![]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
match bigquery_and_generic()
|
let select = bigquery_and_generic()
|
||||||
.verified_stmt("SELECT * EXCEPT (department_id, employee_id) FROM employee_table")
|
.verified_only_select("SELECT * EXCEPT (department_id, employee_id) FROM employee_table");
|
||||||
{
|
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
|
||||||
Statement::Query(query) => match *query.body {
|
opt_except: Some(ExceptSelectItem {
|
||||||
SetExpr::Select(select) => match &select.projection[0] {
|
first_element: Ident::new("department_id"),
|
||||||
SelectItem::Wildcard(WildcardAdditionalOptions {
|
additional_elements: vec![Ident::new("employee_id")],
|
||||||
opt_except: Some(except),
|
}),
|
||||||
..
|
..Default::default()
|
||||||
}) => {
|
});
|
||||||
assert_eq!(
|
assert_eq!(expected, select.projection[0]);
|
||||||
*except,
|
|
||||||
ExceptSelectItem {
|
|
||||||
fist_elemnt: Ident::new("department_id"),
|
|
||||||
additional_elements: vec![Ident::new("employee_id")]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
bigquery_and_generic()
|
bigquery_and_generic()
|
||||||
|
|
|
@ -389,70 +389,91 @@ fn snowflake_and_generic() -> TestedDialects {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_select_wildcard_with_exclude() {
|
fn test_select_wildcard_with_exclude() {
|
||||||
match snowflake_and_generic().verified_stmt("SELECT * EXCLUDE (col_a) FROM data") {
|
let select = snowflake_and_generic().verified_only_select("SELECT * EXCLUDE (col_a) FROM data");
|
||||||
Statement::Query(query) => match *query.body {
|
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
|
||||||
SetExpr::Select(select) => match &select.projection[0] {
|
opt_exclude: Some(ExcludeSelectItem::Multiple(vec![Ident::new("col_a")])),
|
||||||
SelectItem::Wildcard(WildcardAdditionalOptions {
|
..Default::default()
|
||||||
opt_exclude: Some(exclude),
|
});
|
||||||
..
|
assert_eq!(expected, select.projection[0]);
|
||||||
}) => {
|
|
||||||
assert_eq!(
|
|
||||||
*exclude,
|
|
||||||
ExcludeSelectItem::Multiple(vec![Ident::new("col_a")])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
match snowflake_and_generic()
|
let select = snowflake_and_generic()
|
||||||
.verified_stmt("SELECT name.* EXCLUDE department_id FROM employee_table")
|
.verified_only_select("SELECT name.* EXCLUDE department_id FROM employee_table");
|
||||||
{
|
let expected = SelectItem::QualifiedWildcard(
|
||||||
Statement::Query(query) => match *query.body {
|
ObjectName(vec![Ident::new("name")]),
|
||||||
SetExpr::Select(select) => match &select.projection[0] {
|
WildcardAdditionalOptions {
|
||||||
SelectItem::QualifiedWildcard(
|
opt_exclude: Some(ExcludeSelectItem::Single(Ident::new("department_id"))),
|
||||||
_,
|
..Default::default()
|
||||||
WildcardAdditionalOptions {
|
|
||||||
opt_exclude: Some(exclude),
|
|
||||||
..
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
assert_eq!(
|
|
||||||
*exclude,
|
|
||||||
ExcludeSelectItem::Single(Ident::new("department_id"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
},
|
||||||
_ => unreachable!(),
|
);
|
||||||
};
|
assert_eq!(expected, select.projection[0]);
|
||||||
|
|
||||||
match snowflake_and_generic()
|
let select = snowflake_and_generic()
|
||||||
.verified_stmt("SELECT * EXCLUDE (department_id, employee_id) FROM employee_table")
|
.verified_only_select("SELECT * EXCLUDE (department_id, employee_id) FROM employee_table");
|
||||||
{
|
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
|
||||||
Statement::Query(query) => match *query.body {
|
opt_exclude: Some(ExcludeSelectItem::Multiple(vec![
|
||||||
SetExpr::Select(select) => match &select.projection[0] {
|
Ident::new("department_id"),
|
||||||
SelectItem::Wildcard(WildcardAdditionalOptions {
|
Ident::new("employee_id"),
|
||||||
opt_exclude: Some(exclude),
|
])),
|
||||||
..
|
..Default::default()
|
||||||
}) => {
|
});
|
||||||
assert_eq!(
|
assert_eq!(expected, select.projection[0]);
|
||||||
*exclude,
|
}
|
||||||
ExcludeSelectItem::Multiple(vec![
|
|
||||||
Ident::new("department_id"),
|
#[test]
|
||||||
Ident::new("employee_id")
|
fn test_select_wildcard_with_rename() {
|
||||||
])
|
let select =
|
||||||
)
|
snowflake_and_generic().verified_only_select("SELECT * RENAME col_a AS col_b FROM data");
|
||||||
}
|
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
|
||||||
_ => unreachable!(),
|
opt_rename: Some(RenameSelectItem::Single(IdentWithAlias {
|
||||||
},
|
ident: Ident::new("col_a"),
|
||||||
_ => unreachable!(),
|
alias: Ident::new("col_b"),
|
||||||
},
|
})),
|
||||||
_ => unreachable!(),
|
..Default::default()
|
||||||
};
|
});
|
||||||
|
assert_eq!(expected, select.projection[0]);
|
||||||
|
|
||||||
|
let select = snowflake_and_generic().verified_only_select(
|
||||||
|
"SELECT name.* RENAME (department_id AS new_dep, employee_id AS new_emp) FROM employee_table",
|
||||||
|
);
|
||||||
|
let expected = SelectItem::QualifiedWildcard(
|
||||||
|
ObjectName(vec![Ident::new("name")]),
|
||||||
|
WildcardAdditionalOptions {
|
||||||
|
opt_rename: Some(RenameSelectItem::Multiple(vec![
|
||||||
|
IdentWithAlias {
|
||||||
|
ident: Ident::new("department_id"),
|
||||||
|
alias: Ident::new("new_dep"),
|
||||||
|
},
|
||||||
|
IdentWithAlias {
|
||||||
|
ident: Ident::new("employee_id"),
|
||||||
|
alias: Ident::new("new_emp"),
|
||||||
|
},
|
||||||
|
])),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert_eq!(expected, select.projection[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_select_wildcard_with_exclude_and_rename() {
|
||||||
|
let select = snowflake_and_generic()
|
||||||
|
.verified_only_select("SELECT * EXCLUDE col_z RENAME col_a AS col_b FROM data");
|
||||||
|
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
|
||||||
|
opt_exclude: Some(ExcludeSelectItem::Single(Ident::new("col_z"))),
|
||||||
|
opt_rename: Some(RenameSelectItem::Single(IdentWithAlias {
|
||||||
|
ident: Ident::new("col_a"),
|
||||||
|
alias: Ident::new("col_b"),
|
||||||
|
})),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
assert_eq!(expected, select.projection[0]);
|
||||||
|
|
||||||
|
// rename cannot precede exclude
|
||||||
|
assert_eq!(
|
||||||
|
snowflake_and_generic()
|
||||||
|
.parse_sql_statements("SELECT * RENAME col_a AS col_b EXCLUDE col_z FROM data")
|
||||||
|
.unwrap_err()
|
||||||
|
.to_string(),
|
||||||
|
"sql parser error: Expected end of statement, found: EXCLUDE"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue