| 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 | |