blob: cd866609848edeeae434823a2e9e6f75480e549f [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 "paimon/core/io/data_file_meta.h"
#include <algorithm>
#include <cstddef>
#include <utility>
#include "arrow/type_fwd.h"
#include "fmt/format.h"
#include "fmt/ranges.h"
#include "paimon/common/utils/date_time_utils.h"
#include "paimon/common/utils/path_util.h"
#include "paimon/memory/bytes.h"
#include "paimon/status.h"
namespace arrow {
class DataType;
} // namespace arrow
namespace paimon {
const BinaryRow& DataFileMeta::EmptyMinKey() {
static const BinaryRow empty_row = BinaryRow::EmptyRow();
return empty_row;
}
const BinaryRow& DataFileMeta::EmptyMaxKey() {
static const BinaryRow empty_row = BinaryRow::EmptyRow();
return empty_row;
}
Result<std::shared_ptr<DataFileMeta>> DataFileMeta::ForAppend(
const std::string& file_name, int64_t file_size, int64_t row_count,
const SimpleStats& row_stats, int64_t min_sequence_number, int64_t max_sequence_number,
int64_t schema_id, const std::optional<FileSource>& file_source,
const std::optional<std::vector<std::string>>& value_stats_cols,
const std::optional<std::string>& external_path, const std::optional<int64_t>& first_row_id,
const std::optional<std::vector<std::string>>& write_cols) {
return ForAppend(file_name, file_size, row_count, row_stats, min_sequence_number,
max_sequence_number, schema_id, std::vector<std::optional<std::string>>(),
nullptr, file_source, value_stats_cols, external_path, first_row_id,
write_cols);
}
Result<std::shared_ptr<DataFileMeta>> DataFileMeta::ForAppend(
const std::string& file_name, int64_t file_size, int64_t row_count,
const SimpleStats& row_stats, int64_t min_sequence_number, int64_t max_sequence_number,
int64_t schema_id, const std::vector<std::optional<std::string>>& extra_files,
const std::shared_ptr<Bytes>& embedded_index, const std::optional<FileSource>& file_source,
const std::optional<std::vector<std::string>>& value_stats_cols,
const std::optional<std::string>& external_path, const std::optional<int64_t>& first_row_id,
const std::optional<std::vector<std::string>>& write_cols) {
PAIMON_ASSIGN_OR_RAISE(int64_t local_micro, DateTimeUtils::GetCurrentLocalTimeUs());
return std::make_shared<DataFileMeta>(
file_name, file_size, row_count, EmptyMinKey(), EmptyMaxKey(), SimpleStats::EmptyStats(),
row_stats, min_sequence_number, max_sequence_number, schema_id, DUMMY_LEVEL, extra_files,
Timestamp(/*millisecond=*/local_micro / 1000, /*nano_of_millisecond=*/0), 0ll,
embedded_index, file_source, value_stats_cols, external_path, first_row_id, write_cols,
/*column_max_sequence_numbers=*/std::nullopt);
}
Result<std::shared_ptr<DataFileMeta>> DataFileMeta::Upgrade(int32_t new_level) const {
if (new_level <= level) {
return Status::Invalid(
fmt::format("new level {} should be greater than current level {}", new_level, level));
}
return std::make_shared<DataFileMeta>(
file_name, file_size, row_count, min_key, max_key, key_stats, value_stats,
min_sequence_number, max_sequence_number, schema_id, new_level, extra_files, creation_time,
delete_row_count, embedded_index, file_source, value_stats_cols, external_path,
first_row_id, write_cols, column_max_sequence_numbers);
}
std::shared_ptr<DataFileMeta> DataFileMeta::CopyWithExtraFiles(
const std::vector<std::optional<std::string>>& new_extra_files) const {
return std::make_shared<DataFileMeta>(
file_name, file_size, row_count, min_key, max_key, key_stats, value_stats,
min_sequence_number, max_sequence_number, schema_id, level, new_extra_files, creation_time,
delete_row_count, embedded_index, file_source, value_stats_cols, external_path,
first_row_id, write_cols, column_max_sequence_numbers);
}
std::shared_ptr<DataFileMeta> DataFileMeta::CopyWithColumnMaxSequenceNumbers(
const std::optional<std::vector<int64_t>>& new_column_max_sequence_numbers) const {
return std::make_shared<DataFileMeta>(
file_name, file_size, row_count, min_key, max_key, key_stats, value_stats,
min_sequence_number, max_sequence_number, schema_id, level, extra_files, creation_time,
delete_row_count, embedded_index, file_source, value_stats_cols, external_path,
first_row_id, write_cols, new_column_max_sequence_numbers);
}
std::shared_ptr<DataFileMeta> DataFileMeta::CopyWithoutStats() const {
return std::make_shared<DataFileMeta>(
file_name, file_size, row_count, min_key, max_key, key_stats, SimpleStats::EmptyStats(),
min_sequence_number, max_sequence_number, schema_id, level, extra_files, creation_time,
delete_row_count, embedded_index, file_source, std::vector<std::string>(), external_path,
first_row_id, write_cols, column_max_sequence_numbers);
}
DataFileMeta::DataFileMeta(
const std::string& _file_name, int64_t _file_size, int64_t _row_count,
const BinaryRow& _min_key, const BinaryRow& _max_key, const SimpleStats& _key_stats,
const SimpleStats& _value_stats, int64_t _min_sequence_number, int64_t _max_sequence_number,
int64_t _schema_id, int32_t _level, const std::vector<std::optional<std::string>>& _extra_files,
const Timestamp& _creation_time, const std::optional<int64_t>& _delete_row_count,
const std::shared_ptr<Bytes>& _embedded_index, const std::optional<FileSource>& _file_source,
const std::optional<std::vector<std::string>>& _value_stats_cols,
const std::optional<std::string>& _external_path, const std::optional<int64_t>& _first_row_id,
const std::optional<std::vector<std::string>>& _write_cols,
const std::optional<std::vector<int64_t>>& _column_max_sequence_numbers)
: file_name(_file_name),
file_size(_file_size),
row_count(_row_count),
min_key(_min_key),
max_key(_max_key),
key_stats(_key_stats),
value_stats(_value_stats),
min_sequence_number(_min_sequence_number),
max_sequence_number(_max_sequence_number),
schema_id(_schema_id),
level(_level),
extra_files(_extra_files),
creation_time(_creation_time),
delete_row_count(_delete_row_count),
embedded_index(_embedded_index),
file_source(_file_source),
value_stats_cols(_value_stats_cols),
external_path(_external_path),
first_row_id(_first_row_id),
write_cols(_write_cols),
column_max_sequence_numbers(_column_max_sequence_numbers) {}
Result<std::string> DataFileMeta::FileFormat() const {
size_t last_dot_index = file_name.find_last_of(".");
if (last_dot_index == std::string::npos || last_dot_index == file_name.length() - 1) {
return Status::Invalid("cannot find format from file ", file_name);
}
return file_name.substr(last_dot_index + 1);
}
std::optional<std::string> DataFileMeta::ExternalPathDir() const {
if (!external_path) {
return std::nullopt;
}
return PathUtil::GetParentDirPath(external_path.value());
}
Result<int64_t> DataFileMeta::CreationTimeEpochMillis() const {
PAIMON_ASSIGN_OR_RAISE(Timestamp utc_ts, DateTimeUtils::ToUTCTimestamp(creation_time));
return utc_ts.GetMillisecond();
}
Result<std::optional<RoaringBitmap32>> DataFileMeta::ToFileSelection(
const std::optional<std::vector<Range>>& row_ranges) const {
if (!row_ranges) {
return std::optional<RoaringBitmap32>();
}
PAIMON_ASSIGN_OR_RAISE(int64_t start, NonNullFirstRowId());
int64_t end = start + row_count - 1;
Range file_range(start, end);
RoaringBitmap32 selection;
for (const auto& row_range : row_ranges.value()) {
auto intersect_result = Range::Intersection(file_range, row_range);
if (intersect_result) {
selection.AddRange(static_cast<int32_t>(intersect_result.value().from - start),
static_cast<int32_t>(intersect_result.value().to - start + 1));
}
}
if (selection.Cardinality() == row_count) {
// If all rows are selected, do not push down selection bitmap.
return std::optional<RoaringBitmap32>();
}
return std::optional<RoaringBitmap32>(selection);
}
bool DataFileMeta::operator==(const DataFileMeta& other) const {
if (this == &other) {
return true;
}
if ((embedded_index && !other.embedded_index) || (!embedded_index && other.embedded_index)) {
return false;
}
if (embedded_index && other.embedded_index && !(*embedded_index == *other.embedded_index)) {
return false;
}
return file_name == other.file_name && file_size == other.file_size &&
row_count == other.row_count && min_key == other.min_key && max_key == other.max_key &&
key_stats == other.key_stats && value_stats == other.value_stats &&
min_sequence_number == other.min_sequence_number &&
max_sequence_number == other.max_sequence_number && schema_id == other.schema_id &&
level == other.level && extra_files == other.extra_files &&
creation_time == other.creation_time && delete_row_count == other.delete_row_count &&
file_source == other.file_source && value_stats_cols == other.value_stats_cols &&
external_path == other.external_path && first_row_id == other.first_row_id &&
write_cols == other.write_cols &&
column_max_sequence_numbers == other.column_max_sequence_numbers;
}
bool DataFileMeta::operator!=(const DataFileMeta& other) const {
return !(*this == other);
}
bool DataFileMeta::TEST_Equal(const DataFileMeta& other) const {
if (this == &other) {
return true;
}
auto compare_optional_ignore_name = [](const std::optional<std::string>& lhs,
const std::optional<std::string>& rhs) -> bool {
if (lhs != rhs) {
if (lhs == std::nullopt || rhs == std::nullopt) {
return false;
}
}
return true;
};
if (extra_files.size() != other.extra_files.size()) {
return false;
} else {
for (size_t i = 0; i < extra_files.size(); ++i) {
if (!compare_optional_ignore_name(extra_files[i], other.extra_files[i])) {
return false;
}
}
}
if ((embedded_index && !other.embedded_index) || (!embedded_index && other.embedded_index)) {
return false;
}
if (embedded_index && other.embedded_index && !(*embedded_index == *other.embedded_index)) {
return false;
}
// ignore file_name, file_size, extra_files, creation_time and external path
return row_count == other.row_count && min_key == other.min_key && max_key == other.max_key &&
key_stats == other.key_stats && value_stats == other.value_stats &&
min_sequence_number == other.min_sequence_number &&
max_sequence_number == other.max_sequence_number && schema_id == other.schema_id &&
level == other.level && delete_row_count == other.delete_row_count &&
file_source == other.file_source && value_stats_cols == other.value_stats_cols &&
compare_optional_ignore_name(external_path, other.external_path) &&
first_row_id == other.first_row_id && write_cols == other.write_cols &&
column_max_sequence_numbers == other.column_max_sequence_numbers;
}
std::string DataFileMeta::ToString() const {
std::vector<std::string> extra_files_str;
for (const auto& file : extra_files) {
if (file == std::nullopt) {
extra_files_str.emplace_back("null");
} else {
extra_files_str.emplace_back(file.value());
}
}
return fmt::format(
"{{fileName: {}, fileSize: {}, rowCount: {}, embeddedIndex: {}, minKey: {}, maxKey: "
"{}, "
"keyStats: {}, valueStats: {}, minSequenceNumber: {}, maxSequenceNumber: {}, schemaId: "
"{}, level: {}, extraFiles: {}, creationTime: {}, deleteRowCount: {}, fileSource: {}, "
"valueStatsCols: {}, externalPath: {}, firstRowId: {}, writeCols: {}, "
"columnMaxSequenceNumbers: {}}}",
file_name, file_size, row_count,
embedded_index == nullptr ? "null"
: std::string(embedded_index->data(), embedded_index->size()),
min_key.ToString(), max_key.ToString(), key_stats.ToString(), value_stats.ToString(),
min_sequence_number, max_sequence_number, schema_id, level, extra_files_str,
creation_time.ToString(),
delete_row_count == std::nullopt ? "null" : std::to_string(delete_row_count.value()),
file_source == std::nullopt ? "null" : file_source.value().ToString(),
value_stats_cols == std::nullopt
? "null"
: fmt::format("{}", fmt::join(value_stats_cols.value(), ", ")),
external_path == std::nullopt ? "null" : external_path.value(),
first_row_id == std::nullopt ? "null" : std::to_string(first_row_id.value()),
write_cols == std::nullopt ? "null" : fmt::format("{}", write_cols.value()),
column_max_sequence_numbers == std::nullopt
? "null"
: fmt::format("{}", column_max_sequence_numbers.value()));
}
int64_t DataFileMeta::GetMaxSequenceNumber(
const std::vector<std::shared_ptr<DataFileMeta>>& file_metas) {
int64_t ret = -1;
for (const auto& meta : file_metas) {
ret = std::max(ret, meta->max_sequence_number);
}
return ret;
}
const std::shared_ptr<arrow::DataType>& DataFileMeta::DataType() {
static std::shared_ptr<arrow::DataType> schema = arrow::struct_(
{arrow::field("_FILE_NAME", arrow::utf8(), /*nullable=*/false),
arrow::field("_FILE_SIZE", arrow::int64(), /*nullable=*/false),
arrow::field("_ROW_COUNT", arrow::int64(), /*nullable=*/false),
arrow::field("_MIN_KEY", arrow::binary(), /*nullable=*/false),
arrow::field("_MAX_KEY", arrow::binary(), /*nullable=*/false),
arrow::field("_KEY_STATS", SimpleStats::DataType(), /*nullable=*/false),
arrow::field("_VALUE_STATS", SimpleStats::DataType(), /*nullable=*/false),
arrow::field("_MIN_SEQUENCE_NUMBER", arrow::int64(), /*nullable=*/false),
arrow::field("_MAX_SEQUENCE_NUMBER", arrow::int64(), /*nullable=*/false),
arrow::field("_SCHEMA_ID", arrow::int64(), /*nullable=*/false),
arrow::field("_LEVEL", arrow::int32(), /*nullable=*/false),
arrow::field("_EXTRA_FILES",
arrow::list(arrow::field("item", arrow::utf8(), /*nullable=*/false)),
/*nullable=*/false),
arrow::field("_CREATION_TIME", arrow::timestamp(arrow::TimeUnit::MILLI),
/*nullable=*/true),
arrow::field("_DELETE_ROW_COUNT", arrow::int64(), /*nullable=*/true),
arrow::field("_EMBEDDED_FILE_INDEX", arrow::binary(), /*nullable=*/true),
arrow::field("_FILE_SOURCE", arrow::int8(), /*nullable=*/true),
arrow::field("_VALUE_STATS_COLS",
arrow::list(arrow::field("item", arrow::utf8(), /*nullable=*/false)),
/*nullable=*/true),
arrow::field("_EXTERNAL_PATH", arrow::utf8(), /*nullable=*/true),
arrow::field("_FIRST_ROW_ID", arrow::int64(), /*nullable=*/true),
arrow::field("_WRITE_COLS",
arrow::list(arrow::field("item", arrow::utf8(), /*nullable=*/false)),
/*nullable=*/true),
arrow::field("_WRITE_COLS_SEQUENCES",
arrow::list(arrow::field("item", arrow::int64(), /*nullable=*/false)),
/*nullable=*/true)});
return schema;
}
} // namespace paimon