diff --git a/include/paimon/catalog/catalog.h b/include/paimon/catalog/catalog.h index 0ff9349bd..745756b71 100644 --- a/include/paimon/catalog/catalog.h +++ b/include/paimon/catalog/catalog.h @@ -183,6 +183,11 @@ class PAIMON_EXPORT Catalog { /// @return A shared pointer to the file system instance. virtual std::shared_ptr GetFileSystem() const = 0; + /// Returns the catalog-level options that were passed during catalog creation. + /// + /// @return A const reference to the map of catalog options (key-value pairs). + virtual const std::map& GetOptions() const = 0; + /// Loads the latest schema of a specified table. /// /// @note System tables will not be supported. diff --git a/src/paimon/CMakeLists.txt b/src/paimon/CMakeLists.txt index 68678ee9f..15bb330bf 100644 --- a/src/paimon/CMakeLists.txt +++ b/src/paimon/CMakeLists.txt @@ -328,6 +328,7 @@ set(PAIMON_CORE_SRCS core/table/source/data_evolution_batch_scan.cpp core/table/system/audit_log_system_table.cpp core/table/system/binlog_system_table.cpp + core/table/system/global_system_tables.cpp core/table/system/in_memory_system_table.cpp core/table/system/metadata_system_tables.cpp core/table/system/system_table.cpp diff --git a/src/paimon/core/catalog/catalog.cpp b/src/paimon/core/catalog/catalog.cpp index 08b879c7a..80f93cb29 100644 --- a/src/paimon/core/catalog/catalog.cpp +++ b/src/paimon/core/catalog/catalog.cpp @@ -32,7 +32,7 @@ Result> Catalog::Create(const std::string& root_path, const std::map& options, const std::shared_ptr& file_system) { PAIMON_ASSIGN_OR_RAISE(CoreOptions core_options, CoreOptions::FromMap(options, file_system)); - return std::make_unique(core_options.GetFileSystem(), root_path); + return std::make_unique(core_options.GetFileSystem(), root_path, options); } } // namespace paimon diff --git a/src/paimon/core/catalog/file_system_catalog.cpp b/src/paimon/core/catalog/file_system_catalog.cpp index db9c2494e..eab4ae666 100644 --- a/src/paimon/core/catalog/file_system_catalog.cpp +++ b/src/paimon/core/catalog/file_system_catalog.cpp @@ -31,6 +31,7 @@ #include "paimon/common/utils/string_utils.h" #include "paimon/core/core_options.h" #include "paimon/core/snapshot.h" +#include "paimon/core/table/system/global_system_tables.h" #include "paimon/core/table/system/system_table.h" #include "paimon/core/table/system/system_table_schema.h" #include "paimon/core/utils/branch_manager.h" @@ -47,8 +48,12 @@ struct ArrowSchema; namespace paimon { FileSystemCatalog::FileSystemCatalog(const std::shared_ptr& fs, - const std::string& warehouse) - : fs_(fs), warehouse_(warehouse), logger_(Logger::GetLogger("FileSystemCatalog")) {} + const std::string& warehouse, + const std::map& catalog_options) + : fs_(fs), + warehouse_(warehouse), + catalog_options_(catalog_options), + logger_(Logger::GetLogger("FileSystemCatalog")) {} Status FileSystemCatalog::CreateDatabase(const std::string& db_name, const std::map& options, @@ -88,13 +93,16 @@ Status FileSystemCatalog::CreateDatabaseImpl(const std::string& db_name, Result FileSystemCatalog::DatabaseExists(const std::string& db_name) const { if (IsSystemDatabase(db_name)) { - return Status::NotImplemented( - "do not support checking DatabaseExists for system database."); + return true; } return fs_->Exists(NewDatabasePath(warehouse_, db_name)); } Result FileSystemCatalog::TableExists(const Identifier& identifier) const { + // Handle sys database global tables + if (IsSystemDatabase(identifier.GetDatabaseName())) { + return GlobalSystemTableLoader::IsSupported(identifier.GetTableName()); + } PAIMON_ASSIGN_OR_RAISE(bool is_system_table, identifier.IsSystemTable()); if (is_system_table) { PAIMON_ASSIGN_OR_RAISE(std::optional system_table_name, @@ -184,6 +192,10 @@ std::shared_ptr FileSystemCatalog::GetFileSystem() const { return fs_; } +const std::map& FileSystemCatalog::GetOptions() const { + return catalog_options_; +} + bool FileSystemCatalog::IsSystemDatabase(const std::string& db_name) { return db_name == SYSTEM_DATABASE_NAME; } @@ -228,7 +240,7 @@ Result> FileSystemCatalog::ListDatabases() const { Result> FileSystemCatalog::ListTables(const std::string& db_name) const { if (IsSystemDatabase(db_name)) { - return Status::NotImplemented("do not support listing tables for system database."); + return GlobalSystemTableLoader::GetSupportedTableNames(); } std::string database_path = NewDatabasePath(warehouse_, db_name); std::vector> file_status_list; @@ -261,6 +273,23 @@ Result FileSystemCatalog::TableExistsInFileSystem(const std::string& table Result> FileSystemCatalog::LoadTableSchema( const Identifier& identifier) const { + // Handle sys database global tables + if (IsSystemDatabase(identifier.GetDatabaseName())) { + if (!GlobalSystemTableLoader::IsSupported(identifier.GetTableName())) { + return Status::NotExist(fmt::format("{} not exist", identifier.ToString())); + } + GlobalSystemTableContext context; + context.catalog = const_cast(this); + context.fs = fs_; + context.warehouse = warehouse_; + context.catalog_options = catalog_options_; + PAIMON_ASSIGN_OR_RAISE( + std::shared_ptr system_table, + GlobalSystemTableLoader::Load(identifier.GetTableName(), context)); + PAIMON_ASSIGN_OR_RAISE(std::shared_ptr arrow_schema, + system_table->ArrowSchema()); + return std::make_shared(std::move(arrow_schema)); + } PAIMON_ASSIGN_OR_RAISE(bool is_system_table, identifier.IsSystemTable()); if (is_system_table) { PAIMON_ASSIGN_OR_RAISE(std::optional system_table_name, diff --git a/src/paimon/core/catalog/file_system_catalog.h b/src/paimon/core/catalog/file_system_catalog.h index b9fbc2b47..2a4656cc1 100644 --- a/src/paimon/core/catalog/file_system_catalog.h +++ b/src/paimon/core/catalog/file_system_catalog.h @@ -38,7 +38,8 @@ class Logger; class FileSystemCatalog : public Catalog { public: - FileSystemCatalog(const std::shared_ptr& fs, const std::string& warehouse); + FileSystemCatalog(const std::shared_ptr& fs, const std::string& warehouse, + const std::map& catalog_options = {}); Status CreateDatabase(const std::string& db_name, const std::map& options, @@ -61,6 +62,7 @@ class FileSystemCatalog : public Catalog { Result> LoadTableSchema(const Identifier& identifier) const override; std::string GetRootPath() const override; std::shared_ptr GetFileSystem() const override; + const std::map& GetOptions() const override; Result> GetTable(const Identifier& identifier) const override; Result> ListSnapshots(const Identifier& identifier, const std::string& branch) const override; @@ -92,6 +94,7 @@ class FileSystemCatalog : public Catalog { std::shared_ptr fs_; std::string warehouse_; + std::map catalog_options_; std::shared_ptr logger_; }; diff --git a/src/paimon/core/catalog/file_system_catalog_test.cpp b/src/paimon/core/catalog/file_system_catalog_test.cpp index b69663caa..a15e1b86c 100644 --- a/src/paimon/core/catalog/file_system_catalog_test.cpp +++ b/src/paimon/core/catalog/file_system_catalog_test.cpp @@ -16,6 +16,8 @@ #include "paimon/core/catalog/file_system_catalog.h" +#include + #include "arrow/api.h" #include "arrow/c/abi.h" #include "arrow/c/bridge.h" @@ -557,8 +559,16 @@ TEST(FileSystemCatalogTest, TestInvalidList) { auto dir = UniqueTestDirectory::Create(); ASSERT_TRUE(dir); FileSystemCatalog catalog(core_options.GetFileSystem(), dir->Str()); - ASSERT_NOK_WITH_MSG(catalog.ListTables("sys"), - "do not support listing tables for system database."); + ASSERT_OK_AND_ASSIGN(auto sys_tables, catalog.ListTables("sys")); + ASSERT_FALSE(sys_tables.empty()); + // Verify expected global system table names are present + ASSERT_TRUE(std::find(sys_tables.begin(), sys_tables.end(), "catalog_options") != + sys_tables.end()); + ASSERT_TRUE(std::find(sys_tables.begin(), sys_tables.end(), "all_table_options") != + sys_tables.end()); + ASSERT_TRUE(std::find(sys_tables.begin(), sys_tables.end(), "tables") != sys_tables.end()); + ASSERT_TRUE(std::find(sys_tables.begin(), sys_tables.end(), "partitions") != + sys_tables.end()); } TEST(FileSystemCatalogTest, TestValidateTableSchema) { diff --git a/src/paimon/core/table/system/global_system_tables.cpp b/src/paimon/core/table/system/global_system_tables.cpp new file mode 100644 index 000000000..4da161a30 --- /dev/null +++ b/src/paimon/core/table/system/global_system_tables.cpp @@ -0,0 +1,354 @@ +/* + * Copyright 2026-present Alibaba Inc. + * + * Licensed 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/table/system/global_system_tables.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "arrow/api.h" +#include "paimon/catalog/catalog.h" +#include "paimon/catalog/identifier.h" +#include "paimon/common/data/binary_string.h" +#include "paimon/common/data/generic_row.h" +#include "paimon/common/utils/string_utils.h" +#include "paimon/common/utils/path_util.h" +#include "paimon/core/core_options.h" +#include "paimon/defs.h" +#include "paimon/core/schema/schema_manager.h" +#include "paimon/core/schema/table_schema.h" +#include "paimon/core/snapshot.h" +#include "paimon/core/utils/branch_manager.h" +#include "paimon/core/utils/snapshot_manager.h" +#include "paimon/memory/memory_pool.h" +#include "paimon/status.h" + +namespace paimon { +namespace { + +// ============================================================================= +// Registry +// ============================================================================= + +using GlobalSystemTableFactory = + std::function>(const GlobalSystemTableContext&)>; + +struct GlobalSystemTableRegistryEntry { + std::string name; + GlobalSystemTableFactory factory; +}; + +const std::vector& GlobalSystemTableRegistry() { + static const std::vector registry = { + {CatalogOptionsSystemTable::kName, + [](const GlobalSystemTableContext& ctx) -> Result> { + return std::make_shared(ctx); + }}, + {AllTableOptionsSystemTable::kName, + [](const GlobalSystemTableContext& ctx) -> Result> { + return std::make_shared(ctx); + }}, + {TablesSystemTable::kName, + [](const GlobalSystemTableContext& ctx) -> Result> { + return std::make_shared(ctx); + }}, + {PartitionsSystemTable::kName, + [](const GlobalSystemTableContext& ctx) -> Result> { + return std::make_shared(ctx); + }}, + }; + return registry; +} + +// ============================================================================= +// Helpers for sys.tables and sys.partitions +// ============================================================================= + +VariantType StringValue(const std::string& value) { + return BinaryString::FromString(value, GetDefaultPool().get()); +} + +} // namespace + +// ============================================================================= +// GlobalSystemTableLoader +// ============================================================================= + +bool GlobalSystemTableLoader::IsSupported(const std::string& table_name) { + std::string normalized = StringUtils::ToLowerCase(table_name); + for (const auto& entry : GlobalSystemTableRegistry()) { + if (entry.name == normalized) { + return true; + } + } + return false; +} + +Result> GlobalSystemTableLoader::Load( + const std::string& table_name, const GlobalSystemTableContext& context) { + std::string normalized = StringUtils::ToLowerCase(table_name); + for (const auto& entry : GlobalSystemTableRegistry()) { + if (entry.name == normalized) { + return entry.factory(context); + } + } + return Status::NotImplemented("unsupported global system table: ", table_name); +} + +std::vector GlobalSystemTableLoader::GetSupportedTableNames() { + std::vector names; + names.reserve(GlobalSystemTableRegistry().size()); + for (const auto& entry : GlobalSystemTableRegistry()) { + names.push_back(entry.name); + } + return names; +} + +// ============================================================================= +// sys.catalog_options +// ============================================================================= + +CatalogOptionsSystemTable::CatalogOptionsSystemTable(GlobalSystemTableContext context) + : InMemorySystemTable("sys/catalog_options"), context_(std::move(context)) {} + +std::string CatalogOptionsSystemTable::Name() const { + return kName; +} + +Result> CatalogOptionsSystemTable::ArrowSchema() const { + return arrow::schema({ + arrow::field("key", arrow::utf8(), /*nullable=*/false), + arrow::field("value", arrow::utf8(), /*nullable=*/false), + }); +} + +Result> CatalogOptionsSystemTable::BuildRows() const { + PAIMON_ASSIGN_OR_RAISE(std::shared_ptr schema, ArrowSchema()); + std::vector rows; + rows.reserve(context_.catalog_options.size()); + for (const auto& [key, value] : context_.catalog_options) { + GenericRow row(schema->num_fields()); + row.SetField(0, std::string_view(key)); + row.SetField(1, std::string_view(value)); + rows.push_back(std::move(row)); + } + return rows; +} + +// ============================================================================= +// sys.all_table_options +// ============================================================================= + +AllTableOptionsSystemTable::AllTableOptionsSystemTable(GlobalSystemTableContext context) + : InMemorySystemTable("sys/all_table_options"), context_(std::move(context)) {} + +std::string AllTableOptionsSystemTable::Name() const { + return kName; +} + +Result> AllTableOptionsSystemTable::ArrowSchema() const { + return arrow::schema({ + arrow::field("database_name", arrow::utf8(), /*nullable=*/false), + arrow::field("table_name", arrow::utf8(), /*nullable=*/false), + arrow::field("key", arrow::utf8(), /*nullable=*/false), + arrow::field("value", arrow::utf8(), /*nullable=*/false), + }); +} + +Result> AllTableOptionsSystemTable::BuildRows() const { + PAIMON_ASSIGN_OR_RAISE(std::shared_ptr schema, ArrowSchema()); + std::vector rows; + + PAIMON_ASSIGN_OR_RAISE(std::vector databases, + context_.catalog->ListDatabases()); + for (const auto& db : databases) { + PAIMON_ASSIGN_OR_RAISE(std::vector tables, + context_.catalog->ListTables(db)); + for (const auto& table : tables) { + Identifier id(db, table); + auto schema_result = context_.catalog->LoadTableSchema(id); + if (!schema_result.ok()) { + continue; // skip tables with errors (e.g. dropped concurrently) + } + auto schema_ptr = schema_result.value(); + auto data_schema = std::dynamic_pointer_cast(schema_ptr); + if (!data_schema) { + continue; + } + for (const auto& [key, value] : data_schema->Options()) { + GenericRow row(schema->num_fields()); + row.SetField(0, std::string_view(db)); + row.SetField(1, std::string_view(table)); + row.SetField(2, std::string_view(key)); + row.SetField(3, std::string_view(value)); + rows.push_back(std::move(row)); + } + } + } + return rows; +} + +// ============================================================================= +// sys.tables +// ============================================================================= + +TablesSystemTable::TablesSystemTable(GlobalSystemTableContext context) + : InMemorySystemTable("sys/tables"), context_(std::move(context)) {} + +std::string TablesSystemTable::Name() const { + return kName; +} + +Result> TablesSystemTable::ArrowSchema() const { + return arrow::schema({ + arrow::field("database_name", arrow::utf8(), /*nullable=*/false), + arrow::field("table_name", arrow::utf8(), /*nullable=*/false), + arrow::field("table_type", arrow::utf8(), /*nullable=*/false), + arrow::field("partitioned", arrow::boolean(), /*nullable=*/false), + arrow::field("primary_key", arrow::utf8(), /*nullable=*/false), + arrow::field("record_count", arrow::int64(), /*nullable=*/true), + arrow::field("file_size_in_bytes", arrow::int64(), /*nullable=*/true), + arrow::field("file_count", arrow::int64(), /*nullable=*/true), + arrow::field("last_file_creation_time", + arrow::timestamp(arrow::TimeUnit::MILLI), /*nullable=*/true), + }); +} + +Result> TablesSystemTable::BuildRows() const { + PAIMON_ASSIGN_OR_RAISE(std::shared_ptr schema, ArrowSchema()); + std::vector rows; + + PAIMON_ASSIGN_OR_RAISE(std::vector databases, + context_.catalog->ListDatabases()); + for (const auto& db : databases) { + PAIMON_ASSIGN_OR_RAISE(std::vector tables, + context_.catalog->ListTables(db)); + for (const auto& table : tables) { + Identifier id(db, table); + auto schema_result = context_.catalog->LoadTableSchema(id); + if (!schema_result.ok()) { + continue; + } + auto schema_ptr = schema_result.value(); + auto data_schema = std::dynamic_pointer_cast(schema_ptr); + if (!data_schema) { + continue; + } + + // Determine table type: EXTERNAL if data-file.external-paths is set + std::string table_type_str = "MANAGED"; + const auto& opts = data_schema->Options(); + if (opts.find(Options::DATA_FILE_EXTERNAL_PATHS) != opts.end()) { + table_type_str = "EXTERNAL"; + } + + bool partitioned = !data_schema->PartitionKeys().empty(); + std::string primary_keys_str; + const auto& pks = data_schema->PrimaryKeys(); + for (size_t i = 0; i < pks.size(); ++i) { + if (i > 0) primary_keys_str += ","; + primary_keys_str += pks[i]; + } + + GenericRow row(schema->num_fields()); + row.SetField(0, std::string_view(db)); + row.SetField(1, std::string_view(table)); + row.SetField(2, StringValue(table_type_str)); + row.SetField(3, partitioned); + row.SetField(4, primary_keys_str.empty() + ? VariantType(NullType()) + : VariantType(StringValue(primary_keys_str))); + + // Try to get stats from latest snapshot + PAIMON_ASSIGN_OR_RAISE(std::string table_path, + context_.catalog->GetTableLocation(id)); + SnapshotManager snapshot_manager(context_.fs, table_path, + BranchManager::DEFAULT_MAIN_BRANCH); + auto snapshot_result = snapshot_manager.LatestSnapshot(); + if (snapshot_result.ok() && snapshot_result.value()) { + const auto& snapshot = *snapshot_result.value(); + auto total_count = snapshot.TotalRecordCount(); + row.SetField(5, total_count ? VariantType(total_count.value()) + : VariantType(NullType())); + // TODO(suxiaogang223): Populate file_size_in_bytes, file_count, and + // last_file_creation_time by reading manifest entries. This requires + // the manifest reading infrastructure from the files/manifests system + // tables PR (codex/system-table-files-manifests-pr4). + row.SetField(6, NullType()); + row.SetField(7, NullType()); + row.SetField(8, NullType()); + } else { + row.SetField(5, NullType()); + row.SetField(6, NullType()); + row.SetField(7, NullType()); + row.SetField(8, NullType()); + } + + rows.push_back(std::move(row)); + } + } + return rows; +} + +// ============================================================================= +// sys.partitions +// ============================================================================= + +PartitionsSystemTable::PartitionsSystemTable(GlobalSystemTableContext context) + : InMemorySystemTable("sys/partitions"), context_(std::move(context)) {} + +std::string PartitionsSystemTable::Name() const { + return kName; +} + +Result> PartitionsSystemTable::ArrowSchema() const { + return arrow::schema({ + arrow::field("database_name", arrow::utf8(), /*nullable=*/false), + arrow::field("table_name", arrow::utf8(), /*nullable=*/false), + arrow::field("partition_name", arrow::utf8(), /*nullable=*/true), + arrow::field("record_count", arrow::int64(), /*nullable=*/true), + arrow::field("file_size_in_bytes", arrow::int64(), /*nullable=*/true), + arrow::field("file_count", arrow::int64(), /*nullable=*/true), + arrow::field("last_update_time", + arrow::timestamp(arrow::TimeUnit::MILLI), /*nullable=*/true), + }); +} + +Result> PartitionsSystemTable::BuildRows() const { + PAIMON_ASSIGN_OR_RAISE(std::shared_ptr schema, ArrowSchema()); + std::vector rows; + + // TODO(suxiaogang223): Implement partition-level aggregation using + // manifest entry reading (similar to FilesSystemTable::BuildRows() + // but grouped by partition). For now, return empty result set. + // + // The implementation should: + // 1. Enumerate all databases and tables + // 2. For each partitioned table, read latest snapshot's manifest entries + // 3. Group DataFileMeta entries by entry.Partition() + // 4. Aggregate: sum(file_size), sum(record_count), count files, + // max(creation_time) + + return rows; +} + +} // namespace paimon diff --git a/src/paimon/core/table/system/global_system_tables.h b/src/paimon/core/table/system/global_system_tables.h new file mode 100644 index 000000000..b3dc37570 --- /dev/null +++ b/src/paimon/core/table/system/global_system_tables.h @@ -0,0 +1,117 @@ +/* + * Copyright 2026-present Alibaba Inc. + * + * Licensed 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. + */ + +#pragma once + +#include +#include +#include +#include + +#include "paimon/core/table/system/in_memory_system_table.h" + +namespace paimon { +class Catalog; +class FileSystem; + +/// Context passed to global system table constructors, providing catalog-level +/// access for enumerating databases, tables, and reading metadata. +struct GlobalSystemTableContext { + Catalog* catalog; // non-owning pointer + std::shared_ptr fs; + std::string warehouse; + std::map catalog_options; +}; + +/// System table for `sys.catalog_options`, exposing catalog-level configuration +/// as key/value rows. +class CatalogOptionsSystemTable : public InMemorySystemTable { + public: + static constexpr const char* kName = "catalog_options"; + + explicit CatalogOptionsSystemTable(GlobalSystemTableContext context); + + std::string Name() const override; + Result> ArrowSchema() const override; + Result> BuildRows() const override; + + private: + GlobalSystemTableContext context_; +}; + +/// System table for `sys.all_table_options`, exposing all table options across +/// all databases as (database_name, table_name, key, value) rows. +class AllTableOptionsSystemTable : public InMemorySystemTable { + public: + static constexpr const char* kName = "all_table_options"; + + explicit AllTableOptionsSystemTable(GlobalSystemTableContext context); + + std::string Name() const override; + Result> ArrowSchema() const override; + Result> BuildRows() const override; + + private: + GlobalSystemTableContext context_; +}; + +/// System table for `sys.tables`, exposing metadata for all tables across all +/// databases including record counts and file statistics. +class TablesSystemTable : public InMemorySystemTable { + public: + static constexpr const char* kName = "tables"; + + explicit TablesSystemTable(GlobalSystemTableContext context); + + std::string Name() const override; + Result> ArrowSchema() const override; + Result> BuildRows() const override; + + private: + GlobalSystemTableContext context_; +}; + +/// System table for `sys.partitions`, exposing partition-level file statistics +/// for all tables across all databases. +class PartitionsSystemTable : public InMemorySystemTable { + public: + static constexpr const char* kName = "partitions"; + + explicit PartitionsSystemTable(GlobalSystemTableContext context); + + std::string Name() const override; + Result> ArrowSchema() const override; + Result> BuildRows() const override; + + private: + GlobalSystemTableContext context_; +}; + +/// Loader for global system tables under the `sys` database. +/// +/// Maintains its own registry with a factory signature that receives a +/// GlobalSystemTableContext instead of a per-table TableSchema. +class GlobalSystemTableLoader { + public: + static bool IsSupported(const std::string& table_name); + + static Result> Load( + const std::string& table_name, const GlobalSystemTableContext& context); + + static std::vector GetSupportedTableNames(); +}; + +} // namespace paimon diff --git a/src/paimon/core/table/system/system_table.cpp b/src/paimon/core/table/system/system_table.cpp index cc176cd5e..0d58d5eb3 100644 --- a/src/paimon/core/table/system/system_table.cpp +++ b/src/paimon/core/table/system/system_table.cpp @@ -30,6 +30,7 @@ #include "paimon/core/schema/table_schema.h" #include "paimon/core/table/system/audit_log_system_table.h" #include "paimon/core/table/system/binlog_system_table.h" +#include "paimon/core/table/system/global_system_tables.h" #include "paimon/core/table/system/metadata_system_tables.h" #include "paimon/core/utils/branch_manager.h" #include "paimon/status.h" @@ -159,6 +160,17 @@ Result> SystemTableLoader::Load( Result> SystemTableLoader::TryParsePath(const std::string& path) { std::string table_name = PathUtil::GetName(path); + std::string parent = PathUtil::GetParentDirPath(path); + std::string parent_name = PathUtil::GetName(parent); + + // Detect global system table paths: /sys/ + if (parent_name == "sys") { + SystemTablePath system_table_path; + system_table_path.is_global = true; + system_table_path.system_table_name = table_name; + return std::optional(std::move(system_table_path)); + } + Identifier identifier(table_name); PAIMON_ASSIGN_OR_RAISE(bool is_system_table, identifier.IsSystemTable()); if (!is_system_table) { @@ -168,7 +180,6 @@ Result> SystemTableLoader::TryParsePath(const std PAIMON_ASSIGN_OR_RAISE(std::optional branch, identifier.GetBranchName()); PAIMON_ASSIGN_OR_RAISE(std::optional system_table_name, identifier.GetSystemTableName()); - std::string parent = PathUtil::GetParentDirPath(path); SystemTablePath system_table_path; system_table_path.table_path = PathUtil::JoinPath(parent, data_table_name); system_table_path.branch = std::move(branch); @@ -184,6 +195,21 @@ Result> SystemTableLoader::LoadFromPath( return Status::Invalid("path is not a system table path: ", path); } const auto& parsed = system_table_path.value(); + + // Handle global system tables (under sys/ directory) + if (parsed.is_global) { + GlobalSystemTableContext context; + context.fs = fs; + // The warehouse is the grandparent of the sys/ path + context.warehouse = PathUtil::GetParentDirPath(PathUtil::GetParentDirPath(path)); + context.catalog_options = dynamic_options; + // Note: context.catalog is intentionally left as nullptr here. + // Global tables loaded from path do not have a Catalog reference and + // cannot enumerate databases/tables. Only tables that don't require + // catalog enumeration (e.g. catalog_options) will work in this path. + return GlobalSystemTableLoader::Load(parsed.system_table_name, context); + } + SchemaManager schema_manager(fs, parsed.table_path, parsed.branch.value_or(BranchManager::DEFAULT_MAIN_BRANCH)); PAIMON_ASSIGN_OR_RAISE(std::optional> latest_schema, diff --git a/src/paimon/core/table/system/system_table.h b/src/paimon/core/table/system/system_table.h index 5db20789e..3f897460c 100644 --- a/src/paimon/core/table/system/system_table.h +++ b/src/paimon/core/table/system/system_table.h @@ -42,6 +42,8 @@ struct SystemTablePath { std::optional branch; /// System table name, for example `options` or `snapshots`. std::string system_table_name; + /// Whether this is a global system table under the `sys` database. + bool is_global = false; }; /// Base interface for table-scoped system tables such as `T$options` and `T$snapshots`.