Skip to content

Commit 2950a84

Browse files
authored
snowflake: fix rendering of SELECT TOP (#1070)
1 parent 1baec96 commit 2950a84

File tree

5 files changed

+53
-11
lines changed

5 files changed

+53
-11
lines changed

src/ast/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ pub use self::query::{
4242
JsonTableColumnErrorHandling, LateralView, LockClause, LockType, NamedWindowDefinition,
4343
NonBlock, Offset, OffsetRows, OrderByExpr, Query, RenameSelectItem, ReplaceSelectElement,
4444
ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, Table,
45-
TableAlias, TableFactor, TableVersion, TableWithJoins, Top, Values, WildcardAdditionalOptions,
46-
With,
45+
TableAlias, TableFactor, TableVersion, TableWithJoins, Top, TopQuantity, Values,
46+
WildcardAdditionalOptions, With,
4747
};
4848
pub use self::value::{
4949
escape_quoted_string, DateTimeField, DollarQuotedString, TrimWhereField, Value,

src/ast/query.rs

+19-2
Original file line numberDiff line numberDiff line change
@@ -1278,17 +1278,34 @@ impl fmt::Display for Distinct {
12781278
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
12791279
pub struct Top {
12801280
/// SQL semantic equivalent of LIMIT but with same structure as FETCH.
1281+
/// MSSQL only.
12811282
pub with_ties: bool,
1283+
/// MSSQL only.
12821284
pub percent: bool,
1283-
pub quantity: Option<Expr>,
1285+
pub quantity: Option<TopQuantity>,
1286+
}
1287+
1288+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1289+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1290+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1291+
pub enum TopQuantity {
1292+
// A parenthesized expression. MSSQL only.
1293+
Expr(Expr),
1294+
// An unparenthesized integer constant.
1295+
Constant(u64),
12841296
}
12851297

12861298
impl fmt::Display for Top {
12871299
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
12881300
let extension = if self.with_ties { " WITH TIES" } else { "" };
12891301
if let Some(ref quantity) = self.quantity {
12901302
let percent = if self.percent { " PERCENT" } else { "" };
1291-
write!(f, "TOP ({quantity}){percent}{extension}")
1303+
match quantity {
1304+
TopQuantity::Expr(quantity) => write!(f, "TOP ({quantity}){percent}{extension}"),
1305+
TopQuantity::Constant(quantity) => {
1306+
write!(f, "TOP {quantity}{percent}{extension}")
1307+
}
1308+
}
12921309
} else {
12931310
write!(f, "TOP{extension}")
12941311
}

src/parser/mod.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -7843,9 +7843,14 @@ impl<'a> Parser<'a> {
78437843
let quantity = if self.consume_token(&Token::LParen) {
78447844
let quantity = self.parse_expr()?;
78457845
self.expect_token(&Token::RParen)?;
7846-
Some(quantity)
7846+
Some(TopQuantity::Expr(quantity))
78477847
} else {
7848-
Some(Expr::Value(self.parse_number_value()?))
7848+
let next_token = self.next_token();
7849+
let quantity = match next_token.token {
7850+
Token::Number(s, _) => s.parse::<u64>().expect("literal int"),
7851+
_ => self.expected("literal int", next_token)?,
7852+
};
7853+
Some(TopQuantity::Constant(quantity))
78497854
};
78507855

78517856
let percent = self.parse_keyword(Keyword::PERCENT);

tests/sqlparser_mssql.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,10 @@ fn parse_mssql_top_paren() {
180180
let sql = "SELECT TOP (5) * FROM foo";
181181
let select = ms_and_generic().verified_only_select(sql);
182182
let top = select.top.unwrap();
183-
assert_eq!(Some(Expr::Value(number("5"))), top.quantity);
183+
assert_eq!(
184+
Some(TopQuantity::Expr(Expr::Value(number("5")))),
185+
top.quantity
186+
);
184187
assert!(!top.percent);
185188
}
186189

@@ -189,7 +192,10 @@ fn parse_mssql_top_percent() {
189192
let sql = "SELECT TOP (5) PERCENT * FROM foo";
190193
let select = ms_and_generic().verified_only_select(sql);
191194
let top = select.top.unwrap();
192-
assert_eq!(Some(Expr::Value(number("5"))), top.quantity);
195+
assert_eq!(
196+
Some(TopQuantity::Expr(Expr::Value(number("5")))),
197+
top.quantity
198+
);
193199
assert!(top.percent);
194200
}
195201

@@ -198,7 +204,10 @@ fn parse_mssql_top_with_ties() {
198204
let sql = "SELECT TOP (5) WITH TIES * FROM foo";
199205
let select = ms_and_generic().verified_only_select(sql);
200206
let top = select.top.unwrap();
201-
assert_eq!(Some(Expr::Value(number("5"))), top.quantity);
207+
assert_eq!(
208+
Some(TopQuantity::Expr(Expr::Value(number("5")))),
209+
top.quantity
210+
);
202211
assert!(top.with_ties);
203212
}
204213

@@ -207,14 +216,17 @@ fn parse_mssql_top_percent_with_ties() {
207216
let sql = "SELECT TOP (10) PERCENT WITH TIES * FROM foo";
208217
let select = ms_and_generic().verified_only_select(sql);
209218
let top = select.top.unwrap();
210-
assert_eq!(Some(Expr::Value(number("10"))), top.quantity);
219+
assert_eq!(
220+
Some(TopQuantity::Expr(Expr::Value(number("10")))),
221+
top.quantity
222+
);
211223
assert!(top.percent);
212224
}
213225

214226
#[test]
215227
fn parse_mssql_top() {
216228
let sql = "SELECT TOP 5 bar, baz FROM foo";
217-
let _ = ms_and_generic().one_statement_parses_to(sql, "SELECT TOP (5) bar, baz FROM foo");
229+
let _ = ms_and_generic().one_statement_parses_to(sql, "SELECT TOP 5 bar, baz FROM foo");
218230
}
219231

220232
#[test]

tests/sqlparser_snowflake.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1147,3 +1147,11 @@ fn parse_pivot_of_table_factor_derived() {
11471147
"SELECT * FROM (SELECT place_id, weekday, open FROM times AS p) PIVOT(max(open) FOR weekday IN (0, 1, 2, 3, 4, 5, 6)) AS p (place_id, open_sun, open_mon, open_tue, open_wed, open_thu, open_fri, open_sat)"
11481148
);
11491149
}
1150+
1151+
#[test]
1152+
fn parse_top() {
1153+
snowflake().one_statement_parses_to(
1154+
"SELECT TOP 4 c1 FROM testtable",
1155+
"SELECT TOP 4 c1 FROM testtable",
1156+
);
1157+
}

0 commit comments

Comments
 (0)