Skip to content

Commit 0f4cd63

Browse files
committed
feat(shared-shredding): Expose shared-shredding utilities
1 parent ffbda4f commit 0f4cd63

15 files changed

Lines changed: 518 additions & 125 deletions
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright 2026-present Alibaba Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <cstdint>
20+
#include <map>
21+
#include <memory>
22+
#include <set>
23+
#include <string>
24+
#include <vector>
25+
26+
#include "paimon/arrow/abi.h"
27+
#include "paimon/result.h"
28+
#include "paimon/status.h"
29+
#include "paimon/visibility.h"
30+
31+
namespace paimon {
32+
33+
/// Parsed file-level meta for one shared-shredding MAP column.
34+
struct PAIMON_EXPORT MapSharedShreddingFieldMeta {
35+
/// field_name -> field_id
36+
std::map<std::string, int32_t> name_to_id;
37+
/// field_id -> set of physical column indices S
38+
std::map<int32_t, std::vector<int32_t>> field_to_columns;
39+
/// Set of field_ids that ever spilled into __overflow
40+
std::set<int32_t> overflow_field_set;
41+
/// Number of physical columns K in this file
42+
int32_t num_columns = 0;
43+
/// Maximum row width observed in this file
44+
int32_t max_row_width = 0;
45+
46+
bool operator==(const MapSharedShreddingFieldMeta& other) const {
47+
if (this == &other) {
48+
return true;
49+
}
50+
return name_to_id == other.name_to_id && field_to_columns == other.field_to_columns &&
51+
overflow_field_set == other.overflow_field_set && num_columns == other.num_columns &&
52+
max_row_width == other.max_row_width;
53+
}
54+
};
55+
56+
class PAIMON_EXPORT MapSharedShreddingSchemaUtils {
57+
public:
58+
MapSharedShreddingSchemaUtils() = delete;
59+
~MapSharedShreddingSchemaUtils() = delete;
60+
61+
/// Converts a logical schema to a physical schema by replacing shredding MAP columns
62+
/// with their physical Struct representation.
63+
/// @param logical_schema The original Arrow C schema with MAP<STRING, T> columns.
64+
/// Ownership of schema resources is transferred to this method.
65+
/// @param field_to_num_columns Map from field name to its physical column count K.
66+
/// Each shredding column can have its own width.
67+
/// @return The exported Arrow C schema for file writing.
68+
static Result<std::unique_ptr<::ArrowSchema>> LogicalToPhysicalSchema(
69+
std::unique_ptr<::ArrowSchema> logical_schema,
70+
const std::map<std::string, int32_t>& field_to_num_columns);
71+
72+
/// Attaches shared-shredding metadata to fields in a physical schema.
73+
/// @param physical_schema The Arrow C physical schema whose fields should receive metadata.
74+
/// Ownership of schema resources is transferred to this method.
75+
/// @param field_name_to_meta Map from physical field name to its shared-shredding metadata.
76+
/// @param compression Compression codec name for field_dict serialization.
77+
/// @return A new Arrow C schema with shared-shredding metadata attached to matching fields.
78+
static Result<std::unique_ptr<::ArrowSchema>> AttachMetadataToSchema(
79+
std::unique_ptr<::ArrowSchema> physical_schema,
80+
const std::map<std::string, MapSharedShreddingFieldMeta>& field_name_to_meta,
81+
const std::string& compression);
82+
83+
/// Extracts shared-shredding metadata from a named field in a physical schema.
84+
/// @param physical_schema The Arrow C physical schema that contains the target field.
85+
/// Ownership of schema resources is transferred to this method.
86+
/// @param field_name The physical field name whose metadata should be extracted.
87+
/// @param compression Compression codec name for field_dict deserialization.
88+
/// @return Parsed shared-shredding metadata for the field.
89+
static Result<MapSharedShreddingFieldMeta> ExtractMetadataFromField(
90+
std::unique_ptr<::ArrowSchema> physical_schema, const std::string& field_name,
91+
const std::string& compression);
92+
};
93+
94+
} // namespace paimon

