| 1 | #include "duckdb/planner/operator/logical_get.hpp" |
| 2 | |
| 3 | #include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp" |
| 4 | #include "duckdb/catalog/catalog_entry/table_function_catalog_entry.hpp" |
| 5 | #include "duckdb/common/field_writer.hpp" |
| 6 | #include "duckdb/common/string_util.hpp" |
| 7 | #include "duckdb/function/function_serialization.hpp" |
| 8 | #include "duckdb/function/table/table_scan.hpp" |
| 9 | #include "duckdb/main/config.hpp" |
| 10 | #include "duckdb/storage/data_table.hpp" |
| 11 | |
| 12 | namespace duckdb { |
| 13 | |
| 14 | LogicalGet::LogicalGet(idx_t table_index, TableFunction function, unique_ptr<FunctionData> bind_data, |
| 15 | vector<LogicalType> returned_types, vector<string> returned_names) |
| 16 | : LogicalOperator(LogicalOperatorType::LOGICAL_GET), table_index(table_index), function(std::move(function)), |
| 17 | bind_data(std::move(bind_data)), returned_types(std::move(returned_types)), names(std::move(returned_names)) { |
| 18 | } |
| 19 | |
| 20 | optional_ptr<TableCatalogEntry> LogicalGet::GetTable() const { |
| 21 | return TableScanFunction::GetTableEntry(function, bind_data: bind_data.get()); |
| 22 | } |
| 23 | |
| 24 | string LogicalGet::ParamsToString() const { |
| 25 | string result; |
| 26 | for (auto &kv : table_filters.filters) { |
| 27 | auto &column_index = kv.first; |
| 28 | auto &filter = kv.second; |
| 29 | if (column_index < names.size()) { |
| 30 | result += filter->ToString(column_name: names[column_index]); |
| 31 | } |
| 32 | result += "\n" ; |
| 33 | } |
| 34 | if (!function.to_string) { |
| 35 | return string(); |
| 36 | } |
| 37 | return function.to_string(bind_data.get()); |
| 38 | } |
| 39 | |
| 40 | vector<ColumnBinding> LogicalGet::GetColumnBindings() { |
| 41 | if (column_ids.empty()) { |
| 42 | return {ColumnBinding(table_index, 0)}; |
| 43 | } |
| 44 | vector<ColumnBinding> result; |
| 45 | if (projection_ids.empty()) { |
| 46 | for (idx_t col_idx = 0; col_idx < column_ids.size(); col_idx++) { |
| 47 | result.emplace_back(args&: table_index, args&: col_idx); |
| 48 | } |
| 49 | } else { |
| 50 | for (auto proj_id : projection_ids) { |
| 51 | result.emplace_back(args&: table_index, args&: proj_id); |
| 52 | } |
| 53 | } |
| 54 | if (!projected_input.empty()) { |
| 55 | if (children.size() != 1) { |
| 56 | throw InternalException("LogicalGet::project_input can only be set for table-in-out functions" ); |
| 57 | } |
| 58 | auto child_bindings = children[0]->GetColumnBindings(); |
| 59 | for (auto entry : projected_input) { |
| 60 | D_ASSERT(entry < child_bindings.size()); |
| 61 | result.emplace_back(args&: child_bindings[entry]); |
| 62 | } |
| 63 | } |
| 64 | return result; |
| 65 | } |
| 66 | |
| 67 | void LogicalGet::ResolveTypes() { |
| 68 | if (column_ids.empty()) { |
| 69 | column_ids.push_back(x: COLUMN_IDENTIFIER_ROW_ID); |
| 70 | } |
| 71 | |
| 72 | if (projection_ids.empty()) { |
| 73 | for (auto &index : column_ids) { |
| 74 | if (index == COLUMN_IDENTIFIER_ROW_ID) { |
| 75 | types.emplace_back(args: LogicalType::ROW_TYPE); |
| 76 | } else { |
| 77 | types.push_back(x: returned_types[index]); |
| 78 | } |
| 79 | } |
| 80 | } else { |
| 81 | for (auto &proj_index : projection_ids) { |
| 82 | auto &index = column_ids[proj_index]; |
| 83 | if (index == COLUMN_IDENTIFIER_ROW_ID) { |
| 84 | types.emplace_back(args: LogicalType::ROW_TYPE); |
| 85 | } else { |
| 86 | types.push_back(x: returned_types[index]); |
| 87 | } |
| 88 | } |
| 89 | } |
| 90 | if (!projected_input.empty()) { |
| 91 | if (children.size() != 1) { |
| 92 | throw InternalException("LogicalGet::project_input can only be set for table-in-out functions" ); |
| 93 | } |
| 94 | for (auto entry : projected_input) { |
| 95 | D_ASSERT(entry < children[0]->types.size()); |
| 96 | types.push_back(x: children[0]->types[entry]); |
| 97 | } |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | idx_t LogicalGet::EstimateCardinality(ClientContext &context) { |
| 102 | if (function.cardinality) { |
| 103 | auto node_stats = function.cardinality(context, bind_data.get()); |
| 104 | if (node_stats && node_stats->has_estimated_cardinality) { |
| 105 | return node_stats->estimated_cardinality; |
| 106 | } |
| 107 | } |
| 108 | return 1; |
| 109 | } |
| 110 | |
| 111 | void LogicalGet::Serialize(FieldWriter &writer) const { |
| 112 | writer.WriteField(element: table_index); |
| 113 | writer.WriteRegularSerializableList(elements: returned_types); |
| 114 | writer.WriteList<string>(elements: names); |
| 115 | writer.WriteList<column_t>(elements: column_ids); |
| 116 | writer.WriteList<column_t>(elements: projection_ids); |
| 117 | writer.WriteSerializable(element: table_filters); |
| 118 | |
| 119 | FunctionSerializer::SerializeBase<TableFunction>(writer, function, bind_info: bind_data.get()); |
| 120 | if (!function.serialize) { |
| 121 | D_ASSERT(!function.deserialize); |
| 122 | // no serialize method: serialize input values and named_parameters for rebinding purposes |
| 123 | writer.WriteRegularSerializableList(elements: parameters); |
| 124 | writer.WriteField<idx_t>(element: named_parameters.size()); |
| 125 | for (auto &pair : named_parameters) { |
| 126 | writer.WriteString(val: pair.first); |
| 127 | writer.WriteSerializable(element: pair.second); |
| 128 | } |
| 129 | writer.WriteRegularSerializableList(elements: input_table_types); |
| 130 | writer.WriteList<string>(elements: input_table_names); |
| 131 | } |
| 132 | writer.WriteList<column_t>(elements: projected_input); |
| 133 | } |
| 134 | |
| 135 | unique_ptr<LogicalOperator> LogicalGet::Deserialize(LogicalDeserializationState &state, FieldReader &reader) { |
| 136 | auto table_index = reader.ReadRequired<idx_t>(); |
| 137 | auto returned_types = reader.ReadRequiredSerializableList<LogicalType, LogicalType>(); |
| 138 | auto returned_names = reader.ReadRequiredList<string>(); |
| 139 | auto column_ids = reader.ReadRequiredList<column_t>(); |
| 140 | auto projection_ids = reader.ReadRequiredList<column_t>(); |
| 141 | auto table_filters = reader.ReadRequiredSerializable<TableFilterSet>(); |
| 142 | |
| 143 | unique_ptr<FunctionData> bind_data; |
| 144 | bool has_deserialize; |
| 145 | auto function = FunctionSerializer::DeserializeBaseInternal<TableFunction, TableFunctionCatalogEntry>( |
| 146 | reader, state&: state.gstate, type: CatalogType::TABLE_FUNCTION_ENTRY, bind_info&: bind_data, has_deserialize); |
| 147 | |
| 148 | vector<Value> parameters; |
| 149 | named_parameter_map_t named_parameters; |
| 150 | vector<LogicalType> input_table_types; |
| 151 | vector<string> input_table_names; |
| 152 | if (!has_deserialize) { |
| 153 | D_ASSERT(!bind_data); |
| 154 | parameters = reader.ReadRequiredSerializableList<Value, Value>(); |
| 155 | |
| 156 | auto named_parameters_size = reader.ReadRequired<idx_t>(); |
| 157 | for (idx_t i = 0; i < named_parameters_size; i++) { |
| 158 | auto first = reader.ReadRequired<string>(); |
| 159 | auto second = reader.ReadRequiredSerializable<Value, Value>(); |
| 160 | auto pair = make_pair(x&: first, y&: second); |
| 161 | named_parameters.insert(x&: pair); |
| 162 | } |
| 163 | |
| 164 | input_table_types = reader.ReadRequiredSerializableList<LogicalType, LogicalType>(); |
| 165 | input_table_names = reader.ReadRequiredList<string>(); |
| 166 | TableFunctionBindInput input(parameters, named_parameters, input_table_types, input_table_names, |
| 167 | function.function_info.get()); |
| 168 | |
| 169 | vector<LogicalType> bind_return_types; |
| 170 | vector<string> bind_names; |
| 171 | bind_data = function.bind(state.gstate.context, input, bind_return_types, bind_names); |
| 172 | if (returned_types != bind_return_types) { |
| 173 | throw SerializationException( |
| 174 | "Table function deserialization failure - bind returned different return types than were serialized" ); |
| 175 | } |
| 176 | // names can actually be different because of aliases - only the sizes cannot be different |
| 177 | if (returned_names.size() != bind_names.size()) { |
| 178 | throw SerializationException( |
| 179 | "Table function deserialization failure - bind returned different returned names than were serialized" ); |
| 180 | } |
| 181 | } |
| 182 | vector<column_t> projected_input; |
| 183 | reader.ReadList<column_t>(result&: projected_input); |
| 184 | |
| 185 | auto result = make_uniq<LogicalGet>(args&: table_index, args&: function, args: std::move(bind_data), args&: returned_types, args&: returned_names); |
| 186 | result->column_ids = std::move(column_ids); |
| 187 | result->projection_ids = std::move(projection_ids); |
| 188 | result->table_filters = std::move(*table_filters); |
| 189 | result->parameters = std::move(parameters); |
| 190 | result->named_parameters = std::move(named_parameters); |
| 191 | result->input_table_types = input_table_types; |
| 192 | result->input_table_names = input_table_names; |
| 193 | result->projected_input = std::move(projected_input); |
| 194 | return std::move(result); |
| 195 | } |
| 196 | |
| 197 | vector<idx_t> LogicalGet::GetTableIndex() const { |
| 198 | return vector<idx_t> {table_index}; |
| 199 | } |
| 200 | |
| 201 | string LogicalGet::GetName() const { |
| 202 | #ifdef DEBUG |
| 203 | if (DBConfigOptions::debug_print_bindings) { |
| 204 | return StringUtil::Upper(function.name) + StringUtil::Format(" #%llu" , table_index); |
| 205 | } |
| 206 | #endif |
| 207 | return StringUtil::Upper(str: function.name); |
| 208 | } |
| 209 | |
| 210 | } // namespace duckdb |
| 211 | |