Support multiple-table DELETE syntax (#855)

This commit is contained in:
AviRaboah 2023-04-27 18:41:20 +03:00 committed by GitHub
parent 5ecf633e31
commit f72e2ec382
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 131 additions and 26 deletions

View file

@ -1229,10 +1229,12 @@ pub enum Statement {
}, },
/// DELETE /// DELETE
Delete { Delete {
/// Multi tables delete are supported in mysql
tables: Vec<ObjectName>,
/// FROM /// FROM
table_name: TableFactor, from: Vec<TableWithJoins>,
/// USING (Snowflake, Postgres) /// USING (Snowflake, Postgres, MySQL)
using: Option<TableFactor>, using: Option<Vec<TableWithJoins>>,
/// WHERE /// WHERE
selection: Option<Expr>, selection: Option<Expr>,
/// RETURNING /// RETURNING
@ -1982,14 +1984,19 @@ impl fmt::Display for Statement {
Ok(()) Ok(())
} }
Statement::Delete { Statement::Delete {
table_name, tables,
from,
using, using,
selection, selection,
returning, returning,
} => { } => {
write!(f, "DELETE FROM {table_name}")?; write!(f, "DELETE ")?;
if !tables.is_empty() {
write!(f, "{} ", display_comma_separated(tables))?;
}
write!(f, "FROM {}", display_comma_separated(from))?;
if let Some(using) = using { if let Some(using) = using {
write!(f, " USING {using}")?; write!(f, " USING {}", display_comma_separated(using))?;
} }
if let Some(selection) = selection { if let Some(selection) = selection {
write!(f, " WHERE {selection}")?; write!(f, " WHERE {selection}")?;

View file

@ -4813,10 +4813,17 @@ impl<'a> Parser<'a> {
} }
pub fn parse_delete(&mut self) -> Result<Statement, ParserError> { pub fn parse_delete(&mut self) -> Result<Statement, ParserError> {
self.expect_keyword(Keyword::FROM)?; let tables = if !self.parse_keyword(Keyword::FROM) {
let table_name = self.parse_table_factor()?; let tables = self.parse_comma_separated(Parser::parse_object_name)?;
self.expect_keyword(Keyword::FROM)?;
tables
} else {
vec![]
};
let from = self.parse_comma_separated(Parser::parse_table_and_joins)?;
let using = if self.parse_keyword(Keyword::USING) { let using = if self.parse_keyword(Keyword::USING) {
Some(self.parse_table_factor()?) Some(self.parse_comma_separated(Parser::parse_table_and_joins)?)
} else { } else {
None None
}; };
@ -4833,7 +4840,8 @@ impl<'a> Parser<'a> {
}; };
Ok(Statement::Delete { Ok(Statement::Delete {
table_name, tables,
from,
using, using,
selection, selection,
returning, returning,

View file

@ -385,7 +385,7 @@ fn parse_no_table_name() {
fn parse_delete_statement() { fn parse_delete_statement() {
let sql = "DELETE FROM \"table\""; let sql = "DELETE FROM \"table\"";
match verified_stmt(sql) { match verified_stmt(sql) {
Statement::Delete { table_name, .. } => { Statement::Delete { from, .. } => {
assert_eq!( assert_eq!(
TableFactor::Table { TableFactor::Table {
name: ObjectName(vec![Ident::with_quote('"', "table")]), name: ObjectName(vec![Ident::with_quote('"', "table")]),
@ -393,7 +393,93 @@ fn parse_delete_statement() {
args: None, args: None,
with_hints: vec![], with_hints: vec![],
}, },
table_name from[0].relation
);
}
_ => unreachable!(),
}
}
#[test]
fn parse_delete_statement_for_multi_tables() {
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";
match verified_stmt(sql) {
Statement::Delete { tables, from, .. } => {
assert_eq!(
ObjectName(vec![Ident::new("schema1"), Ident::new("table1")]),
tables[0]
);
assert_eq!(
ObjectName(vec![Ident::new("schema2"), Ident::new("table2")]),
tables[1]
);
assert_eq!(
TableFactor::Table {
name: ObjectName(vec![Ident::new("schema1"), Ident::new("table1")]),
alias: None,
args: None,
with_hints: vec![],
},
from[0].relation
);
assert_eq!(
TableFactor::Table {
name: ObjectName(vec![Ident::new("schema2"), Ident::new("table2")]),
alias: None,
args: None,
with_hints: vec![],
},
from[0].joins[0].relation
);
}
_ => unreachable!(),
}
}
#[test]
fn parse_delete_statement_for_multi_tables_with_using() {
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";
match verified_stmt(sql) {
Statement::Delete {
from,
using: Some(using),
..
} => {
assert_eq!(
TableFactor::Table {
name: ObjectName(vec![Ident::new("schema1"), Ident::new("table1")]),
alias: None,
args: None,
with_hints: vec![],
},
from[0].relation
);
assert_eq!(
TableFactor::Table {
name: ObjectName(vec![Ident::new("schema2"), Ident::new("table2")]),
alias: None,
args: None,
with_hints: vec![],
},
from[1].relation
);
assert_eq!(
TableFactor::Table {
name: ObjectName(vec![Ident::new("schema1"), Ident::new("table1")]),
alias: None,
args: None,
with_hints: vec![],
},
using[0].relation
);
assert_eq!(
TableFactor::Table {
name: ObjectName(vec![Ident::new("schema2"), Ident::new("table2")]),
alias: None,
args: None,
with_hints: vec![],
},
using[0].joins[0].relation
); );
} }
_ => unreachable!(), _ => unreachable!(),
@ -407,7 +493,8 @@ fn parse_where_delete_statement() {
let sql = "DELETE FROM foo WHERE name = 5"; let sql = "DELETE FROM foo WHERE name = 5";
match verified_stmt(sql) { match verified_stmt(sql) {
Statement::Delete { Statement::Delete {
table_name, tables: _,
from,
using, using,
selection, selection,
returning, returning,
@ -419,7 +506,7 @@ fn parse_where_delete_statement() {
args: None, args: None,
with_hints: vec![], with_hints: vec![],
}, },
table_name, from[0].relation,
); );
assert_eq!(None, using); assert_eq!(None, using);
@ -444,7 +531,8 @@ fn parse_where_delete_with_alias_statement() {
let sql = "DELETE FROM basket AS a USING basket AS b WHERE a.id < b.id"; let sql = "DELETE FROM basket AS a USING basket AS b WHERE a.id < b.id";
match verified_stmt(sql) { match verified_stmt(sql) {
Statement::Delete { Statement::Delete {
table_name, tables: _,
from,
using, using,
selection, selection,
returning, returning,
@ -459,19 +547,21 @@ fn parse_where_delete_with_alias_statement() {
args: None, args: None,
with_hints: vec![], with_hints: vec![],
}, },
table_name, from[0].relation,
); );
assert_eq!( assert_eq!(
Some(TableFactor::Table { Some(vec![TableWithJoins {
name: ObjectName(vec![Ident::new("basket")]), relation: TableFactor::Table {
alias: Some(TableAlias { name: ObjectName(vec![Ident::new("basket")]),
name: Ident::new("b"), alias: Some(TableAlias {
columns: vec![], name: Ident::new("b"),
}), columns: vec![],
args: None, }),
with_hints: vec![], args: None,
}), with_hints: vec![],
},
joins: vec![],
}]),
using using
); );
assert_eq!( assert_eq!(