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 | |
14 | namespace duckdb { |
15 | |
16 | unique_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 | |