diff --git a/src/iceberg/CMakeLists.txt b/src/iceberg/CMakeLists.txt index 18cf70bdb..f5a9b986c 100644 --- a/src/iceberg/CMakeLists.txt +++ b/src/iceberg/CMakeLists.txt @@ -41,6 +41,9 @@ set(ICEBERG_SOURCES file_io_registry.cc file_reader.cc file_writer.cc + inspect/history_table.cc + inspect/metadata_table.cc + inspect/snapshots_table.cc inheritable_metadata.cc json_serde.cc location_provider.cc diff --git a/src/iceberg/inspect/CMakeLists.txt b/src/iceberg/inspect/CMakeLists.txt new file mode 100644 index 000000000..2df844359 --- /dev/null +++ b/src/iceberg/inspect/CMakeLists.txt @@ -0,0 +1,18 @@ +# 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. + +iceberg_install_all_headers(iceberg/inspect) diff --git a/src/iceberg/inspect/history_table.cc b/src/iceberg/inspect/history_table.cc new file mode 100644 index 000000000..1a8041f41 --- /dev/null +++ b/src/iceberg/inspect/history_table.cc @@ -0,0 +1,56 @@ +/* + * 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 "iceberg/inspect/history_table.h" + +#include +#include + +#include "iceberg/inspect/metadata_table.h" +#include "iceberg/schema.h" +#include "iceberg/schema_field.h" +#include "iceberg/table_identifier.h" +#include "iceberg/type.h" + +namespace iceberg { + +HistoryTable::HistoryTable(std::shared_ptr table) + : MetadataTable(table, CreateName(table->name())) {} + +HistoryTable::~HistoryTable() = default; + +std::shared_ptr HistoryTable::GetSchema() const { + return std::make_shared( + std::vector{ + SchemaField::MakeRequired(1, "made_current_at", timestamp_tz()), + SchemaField::MakeRequired(2, "snapshot_id", int64()), + SchemaField::MakeOptional(3, "parent_id", int64()), + SchemaField::MakeRequired(4, "is_current_ancestor", boolean())}, + 1); +} + +TableIdentifier HistoryTable::CreateName(const TableIdentifier& source_name) { + return TableIdentifier{source_name.ns, source_name.name + ".history"}; +} + +Result> HistoryTable::Make(std::shared_ptr
table) { + return std::unique_ptr(new HistoryTable(table)); +} + +} // namespace iceberg diff --git a/src/iceberg/inspect/history_table.h b/src/iceberg/inspect/history_table.h new file mode 100644 index 000000000..d10a4967e --- /dev/null +++ b/src/iceberg/inspect/history_table.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#pragma once + +#include + +#include "iceberg/iceberg_export.h" +#include "iceberg/inspect/metadata_table.h" +#include "iceberg/result.h" +#include "iceberg/table.h" + +namespace iceberg { + +/// \brief History metadata table +/// +/// History is based on the table's snapshot log, which logs each update +/// to the table's current snapshot. Each row has columns: +/// - made_current_at (long, timestamp) +/// - snapshot_id (long) +/// - parent_id (long, optional) +/// - is_current_ancestor (bool) +class ICEBERG_EXPORT HistoryTable : public MetadataTable { + public: + /// \brief Create a HistoryTable from table metadata + /// + /// \param[in] table The source table + /// \return A HistoryTable instance or error status + static Result> Make(std::shared_ptr
table); + + ~HistoryTable() override; + + MetadataTableType type() const noexcept override { return MetadataTableType::kHistory; } + + std::shared_ptr GetSchema() const override; + + private: + explicit HistoryTable(std::shared_ptr
table); + + TableIdentifier CreateName(const TableIdentifier& source_name); +}; + +} // namespace iceberg diff --git a/src/iceberg/inspect/meson.build b/src/iceberg/inspect/meson.build new file mode 100644 index 000000000..bcb9d8a97 --- /dev/null +++ b/src/iceberg/inspect/meson.build @@ -0,0 +1,25 @@ +# 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. + +install_headers( + [ + 'history_table.h', + 'metadata_table.h', + 'snapshots_table.h', + ], + subdir: 'iceberg/inspect', +) diff --git a/src/iceberg/inspect/metadata_table.cc b/src/iceberg/inspect/metadata_table.cc new file mode 100644 index 000000000..f285bc81f --- /dev/null +++ b/src/iceberg/inspect/metadata_table.cc @@ -0,0 +1,95 @@ +/* + * 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 "iceberg/inspect/metadata_table.h" + +#include +#include +#include + +#include "iceberg/file_io.h" +#include "iceberg/inspect/history_table.h" +#include "iceberg/inspect/snapshots_table.h" +#include "iceberg/partition_spec.h" +#include "iceberg/schema.h" +#include "iceberg/schema_field.h" +#include "iceberg/sort_order.h" +#include "iceberg/table_identifier.h" +#include "iceberg/table_metadata.h" +#include "iceberg/table_properties.h" +#include "iceberg/table_scan.h" +#include "iceberg/type.h" +#include "iceberg/type_fwd.h" +#include "iceberg/util/uuid.h" + +namespace iceberg { + +MetadataTable::MetadataTable(std::shared_ptr
source_table, + TableIdentifier identifier) + : StaticTable(identifier, source_table->metadata(), + std::string(source_table->metadata_file_location()), source_table->io(), + source_table->catalog()), + source_table_(std::move(source_table)) { + auto schema = GetSchema(); + if (!schema) { + schema = std::make_shared(std::vector{}, 1); + } + + auto builder = + TableMetadataBuilder::BuildFromEmpty(TableMetadata::kDefaultTableFormatVersion); + auto result = builder->AssignUUID(Uuid::GenerateV4().ToString()) + .SetLocation(std::string(source_table_->location())) + .SetCurrentSchema(schema, schema->schema_id()) + .SetDefaultSortOrder(SortOrder::Unsorted()) + .SetDefaultPartitionSpec(PartitionSpec::Unpartitioned()) + .SetProperties({}) + .Build(); + + if (!result.has_value()) { + // If metadata building fails, keep the original metadata from source_table + return; + } + + std::shared_ptr built_metadata = std::move(result.value()); + + metadata_ = built_metadata; + metadata_cache_ = std::make_unique(metadata_.get()); +} + +MetadataTable::~MetadataTable() = default; + +Status MetadataTable::Refresh() { return source_table_->Refresh(); } + +Result> MetadataTable::NewScan() const { + return NotSupported("TODO: Scanning metadata tables is not yet supported"); +}; + +Result> MetadataTableFactory::CreateMetadataTable( + std::shared_ptr
table, MetadataTableType type) { + switch (type) { + case MetadataTableType::kSnapshots: + return SnapshotsTable::Make(table); + case MetadataTableType::kHistory: + return HistoryTable::Make(table); + } + + return Invalid("Unsupported metadata table type"); +} + +} // namespace iceberg diff --git a/src/iceberg/inspect/metadata_table.h b/src/iceberg/inspect/metadata_table.h new file mode 100644 index 000000000..54c6a89fc --- /dev/null +++ b/src/iceberg/inspect/metadata_table.h @@ -0,0 +1,141 @@ +/* + * 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. + */ + +#pragma once + +#include +#include + +#include "iceberg/iceberg_export.h" +#include "iceberg/location_provider.h" +#include "iceberg/result.h" +#include "iceberg/sort_order.h" +#include "iceberg/table.h" +#include "iceberg/table_metadata.h" +#include "iceberg/table_scan.h" +#include "iceberg/type_fwd.h" + +namespace iceberg { + +/// \brief The type of metadata table +enum class MetadataTableType { + kSnapshots, + kHistory, +}; + +/// \brief Base class for Iceberg metadata tables +/// +/// Metadata tables expose table metadata as queryable tables with schemas and scan +/// support. They provide read-only access to metadata. +class ICEBERG_EXPORT MetadataTable : public StaticTable { + public: + virtual MetadataTableType type() const noexcept = 0; + + /// \brief Returns the table's metadata file location + std::string_view metadata_file_location() const { + return source_table_->metadata_file_location(); + } + + /// \brief Returns the table's base location + std::string_view location() const { return source_table_->location(); } + + /// \brief Returns the time when this table was last updated + TimePointMs last_updated_ms() const { return source_table_->last_updated_ms(); } + + /// \brief Returns the table's current snapshot, return NotFoundError if not found + Result> current_snapshot() const { + return source_table_->current_snapshot(); + } + + /// \brief Get the snapshot of this table with the given id + /// + /// \param snapshot_id the ID of the snapshot to get + /// \return the Snapshot with the given id, return NotFoundError if not found + Result> SnapshotById(int64_t snapshot_id) const { + return source_table_->SnapshotById(snapshot_id); + } + + /// \brief Get the snapshots of this table + const std::vector>& snapshots() const { + return source_table_->snapshots(); + } + + /// \brief Get the snapshot history of this table + const std::vector& history() const { + return source_table_->history(); + } + + /// \brief Returns the catalog that this table belongs to + const std::shared_ptr& catalog() const { return source_table_->catalog(); } + + /// \brief Returns a LocationProvider for this table + Result> location_provider() const { + return source_table_->location_provider(); + } + + /// \brief Refreshing is not supported in metadata tables. + Status Refresh() override; + + /// \brief Create a new table scan builder for this table + /// + /// Once a table scan builder is created, it can be refined to project columns and + /// filter data. + Result> NewScan() const override; + + ~MetadataTable(); + + protected: + explicit MetadataTable(std::shared_ptr
source_table, TableIdentifier identifier); + + /// \brief Returns the schema for this metadata table + /// + /// Subclasses override this method to provide their custom schema during + /// MetadataTable construction. The returned schema is used to initialize + /// the underlying TableMetadata. + /// + /// \return The schema for this metadata table, or nullptr for default schema + virtual std::shared_ptr GetSchema() const { return nullptr; } + + std::shared_ptr
source_table_; +}; + +/// \brief Metadata table factory and inspector +/// +/// MetadataTable provides factory methods to create specific metadata tables for +/// inspecting table metadata. Each metadata table exposes a different aspect of the +/// table's metadata as a scannable Iceberg table. +/// +/// Usage: +/// auto metadata_table = ICEBERG_TRY( +/// MetadataTableFactory::CreateMetadataTable( +/// table, MetadataTableType::kSnapshots)); +/// auto scan = ICEBERG_TRY(metadata_table->NewScan()); +/// // ... scan and read metadata table data +class ICEBERG_EXPORT MetadataTableFactory { + public: + /// \brief Create a metadata table from a table + /// + /// \param table The source table + /// \param type The metadata table type to create + /// \return A MetadataTable instance or error status + static Result> CreateMetadataTable( + std::shared_ptr
table, MetadataTableType type); +}; + +} // namespace iceberg diff --git a/src/iceberg/inspect/snapshots_table.cc b/src/iceberg/inspect/snapshots_table.cc new file mode 100644 index 000000000..22b189027 --- /dev/null +++ b/src/iceberg/inspect/snapshots_table.cc @@ -0,0 +1,63 @@ +/* + * 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 "iceberg/inspect/snapshots_table.h" + +#include +#include + +#include "iceberg/inspect/metadata_table.h" +#include "iceberg/schema.h" +#include "iceberg/schema_field.h" +#include "iceberg/table_identifier.h" +#include "iceberg/type.h" + +namespace iceberg { + +SnapshotsTable::SnapshotsTable(std::shared_ptr
table) + : MetadataTable(table, CreateName(table->name())) {} + +SnapshotsTable::~SnapshotsTable() = default; + +std::shared_ptr SnapshotsTable::GetSchema() const { + return std::make_shared( + std::vector{ + SchemaField::MakeRequired(1, "committed_at", timestamp_tz()), + SchemaField::MakeRequired(2, "snapshot_id", int64()), + SchemaField::MakeRequired(3, "parent_id", int64()), + SchemaField::MakeOptional(4, "operation", int64()), + SchemaField::MakeOptional(5, "manifest_list", string()), + SchemaField::MakeOptional( + 6, "summary", + std::make_shared( + SchemaField::MakeRequired(7, "key", string()), + SchemaField::MakeRequired(8, "value", string())))}, + 1); +} + +TableIdentifier SnapshotsTable::CreateName(const TableIdentifier& source_name) { + return TableIdentifier{source_name.ns, source_name.name + ".snapshots"}; +} + +Result> SnapshotsTable::Make( + std::shared_ptr
table) { + return std::unique_ptr(new SnapshotsTable(table)); +} + +} // namespace iceberg diff --git a/src/iceberg/inspect/snapshots_table.h b/src/iceberg/inspect/snapshots_table.h new file mode 100644 index 000000000..86e57dc13 --- /dev/null +++ b/src/iceberg/inspect/snapshots_table.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#pragma once + +#include + +#include "iceberg/iceberg_export.h" +#include "iceberg/inspect/metadata_table.h" +#include "iceberg/result.h" +#include "iceberg/table.h" + +namespace iceberg { + +/// \brief Snapshots metadata table +/// +/// Exposes all snapshots in the table as rows with columns: +/// - committed_at (timestamp) +/// - snapshot_id (long) +/// - parent_id (long) +/// - manifest_list (string) +/// - summary (map) +class ICEBERG_EXPORT SnapshotsTable : public MetadataTable { + public: + /// \brief Create a SnapshotsTable from table metadata + /// + /// \param[in] table The source table + /// \return A SnapshotsTable instance or error status + static Result> Make(std::shared_ptr
table); + + ~SnapshotsTable() override; + + MetadataTableType type() const noexcept override { + return MetadataTableType::kSnapshots; + } + + std::shared_ptr GetSchema() const override; + + private: + explicit SnapshotsTable(std::shared_ptr
table); + + TableIdentifier CreateName(const TableIdentifier& source_name); +}; + +} // namespace iceberg diff --git a/src/iceberg/meson.build b/src/iceberg/meson.build index a5a60b605..d162f5a99 100644 --- a/src/iceberg/meson.build +++ b/src/iceberg/meson.build @@ -64,6 +64,9 @@ iceberg_sources = files( 'file_reader.cc', 'file_writer.cc', 'inheritable_metadata.cc', + 'inspect/metadata_table.cc', + 'inspect/snapshots_table.cc', + 'inspect/history_table.cc', 'json_serde.cc', 'location_provider.cc', 'manifest/manifest_adapter.cc', @@ -283,6 +286,7 @@ subdir('puffin') subdir('row') subdir('update') subdir('util') +subdir('inspect') if get_option('tests').enabled() subdir('test') diff --git a/src/iceberg/table.cc b/src/iceberg/table.cc index 1255871c3..45a3882cb 100644 --- a/src/iceberg/table.cc +++ b/src/iceberg/table.cc @@ -287,4 +287,20 @@ Result> StaticTable::NewUpdateSchema() { return NotSupported("Cannot create an update schema for a static table"); } +Result> StaticTable::NewUpdateLocation() { + return NotSupported("Cannot create an update location for a static table"); +} + +Result> StaticTable::NewUpdatePartitionSpec() { + return NotSupported("Cannot create an update partition spec for a static table"); +} + +Result> StaticTable::NewUpdateSortOrder() { + return NotSupported("Cannot create an update sort order for a static table"); +} + +Result> StaticTable::NewExpireSnapshots() { + return NotSupported("Cannot create an expire snapshots for a static table"); +} + } // namespace iceberg diff --git a/src/iceberg/table.h b/src/iceberg/table.h index 8d8849f37..e560e0a0c 100644 --- a/src/iceberg/table.h +++ b/src/iceberg/table.h @@ -52,7 +52,7 @@ class ICEBERG_EXPORT Table : public std::enable_shared_from_this
{ virtual ~Table(); /// \brief Returns the identifier of this table - const TableIdentifier& name() const { return identifier_; } + virtual const TableIdentifier& name() const { return identifier_; } /// \brief Returns the UUID of the table const std::string& uuid() const; @@ -212,7 +212,7 @@ class ICEBERG_EXPORT StagedTable final : public Table { /// \brief A read-only table. -class ICEBERG_EXPORT StaticTable final : public Table { +class ICEBERG_EXPORT StaticTable : public Table { public: static Result> Make( TableIdentifier identifier, std::shared_ptr metadata, @@ -228,6 +228,14 @@ class ICEBERG_EXPORT StaticTable final : public Table { Result> NewUpdateSchema() override; + Result> NewUpdateLocation() override; + + Result> NewUpdatePartitionSpec() override; + + Result> NewUpdateSortOrder() override; + + Result> NewExpireSnapshots() override; + private: using Table::Table; }; diff --git a/src/iceberg/test/CMakeLists.txt b/src/iceberg/test/CMakeLists.txt index 997d18354..87f4b0857 100644 --- a/src/iceberg/test/CMakeLists.txt +++ b/src/iceberg/test/CMakeLists.txt @@ -233,6 +233,10 @@ if(ICEBERG_BUILD_BUNDLE) delete_filter_test.cc delete_loader_test.cc file_scan_task_reader_test.cc) + + add_iceberg_test(metadata_table_test SOURCES metadata_table_test.cc) + + add_iceberg_test(data_writer_test USE_BUNDLE SOURCES data_writer_test.cc) endif() diff --git a/src/iceberg/test/meson.build b/src/iceberg/test/meson.build index b21a264b1..6cc34bbb2 100644 --- a/src/iceberg/test/meson.build +++ b/src/iceberg/test/meson.build @@ -56,6 +56,7 @@ iceberg_tests = { 'table_requirements_test.cc', 'table_test.cc', 'table_update_test.cc', + 'metadata_table_test.cc', ), }, 'expression_test': { diff --git a/src/iceberg/test/metadata_table_test.cc b/src/iceberg/test/metadata_table_test.cc new file mode 100644 index 000000000..058b10107 --- /dev/null +++ b/src/iceberg/test/metadata_table_test.cc @@ -0,0 +1,135 @@ +/* + * 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 "iceberg/inspect/metadata_table.h" + +#include +#include + +#include "iceberg/inspect/snapshots_table.h" +#include "iceberg/schema.h" +#include "iceberg/schema_field.h" +#include "iceberg/table.h" +#include "iceberg/table_identifier.h" +#include "iceberg/table_metadata.h" +#include "iceberg/test/matchers.h" +#include "iceberg/test/mock_catalog.h" +#include "iceberg/test/mock_io.h" + +namespace iceberg { + +class MetadataTableTest : public ::testing::Test { + protected: + void SetUp() override { + io_ = std::make_shared(); + catalog_ = std::make_shared(); + + auto schema = std::make_shared( + std::vector{SchemaField::MakeRequired(1, "id", int64()), + SchemaField::MakeOptional(2, "name", string())}, + 1); + metadata_ = std::make_shared( + TableMetadata{.format_version = 2, .schemas = {schema}, .current_schema_id = 1}); + + TableIdentifier source_ident{.ns = Namespace{.levels = {"db"}}, + .name = "source_table"}; + auto source_table_result = + Table::Make(source_ident, metadata_, "s3://bucket/meta.json", io_, catalog_); + EXPECT_THAT(source_table_result, IsOk()); + source_table_ = *source_table_result; + + auto snapshots_table_result = MetadataTableFactory::CreateMetadataTable( + source_table_, MetadataTableType::kSnapshots); + EXPECT_THAT(snapshots_table_result, IsOk()); + snapshots_table_ = std::move(*snapshots_table_result); + } + + std::shared_ptr io_; + std::shared_ptr catalog_; + std::shared_ptr metadata_; + std::shared_ptr
source_table_; + std::unique_ptr snapshots_table_; +}; + +TEST_F(MetadataTableTest, Constructor) { + EXPECT_EQ(snapshots_table_->name().name, "source_table.snapshots"); + EXPECT_FALSE(snapshots_table_->uuid().empty()); + auto schema_result = snapshots_table_->schema(); + EXPECT_THAT(schema_result, IsOk()); +} + +TEST_F(MetadataTableTest, DelegatesToSourceTable) { + EXPECT_EQ(snapshots_table_->location(), source_table_->location()); + EXPECT_EQ(snapshots_table_->last_updated_ms(), source_table_->last_updated_ms()); + EXPECT_EQ(snapshots_table_->catalog(), source_table_->catalog()); +} + +TEST_F(MetadataTableTest, NotSupportedOperations) { + EXPECT_THAT(snapshots_table_->NewTransaction(), HasErrorMessage("Cannot")); + EXPECT_THAT(snapshots_table_->NewUpdateProperties(), HasErrorMessage("Cannot")); + EXPECT_THAT(snapshots_table_->NewUpdateSchema(), HasErrorMessage("Cannot")); + EXPECT_THAT(snapshots_table_->NewUpdateLocation(), HasErrorMessage("Cannot")); + EXPECT_THAT(snapshots_table_->NewUpdatePartitionSpec(), HasErrorMessage("Cannot")); + EXPECT_THAT(snapshots_table_->NewUpdateSortOrder(), HasErrorMessage("Cannot")); + EXPECT_THAT(snapshots_table_->NewExpireSnapshots(), HasErrorMessage("Cannot")); +} + +TEST_F(MetadataTableTest, SchemasAndSpecs) { + auto schemas_result = snapshots_table_->schemas(); + EXPECT_THAT(schemas_result, IsOk()); + EXPECT_EQ(schemas_result->get().size(), 1); + + auto spec_result = snapshots_table_->spec(); + EXPECT_THAT(spec_result, IsOk()); + + auto specs_result = snapshots_table_->specs(); + EXPECT_THAT(specs_result, IsOk()); + EXPECT_EQ(specs_result->get().size(), 1); +} + +TEST_F(MetadataTableTest, SortOrders) { + auto sort_order_result = snapshots_table_->sort_order(); + EXPECT_THAT(sort_order_result, IsOk()); + + auto sort_orders_result = snapshots_table_->sort_orders(); + EXPECT_THAT(sort_orders_result, IsOk()); + EXPECT_EQ(sort_orders_result->get().size(), 1); +} + +TEST_F(MetadataTableTest, Properties) { + EXPECT_EQ(snapshots_table_->properties().configs().size(), 0); +} + +TEST_F(MetadataTableTest, Snapshots) { + // Assuming source table has no current snapshot + auto cur_snapshot_result = snapshots_table_->current_snapshot(); + EXPECT_THAT(cur_snapshot_result, IsError(ErrorKind::kNotFound)); + auto snapshot_result = snapshots_table_->SnapshotById(1); + EXPECT_THAT(snapshot_result, IsError(ErrorKind::kNotFound)); + EXPECT_TRUE(snapshots_table_->snapshots().empty()); +} + +TEST_F(MetadataTableTest, History) { EXPECT_TRUE(snapshots_table_->history().empty()); } + +TEST_F(MetadataTableTest, LocationProvider) { + auto lp_result = snapshots_table_->location_provider(); + EXPECT_THAT(lp_result, IsOk()); +} + +} // namespace iceberg diff --git a/src/iceberg/type_fwd.h b/src/iceberg/type_fwd.h index 064ec285a..f0327fa79 100644 --- a/src/iceberg/type_fwd.h +++ b/src/iceberg/type_fwd.h @@ -238,6 +238,11 @@ class UpdateStatistics; class DeleteLoader; class PositionDeleteIndex; +/// \brief Metadata tables. +class HistoryTable; +class MetadataTable; +class SnapshotsTable; + /// ---------------------------------------------------------------------------- /// TODO: Forward declarations below are not added yet. /// ----------------------------------------------------------------------------