Skip to content

Commit 814367a

Browse files
main--gamife
and
gamife
authored
Implement ON CONFLICT and RETURNING (apache#666)
* Implement RETURNING on INSERT/UPDATE/DELETE * Implement INSERT ... ON CONFLICT * Fix tests * cargo fmt * tests: on conflict and returning Co-authored-by: gamife <[email protected]>
1 parent ae1c690 commit 814367a

File tree

6 files changed

+250
-8
lines changed

6 files changed

+250
-8
lines changed

Diff for: src/ast/mod.rs

+55-3
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,8 @@ pub enum Statement {
10491049
/// whether the insert has the table keyword (Hive)
10501050
table: bool,
10511051
on: Option<OnInsert>,
1052+
/// RETURNING
1053+
returning: Option<Vec<SelectItem>>,
10521054
},
10531055
// TODO: Support ROW FORMAT
10541056
Directory {
@@ -1089,6 +1091,8 @@ pub enum Statement {
10891091
from: Option<TableWithJoins>,
10901092
/// WHERE
10911093
selection: Option<Expr>,
1094+
/// RETURNING
1095+
returning: Option<Vec<SelectItem>>,
10921096
},
10931097
/// DELETE
10941098
Delete {
@@ -1098,6 +1102,8 @@ pub enum Statement {
10981102
using: Option<TableFactor>,
10991103
/// WHERE
11001104
selection: Option<Expr>,
1105+
/// RETURNING
1106+
returning: Option<Vec<SelectItem>>,
11011107
},
11021108
/// CREATE VIEW
11031109
CreateView {
@@ -1679,6 +1685,7 @@ impl fmt::Display for Statement {
16791685
source,
16801686
table,
16811687
on,
1688+
returning,
16821689
} => {
16831690
if let Some(action) = or {
16841691
write!(f, "INSERT OR {} INTO {} ", action, table_name)?;
@@ -1706,10 +1713,14 @@ impl fmt::Display for Statement {
17061713
write!(f, "{}", source)?;
17071714

17081715
if let Some(on) = on {
1709-
write!(f, "{}", on)
1710-
} else {
1711-
Ok(())
1716+
write!(f, "{}", on)?;
1717+
}
1718+
1719+
if let Some(returning) = returning {
1720+
write!(f, " RETURNING {}", display_comma_separated(returning))?;
17121721
}
1722+
1723+
Ok(())
17131724
}
17141725

17151726
Statement::Copy {
@@ -1753,6 +1764,7 @@ impl fmt::Display for Statement {
17531764
assignments,
17541765
from,
17551766
selection,
1767+
returning,
17561768
} => {
17571769
write!(f, "UPDATE {}", table)?;
17581770
if !assignments.is_empty() {
@@ -1764,12 +1776,16 @@ impl fmt::Display for Statement {
17641776
if let Some(selection) = selection {
17651777
write!(f, " WHERE {}", selection)?;
17661778
}
1779+
if let Some(returning) = returning {
1780+
write!(f, " RETURNING {}", display_comma_separated(returning))?;
1781+
}
17671782
Ok(())
17681783
}
17691784
Statement::Delete {
17701785
table_name,
17711786
using,
17721787
selection,
1788+
returning,
17731789
} => {
17741790
write!(f, "DELETE FROM {}", table_name)?;
17751791
if let Some(using) = using {
@@ -1778,6 +1794,9 @@ impl fmt::Display for Statement {
17781794
if let Some(selection) = selection {
17791795
write!(f, " WHERE {}", selection)?;
17801796
}
1797+
if let Some(returning) = returning {
1798+
write!(f, " RETURNING {}", display_comma_separated(returning))?;
1799+
}
17811800
Ok(())
17821801
}
17831802
Statement::Close { cursor } => {
@@ -2610,6 +2629,21 @@ pub enum MinMaxValue {
26102629
pub enum OnInsert {
26112630
/// ON DUPLICATE KEY UPDATE (MySQL when the key already exists, then execute an update instead)
26122631
DuplicateKeyUpdate(Vec<Assignment>),
2632+
/// ON CONFLICT is a PostgreSQL and Sqlite extension
2633+
OnConflict(OnConflict),
2634+
}
2635+
2636+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2637+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2638+
pub struct OnConflict {
2639+
pub conflict_target: Vec<Ident>,
2640+
pub action: OnConflictAction,
2641+
}
2642+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2643+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2644+
pub enum OnConflictAction {
2645+
DoNothing,
2646+
DoUpdate(Vec<Assignment>),
26132647
}
26142648

26152649
impl fmt::Display for OnInsert {
@@ -2620,6 +2654,24 @@ impl fmt::Display for OnInsert {
26202654
" ON DUPLICATE KEY UPDATE {}",
26212655
display_comma_separated(expr)
26222656
),
2657+
Self::OnConflict(o) => write!(f, " {o}"),
2658+
}
2659+
}
2660+
}
2661+
impl fmt::Display for OnConflict {
2662+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2663+
write!(f, " ON CONFLICT")?;
2664+
if !self.conflict_target.is_empty() {
2665+
write!(f, "({})", display_comma_separated(&self.conflict_target))?;
2666+
}
2667+
write!(f, " {}", self.action)
2668+
}
2669+
}
2670+
impl fmt::Display for OnConflictAction {
2671+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2672+
match self {
2673+
Self::DoNothing => write!(f, "DO NOTHING"),
2674+
Self::DoUpdate(a) => write!(f, "DO UPDATE SET {}", display_comma_separated(a)),
26232675
}
26242676
}
26252677
}

Diff for: src/keywords.rs

+4
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ define_keywords!(
144144
COMMITTED,
145145
COMPUTE,
146146
CONDITION,
147+
CONFLICT,
147148
CONNECT,
148149
CONNECTION,
149150
CONSTRAINT,
@@ -200,6 +201,7 @@ define_keywords!(
200201
DISCONNECT,
201202
DISTINCT,
202203
DISTRIBUTE,
204+
DO,
203205
DOUBLE,
204206
DOW,
205207
DOY,
@@ -370,6 +372,7 @@ define_keywords!(
370372
NOSCAN,
371373
NOSUPERUSER,
372374
NOT,
375+
NOTHING,
373376
NTH_VALUE,
374377
NTILE,
375378
NULL,
@@ -464,6 +467,7 @@ define_keywords!(
464467
RESTRICT,
465468
RESULT,
466469
RETURN,
470+
RETURNING,
467471
RETURNS,
468472
REVOKE,
469473
RIGHT,

Diff for: src/parser.rs

+45-5
Original file line numberDiff line numberDiff line change
@@ -4070,10 +4070,17 @@ impl<'a> Parser<'a> {
40704070
None
40714071
};
40724072

4073+
let returning = if self.parse_keyword(Keyword::RETURNING) {
4074+
Some(self.parse_comma_separated(Parser::parse_select_item)?)
4075+
} else {
4076+
None
4077+
};
4078+
40734079
Ok(Statement::Delete {
40744080
table_name,
40754081
using,
40764082
selection,
4083+
returning,
40774084
})
40784085
}
40794086

@@ -5191,12 +5198,38 @@ impl<'a> Parser<'a> {
51915198

51925199
let source = Box::new(self.parse_query()?);
51935200
let on = if self.parse_keyword(Keyword::ON) {
5194-
self.expect_keyword(Keyword::DUPLICATE)?;
5195-
self.expect_keyword(Keyword::KEY)?;
5196-
self.expect_keyword(Keyword::UPDATE)?;
5197-
let l = self.parse_comma_separated(Parser::parse_assignment)?;
5201+
if self.parse_keyword(Keyword::CONFLICT) {
5202+
let conflict_target =
5203+
self.parse_parenthesized_column_list(IsOptional::Optional)?;
51985204

5199-
Some(OnInsert::DuplicateKeyUpdate(l))
5205+
self.expect_keyword(Keyword::DO)?;
5206+
let action = if self.parse_keyword(Keyword::NOTHING) {
5207+
OnConflictAction::DoNothing
5208+
} else {
5209+
self.expect_keyword(Keyword::UPDATE)?;
5210+
self.expect_keyword(Keyword::SET)?;
5211+
let l = self.parse_comma_separated(Parser::parse_assignment)?;
5212+
OnConflictAction::DoUpdate(l)
5213+
};
5214+
5215+
Some(OnInsert::OnConflict(OnConflict {
5216+
conflict_target,
5217+
action,
5218+
}))
5219+
} else {
5220+
self.expect_keyword(Keyword::DUPLICATE)?;
5221+
self.expect_keyword(Keyword::KEY)?;
5222+
self.expect_keyword(Keyword::UPDATE)?;
5223+
let l = self.parse_comma_separated(Parser::parse_assignment)?;
5224+
5225+
Some(OnInsert::DuplicateKeyUpdate(l))
5226+
}
5227+
} else {
5228+
None
5229+
};
5230+
5231+
let returning = if self.parse_keyword(Keyword::RETURNING) {
5232+
Some(self.parse_comma_separated(Parser::parse_select_item)?)
52005233
} else {
52015234
None
52025235
};
@@ -5212,6 +5245,7 @@ impl<'a> Parser<'a> {
52125245
source,
52135246
table,
52145247
on,
5248+
returning,
52155249
})
52165250
}
52175251
}
@@ -5230,11 +5264,17 @@ impl<'a> Parser<'a> {
52305264
} else {
52315265
None
52325266
};
5267+
let returning = if self.parse_keyword(Keyword::RETURNING) {
5268+
Some(self.parse_comma_separated(Parser::parse_select_item)?)
5269+
} else {
5270+
None
5271+
};
52335272
Ok(Statement::Update {
52345273
table,
52355274
assignments,
52365275
from,
52375276
selection,
5277+
returning,
52385278
})
52395279
}
52405280

Diff for: tests/sqlparser_common.rs

+6
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ fn parse_update_with_table_alias() {
195195
assignments,
196196
from: _from,
197197
selection,
198+
returning,
198199
} => {
199200
assert_eq!(
200201
TableWithJoins {
@@ -231,6 +232,7 @@ fn parse_update_with_table_alias() {
231232
}),
232233
selection
233234
);
235+
assert_eq!(None, returning);
234236
}
235237
_ => unreachable!(),
236238
}
@@ -278,6 +280,7 @@ fn parse_where_delete_statement() {
278280
table_name,
279281
using,
280282
selection,
283+
returning,
281284
} => {
282285
assert_eq!(
283286
TableFactor::Table {
@@ -298,6 +301,7 @@ fn parse_where_delete_statement() {
298301
},
299302
selection.unwrap(),
300303
);
304+
assert_eq!(None, returning);
301305
}
302306
_ => unreachable!(),
303307
}
@@ -313,6 +317,7 @@ fn parse_where_delete_with_alias_statement() {
313317
table_name,
314318
using,
315319
selection,
320+
returning,
316321
} => {
317322
assert_eq!(
318323
TableFactor::Table {
@@ -353,6 +358,7 @@ fn parse_where_delete_with_alias_statement() {
353358
},
354359
selection.unwrap(),
355360
);
361+
assert_eq!(None, returning);
356362
}
357363
_ => unreachable!(),
358364
}

Diff for: tests/sqlparser_mysql.rs

+2
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,7 @@ fn parse_update_with_joins() {
814814
assignments,
815815
from: _from,
816816
selection,
817+
returning,
817818
} => {
818819
assert_eq!(
819820
TableWithJoins {
@@ -869,6 +870,7 @@ fn parse_update_with_joins() {
869870
}),
870871
selection
871872
);
873+
assert_eq!(None, returning);
872874
}
873875
_ => unreachable!(),
874876
}

0 commit comments

Comments
 (0)