Skip to content

Commit 0da726b

Browse files
authored
feat(blob): support blob-view-field for cross-table blob reference re… (alibaba#327)
1 parent 2466671 commit 0da726b

19 files changed

Lines changed: 1687 additions & 12 deletions

include/paimon/catalog/identifier.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,20 @@ class PAIMON_EXPORT Identifier {
3636
explicit Identifier(const std::string& table);
3737
Identifier(const std::string& database, const std::string& table);
3838

39-
bool operator==(const Identifier& other);
39+
bool operator==(const Identifier& other) const;
4040
const std::string& GetDatabaseName() const;
4141
const std::string& GetTableName() const;
4242
Result<std::string> GetDataTableName() const;
4343
Result<std::optional<std::string>> GetBranchName() const;
4444
Result<std::string> GetBranchNameOrDefault() const;
4545
Result<std::optional<std::string>> GetSystemTableName() const;
4646
Result<bool> IsSystemTable() const;
47+
std::string GetFullName() const;
4748
std::string ToString() const;
49+
int32_t HashCode() const;
50+
51+
public:
52+
static Result<Identifier> FromString(const std::string& full_name);
4853

4954
private:
5055
Status SplitTableName() const;
@@ -58,3 +63,13 @@ class PAIMON_EXPORT Identifier {
5863
};
5964

6065
} // namespace paimon
66+
67+
namespace std {
68+
template <>
69+
struct hash<paimon::Identifier> {
70+
size_t operator()(const paimon::Identifier& identifier) const {
71+
return identifier.HashCode();
72+
}
73+
};
74+
75+
} // namespace std

src/paimon/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ set(PAIMON_COMMON_SRCS
2525
common/data/binary_string.cpp
2626
common/data/blob.cpp
2727
common/data/blob_descriptor.cpp
28+
common/data/blob_view_struct.cpp
2829
common/data/blob_utils.cpp
2930
common/data/columnar/columnar_array.cpp
3031
common/data/columnar/columnar_map.cpp
@@ -110,6 +111,7 @@ set(PAIMON_COMMON_SRCS
110111
common/reader/predicate_batch_reader.cpp
111112
common/reader/prefetch_file_batch_reader_impl.cpp
112113
common/reader/reader_utils.cpp
114+
common/reader/blob_view_resolving_batch_reader.cpp
113115
common/reader/complete_row_kind_batch_reader.cpp
114116
common/reader/data_evolution_file_reader.cpp
115117
common/sst/block_handle.cpp
@@ -332,6 +334,7 @@ set(PAIMON_CORE_SRCS
332334
core/table/system/system_table_schema.cpp
333335
core/tag/tag.cpp
334336
core/utils/branch_manager.cpp
337+
core/utils/blob_view_lookup.cpp
335338
core/utils/consumer_manager.cpp
336339
core/utils/field_mapping.cpp
337340
core/utils/file_store_path_factory.cpp
@@ -408,6 +411,7 @@ if(PAIMON_BUILD_TESTS)
408411
common/data/timestamp_test.cpp
409412
common/data/blob_test.cpp
410413
common/data/blob_descriptor_test.cpp
414+
common/data/blob_view_struct_test.cpp
411415
common/data/blob_utils_test.cpp
412416
common/executor/default_executor_test.cpp
413417
common/format/column_stats_test.cpp
@@ -470,6 +474,7 @@ if(PAIMON_BUILD_TESTS)
470474
common/reader/prefetch_file_batch_reader_impl_test.cpp
471475
common/reader/reader_utils_test.cpp
472476
common/reader/complete_row_kind_batch_reader_test.cpp
477+
common/reader/blob_view_resolving_batch_reader_test.cpp
473478
common/reader/data_evolution_file_reader_test.cpp
474479
common/reader/data_evolution_array_test.cpp
475480
common/reader/data_evolution_row_test.cpp
@@ -715,6 +720,7 @@ if(PAIMON_BUILD_TESTS)
715720
core/table/source/table_scan_test.cpp
716721
core/table/system/system_table_test.cpp
717722
core/tag/tag_test.cpp
723+
core/utils/blob_view_lookup_test.cpp
718724
core/utils/branch_manager_test.cpp
719725
core/utils/consumer_manager_test.cpp
720726
core/utils/file_store_path_factory_cache_test.cpp
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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+
#include <map>
19+
#include <string>
20+
21+
#include "paimon/fs/file_system.h"
22+
23+
namespace paimon {
24+
struct CatalogContext {
25+
CatalogContext(const std::string& _root_path,
26+
const std::map<std::string, std::string>& _options,
27+
const std::shared_ptr<FileSystem>& _file_system)
28+
: root_path(_root_path), options(_options), file_system(_file_system) {}
29+
30+
std::string root_path;
31+
std::map<std::string, std::string> options;
32+
std::shared_ptr<FileSystem> file_system;
33+
};
34+
35+
} // namespace paimon

src/paimon/common/data/blob_descriptor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ Result<std::unique_ptr<BlobDescriptor>> BlobDescriptor::Create(int8_t version,
5050
PAIMON_UNIQUE_PTR<Bytes> BlobDescriptor::Serialize(const std::shared_ptr<MemoryPool>& pool) const {
5151
MemorySegmentOutputStream out(MemorySegmentOutputStream::DEFAULT_SEGMENT_SIZE, pool);
5252
out.SetOrder(ByteOrder::PAIMON_LITTLE_ENDIAN);
53-
out.WriteValue<int8_t>(version_);
53+
out.WriteValue<int8_t>(kCurrentVersion);
5454
out.WriteValue<int64_t>(kMagic);
5555
out.WriteValue<int32_t>(static_cast<int32_t>(uri_.size()));
5656

src/paimon/common/data/blob_descriptor_test.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ TEST_F(BlobDescriptorTest, TestDeserializeCompatibilityForJavaWithVersion1) {
5656
ASSERT_EQ(descriptor->Uri(), "test_uri");
5757
ASSERT_EQ(descriptor->Offset(), 1024);
5858
ASSERT_EQ(descriptor->Length(), 2048);
59+
60+
PAIMON_UNIQUE_PTR<Bytes> cpp_serialized = descriptor->Serialize(pool_);
61+
auto cpp_serialized_string = std::string(cpp_serialized->data(), cpp_serialized->size());
62+
ASSERT_NE(cpp_serialized_string, java_serialized);
5963
}
6064

6165
TEST_F(BlobDescriptorTest, TestDeserializeCompatibilityForJavaWithVersion2) {
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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+
#include "paimon/common/data/blob_view_struct.h"
17+
18+
#include <utility>
19+
20+
#include "fmt/format.h"
21+
#include "paimon/common/io/memory_segment_output_stream.h"
22+
#include "paimon/common/memory/memory_segment_utils.h"
23+
#include "paimon/common/utils/murmurhash_utils.h"
24+
#include "paimon/io/byte_array_input_stream.h"
25+
#include "paimon/io/byte_order.h"
26+
#include "paimon/io/data_input_stream.h"
27+
#include "paimon/memory/bytes.h"
28+
#include "paimon/status.h"
29+
30+
namespace paimon {
31+
PAIMON_UNIQUE_PTR<Bytes> BlobViewStruct::Serialize(const std::shared_ptr<MemoryPool>& pool) const {
32+
MemorySegmentOutputStream out(MemorySegmentOutputStream::DEFAULT_SEGMENT_SIZE, pool);
33+
out.SetOrder(ByteOrder::PAIMON_LITTLE_ENDIAN);
34+
35+
out.WriteValue<int8_t>(kCurrentVersion);
36+
out.WriteValue<int64_t>(kMagic);
37+
std::string identifier = identifier_.GetFullName();
38+
out.WriteValue<int32_t>(static_cast<int32_t>(identifier.size()));
39+
auto uri_bytes = std::make_shared<Bytes>(identifier, pool.get());
40+
out.WriteBytes(uri_bytes);
41+
out.WriteValue<int32_t>(field_id_);
42+
out.WriteValue<int64_t>(row_id_);
43+
return MemorySegmentUtils::CopyToBytes(out.Segments(), 0, out.CurrentSize(), pool.get());
44+
}
45+
46+
Result<std::unique_ptr<BlobViewStruct>> BlobViewStruct::Deserialize(const char* buffer,
47+
uint64_t size) {
48+
auto input_stream = std::make_shared<ByteArrayInputStream>(buffer, size);
49+
DataInputStream in(std::move(input_stream));
50+
in.SetOrder(ByteOrder::PAIMON_LITTLE_ENDIAN);
51+
52+
PAIMON_ASSIGN_OR_RAISE(int8_t version, in.ReadValue<int8_t>());
53+
if (version != kCurrentVersion) {
54+
return Status::Invalid(fmt::format(
55+
"Expecting BlobViewStruct version to be {}, but found {}.", kCurrentVersion, version));
56+
}
57+
PAIMON_ASSIGN_OR_RAISE(int64_t magic, in.ReadValue<int64_t>());
58+
if (kMagic != magic) {
59+
return Status::Invalid(
60+
fmt::format("Invalid BlobViewStruct: missing magic header. Expected magic: {}, "
61+
"but found {}",
62+
kMagic, magic));
63+
}
64+
PAIMON_ASSIGN_OR_RAISE(int32_t length, in.ReadValue<int32_t>());
65+
std::string identifier_str(length, '\0');
66+
PAIMON_RETURN_NOT_OK(in.Read(identifier_str.data(), identifier_str.size()));
67+
PAIMON_ASSIGN_OR_RAISE(int32_t field_id, in.ReadValue<int32_t>());
68+
PAIMON_ASSIGN_OR_RAISE(int64_t row_id, in.ReadValue<int64_t>());
69+
PAIMON_ASSIGN_OR_RAISE(Identifier identifier, Identifier::FromString(identifier_str));
70+
return std::make_unique<BlobViewStruct>(identifier, field_id, row_id);
71+
}
72+
73+
Result<bool> BlobViewStruct::IsBlobViewStruct(const char* buffer, uint64_t size) {
74+
if (size < kMinViewLength) {
75+
return false;
76+
}
77+
auto input_stream = std::make_shared<ByteArrayInputStream>(buffer, size);
78+
DataInputStream in(std::move(input_stream));
79+
in.SetOrder(ByteOrder::PAIMON_LITTLE_ENDIAN);
80+
81+
PAIMON_ASSIGN_OR_RAISE(int8_t version, in.ReadValue<int8_t>());
82+
if (version != kCurrentVersion) {
83+
return false;
84+
}
85+
PAIMON_ASSIGN_OR_RAISE(int64_t magic, in.ReadValue<int64_t>());
86+
return kMagic == magic;
87+
}
88+
89+
std::string BlobViewStruct::ToString() const {
90+
return fmt::format("BlobViewStruct{{identifier={}, fieldId={}, rowId={}}}",
91+
identifier_.GetFullName(), field_id_, row_id_);
92+
}
93+
94+
bool BlobViewStruct::operator==(const BlobViewStruct& other) const {
95+
if (this == &other) {
96+
return true;
97+
}
98+
return field_id_ == other.field_id_ && row_id_ == other.row_id_ &&
99+
identifier_ == other.identifier_;
100+
}
101+
102+
bool BlobViewStruct::operator!=(const BlobViewStruct& other) const {
103+
return !(*this == other);
104+
}
105+
106+
int32_t BlobViewStruct::HashCode() const {
107+
int32_t hash =
108+
MurmurHashUtils::HashUnsafeBytes(reinterpret_cast<const void*>(&field_id_),
109+
/*offset=*/0, sizeof(field_id_), identifier_.HashCode());
110+
return MurmurHashUtils::HashUnsafeBytes(reinterpret_cast<const void*>(&row_id_),
111+
/*offset=*/0, sizeof(row_id_), hash);
112+
}
113+
} // namespace paimon
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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 <functional>
21+
#include <memory>
22+
#include <string>
23+
24+
#include "paimon/catalog/identifier.h"
25+
#include "paimon/memory/bytes.h"
26+
#include "paimon/memory/memory_pool.h"
27+
#include "paimon/result.h"
28+
29+
namespace paimon {
30+
/// Serialized metadata for a BLOB view field.
31+
/// A blob view only stores the coordinates needed to locate the original blob value in the
32+
/// upstream table: identifier, field_id and row_id. The actual blob data is
33+
/// resolved at read time by scanning the upstream table.
34+
class BlobViewStruct {
35+
public:
36+
BlobViewStruct(const Identifier& identifier, int32_t field_id, int64_t row_id)
37+
: identifier_(identifier), field_id_(field_id), row_id_(row_id) {}
38+
39+
const Identifier& GetIdentifier() const {
40+
return identifier_;
41+
}
42+
43+
int32_t FieldId() const {
44+
return field_id_;
45+
}
46+
47+
int64_t RowId() const {
48+
return row_id_;
49+
}
50+
51+
static Result<std::unique_ptr<BlobViewStruct>> Deserialize(const char* buffer, uint64_t size);
52+
static Result<bool> IsBlobViewStruct(const char* buffer, uint64_t size);
53+
PAIMON_UNIQUE_PTR<Bytes> Serialize(const std::shared_ptr<MemoryPool>& pool) const;
54+
std::string ToString() const;
55+
int32_t HashCode() const;
56+
57+
bool operator==(const BlobViewStruct& other) const;
58+
bool operator!=(const BlobViewStruct& other) const;
59+
60+
private:
61+
static constexpr int64_t kMagic = 0x424C4F4256494557l;
62+
static constexpr int8_t kCurrentVersion = 1;
63+
/// one byte for version, eight bytes for magic number.
64+
static constexpr uint64_t kMinViewLength = 9;
65+
66+
Identifier identifier_;
67+
int32_t field_id_;
68+
int64_t row_id_;
69+
};
70+
71+
/// Resolves a BlobViewStruct into the serialized BlobDescriptor bytes stored in the upstream
72+
/// table. Returns nullptr when the referenced source-table cell is null.
73+
using BlobViewResolver = std::function<Result<std::shared_ptr<Bytes>>(const BlobViewStruct&)>;
74+
75+
} // namespace paimon
76+
77+
namespace std {
78+
template <>
79+
struct hash<paimon::BlobViewStruct> {
80+
size_t operator()(const paimon::BlobViewStruct& blob_view_struct) const {
81+
return blob_view_struct.HashCode();
82+
}
83+
};
84+
85+
} // namespace std

0 commit comments

Comments
 (0)