blob: 5afe9938acd19e8cfe61b44c54697bbe638e3966 [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/table/source/table_scan.h"
#include <map>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "fmt/format.h"
#include "paimon/common/predicate/predicate_validator.h"
#include "paimon/common/types/data_field.h"
#include "paimon/common/utils/fields_comparator.h"
#include "paimon/core/core_options.h"
#include "paimon/core/index/index_file_handler.h"
#include "paimon/core/manifest/index_manifest_file.h"
#include "paimon/core/manifest/manifest_file.h"
#include "paimon/core/manifest/manifest_list.h"
#include "paimon/core/operation/append_only_file_store_scan.h"
#include "paimon/core/operation/data_evolution_file_store_scan.h"
#include "paimon/core/operation/file_store_scan.h"
#include "paimon/core/operation/key_value_file_store_scan.h"
#include "paimon/core/schema/schema_manager.h"
#include "paimon/core/schema/schema_validation.h"
#include "paimon/core/schema/table_schema.h"
#include "paimon/core/table/bucket_mode.h"
#include "paimon/core/table/source/abstract_table_scan.h"
#include "paimon/core/table/source/append_only_split_generator.h"
#include "paimon/core/table/source/data_evolution_batch_scan.h"
#include "paimon/core/table/source/data_evolution_split_generator.h"
#include "paimon/core/table/source/data_table_batch_scan.h"
#include "paimon/core/table/source/data_table_stream_scan.h"
#include "paimon/core/table/source/merge_tree_split_generator.h"
#include "paimon/core/table/source/snapshot/snapshot_reader.h"
#include "paimon/core/table/source/split_generator.h"
#include "paimon/core/table/system/system_table.h"
#include "paimon/core/utils/branch_manager.h"
#include "paimon/core/utils/field_mapping.h"
#include "paimon/core/utils/file_store_path_factory.h"
#include "paimon/core/utils/index_file_path_factories.h"
#include "paimon/core/utils/snapshot_manager.h"
#include "paimon/format/file_format.h"
#include "paimon/result.h"
#include "paimon/scan_context.h"
#include "paimon/status.h"
namespace arrow {
class Schema;
} // namespace arrow
namespace paimon {
class Executor;
class MemoryPool;
namespace {
class TableScanImpl {
public:
static Result<std::unique_ptr<FileStoreScan>> CreateFileStoreScan(
const std::shared_ptr<FileStorePathFactory>& path_factory,
const std::shared_ptr<arrow::Schema>& arrow_schema,
const std::shared_ptr<TableSchema>& table_schema, const CoreOptions& core_options,
const std::shared_ptr<Executor>& executor, const std::shared_ptr<MemoryPool>& memory_pool,
const ScanContext* context) {
auto fs = core_options.GetFileSystem();
auto manifest_file_format = core_options.GetManifestFormat();
std::string branch = BranchManager::NormalizeBranch(core_options.GetBranch());
auto snapshot_manager = std::make_shared<SnapshotManager>(fs, context->GetPath(), branch);
// TODO(liancheng.lsz): support fallback branch in scan
auto schema_manager = std::make_shared<SchemaManager>(fs, context->GetPath(), branch);
PAIMON_ASSIGN_OR_RAISE(
std::shared_ptr<ManifestList> manifest_list,
ManifestList::Create(fs, manifest_file_format, core_options.GetManifestCompression(),
path_factory, memory_pool));
PAIMON_ASSIGN_OR_RAISE(
std::shared_ptr<arrow::Schema> partition_schema,
FieldMapping::GetPartitionSchema(arrow_schema, table_schema->PartitionKeys()));
PAIMON_ASSIGN_OR_RAISE(
std::shared_ptr<ManifestFile> manifest_file,
ManifestFile::Create(fs, manifest_file_format, core_options.GetManifestCompression(),
path_factory, core_options.GetManifestTargetFileSize(),
memory_pool, core_options, partition_schema));
if (table_schema->PrimaryKeys().empty()) {
if (core_options.DataEvolutionEnabled()) {
return DataEvolutionFileStoreScan::Create(
snapshot_manager, schema_manager, manifest_list, manifest_file, table_schema,
arrow_schema, context->GetScanFilters(), core_options, executor, memory_pool);
}
return AppendOnlyFileStoreScan::Create(
snapshot_manager, schema_manager, manifest_list, manifest_file, table_schema,
arrow_schema, context->GetScanFilters(), core_options, executor, memory_pool);
}
return KeyValueFileStoreScan::Create(
snapshot_manager, schema_manager, manifest_list, manifest_file, table_schema,
arrow_schema, context->GetScanFilters(), core_options, executor, memory_pool);
}
static Result<std::unique_ptr<SplitGenerator>> CreateSplitGenerator(
const std::shared_ptr<TableSchema>& table_schema, const CoreOptions& core_options,
const ScanContext* context) {
auto source_split_target_size = core_options.GetSourceSplitTargetSize();
auto source_split_open_file_cost = core_options.GetSourceSplitOpenFileCost();
if (table_schema->PrimaryKeys().empty()) {
if (core_options.DataEvolutionEnabled()) {
return std::make_unique<DataEvolutionSplitGenerator>(source_split_target_size,
source_split_open_file_cost);
}
BucketMode bucket_mode = (core_options.GetBucket() == -1 ? BucketMode::BUCKET_UNAWARE
: BucketMode::HASH_FIXED);
return std::make_unique<AppendOnlySplitGenerator>(
source_split_target_size, source_split_open_file_cost, bucket_mode);
} else {
// TODO(liancheng.lsz): support evolution
PAIMON_ASSIGN_OR_RAISE(std::vector<std::string> trimmed_primary_keys,
table_schema->TrimmedPrimaryKeys());
PAIMON_ASSIGN_OR_RAISE(std::vector<DataField> trimmed_pk_fields,
table_schema->GetFields(trimmed_primary_keys));
PAIMON_ASSIGN_OR_RAISE(
std::shared_ptr<FieldsComparator> key_comparator,
FieldsComparator::Create(trimmed_pk_fields, /*is_ascending_order=*/true));
return std::make_unique<MergeTreeSplitGenerator>(
source_split_target_size, source_split_open_file_cost,
core_options.DeletionVectorsEnabled(), core_options.GetMergeEngine(),
key_comparator);
}
}
static Result<std::unique_ptr<IndexFileHandler>> CreateIndexFileHandler(
const CoreOptions& core_options, const std::shared_ptr<FileStorePathFactory>& path_factory,
const std::shared_ptr<MemoryPool>& memory_pool) {
PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<IndexManifestFile> index_manifest_file,
IndexManifestFile::Create(
core_options.GetFileSystem(), core_options.GetManifestFormat(),
core_options.GetManifestCompression(), path_factory,
core_options.GetBucket(), memory_pool, core_options));
return std::make_unique<IndexFileHandler>(
core_options.GetFileSystem(), std::move(index_manifest_file),
std::make_shared<IndexFilePathFactories>(path_factory),
core_options.DeletionVectorsBitmap64(), memory_pool);
}
};
Result<std::unique_ptr<TableScan>> NewDataTableScan(const std::shared_ptr<ScanContext>& context);
} // namespace
Result<std::unique_ptr<TableScan>> TableScan::Create(std::unique_ptr<ScanContext> context) {
if (context == nullptr) {
return Status::Invalid("scan context is null pointer");
}
if (context->GetMemoryPool() == nullptr) {
return Status::Invalid("memory pool is null pointer");
}
if (context->GetExecutor() == nullptr) {
return Status::Invalid("executor is null pointer");
}
std::shared_ptr<ScanContext> shared_context = std::move(context);
// load schema
PAIMON_ASSIGN_OR_RAISE(CoreOptions tmp_options,
CoreOptions::FromMap(shared_context->GetOptions(),
shared_context->GetSpecificFileSystem()));
PAIMON_ASSIGN_OR_RAISE(std::optional<SystemTablePath> system_table_path,
SystemTableLoader::TryParsePath(shared_context->GetPath()));
if (system_table_path) {
PAIMON_ASSIGN_OR_RAISE(
std::shared_ptr<SystemTable> system_table,
SystemTableLoader::LoadFromPath(tmp_options.GetFileSystem(), shared_context->GetPath(),
shared_context->GetOptions()));
return system_table->NewScan(shared_context);
}
return NewDataTableScan(shared_context);
}
namespace {
Result<std::unique_ptr<TableScan>> NewDataTableScan(const std::shared_ptr<ScanContext>& context) {
PAIMON_ASSIGN_OR_RAISE(
CoreOptions tmp_options,
CoreOptions::FromMap(context->GetOptions(), context->GetSpecificFileSystem()));
std::string branch = BranchManager::NormalizeBranch(tmp_options.GetBranch());
SchemaManager schema_manager(tmp_options.GetFileSystem(), context->GetPath(), branch);
PAIMON_ASSIGN_OR_RAISE(std::optional<std::shared_ptr<TableSchema>> latest_table_schema,
schema_manager.Latest());
if (latest_table_schema == std::nullopt) {
return Status::Invalid("not found latest schema");
}
const auto& table_schema = latest_table_schema.value();
if (table_schema->Id() != TableSchema::FIRST_SCHEMA_ID &&
!table_schema->PrimaryKeys().empty()) {
return Status::NotImplemented(
"do not support schema evolution in pk table while scan process");
}
// merge options
auto options = table_schema->Options();
for (const auto& [key, value] : context->GetOptions()) {
options[key] = value;
}
PAIMON_ASSIGN_OR_RAISE(CoreOptions core_options,
CoreOptions::FromMap(options, context->GetSpecificFileSystem()));
// validate options
if (core_options.GetBucket() == -1) {
if (!table_schema->PrimaryKeys().empty()) {
return Status::NotImplemented(fmt::format(
"do not support pk table bucket={} in scan process", core_options.GetBucket()));
}
} else if (core_options.GetBucket() < 1 &&
!SchemaValidation::IsPostponeBucketTable(*table_schema, core_options.GetBucket())) {
return Status::Invalid(
fmt::format("do not support bucket={} in scan process", core_options.GetBucket()));
}
// validate schema and scan filter
auto arrow_schema = DataField::ConvertDataFieldsToArrowSchema(table_schema->Fields());
if (context->GetScanFilters() && context->GetScanFilters()->GetPredicate()) {
PAIMON_RETURN_NOT_OK(PredicateValidator::ValidatePredicateWithSchema(
*arrow_schema, context->GetScanFilters()->GetPredicate(),
/*validate_field_idx=*/false));
PAIMON_RETURN_NOT_OK(PredicateValidator::ValidatePredicateWithLiterals(
context->GetScanFilters()->GetPredicate()));
}
PAIMON_ASSIGN_OR_RAISE(std::vector<std::string> external_paths,
core_options.CreateExternalPaths());
PAIMON_ASSIGN_OR_RAISE(std::optional<std::string> global_index_external_path,
core_options.CreateGlobalIndexExternalPath());
PAIMON_ASSIGN_OR_RAISE(
std::shared_ptr<FileStorePathFactory> path_factory,
FileStorePathFactory::Create(
context->GetPath(), arrow_schema, table_schema->PartitionKeys(),
core_options.GetPartitionDefaultName(), core_options.GetFileFormat()->Identifier(),
core_options.DataFilePrefix(), core_options.LegacyPartitionNameEnabled(),
external_paths, global_index_external_path, core_options.IndexFileInDataFileDir(),
context->GetMemoryPool()));
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<FileStoreScan> file_store_scan,
TableScanImpl::CreateFileStoreScan(
path_factory, arrow_schema, table_schema, core_options,
context->GetExecutor(), context->GetMemoryPool(), context.get()));
PAIMON_ASSIGN_OR_RAISE(
std::unique_ptr<SplitGenerator> split_generator,
TableScanImpl::CreateSplitGenerator(table_schema, core_options, context.get()));
PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<IndexFileHandler> index_file_handler,
TableScanImpl::CreateIndexFileHandler(core_options, path_factory,
context->GetMemoryPool()));
auto snapshot_reader = std::make_shared<SnapshotReader>(
file_store_scan, path_factory, std::move(split_generator), std::move(index_file_handler));
if (context->IsStreamingMode()) {
return std::make_unique<DataTableStreamScan>(core_options, snapshot_reader);
}
auto batch_scan =
std::make_unique<DataTableBatchScan>(/*pk_table=*/!table_schema->PrimaryKeys().empty(),
core_options, snapshot_reader, context->GetLimit());
if (!core_options.DataEvolutionEnabled()) {
return batch_scan;
}
return std::make_unique<DataEvolutionBatchScan>(
context->GetPath(), snapshot_reader, std::move(batch_scan), context->GetGlobalIndexResult(),
core_options, context->GetMemoryPool(), context->GetExecutor());
}
} // namespace
} // namespace paimon