Skip to content

Commit 79729d7

Browse files
authored
feat: introduce FieldMapping and FieldMappingBuilder (#137)
1 parent eec4f8d commit 79729d7

3 files changed

Lines changed: 1107 additions & 0 deletions

File tree

Lines changed: 349 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,349 @@
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

Comments
 (0)