Skip to content

Commit ab8b872

Browse files
committed
add support for with clauses in delete statements
fixes apache#1763
1 parent 6ec5223 commit ab8b872

File tree

4 files changed

+64
-6
lines changed

4 files changed

+64
-6
lines changed

src/ast/query.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ pub enum SetExpr {
156156
Values(Values),
157157
Insert(Statement),
158158
Update(Statement),
159+
Delete(Statement),
159160
Table(Box<Table>),
160161
}
161162

@@ -178,6 +179,7 @@ impl fmt::Display for SetExpr {
178179
SetExpr::Values(v) => write!(f, "{v}"),
179180
SetExpr::Insert(v) => write!(f, "{v}"),
180181
SetExpr::Update(v) => write!(f, "{v}"),
182+
SetExpr::Delete(v) => write!(f, "{v}"),
181183
SetExpr::Table(t) => write!(f, "{t}"),
182184
SetExpr::SetOperation {
183185
left,

src/ast/spans.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ impl Spanned for SetExpr {
191191
SetExpr::Insert(statement) => statement.span(),
192192
SetExpr::Table(_) => Span::empty(),
193193
SetExpr::Update(statement) => statement.span(),
194+
SetExpr::Delete(statement) => statement.span(),
194195
}
195196
}
196197
}

src/parser/mod.rs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10049,6 +10049,13 @@ impl<'a> Parser<'a> {
1004910049
Ok(parent_type(inside_type.into()))
1005010050
}
1005110051

10052+
/// Parse a DELETE statement, returning a `Box`ed SetExpr
10053+
///
10054+
/// This is used to reduce the size of the stack frames in debug builds
10055+
fn parse_delete_setexpr_boxed(&mut self) -> Result<Box<SetExpr>, ParserError> {
10056+
Ok(Box::new(SetExpr::Delete(self.parse_delete()?)))
10057+
}
10058+
1005210059
pub fn parse_delete(&mut self) -> Result<Statement, ParserError> {
1005310060
let (tables, with_from_keyword) = if !self.parse_keyword(Keyword::FROM) {
1005410061
// `FROM` keyword is optional in BigQuery SQL.
@@ -10202,19 +10209,25 @@ impl<'a> Parser<'a> {
1020210209
}
1020310210
}
1020410211

10212+
/// Parse a `WITH` clause, i.e. a `WITH` keyword followed by a `RECURSIVE` keyword
10213+
/// and a comma-separated list of CTE declarations.
10214+
fn parse_with_clause(&mut self) -> Result<With, ParserError> {
10215+
let with_token = self.get_current_token();
10216+
Ok(With {
10217+
with_token: with_token.clone().into(),
10218+
recursive: self.parse_keyword(Keyword::RECURSIVE),
10219+
cte_tables: self.parse_comma_separated(Parser::parse_cte)?,
10220+
})
10221+
}
10222+
1020510223
/// Parse a query expression, i.e. a `SELECT` statement optionally
1020610224
/// preceded with some `WITH` CTE declarations and optionally followed
1020710225
/// by `ORDER BY`. Unlike some other parse_... methods, this one doesn't
1020810226
/// expect the initial keyword to be already consumed
1020910227
pub fn parse_query(&mut self) -> Result<Box<Query>, ParserError> {
1021010228
let _guard = self.recursion_counter.try_decrease()?;
1021110229
let with = if self.parse_keyword(Keyword::WITH) {
10212-
let with_token = self.get_current_token();
10213-
Some(With {
10214-
with_token: with_token.clone().into(),
10215-
recursive: self.parse_keyword(Keyword::RECURSIVE),
10216-
cte_tables: self.parse_comma_separated(Parser::parse_cte)?,
10217-
})
10230+
Some(self.parse_with_clause()?)
1021810231
} else {
1021910232
None
1022010233
};
@@ -10248,6 +10261,21 @@ impl<'a> Parser<'a> {
1024810261
format_clause: None,
1024910262
}
1025010263
.into())
10264+
} else if self.parse_keyword(Keyword::DELETE) {
10265+
Ok(Query {
10266+
with,
10267+
body: self.parse_delete_setexpr_boxed()?,
10268+
limit: None,
10269+
limit_by: vec![],
10270+
order_by: None,
10271+
offset: None,
10272+
fetch: None,
10273+
locks: vec![],
10274+
for_clause: None,
10275+
settings: None,
10276+
format_clause: None,
10277+
}
10278+
.into())
1025110279
} else {
1025210280
let body = self.parse_query_body(self.dialect.prec_unknown())?;
1025310281

tests/sqlparser_common.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7383,6 +7383,33 @@ fn parse_recursive_cte() {
73837383
assert_eq!(with.cte_tables.first().unwrap(), &expected);
73847384
}
73857385

7386+
#[test]
7387+
fn parse_cte_in_data_modification_statements() {
7388+
match verified_stmt("WITH x AS (SELECT 1) UPDATE t SET bar = (SELECT * FROM x)") {
7389+
Statement::Query(query) => {
7390+
assert_eq!(query.with.unwrap().to_string(), "WITH x AS (SELECT 1)");
7391+
assert!(matches!(*query.body, SetExpr::Update(_)));
7392+
}
7393+
other => panic!("Expected: UPDATE, got: {:?}", other),
7394+
}
7395+
7396+
match verified_stmt("WITH t (x) AS (SELECT 9) DELETE FROM q WHERE id IN (SELECT x FROM t)") {
7397+
Statement::Query(query) => {
7398+
assert_eq!(query.with.unwrap().to_string(), "WITH t (x) AS (SELECT 9)");
7399+
assert!(matches!(*query.body, SetExpr::Delete(_)));
7400+
}
7401+
other => panic!("Expected: DELETE, got: {:?}", other),
7402+
}
7403+
7404+
match verified_stmt("WITH x AS (SELECT 42) INSERT INTO t SELECT foo FROM x") {
7405+
Statement::Query(query) => {
7406+
assert_eq!(query.with.unwrap().to_string(), "WITH x AS (SELECT 42)");
7407+
assert!(matches!(*query.body, SetExpr::Insert(_)));
7408+
}
7409+
other => panic!("Expected: INSERT, got: {:?}", other),
7410+
}
7411+
}
7412+
73867413
#[test]
73877414
fn parse_derived_tables() {
73887415
let sql = "SELECT a.x, b.y FROM (SELECT x FROM foo) AS a CROSS JOIN (SELECT y FROM bar) AS b";

0 commit comments

Comments
 (0)