| 1 | #include "duckdb/execution/operator/projection/physical_projection.hpp" |
| 2 | #include "duckdb/execution/operator/projection/physical_tableinout_function.hpp" |
| 3 | #include "duckdb/execution/operator/scan/physical_table_scan.hpp" |
| 4 | #include "duckdb/execution/physical_plan_generator.hpp" |
| 5 | #include "duckdb/function/table/table_scan.hpp" |
| 6 | #include "duckdb/planner/expression/bound_constant_expression.hpp" |
| 7 | #include "duckdb/planner/expression/bound_reference_expression.hpp" |
| 8 | #include "duckdb/planner/operator/logical_get.hpp" |
| 9 | |
| 10 | namespace duckdb { |
| 11 | |
| 12 | unique_ptr<TableFilterSet> CreateTableFilterSet(TableFilterSet &table_filters, vector<column_t> &column_ids) { |
| 13 | // create the table filter map |
| 14 | auto table_filter_set = make_uniq<TableFilterSet>(); |
| 15 | for (auto &table_filter : table_filters.filters) { |
| 16 | // find the relative column index from the absolute column index into the table |
| 17 | idx_t column_index = DConstants::INVALID_INDEX; |
| 18 | for (idx_t i = 0; i < column_ids.size(); i++) { |
| 19 | if (table_filter.first == column_ids[i]) { |
| 20 | column_index = i; |
| 21 | break; |
| 22 | } |
| 23 | } |
| 24 | if (column_index == DConstants::INVALID_INDEX) { |
| 25 | throw InternalException("Could not find column index for table filter" ); |
| 26 | } |
| 27 | table_filter_set->filters[column_index] = std::move(table_filter.second); |
| 28 | } |
| 29 | return table_filter_set; |
| 30 | } |
| 31 | |
| 32 | unique_ptr<PhysicalOperator> PhysicalPlanGenerator::CreatePlan(LogicalGet &op) { |
| 33 | if (!op.children.empty()) { |
| 34 | // this is for table producing functions that consume subquery results |
| 35 | D_ASSERT(op.children.size() == 1); |
| 36 | auto node = make_uniq<PhysicalTableInOutFunction>(args&: op.types, args&: op.function, args: std::move(op.bind_data), args&: op.column_ids, |
| 37 | args&: op.estimated_cardinality, args: std::move(op.projected_input)); |
| 38 | node->children.push_back(x: CreatePlan(logical: std::move(op.children[0]))); |
| 39 | return std::move(node); |
| 40 | } |
| 41 | if (!op.projected_input.empty()) { |
| 42 | throw InternalException("LogicalGet::project_input can only be set for table-in-out functions" ); |
| 43 | } |
| 44 | |
| 45 | unique_ptr<TableFilterSet> table_filters; |
| 46 | if (!op.table_filters.filters.empty()) { |
| 47 | table_filters = CreateTableFilterSet(table_filters&: op.table_filters, column_ids&: op.column_ids); |
| 48 | } |
| 49 | |
| 50 | if (op.function.dependency) { |
| 51 | op.function.dependency(dependencies, op.bind_data.get()); |
| 52 | } |
| 53 | // create the table scan node |
| 54 | if (!op.function.projection_pushdown) { |
| 55 | // function does not support projection pushdown |
| 56 | auto node = make_uniq<PhysicalTableScan>(args&: op.returned_types, args&: op.function, args: std::move(op.bind_data), |
| 57 | args&: op.returned_types, args&: op.column_ids, args: vector<column_t>(), args&: op.names, |
| 58 | args: std::move(table_filters), args&: op.estimated_cardinality); |
| 59 | // first check if an additional projection is necessary |
| 60 | if (op.column_ids.size() == op.returned_types.size()) { |
| 61 | bool projection_necessary = false; |
| 62 | for (idx_t i = 0; i < op.column_ids.size(); i++) { |
| 63 | if (op.column_ids[i] != i) { |
| 64 | projection_necessary = true; |
| 65 | break; |
| 66 | } |
| 67 | } |
| 68 | if (!projection_necessary) { |
| 69 | // a projection is not necessary if all columns have been requested in-order |
| 70 | // in that case we just return the node |
| 71 | |
| 72 | return std::move(node); |
| 73 | } |
| 74 | } |
| 75 | // push a projection on top that does the projection |
| 76 | vector<LogicalType> types; |
| 77 | vector<unique_ptr<Expression>> expressions; |
| 78 | for (auto &column_id : op.column_ids) { |
| 79 | if (column_id == COLUMN_IDENTIFIER_ROW_ID) { |
| 80 | types.emplace_back(args: LogicalType::BIGINT); |
| 81 | expressions.push_back(x: make_uniq<BoundConstantExpression>(args: Value::BIGINT(value: 0))); |
| 82 | } else { |
| 83 | auto type = op.returned_types[column_id]; |
| 84 | types.push_back(x: type); |
| 85 | expressions.push_back(x: make_uniq<BoundReferenceExpression>(args&: type, args&: column_id)); |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | auto projection = |
| 90 | make_uniq<PhysicalProjection>(args: std::move(types), args: std::move(expressions), args&: op.estimated_cardinality); |
| 91 | projection->children.push_back(x: std::move(node)); |
| 92 | return std::move(projection); |
| 93 | } else { |
| 94 | return make_uniq<PhysicalTableScan>(args&: op.types, args&: op.function, args: std::move(op.bind_data), args&: op.returned_types, |
| 95 | args&: op.column_ids, args&: op.projection_ids, args&: op.names, args: std::move(table_filters), |
| 96 | args&: op.estimated_cardinality); |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | } // namespace duckdb |
| 101 | |