Skip to content

Commit

Permalink
Expand handling of LIMIT 1, 2 handling to include sqlite (#1447)
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuawarner32 authored Sep 30, 2024
1 parent ce2686a commit 1e0460a
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 24 deletions.
4 changes: 4 additions & 0 deletions src/dialect/clickhouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,8 @@ impl Dialect for ClickHouseDialect {
fn require_interval_qualifier(&self) -> bool {
true
}

fn supports_limit_comma(&self) -> bool {
true
}
}
4 changes: 4 additions & 0 deletions src/dialect/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,8 @@ impl Dialect for GenericDialect {
fn supports_explain_with_utility_options(&self) -> bool {
true
}

fn supports_limit_comma(&self) -> bool {
true
}
}
7 changes: 6 additions & 1 deletion src/dialect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ use alloc::boxed::Box;
/// 1. user defined [`Dialect`]s can customize the parsing behavior
/// 2. The differences between dialects can be clearly documented in the trait
///
/// `dialect_of!(parser Is SQLiteDialect | GenericDialect)` evaluates
/// `dialect_of!(parser is SQLiteDialect | GenericDialect)` evaluates
/// to `true` if `parser.dialect` is one of the [`Dialect`]s specified.
macro_rules! dialect_of {
( $parsed_dialect: ident is $($dialect_type: ty)|+ ) => {
Expand Down Expand Up @@ -323,6 +323,11 @@ pub trait Dialect: Debug + Any {
false
}

/// Does the dialect support parsing `LIMIT 1, 2` as `LIMIT 2 OFFSET 1`?
fn supports_limit_comma(&self) -> bool {
false
}

/// Does the dialect support trailing commas in the projection list?
fn supports_projection_trailing_commas(&self) -> bool {
self.supports_trailing_commas()
Expand Down
4 changes: 4 additions & 0 deletions src/dialect/mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ impl Dialect for MySqlDialect {
fn require_interval_qualifier(&self) -> bool {
true
}

fn supports_limit_comma(&self) -> bool {
true
}
}

/// `LOCK TABLES`
Expand Down
4 changes: 4 additions & 0 deletions src/dialect/sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,8 @@ impl Dialect for SQLiteDialect {
fn supports_in_empty_list(&self) -> bool {
true
}

fn supports_limit_comma(&self) -> bool {
true
}
}
2 changes: 1 addition & 1 deletion src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8739,7 +8739,7 @@ impl<'a> Parser<'a> {
offset = Some(self.parse_offset()?)
}

if dialect_of!(self is GenericDialect | MySqlDialect | ClickHouseDialect)
if self.dialect.supports_limit_comma()
&& limit.is_some()
&& offset.is_none()
&& self.consume_token(&Token::Comma)
Expand Down
41 changes: 23 additions & 18 deletions src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ pub struct TestedDialects {
}

impl TestedDialects {
/// Create a TestedDialects with default options and the given dialects.
pub fn new(dialects: Vec<Box<dyn Dialect>>) -> Self {
Self {
dialects,
options: None,
}
}

fn new_parser<'a>(&self, dialect: &'a dyn Dialect) -> Parser<'a> {
let parser = Parser::new(dialect);
if let Some(options) = &self.options {
Expand Down Expand Up @@ -211,24 +219,21 @@ impl TestedDialects {

/// Returns all available dialects.
pub fn all_dialects() -> TestedDialects {
let all_dialects = vec![
Box::new(GenericDialect {}) as Box<dyn Dialect>,
Box::new(PostgreSqlDialect {}) as Box<dyn Dialect>,
Box::new(MsSqlDialect {}) as Box<dyn Dialect>,
Box::new(AnsiDialect {}) as Box<dyn Dialect>,
Box::new(SnowflakeDialect {}) as Box<dyn Dialect>,
Box::new(HiveDialect {}) as Box<dyn Dialect>,
Box::new(RedshiftSqlDialect {}) as Box<dyn Dialect>,
Box::new(MySqlDialect {}) as Box<dyn Dialect>,
Box::new(BigQueryDialect {}) as Box<dyn Dialect>,
Box::new(SQLiteDialect {}) as Box<dyn Dialect>,
Box::new(DuckDbDialect {}) as Box<dyn Dialect>,
Box::new(DatabricksDialect {}) as Box<dyn Dialect>,
];
TestedDialects {
dialects: all_dialects,
options: None,
}
TestedDialects::new(vec![
Box::new(GenericDialect {}),
Box::new(PostgreSqlDialect {}),
Box::new(MsSqlDialect {}),
Box::new(AnsiDialect {}),
Box::new(SnowflakeDialect {}),
Box::new(HiveDialect {}),
Box::new(RedshiftSqlDialect {}),
Box::new(MySqlDialect {}),
Box::new(BigQueryDialect {}),
Box::new(SQLiteDialect {}),
Box::new(DuckDbDialect {}),
Box::new(DatabricksDialect {}),
Box::new(ClickHouseDialect {}),
])
}

/// Returns all dialects matching the given predicate.
Expand Down
19 changes: 15 additions & 4 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6832,7 +6832,9 @@ fn parse_create_view_with_options() {
#[test]
fn parse_create_view_with_columns() {
let sql = "CREATE VIEW v (has, cols) AS SELECT 1, 2";
match verified_stmt(sql) {
// TODO: why does this fail for ClickHouseDialect? (#1449)
// match all_dialects().verified_stmt(sql) {
match all_dialects_except(|d| d.is::<ClickHouseDialect>()).verified_stmt(sql) {
Statement::CreateView {
name,
columns,
Expand Down Expand Up @@ -8624,17 +8626,26 @@ fn verified_expr(query: &str) -> Expr {

#[test]
fn parse_offset_and_limit() {
let sql = "SELECT foo FROM bar LIMIT 2 OFFSET 2";
let sql = "SELECT foo FROM bar LIMIT 1 OFFSET 2";
let expect = Some(Offset {
value: Expr::Value(number("2")),
rows: OffsetRows::None,
});
let ast = verified_query(sql);
assert_eq!(ast.offset, expect);
assert_eq!(ast.limit, Some(Expr::Value(number("2"))));
assert_eq!(ast.limit, Some(Expr::Value(number("1"))));

// different order is OK
one_statement_parses_to("SELECT foo FROM bar OFFSET 2 LIMIT 2", sql);
one_statement_parses_to("SELECT foo FROM bar OFFSET 2 LIMIT 1", sql);

// mysql syntax is ok for some dialects
TestedDialects::new(vec![
Box::new(GenericDialect {}),
Box::new(MySqlDialect {}),
Box::new(SQLiteDialect {}),
Box::new(ClickHouseDialect {}),
])
.one_statement_parses_to("SELECT foo FROM bar LIMIT 2, 1", sql);

// expressions are allowed
let sql = "SELECT foo FROM bar LIMIT 1 + 2 OFFSET 3 * 4";
Expand Down

0 comments on commit 1e0460a

Please sign in to comment.