src/paimon/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ set(PAIMON_COMMON_SRCS
139139
common/utils/crc32c.cpp
140140
common/utils/decimal_utils.cpp
141141
common/data/shredding/map_shared_shredding_utils.cpp
142+
common/data/shredding/map_shared_shredding_schema_utils.cpp
142143
common/data/shredding/map_shared_shredding_context.cpp
143144
common/data/shredding/map_shared_shredding_batch_converter.cpp
144145
common/data/shredding/map_shared_shredding_column_allocator.cpp
@@ -547,6 +548,7 @@ if(PAIMON_BUILD_TESTS)
547548
common/utils/threadsafe_queue_test.cpp
548549
common/utils/generic_lru_cache_test.cpp
549550
common/data/shredding/map_shared_shredding_utils_test.cpp
551+
common/data/shredding/map_shared_shredding_schema_utils_test.cpp
550552
common/data/shredding/map_shared_shredding_batch_converter_test.cpp
551553
common/data/shredding/lru_map_shared_shredding_column_allocator_test.cpp
552554
common/data/shredding/plain_map_shared_shredding_column_allocator_test.cpp

src/paimon/common/data/shredding/map_shared_shredding_batch_converter.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,9 @@ Result<std::shared_ptr<MapSharedShreddingBatchConverter>> MapSharedShreddingBatc
6565
const std::shared_ptr<MapSharedShreddingContext>& context, const CoreOptions& options,
6666
const std::shared_ptr<MemoryPool>& pool) {
6767
std::map<std::string, int32_t> field_to_num_columns = context->ComputeNextK();
68-
std::shared_ptr<arrow::Schema> physical_schema =
69-
MapSharedShreddingUtils::LogicalToPhysicalSchema(logical_schema, field_to_num_columns);
68+
PAIMON_ASSIGN_OR_RAISE(
69+
std::shared_ptr<arrow::Schema> physical_schema,
70+
MapSharedShreddingUtils::LogicalToPhysicalSchema(logical_schema, field_to_num_columns));
7071
std::vector<ColumnContext> contexts;
7172
std::vector<std::string> shredding_field_names;
7273
contexts.reserve(field_to_num_columns.size());

src/paimon/common/data/shredding/map_shared_shredding_file_reader_test.cpp

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -63,26 +63,28 @@ class MapSharedShreddingFileReaderTest : public ::testing::Test {
6363
return meta;
6464
}
6565

66-
std::shared_ptr<arrow::Schema> PhysicalSchemaWithMetadata() const {
66+
Result<std::shared_ptr<arrow::Schema>> PhysicalSchemaWithMetadata() const {
6767
return PhysicalSchemaWithMetadata(TagsMeta());
6868
}
6969

70-
std::shared_ptr<arrow::Schema> PhysicalSchemaWithMetadata(
70+
Result<std::shared_ptr<arrow::Schema>> PhysicalSchemaWithMetadata(
7171
const MapSharedShreddingFieldMeta& meta) const {
7272
std::map<std::string, int32_t> field_to_num_columns = {{"tags", 2}};
73-
auto physical_schema =
74-
MapSharedShreddingUtils::LogicalToPhysicalSchema(logical_schema_, field_to_num_columns);
73+
PAIMON_ASSIGN_OR_RAISE(auto physical_schema,
74+
MapSharedShreddingUtils::LogicalToPhysicalSchema(
75+
logical_schema_, field_to_num_columns));
7576
auto metadata = std::make_shared<arrow::KeyValueMetadata>();
76-
EXPECT_OK(MapSharedShreddingUtils::SerializeMetadata(
77+
PAIMON_RETURN_NOT_OK(MapSharedShreddingUtils::SerializeMetadata(
7778
meta, MapSharedShreddingDefine::kDefaultDictCompression, metadata.get()));
7879

7980
arrow::FieldVector fields = physical_schema->fields();
8081
fields[1] = fields[1]->WithMetadata(metadata);
8182
return arrow::schema(std::move(fields));
8283
}
8384

84-
std::shared_ptr<arrow::Array> PhysicalArray() const {
85-
std::shared_ptr<arrow::Schema> physical_schema = PhysicalSchemaWithMetadata();
85+
Result<std::shared_ptr<arrow::Array>> PhysicalArray() const {
86+
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<arrow::Schema> physical_schema,
87+
PhysicalSchemaWithMetadata());
8688
std::string json = R"([
8789
[1, [[0, 1], 10, 20, null]],
8890
[2, [[2, 0], 30, 40, null]],
@@ -140,15 +142,15 @@ class MapSharedShreddingFileReaderTest : public ::testing::Test {
140142
std::move(reader), std::move(shared_shredding_name_to_context), pool_);
141143
}
142144

143-
std::unique_ptr<MapSharedShreddingFileReader> CreateReader(
145+
Result<std::unique_ptr<MapSharedShreddingFileReader>> CreateReader(
144146
std::shared_ptr<arrow::Array> physical_array = nullptr,
145147
std::shared_ptr<arrow::Schema> physical_schema = nullptr,
146148
const std::optional<std::string>& selected_keys = std::nullopt) const {
147149
if (!physical_schema) {
148-
physical_schema = PhysicalSchemaWithMetadata();
150+
PAIMON_ASSIGN_OR_RAISE(physical_schema, PhysicalSchemaWithMetadata());
149151
}
150152
if (!physical_array) {
151-
physical_array = PhysicalArray();
153+
PAIMON_ASSIGN_OR_RAISE(physical_array, PhysicalArray());
152154
}
153155
auto mock_reader = std::make_unique<MockFileBatchReader>(
154156
physical_array, arrow::struct_(physical_schema->fields()), /*read_batch_size=*/10);
@@ -243,7 +245,7 @@ class MapSharedShreddingFileReaderTest : public ::testing::Test {
243245
};
244246

245247
TEST_F(MapSharedShreddingFileReaderTest, TestGetFileSchemaReturnsLogicalMapSchema) {
246-
auto reader = CreateReader();
248+
ASSERT_OK_AND_ASSIGN(auto reader, CreateReader());
247249

248250
ASSERT_OK_AND_ASSIGN(auto c_schema, reader->GetFileSchema());
249251
auto schema = arrow::ImportSchema(c_schema.get()).ValueOrDie();
@@ -256,8 +258,9 @@ TEST_F(MapSharedShreddingFileReaderTest, TestGetFileSchemaReturnsLogicalMapSchem
256258
}
257259

258260
TEST_F(MapSharedShreddingFileReaderTest, TestAllExistSelectedKeysWithoutOverflow) {
259-
auto reader = CreateReader(/*physical_array=*/nullptr, /*physical_schema=*/nullptr,
260-
/*selected_keys=*/"b");
261+
ASSERT_OK_AND_ASSIGN(auto reader,
262+
CreateReader(/*physical_array=*/nullptr, /*physical_schema=*/nullptr,
263+
/*selected_keys=*/"b"));
261264
auto read_schema = ExportSchema(ReadSchema("b"));
262265
ASSERT_OK(reader->SetReadSchema(read_schema.get(), /*predicate=*/nullptr,
263266
/*selection_bitmap=*/std::nullopt));
@@ -277,8 +280,9 @@ TEST_F(MapSharedShreddingFileReaderTest, TestAllExistSelectedKeysWithoutOverflow
277280
}
278281

279282
TEST_F(MapSharedShreddingFileReaderTest, TestAllExistSelectedKeysWithOverflow) {
280-
auto reader = CreateReader(/*physical_array=*/nullptr, /*physical_schema=*/nullptr,
281-
/*selected_keys=*/"a,c");
283+
ASSERT_OK_AND_ASSIGN(auto reader,
284+
CreateReader(/*physical_array=*/nullptr, /*physical_schema=*/nullptr,
285+
/*selected_keys=*/"a,c"));
282286
auto read_schema = ExportSchema(ReadSchema("a,c"));
283287
ASSERT_OK(reader->SetReadSchema(read_schema.get(), /*predicate=*/nullptr,
284288
/*selection_bitmap=*/std::nullopt));
@@ -298,8 +302,9 @@ TEST_F(MapSharedShreddingFileReaderTest, TestAllExistSelectedKeysWithOverflow) {
298302
}
299303

300304
TEST_F(MapSharedShreddingFileReaderTest, TestPartialExistSelectedKeys) {
301-
auto reader = CreateReader(/*physical_array=*/nullptr, /*physical_schema=*/nullptr,
302-
/*selected_keys=*/"a,c,missing");
305+
ASSERT_OK_AND_ASSIGN(auto reader,
306+
CreateReader(/*physical_array=*/nullptr, /*physical_schema=*/nullptr,
307+
/*selected_keys=*/"a,c,missing"));
303308
auto read_schema = ExportSchema(ReadSchema("a,c,missing"));
304309
ASSERT_OK(reader->SetReadSchema(read_schema.get(), /*predicate=*/nullptr,
305310
/*selection_bitmap=*/std::nullopt));
@@ -320,7 +325,7 @@ TEST_F(MapSharedShreddingFileReaderTest, TestPartialExistSelectedKeys) {
320325
}
321326

322327
TEST_F(MapSharedShreddingFileReaderTest, TestMissingSelectedKeysReadsWholeMap) {
323-
auto reader = CreateReader();
328+
ASSERT_OK_AND_ASSIGN(auto reader, CreateReader());
324329
auto read_schema = ExportSchema(ReadSchema(std::nullopt));
325330
ASSERT_OK(reader->SetReadSchema(read_schema.get(), /*predicate=*/nullptr,
326331
/*selection_bitmap=*/std::nullopt));
@@ -345,7 +350,7 @@ TEST_F(MapSharedShreddingFileReaderTest, TestSpecialSelectedKeys) {
345350
meta.field_to_columns = {{0, {0}}, {1, {1}}, {2, {0}}, {3, {1}}};
346351
meta.num_columns = 2;
347352
meta.max_row_width = 2;
348-
auto physical_schema = PhysicalSchemaWithMetadata(meta);
353+
ASSERT_OK_AND_ASSIGN(auto physical_schema, PhysicalSchemaWithMetadata(meta));
349354
std::string json = R"([
350355
[1, [[0, 1], 10, 20, null]],
351356
[2, [[2, 3], 30, 40, null]],
@@ -356,7 +361,8 @@ TEST_F(MapSharedShreddingFileReaderTest, TestSpecialSelectedKeys) {
356361
.ValueOrDie();
357362

358363
auto assert_read = [&](const std::string& selected_keys, const std::string& expected_json) {
359-
auto reader = CreateReader(physical_array, physical_schema, selected_keys);
364+
ASSERT_OK_AND_ASSIGN(auto reader,
365+
CreateReader(physical_array, physical_schema, selected_keys));
360366
auto read_schema = ExportSchema(ReadSchema(selected_keys));
361367
ASSERT_OK(reader->SetReadSchema(read_schema.get(), /*predicate=*/nullptr,
362368
/*selection_bitmap=*/std::nullopt));
@@ -392,8 +398,9 @@ TEST_F(MapSharedShreddingFileReaderTest, TestSpecialSelectedKeys) {
392398
}
393399

394400
TEST_F(MapSharedShreddingFileReaderTest, TestUnknownSelectedKeyReturnsEmptyMap) {
395-
auto reader = CreateReader(/*physical_array=*/nullptr, /*physical_schema=*/nullptr,
396-
/*selected_keys=*/"missing");
401+
ASSERT_OK_AND_ASSIGN(auto reader,
402+
CreateReader(/*physical_array=*/nullptr, /*physical_schema=*/nullptr,
403+
/*selected_keys=*/"missing"));
397404
auto read_schema = ExportSchema(ReadSchema("missing"));
398405
ASSERT_OK(reader->SetReadSchema(read_schema.get(), /*predicate=*/nullptr,
399406
/*selection_bitmap=*/std::nullopt));
@@ -414,14 +421,15 @@ TEST_F(MapSharedShreddingFileReaderTest, TestUnknownSelectedKeyReturnsEmptyMap)
414421
}
415422

416423
TEST_F(MapSharedShreddingFileReaderTest, TestInvalidNullFieldMappingField) {
417-
auto physical_schema = PhysicalSchemaWithMetadata();
424+
ASSERT_OK_AND_ASSIGN(auto physical_schema, PhysicalSchemaWithMetadata());
418425
std::string json = R"([
419426
[1, [null, 10, null, null]]
420427
])";
421428
auto physical_array =
422429
arrow::ipc::internal::json::ArrayFromJSON(arrow::struct_(physical_schema->fields()), json)
423430
.ValueOrDie();
424-
auto reader = CreateReader(physical_array, physical_schema, /*selected_keys=*/"a");
431+
ASSERT_OK_AND_ASSIGN(auto reader,
432+
CreateReader(physical_array, physical_schema, /*selected_keys=*/"a"));
425433
auto read_schema = ExportSchema(ReadSchema("a"));
426434
ASSERT_OK(reader->SetReadSchema(read_schema.get(), /*predicate=*/nullptr,
427435
/*selection_bitmap=*/std::nullopt));
@@ -430,14 +438,15 @@ TEST_F(MapSharedShreddingFileReaderTest, TestInvalidNullFieldMappingField) {
430438
}
431439

432440
TEST_F(MapSharedShreddingFileReaderTest, TestInvalidNullFieldMappingFieldElement) {
433-
auto physical_schema = PhysicalSchemaWithMetadata();
441+
ASSERT_OK_AND_ASSIGN(auto physical_schema, PhysicalSchemaWithMetadata());
434442
std::string json = R"([
435443
[1, [[0, null], 10, null, null]]
436444
])";
437445
auto physical_array =
438446
arrow::ipc::internal::json::ArrayFromJSON(arrow::struct_(physical_schema->fields()), json)
439447
.ValueOrDie();
440-
auto reader = CreateReader(physical_array, physical_schema, /*selected_keys=*/"b");
448+
ASSERT_OK_AND_ASSIGN(auto reader,
449+
CreateReader(physical_array, physical_schema, /*selected_keys=*/"b"));
441450
auto read_schema = ExportSchema(ReadSchema("b"));
442451
ASSERT_OK(reader->SetReadSchema(read_schema.get(), /*predicate=*/nullptr,
443452
/*selection_bitmap=*/std::nullopt));
@@ -458,8 +467,8 @@ TEST_F(MapSharedShreddingFileReaderTest, TestListValue) {
458467
meta.max_row_width = 3;
459468

460469
std::map<std::string, int32_t> field_to_num_columns = {{"tags", 2}};
461-
auto physical_schema =
462-
MapSharedShreddingUtils::LogicalToPhysicalSchema(logical_schema, field_to_num_columns);
470+
ASSERT_OK_AND_ASSIGN(auto physical_schema, MapSharedShreddingUtils::LogicalToPhysicalSchema(
471+
logical_schema, field_to_num_columns));
463472
auto metadata = std::make_shared<arrow::KeyValueMetadata>();
464473
ASSERT_OK(MapSharedShreddingUtils::SerializeMetadata(
465474
meta, MapSharedShreddingDefine::kDefaultDictCompression, metadata.get()));
@@ -475,8 +484,9 @@ TEST_F(MapSharedShreddingFileReaderTest, TestListValue) {
475484
[4, [[1, 0], [8], [9, 10], [[2, [null]]]]]
476485
])")
477486
.ValueOrDie();
478-
auto reader = CreateReader(physical_array, physical_schema,
479-
/*selected_keys=*/"a,c"); // NOLINT(whitespace/comma)
487+
ASSERT_OK_AND_ASSIGN(auto reader,
488+
CreateReader(physical_array, physical_schema,
489+
/*selected_keys=*/"a,c")); // NOLINT(whitespace/comma)
480490

481491
auto read_metadata = std::make_shared<arrow::KeyValueMetadata>();
482492
read_metadata->Append("paimon.map.selected-keys", "a,c");

0 commit comments

Comments
 (0)