diff --git a/src/ast/query.rs b/src/ast/query.rs index ff617a38e..c54b37914 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -933,6 +933,9 @@ pub struct WildcardAdditionalOptions { pub opt_replace: Option, /// `[RENAME ...]`. pub opt_rename: Option, + /// `[AS ]`. + /// Redshift syntax: + pub opt_alias: Option, } impl Default for WildcardAdditionalOptions { @@ -944,6 +947,7 @@ impl Default for WildcardAdditionalOptions { opt_except: None, opt_replace: None, opt_rename: None, + opt_alias: None, } } } @@ -965,6 +969,9 @@ impl fmt::Display for WildcardAdditionalOptions { if let Some(rename) = &self.opt_rename { write!(f, " {rename}")?; } + if let Some(alias) = &self.opt_alias { + write!(f, " AS {alias}")?; + } Ok(()) } } diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 128fe01be..e00cee3dc 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -1824,6 +1824,7 @@ impl Spanned for WildcardAdditionalOptions { opt_except, opt_replace, opt_rename, + opt_alias, } = self; union_spans( @@ -1832,7 +1833,8 @@ impl Spanned for WildcardAdditionalOptions { .chain(opt_exclude.as_ref().map(|i| i.span())) .chain(opt_rename.as_ref().map(|i| i.span())) .chain(opt_replace.as_ref().map(|i| i.span())) - .chain(opt_except.as_ref().map(|i| i.span())), + .chain(opt_except.as_ref().map(|i| i.span())) + .chain(opt_alias.as_ref().map(|i| i.span)), ) } } diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index b1be1590d..a3bd36646 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -1512,6 +1512,18 @@ pub trait Dialect: Debug + Any { false } + /// Returns true if this dialect supports aliasing a wildcard select item. + /// + /// Example: + /// ```sql + /// SELECT t.* AS alias FROM t + /// ``` + /// + /// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_SELECT_list.html) + fn supports_select_wildcard_with_alias(&self) -> bool { + false + } + /// Returns true if this dialect supports the `OPTIMIZE TABLE` statement. /// /// Example: diff --git a/src/dialect/redshift.rs b/src/dialect/redshift.rs index 21958e382..5969ee55e 100644 --- a/src/dialect/redshift.rs +++ b/src/dialect/redshift.rs @@ -141,6 +141,10 @@ impl Dialect for RedshiftSqlDialect { true } + fn supports_select_wildcard_with_alias(&self) -> bool { + true + } + fn supports_select_exclude(&self) -> bool { true } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 16eb7a8b1..91bdb1449 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -17847,6 +17847,16 @@ impl<'a> Parser<'a> { None }; + let opt_alias = if self.dialect.supports_select_wildcard_with_alias() { + if self.parse_keyword(Keyword::AS) { + Some(self.parse_identifier()?) + } else { + None + } + } else { + None + }; + Ok(WildcardAdditionalOptions { wildcard_token: wildcard_token.into(), opt_ilike, @@ -17854,6 +17864,7 @@ impl<'a> Parser<'a> { opt_except, opt_rename, opt_replace, + opt_alias, }) } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index a3b5404d3..3f0ca96a0 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -1280,6 +1280,26 @@ fn parse_select_expr_star() { dialects.verified_only_select("SELECT myfunc().* EXCEPT (foo) FROM T"); } +#[test] +fn parse_select_wildcard_with_alias() { + let dialects = all_dialects_where(|d| d.supports_select_wildcard_with_alias()); + + // qualified wildcard with alias + dialects + .parse_sql_statements("SELECT t.* AS all_cols FROM t") + .unwrap(); + + // unqualified wildcard with alias + dialects + .parse_sql_statements("SELECT * AS all_cols FROM t") + .unwrap(); + + // mixed: regular column + qualified wildcard with alias + dialects + .parse_sql_statements("SELECT a.id, b.* AS b_cols FROM a JOIN b ON (a.id = b.a_id)") + .unwrap(); +} + #[test] fn test_eof_after_as() { let res = parse_sql_statements("SELECT foo AS");