Skip to content

Commit 17f604f

Browse files
authored
Support RENAME for wildcard SELECTs (#784)
1 parent 98403c0 commit 17f604f

File tree

5 files changed

+215
-118
lines changed

5 files changed

+215
-118
lines changed

src/ast/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ pub use self::ddl::{
3434
};
3535
pub use self::operator::{BinaryOperator, UnaryOperator};
3636
pub use self::query::{
37-
Cte, ExceptSelectItem, ExcludeSelectItem, Fetch, Join, JoinConstraint, JoinOperator,
38-
LateralView, LockClause, LockType, NonBlock, Offset, OffsetRows, OrderByExpr, Query, Select,
39-
SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, Table, TableAlias, TableFactor,
40-
TableWithJoins, Top, Values, WildcardAdditionalOptions, With,
37+
Cte, ExceptSelectItem, ExcludeSelectItem, Fetch, IdentWithAlias, Join, JoinConstraint,
38+
JoinOperator, LateralView, LockClause, LockType, NonBlock, Offset, OffsetRows, OrderByExpr,
39+
Query, RenameSelectItem, Select, SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier,
40+
Table, TableAlias, TableFactor, TableWithJoins, Top, Values, WildcardAdditionalOptions, With,
4141
};
4242
pub use self::value::{
4343
escape_quoted_string, DateTimeField, DollarQuotedString, TrimWhereField, Value,

src/ast/query.rs

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,27 @@ pub enum SelectItem {
365365
Wildcard(WildcardAdditionalOptions),
366366
}
367367

368-
/// Additional options for wildcards, e.g. Snowflake `EXCLUDE` and Bigquery `EXCEPT`.
368+
/// Single aliased identifier
369+
///
370+
/// # Syntax
371+
/// ```plaintext
372+
/// <ident> AS <alias>
373+
/// ```
374+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
375+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
376+
#[cfg_attr(feature = "visitor", derive(Visit))]
377+
pub struct IdentWithAlias {
378+
pub ident: Ident,
379+
pub alias: Ident,
380+
}
381+
382+
impl fmt::Display for IdentWithAlias {
383+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
384+
write!(f, "{} AS {}", self.ident, self.alias)
385+
}
386+
}
387+
388+
/// Additional options for wildcards, e.g. Snowflake `EXCLUDE`/`RENAME` and Bigquery `EXCEPT`.
369389
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
370390
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
371391
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
@@ -374,6 +394,8 @@ pub struct WildcardAdditionalOptions {
374394
pub opt_exclude: Option<ExcludeSelectItem>,
375395
/// `[EXCEPT...]`.
376396
pub opt_except: Option<ExceptSelectItem>,
397+
/// `[RENAME ...]`.
398+
pub opt_rename: Option<RenameSelectItem>,
377399
}
378400

379401
impl fmt::Display for WildcardAdditionalOptions {
@@ -384,6 +406,9 @@ impl fmt::Display for WildcardAdditionalOptions {
384406
if let Some(except) = &self.opt_except {
385407
write!(f, " {except}")?;
386408
}
409+
if let Some(rename) = &self.opt_rename {
410+
write!(f, " {rename}")?;
411+
}
387412
Ok(())
388413
}
389414
}
@@ -429,6 +454,47 @@ impl fmt::Display for ExcludeSelectItem {
429454
}
430455
}
431456

