risingwave_frontend/planner/
delete.rs1use fixedbitset::FixedBitSet;
16
17use super::Planner;
18use crate::binder::BoundDelete;
19use crate::error::Result;
20use crate::optimizer::plan_node::{LogicalDelete, LogicalProject, generic};
21use crate::optimizer::property::{Order, RequiredDist};
22use crate::optimizer::{PlanRef, PlanRoot};
23
24impl Planner {
25 pub(super) fn plan_delete(&mut self, delete: BoundDelete) -> Result<PlanRoot> {
26 let scan = self.plan_base_table(&delete.table)?;
27 let input = if let Some(expr) = delete.selection {
28 self.plan_where(scan, expr)?
29 } else {
30 scan
31 };
32 let input = if delete.table.table_catalog.has_generated_column()
33 || delete.table.table_catalog.has_rw_timestamp_column()
34 {
35 LogicalProject::with_out_col_idx(
36 input,
37 delete
38 .table
39 .table_catalog
40 .columns()
41 .iter()
42 .enumerate()
43 .filter_map(|(i, c)| (c.can_dml()).then_some(i)),
44 )
45 .into()
46 } else {
47 input
48 };
49 let returning = !delete.returning_list.is_empty();
50 let mut plan: PlanRef = LogicalDelete::from(generic::Delete::new(
51 input,
52 delete.table_name.clone(),
53 delete.table_id,
54 delete.table_version_id,
55 returning,
56 ))
57 .into();
58
59 if returning {
60 plan = LogicalProject::create(plan, delete.returning_list);
61 }
62
63 let dist = RequiredDist::Any;
65 let mut out_fields = FixedBitSet::with_capacity(plan.schema().len());
66 out_fields.insert_range(..);
67 let out_names = if returning {
68 delete.returning_schema.expect("If returning list is not empty, should provide returning schema in BoundDelete.").names()
69 } else {
70 plan.schema().names()
71 };
72
73 let root = PlanRoot::new_with_logical_plan(plan, dist, Order::any(), out_fields, out_names);
74 Ok(root)
75 }
76}