diff --git a/src/iceberg/inspect/metadata_table.cc b/src/iceberg/inspect/metadata_table.cc index 5e9504003..54af967e2 100644 --- a/src/iceberg/inspect/metadata_table.cc +++ b/src/iceberg/inspect/metadata_table.cc @@ -35,6 +35,13 @@ MetadataTable::MetadataTable(std::shared_ptr source_table, MetadataTable::~MetadataTable() = default; +bool MetadataTable::supports_time_travel() const noexcept { return false; } + +Result MetadataTable::Scan( + std::optional /* snapshot_selection */) { + return NotSupported("Scan is not supported for this metadata table type"); +} + Result> MetadataTable::Make(std::shared_ptr
table, Kind kind) { if (table == nullptr) [[unlikely]] { @@ -46,9 +53,24 @@ Result> MetadataTable::Make(std::shared_ptr
+#include +#include +#include "iceberg/arrow_c_data.h" #include "iceberg/iceberg_export.h" #include "iceberg/result.h" #include "iceberg/table_identifier.h" #include "iceberg/type_fwd.h" +#include "iceberg/util/timepoint.h" namespace iceberg { +/// \brief Parameters for snapshot selection (time travel). +struct SnapshotSelection { + /// \brief The snapshot ID to read. + std::optional snapshot_id; + /// \brief Read the snapshot that was current at this timestamp. + std::optional as_of_timestamp; + /// \brief Read the snapshot referenced by this named ref (branch or tag). + std::optional ref_name; +}; + /// \brief Base class for Iceberg metadata tables. class ICEBERG_EXPORT MetadataTable { public: enum class Kind { - kSnapshots, + kEntries, + kFiles, + kDataFiles, + kDeleteFiles, kHistory, + kMetadataLogEntries, + kSnapshots, + kRefs, + kManifests, + kPartitions, + kAllDataFiles, + kAllDeleteFiles, + kAllFiles, + kAllManifests, + kAllEntries, + kPositionDeletes, }; static Result> Make(std::shared_ptr
table, @@ -43,6 +71,24 @@ class ICEBERG_EXPORT MetadataTable { virtual Kind kind() const noexcept = 0; + /// \brief Whether this metadata table supports time-travel queries. + /// + /// The default implementation returns false. Subclasses that support + /// reading historical snapshots should override to return true. + virtual bool supports_time_travel() const noexcept; + + /// \brief Scan the metadata table and return all rows as an Arrow struct array. + /// + /// The returned ArrowArray is a struct array where each element is one row. + /// The caller takes ownership and must call ArrowArrayRelease when done. + /// + /// \param snapshot_selection optional snapshot selection for time travel. + /// Pass std::nullopt to use the current snapshot. + /// + /// The default implementation returns NotSupported. Subclasses override this + /// to materialize their data. + virtual Result Scan(std::optional snapshot_selection); + const TableIdentifier& name() const { return identifier_; } const std::shared_ptr& schema() const { return schema_; } diff --git a/src/iceberg/test/metadata_table_test.cc b/src/iceberg/test/metadata_table_test.cc index 1e0a664c3..8caab2c55 100644 --- a/src/iceberg/test/metadata_table_test.cc +++ b/src/iceberg/test/metadata_table_test.cc @@ -19,6 +19,8 @@ #include "iceberg/inspect/metadata_table.h" +#include + #include #include @@ -117,4 +119,61 @@ TEST_F(MetadataTableTest, FactoryRejectsNullSourceTable) { EXPECT_THAT(result, HasErrorMessage("Table cannot be null")); } +TEST_F(MetadataTableTest, AllKindEnumValues) { + // Verify all 16 Kind enum values are present and distinct. + EXPECT_EQ(static_cast(MetadataTable::Kind::kEntries), 0); + EXPECT_EQ(static_cast(MetadataTable::Kind::kFiles), 1); + EXPECT_EQ(static_cast(MetadataTable::Kind::kDataFiles), 2); + EXPECT_EQ(static_cast(MetadataTable::Kind::kDeleteFiles), 3); + EXPECT_EQ(static_cast(MetadataTable::Kind::kHistory), 4); + EXPECT_EQ(static_cast(MetadataTable::Kind::kMetadataLogEntries), 5); + EXPECT_EQ(static_cast(MetadataTable::Kind::kSnapshots), 6); + EXPECT_EQ(static_cast(MetadataTable::Kind::kRefs), 7); + EXPECT_EQ(static_cast(MetadataTable::Kind::kManifests), 8); + EXPECT_EQ(static_cast(MetadataTable::Kind::kPartitions), 9); + EXPECT_EQ(static_cast(MetadataTable::Kind::kAllDataFiles), 10); + EXPECT_EQ(static_cast(MetadataTable::Kind::kAllDeleteFiles), 11); + EXPECT_EQ(static_cast(MetadataTable::Kind::kAllFiles), 12); + EXPECT_EQ(static_cast(MetadataTable::Kind::kAllManifests), 13); + EXPECT_EQ(static_cast(MetadataTable::Kind::kAllEntries), 14); + EXPECT_EQ(static_cast(MetadataTable::Kind::kPositionDeletes), 15); +} + +TEST_F(MetadataTableTest, UnimplementedKindsReturnNotSupported) { + // All new (not-yet-implemented) kinds should return NotSupported from the factory. + std::vector unimplemented = { + MetadataTable::Kind::kEntries, + MetadataTable::Kind::kFiles, + MetadataTable::Kind::kDataFiles, + MetadataTable::Kind::kDeleteFiles, + MetadataTable::Kind::kMetadataLogEntries, + MetadataTable::Kind::kRefs, + MetadataTable::Kind::kManifests, + MetadataTable::Kind::kPartitions, + MetadataTable::Kind::kAllDataFiles, + MetadataTable::Kind::kAllDeleteFiles, + MetadataTable::Kind::kAllFiles, + MetadataTable::Kind::kAllManifests, + MetadataTable::Kind::kAllEntries, + MetadataTable::Kind::kPositionDeletes, + }; + + for (auto kind : unimplemented) { + auto result = MetadataTable::Make(source_table_, kind); + EXPECT_THAT(result, IsError(ErrorKind::kNotSupported)) + << "Kind " << static_cast(kind) << " should return NotSupported"; + } +} + +TEST_F(MetadataTableTest, DefaultSupportsTimeTravelReturnsFalse) { + // The base class default implementation should return false. + EXPECT_FALSE(snapshots_table_->supports_time_travel()); +} + +TEST_F(MetadataTableTest, DefaultScanReturnsNotSupported) { + // The base class default Scan() should return NotSupported. + auto result = snapshots_table_->Scan(std::nullopt); + EXPECT_THAT(result, IsError(ErrorKind::kNotSupported)); +} + } // namespace iceberg