Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 376f47e

Browse files
authored
feat: MERGE statements: add RETURNING and OUTPUT without INTO (#2011)
1 parent 5d5c90c commit 376f47e

File tree

5 files changed

+98
-25
lines changed

5 files changed

+98
-25
lines changed

‎src/ast/mod.rs‎

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9107,24 +9107,36 @@ impl Display for MergeClause {
91079107
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
91089108
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
91099109
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
9110-
pub struct OutputClause {
9111-
pub select_items: Vec<SelectItem>,
9112-
pub into_table: SelectInto,
9110+
pub enum OutputClause {
9111+
Output {
9112+
select_items: Vec<SelectItem>,
9113+
into_table: Option<SelectInto>,
9114+
},
9115+
Returning {
9116+
select_items: Vec<SelectItem>,
9117+
},
91139118
}
91149119

91159120
impl fmt::Display for OutputClause {
91169121
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
9117-
let OutputClause {
9118-
select_items,
9119-
into_table,
9120-
} = self;
9121-
9122-
write!(
9123-
f,
9124-
"OUTPUT {} {}",
9125-
display_comma_separated(select_items),
9126-
into_table
9127-
)
9122+
match self {
9123+
OutputClause::Output {
9124+
select_items,
9125+
into_table,
9126+
} => {
9127+
f.write_str("OUTPUT ")?;
9128+
display_comma_separated(select_items).fmt(f)?;
9129+
if let Some(into_table) = into_table {
9130+
f.write_str(" ")?;
9131+
into_table.fmt(f)?;
9132+
}
9133+
Ok(())
9134+
}
9135+
OutputClause::Returning { select_items } => {
9136+
f.write_str("RETURNING ")?;
9137+
display_comma_separated(select_items).fmt(f)
9138+
}
9139+
}
91289140
}
91299141
}
91309142

‎src/ast/query.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ pub enum SetExpr {
161161
Insert(Statement),
162162
Update(Statement),
163163
Delete(Statement),
164+
Merge(Statement),
164165
Table(Box<Table>),
165166
}
166167

@@ -188,6 +189,7 @@ impl fmt::Display for SetExpr {
188189
SetExpr::Insert(v) => v.fmt(f),
189190
SetExpr::Update(v) => v.fmt(f),
190191
SetExpr::Delete(v) => v.fmt(f),
192+
SetExpr::Merge(v) => v.fmt(f),
191193
SetExpr::Table(t) => t.fmt(f),
192194
SetExpr::SetOperation {
193195
left,

‎src/ast/spans.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ impl Spanned for SetExpr {
214214
SetExpr::Table(_) => Span::empty(),
215215
SetExpr::Update(statement) => statement.span(),
216216
SetExpr::Delete(statement) => statement.span(),
217+
SetExpr::Merge(statement) => statement.span(),
217218
}
218219
}
219220
}

‎src/parser/mod.rs‎

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11508,6 +11508,13 @@ impl<'a> Parser<'a> {
1150811508
Ok(Box::new(SetExpr::Delete(self.parse_delete()?)))
1150911509
}
1151011510

11511+
/// Parse a MERGE statement, returning a `Box`ed SetExpr
11512+
///
11513+
/// This is used to reduce the size of the stack frames in debug builds
11514+
fn parse_merge_setexpr_boxed(&mut self) -> Result<Box<SetExpr>, ParserError> {
11515+
Ok(Box::new(SetExpr::Merge(self.parse_merge()?)))
11516+
}
11517+
1151111518
pub fn parse_delete(&mut self) -> Result<Statement, ParserError> {
1151211519
let (tables, with_from_keyword) = if !self.parse_keyword(Keyword::FROM) {
1151311520
// `FROM` keyword is optional in BigQuery SQL.
@@ -11719,6 +11726,20 @@ impl<'a> Parser<'a> {
1171911726
pipe_operators: vec![],
1172011727
}
1172111728
.into())
11729+
} else if self.parse_keyword(Keyword::MERGE) {
11730+
Ok(Query {
11731+
with,
11732+
body: self.parse_merge_setexpr_boxed()?,
11733+
limit_clause: None,
11734+
order_by: None,
11735+
fetch: None,
11736+
locks: vec![],
11737+
for_clause: None,
11738+
settings: None,
11739+
format_clause: None,
11740+
pipe_operators: vec![],
11741+
}
11742+
.into())
1172211743
} else {
1172311744
let body = self.parse_query_body(self.dialect.prec_unknown())?;
1172411745

@@ -16571,15 +16592,22 @@ impl<'a> Parser<'a> {
1657116592
Ok(clauses)
1657216593
}
1657316594

16574-
fn parse_output(&mut self) -> Result<OutputClause, ParserError> {
16575-
self.expect_keyword_is(Keyword::OUTPUT)?;
16595+
fn parse_output(&mut self, start_keyword: Keyword) -> Result<OutputClause, ParserError> {
1657616596
let select_items = self.parse_projection()?;
16577-
self.expect_keyword_is(Keyword::INTO)?;
16578-
let into_table = self.parse_select_into()?;
16597+
let into_table = if start_keyword == Keyword::OUTPUT && self.peek_keyword(Keyword::INTO) {
16598+
self.expect_keyword_is(Keyword::INTO)?;
16599+
Some(self.parse_select_into()?)
16600+
} else {
16601+
None
16602+
};
1657916603

16580-
Ok(OutputClause {
16581-
select_items,
16582-
into_table,
16604+
Ok(if start_keyword == Keyword::OUTPUT {
16605+
OutputClause::Output {
16606+
select_items,
16607+
into_table,
16608+
}
16609+
} else {
16610+
OutputClause::Returning { select_items }
1658316611
})
1658416612
}
1658516613

@@ -16609,10 +16637,9 @@ impl<'a> Parser<'a> {
1660916637
self.expect_keyword_is(Keyword::ON)?;
1661016638
let on = self.parse_expr()?;
1661116639
let clauses = self.parse_merge_clauses()?;
16612-
let output = if self.peek_keyword(Keyword::OUTPUT) {
16613-
Some(self.parse_output()?)
16614-
} else {
16615-
None
16640+
let output = match self.parse_one_of_keywords(&[Keyword::OUTPUT, Keyword::RETURNING]) {
16641+
Some(start_keyword) => Some(self.parse_output(start_keyword)?),
16642+
None => None,
1661616643
};
1661716644

1661816645
Ok(Statement::Merge {

‎tests/sqlparser_common.rs‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9902,6 +9902,29 @@ fn parse_merge() {
99029902
verified_stmt(sql);
99039903
}
99049904

9905+
#[test]
9906+
fn test_merge_in_cte() {
9907+
verified_only_select(
9908+
"WITH x AS (\
9909+
MERGE INTO t USING (VALUES (1)) ON 1 = 1 \
9910+
WHEN MATCHED THEN DELETE \
9911+
RETURNING *\
9912+
) SELECT * FROM x",
9913+
);
9914+
}
9915+
9916+
#[test]
9917+
fn test_merge_with_returning() {
9918+
let sql = "MERGE INTO wines AS w \
9919+
USING wine_stock_changes AS s \
9920+
ON s.winename = w.winename \
9921+
WHEN NOT MATCHED AND s.stock_delta > 0 THEN INSERT VALUES (s.winename, s.stock_delta) \
9922+
WHEN MATCHED AND w.stock + s.stock_delta > 0 THEN UPDATE SET stock = w.stock + s.stock_delta \
9923+
WHEN MATCHED THEN DELETE \
9924+
RETURNING merge_action(), w.*";
9925+
verified_stmt(sql);
9926+
}
9927+
99059928
#[test]
99069929
fn test_merge_with_output() {
99079930
let sql = "MERGE INTO target_table USING source_table \
@@ -9915,6 +9938,14 @@ fn test_merge_with_output() {
99159938
verified_stmt(sql);
99169939
}
99179940

9941+
#[test]
9942+
fn test_merge_with_output_without_into() {
9943+
let sql = "MERGE INTO a USING b ON a.id = b.id \
9944+
WHEN MATCHED THEN DELETE \
9945+
OUTPUT inserted.*";
9946+
verified_stmt(sql);
9947+
}
9948+
99189949
#[test]
99199950
fn test_merge_into_using_table() {
99209951
let sql = "MERGE INTO target_table USING source_table \

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /