Skip to content

Commit 042effd

Browse files
authored
update on conflict method (#735)
1 parent a422116 commit 042effd

File tree

3 files changed

+94
-18
lines changed

3 files changed

+94
-18
lines changed

src/ast/mod.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2702,7 +2702,16 @@ pub struct OnConflict {
27022702
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
27032703
pub enum OnConflictAction {
27042704
DoNothing,
2705-
DoUpdate(Vec<Assignment>),
2705+
DoUpdate(DoUpdate),
2706+
}
2707+
2708+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2709+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2710+
pub struct DoUpdate {
2711+
/// Column assignments
2712+
pub assignments: Vec<Assignment>,
2713+
/// WHERE
2714+
pub selection: Option<Expr>,
27062715
}
27072716

27082717
impl fmt::Display for OnInsert {
@@ -2730,7 +2739,20 @@ impl fmt::Display for OnConflictAction {
27302739
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
27312740
match self {
27322741
Self::DoNothing => write!(f, "DO NOTHING"),
2733-
Self::DoUpdate(a) => write!(f, "DO UPDATE SET {}", display_comma_separated(a)),
2742+
Self::DoUpdate(do_update) => {
2743+
write!(f, "DO UPDATE")?;
2744+
if !do_update.assignments.is_empty() {
2745+
write!(
2746+
f,
2747+
" SET {}",
2748+
display_comma_separated(&do_update.assignments)
2749+
)?;
2750+
}
2751+
if let Some(selection) = &do_update.selection {
2752+
write!(f, " WHERE {}", selection)?;
2753+
}
2754+
Ok(())
2755+
}
27342756
}
27352757
}
27362758
}

src/parser.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5351,8 +5351,16 @@ impl<'a> Parser<'a> {
53515351
} else {
53525352
self.expect_keyword(Keyword::UPDATE)?;
53535353
self.expect_keyword(Keyword::SET)?;
5354-
let l = self.parse_comma_separated(Parser::parse_assignment)?;
5355-
OnConflictAction::DoUpdate(l)
5354+
let assignments = self.parse_comma_separated(Parser::parse_assignment)?;
5355+
let selection = if self.parse_keyword(Keyword::WHERE) {
5356+
Some(self.parse_expr()?)
5357+
} else {
5358+
None
5359+
};
5360+
OnConflictAction::DoUpdate(DoUpdate {
5361+
assignments,
5362+
selection,
5363+
})
53565364
};
53575365

53585366
Some(OnInsert::OnConflict(OnConflict {

tests/sqlparser_postgres.rs

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,10 +1117,13 @@ fn parse_pg_on_conflict() {
11171117
} => {
11181118
assert_eq!(vec![Ident::from("did")], conflict_target);
11191119
assert_eq!(
1120-
OnConflictAction::DoUpdate(vec![Assignment {
1121-
id: vec!["dname".into()],
1122-
value: Expr::CompoundIdentifier(vec!["EXCLUDED".into(), "dname".into()])
1123-
},]),
1120+
OnConflictAction::DoUpdate(DoUpdate {
1121+
assignments: vec![Assignment {
1122+
id: vec!["dname".into()],
1123+
value: Expr::CompoundIdentifier(vec!["EXCLUDED".into(), "dname".into()])
1124+
},],
1125+
selection: None
1126+
}),
11241127
action
11251128
);
11261129
}
@@ -1147,16 +1150,22 @@ fn parse_pg_on_conflict() {
11471150
conflict_target
11481151
);
11491152
assert_eq!(
1150-
OnConflictAction::DoUpdate(vec![
1151-
Assignment {
1152-
id: vec!["dname".into()],
1153-
value: Expr::CompoundIdentifier(vec!["EXCLUDED".into(), "dname".into()])
1154-
},
1155-
Assignment {
1156-
id: vec!["area".into()],
1157-
value: Expr::CompoundIdentifier(vec!["EXCLUDED".into(), "area".into()])
1158-
},
1159-
]),
1153+
OnConflictAction::DoUpdate(DoUpdate {
1154+
assignments: vec![
1155+
Assignment {
1156+
id: vec!["dname".into()],
1157+
value: Expr::CompoundIdentifier(vec![
1158+
"EXCLUDED".into(),
1159+
"dname".into()
1160+
])
1161+
},
1162+
Assignment {
1163+
id: vec!["area".into()],
1164+
value: Expr::CompoundIdentifier(vec!["EXCLUDED".into(), "area".into()])
1165+
},
1166+
],
1167+
selection: None
1168+
}),
11601169
action
11611170
);
11621171
}
@@ -1182,6 +1191,43 @@ fn parse_pg_on_conflict() {
11821191
}
11831192
_ => unreachable!(),
11841193
};
1194+
1195+
let stmt = pg_and_generic().verified_stmt(
1196+
"INSERT INTO distributors (did, dname, dsize) \
1197+
VALUES (5, 'Gizmo Transglobal', 1000), (6, 'Associated Computing, Inc', 1010) \
1198+
ON CONFLICT(did) \
1199+
DO UPDATE SET dname = $1 WHERE dsize > $2",
1200+
);
1201+
match stmt {
1202+
Statement::Insert {
1203+
on:
1204+
Some(OnInsert::OnConflict(OnConflict {
1205+
conflict_target,
1206+
action,
1207+
})),
1208+
..
1209+
} => {
1210+
assert_eq!(vec![Ident::from("did")], conflict_target);
1211+
assert_eq!(
1212+
OnConflictAction::DoUpdate(DoUpdate {
1213+
assignments: vec![Assignment {
1214+
id: vec!["dname".into()],
1215+
value: Expr::Value(Value::Placeholder("$1".to_string()))
1216+
},],
1217+
selection: Some(Expr::BinaryOp {
1218+
left: Box::new(Expr::Identifier(Ident {
1219+
value: "dsize".to_string(),
1220+
quote_style: None
1221+
})),
1222+
op: BinaryOperator::Gt,
1223+
right: Box::new(Expr::Value(Value::Placeholder("$2".to_string())))
1224+
})
1225+
}),
1226+
action
1227+
);
1228+
}
1229+
_ => unreachable!(),
1230+
};
11851231
}
11861232

11871233
#[test]

0 commit comments

Comments
 (0)