diff --git a/src/parser/mod.rs b/src/parser/mod.rs index ac1b5e376..80fbd0689 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -15115,7 +15115,8 @@ impl<'a> Parser<'a> { let mut top_before_distinct = false; let mut top = None; - if self.dialect.supports_top_before_distinct() && self.parse_keyword(Keyword::TOP) { + if self.dialect.supports_top_before_distinct() && self.peek_top_clause() { + self.expect_keyword(Keyword::TOP)?; top = Some(self.parse_top()?); top_before_distinct = true; } @@ -15126,7 +15127,8 @@ impl<'a> Parser<'a> { self.parse_all_or_distinct()? }; - if !self.dialect.supports_top_before_distinct() && self.parse_keyword(Keyword::TOP) { + if !self.dialect.supports_top_before_distinct() && self.peek_top_clause() { + self.expect_keyword(Keyword::TOP)?; top = Some(self.parse_top()?); } @@ -19386,6 +19388,18 @@ impl<'a> Parser<'a> { Ok(InterpolateExpr { column, expr }) } + /// Returns true if the parser is positioned at a `TOP` clause, i.e. the + /// `TOP` keyword followed by `(` or a number. When `TOP` is followed by + /// anything else it is an ordinary identifier (column, alias, or table + /// name), not the row-limit clause parsed by [`Self::parse_top`]. + fn peek_top_clause(&self) -> bool { + self.peek_keyword(Keyword::TOP) + && matches!( + self.peek_nth_token_ref(1).token, + Token::LParen | Token::Number(_, _) + ) + } + /// Parse a TOP clause, MSSQL equivalent of LIMIT, /// that follows after `SELECT [DISTINCT]`. pub fn parse_top(&mut self) -> Result { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 634b9aea2..c31b13d84 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -15376,6 +15376,23 @@ fn test_select_top() { dialects.verified_stmt("SELECT TOP 3 DISTINCT a, b, c FROM tbl"); } +#[test] +fn parse_top_as_identifier() { + // `TOP` is only the row-limit clause when followed by `(` or a number. + // Otherwise it is an ordinary identifier and must remain usable as a + // column name, alias, or table name. Covers both the `TOP`-before-distinct + // dialects and the general case. + let dialects = all_dialects_where(|d| d.supports_top_before_distinct()); + dialects.verified_stmt("SELECT top FROM tbl"); + dialects.verified_stmt("SELECT top, bottom FROM tbl"); + dialects.verified_stmt("SELECT top.val FROM tbl AS top"); + + let dialects = all_dialects_where(|d| !d.supports_top_before_distinct()); + dialects.verified_stmt("SELECT top FROM tbl"); + dialects.verified_stmt("SELECT top, bottom FROM tbl"); + dialects.verified_stmt("SELECT top.val FROM tbl AS top"); +} + #[test] fn parse_bang_not() { let dialects = all_dialects_where(|d| d.supports_bang_not_operator());