Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/ast/dml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,13 @@ pub enum MergeAction {
/// The `DELETE` token that starts the sub-expression.
delete_token: AttachedToken,
},
/// A `DO NOTHING` clause.
DoNothing {
/// The `DO` token that starts the sub-expression.
do_token: AttachedToken,
/// The `NOTHING` token that ends the sub-expression.
nothing_token: AttachedToken,
},
}

impl Display for MergeAction {
Expand All @@ -622,6 +629,9 @@ impl Display for MergeAction {
MergeAction::Delete { .. } => {
write!(f, "DELETE")
}
MergeAction::DoNothing { .. } => {
write!(f, "DO NOTHING")
}
}
}
}
Expand Down
34 changes: 32 additions & 2 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2557,6 +2557,10 @@ impl Spanned for MergeAction {
MergeAction::Insert(expr) => expr.span(),
MergeAction::Update(expr) => expr.span(),
MergeAction::Delete { delete_token } => delete_token.0.span,
MergeAction::DoNothing {
do_token,
nothing_token,
} => do_token.0.span.union(&nothing_token.0.span),
}
}
}
Expand Down Expand Up @@ -2909,6 +2913,7 @@ WHERE id = 1

WHEN MATCHED AND target_table.x != 'X' THEN DELETE
WHEN NOT MATCHED AND 1 THEN INSERT (product, quantity) ROW
WHEN MATCHED THEN DO NOTHING
"#;

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

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

// ~ the INSERT clause's TOKENs
assert_eq!(
Expand Down Expand Up @@ -3019,6 +3024,31 @@ WHERE id = 1
panic!("not a MERGE INSERT clause");
}

assert_eq!(
clauses[4].when_token.0.span,
Span::new(Location::new(17, 9), Location::new(17, 13))
);
if let MergeAction::DoNothing {
do_token,
nothing_token,
} = &clauses[4].action
{
assert_eq!(
do_token.0.span,
Span::new(Location::new(17, 27), Location::new(17, 29))
);
assert_eq!(
nothing_token.0.span,
Span::new(Location::new(17, 30), Location::new(17, 37))
);
assert_eq!(
clauses[4].action.span(),
Span::new(Location::new(17, 27), Location::new(17, 37))
);
} else {
panic!("not a MERGE DO NOTHING clause");
}

assert!(output.is_none());
}

Expand Down
11 changes: 10 additions & 1 deletion src/parser/merge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,16 @@ impl Parser<'_> {
Keyword::UPDATE,
Keyword::INSERT,
Keyword::DELETE,
Keyword::DO,
]) {
Some(Keyword::DO) => {
let do_token = self.get_current_token().clone();
let nothing_token = self.expect_keyword(Keyword::NOTHING)?;
MergeAction::DoNothing {
do_token: do_token.into(),
nothing_token: nothing_token.into(),
}
}
Some(Keyword::UPDATE) => {
if matches!(
clause_kind,
Expand Down Expand Up @@ -212,7 +221,7 @@ impl Parser<'_> {
}
_ => {
return parser_err!(
"expected UPDATE, DELETE or INSERT in merge clause",
"expected UPDATE, DELETE, INSERT or DO NOTHING in merge clause",
self.peek_token_ref().span.start
);
}
Expand Down
36 changes: 36 additions & 0 deletions tests/sqlparser_postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9852,3 +9852,39 @@ fn parse_alter_table_constraint_check_no_inherit() {
}
pg_and_generic().verified_stmt("ALTER TABLE docs ADD CONSTRAINT c CHECK (id > 0) NO INHERIT");
}

#[test]
fn parse_merge_do_nothing() {
let Statement::Merge(merge) = pg_and_generic().verified_stmt(
"MERGE INTO target USING source ON target.id = source.id WHEN MATCHED THEN DO NOTHING WHEN NOT MATCHED THEN DO NOTHING",
) else {
panic!("expected MERGE statement");
};
assert!(matches!(
merge.clauses.as_slice(),
[
MergeClause {
clause_kind: MergeClauseKind::Matched,
action: MergeAction::DoNothing { .. },
..
},
MergeClause {
clause_kind: MergeClauseKind::NotMatched,
action: MergeAction::DoNothing { .. },
..
}
]
));
}

#[test]
fn reject_merge_do_without_nothing() {
assert_eq!(
pg_and_generic().parse_sql_statements(
"MERGE INTO target USING source ON target.id = source.id WHEN MATCHED THEN DO UPDATE"
),
Err(ParserError::ParserError(
"Expected: NOTHING, found: UPDATE".into()
))
);
}
Loading