Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion src/iceberg/inspect/metadata_table.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ MetadataTable::MetadataTable(std::shared_ptr<Table> source_table,

MetadataTable::~MetadataTable() = default;

bool MetadataTable::supports_time_travel() const noexcept { return false; }

Result<ArrowArray> MetadataTable::Scan(
std::optional<SnapshotSelection> /* snapshot_selection */) {
return NotSupported("Scan is not supported for this metadata table type");
}

Result<std::unique_ptr<MetadataTable>> MetadataTable::Make(std::shared_ptr<Table> table,
Kind kind) {
if (table == nullptr) [[unlikely]] {
Expand All @@ -46,9 +53,24 @@ Result<std::unique_ptr<MetadataTable>> MetadataTable::Make(std::shared_ptr<Table
return SnapshotsTable::Make(table);
case Kind::kHistory:
return HistoryTable::Make(table);
case Kind::kEntries:
case Kind::kFiles:
case Kind::kDataFiles:
case Kind::kDeleteFiles:
case Kind::kMetadataLogEntries:
case Kind::kRefs:
case Kind::kManifests:
case Kind::kPartitions:
case Kind::kAllDataFiles:
case Kind::kAllDeleteFiles:
case Kind::kAllFiles:
case Kind::kAllManifests:
case Kind::kAllEntries:
case Kind::kPositionDeletes:
return NotSupported("Metadata table type not yet implemented");
}

return NotSupported("Unsupported metadata table type");
return NotSupported("Unknown metadata table type");
}

} // namespace iceberg
48 changes: 47 additions & 1 deletion src/iceberg/inspect/metadata_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,48 @@
#pragma once

#include <memory>
#include <optional>
#include <string>

#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<int64_t> snapshot_id;
/// \brief Read the snapshot that was current at this timestamp.
std::optional<TimePointMs> as_of_timestamp;
/// \brief Read the snapshot referenced by this named ref (branch or tag).
std::optional<std::string> 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<std::unique_ptr<MetadataTable>> Make(std::shared_ptr<Table> table,
Expand All @@ -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<ArrowArray> Scan(std::optional<SnapshotSelection> snapshot_selection);

const TableIdentifier& name() const { return identifier_; }

const std::shared_ptr<Schema>& schema() const { return schema_; }
Expand Down
59 changes: 59 additions & 0 deletions src/iceberg/test/metadata_table_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

#include "iceberg/inspect/metadata_table.h"

#include <vector>

#include <gmock/gmock.h>
#include <gtest/gtest.h>

Expand Down Expand Up @@ -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<int>(MetadataTable::Kind::kEntries), 0);
EXPECT_EQ(static_cast<int>(MetadataTable::Kind::kFiles), 1);
EXPECT_EQ(static_cast<int>(MetadataTable::Kind::kDataFiles), 2);
EXPECT_EQ(static_cast<int>(MetadataTable::Kind::kDeleteFiles), 3);
EXPECT_EQ(static_cast<int>(MetadataTable::Kind::kHistory), 4);
EXPECT_EQ(static_cast<int>(MetadataTable::Kind::kMetadataLogEntries), 5);
EXPECT_EQ(static_cast<int>(MetadataTable::Kind::kSnapshots), 6);
EXPECT_EQ(static_cast<int>(MetadataTable::Kind::kRefs), 7);
EXPECT_EQ(static_cast<int>(MetadataTable::Kind::kManifests), 8);
EXPECT_EQ(static_cast<int>(MetadataTable::Kind::kPartitions), 9);
EXPECT_EQ(static_cast<int>(MetadataTable::Kind::kAllDataFiles), 10);
EXPECT_EQ(static_cast<int>(MetadataTable::Kind::kAllDeleteFiles), 11);
EXPECT_EQ(static_cast<int>(MetadataTable::Kind::kAllFiles), 12);
EXPECT_EQ(static_cast<int>(MetadataTable::Kind::kAllManifests), 13);
EXPECT_EQ(static_cast<int>(MetadataTable::Kind::kAllEntries), 14);
EXPECT_EQ(static_cast<int>(MetadataTable::Kind::kPositionDeletes), 15);
}

TEST_F(MetadataTableTest, UnimplementedKindsReturnNotSupported) {
// All new (not-yet-implemented) kinds should return NotSupported from the factory.
std::vector<MetadataTable::Kind> 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<int>(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
Loading