Repository: apache/datafusion-sqlparser-rs (Rust)

Issue:
MySQL/MariaDB: Wrong LIMIT x,y to LIMIT x OFFSET y conversion
While `LIMIT x, y` can be converted to a `LIMIT y OFFSET x` notation, it is currently converted to `LIMIT x OFFSET y`, which does not have the same meaning.  


Discussion:
PR https://github.com/sqlparser-rs/sqlparser-rs/pull/642 for this fix :D 



Patch:
diff --git a/src/parser.rs b/src/parser.rs
index f61ea4832..abe934581 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -24,6 +24,9 @@ use core::fmt;
 
 use log::debug;
 
+use IsLateral::*;
+use IsOptional::*;
+
 use crate::ast::*;
 use crate::dialect::*;
 use crate::keywords::{self, Keyword};
@@ -57,15 +60,11 @@ pub enum IsOptional {
     Mandatory,
 }
 
-use IsOptional::*;
-
 pub enum IsLateral {
     Lateral,
     NotLateral,
 }
 
-use IsLateral::*;
-
 pub enum WildcardExpr {
     Expr(Expr),
     QualifiedWildcard(ObjectName),
@@ -3762,9 +3761,18 @@ impl<'a> Parser<'a> {
                     offset = Some(self.parse_offset()?)
                 }
 
-                if offset.is_none() && self.consume_token(&Token::Comma) {
-                    // mysql style LIMIT 10, offset 5
-                    offset = Some(self.parse_offset()?)
+                if dialect_of!(self is GenericDialect | MySqlDialect)
+                    && limit.is_some()
+                    && offset.is_none()
+                    && self.consume_token(&Token::Comma)
+                {
+                    // MySQL style LIMIT x,y => LIMIT y OFFSET x.
+                    // Check <https://dev.mysql.com/doc/refman/8.0/en/select.html> for more details.
+                    offset = Some(Offset {
+                        value: limit.unwrap(),
+                        rows: OffsetRows::None,
+                    });
+                    limit = Some(self.parse_expr()?);
                 }
             }
 
@@ -5197,9 +5205,10 @@ impl Word {
 
 #[cfg(test)]
 mod tests {
-    use super::*;
     use crate::test_utils::{all_dialects, TestedDialects};
 
+    use super::*;
+
     #[test]
     fn test_prev_index() {
         let sql = "SELECT version";


Test patch:
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index 57fa35709..f25a36754 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -1593,14 +1593,6 @@ fn parse_limit_accepts_all() {
     );
 }
 
-#[test]
-fn parse_limit_my_sql_syntax() {
-    one_statement_parses_to(
-        "SELECT id, fname, lname FROM customer LIMIT 5, 10",
-        "SELECT id, fname, lname FROM customer LIMIT 5 OFFSET 10",
-    );
-}
-
 #[test]
 fn parse_cast() {
     let sql = "SELECT CAST(id AS BIGINT) FROM customer";
diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs
index a91be9833..8b8754db4 100644
--- a/tests/sqlparser_mysql.rs
+++ b/tests/sqlparser_mysql.rs
@@ -1065,6 +1065,14 @@ fn parse_set_names() {
     assert_eq!(stmt, Statement::SetNamesDefault {});
 }
 
+#[test]
+fn parse_limit_my_sql_syntax() {
+    mysql_and_generic().one_statement_parses_to(
+        "SELECT id, fname, lname FROM customer LIMIT 5, 10",
+        "SELECT id, fname, lname FROM customer LIMIT 10 OFFSET 5",
+    );
+}
+
 fn mysql() -> TestedDialects {
     TestedDialects {
         dialects: vec![Box::new(MySqlDialect {})],