Skip to content

Commit 3286219

Browse files
authored
support for SELECT TOP on snowflake (#14)
1 parent cf4bd95 commit 3286219

5 files changed

Lines changed: 51 additions & 10 deletions

File tree

src/ast/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ pub use self::ddl::{
3030
pub use self::operator::{BinaryOperator, UnaryOperator};
3131
pub use self::query::{
3232
Cte, Fetch, Join, JoinConstraint, JoinOperator, Offset, OffsetRows, OrderByExpr, Query, Select,
33-
SelectItem, SetExpr, SetOperator, TableAlias, TableFactor, TableWithJoins, Top, Values, With,
33+
SelectItem, SetExpr, SetOperator, TableAlias, TableFactor, TableWithJoins, Top, TopQuantity,
34+
Values, With,
3435
};
3536
pub use self::value::{DateTimeField, Value};
3637

src/ast/query.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -654,17 +654,32 @@ impl fmt::Display for Fetch {
654654
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
655655
pub struct Top {
656656
/// SQL semantic equivalent of LIMIT but with same structure as FETCH.
657+
/// MSSQL only.
657658
pub with_ties: bool,
659+
/// MSSQL only.
658660
pub percent: bool,
659-
pub quantity: Option<Expr>,
661+
pub quantity: Option<TopQuantity>,
662+
}
663+
664+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
665+
pub enum TopQuantity {
666+
// A parenthesized expression. MSSQL only.
667+
Expr(Expr),
668+
// An unparenthesized integer constant.
669+
Constant(u64),
660670
}
661671

662672
impl fmt::Display for Top {
663673
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
664674
let extension = if self.with_ties { " WITH TIES" } else { "" };
665675
if let Some(ref quantity) = self.quantity {
666676
let percent = if self.percent { " PERCENT" } else { "" };
667-
write!(f, "TOP ({}){}{}", quantity, percent, extension)
677+
match quantity {
678+
TopQuantity::Expr(quantity) => write!(f, "TOP ({quantity}){percent}{extension}"),
679+
TopQuantity::Constant(quantity) => {
680+
write!(f, "TOP {quantity}{percent}{extension}")
681+
}
682+
}
668683
} else {
669684
write!(f, "TOP{}", extension)
670685
}

src/parser.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2983,9 +2983,14 @@ impl<'a> Parser<'a> {
29832983
let quantity = if self.consume_token(&Token::LParen) {
29842984
let quantity = self.parse_expr()?;
29852985
self.expect_token(&Token::RParen)?;
2986-
Some(quantity)
2986+
Some(TopQuantity::Expr(quantity))
29872987
} else {
2988-
Some(self.parse_number_value_or_ident()?)
2988+
let next_token = self.next_token();
2989+
let quantity = match next_token {
2990+
Token::Number(s) => s.parse::<u64>().expect("literal int"),
2991+
_ => self.expected("literal int", next_token)?,
2992+
};
2993+
Some(TopQuantity::Constant(quantity))
29892994
};
29902995

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

tests/sqlparser_mssql.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,10 @@ fn parse_mssql_top_paren() {
7676
let sql = "SELECT TOP (5) * FROM foo";
7777
let select = ms_and_generic().verified_only_select(sql);
7878
let top = select.top.unwrap();
79-
assert_eq!(Some(Expr::Value(number("5"))), top.quantity);
79+
assert_eq!(
80+
Some(TopQuantity::Expr(Expr::Value(number("5")))),
81+
top.quantity
82+
);
8083
assert!(!top.percent);
8184
}
8285

@@ -85,7 +88,10 @@ fn parse_mssql_top_percent() {
8588
let sql = "SELECT TOP (5) PERCENT * FROM foo";
8689
let select = ms_and_generic().verified_only_select(sql);
8790
let top = select.top.unwrap();
88-
assert_eq!(Some(Expr::Value(number("5"))), top.quantity);
91+
assert_eq!(
92+
Some(TopQuantity::Expr(Expr::Value(number("5")))),
93+
top.quantity
94+
);
8995
assert!(top.percent);
9096
}
9197

@@ -94,7 +100,10 @@ fn parse_mssql_top_with_ties() {
94100
let sql = "SELECT TOP (5) WITH TIES * FROM foo";
95101
let select = ms_and_generic().verified_only_select(sql);
96102
let top = select.top.unwrap();
97-
assert_eq!(Some(Expr::Value(number("5"))), top.quantity);
103+
assert_eq!(
104+
Some(TopQuantity::Expr(Expr::Value(number("5")))),
105+
top.quantity
106+
);
98107
assert!(top.with_ties);
99108
}
100109

@@ -103,14 +112,17 @@ fn parse_mssql_top_percent_with_ties() {
103112
let sql = "SELECT TOP (10) PERCENT WITH TIES * FROM foo";
104113
let select = ms_and_generic().verified_only_select(sql);
105114
let top = select.top.unwrap();
106-
assert_eq!(Some(Expr::Value(number("10"))), top.quantity);
115+
assert_eq!(
116+
Some(TopQuantity::Expr(Expr::Value(number("10")))),
117+
top.quantity
118+
);
107119
assert!(top.percent);
108120
}
109121

110122
#[test]
111123
fn parse_mssql_top() {
112124
let sql = "SELECT TOP 5 bar, baz FROM foo";
113-
let _ = ms_and_generic().one_statement_parses_to(sql, "SELECT TOP (5) bar, baz FROM foo");
125+
let _ = ms_and_generic().one_statement_parses_to(sql, "SELECT TOP 5 bar, baz FROM foo");
114126
}
115127

116128
fn ms() -> TestedDialects {

tests/sqlparser_snowflake.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,11 @@ fn snowflake_and_generic() -> TestedDialects {
200200
dialects: vec![Box::new(SnowflakeDialect {}), Box::new(GenericDialect {})],
201201
}
202202
}
203+
204+
#[test]
205+
fn parse_top() {
206+
snowflake().one_statement_parses_to(
207+
"SELECT TOP 4 c1 FROM testtable",
208+
"SELECT TOP 4 c1 FROM testtable",
209+
);
210+
}

0 commit comments

Comments
 (0)