Skip to content

Commit f72e2ec

Browse files
authored
Support multiple-table DELETE syntax (#855)
1 parent 5ecf633 commit f72e2ec

File tree

3 files changed

+131
-26
lines changed

3 files changed

+131
-26
lines changed

src/ast/mod.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,10 +1229,12 @@ pub enum Statement {
12291229
},
12301230
/// DELETE
12311231
Delete {
1232+
/// Multi tables delete are supported in mysql
1233+
tables: Vec<ObjectName>,
12321234
/// FROM
1233-
table_name: TableFactor,
1234-
/// USING (Snowflake, Postgres)
1235-
using: Option<TableFactor>,
1235+
from: Vec<TableWithJoins>,
1236+
/// USING (Snowflake, Postgres, MySQL)
1237+
using: Option<Vec<TableWithJoins>>,
12361238
/// WHERE
12371239
selection: Option<Expr>,
12381240
/// RETURNING
@@ -1982,14 +1984,19 @@ impl fmt::Display for Statement {
19821984
Ok(())
19831985
}
19841986
Statement::Delete {
1985-
table_name,
1987+
tables,
1988+
from,
19861989
using,
19871990
selection,
19881991
returning,
19891992
} => {
1990-
write!(f, "DELETE FROM {table_name}")?;
1993+
write!(f, "DELETE ")?;
1994+
if !tables.is_empty() {
1995+
write!(f, "{} ", display_comma_separated(tables))?;
1996+
}
1997+
write!(f, "FROM {}", display_comma_separated(from))?;
19911998
if let Some(using) = using {
1992-
write!(f, " USING {using}")?;
1999+
write!(f, " USING {}", display_comma_separated(using))?;
19932000
}
19942001
if let Some(selection) = selection {
19952002
write!(f, " WHERE {selection}")?;

src/parser.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4813,10 +4813,17 @@ impl<'a> Parser<'a> {
48134813
}
48144814

48154815
pub fn parse_delete(&mut self) -> Result<Statement, ParserError> {
4816-
self.expect_keyword(Keyword::FROM)?;
4817-
let table_name = self.parse_table_factor()?;
4816+
let tables = if !self.parse_keyword(Keyword::FROM) {
4817+
let tables = self.parse_comma_separated(Parser::parse_object_name)?;
4818+
self.expect_keyword(Keyword::FROM)?;
4819+
tables
4820+
} else {
4821+
vec![]
4822+
};
4823+
4824+
let from = self.parse_comma_separated(Parser::parse_table_and_joins)?;
48184825
let using = if self.parse_keyword(Keyword::USING) {
4819-
Some(self.parse_table_factor()?)
4826+
Some(self.parse_comma_separated(Parser::parse_table_and_joins)?)
48204827
} else {
48214828
None
48224829
};
@@ -4833,7 +4840,8 @@ impl<'a> Parser<'a> {
48334840
};
48344841

48354842
Ok(Statement::Delete {
4836-
table_name,
4843+
tables,
4844+
from,
48374845
using,
48384846
selection,
48394847
returning,

tests/sqlparser_common.rs

Lines changed: 106 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -385,15 +385,101 @@ fn parse_no_table_name() {
385385
fn parse_delete_statement() {
386386
let sql = "DELETE FROM \"table\"";
387387
match verified_stmt(sql) {
388-
Statement::Delete { table_name, .. } => {
388+
Statement::Delete { from, .. } => {
389389
assert_eq!(
390390
TableFactor::Table {
391391
name: ObjectName(vec![Ident::with_quote('"', "table")]),
392392
alias: None,
393393
args: None,
394394
with_hints: vec![],
395395
},
396-
table_name
396+
from[0].relation
397+
);
398+
}
399+
_ => unreachable!(),
400+
}
401+
}
402+
403+
#[test]
404+
fn parse_delete_statement_for_multi_tables() {
405+
let sql = "DELETE schema1.table1, schema2.table2 FROM schema1.table1 JOIN schema2.table2 ON schema2.table2.col1 = schema1.table1.col1 WHERE schema2.table2.col2 = 1";
406+
match verified_stmt(sql) {
407+
Statement::Delete { tables, from, .. } => {
408+
assert_eq!(
409+
ObjectName(vec![Ident::new("schema1"), Ident::new("table1")]),
410+
tables[0]
411+
);
412+
assert_eq!(
413+
ObjectName(vec![Ident::new("schema2"), Ident::new("table2")]),
414+
tables[1]
415+
);
416+
assert_eq!(
417+
TableFactor::Table {
418+
name: ObjectName(vec![Ident::new("schema1"), Ident::new("table1")]),
419+
alias: None,
420+
args: None,
421+
with_hints: vec![],
422+
},
423+
from[0].relation
424+
);
425+
assert_eq!(
426+
TableFactor::Table {
427+
name: ObjectName(vec![Ident::new("schema2"), Ident::new("table2")]),
428+
alias: None,
429+
args: None,
430+
with_hints: vec![],
431+
},
432+
from[0].joins[0].relation
433+
);
434+
}
435+
_ => unreachable!(),
436+
}
437+
}
438+
439+
#[test]
440+
fn parse_delete_statement_for_multi_tables_with_using() {
441+
let sql = "DELETE FROM schema1.table1, schema2.table2 USING schema1.table1 JOIN schema2.table2 ON schema2.table2.pk = schema1.table1.col1 WHERE schema2.table2.col2 = 1";
442+
match verified_stmt(sql) {
443+
Statement::Delete {
444+
from,
445+
using: Some(using),
446+
..
447+
} => {
448+
assert_eq!(
449+
TableFactor::Table {
450+
name: ObjectName(vec![Ident::new("schema1"), Ident::new("table1")]),
451+
alias: None,
452+
args: None,
453+
with_hints: vec![],
454+
},
455+
from[0].relation
456+
);
457+
assert_eq!(
458+
TableFactor::Table {
459+
name: ObjectName(vec![Ident::new("schema2"), Ident::new("table2")]),
460+
alias: None,
461+
args: None,
462+
with_hints: vec![],
463+
},
464+
from[1].relation
465+
);
466+
assert_eq!(
467+
TableFactor::Table {
468+
name: ObjectName(vec![Ident::new("schema1"), Ident::new("table1")]),
469+
alias: None,
470+
args: None,
471+
with_hints: vec![],
472+
},
473+
using[0].relation
474+
);
475+
assert_eq!(
476+
TableFactor::Table {
477+
name: ObjectName(vec![Ident::new("schema2"), Ident::new("table2")]),
478+
alias: None,
479+
args: None,
480+
with_hints: vec![],
481+
},
482+
using[0].joins[0].relation
397483
);
398484
}
399485
_ => unreachable!(),
@@ -407,7 +493,8 @@ fn parse_where_delete_statement() {
407493
let sql = "DELETE FROM foo WHERE name = 5";
408494
match verified_stmt(sql) {
409495
Statement::Delete {
410-
table_name,
496+
tables: _,
497+
from,
411498
using,
412499
selection,
413500
returning,
@@ -419,7 +506,7 @@ fn parse_where_delete_statement() {
419506
args: None,
420507
with_hints: vec![],
421508
},
422-
table_name,
509+
from[0].relation,
423510
);
424511

425512
assert_eq!(None, using);
@@ -444,7 +531,8 @@ fn parse_where_delete_with_alias_statement() {
444531
let sql = "DELETE FROM basket AS a USING basket AS b WHERE a.id < b.id";
445532
match verified_stmt(sql) {
446533
Statement::Delete {
447-
table_name,
534+
tables: _,
535+
from,
448536
using,
449537
selection,
450538
returning,
@@ -459,19 +547,21 @@ fn parse_where_delete_with_alias_statement() {
459547
args: None,
460548
with_hints: vec![],
461549
},
462-
table_name,
550+
from[0].relation,
463551
);
464-
465552
assert_eq!(
466-
Some(TableFactor::Table {
467-
name: ObjectName(vec![Ident::new("basket")]),
468-
alias: Some(TableAlias {
469-
name: Ident::new("b"),
470-
columns: vec![],
471-
}),
472-
args: None,
473-
with_hints: vec![],
474-
}),
553+
Some(vec![TableWithJoins {
554+
relation: TableFactor::Table {
555+
name: ObjectName(vec![Ident::new("basket")]),
556+
alias: Some(TableAlias {
557+
name: Ident::new("b"),
558+
columns: vec![],
559+
}),
560+
args: None,
561+
with_hints: vec![],
562+
},
563+
joins: vec![],
564+
}]),
475565
using
476566
);
477567
assert_eq!(

0 commit comments

Comments
 (0)