@@ -8848,38 +8848,76 @@ impl<'a> Parser<'a> {
8848
8848
Ok(IdentWithAlias { ident, alias })
8849
8849
}
8850
8850
8851
- /// Parse `AS identifier` (or simply `identifier` if it's not a reserved keyword)
8852
- /// Some examples with aliases: `SELECT 1 foo`, `SELECT COUNT(*) AS cnt`,
8853
- /// `SELECT ... FROM t1 foo, t2 bar`, `SELECT ... FROM (...) AS bar`
8851
+ /// Optionally parses an alias for a select list item
8852
+ fn maybe_parse_select_item_alias(&mut self) -> Result<Option<Ident>, ParserError> {
8853
+ fn validator(explicit: bool, kw: &Keyword, parser: &mut Parser) -> bool {
8854
+ parser.dialect.is_select_item_alias(explicit, kw, parser)
8855
+ }
8856
+ self.parse_optional_alias_inner(None, validator)
8857
+ }
8858
+
8859
+ /// Optionally parses an alias for a table like in `... FROM generate_series(1, 10) AS t (col)`.
8860
+ /// In this case, the alias is allowed to optionally name the columns in the table, in
8861
+ /// addition to the table itself.
8862
+ pub fn maybe_parse_table_alias(&mut self) -> Result<Option<TableAlias>, ParserError> {
8863
+ fn validator(explicit: bool, kw: &Keyword, parser: &mut Parser) -> bool {
8864
+ parser.dialect.is_table_factor_alias(explicit, kw, parser)
8865
+ }
8866
+ match self.parse_optional_alias_inner(None, validator)? {
8867
+ Some(name) => {
8868
+ let columns = self.parse_table_alias_column_defs()?;
8869
+ Ok(Some(TableAlias { name, columns }))
8870
+ }
8871
+ None => Ok(None),
8872
+ }
8873
+ }
8874
+
8875
+ /// Wrapper for parse_optional_alias_inner, left for backwards-compatibility
8876
+ /// but new flows should use the context-specific methods such as `maybe_parse_select_item_alias`
8877
+ /// and `maybe_parse_table_alias`.
8854
8878
pub fn parse_optional_alias(
8855
8879
&mut self,
8856
8880
reserved_kwds: &[Keyword],
8857
8881
) -> Result<Option<Ident>, ParserError> {
8882
+ fn validator(_explicit: bool, _kw: &Keyword, _parser: &mut Parser) -> bool {
8883
+ false
8884
+ }
8885
+ self.parse_optional_alias_inner(Some(reserved_kwds), validator)
8886
+ }
8887
+
8888
+ /// Parses an optional alias after a SQL element such as a select list item
8889
+ /// or a table name.
8890
+ ///
8891
+ /// This method accepts an optional list of reserved keywords or a function
8892
+ /// to call to validate if a keyword should be parsed as an alias, to allow
8893
+ /// callers to customize the parsing logic based on their context.
8894
+ fn parse_optional_alias_inner<F>(
8895
+ &mut self,
8896
+ reserved_kwds: Option<&[Keyword]>,
8897
+ validator: F,
8898
+ ) -> Result<Option<Ident>, ParserError>
8899
+ where
8900
+ F: Fn(bool, &Keyword, &mut Parser) -> bool,
8901
+ {
8858
8902
let after_as = self.parse_keyword(Keyword::AS);
8903
+
8859
8904
let next_token = self.next_token();
8860
8905
match next_token.token {
8861
- // Accept any identifier after `AS` (though many dialects have restrictions on
8862
- // keywords that may appear here). If there's no `AS`: don't parse keywords,
8863
- // which may start a construct allowed in this position, to be parsed as aliases.
8864
- // (For example, in `FROM t1 JOIN` the `JOIN` will always be parsed as a keyword,
8865
- // not an alias.)
8866
- Token::Word(w) if after_as || !reserved_kwds.contains(&w.keyword) => {
8906
+ // By default, if a word is located after the `AS` keyword we consider it an alias
8907
+ // as long as it's not reserved.
8908
+ Token::Word(w)
8909
+ if after_as || reserved_kwds.is_some_and(|x| !x.contains(&w.keyword)) =>
8910
+ {
8867
8911
Ok(Some(w.into_ident(next_token.span)))
8868
8912
}
8869
- // MSSQL supports single-quoted strings as aliases for columns
8870
- // We accept them as table aliases too, although MSSQL does not.
8871
- //
8872
- // Note, that this conflicts with an obscure rule from the SQL
8873
- // standard, which we don't implement:
8874
- // https://crate.io/docs/sql-99/en/latest/chapters/07.html#character-string-literal-s
8875
- // "[Obscure Rule] SQL allows you to break a long <character
8876
- // string literal> up into two or more smaller <character string
8877
- // literal>s, split by a <separator> that includes a newline
8878
- // character. When it sees such a <literal>, your DBMS will
8879
- // ignore the <separator> and treat the multiple strings as
8880
- // a single <literal>."
8913
+ // This pattern allows for customizing the acceptance of words as aliases based on the caller's
8914
+ // context, such as to what SQL element this word is a potential alias of (select item alias, table name
8915
+ // alias, etc.) or dialect-specific logic that goes beyond a simple list of reserved keywords.
8916
+ Token::Word(w) if validator(after_as, &w.keyword, self) => {
8917
+ Ok(Some(w.into_ident(next_token.span)))
8918
+ }
8919
+ // For backwards-compatibility, we accept quoted strings as aliases regardless of the context.
8881
8920
Token::SingleQuotedString(s) => Ok(Some(Ident::with_quote('\'', s))),
8882
- // Support for MySql dialect double-quoted string, `AS "HOUR"` for example
8883
8921
Token::DoubleQuotedString(s) => Ok(Some(Ident::with_quote('\"', s))),
8884
8922
_ => {
8885
8923
if after_as {
@@ -8891,23 +8929,6 @@ impl<'a> Parser<'a> {
8891
8929
}
8892
8930
}
8893
8931
8894
- /// Parse `AS identifier` when the AS is describing a table-valued object,
8895
- /// like in `... FROM generate_series(1, 10) AS t (col)`. In this case
8896
- /// the alias is allowed to optionally name the columns in the table, in
8897
- /// addition to the table itself.
8898
- pub fn parse_optional_table_alias(
8899
- &mut self,
8900
- reserved_kwds: &[Keyword],
8901
- ) -> Result<Option<TableAlias>, ParserError> {
8902
- match self.parse_optional_alias(reserved_kwds)? {
8903
- Some(name) => {
8904
- let columns = self.parse_table_alias_column_defs()?;
8905
- Ok(Some(TableAlias { name, columns }))
8906
- }
8907
- None => Ok(None),
8908
- }
8909
- }
8910
-
8911
8932
pub fn parse_optional_group_by(&mut self) -> Result<Option<GroupByExpr>, ParserError> {
8912
8933
if self.parse_keywords(&[Keyword::GROUP, Keyword::BY]) {
8913
8934
let expressions = if self.parse_keyword(Keyword::ALL) {
@@ -10909,7 +10930,7 @@ impl<'a> Parser<'a> {
10909
10930
let name = self.parse_object_name(false)?;
10910
10931
self.expect_token(&Token::LParen)?;
10911
10932
let args = self.parse_optional_args()?;
10912
- let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS )?;
10933
+ let alias = self.maybe_parse_table_alias( )?;
10913
10934
Ok(TableFactor::Function {
10914
10935
lateral: true,
10915
10936
name,
@@ -10922,7 +10943,7 @@ impl<'a> Parser<'a> {
10922
10943
self.expect_token(&Token::LParen)?;
10923
10944
let expr = self.parse_expr()?;
10924
10945
self.expect_token(&Token::RParen)?;
10925
- let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS )?;
10946
+ let alias = self.maybe_parse_table_alias( )?;
10926
10947
Ok(TableFactor::TableFunction { expr, alias })
10927
10948
} else if self.consume_token(&Token::LParen) {
10928
10949
// A left paren introduces either a derived table (i.e., a subquery)
@@ -10971,7 +10992,7 @@ impl<'a> Parser<'a> {
10971
10992
#[allow(clippy::if_same_then_else)]
10972
10993
if !table_and_joins.joins.is_empty() {
10973
10994
self.expect_token(&Token::RParen)?;
10974
- let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS )?;
10995
+ let alias = self.maybe_parse_table_alias( )?;
10975
10996
Ok(TableFactor::NestedJoin {
10976
10997
table_with_joins: Box::new(table_and_joins),
10977
10998
alias,
@@ -10984,7 +11005,7 @@ impl<'a> Parser<'a> {
10984
11005
// (B): `table_and_joins` (what we found inside the parentheses)
10985
11006
// is a nested join `(foo JOIN bar)`, not followed by other joins.
10986
11007
self.expect_token(&Token::RParen)?;
10987
- let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS )?;
11008
+ let alias = self.maybe_parse_table_alias( )?;
10988
11009
Ok(TableFactor::NestedJoin {
10989
11010
table_with_joins: Box::new(table_and_joins),
10990
11011
alias,
@@ -10998,9 +11019,7 @@ impl<'a> Parser<'a> {
10998
11019
// [AS alias])`) as well.
10999
11020
self.expect_token(&Token::RParen)?;
11000
11021
11001
- if let Some(outer_alias) =
11002
- self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?
11003
- {
11022
+ if let Some(outer_alias) = self.maybe_parse_table_alias()? {
11004
11023
// Snowflake also allows specifying an alias *after* parens
11005
11024
// e.g. `FROM (mytable) AS alias`
11006
11025
match &mut table_and_joins.relation {
@@ -11053,7 +11072,7 @@ impl<'a> Parser<'a> {
11053
11072
// SELECT * FROM VALUES (1, 'a'), (2, 'b') AS t (col1, col2)
11054
11073
// where there are no parentheses around the VALUES clause.
11055
11074
let values = SetExpr::Values(self.parse_values(false)?);
11056
- let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS )?;
11075
+ let alias = self.maybe_parse_table_alias( )?;
11057
11076
Ok(TableFactor::Derived {
11058
11077
lateral: false,
11059
11078
subquery: Box::new(Query {
@@ -11079,7 +11098,7 @@ impl<'a> Parser<'a> {
11079
11098
self.expect_token(&Token::RParen)?;
11080
11099
11081
11100
let with_ordinality = self.parse_keywords(&[Keyword::WITH, Keyword::ORDINALITY]);
11082
- let alias = match self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS ) {
11101
+ let alias = match self.maybe_parse_table_alias( ) {
11083
11102
Ok(Some(alias)) => Some(alias),
11084
11103
Ok(None) => None,
11085
11104
Err(e) => return Err(e),
@@ -11116,7 +11135,7 @@ impl<'a> Parser<'a> {
11116
11135
let columns = self.parse_comma_separated(Parser::parse_json_table_column_def)?;
11117
11136
self.expect_token(&Token::RParen)?;
11118
11137
self.expect_token(&Token::RParen)?;
11119
- let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS )?;
11138
+ let alias = self.maybe_parse_table_alias( )?;
11120
11139
Ok(TableFactor::JsonTable {
11121
11140
json_expr,
11122
11141
json_path,
@@ -11161,7 +11180,7 @@ impl<'a> Parser<'a> {
11161
11180
}
11162
11181
}
11163
11182
11164
- let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS )?;
11183
+ let alias = self.maybe_parse_table_alias( )?;
11165
11184
11166
11185
// MSSQL-specific table hints:
11167
11186
let mut with_hints = vec![];
@@ -11339,7 +11358,7 @@ impl<'a> Parser<'a> {
11339
11358
} else {
11340
11359
Vec::new()
11341
11360
};
11342
- let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS )?;
11361
+ let alias = self.maybe_parse_table_alias( )?;
11343
11362
Ok(TableFactor::OpenJsonTable {
11344
11363
json_expr,
11345
11364
json_path,
@@ -11438,7 +11457,7 @@ impl<'a> Parser<'a> {
11438
11457
11439
11458
self.expect_token(&Token::RParen)?;
11440
11459
11441
- let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS )?;
11460
+ let alias = self.maybe_parse_table_alias( )?;
11442
11461
11443
11462
Ok(TableFactor::MatchRecognize {
11444
11463
table: Box::new(table),
@@ -11682,7 +11701,7 @@ impl<'a> Parser<'a> {
11682
11701
) -> Result<TableFactor, ParserError> {
11683
11702
let subquery = self.parse_query()?;
11684
11703
self.expect_token(&Token::RParen)?;
11685
- let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS )?;
11704
+ let alias = self.maybe_parse_table_alias( )?;
11686
11705
Ok(TableFactor::Derived {
11687
11706
lateral: match lateral {
11688
11707
Lateral => true,
@@ -11776,7 +11795,7 @@ impl<'a> Parser<'a> {
11776
11795
};
11777
11796
11778
11797
self.expect_token(&Token::RParen)?;
11779
- let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS )?;
11798
+ let alias = self.maybe_parse_table_alias( )?;
11780
11799
Ok(TableFactor::Pivot {
11781
11800
table: Box::new(table),
11782
11801
aggregate_functions,
@@ -11798,7 +11817,7 @@ impl<'a> Parser<'a> {
11798
11817
self.expect_keyword_is(Keyword::IN)?;
11799
11818
let columns = self.parse_parenthesized_column_list(Mandatory, false)?;
11800
11819
self.expect_token(&Token::RParen)?;
11801
- let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS )?;
11820
+ let alias = self.maybe_parse_table_alias( )?;
11802
11821
Ok(TableFactor::Unpivot {
11803
11822
table: Box::new(table),
11804
11823
value,
@@ -12624,7 +12643,7 @@ impl<'a> Parser<'a> {
12624
12643
})
12625
12644
}
12626
12645
expr => self
12627
- .parse_optional_alias(keywords::RESERVED_FOR_COLUMN_ALIAS )
12646
+ .maybe_parse_select_item_alias( )
12628
12647
.map(|alias| match alias {
12629
12648
Some(alias) => SelectItem::ExprWithAlias { expr, alias },
12630
12649
None => SelectItem::UnnamedExpr(expr),
0 commit comments