blob: 52cc07de95fa520fc12aa8c653480e94734cc9ca [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <map>
#include <optional>
#include <string>
#include <utility>
#include "fmt/format.h"
#include "paimon/common/data/binary_row.h"
#include "paimon/common/io/memory_segment_output_stream.h"
#include "paimon/common/memory/memory_segment_utils.h"
#include "paimon/common/utils/math.h"
#include "paimon/common/utils/serialization_utils.h"
#include "paimon/core/global_index/indexed_split_impl.h"
#include "paimon/core/io/data_file_meta_serializer.h"
#include "paimon/core/table/format/format_data_split.h"
#include "paimon/core/table/source/data_split_impl.h"
#include "paimon/core/table/source/deletion_file.h"
#include "paimon/core/table/source/fallback_data_split.h"
#include "paimon/core/table/source/query_auth_split.h"
#include "paimon/core/utils/object_serializer.h"
#include "paimon/global_index/indexed_split.h"
#include "paimon/io/byte_array_input_stream.h"
#include "paimon/io/data_input_stream.h"
#include "paimon/memory/bytes.h"
#include "paimon/memory/memory_pool.h"
#include "paimon/status.h"
#include "paimon/table/source/data_split.h"
namespace paimon {
struct DataFileMeta;
namespace {
/// Checks a serialized count before allocating: each entry needs at least `min_entry_bytes`.
Result<int32_t> ReadSplitCount(DataInputStream* in, int64_t min_entry_bytes,
const char* split_type) {
PAIMON_ASSIGN_OR_RAISE(int32_t count, in->ReadValue<int32_t>());
PAIMON_ASSIGN_OR_RAISE(int64_t pos, in->GetPos());
PAIMON_ASSIGN_OR_RAISE(int64_t stream_length, in->Length());
if (count < 0 || count * min_entry_bytes > stream_length - pos) {
return Status::Invalid(
fmt::format("invalid {}, {} entries do not fit the split", split_type, count));
}
return count;
}
/// Java DataInput.readBoolean() treats any nonzero byte as true. Reading into a C++ bool would
/// create an invalid representation for a byte other than 0 or 1.
Result<bool> ReadSplitFlag(DataInputStream* in) {
PAIMON_ASSIGN_OR_RAISE(int8_t value, in->ReadValue<int8_t>());
return value != 0;
}
Status WriteDataSplit(const std::shared_ptr<DataSplitImpl>& data_split_impl,
MemorySegmentOutputStream* out, const std::shared_ptr<MemoryPool>& pool) {
out->WriteValue<int64_t>(DataSplitImpl::MAGIC);
out->WriteValue<int32_t>(DataSplitImpl::VERSION);
out->WriteValue<int64_t>(data_split_impl->SnapshotId());
PAIMON_RETURN_NOT_OK(SerializationUtils::SerializeBinaryRow(data_split_impl->Partition(), out));
out->WriteValue<int32_t>(data_split_impl->Bucket());
out->WriteString(data_split_impl->BucketPath());
std::optional<int32_t> total_buckets = data_split_impl->TotalBuckets();
if (total_buckets == std::nullopt) {
out->WriteValue<bool>(false);
} else {
out->WriteValue<bool>(true);
out->WriteValue<int32_t>(total_buckets.value());
}
DataFileMetaSerializer serializer(pool);
PAIMON_RETURN_NOT_OK(serializer.SerializeList(data_split_impl->BeforeFiles(), out));
DeletionFile::SerializeList(data_split_impl->BeforeDeletionFiles(), out);
PAIMON_RETURN_NOT_OK(serializer.SerializeList(data_split_impl->DataFiles(), out));
DeletionFile::SerializeList(data_split_impl->DeletionFiles(), out);
out->WriteValue<bool>(data_split_impl->IsStreaming());
out->WriteValue<bool>(data_split_impl->RawConvertible());
return Status::OK();
}
Result<std::shared_ptr<DataSplitImpl>> ReadDataSplitWithoutMagicNumber(
int64_t magic, DataInputStream* in, const std::shared_ptr<MemoryPool>& pool) {
int32_t version = 1;
if (magic == DataSplitImpl::MAGIC) {
PAIMON_ASSIGN_OR_RAISE(version, in->ReadValue<int32_t>());
}
// version 1 does not write magic number in, so the first long is snapshot id.
int64_t snapshot_id = magic;
if (version != 1) {
PAIMON_ASSIGN_OR_RAISE(snapshot_id, in->ReadValue<int64_t>());
}
PAIMON_ASSIGN_OR_RAISE(BinaryRow partition,
SerializationUtils::DeserializeBinaryRow(in, pool.get()));
int32_t bucket = -1;
PAIMON_ASSIGN_OR_RAISE(bucket, in->ReadValue<int32_t>());
std::string bucket_path;
PAIMON_ASSIGN_OR_RAISE(bucket_path, in->ReadString());
std::optional<int32_t> total_buckets;
if (version >= 6) {
PAIMON_ASSIGN_OR_RAISE(bool total_buckets_exist, ReadSplitFlag(in));
if (total_buckets_exist) {
PAIMON_ASSIGN_OR_RAISE(total_buckets, in->ReadValue<int32_t>());
}
}
PAIMON_ASSIGN_OR_RAISE(
std::unique_ptr<ObjectSerializer<std::shared_ptr<DataFileMeta>>> data_file_serializer,
DataSplitImpl::GetFileMetaSerializer(version, pool));
std::vector<std::shared_ptr<DataFileMeta>> before_files;
PAIMON_ASSIGN_OR_RAISE(before_files, data_file_serializer->DeserializeList(in));
// compatible for deletion file
std::vector<std::optional<DeletionFile>> before_deletion_files;
PAIMON_ASSIGN_OR_RAISE(before_deletion_files, DeletionFile::DeserializeList(in, version));
std::vector<std::shared_ptr<DataFileMeta>> data_files;
PAIMON_ASSIGN_OR_RAISE(data_files, data_file_serializer->DeserializeList(in));
// compatible for deletion file
std::vector<std::optional<DeletionFile>> data_deletion_files;
PAIMON_ASSIGN_OR_RAISE(data_deletion_files, DeletionFile::DeserializeList(in, version));
bool is_streaming = false;
PAIMON_ASSIGN_OR_RAISE(is_streaming, ReadSplitFlag(in));
bool raw_convertible = false;
PAIMON_ASSIGN_OR_RAISE(raw_convertible, ReadSplitFlag(in));
DataSplitImpl::Builder builder(partition, bucket, bucket_path, std::move(data_files));
builder.WithTotalBuckets(total_buckets)
.WithSnapshot(snapshot_id)
.WithBeforeFiles(std::move(before_files))
.IsStreaming(is_streaming)
.RawConvertible(raw_convertible);
if (!before_deletion_files.empty()) {
builder.WithBeforeDeletionFiles(before_deletion_files);
}
if (!data_deletion_files.empty()) {
builder.WithDataDeletionFiles(data_deletion_files);
}
return builder.Build();
}
Status WriteIndexedSplit(const std::shared_ptr<IndexedSplitImpl>& indexed_split_impl,
MemorySegmentOutputStream* out, const std::shared_ptr<MemoryPool>& pool) {
out->WriteValue<int64_t>(IndexedSplitImpl::MAGIC);
out->WriteValue<int32_t>(IndexedSplitImpl::VERSION);
auto inner_split_impl =
std::dynamic_pointer_cast<DataSplitImpl>(indexed_split_impl->GetDataSplit());
if (!inner_split_impl) {
return Status::Invalid("inner split in IndexedSplit is supposed to be DataSplit");
}
PAIMON_RETURN_NOT_OK(WriteDataSplit(inner_split_impl, out, pool));
auto row_ranges = indexed_split_impl->RowRanges();
out->WriteValue<int32_t>(row_ranges.size());
for (const auto& range : row_ranges) {
out->WriteValue<int64_t>(range.from);
out->WriteValue<int64_t>(range.to);
}
auto scores = indexed_split_impl->Scores();
if (!scores.empty()) {
out->WriteValue<bool>(true);
out->WriteValue<int32_t>(scores.size());
for (float score : scores) {
out->WriteValue<float>(CanonicalizeFloatingPoint(score));
}
} else {
out->WriteValue<bool>(false);
}
return Status::OK();
}
/// Reads an IndexedSplit after its magic number.
Result<std::shared_ptr<IndexedSplitImpl>> ReadIndexedSplitWithoutMagicNumber(
DataInputStream* in, const std::shared_ptr<MemoryPool>& pool) {
PAIMON_ASSIGN_OR_RAISE(int32_t version, in->ReadValue<int32_t>());
if (version != IndexedSplitImpl::VERSION) {
return Status::Invalid(fmt::format("Unsupported IndexedSplit version: {}", version));
}
PAIMON_ASSIGN_OR_RAISE(int64_t data_split_magic, in->ReadValue<int64_t>());
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<DataSplitImpl> data_split,
ReadDataSplitWithoutMagicNumber(data_split_magic, in, pool));
PAIMON_ASSIGN_OR_RAISE(int32_t range_size,
ReadSplitCount(in, 2 * sizeof(int64_t), "IndexedSplit"));
std::vector<Range> row_ranges;
row_ranges.reserve(range_size);
for (int32_t i = 0; i < range_size; ++i) {
PAIMON_ASSIGN_OR_RAISE(int64_t range_from, in->ReadValue<int64_t>());
PAIMON_ASSIGN_OR_RAISE(int64_t range_to, in->ReadValue<int64_t>());
row_ranges.emplace_back(range_from, range_to);
}
std::vector<float> scores;
PAIMON_ASSIGN_OR_RAISE(bool has_scores, ReadSplitFlag(in));
if (has_scores) {
PAIMON_ASSIGN_OR_RAISE(int32_t scores_length,
ReadSplitCount(in, sizeof(float), "IndexedSplit"));
scores.resize(scores_length);
for (int32_t i = 0; i < scores_length; ++i) {
PAIMON_ASSIGN_OR_RAISE(float score, in->ReadValue<float>());
scores[i] = score;
}
}
return std::make_shared<IndexedSplitImpl>(data_split, row_ranges, scores);
}
/// Java `QueryAuthSplit.writeString` of a non-null string: its length, then the UTF-8 bytes.
void WriteAuthString(const std::string& value, MemorySegmentOutputStream* out) {
out->WriteValue<int32_t>(static_cast<int32_t>(value.size()));
out->Write(value.data(), static_cast<uint32_t>(value.size()));
}
/// A null string reads as an empty one, which the rules refuse.
Result<std::string> ReadAuthString(DataInputStream* in) {
PAIMON_ASSIGN_OR_RAISE(int32_t length, in->ReadValue<int32_t>());
if (length < 0) {
return std::string();
}
PAIMON_ASSIGN_OR_RAISE(int64_t pos, in->GetPos());
PAIMON_ASSIGN_OR_RAISE(int64_t stream_length, in->Length());
if (length > stream_length - pos) {
return Status::Invalid(
fmt::format("invalid QueryAuthSplit, a string of {} bytes overruns the split", length));
}
std::string value(length, '\0');
PAIMON_RETURN_NOT_OK(in->Read(value.data(), length));
return value;
}
Status WriteWithSplitSerializer(const std::shared_ptr<Split>& split, MemorySegmentOutputStream* out,
const std::shared_ptr<MemoryPool>& pool) {
out->WriteValue<int64_t>(QueryAuthSplit::kSplitSerializerMagic);
out->WriteValue<int32_t>(QueryAuthSplit::kSplitSerializerVersion);
if (auto data_split_impl = std::dynamic_pointer_cast<DataSplitImpl>(split)) {
out->WriteValue<int32_t>(QueryAuthSplit::kSplitSerializerDataSplit);
return WriteDataSplit(data_split_impl, out, pool);
}
if (auto indexed_split_impl = std::dynamic_pointer_cast<IndexedSplitImpl>(split)) {
out->WriteValue<int32_t>(QueryAuthSplit::kSplitSerializerIndexedSplit);
return WriteIndexedSplit(indexed_split_impl, out, pool);
}
return Status::Invalid("a QueryAuthSplit can only wrap a DataSplit or an IndexedSplit");
}
/// Java `QueryAuthSplit.serialize`, framed by `SplitSerializer`.
Status WriteQueryAuthSplitWithSplitSerializer(const QueryAuthSplit& split,
MemorySegmentOutputStream* out,
const std::shared_ptr<MemoryPool>& pool) {
out->WriteValue<int64_t>(QueryAuthSplit::kSplitSerializerMagic);
out->WriteValue<int32_t>(QueryAuthSplit::kSplitSerializerVersion);
out->WriteValue<int32_t>(QueryAuthSplit::kSplitSerializerQueryAuthSplit);
PAIMON_RETURN_NOT_OK(WriteWithSplitSerializer(split.GetSplit(), out, pool));
const std::shared_ptr<TableQueryAuthResult>& auth_result = split.GetAuthResult();
if (auth_result == nullptr) {
return Status::Invalid("a QueryAuthSplit must carry the rules it was planned with");
}
out->WriteValue<bool>(true);
out->WriteValue<bool>(true);
out->WriteValue<int32_t>(static_cast<int32_t>(auth_result->Filter().size()));
for (const std::string& filter : auth_result->Filter()) {
WriteAuthString(filter, out);
}
out->WriteValue<bool>(true);
out->WriteValue<int32_t>(static_cast<int32_t>(auth_result->ColumnMasking().size()));
for (const auto& [column, transform] : auth_result->ColumnMasking()) {
WriteAuthString(column, out);
WriteAuthString(transform, out);
}
return Status::OK();
}
void WriteFieldIds(const std::map<std::string, int32_t>& field_ids,
MemorySegmentOutputStream* out) {
out->WriteValue<int32_t>(static_cast<int32_t>(field_ids.size()));
for (const auto& [field, id] : field_ids) {
WriteAuthString(field, out);
out->WriteValue<int32_t>(id);
}
}
/// A split without a scope, as Java wrote it, is written in the Java format alone.
Status WriteQueryAuthSplit(const QueryAuthSplit& split, MemorySegmentOutputStream* out,
const std::shared_ptr<MemoryPool>& pool) {
const std::optional<QueryAuthScope>& scope = split.GetScope();
if (!scope) {
return WriteQueryAuthSplitWithSplitSerializer(split, out, pool);
}
out->WriteValue<int64_t>(QueryAuthSplit::kScopedMagic);
out->WriteValue<int32_t>(QueryAuthSplit::kScopedVersion);
PAIMON_RETURN_NOT_OK(WriteQueryAuthSplitWithSplitSerializer(split, out, pool));
WriteFieldIds(scope->authorized_field_ids, out);
WriteFieldIds(scope->rule_field_ids, out);
WriteAuthString(scope->table_path, out);
WriteAuthString(scope->branch, out);
return Status::OK();
}
/// Reads a split Java `SplitSerializer` wrote, after its magic number; `in_query_auth_split` when
/// it is the split a `QueryAuthSplit` wraps.
Result<std::shared_ptr<Split>> ReadWithSplitSerializerWithoutMagicNumber(
DataInputStream* in, const std::shared_ptr<MemoryPool>& pool, bool in_query_auth_split) {
PAIMON_ASSIGN_OR_RAISE(int32_t version, in->ReadValue<int32_t>());
if (version != QueryAuthSplit::kSplitSerializerVersion) {
return Status::Invalid(fmt::format("Unsupported split serializer version: {}", version));
}
PAIMON_ASSIGN_OR_RAISE(int32_t type, in->ReadValue<int32_t>());
if (type == QueryAuthSplit::kSplitSerializerDataSplit) {
PAIMON_ASSIGN_OR_RAISE(int64_t magic, in->ReadValue<int64_t>());
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<DataSplitImpl> data_split,
ReadDataSplitWithoutMagicNumber(magic, in, pool));
return std::shared_ptr<Split>(data_split);
}
if (type == QueryAuthSplit::kSplitSerializerIndexedSplit) {
PAIMON_ASSIGN_OR_RAISE(int64_t magic, in->ReadValue<int64_t>());
if (magic != IndexedSplitImpl::MAGIC) {
return Status::Invalid("invalid IndexedSplit, wrong magic number");
}
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<IndexedSplitImpl> indexed_split,
ReadIndexedSplitWithoutMagicNumber(in, pool));
return std::shared_ptr<Split>(indexed_split);
}
if (type != QueryAuthSplit::kSplitSerializerQueryAuthSplit) {
return Status::Invalid(fmt::format("Unsupported split type: {}", type));
}
// Refused before reading it, so nested wrappers cannot recurse without bound.
if (in_query_auth_split) {
return Status::Invalid("invalid QueryAuthSplit, it wraps another QueryAuthSplit");
}
PAIMON_ASSIGN_OR_RAISE(int64_t inner_magic, in->ReadValue<int64_t>());
if (inner_magic != QueryAuthSplit::kSplitSerializerMagic) {
return Status::Invalid(
"invalid QueryAuthSplit, its split is not framed by SplitSerializer");
}
PAIMON_ASSIGN_OR_RAISE(
std::shared_ptr<Split> inner,
ReadWithSplitSerializerWithoutMagicNumber(in, pool, /*in_query_auth_split=*/true));
PAIMON_ASSIGN_OR_RAISE(bool has_auth_result, ReadSplitFlag(in));
if (!has_auth_result) {
// Rules that cannot be read must not read as no rules.
return Status::Invalid("invalid QueryAuthSplit, it carries no rules");
}
std::vector<std::string> filter;
PAIMON_ASSIGN_OR_RAISE(bool has_filter, ReadSplitFlag(in));
if (has_filter) {
PAIMON_ASSIGN_OR_RAISE(int32_t size, ReadSplitCount(in, sizeof(int32_t), "QueryAuthSplit"));
for (int32_t i = 0; i < size; ++i) {
PAIMON_ASSIGN_OR_RAISE(std::string value, ReadAuthString(in));
filter.push_back(std::move(value));
}
}
std::map<std::string, std::string> column_masking;
PAIMON_ASSIGN_OR_RAISE(bool has_column_masking, ReadSplitFlag(in));
if (has_column_masking) {
PAIMON_ASSIGN_OR_RAISE(int32_t size,
ReadSplitCount(in, 2 * sizeof(int32_t), "QueryAuthSplit"));
for (int32_t i = 0; i < size; ++i) {
PAIMON_ASSIGN_OR_RAISE(std::string column, ReadAuthString(in));
PAIMON_ASSIGN_OR_RAISE(std::string transform, ReadAuthString(in));
if (!column_masking.emplace(std::move(column), std::move(transform)).second) {
return Status::Invalid("invalid QueryAuthSplit, its rules mask a column twice");
}
}
}
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<TableQueryAuthResult> auth_result,
TableQueryAuthResult::Create(filter, column_masking));
return std::shared_ptr<Split>(
std::make_shared<QueryAuthSplit>(inner, auth_result, /*scope=*/std::nullopt));
}
Result<std::map<std::string, int32_t>> ReadFieldIds(DataInputStream* in) {
PAIMON_ASSIGN_OR_RAISE(int32_t size, ReadSplitCount(in, 2 * sizeof(int32_t), "QueryAuthSplit"));
std::map<std::string, int32_t> field_ids;
for (int32_t i = 0; i < size; ++i) {
PAIMON_ASSIGN_OR_RAISE(std::string field, ReadAuthString(in));
PAIMON_ASSIGN_OR_RAISE(int32_t id, in->ReadValue<int32_t>());
if (!field_ids.emplace(std::move(field), id).second) {
return Status::Invalid("invalid QueryAuthSplit, its scope names a column twice");
}
}
return field_ids;
}
/// Reads a split `WriteQueryAuthSplit` wrote with its scope, after the magic number.
Result<std::shared_ptr<Split>> ReadScopedQueryAuthSplitWithoutMagicNumber(
DataInputStream* in, const std::shared_ptr<MemoryPool>& pool) {
PAIMON_ASSIGN_OR_RAISE(int32_t version, in->ReadValue<int32_t>());
if (version != QueryAuthSplit::kScopedVersion) {
return Status::Invalid(fmt::format("Unsupported QueryAuthSplit version: {}", version));
}
PAIMON_ASSIGN_OR_RAISE(int64_t magic, in->ReadValue<int64_t>());
if (magic != QueryAuthSplit::kSplitSerializerMagic) {
return Status::Invalid(
"invalid QueryAuthSplit, its split is not framed by SplitSerializer");
}
PAIMON_ASSIGN_OR_RAISE(
std::shared_ptr<Split> split,
ReadWithSplitSerializerWithoutMagicNumber(in, pool, /*in_query_auth_split=*/false));
auto auth_split = std::dynamic_pointer_cast<QueryAuthSplit>(split);
if (auth_split == nullptr) {
return Status::Invalid("invalid QueryAuthSplit, its envelope holds another split");
}
QueryAuthScope scope;
PAIMON_ASSIGN_OR_RAISE(scope.authorized_field_ids, ReadFieldIds(in));
PAIMON_ASSIGN_OR_RAISE(scope.rule_field_ids, ReadFieldIds(in));
PAIMON_ASSIGN_OR_RAISE(scope.table_path, ReadAuthString(in));
PAIMON_ASSIGN_OR_RAISE(scope.branch, ReadAuthString(in));
return std::shared_ptr<Split>(std::make_shared<QueryAuthSplit>(
auth_split->GetSplit(), auth_split->GetAuthResult(), std::move(scope)));
}
} // namespace
Result<std::string> Split::Serialize(const std::shared_ptr<Split>& split,
const std::shared_ptr<MemoryPool>& pool) {
MemorySegmentOutputStream out(MemorySegmentOutputStream::DEFAULT_SEGMENT_SIZE, pool);
if (auto query_auth_split = std::dynamic_pointer_cast<QueryAuthSplit>(split)) {
PAIMON_RETURN_NOT_OK(WriteQueryAuthSplit(*query_auth_split, &out, pool));
} else if (auto data_split_impl = std::dynamic_pointer_cast<DataSplitImpl>(split)) {
PAIMON_RETURN_NOT_OK(WriteDataSplit(data_split_impl, &out, pool));
} else if (auto indexed_split_impl = std::dynamic_pointer_cast<IndexedSplitImpl>(split)) {
PAIMON_RETURN_NOT_OK(WriteIndexedSplit(indexed_split_impl, &out, pool));
} else if (std::dynamic_pointer_cast<FormatDataSplit>(split) != nullptr) {
// A format table's plan has no cross-runtime encoding, and one of paimon-cpp's own would
// let a plan made here be handed to a runtime that cannot read it back.
return Status::NotImplemented(
"a FormatDataSplit cannot be serialized: a format table's plan is in-memory only, so "
"plan and read it within one process");
} else {
return Status::Invalid("invalid split, cannot cast to DataSplit or IndexedSplit");
}
PAIMON_UNIQUE_PTR<Bytes> bytes =
MemorySegmentUtils::CopyToBytes(out.Segments(), 0, out.CurrentSize(), pool.get());
return std::string(bytes->data(), bytes->size());
}
Result<std::shared_ptr<Split>> Split::Deserialize(const char* buffer, size_t length,
const std::shared_ptr<MemoryPool>& pool) {
auto input_stream = std::make_shared<ByteArrayInputStream>(buffer, length);
DataInputStream in(input_stream);
int64_t magic = -1;
PAIMON_ASSIGN_OR_RAISE(magic, in.ReadValue<int64_t>());
if (magic == QueryAuthSplit::kSplitSerializerMagic || magic == QueryAuthSplit::kScopedMagic) {
std::shared_ptr<Split> split;
if (magic == QueryAuthSplit::kScopedMagic) {
PAIMON_ASSIGN_OR_RAISE(split, ReadScopedQueryAuthSplitWithoutMagicNumber(&in, pool));
} else {
PAIMON_ASSIGN_OR_RAISE(split, ReadWithSplitSerializerWithoutMagicNumber(
&in, pool, /*in_query_auth_split=*/false));
}
PAIMON_ASSIGN_OR_RAISE(int64_t pos, in.GetPos());
PAIMON_ASSIGN_OR_RAISE(int64_t stream_length, in.Length());
if (pos != stream_length) {
return Status::Invalid(
fmt::format("invalid split byte stream, remaining {} bytes after deserializing",
stream_length - pos));
}
return split;
} else if (magic == IndexedSplitImpl::MAGIC) {
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<IndexedSplitImpl> indexed_split,
ReadIndexedSplitWithoutMagicNumber(&in, pool));
// TODO(lisizhuo.lsz): support fallback split in IndexedSplit
PAIMON_ASSIGN_OR_RAISE(int64_t pos, in.GetPos());
PAIMON_ASSIGN_OR_RAISE(int64_t stream_length, in.Length());
if (pos == stream_length) {
return indexed_split;
} else if (pos == stream_length - 1) {
return Status::Invalid(
"invalid IndexedSplit, do not support FallbackSplit in IndexedSplit");
} else {
return Status::Invalid(
fmt::format("invalid IndexedSplit, remaining {} bytes after deserializing",
stream_length - pos));
}
} else if (magic == DataSplitImpl::MAGIC) {
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<DataSplitImpl> data_split,
ReadDataSplitWithoutMagicNumber(magic, &in, pool));
PAIMON_ASSIGN_OR_RAISE(int64_t pos, in.GetPos());
PAIMON_ASSIGN_OR_RAISE(int64_t stream_length, in.Length());
if (pos == stream_length) {
return data_split;
} else if (pos == stream_length - 1) {
PAIMON_ASSIGN_OR_RAISE(bool is_fallback, ReadSplitFlag(&in));
return std::make_shared<FallbackDataSplit>(data_split, is_fallback);
} else {
return Status::Invalid(fmt::format(
"invalid data split byte stream, remaining {} bytes after deserializing",
stream_length - pos));
}
}
return Status::Invalid("invalid split, must be DataSplit or IndexedSplit");
}
} // namespace paimon