feat(inspect): implement SnapshotsTable scanning - #801
Conversation
28c4513 to
a53f8ce
Compare
9bc23e1 to
04b657f
Compare
- Add Scan() virtual method and Scan() convenience overload to MetadataTable - Add SnapshotSelection struct for time-travel snapshot resolution - Add supports_time_travel() concrete method driven by kind() - Implement SnapshotsTable::Scan() to materialize snapshot rows via ArrowRowBuilder
There was a problem hiding this comment.
Pull request overview
Implements initial metadata-table scanning support by adding a Scan() API to MetadataTable (with snapshot-selection parameters for future time-travel) and providing a concrete SnapshotsTable::Scan() implementation that materializes snapshot rows into Arrow arrays. The PR also restructures/extends the metadata-table test suite to validate schemas and snapshot scanning behavior.
Changes:
- Added
SnapshotSelectionand a virtualMetadataTable::Scan()API (with a convenience overload) plussupports_time_travel(). - Implemented
SnapshotsTable::Scan()to emit snapshot rows (6 columns) viaArrowRowBuilder. - Added/expanded tests and wired new test sources into the metadata-table test target.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/iceberg/inspect/metadata_table.h | Adds SnapshotSelection, Scan() API, and supports_time_travel() declaration/docs. |
| src/iceberg/inspect/metadata_table.cc | Implements default Scan() + supports_time_travel() and wires factory for kinds. |
| src/iceberg/inspect/snapshots_table.h | Declares SnapshotsTable::Scan() override. |
| src/iceberg/inspect/snapshots_table.cc | Implements snapshot scanning into Arrow via ArrowRowBuilder. |
| src/iceberg/test/metadata_table_test.cc | Simplifies base setup and adds SupportsTimeTravel test. |
| src/iceberg/test/metadata_table_test_base.h | New shared fixture/helpers for metadata table tests. |
| src/iceberg/test/snapshots_table_test.cc | New tests validating snapshots table construction/schema/scan output. |
| src/iceberg/test/history_table_test.cc | New schema test for history table. |
| src/iceberg/test/CMakeLists.txt | Adds new test sources to the metadata-table test target. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/iceberg/inspect/metadata_table.cc:38
supports_time_travel()is currently hard-coded to return false. That matches today’s twoKindvalues, but it’s easy to forget to update once additional metadata table kinds are added, and it doesn’t reflect the docstring/PR description that this is kind-driven. Consider switching onkind()and making the non-exhaustive case unreachable to keep future additions honest.
bool MetadataTable::supports_time_travel() const noexcept { return false; }
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/iceberg/inspect/metadata_table.cc:38
- supports_time_travel() is documented (and described in the PR) as being driven by the metadata table kind, but the implementation always returns false regardless of kind. This makes the API misleading and forces future kinds to remember to update callers rather than the method itself.
bool MetadataTable::supports_time_travel() const noexcept { return false; }
| /// \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; |
There was a problem hiding this comment.
Should it be std::variant<std::monostate, int64_t, TimePointMs>? Otherwise we need to define the priority if both snapshot_id and as_of_timestamp are set.
| /// \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; |
There was a problem hiding this comment.
Is std::string enough? An empty one is unset.
| /// The default implementation returns NotSupported. Subclasses override this | ||
| /// to materialize their data. | ||
| virtual Result<ArrowArray> Scan( | ||
| const std::optional<SnapshotSelection>& snapshot_selection); |
There was a problem hiding this comment.
It doesn't seem necessary to add a std::optional on top of SnapshotSelection if all its values are optional.
| /// | ||
| /// The default implementation returns NotSupported. Subclasses override this | ||
| /// to materialize their data. | ||
| virtual Result<ArrowArray> Scan( |
There was a problem hiding this comment.
Should we return ArrowArrayStream instead? My concern is that a single ArrowArray may lead to OOM if the result is too large. ArrowArrayStream is an iterator of ArrowArrays which is more scalable.
| ICEBERG_RETURN_UNEXPECTED(AppendString(builder.column(4), snapshot->manifest_list)); | ||
|
|
||
| // column 5: summary (map<string,string>) | ||
| ICEBERG_RETURN_UNEXPECTED(AppendStringMap(builder.column(5), snapshot->summary)); |
There was a problem hiding this comment.
Snapshot::summary is not equivalent to Java’s Snapshot.summary() here. In C++, the map still contains the operation entry, while Java stores the operation separately and SnapshotsTable.snapshotToRow emits the remaining summary map. As a result, every normal row duplicates operation in both columns (the copied Java fixture has 10 summary entries, not 11). Please omit SnapshotSummaryFields::kOperation when materializing this map, while preserving null versus empty-map semantics.
| } | ||
|
|
||
| Result<ArrowArray> SnapshotsTable::Scan( | ||
| const std::optional<SnapshotSelection>& /*snapshot_selection*/) { |
There was a problem hiding this comment.
Silently ignoring an invalid selection diverges from Java. SnapshotScan.useSnapshot, useRef, and asOfTime validate or resolve the requested snapshot before SnapshotsTable returns all known snapshots. With this implementation, Scan({.snapshot_id = 999}) succeeds and returns unrelated rows. Please validate the selector (or reject a non-null selection as unsupported) instead of treating invalid input as an unqualified scan.
| /// | ||
| /// The snapshots table always returns every known snapshot, so the | ||
| /// snapshot_selection parameter is ignored. | ||
| Result<ArrowArray> Scan( |
There was a problem hiding this comment.
This override hides the zero-argument MetadataTable::Scan() overload under C++ name lookup. A caller using the public SnapshotsTable::Make() result cannot write snapshots_table->Scan(); only callers typed as MetadataTable compile, which is why the current tests miss it. Please add using MetadataTable::Scan; (or an explicit zero-argument overload).
Summary
Implement
Scan()forSnapshotsTable, adding the core scanning infrastructure toMetadataTableand materializing snapshot rows into Arrow arrays viaArrowRowBuilder.Changes
metadata_table.h/.ccSnapshotSelectionstruct withsnapshot_id,as_of_timestamp,ref_nameoptional fields for time-travel queriesScan()virtual method returningResult<ArrowArray>with a convenience zero-arg overloadsupports_time_travel()concrete method driven bykind(), matching Java'sTIME_TRAVEL_TABLE_TYPESstatic set (currently bothkSnapshotsandkHistoryreturnfalse)Scan()returnsNotSupportedsnapshots_table.h/.ccSnapshotsTable::Scan()— iteratessource_table()->snapshots()and writes 6 columns (committed_at,snapshot_id,parent_id,operation,manifest_list,summary) viaArrowRowBuildersnap.timestampMillis() \* 1000Tests
metadata_table_test_base.h— sharedMetadataTableTestBasewithMockFileIO+MockCatalogfixture,FinishAndImport(),MakeTestSnapshots(),MakeTableWithSnapshots()helpersmetadata_table_test.cc— 2 framework tests (FactoryRejectsNullSourceTable,SupportsTimeTravel)snapshots_table_test.cc— 11 tests (Construct,SchemaMatchesIcebergSchema, 9 scan data tests matching JavaTestDataTaskParserfixture)history_table_test.cc— 1 schema test