Skip to content

Commit 492fc2a

Browse files
committed
support for SQLite UPDATE ... LIMIT ...
1 parent c1648e7 commit 492fc2a

File tree

7 files changed

+66
-2
lines changed

7 files changed

+66
-2
lines changed

src/ast/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3238,6 +3238,8 @@ pub enum Statement {
32383238
returning: Option<Vec<SelectItem>>,
32393239
/// SQLite-specific conflict resolution clause
32403240
or: Option<SqliteOnConflict>,
3241+
/// LIMIT
3242+
limit: Option<Expr>,
32413243
},
32423244
/// ```sql
32433245
/// DELETE
@@ -4799,6 +4801,7 @@ impl fmt::Display for Statement {
47994801
selection,
48004802
returning,
48014803
or,
4804+
limit,
48024805
} => {
48034806
f.write_str("UPDATE ")?;
48044807
if let Some(or) = or {
@@ -4832,6 +4835,10 @@ impl fmt::Display for Statement {
48324835
f.write_str("RETURNING")?;
48334836
indented_list(f, returning)?;
48344837
}
4838+
if let Some(limit) = limit {
4839+
SpaceOrNewline.fmt(f)?;
4840+
write!(f, "LIMIT {limit}")?;
4841+
}
48354842
Ok(())
48364843
}
48374844
Statement::Delete(delete) => delete.fmt(f),

src/ast/spans.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ impl Spanned for Statement {
378378
selection,
379379
returning,
380380
or: _,
381+
limit: _,
381382
} => union_spans(
382383
core::iter::once(table.span())
383384
.chain(assignments.iter().map(|i| i.span()))

src/dialect/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,6 +1163,16 @@ pub trait Dialect: Debug + Any {
11631163
fn supports_interval_options(&self) -> bool {
11641164
false
11651165
}
1166+
1167+
/// Returns true if this dialect allows an optional ORDER BY and LIMIT clause
1168+
/// on UPDATE and DELETE statements
1169+
/// e.g. `UPDATE table SET value=1 LIMIT 1` (SQLite)
1170+
///
1171+
/// For SQLite the compilation flag `SQLITE_ENABLE_UPDATE_DELETE_LIMIT` needs
1172+
/// to be set.
1173+
fn supports_update_delete_limit(&self) -> bool {
1174+
false
1175+
}
11661176
}
11671177

11681178
/// This represents the operators for which precedence must be defined

src/parser/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15116,13 +15116,20 @@ impl<'a> Parser<'a> {
1511615116
} else {
1511715117
None
1511815118
};
15119+
let limit =
15120+
if self.dialect.supports_update_delete_limit() && self.parse_keyword(Keyword::LIMIT) {
15121+
Some(self.parse_expr()?)
15122+
} else {
15123+
None
15124+
};
1511915125
Ok(Statement::Update {
1512015126
table,
1512115127
assignments,
1512215128
from,
1512315129
selection,
1512415130
returning,
1512515131
or,
15132+
limit,
1512615133
})
1512715134
}
1512815135

tests/sqlparser_common.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,7 @@ fn parse_update_set_from() {
515515
}),
516516
returning: None,
517517
or: None,
518+
limit: None
518519
}
519520
);
520521

@@ -533,6 +534,7 @@ fn parse_update_with_table_alias() {
533534
selection,
534535
returning,
535536
or: None,
537+
limit: None,
536538
} => {
537539
assert_eq!(
538540
TableWithJoins {

tests/sqlparser_mysql.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2509,6 +2509,7 @@ fn parse_update_with_joins() {
25092509
selection,
25102510
returning,
25112511
or: None,
2512+
limit: None,
25122513
} => {
25132514
assert_eq!(
25142515
TableWithJoins {

tests/sqlparser_sqlite.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use test_utils::*;
2828
use sqlparser::ast::SelectItem::UnnamedExpr;
2929
use sqlparser::ast::Value::Placeholder;
3030
use sqlparser::ast::*;
31-
use sqlparser::dialect::{GenericDialect, SQLiteDialect};
31+
use sqlparser::dialect::{Dialect, GenericDialect, SQLiteDialect};
3232
use sqlparser::parser::{ParserError, ParserOptions};
3333
use sqlparser::tokenizer::Token;
3434

@@ -485,7 +485,8 @@ fn parse_update_tuple_row_values() {
485485
joins: vec![],
486486
},
487487
from: None,
488-
returning: None
488+
returning: None,
489+
limit: None
489490
}
490491
);
491492
}
@@ -592,6 +593,41 @@ fn test_regexp_operator() {
592593
sqlite().verified_only_select(r#"SELECT count(*) FROM messages WHERE msg_text REGEXP '\d+'"#);
593594
}
594595

596+
#[test]
597+
fn test_update_delete_limit() {
598+
#[derive(Debug)]
599+
struct SQLiteDialect;
600+
impl Dialect for SQLiteDialect {
601+
fn is_identifier_start(&self, ch: char) -> bool {
602+
ch.is_ascii_lowercase() || ch.is_ascii_uppercase()
603+
}
604+
605+
fn is_identifier_part(&self, ch: char) -> bool {
606+
self.is_identifier_start(ch)
607+
}
608+
609+
fn supports_update_delete_limit(&self) -> bool {
610+
true
611+
}
612+
}
613+
614+
let sqlite = TestedDialects::new(vec![Box::new(SQLiteDialect {})]);
615+
616+
match sqlite.verified_stmt("UPDATE foo SET bar = 1 LIMIT 99") {
617+
Statement::Update { limit, .. } => {
618+
assert_eq!(limit, Some(Expr::value(number("99"))));
619+
}
620+
_ => unreachable!(),
621+
}
622+
623+
match sqlite.verified_stmt("DELETE FROM foo LIMIT 99") {
624+
Statement::Delete(Delete { limit, .. }) => {
625+
assert_eq!(limit, Some(Expr::value(number("99"))));
626+
}
627+
_ => unreachable!(),
628+
}
629+
}
630+
595631
fn sqlite() -> TestedDialects {
596632
TestedDialects::new(vec![Box::new(SQLiteDialect {})])
597633
}

0 commit comments

Comments
 (0)