1 | #include "duckdb/parser/statement/delete_statement.hpp" |
2 | #include "duckdb/planner/binder.hpp" |
3 | #include "duckdb/planner/expression_binder/where_binder.hpp" |
4 | #include "duckdb/planner/expression_binder/returning_binder.hpp" |
5 | #include "duckdb/planner/operator/logical_delete.hpp" |
6 | #include "duckdb/planner/operator/logical_filter.hpp" |
7 | #include "duckdb/planner/operator/logical_get.hpp" |
8 | #include "duckdb/planner/bound_tableref.hpp" |
9 | #include "duckdb/planner/tableref/bound_basetableref.hpp" |
10 | #include "duckdb/planner/operator/logical_cross_product.hpp" |
11 | #include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp" |
12 | |
13 | namespace duckdb { |
14 | |
15 | BoundStatement Binder::Bind(DeleteStatement &stmt) { |
16 | BoundStatement result; |
17 | |
18 | // visit the table reference |
19 | auto bound_table = Bind(ref&: *stmt.table); |
20 | if (bound_table->type != TableReferenceType::BASE_TABLE) { |
21 | throw BinderException("Can only delete from base table!" ); |
22 | } |
23 | auto &table_binding = bound_table->Cast<BoundBaseTableRef>(); |
24 | auto &table = table_binding.table; |
25 | |
26 | auto root = CreatePlan(ref&: *bound_table); |
27 | auto &get = root->Cast<LogicalGet>(); |
28 | D_ASSERT(root->type == LogicalOperatorType::LOGICAL_GET); |
29 | |
30 | if (!table.temporary) { |
31 | // delete from persistent table: not read only! |
32 | properties.modified_databases.insert(x: table.catalog.GetName()); |
33 | } |
34 | |
35 | // Add CTEs as bindable |
36 | AddCTEMap(cte_map&: stmt.cte_map); |
37 | |
38 | // plan any tables from the various using clauses |
39 | if (!stmt.using_clauses.empty()) { |
40 | unique_ptr<LogicalOperator> child_operator; |
41 | for (auto &using_clause : stmt.using_clauses) { |
42 | // bind the using clause |
43 | auto using_binder = Binder::CreateBinder(context, parent: this); |
44 | auto bound_node = using_binder->Bind(ref&: *using_clause); |
45 | auto op = CreatePlan(ref&: *bound_node); |
46 | if (child_operator) { |
47 | // already bound a child: create a cross product to unify the two |
48 | child_operator = LogicalCrossProduct::Create(left: std::move(child_operator), right: std::move(op)); |
49 | } else { |
50 | child_operator = std::move(op); |
51 | } |
52 | bind_context.AddContext(other: std::move(using_binder->bind_context)); |
53 | } |
54 | if (child_operator) { |
55 | root = LogicalCrossProduct::Create(left: std::move(root), right: std::move(child_operator)); |
56 | } |
57 | } |
58 | |
59 | // project any additional columns required for the condition |
60 | unique_ptr<Expression> condition; |
61 | if (stmt.condition) { |
62 | WhereBinder binder(*this, context); |
63 | condition = binder.Bind(expr&: stmt.condition); |
64 | |
65 | PlanSubqueries(expr&: condition, root); |
66 | auto filter = make_uniq<LogicalFilter>(args: std::move(condition)); |
67 | filter->AddChild(child: std::move(root)); |
68 | root = std::move(filter); |
69 | } |
70 | // create the delete node |
71 | auto del = make_uniq<LogicalDelete>(args&: table, args: GenerateTableIndex()); |
72 | del->AddChild(child: std::move(root)); |
73 | |
74 | // set up the delete expression |
75 | del->expressions.push_back(x: make_uniq<BoundColumnRefExpression>( |
76 | args: LogicalType::ROW_TYPE, args: ColumnBinding(get.table_index, get.column_ids.size()))); |
77 | get.column_ids.push_back(x: COLUMN_IDENTIFIER_ROW_ID); |
78 | |
79 | if (!stmt.returning_list.empty()) { |
80 | del->return_chunk = true; |
81 | |
82 | auto update_table_index = GenerateTableIndex(); |
83 | del->table_index = update_table_index; |
84 | |
85 | unique_ptr<LogicalOperator> del_as_logicaloperator = std::move(del); |
86 | return BindReturning(returning_list: std::move(stmt.returning_list), table, alias: stmt.table->alias, update_table_index, |
87 | child_operator: std::move(del_as_logicaloperator), result: std::move(result)); |
88 | } |
89 | result.plan = std::move(del); |
90 | result.names = {"Count" }; |
91 | result.types = {LogicalType::BIGINT}; |
92 | properties.allow_stream_result = false; |
93 | properties.return_type = StatementReturnType::CHANGED_ROWS; |
94 | |
95 | return result; |
96 | } |
97 | |
98 | } // namespace duckdb |
99 | |