|
| 1 | +/* |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one |
| 3 | + * or more contributor license agreements. See the NOTICE file |
| 4 | + * distributed with this work for additional information |
| 5 | + * regarding copyright ownership. The ASF licenses this file |
| 6 | + * to you under the Apache License, Version 2.0 (the |
| 7 | + * "License"); you may not use this file except in compliance |
| 8 | + * with the License. You may obtain a copy of the License at |
| 9 | + * |
| 10 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | + * |
| 12 | + * Unless required by applicable law or agreed to in writing, |
| 13 | + * software distributed under the License is distributed on an |
| 14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 15 | + * KIND, either express or implied. See the License for the |
| 16 | + * specific language governing permissions and limitations |
| 17 | + * under the License. |
| 18 | + */ |
| 19 | + |
| 20 | +#include "paimon/core/utils/field_mapping.h" |
| 21 | + |
| 22 | +#include <algorithm> |
| 23 | +#include <cassert> |
| 24 | +#include <cstddef> |
| 25 | +#include <set> |
| 26 | + |
| 27 | +#include "arrow/type.h" |
| 28 | +#include "fmt/format.h" |
| 29 | +#include "paimon/common/predicate/compound_predicate_impl.h" |
| 30 | +#include "paimon/common/predicate/leaf_predicate_impl.h" |
| 31 | +#include "paimon/common/utils/field_type_utils.h" |
| 32 | +#include "paimon/common/utils/object_utils.h" |
| 33 | +#include "paimon/core/casting/cast_executor_factory.h" |
| 34 | +#include "paimon/core/casting/casting_utils.h" |
| 35 | +#include "paimon/defs.h" |
| 36 | +#include "paimon/predicate/literal.h" |
| 37 | +#include "paimon/predicate/predicate_builder.h" |
| 38 | +#include "paimon/predicate/predicate_utils.h" |
| 39 | +#include "paimon/status.h" |
| 40 | + |
| 41 | +namespace paimon { |
| 42 | + |
| 43 | +Result<std::shared_ptr<arrow::Schema>> FieldMapping::GetPartitionSchema( |
| 44 | + const std::shared_ptr<arrow::Schema>& schema, const std::vector<std::string>& partition_keys) { |
| 45 | + arrow::FieldVector partition_fields; |
| 46 | + partition_fields.reserve(partition_keys.size()); |
| 47 | + for (const auto& partition_key : partition_keys) { |
| 48 | + auto field = schema->GetFieldByName(partition_key); |
| 49 | + if (!field) { |
| 50 | + return Status::Invalid( |
| 51 | + fmt::format("get partition schema failed, cannot find partition key {} in schema", |
| 52 | + partition_key)); |
| 53 | + } |
| 54 | + partition_fields.push_back(field); |
| 55 | + } |
| 56 | + return arrow::schema(partition_fields); |
| 57 | +} |
| 58 | + |
| 59 | +Result<std::unique_ptr<FieldMappingBuilder>> FieldMappingBuilder::Create( |
| 60 | + const std::shared_ptr<arrow::Schema>& read_schema, |
| 61 | + const std::vector<std::string>& partition_keys, const std::shared_ptr<Predicate>& predicate) { |
| 62 | + PAIMON_ASSIGN_OR_RAISE(std::vector<DataField> read_fields, |
| 63 | + DataField::ConvertArrowSchemaToDataFields(read_schema)); |
| 64 | + return std::unique_ptr<FieldMappingBuilder>( |
| 65 | + new FieldMappingBuilder(read_fields, partition_keys, predicate)); |
| 66 | +} |
| 67 | + |
| 68 | +Result<std::unique_ptr<FieldMapping>> FieldMappingBuilder::CreateFieldMapping( |
| 69 | + const std::shared_ptr<arrow::Schema>& data_schema) const { |
| 70 | + PAIMON_ASSIGN_OR_RAISE(std::vector<DataField> data_fields, |
| 71 | + DataField::ConvertArrowSchemaToDataFields(data_schema)); |
| 72 | + return CreateFieldMapping(data_fields); |
| 73 | +} |
| 74 | + |
| 75 | +Result<std::unique_ptr<FieldMapping>> FieldMappingBuilder::CreateFieldMapping( |
| 76 | + const std::vector<DataField>& data_fields) const { |
| 77 | + // generate non-exist field info |
| 78 | + std::optional<NonExistFieldInfo> non_exist_field_info = CreateNonExistFieldInfo(data_fields); |
| 79 | + |
| 80 | + // generate exist field info |
| 81 | + ExistFieldInfo exist_field_info = CreateExistFieldInfo(data_fields); |
| 82 | + |
| 83 | + // key: partition key, value: partition idx |
| 84 | + std::map<std::string, int32_t> partition_key_to_idx = |
| 85 | + ObjectUtils::CreateIdentifierToIndexMap(partition_keys_); |
| 86 | + |
| 87 | + PAIMON_ASSIGN_OR_RAISE( |
| 88 | + NonPartitionInfo non_partition_info, |
| 89 | + CreateNonPartitionInfo(data_fields, exist_field_info, partition_key_to_idx)); |
| 90 | + PAIMON_ASSIGN_OR_RAISE(std::optional<PartitionInfo> partition_info, |
| 91 | + CreatePartitionInfo(exist_field_info, partition_key_to_idx)); |
| 92 | + return std::make_unique<FieldMapping>(partition_info, non_partition_info, non_exist_field_info); |
| 93 | +} |
| 94 | + |
| 95 | +ExistFieldInfo FieldMappingBuilder::CreateExistFieldInfo( |
| 96 | + const std::vector<DataField>& data_fields) const { |
| 97 | + // key:field id, value: {target_idx, read field} |
| 98 | + std::map<int32_t, std::pair<int32_t, DataField>> field_id_to_read_fields; |
| 99 | + for (size_t i = 0; i < read_fields_.size(); i++) { |
| 100 | + const auto& read_field = read_fields_[i]; |
| 101 | + field_id_to_read_fields.emplace(read_field.Id(), std::make_pair(i, read_field)); |
| 102 | + } |
| 103 | + |
| 104 | + ExistFieldInfo exist_field_info; |
| 105 | + for (const auto& data_field : data_fields) { |
| 106 | + auto iter = field_id_to_read_fields.find(data_field.Id()); |
| 107 | + if (iter != field_id_to_read_fields.end()) { |
| 108 | + const auto& [target_idx, read_field] = iter->second; |
| 109 | + exist_field_info.exist_read_schema.push_back(read_field); |
| 110 | + exist_field_info.exist_data_schema.push_back(data_field); |
| 111 | + exist_field_info.idx_in_target_read_schema.push_back(target_idx); |
| 112 | + } |
| 113 | + } |
| 114 | + return exist_field_info; |
| 115 | +} |
| 116 | + |
| 117 | +std::optional<NonExistFieldInfo> FieldMappingBuilder::CreateNonExistFieldInfo( |
| 118 | + const std::vector<DataField>& data_fields) const { |
| 119 | + // key: field id, value: data field |
| 120 | + std::map<int32_t, DataField> field_id_to_data_fields; |
| 121 | + for (const auto& data_field : data_fields) { |
| 122 | + field_id_to_data_fields.emplace(data_field.Id(), data_field); |
| 123 | + } |
| 124 | + |
| 125 | + NonExistFieldInfo non_exist_field_info; |
| 126 | + for (size_t i = 0; i < read_fields_.size(); i++) { |
| 127 | + const auto& read_field = read_fields_[i]; |
| 128 | + auto iter = field_id_to_data_fields.find(read_field.Id()); |
| 129 | + if (iter == field_id_to_data_fields.end()) { |
| 130 | + non_exist_field_info.non_exist_read_schema.push_back(read_field); |
| 131 | + non_exist_field_info.idx_in_target_read_schema.push_back(i); |
| 132 | + } |
| 133 | + } |
| 134 | + if (non_exist_field_info.idx_in_target_read_schema.empty()) { |
| 135 | + return std::nullopt; |
| 136 | + } |
| 137 | + return non_exist_field_info; |
| 138 | +} |
| 139 | + |
| 140 | +Result<std::vector<std::shared_ptr<CastExecutor>>> FieldMappingBuilder::CreateDataCastExecutors( |
| 141 | + const std::vector<DataField>& read_fields, const std::vector<DataField>& data_fields) { |
| 142 | + assert(read_fields.size() == data_fields.size()); |
| 143 | + std::vector<std::shared_ptr<CastExecutor>> cast_executors; |
| 144 | + cast_executors.reserve(read_fields.size()); |
| 145 | + for (size_t i = 0; i < read_fields.size(); i++) { |
| 146 | + PAIMON_ASSIGN_OR_RAISE(FieldType read_type, |
| 147 | + FieldTypeUtils::ConvertToFieldType(read_fields[i].Type()->id())); |
| 148 | + PAIMON_ASSIGN_OR_RAISE(FieldType data_type, |
| 149 | + FieldTypeUtils::ConvertToFieldType(data_fields[i].Type()->id())); |
| 150 | + |
| 151 | + if (!read_fields[i].Type()->Equals(data_fields[i].Type())) { |
| 152 | + if (read_type == FieldType::MAP || read_type == FieldType::ARRAY || |
| 153 | + read_type == FieldType::STRUCT) { |
| 154 | + return Status::Invalid("Only support column type evolution in atomic data type."); |
| 155 | + } |
| 156 | + auto executor_factory = CastExecutorFactory::GetCastExecutorFactory(); |
| 157 | + auto cast_executor = |
| 158 | + executor_factory->GetCastExecutor(/*src=*/data_type, /*target=*/read_type); |
| 159 | + if (!cast_executor) { |
| 160 | + return Status::Invalid( |
| 161 | + fmt::format("CreateDataCastExecutors failed: cannot find cast " |
| 162 | + "executor for field {} from type {} to {}", |
| 163 | + read_fields[i].Name(), FieldTypeUtils::FieldTypeToString(data_type), |
| 164 | + FieldTypeUtils::FieldTypeToString(read_type))); |
| 165 | + } |
| 166 | + cast_executors.push_back(cast_executor); |
| 167 | + } else { |
| 168 | + cast_executors.push_back(nullptr); |
| 169 | + } |
| 170 | + } |
| 171 | + return cast_executors; |
| 172 | +} |
| 173 | + |
| 174 | +Result<NonPartitionInfo> FieldMappingBuilder::CreateNonPartitionInfo( |
| 175 | + const std::vector<DataField>& data_fields, const ExistFieldInfo& exist_field_info, |
| 176 | + const std::map<std::string, int32_t>& partition_keys) const { |
| 177 | + NonPartitionInfo non_partition_info; |
| 178 | + for (size_t i = 0; i < exist_field_info.exist_data_schema.size(); i++) { |
| 179 | + const auto& data_field = exist_field_info.exist_data_schema[i]; |
| 180 | + const auto& read_field = exist_field_info.exist_read_schema[i]; |
| 181 | + auto iter = partition_keys.find(read_field.Name()); |
| 182 | + if (iter == partition_keys.end()) { |
| 183 | + non_partition_info.non_partition_read_schema.push_back(read_field); |
| 184 | + non_partition_info.non_partition_data_schema.push_back(data_field); |
| 185 | + non_partition_info.idx_in_target_read_schema.push_back( |
| 186 | + exist_field_info.idx_in_target_read_schema[i]); |
| 187 | + } |
| 188 | + } |
| 189 | + PAIMON_ASSIGN_OR_RAISE(non_partition_info.cast_executors, |
| 190 | + CreateDataCastExecutors(non_partition_info.non_partition_read_schema, |
| 191 | + non_partition_info.non_partition_data_schema)); |
| 192 | + // prepare predicate: exclude the push down predicate with partition key and non-exist fields |
| 193 | + PAIMON_ASSIGN_OR_RAISE(non_partition_info.non_partition_filter, CreateDataFilters(data_fields)); |
| 194 | + return non_partition_info; |
| 195 | +} |
| 196 | + |
| 197 | +Result<std::shared_ptr<Predicate>> FieldMappingBuilder::CreateDataFilters( |
| 198 | + const std::vector<DataField>& data_fields) const { |
| 199 | + if (!predicate_) { |
| 200 | + return predicate_; |
| 201 | + } |
| 202 | + // data schema mapping: id -> {data field idx, data field} |
| 203 | + std::map<int32_t, std::pair<int32_t, DataField>> field_id_to_data_fields; |
| 204 | + for (size_t i = 0; i < data_fields.size(); i++) { |
| 205 | + const auto& data_field = data_fields[i]; |
| 206 | + field_id_to_data_fields.emplace(data_field.Id(), std::make_pair(i, data_field)); |
| 207 | + } |
| 208 | + // table schema mapping: name -> read field |
| 209 | + std::map<std::string, DataField> field_name_to_read_fields; |
| 210 | + for (const auto& read_field : read_fields_) { |
| 211 | + field_name_to_read_fields.emplace(read_field.Name(), read_field); |
| 212 | + } |
| 213 | + // reconstruct predicate (e.g., modify field idx, cast literal) |
| 214 | + auto split_predicates = PredicateUtils::SplitAnd(predicate_); |
| 215 | + std::vector<std::shared_ptr<Predicate>> converted_predicates; |
| 216 | + converted_predicates.reserve(split_predicates.size()); |
| 217 | + for (const auto& predicate : split_predicates) { |
| 218 | + PAIMON_ASSIGN_OR_RAISE(std::optional<std::shared_ptr<Predicate>> converted, |
| 219 | + ReconstructPredicateWithDataFields( |
| 220 | + predicate, field_name_to_read_fields, field_id_to_data_fields)); |
| 221 | + if (converted != std::nullopt) { |
| 222 | + converted_predicates.push_back(converted.value()); |
| 223 | + } |
| 224 | + } |
| 225 | + // exclude partition fields in predicate |
| 226 | + std::set<std::string> partition_key_field_name_set(partition_keys_.begin(), |
| 227 | + partition_keys_.end()); |
| 228 | + PAIMON_ASSIGN_OR_RAISE(std::vector<std::shared_ptr<Predicate>> remain_predicates, |
| 229 | + PredicateUtils::ExcludePredicateWithFields( |
| 230 | + converted_predicates, partition_key_field_name_set)); |
| 231 | + if (remain_predicates.empty()) { |
| 232 | + return std::shared_ptr<Predicate>(); |
| 233 | + } |
| 234 | + return PredicateBuilder::And(remain_predicates); |
| 235 | +} |
| 236 | + |
| 237 | +Result<std::optional<std::shared_ptr<Predicate>>> FieldMappingBuilder::ReconstructLeafPredicate( |
| 238 | + const std::shared_ptr<LeafPredicateImpl>& leaf_predicate, |
| 239 | + const std::map<std::string, DataField>& field_name_to_read_fields, |
| 240 | + const std::map<int32_t, std::pair<int32_t, DataField>>& field_id_to_data_fields) { |
| 241 | + auto read_field_iter = field_name_to_read_fields.find(leaf_predicate->FieldName()); |
| 242 | + if (read_field_iter == field_name_to_read_fields.end()) { |
| 243 | + return Status::Invalid( |
| 244 | + fmt::format("invalid predicate, cannot find field {}", leaf_predicate->FieldName())); |
| 245 | + } |
| 246 | + auto data_field_iter = field_id_to_data_fields.find(read_field_iter->second.Id()); |
| 247 | + if (data_field_iter == field_id_to_data_fields.end()) { |
| 248 | + return std::optional<std::shared_ptr<Predicate>>(); |
| 249 | + } |
| 250 | + std::vector<Literal> new_literals; |
| 251 | + PAIMON_ASSIGN_OR_RAISE(FieldType read_type, FieldTypeUtils::ConvertToFieldType( |
| 252 | + read_field_iter->second.Type()->id())); |
| 253 | + PAIMON_ASSIGN_OR_RAISE(FieldType data_type, FieldTypeUtils::ConvertToFieldType( |
| 254 | + data_field_iter->second.second.Type()->id())); |
| 255 | + if (read_type != data_type || |
| 256 | + (!read_field_iter->second.Type()->Equals(data_field_iter->second.second.Type()))) { |
| 257 | + // read type and data type are different, only push down integer predicates |
| 258 | + if (FieldTypeUtils::IsIntegerNumeric(read_type) && |
| 259 | + FieldTypeUtils::IsIntegerNumeric(data_type) && |
| 260 | + FieldTypeUtils::IntegerScaleLargerThan(read_type, data_type)) { |
| 261 | + auto executor_factory = CastExecutorFactory::GetCastExecutorFactory(); |
| 262 | + auto cast_executor = |
| 263 | + executor_factory->GetCastExecutor(/*src=*/read_type, /*target=*/data_type); |
| 264 | + if (!cast_executor) { |
| 265 | + return Status::Invalid(fmt::format( |
| 266 | + "ReconstructLeafPredicate failed: cannot find cast " |
| 267 | + "executor for field {} from type {} to {}", |
| 268 | + read_field_iter->second.Name(), FieldTypeUtils::FieldTypeToString(read_type), |
| 269 | + FieldTypeUtils::FieldTypeToString(data_type))); |
| 270 | + } |
| 271 | + new_literals.reserve(leaf_predicate->Literals().size()); |
| 272 | + for (const auto& literal : leaf_predicate->Literals()) { |
| 273 | + PAIMON_ASSIGN_OR_RAISE( |
| 274 | + Literal casted, |
| 275 | + cast_executor->Cast(literal, data_field_iter->second.second.Type())); |
| 276 | + // ignore if any literal is overflowed. |
| 277 | + if (CastingUtils::IsIntegerLiteralCastedOverflow(literal, casted)) { |
| 278 | + return std::optional<std::shared_ptr<Predicate>>(); |
| 279 | + } |
| 280 | + new_literals.push_back(casted); |
| 281 | + } |
| 282 | + } else { |
| 283 | + return std::optional<std::shared_ptr<Predicate>>(); |
| 284 | + } |
| 285 | + } else { |
| 286 | + new_literals = leaf_predicate->Literals(); |
| 287 | + } |
| 288 | + |
| 289 | + const auto& [data_field_idx, data_field] = data_field_iter->second; |
| 290 | + return std::optional<std::shared_ptr<Predicate>>(std::make_shared<LeafPredicateImpl>( |
| 291 | + leaf_predicate->GetLeafFunction(), /*data field idx*/ data_field_idx, |
| 292 | + /*data field name*/ data_field.Name(), |
| 293 | + /*data field type*/ data_type, new_literals)); |
| 294 | +} |
| 295 | + |
| 296 | +Result<std::optional<std::shared_ptr<Predicate>>> |
| 297 | +FieldMappingBuilder::ReconstructPredicateWithDataFields( |
| 298 | + const std::shared_ptr<Predicate>& predicate, |
| 299 | + const std::map<std::string, DataField>& field_name_to_read_fields, |
| 300 | + const std::map<int32_t, std::pair<int32_t, DataField>>& field_id_to_data_fields) { |
| 301 | + if (auto leaf_predicate = std::dynamic_pointer_cast<LeafPredicateImpl>(predicate)) { |
| 302 | + return ReconstructLeafPredicate(leaf_predicate, field_name_to_read_fields, |
| 303 | + field_id_to_data_fields); |
| 304 | + } else if (auto compound_predicate = |
| 305 | + std::dynamic_pointer_cast<CompoundPredicateImpl>(predicate)) { |
| 306 | + std::vector<std::shared_ptr<Predicate>> converted_children; |
| 307 | + for (const auto& child : compound_predicate->Children()) { |
| 308 | + PAIMON_ASSIGN_OR_RAISE(std::optional<std::shared_ptr<Predicate>> converted_child, |
| 309 | + ReconstructPredicateWithDataFields( |
| 310 | + child, field_name_to_read_fields, field_id_to_data_fields)); |
| 311 | + if (converted_child == std::nullopt) { |
| 312 | + return std::optional<std::shared_ptr<Predicate>>(); |
| 313 | + } |
| 314 | + assert(converted_child.value()); |
| 315 | + converted_children.push_back(converted_child.value()); |
| 316 | + } |
| 317 | + return std::optional<std::shared_ptr<Predicate>>( |
| 318 | + compound_predicate->NewCompoundPredicate(converted_children)); |
| 319 | + } |
| 320 | + return Status::Invalid( |
| 321 | + "invalid predicate, cannot convert to leaf predicate or compound predicate"); |
| 322 | +} |
| 323 | + |
| 324 | +Result<std::optional<PartitionInfo>> FieldMappingBuilder::CreatePartitionInfo( |
| 325 | + const ExistFieldInfo& exist_field_info, |
| 326 | + const std::map<std::string, int32_t>& partition_keys) const { |
| 327 | + if (partition_keys.empty()) { |
| 328 | + return std::optional<PartitionInfo>(); |
| 329 | + } |
| 330 | + PartitionInfo partition_info; |
| 331 | + for (size_t i = 0; i < exist_field_info.exist_read_schema.size(); i++) { |
| 332 | + const auto& read_field = exist_field_info.exist_read_schema[i]; |
| 333 | + auto iter = partition_keys.find(read_field.Name()); |
| 334 | + if (iter != partition_keys.end()) { |
| 335 | + partition_info.partition_read_schema.push_back(read_field); |
| 336 | + partition_info.idx_in_target_read_schema.push_back( |
| 337 | + exist_field_info.idx_in_target_read_schema[i]); |
| 338 | + partition_info.idx_in_partition.push_back(iter->second); |
| 339 | + } |
| 340 | + } |
| 341 | + if (partition_info.idx_in_target_read_schema.empty()) { |
| 342 | + return std::optional<PartitionInfo>(); |
| 343 | + } |
| 344 | + PAIMON_ASSIGN_OR_RAISE(partition_info.partition_filter, |
| 345 | + PredicateUtils::CreatePickedFieldFilter(predicate_, partition_keys)); |
| 346 | + return std::optional<PartitionInfo>(partition_info); |
| 347 | +} |
| 348 | + |
| 349 | +} // namespace paimon |
0 commit comments