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
13namespace duckdb {
14
15BoundStatement 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