|
17 | 17 | #include "paimon/common/data/blob_utils.h" |
18 | 18 |
|
19 | 19 | #include <cstddef> |
20 | | -#include <utility> |
| 20 | +#include <set> |
21 | 21 | #include <vector> |
22 | 22 |
|
23 | 23 | #include "arrow/api.h" |
24 | 24 | #include "arrow/array/array_nested.h" |
25 | 25 | #include "arrow/type.h" |
| 26 | +#include "fmt/format.h" |
26 | 27 | #include "paimon/common/data/blob_defs.h" |
| 28 | +#include "paimon/common/data/blob_descriptor.h" |
| 29 | +#include "paimon/common/types/data_field.h" |
27 | 30 | #include "paimon/common/utils/arrow/status_utils.h" |
28 | 31 | #include "paimon/common/utils/string_utils.h" |
29 | | - |
30 | 32 | namespace arrow { |
31 | 33 | class Array; |
32 | 34 | } |
33 | 35 |
|
34 | 36 | namespace paimon { |
35 | | - |
36 | 37 | BlobUtils::SeparatedSchemas BlobUtils::SeparateBlobSchema( |
37 | | - const std::shared_ptr<arrow::Schema>& schema) { |
38 | | - std::vector<std::shared_ptr<arrow::Field>> remaining_fields; |
| 38 | + const std::shared_ptr<arrow::Schema>& schema, const std::set<std::string>& inline_fields) { |
| 39 | + std::vector<std::shared_ptr<arrow::Field>> main_fields; |
39 | 40 | std::vector<std::shared_ptr<arrow::Field>> blob_fields; |
40 | | - for (auto i = 0; i < schema->num_fields(); i++) { |
| 41 | + for (int32_t i = 0; i < schema->num_fields(); i++) { |
41 | 42 | auto field = schema->field(i); |
42 | | - if (IsBlobField(field)) { |
| 43 | + if (IsBlobField(field) && inline_fields.count(field->name()) == 0) { |
| 44 | + // Non-inline BLOB -> goes to blob file |
43 | 45 | blob_fields.emplace_back(field); |
44 | 46 | } else { |
45 | | - remaining_fields.emplace_back(field); |
| 47 | + // Non-blob fields OR inline BLOB fields -> stay in main |
| 48 | + main_fields.emplace_back(field); |
46 | 49 | } |
47 | 50 | } |
48 | 51 | SeparatedSchemas result; |
49 | | - result.main_schema = arrow::schema(remaining_fields); |
| 52 | + result.main_schema = arrow::schema(main_fields); |
50 | 53 | result.blob_schema = arrow::schema(blob_fields); |
51 | 54 | return result; |
52 | 55 | } |
53 | 56 |
|
54 | 57 | Result<BlobUtils::SeparatedStructArrays> BlobUtils::SeparateBlobArray( |
55 | | - const std::shared_ptr<arrow::StructArray>& struct_array) { |
| 58 | + const std::shared_ptr<arrow::StructArray>& struct_array, |
| 59 | + const std::set<std::string>& inline_fields) { |
56 | 60 | std::shared_ptr<arrow::StructType> old_type = |
57 | 61 | std::static_pointer_cast<arrow::StructType>(struct_array->type()); |
58 | 62 | const auto& old_fields = old_type->fields(); |
59 | 63 | const auto& old_arrays = struct_array->fields(); |
60 | 64 |
|
61 | | - std::vector<std::shared_ptr<arrow::Field>> remaining_fields; |
62 | | - std::vector<std::shared_ptr<arrow::Array>> remaining_arrays; |
63 | | - std::vector<std::shared_ptr<arrow::Field>> blob_fields; |
64 | | - std::vector<std::shared_ptr<arrow::Array>> blob_arrays; |
| 65 | + arrow::ArrayVector main_arrays; |
| 66 | + arrow::ArrayVector blob_arrays; |
| 67 | + arrow::FieldVector main_fields; |
| 68 | + arrow::FieldVector blob_fields; |
65 | 69 |
|
66 | 70 | for (size_t i = 0; i < old_fields.size(); i++) { |
67 | | - if (IsBlobField(old_fields[i])) { |
| 71 | + if (IsBlobField(old_fields[i]) && inline_fields.count(old_fields[i]->name()) == 0) { |
68 | 72 | blob_fields.push_back(old_fields[i]); |
69 | 73 | blob_arrays.push_back(old_arrays[i]); |
70 | 74 | } else { |
71 | | - remaining_fields.push_back(old_fields[i]); |
72 | | - remaining_arrays.push_back(old_arrays[i]); |
| 75 | + main_fields.push_back(old_fields[i]); |
| 76 | + main_arrays.push_back(old_arrays[i]); |
73 | 77 | } |
74 | 78 | } |
75 | 79 |
|
| 80 | + if (blob_fields.empty()) { |
| 81 | + return Status::Invalid( |
| 82 | + "SeparateBlobArray expects at least one non-inline blob field, but got none."); |
| 83 | + } |
| 84 | + if (main_fields.empty()) { |
| 85 | + return Status::Invalid("SeparateBlobArray expects at least one main field, but got none."); |
| 86 | + } |
| 87 | + |
76 | 88 | SeparatedStructArrays result; |
77 | 89 | PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(result.main_array, |
78 | | - arrow::StructArray::Make(remaining_arrays, remaining_fields)); |
| 90 | + arrow::StructArray::Make(main_arrays, main_fields)); |
79 | 91 | PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(result.blob_array, |
80 | 92 | arrow::StructArray::Make(blob_arrays, blob_fields)); |
81 | 93 | return result; |
@@ -114,4 +126,66 @@ std::shared_ptr<arrow::Field> BlobUtils::ToArrowField( |
114 | 126 | return arrow::field(field_name, arrow::large_binary(), nullable, |
115 | 127 | std::make_shared<arrow::KeyValueMetadata>(metadata)); |
116 | 128 | } |
| 129 | + |
| 130 | +Status BlobUtils::ValidateInlineBlobDescriptors( |
| 131 | + const std::shared_ptr<arrow::StructArray>& struct_array, |
| 132 | + const std::set<std::string>& inline_descriptor_fields) { |
| 133 | + if (inline_descriptor_fields.empty()) { |
| 134 | + return Status::OK(); |
| 135 | + } |
| 136 | + if (!struct_array) { |
| 137 | + return Status::Invalid("array in ValidateInlineBlobDescriptors must be a struct_array"); |
| 138 | + } |
| 139 | + for (const auto& field_name : inline_descriptor_fields) { |
| 140 | + auto field_array = struct_array->GetFieldByName(field_name); |
| 141 | + if (!field_array) { |
| 142 | + continue; |
| 143 | + } |
| 144 | + const auto* binary_array = |
| 145 | + arrow::internal::checked_cast<const arrow::LargeBinaryArray*>(field_array.get()); |
| 146 | + if (!binary_array) { |
| 147 | + return Status::Invalid( |
| 148 | + fmt::format("cannot cast array for field {} to LargeBinaryArray", field_name)); |
| 149 | + } |
| 150 | + for (int64_t row = 0; row < binary_array->length(); ++row) { |
| 151 | + if (binary_array->IsNull(row)) { |
| 152 | + continue; |
| 153 | + } |
| 154 | + auto value = binary_array->GetView(row); |
| 155 | + PAIMON_ASSIGN_OR_RAISE(bool is_descriptor, |
| 156 | + BlobDescriptor::IsBlobDescriptor(value.data(), value.size())); |
| 157 | + if (!is_descriptor) { |
| 158 | + return Status::Invalid(fmt::format( |
| 159 | + "BLOB inline field {} configured by blob-descriptor-field or blob-view-field " |
| 160 | + "require values to be a BlobDescriptor or BlobViewStruct.", |
| 161 | + field_name)); |
| 162 | + } |
| 163 | + } |
| 164 | + } |
| 165 | + return Status::OK(); |
| 166 | +} |
| 167 | + |
| 168 | +std::vector<DataField> BlobUtils::ConvertBlobInlineDataFields( |
| 169 | + const std::vector<DataField>& data_fields, const std::vector<std::string>& blob_inline_fields) { |
| 170 | + if (blob_inline_fields.empty()) { |
| 171 | + return data_fields; |
| 172 | + } |
| 173 | + |
| 174 | + std::set<std::string> blob_inline_field_set(blob_inline_fields.begin(), |
| 175 | + blob_inline_fields.end()); |
| 176 | + std::vector<DataField> converted_fields; |
| 177 | + converted_fields.reserve(data_fields.size()); |
| 178 | + for (const auto& data_field : data_fields) { |
| 179 | + if (blob_inline_field_set.find(data_field.Name()) == blob_inline_field_set.end()) { |
| 180 | + converted_fields.push_back(data_field); |
| 181 | + continue; |
| 182 | + } |
| 183 | + |
| 184 | + auto binary_field = arrow::field(data_field.Name(), arrow::binary(), data_field.Nullable(), |
| 185 | + data_field.ArrowField()->metadata()); |
| 186 | + converted_fields.emplace_back(data_field.Id(), binary_field, data_field.Description()); |
| 187 | + } |
| 188 | + return converted_fields; |
| 189 | +} |
| 190 | + |
117 | 191 | } // namespace paimon |
0 commit comments