Skip to content

Commit 9750841

Browse files
mskrzypkowsMaciej Skrzypkowski
and
Maciej Skrzypkowski
authored
Parse merge source as table factor (apache#483)
* Parse source of MERGE as table_factor Some MERGE queries need a table as a soruce, added proper test showing it * Clippy fix Co-authored-by: Maciej Skrzypkowski <maciej.skrzypkowski@satoricyber.com>
1 parent 35f5f0b commit 9750841

File tree

3 files changed

+58
-55
lines changed

3 files changed

+58
-55
lines changed

src/ast/mod.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,9 +1058,7 @@ pub enum Statement {
10581058
// Specifies the table to merge
10591059
table: TableFactor,
10601060
// Specifies the table or subquery to join with the target table
1061-
source: Box<SetExpr>,
1062-
// Specifies alias to the table that is joined with target table
1063-
alias: Option<TableAlias>,
1061+
source: TableFactor,
10641062
// Specifies the expression on which to join the target table and source
10651063
on: Box<Expr>,
10661064
// Specifies the actions to perform when values match or do not match.
@@ -1772,7 +1770,6 @@ impl fmt::Display for Statement {
17721770
into,
17731771
table,
17741772
source,
1775-
alias,
17761773
on,
17771774
clauses,
17781775
} => {
@@ -1781,9 +1778,6 @@ impl fmt::Display for Statement {
17811778
"MERGE{int} {table} USING {source} ",
17821779
int = if *into { " INTO" } else { "" }
17831780
)?;
1784-
if let Some(a) = alias {
1785-
write!(f, "as {} ", a)?;
1786-
};
17871781
write!(f, "ON {} ", on)?;
17881782
write!(f, "{}", display_separated(clauses, " "))
17891783
}

src/parser.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4288,17 +4288,15 @@ impl<'a> Parser<'a> {
42884288
let table = self.parse_table_factor()?;
42894289

42904290
self.expect_keyword(Keyword::USING)?;
4291-
let source = self.parse_query_body(0)?;
4292-
let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?;
4291+
let source = self.parse_table_factor()?;
42934292
self.expect_keyword(Keyword::ON)?;
42944293
let on = self.parse_expr()?;
42954294
let clauses = self.parse_merge_clauses()?;
42964295

42974296
Ok(Statement::Merge {
42984297
into,
42994298
table,
4300-
source: Box::new(source),
4301-
alias,
4299+
source,
43024300
on: Box::new(on),
43034301
clauses,
43044302
})

tests/sqlparser_common.rs

Lines changed: 55 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4353,23 +4353,21 @@ fn test_revoke() {
43534353

43544354
#[test]
43554355
fn parse_merge() {
4356-
let sql = "MERGE INTO s.bar AS dest USING (SELECT * FROM s.foo) as stg ON dest.D = stg.D AND dest.E = stg.E WHEN NOT MATCHED THEN INSERT (A, B, C) VALUES (stg.A, stg.B, stg.C) WHEN MATCHED AND dest.A = 'a' THEN UPDATE SET dest.F = stg.F, dest.G = stg.G WHEN MATCHED THEN DELETE";
4357-
let sql_no_into = "MERGE s.bar AS dest USING (SELECT * FROM s.foo) as stg ON dest.D = stg.D AND dest.E = stg.E WHEN NOT MATCHED THEN INSERT (A, B, C) VALUES (stg.A, stg.B, stg.C) WHEN MATCHED AND dest.A = 'a' THEN UPDATE SET dest.F = stg.F, dest.G = stg.G WHEN MATCHED THEN DELETE";
4356+
let sql = "MERGE INTO s.bar AS dest USING (SELECT * FROM s.foo) AS stg ON dest.D = stg.D AND dest.E = stg.E WHEN NOT MATCHED THEN INSERT (A, B, C) VALUES (stg.A, stg.B, stg.C) WHEN MATCHED AND dest.A = 'a' THEN UPDATE SET dest.F = stg.F, dest.G = stg.G WHEN MATCHED THEN DELETE";
4357+
let sql_no_into = "MERGE s.bar AS dest USING (SELECT * FROM s.foo) AS stg ON dest.D = stg.D AND dest.E = stg.E WHEN NOT MATCHED THEN INSERT (A, B, C) VALUES (stg.A, stg.B, stg.C) WHEN MATCHED AND dest.A = 'a' THEN UPDATE SET dest.F = stg.F, dest.G = stg.G WHEN MATCHED THEN DELETE";
43584358
match (verified_stmt(sql), verified_stmt(sql_no_into)) {
43594359
(
43604360
Statement::Merge {
43614361
into,
43624362
table,
43634363
source,
4364-
alias,
43654364
on,
43664365
clauses,
43674366
},
43684367
Statement::Merge {
43694368
into: no_into,
43704369
table: table_no_into,
43714370
source: source_no_into,
4372-
alias: alias_no_into,
43734371
on: on_no_into,
43744372
clauses: clauses_no_into,
43754373
},
@@ -4393,49 +4391,50 @@ fn parse_merge() {
43934391

43944392
assert_eq!(
43954393
source,
4396-
Box::new(SetExpr::Query(Box::new(Query {
4397-
with: None,
4398-
body: SetExpr::Select(Box::new(Select {
4399-
distinct: false,
4400-
top: None,
4401-
projection: vec![SelectItem::Wildcard],
4402-
into: None,
4403-
from: vec![TableWithJoins {
4404-
relation: TableFactor::Table {
4405-
name: ObjectName(vec![Ident::new("s"), Ident::new("foo")]),
4406-
alias: None,
4407-
args: vec![],
4408-
with_hints: vec![],
4409-
},
4410-
joins: vec![]
4411-
}],
4412-
lateral_views: vec![],
4413-
selection: None,
4414-
group_by: vec![],
4415-
cluster_by: vec![],
4416-
distribute_by: vec![],
4417-
sort_by: vec![],
4418-
having: None,
4419-
qualify: None
4420-
})),
4421-
order_by: vec![],
4422-
limit: None,
4423-
offset: None,
4424-
fetch: None,
4425-
lock: None
4426-
})))
4394+
TableFactor::Derived {
4395+
lateral: false,
4396+
subquery: Box::new(Query {
4397+
with: None,
4398+
body: SetExpr::Select(Box::new(Select {
4399+
distinct: false,
4400+
top: None,
4401+
projection: vec![SelectItem::Wildcard],
4402+
into: None,
4403+
from: vec![TableWithJoins {
4404+
relation: TableFactor::Table {
4405+
name: ObjectName(vec![Ident::new("s"), Ident::new("foo")]),
4406+
alias: None,
4407+
args: vec![],
4408+
with_hints: vec![]
4409+
},
4410+
joins: vec![]
4411+
}],
4412+
lateral_views: vec![],
4413+
selection: None,
4414+
group_by: vec![],
4415+
cluster_by: vec![],
4416+
distribute_by: vec![],
4417+
sort_by: vec![],
4418+
having: None,
4419+
qualify: None,
4420+
})),
4421+
order_by: vec![],
4422+
limit: None,
4423+
offset: None,
4424+
fetch: None,
4425+
lock: None
4426+
}),
4427+
alias: Some(TableAlias {
4428+
name: Ident {
4429+
value: "stg".to_string(),
4430+
quote_style: None
4431+
},
4432+
columns: vec![]
4433+
})
4434+
}
44274435
);
44284436
assert_eq!(source, source_no_into);
44294437

4430-
assert_eq!(
4431-
alias,
4432-
Some(TableAlias {
4433-
name: Ident::new("stg"),
4434-
columns: vec![]
4435-
})
4436-
);
4437-
assert_eq!(alias, alias_no_into);
4438-
44394438
assert_eq!(
44404439
on,
44414440
Box::new(Expr::BinaryOp {
@@ -4515,6 +4514,18 @@ fn parse_merge() {
45154514
}
45164515
}
45174516

4517+
#[test]
4518+
fn test_merge_into_using_table() {
4519+
let sql = "MERGE INTO target_table USING source_table \
4520+
ON target_table.id = source_table.oooid \
4521+
WHEN MATCHED THEN \
4522+
UPDATE SET target_table.description = source_table.description \
4523+
WHEN NOT MATCHED THEN \
4524+
INSERT (ID, description) VALUES (source_table.id, source_table.description)";
4525+
4526+
verified_stmt(sql);
4527+
}
4528+
45184529
#[test]
45194530
fn test_lock() {
45204531
let sql = "SELECT * FROM student WHERE id = '1' FOR UPDATE";

0 commit comments

Comments
 (0)