Skip to content

Commit bba2d2d

Browse files
Parse PostgreSQL MERGE DO NOTHING actions
1 parent c0054b7 commit bba2d2d

4 files changed

Lines changed: 88 additions & 3 deletions

File tree

src/ast/dml.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,13 @@ pub enum MergeAction {
608608
/// The `DELETE` token that starts the sub-expression.
609609
delete_token: AttachedToken,
610610
},
611+
/// A `DO NOTHING` clause.
612+
DoNothing {
613+
/// The `DO` token that starts the sub-expression.
614+
do_token: AttachedToken,
615+
/// The `NOTHING` token that ends the sub-expression.
616+
nothing_token: AttachedToken,
617+
},
611618
}
612619

613620
impl Display for MergeAction {
@@ -622,6 +629,9 @@ impl Display for MergeAction {
622629
MergeAction::Delete { .. } => {
623630
write!(f, "DELETE")
624631
}
632+
MergeAction::DoNothing { .. } => {
633+
write!(f, "DO NOTHING")
634+
}
625635
}
626636
}
627637
}

src/ast/spans.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2557,6 +2557,10 @@ impl Spanned for MergeAction {
25572557
MergeAction::Insert(expr) => expr.span(),
25582558
MergeAction::Update(expr) => expr.span(),
25592559
MergeAction::Delete { delete_token } => delete_token.0.span,
2560+
MergeAction::DoNothing {
2561+
do_token,
2562+
nothing_token,
2563+
} => do_token.0.span.union(&nothing_token.0.span),
25602564
}
25612565
}
25622566
}
@@ -2909,6 +2913,7 @@ WHERE id = 1
29092913
29102914
WHEN MATCHED AND target_table.x != 'X' THEN DELETE
29112915
WHEN NOT MATCHED AND 1 THEN INSERT (product, quantity) ROW
2916+
WHEN MATCHED THEN DO NOTHING
29122917
"#;
29132918

29142919
let r = Parser::parse_sql(&crate::dialect::GenericDialect, sql).unwrap();
@@ -2917,7 +2922,7 @@ WHERE id = 1
29172922
// ~ assert the span of the whole statement
29182923
let stmt_span = r[0].span();
29192924
assert_eq!(stmt_span.start, (4, 9).into());
2920-
assert_eq!(stmt_span.end, (16, 67).into());
2925+
assert_eq!(stmt_span.end, (17, 37).into());
29212926

29222927
// ~ individual tokens within the statement
29232928
let Statement::Merge(Merge {
@@ -2937,7 +2942,7 @@ WHERE id = 1
29372942
merge_token.0.span,
29382943
Span::new(Location::new(4, 9), Location::new(4, 14))
29392944
);
2940-
assert_eq!(clauses.len(), 4);
2945+
assert_eq!(clauses.len(), 5);
29412946

29422947
// ~ the INSERT clause's TOKENs
29432948
assert_eq!(
@@ -3019,6 +3024,31 @@ WHERE id = 1
30193024
panic!("not a MERGE INSERT clause");
30203025
}
30213026

3027+
assert_eq!(
3028+
clauses[4].when_token.0.span,
3029+
Span::new(Location::new(17, 9), Location::new(17, 13))
3030+
);
3031+
if let MergeAction::DoNothing {
3032+
do_token,
3033+
nothing_token,
3034+
} = &clauses[4].action
3035+
{
3036+
assert_eq!(
3037+
do_token.0.span,
3038+
Span::new(Location::new(17, 27), Location::new(17, 29))
3039+
);
3040+
assert_eq!(
3041+
nothing_token.0.span,
3042+
Span::new(Location::new(17, 30), Location::new(17, 37))
3043+
);
3044+
assert_eq!(
3045+
clauses[4].action.span(),
3046+
Span::new(Location::new(17, 27), Location::new(17, 37))
3047+
);
3048+
} else {
3049+
panic!("not a MERGE DO NOTHING clause");
3050+
}
3051+
30223052
assert!(output.is_none());
30233053
}
30243054

src/parser/merge.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,16 @@ impl Parser<'_> {
107107
Keyword::UPDATE,
108108
Keyword::INSERT,
109109
Keyword::DELETE,
110+
Keyword::DO,
110111
]) {
112+
Some(Keyword::DO) => {
113+
let do_token = self.get_current_token().clone();
114+
let nothing_token = self.expect_keyword(Keyword::NOTHING)?;
115+
MergeAction::DoNothing {
116+
do_token: do_token.into(),
117+
nothing_token: nothing_token.into(),
118+
}
119+
}
111120
Some(Keyword::UPDATE) => {
112121
if matches!(
113122
clause_kind,
@@ -212,7 +221,7 @@ impl Parser<'_> {
212221
}
213222
_ => {
214223
return parser_err!(
215-
"expected UPDATE, DELETE or INSERT in merge clause",
224+
"expected UPDATE, DELETE, INSERT or DO NOTHING in merge clause",
216225
self.peek_token_ref().span.start
217226
);
218227
}

tests/sqlparser_postgres.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9852,3 +9852,39 @@ fn parse_alter_table_constraint_check_no_inherit() {
98529852
}
98539853
pg_and_generic().verified_stmt("ALTER TABLE docs ADD CONSTRAINT c CHECK (id > 0) NO INHERIT");
98549854
}
9855+
9856+
#[test]
9857+
fn parse_merge_do_nothing() {
9858+
let Statement::Merge(merge) = pg_and_generic().verified_stmt(
9859+
"MERGE INTO target USING source ON target.id = source.id WHEN MATCHED THEN DO NOTHING WHEN NOT MATCHED THEN DO NOTHING",
9860+
) else {
9861+
panic!("expected MERGE statement");
9862+
};
9863+
assert!(matches!(
9864+
merge.clauses.as_slice(),
9865+
[
9866+
MergeClause {
9867+
clause_kind: MergeClauseKind::Matched,
9868+
action: MergeAction::DoNothing { .. },
9869+
..
9870+
},
9871+
MergeClause {
9872+
clause_kind: MergeClauseKind::NotMatched,
9873+
action: MergeAction::DoNothing { .. },
9874+
..
9875+
}
9876+
]
9877+
));
9878+
}
9879+
9880+
#[test]
9881+
fn reject_merge_do_without_nothing() {
9882+
assert_eq!(
9883+
pg_and_generic().parse_sql_statements(
9884+
"MERGE INTO target USING source ON target.id = source.id WHEN MATCHED THEN DO UPDATE"
9885+
),
9886+
Err(ParserError::ParserError(
9887+
"Expected: NOTHING, found: UPDATE".into()
9888+
))
9889+
);
9890+
}

0 commit comments

Comments
 (0)