457+
/// Snowflake `RENAME` information.
458+
///
459+
/// # Syntax
460+
/// ```plaintext
461+
/// <col_name> AS <col_alias>
462+
/// | (<col_name> AS <col_alias>, <col_name> AS <col_alias>, ...)
463+
/// ```
464+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
465+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
466+
#[cfg_attr(feature = "visitor", derive(Visit))]
467+
pub enum RenameSelectItem {
468+
/// Single column name with alias without parenthesis.
469+
///
470+
/// # Syntax
471+
/// ```plaintext
472+
/// <col_name> AS <col_alias>
473+
/// ```
474+
Single(IdentWithAlias),
475+
/// Multiple column names with aliases inside parenthesis.
476+
/// # Syntax
477+
/// ```plaintext
478+
/// (<col_name> AS <col_alias>, <col_name> AS <col_alias>, ...)
479+
/// ```
480+
Multiple(Vec<IdentWithAlias>),
481+
}
482+
483+
impl fmt::Display for RenameSelectItem {
484+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
485+
write!(f, "RENAME")?;
486+
match self {
487+
Self::Single(column) => {
488+
write!(f, " {column}")?;
489+
}
490+
Self::Multiple(columns) => {
491+
write!(f, " ({})", display_comma_separated(columns))?;
492+
}
493+
}
494+
Ok(())
495+
}
496+
}
497+
432498
/// Bigquery `EXCEPT` information, with at least one column.
433499
///
434500
/// # Syntax
@@ -440,7 +506,7 @@ impl fmt::Display for ExcludeSelectItem {
440506
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
441507
pub struct ExceptSelectItem {
442508
/// First guaranteed column.
443-
pub fist_elemnt: Ident,
509+
pub first_element: Ident,
444510
/// Additional columns. This list can be empty.
445511
pub additional_elements: Vec<Ident>,
446512
}
@@ -449,12 +515,12 @@ impl fmt::Display for ExceptSelectItem {
449515
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
450516
write!(f, "EXCEPT ")?;
451517
if self.additional_elements.is_empty() {
452-
write!(f, "({})", self.fist_elemnt)?;
518+
write!(f, "({})", self.first_element)?;
453519
} else {
454520
write!(
455521
f,
456522
"({}, {})",
457-
self.fist_elemnt,
523+
self.first_element,
458524
display_comma_separated(&self.additional_elements)
459525
)?;
460526
}

src/parser.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4401,6 +4401,14 @@ impl<'a> Parser<'a> {
44014401
Ok(values)
44024402
}
44034403

4404+
/// Strictly parse `identifier AS identifier`
4405+
pub fn parse_identifier_with_alias(&mut self) -> Result<IdentWithAlias, ParserError> {
4406+
let ident = self.parse_identifier()?;
4407+
self.expect_keyword(Keyword::AS)?;
4408+
let alias = self.parse_identifier()?;
4409+
Ok(IdentWithAlias { ident, alias })
4410+
}
4411+
44044412
/// Parse `AS identifier` (or simply `identifier` if it's not a reserved keyword)
44054413
/// Some examples with aliases: `SELECT 1 foo`, `SELECT COUNT(*) AS cnt`,
44064414
/// `SELECT ... FROM t1 foo, t2 bar`, `SELECT ... FROM (...) AS bar`
@@ -4432,7 +4440,7 @@ impl<'a> Parser<'a> {
44324440
// ignore the <separator> and treat the multiple strings as
44334441
// a single <literal>."
44344442
Token::SingleQuotedString(s) => Ok(Some(Ident::with_quote('\'', s))),
4435-
// Support for MySql dialect double qouted string, `AS "HOUR"` for example
4443+
// Support for MySql dialect double quoted string, `AS "HOUR"` for example
44364444
Token::DoubleQuotedString(s) => Ok(Some(Ident::with_quote('\"', s))),
44374445
_ => {
44384446
if after_as {
@@ -6063,10 +6071,16 @@ impl<'a> Parser<'a> {
60636071
} else {
60646072
None
60656073
};
6074+
let opt_rename = if dialect_of!(self is GenericDialect | SnowflakeDialect) {
6075+
self.parse_optional_select_item_rename()?
6076+
} else {
6077+
None
6078+
};
60666079

60676080
Ok(WildcardAdditionalOptions {
60686081
opt_exclude,
60696082
opt_except,
6083+
opt_rename,
60706084
})
60716085
}
60726086

@@ -6108,7 +6122,7 @@ impl<'a> Parser<'a> {
61086122
)?;
61096123
}
61106124
[first, idents @ ..] => Some(ExceptSelectItem {
6111-
fist_elemnt: first.clone(),
6125+
first_element: first.clone(),
61126126
additional_elements: idents.to_vec(),
61136127
}),
61146128
}
@@ -6119,6 +6133,27 @@ impl<'a> Parser<'a> {
61196133
Ok(opt_except)
61206134
}
61216135

6136+
/// Parse a [`Rename`](RenameSelectItem) information for wildcard select items.
6137+
pub fn parse_optional_select_item_rename(
6138+
&mut self,
6139+
) -> Result<Option<RenameSelectItem>, ParserError> {
6140+
let opt_rename = if self.parse_keyword(Keyword::RENAME) {
6141+
if self.consume_token(&Token::LParen) {
6142+
let idents =
6143+
self.parse_comma_separated(|parser| parser.parse_identifier_with_alias())?;
6144+
self.expect_token(&Token::RParen)?;
6145+
Some(RenameSelectItem::Multiple(idents))
6146+
} else {
6147+
let ident = self.parse_identifier_with_alias()?;
6148+
Some(RenameSelectItem::Single(ident))
6149+
}
6150+
} else {
6151+
None
6152+
};
6153+
6154+
Ok(opt_rename)
6155+
}
6156+
61226157
/// Parse an expression, optionally followed by ASC or DESC (used in ORDER BY)
61236158
pub fn parse_order_by_expr(&mut self) -> Result<OrderByExpr, ParserError> {
61246159
let expr = self.parse_expr()?;

tests/sqlparser_bigquery.rs

Lines changed: 19 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -265,51 +265,26 @@ fn parse_array_agg_func() {
265265

266266
#[test]
267267
fn test_select_wildcard_with_except() {
268-
match bigquery_and_generic().verified_stmt("SELECT * EXCEPT (col_a) FROM data") {
269-
Statement::Query(query) => match *query.body {
270-
SetExpr::Select(select) => match &select.projection[0] {
271-
SelectItem::Wildcard(WildcardAdditionalOptions {
272-
opt_except: Some(except),
273-
..
274-
}) => {
275-
assert_eq!(
276-
*except,
277-
ExceptSelectItem {
278-
fist_elemnt: Ident::new("col_a"),
279-
additional_elements: vec![]
280-
}
281-
)
282-
}
283-
_ => unreachable!(),
284-
},
285-
_ => unreachable!(),
286-
},
287-
_ => unreachable!(),
288-
};
268+
let select = bigquery_and_generic().verified_only_select("SELECT * EXCEPT (col_a) FROM data");
269+
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
270+
opt_except: Some(ExceptSelectItem {
271+
first_element: Ident::new("col_a"),
272+
additional_elements: vec![],
273+
}),
274+
..Default::default()
275+
});
276+
assert_eq!(expected, select.projection[0]);
289277

290-
match bigquery_and_generic()
291-
.verified_stmt("SELECT * EXCEPT (department_id, employee_id) FROM employee_table")
292-
{
293-
Statement::Query(query) => match *query.body {
294-
SetExpr::Select(select) => match &select.projection[0] {
295-
SelectItem::Wildcard(WildcardAdditionalOptions {
296-
opt_except: Some(except),
297-
..
298-
}) => {
299-
assert_eq!(
300-
*except,
301-
ExceptSelectItem {
302-
fist_elemnt: Ident::new("department_id"),
303-
additional_elements: vec![Ident::new("employee_id")]
304-
}
305-
)
306-
}
307-
_ => unreachable!(),
308-
},
309-
_ => unreachable!(),
310-
},
311-
_ => unreachable!(),
312-
};
278+
let select = bigquery_and_generic()
279+
.verified_only_select("SELECT * EXCEPT (department_id, employee_id) FROM employee_table");
280+
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
281+
opt_except: Some(ExceptSelectItem {
282+
first_element: Ident::new("department_id"),
283+
additional_elements: vec![Ident::new("employee_id")],
284+
}),
285+
..Default::default()
286+
});
287+
assert_eq!(expected, select.projection[0]);
313288

314289
assert_eq!(
315290
bigquery_and_generic()

0 commit comments

Comments
 (0)