Skip to content

Commit 2c54cde

Browse files
authored
feat: support limited deletion (#20137)
## Rationale for this change We support `DELETE LIMIT` query and it would be good to port our fork patch to the upstream. <!-- Why are you proposing this change? If this is already explained clearly in the issue then this section is not needed. Explaining clearly why changes are proposed helps reviewers understand your changes and offer better suggestions for fixes. --> ## What changes are included in this PR? This patch adds a support for delete statement with limit planning. An inner table scan is wrapped with a limit in this case. e.g.: ``` query TT explain delete from t1 limit 10 ---- logical_plan 01)Dml: op=[Delete] table=[t1] 02)--Limit: skip=0, fetch=10 03)----TableScan: t1 physical_plan 01)CooperativeExec 02)--DmlResultExec: rows_affected=0 ``` ## Are these changes tested? Covered with SLT. ## Are there any user-facing changes? Now queries with limited deletion are successfully planned, instead of returning not-supported error.
1 parent aeeb0ba commit 2c54cde

2 files changed

Lines changed: 39 additions & 7 deletions

File tree

datafusion/sql/src/statement.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,12 +1108,8 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
11081108
plan_err!("Delete-order-by clause not yet supported")?;
11091109
}
11101110

1111-
if limit.is_some() {
1112-
plan_err!("Delete-limit clause not yet supported")?;
1113-
}
1114-
11151111
let table_name = self.get_delete_target(from)?;
1116-
self.delete_to_plan(&table_name, selection)
1112+
self.delete_to_plan(&table_name, selection, limit)
11171113
}
11181114

11191115
Statement::StartTransaction {
@@ -1322,7 +1318,7 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
13221318
let function_body = match function_body {
13231319
Some(r) => Some(self.sql_to_expr(
13241320
match r {
1325-
// `link_symbol` indicates if the primary expression contains the name of shared library file.
1321+
// `link_symbol` indicates if the primary expression contains the name of shared library file.
13261322
ast::CreateFunctionBody::AsBeforeOptions{body: expr, link_symbol: _link_symbol} => expr,
13271323
ast::CreateFunctionBody::AsAfterOptions(expr) => expr,
13281324
ast::CreateFunctionBody::Return(expr) => expr,
@@ -2072,6 +2068,7 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
20722068
&self,
20732069
table_name: &ObjectName,
20742070
predicate_expr: Option<SQLExpr>,
2071+
limit: Option<SQLExpr>,
20752072
) -> Result<LogicalPlan> {
20762073
// Do a table lookup to verify the table exists
20772074
let table_ref = self.object_name_to_table_reference(table_name.clone())?;
@@ -2085,7 +2082,7 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
20852082
.build()?;
20862083
let mut planner_context = PlannerContext::new();
20872084

2088-
let source = match predicate_expr {
2085+
let mut source = match predicate_expr {
20892086
None => scan,
20902087
Some(predicate_expr) => {
20912088
let filter_expr =
@@ -2102,6 +2099,14 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
21022099
}
21032100
};
21042101

2102+
if let Some(limit) = limit {
2103+
let empty_schema = DFSchema::empty();
2104+
let limit = self.sql_to_expr(limit, &empty_schema, &mut planner_context)?;
2105+
source = LogicalPlanBuilder::from(source)
2106+
.limit_by_expr(None, Some(limit))?
2107+
.build()?
2108+
}
2109+
21052110
let plan = LogicalPlan::Dml(DmlStatement::new(
21062111
table_ref,
21072112
table_source,

datafusion/sqllogictest/test_files/delete.slt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,30 @@ logical_plan
113113
05)--------TableScan: t2
114114
06)----TableScan: t1
115115
physical_plan_error This feature is not implemented: Physical plan does not support logical expression InSubquery(InSubquery { expr: Column(Column { relation: Some(Bare { table: "t1" }), name: "a" }), subquery: <subquery>, negated: false })
116+
117+
118+
# Delete with limit
119+
120+
query TT
121+
explain delete from t1 limit 10
122+
----
123+
logical_plan
124+
01)Dml: op=[Delete] table=[t1]
125+
02)--Limit: skip=0, fetch=10
126+
03)----TableScan: t1
127+
physical_plan
128+
01)CooperativeExec
129+
02)--DmlResultExec: rows_affected=0
130+
131+
132+
query TT
133+
explain delete from t1 where a = 1 and b = '2' limit 10
134+
----
135+
logical_plan
136+
01)Dml: op=[Delete] table=[t1]
137+
02)--Limit: skip=0, fetch=10
138+
03)----Filter: CAST(t1.a AS Int64) = Int64(1) AND t1.b = CAST(Utf8("2") AS Utf8View)
139+
04)------TableScan: t1
140+
physical_plan
141+
01)CooperativeExec
142+
02)--DmlResultExec: rows_affected=0

0 commit comments

Comments
 (0)