1#include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp"
2#include "duckdb/execution/operator/projection/physical_projection.hpp"
3#include "duckdb/execution/operator/filter/physical_filter.hpp"
4#include "duckdb/execution/operator/scan/physical_table_scan.hpp"
5#include "duckdb/execution/operator/schema/physical_create_index.hpp"
6#include "duckdb/execution/operator/order/physical_order.hpp"
7#include "duckdb/execution/physical_plan_generator.hpp"
8#include "duckdb/function/table/table_scan.hpp"
9#include "duckdb/planner/operator/logical_create_index.hpp"
10#include "duckdb/planner/expression/bound_operator_expression.hpp"
11#include "duckdb/planner/expression/bound_reference_expression.hpp"
12#include "duckdb/planner/table_filter.hpp"
13
14namespace duckdb {
15
16unique_ptr<PhysicalOperator> PhysicalPlanGenerator::CreatePlan(LogicalCreateIndex &op) {
17
18 // generate a physical plan for the parallel index creation which consists of the following operators
19 // table scan - projection (for expression execution) - filter (NOT NULL) - order - create index
20
21 D_ASSERT(op.children.empty());
22
23 // validate that all expressions contain valid scalar functions
24 // e.g. get_current_timestamp(), random(), and sequence values are not allowed as ART keys
25 // because they make deletions and lookups unfeasible
26 for (idx_t i = 0; i < op.unbound_expressions.size(); i++) {
27 auto &expr = op.unbound_expressions[i];
28 if (expr->HasSideEffects()) {
29 throw BinderException("Index keys cannot contain expressions with side "
30 "effects.");
31 }
32 }
33
34 // table scan operator for index key columns and row IDs
35
36 unique_ptr<TableFilterSet> table_filters;
37 op.info->column_ids.emplace_back(args: COLUMN_IDENTIFIER_ROW_ID);
38
39 auto &bind_data = op.bind_data->Cast<TableScanBindData>();
40 bind_data.is_create_index = true;
41
42 auto table_scan =
43 make_uniq<PhysicalTableScan>(args&: op.info->scan_types, args&: op.function, args: std::move(op.bind_data), args&: op.info->column_ids,
44 args&: op.info->names, args: std::move(table_filters), args&: op.estimated_cardinality);
45
46 dependencies.AddDependency(entry&: op.table);
47 op.info->column_ids.pop_back();
48
49 D_ASSERT(op.info->scan_types.size() - 1 <= op.info->names.size());
50 D_ASSERT(op.info->scan_types.size() - 1 <= op.info->column_ids.size());
51
52 // projection to execute expressions on the key columns
53
54 vector<LogicalType> new_column_types;
55 vector<unique_ptr<Expression>> select_list;
56 for (idx_t i = 0; i < op.expressions.size(); i++) {
57 new_column_types.push_back(x: op.expressions[i]->return_type);
58 select_list.push_back(x: std::move(op.expressions[i]));
59 }
60 new_column_types.emplace_back(args: LogicalType::ROW_TYPE);
61 select_list.push_back(x: make_uniq<BoundReferenceExpression>(args: LogicalType::ROW_TYPE, args: op.info->scan_types.size() - 1));
62
63 auto projection = make_uniq<PhysicalProjection>(args&: new_column_types, args: std::move(select_list), args&: op.estimated_cardinality);
64 projection->children.push_back(x: std::move(table_scan));
65
66 // filter operator for IS_NOT_NULL on each key column
67
68 vector<LogicalType> filter_types;
69 vector<unique_ptr<Expression>> filter_select_list;
70
71 for (idx_t i = 0; i < new_column_types.size() - 1; i++) {
72 filter_types.push_back(x: new_column_types[i]);
73 auto is_not_null_expr =
74 make_uniq<BoundOperatorExpression>(args: ExpressionType::OPERATOR_IS_NOT_NULL, args: LogicalType::BOOLEAN);
75 auto bound_ref = make_uniq<BoundReferenceExpression>(args&: new_column_types[i], args&: i);
76 is_not_null_expr->children.push_back(x: std::move(bound_ref));
77 filter_select_list.push_back(x: std::move(is_not_null_expr));
78 }
79
80 auto null_filter =
81 make_uniq<PhysicalFilter>(args: std::move(filter_types), args: std::move(filter_select_list), args&: op.estimated_cardinality);
82 null_filter->types.emplace_back(args: LogicalType::ROW_TYPE);
83 null_filter->children.push_back(x: std::move(projection));
84
85 // order operator
86
87 vector<BoundOrderByNode> orders;
88 vector<idx_t> projections;
89 for (idx_t i = 0; i < new_column_types.size() - 1; i++) {
90 auto col_expr = make_uniq_base<Expression, BoundReferenceExpression>(args&: new_column_types[i], args&: i);
91 orders.emplace_back(args: OrderType::ASCENDING, args: OrderByNullType::NULLS_FIRST, args: std::move(col_expr));
92 projections.emplace_back(args&: i);
93 }
94 projections.emplace_back(args: new_column_types.size() - 1);
95
96 auto physical_order =
97 make_uniq<PhysicalOrder>(args&: new_column_types, args: std::move(orders), args: std::move(projections), args&: op.estimated_cardinality);
98 physical_order->children.push_back(x: std::move(null_filter));
99
100 // actual physical create index operator
101
102 auto physical_create_index =
103 make_uniq<PhysicalCreateIndex>(args&: op, args&: op.table, args&: op.info->column_ids, args: std::move(op.info),
104 args: std::move(op.unbound_expressions), args&: op.estimated_cardinality);
105 physical_create_index->children.push_back(x: std::move(physical_order));
106 return std::move(physical_create_index);
107}
108
109} // namespace duckdb
110