blob: 007df3cb113605a36a18aaac6164a4778b350728 [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 <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/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/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/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 {
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, in->ReadValue<bool>());
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, in->ReadValue<bool>());
bool raw_convertible = false;
PAIMON_ASSIGN_OR_RAISE(raw_convertible, in->ReadValue<bool>());
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();
}
} // 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 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)) {
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 (const auto& score : scores) {
out.WriteValue<float>(score);
}
} else {
out.WriteValue<bool>(false);
}
} 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 == IndexedSplitImpl::MAGIC) {
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, in.ReadValue<int32_t>());
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, in.ReadValue<bool>());
if (has_scores) {
PAIMON_ASSIGN_OR_RAISE(int32_t scores_length, in.ReadValue<int32_t>());
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;
}
}
// 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 std::make_shared<IndexedSplitImpl>(data_split, row_ranges, scores);
} 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, in.ReadValue<bool>());
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