Expand handling of LIMIT 1, 2 handling to include sqlite (#1447)

This commit is contained in:
Joshua Warner 2024-09-30 10:40:07 -07:00 committed by GitHub
parent ce2686a169
commit 1e0460a7df
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 61 additions and 24 deletions

View file

@ -46,4 +46,8 @@ impl Dialect for ClickHouseDialect {
fn require_interval_qualifier(&self) -> bool {
true
}
fn supports_limit_comma(&self) -> bool {
true
}
}

View file

@ -99,4 +99,8 @@ impl Dialect for GenericDialect {
fn supports_explain_with_utility_options(&self) -> bool {
true
}
fn supports_limit_comma(&self) -> bool {
true
}
}

View file

@ -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)|+ ) => {
@ -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()

View file

@ -93,6 +93,10 @@ impl Dialect for MySqlDialect {
fn require_interval_qualifier(&self) -> bool {
true
}
fn supports_limit_comma(&self) -> bool {
true
}
}
/// `LOCK TABLES`

View file

@ -73,4 +73,8 @@ impl Dialect for SQLiteDialect {
fn supports_in_empty_list(&self) -> bool {
true
}
fn supports_limit_comma(&self) -> bool {
true
}
}

View file

@ -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)

View file

@ -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 {
@ -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.

View file

@ -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,
@ -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";