You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by gi...@apache.org on 2026/04/09 12:43:52 UTC

(datafusion-sqlparser-rs) branch main updated: MySQL: Add support for `ORDER BY` on single-table `UPDATE` (#2296)

This is an automated email from the ASF dual-hosted git repository.

github-bot pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new 827978dc MySQL: Add support for `ORDER BY` on single-table `UPDATE` (#2296)
827978dc is described below

commit 827978dc5b581acae9711b48f9c03cd2ed47425c
Author: Donovan Schönknecht <do...@tpyo.net>
AuthorDate: Thu Apr 9 14:31:02 2026 +0200

    MySQL: Add support for `ORDER BY` on single-table `UPDATE` (#2296)
---
 src/ast/dml.rs            |  8 +++++++
 src/ast/spans.rs          |  2 ++
 src/dialect/generic.rs    |  4 ++++
 src/dialect/mod.rs        | 10 +++++++++
 src/dialect/mysql.rs      |  5 +++++
 src/parser/mod.rs         |  8 +++++++
 tests/sqlparser_common.rs |  2 ++
 tests/sqlparser_mysql.rs  | 54 +++++++++++++++++++++++++++++++++++++++++++++++
 tests/sqlparser_sqlite.rs |  1 +
 9 files changed, 94 insertions(+)

diff --git a/src/ast/dml.rs b/src/ast/dml.rs
index 446d44b2..3e2f7ee0 100644
--- a/src/ast/dml.rs
+++ b/src/ast/dml.rs
@@ -387,6 +387,9 @@ pub struct Update {
     pub output: Option<OutputClause>,
     /// SQLite-specific conflict resolution clause
     pub or: Option<SqliteOnConflict>,
+    /// ORDER BY (MySQL extension for single-table UPDATE)
+    /// See <https://dev.mysql.com/doc/refman/8.4/en/update.html>
+    pub order_by: Vec<OrderByExpr>,
     /// LIMIT
     pub limit: Option<Expr>,
 }
@@ -434,6 +437,11 @@ impl Display for Update {
             f.write_str("RETURNING")?;
             indented_list(f, returning)?;
         }
+        if !self.order_by.is_empty() {
+            SpaceOrNewline.fmt(f)?;
+            f.write_str("ORDER BY")?;
+            indented_list(f, &self.order_by)?;
+        }
         if let Some(limit) = &self.limit {
             SpaceOrNewline.fmt(f)?;
             write!(f, "LIMIT {limit}")?;
diff --git a/src/ast/spans.rs b/src/ast/spans.rs
index d80a3f4d..70c12de1 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -952,6 +952,7 @@ impl Spanned for Update {
             returning,
             output,
             or: _,
+            order_by,
             limit,
         } = self;
 
@@ -963,6 +964,7 @@ impl Spanned for Update {
                 .chain(selection.iter().map(|i| i.span()))
                 .chain(returning.iter().flat_map(|i| i.iter().map(|k| k.span())))
                 .chain(output.iter().map(|i| i.span()))
+                .chain(order_by.iter().map(|i| i.span()))
                 .chain(limit.iter().map(|i| i.span())),
         )
     }
diff --git a/src/dialect/generic.rs b/src/dialect/generic.rs
index c7f17351..8809bb97 100644
--- a/src/dialect/generic.rs
+++ b/src/dialect/generic.rs
@@ -133,6 +133,10 @@ impl Dialect for GenericDialect {
         true
     }
 
+    fn supports_update_order_by(&self) -> bool {
+        true
+    }
+
     fn supports_from_first_select(&self) -> bool {
         true
     }
diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs
index ef9cafed..bb11f3eb 100644
--- a/src/dialect/mod.rs
+++ b/src/dialect/mod.rs
@@ -520,6 +520,16 @@ pub trait Dialect: Debug + Any {
         false
     }
 
+    /// Returns true if the dialect supports `ORDER BY` in `UPDATE` statements.
+    ///
+    /// ```sql
+    /// UPDATE foo SET bar = false WHERE foo = true ORDER BY foo ASC;
+    /// ```
+    /// See <https://dev.mysql.com/doc/refman/8.4/en/update.html>
+    fn supports_update_order_by(&self) -> bool {
+        false
+    }
+
     /// Returns true if the dialect supports an `EXCEPT` clause following a
     /// wildcard in a select list.
     ///
diff --git a/src/dialect/mysql.rs b/src/dialect/mysql.rs
index 6b057539..7c793d0d 100644
--- a/src/dialect/mysql.rs
+++ b/src/dialect/mysql.rs
@@ -179,6 +179,11 @@ impl Dialect for MySqlDialect {
         true
     }
 
+    /// See: <https://dev.mysql.com/doc/refman/8.4/en/update.html>
+    fn supports_update_order_by(&self) -> bool {
+        true
+    }
+
     fn supports_data_type_signed_suffix(&self) -> bool {
         true
     }
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 9056f85d..a63352e1 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -17755,6 +17755,13 @@ impl<'a> Parser<'a> {
         } else {
             None
         };
+        let order_by = if self.dialect.supports_update_order_by()
+            && self.parse_keywords(&[Keyword::ORDER, Keyword::BY])
+        {
+            self.parse_comma_separated(Parser::parse_order_by_expr)?
+        } else {
+            vec![]
+        };
         let limit = if self.parse_keyword(Keyword::LIMIT) {
             Some(self.parse_expr()?)
         } else {
@@ -17770,6 +17777,7 @@ impl<'a> Parser<'a> {
             returning,
             output,
             or,
+            order_by,
             limit,
         }
         .into())
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index ad1e521f..c21202f0 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -535,6 +535,7 @@ fn parse_update_set_from() {
             returning: None,
             output: None,
             or: None,
+            order_by: vec![],
             limit: None
         })
     );
@@ -554,6 +555,7 @@ fn parse_update_with_table_alias() {
             selection,
             returning,
             or: None,
+            order_by: _,
             limit: None,
             optimizer_hints,
             update_token: _,
diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs
index 269787c2..6680929c 100644
--- a/tests/sqlparser_mysql.rs
+++ b/tests/sqlparser_mysql.rs
@@ -2707,6 +2707,7 @@ fn parse_update_with_joins() {
             selection,
             returning,
             or: None,
+            order_by: _,
             limit: None,
             optimizer_hints,
             update_token: _,
@@ -2784,6 +2785,59 @@ fn parse_update_with_joins() {
     }
 }
 
+#[test]
+fn parse_update_with_order_by() {
+    let sql = "UPDATE foo SET bar = false WHERE foo = true ORDER BY foo ASC";
+    match mysql_and_generic().verified_stmt(sql) {
+        Statement::Update(Update { order_by, .. }) => {
+            assert_eq!(
+                vec![OrderByExpr {
+                    expr: Expr::Identifier(Ident {
+                        value: "foo".to_owned(),
+                        quote_style: None,
+                        span: Span::empty(),
+                    }),
+                    options: OrderByOptions {
+                        asc: Some(true),
+                        nulls_first: None,
+                    },
+                    with_fill: None,
+                }],
+                order_by
+            );
+        }
+        _ => unreachable!(),
+    }
+}
+
+#[test]
+fn parse_update_with_order_by_and_limit() {
+    let sql = "UPDATE foo SET bar = false WHERE foo = true ORDER BY foo ASC LIMIT 10";
+    match mysql_and_generic().verified_stmt(sql) {
+        Statement::Update(Update {
+            order_by, limit, ..
+        }) => {
+            assert_eq!(
+                vec![OrderByExpr {
+                    expr: Expr::Identifier(Ident {
+                        value: "foo".to_owned(),
+                        quote_style: None,
+                        span: Span::empty(),
+                    }),
+                    options: OrderByOptions {
+                        asc: Some(true),
+                        nulls_first: None,
+                    },
+                    with_fill: None,
+                }],
+                order_by
+            );
+            assert_eq!(Some(Expr::value(number("10"))), limit);
+        }
+        _ => unreachable!(),
+    }
+}
+
 #[test]
 fn parse_delete_with_order_by() {
     let sql = "DELETE FROM customers ORDER BY id DESC";
diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs
index 33c38fb0..f9536bc2 100644
--- a/tests/sqlparser_sqlite.rs
+++ b/tests/sqlparser_sqlite.rs
@@ -497,6 +497,7 @@ fn parse_update_tuple_row_values() {
             from: None,
             returning: None,
             output: None,
+            order_by: vec![],
             limit: None,
             update_token: AttachedToken::empty()
         })


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org