Skip to content

Commit 4c629e8

Browse files
authored
support sqlite's OR clauses in update statements (#1530)
1 parent a67a4f3 commit 4c629e8

File tree

6 files changed

+59
-26
lines changed

6 files changed

+59
-26
lines changed

src/ast/dml.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -505,8 +505,8 @@ impl Display for Insert {
505505
self.table_name.to_string()
506506
};
507507

508-
if let Some(action) = self.or {
509-
write!(f, "INSERT OR {action} INTO {table_name} ")?;
508+
if let Some(on_conflict) = self.or {
509+
write!(f, "INSERT {on_conflict} INTO {table_name} ")?;
510510
} else {
511511
write!(
512512
f,

src/ast/mod.rs

+13-6
Original file line numberDiff line numberDiff line change
@@ -2396,6 +2396,8 @@ pub enum Statement {
23962396
selection: Option<Expr>,
23972397
/// RETURNING
23982398
returning: Option<Vec<SelectItem>>,
2399+
/// SQLite-specific conflict resolution clause
2400+
or: Option<SqliteOnConflict>,
23992401
},
24002402
/// ```sql
24012403
/// DELETE
@@ -3691,8 +3693,13 @@ impl fmt::Display for Statement {
36913693
from,
36923694
selection,
36933695
returning,
3696+
or,
36943697
} => {
3695-
write!(f, "UPDATE {table}")?;
3698+
write!(f, "UPDATE ")?;
3699+
if let Some(or) = or {
3700+
write!(f, "{or} ")?;
3701+
}
3702+
write!(f, "{table}")?;
36963703
if !assignments.is_empty() {
36973704
write!(f, " SET {}", display_comma_separated(assignments))?;
36983705
}
@@ -6304,11 +6311,11 @@ impl fmt::Display for SqliteOnConflict {
63046311
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63056312
use SqliteOnConflict::*;
63066313
match self {
6307-
Rollback => write!(f, "ROLLBACK"),
6308-
Abort => write!(f, "ABORT"),
6309-
Fail => write!(f, "FAIL"),
6310-
Ignore => write!(f, "IGNORE"),
6311-
Replace => write!(f, "REPLACE"),
6314+
Rollback => write!(f, "OR ROLLBACK"),
6315+
Abort => write!(f, "OR ABORT"),
6316+
Fail => write!(f, "OR FAIL"),
6317+
Ignore => write!(f, "OR IGNORE"),
6318+
Replace => write!(f, "OR REPLACE"),
63126319
}
63136320
}
63146321
}

src/parser/mod.rs

+21-18
Original file line numberDiff line numberDiff line change
@@ -11042,24 +11042,7 @@ impl<'a> Parser<'a> {
1104211042

1104311043
/// Parse an INSERT statement
1104411044
pub fn parse_insert(&mut self) -> Result<Statement, ParserError> {
11045-
let or = if !dialect_of!(self is SQLiteDialect) {
11046-
None
11047-
} else if self.parse_keywords(&[Keyword::OR, Keyword::REPLACE]) {
11048-
Some(SqliteOnConflict::Replace)
11049-
} else if self.parse_keywords(&[Keyword::OR, Keyword::ROLLBACK]) {
11050-
Some(SqliteOnConflict::Rollback)
11051-
} else if self.parse_keywords(&[Keyword::OR, Keyword::ABORT]) {
11052-
Some(SqliteOnConflict::Abort)
11053-
} else if self.parse_keywords(&[Keyword::OR, Keyword::FAIL]) {
11054-
Some(SqliteOnConflict::Fail)
11055-
} else if self.parse_keywords(&[Keyword::OR, Keyword::IGNORE]) {
11056-
Some(SqliteOnConflict::Ignore)
11057-
} else if self.parse_keyword(Keyword::REPLACE) {
11058-
Some(SqliteOnConflict::Replace)
11059-
} else {
11060-
None
11061-
};
11062-
11045+
let or = self.parse_conflict_clause();
1106311046
let priority = if !dialect_of!(self is MySqlDialect | GenericDialect) {
1106411047
None
1106511048
} else if self.parse_keyword(Keyword::LOW_PRIORITY) {
@@ -11218,6 +11201,24 @@ impl<'a> Parser<'a> {
1121811201
}
1121911202
}
1122011203

11204+
fn parse_conflict_clause(&mut self) -> Option<SqliteOnConflict> {
11205+
if self.parse_keywords(&[Keyword::OR, Keyword::REPLACE]) {
11206+
Some(SqliteOnConflict::Replace)
11207+
} else if self.parse_keywords(&[Keyword::OR, Keyword::ROLLBACK]) {
11208+
Some(SqliteOnConflict::Rollback)
11209+
} else if self.parse_keywords(&[Keyword::OR, Keyword::ABORT]) {
11210+
Some(SqliteOnConflict::Abort)
11211+
} else if self.parse_keywords(&[Keyword::OR, Keyword::FAIL]) {
11212+
Some(SqliteOnConflict::Fail)
11213+
} else if self.parse_keywords(&[Keyword::OR, Keyword::IGNORE]) {
11214+
Some(SqliteOnConflict::Ignore)
11215+
} else if self.parse_keyword(Keyword::REPLACE) {
11216+
Some(SqliteOnConflict::Replace)
11217+
} else {
11218+
None
11219+
}
11220+
}
11221+
1122111222
pub fn parse_insert_partition(&mut self) -> Result<Option<Vec<Expr>>, ParserError> {
1122211223
if self.parse_keyword(Keyword::PARTITION) {
1122311224
self.expect_token(&Token::LParen)?;
@@ -11253,6 +11254,7 @@ impl<'a> Parser<'a> {
1125311254
}
1125411255

1125511256
pub fn parse_update(&mut self) -> Result<Statement, ParserError> {
11257+
let or = self.parse_conflict_clause();
1125611258
let table = self.parse_table_and_joins()?;
1125711259
self.expect_keyword(Keyword::SET)?;
1125811260
let assignments = self.parse_comma_separated(Parser::parse_assignment)?;
@@ -11279,6 +11281,7 @@ impl<'a> Parser<'a> {
1127911281
from,
1128011282
selection,
1128111283
returning,
11284+
or,
1128211285
})
1128311286
}
1128411287

tests/sqlparser_common.rs

+21
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,7 @@ fn parse_update_set_from() {
443443
])),
444444
}),
445445
returning: None,
446+
or: None,
446447
}
447448
);
448449
}
@@ -457,6 +458,7 @@ fn parse_update_with_table_alias() {
457458
from: _from,
458459
selection,
459460
returning,
461+
or: None,
460462
} => {
461463
assert_eq!(
462464
TableWithJoins {
@@ -505,6 +507,25 @@ fn parse_update_with_table_alias() {
505507
}
506508
}
507509

510+
#[test]
511+
fn parse_update_or() {
512+
let expect_or_clause = |sql: &str, expected_action: SqliteOnConflict| match verified_stmt(sql) {
513+
Statement::Update { or, .. } => assert_eq!(or, Some(expected_action)),
514+
other => unreachable!("Expected update with or, got {:?}", other),
515+
};
516+
expect_or_clause(
517+
"UPDATE OR REPLACE t SET n = n + 1",
518+
SqliteOnConflict::Replace,
519+
);
520+
expect_or_clause(
521+
"UPDATE OR ROLLBACK t SET n = n + 1",
522+
SqliteOnConflict::Rollback,
523+
);
524+
expect_or_clause("UPDATE OR ABORT t SET n = n + 1", SqliteOnConflict::Abort);
525+
expect_or_clause("UPDATE OR FAIL t SET n = n + 1", SqliteOnConflict::Fail);
526+
expect_or_clause("UPDATE OR IGNORE t SET n = n + 1", SqliteOnConflict::Ignore);
527+
}
528+
508529
#[test]
509530
fn parse_select_with_table_alias_as() {
510531
// AS is optional

tests/sqlparser_mysql.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1970,6 +1970,7 @@ fn parse_update_with_joins() {
19701970
from: _from,
19711971
selection,
19721972
returning,
1973+
or: None,
19731974
} => {
19741975
assert_eq!(
19751976
TableWithJoins {

tests/sqlparser_sqlite.rs

+1
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ fn parse_update_tuple_row_values() {
465465
assert_eq!(
466466
sqlite().verified_stmt("UPDATE x SET (a, b) = (1, 2)"),
467467
Statement::Update {
468+
or: None,
468469
assignments: vec![Assignment {
469470
target: AssignmentTarget::Tuple(vec![
470471
ObjectName(vec![Ident::new("a"),]),

0 commit comments

Comments
 (0)