Repository navigation
Conversation
Add ColumnSelection and Scan overloads that return only the requested columns, matching Java's metadata table select(). Names resolve through Schema::Select, so nested names, case sensitivity, `*`, and unknown names behave as in TableScanBuilder::Select. Tables now implement ScanProjection(const Schema&) and build only the selected columns. SnapshotsTable uses a per-column append table from the new metadata_table_columns_internal.h helper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Copilot review overview
2 open findings
What changed in this PR
Adds column projection support to C++ Iceberg metadata table scans (including time travel), aligning behavior with Java (newScan().select(...)) and enabling engines to request only needed columns.
Changes:
- Introduces
ColumnSelectionand overloads ofMetadataTable::Scan(...)/TimeTravelMetadataTable::Scan(...)to project columns viaSchema::Select. - Refactors scanning to be implemented via
ScanProjection(const Schema&)/ScanSnapshot(..., const Schema&)instead of overridingScan(). - Adds internal helpers to map projected schema columns to per-column append functions, and updates
SnapshotsTable+ tests accordingly.
| File | Description |
|---|---|
| src/iceberg/inspect/metadata_table.h | Adds ColumnSelection and new Scan overloads; refactors scan implementation to ScanProjection / projected-schema time travel. |
| src/iceberg/inspect/metadata_table.cc | Implements column selection via Schema::Select and routes scans through projected schemas. |
| src/iceberg/inspect/metadata_table_columns_internal.h | New internal helper to select per-column append functions based on projected schema. |
| src/iceberg/inspect/snapshots_table.h | Updates SnapshotsTable to implement ScanProjection(...) rather than Scan(). |
| src/iceberg/inspect/snapshots_table.cc | Implements projected-column building for snapshots using selected append functions. |
| src/iceberg/inspect/history_table.h / .cc | Updates HistoryTable to ScanProjection(...) while keeping scan unsupported. |
| src/iceberg/test/snapshots_table_test.cc | Adds tests for column selection semantics on SnapshotsTable. |
| src/iceberg/test/metadata_table_test.cc | Adds tests for time-travel scans with column projection and HistoryTable behavior. |
🧠 Review effort: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| ICEBERG_CHECK(field.has_value(), "Metadata table column '{}' is not in the schema", | ||
| projected_field.name()); | ||
| if (*field->get().type() != *projected_field.type()) { | ||
| return NotSupported("Cannot select nested fields of metadata table column '{}'", | ||
| projected_field.name()); | ||
| } | ||
| auto column = std::ranges::find(columns, projected_field.field_id(), | ||
| &MetadataTableColumn<Row>::field_id); | ||
| ICEBERG_CHECK(column != columns.end(), | ||
| "Metadata table column '{}' has no append function", | ||
| projected_field.name()); |
| EXPECT_EQ(batch->column_name(0), "summary"); | ||
| ASSERT_EQ(batch->column(0)->type_id(), ::arrow::Type::MAP); | ||
| auto summaries = std::static_pointer_cast<::arrow::MapArray>(batch->column(0)); | ||
| EXPECT_EQ(summaries->value_length(0), 10); |
| /// \brief Scan using the current snapshot on the main branch. | ||
| Result<ArrowArrayStream> Scan() final; | ||
| // Scans without a snapshot selection read the current snapshot on the main branch. | ||
| using MetadataTable::Scan; |
There was a problem hiding this comment.
This pulls in Scan(const ColumnSelection&), which makes an existing call like Scan({}) ambiguous against Scan(const SnapshotSelection&) since both are aggregates. Is that source break intended, or would a distinct name for the column-selection form avoid it?


Metadata table scans currently always return every column. Java's metadata tables support
newScan().select(...):StaticDataTaskprojects each row toscan.schema(), so an engine runningSELECT name FROM t.refsonly getsname. This adds the same column selection to C++ metadata tables.ColumnSelection(column names plus case sensitivity),MetadataTable::Scan(const ColumnSelection&), andTimeTravelMetadataTable::Scan(const SnapshotSelection&, const ColumnSelection&). Names resolve throughSchema::Select, as inTableScanBuilder::Select: nested names are dot-concatenated, columns come back in schema order,*selects all columns, and unknown names are ignored (as in Java'sSchema.select). An empty list returns rows without columns, which is enough to count rows.Scan()is no longer virtual. Tables implement the protectedScanProjection(const Schema& projected_schema)instead, andScanSnapshotalso receives the projected schema.inspect/metadata_table_columns_internal.h, which maps the projected columns to per-column append functions so a table builds only the selected columns. Selecting only some fields of a nested struct column returnsNotSupportedfor now; no current metadata table has a struct column.SnapshotsTablebuilds only the selected columns; for example, it no longer copies thesummarymap unless that column is selected.HistoryTable::Scanstill returnsNotSupported.Open metadata table PRs (#876, #920, #1007) will need to override
ScanProjection(const Schema&)instead ofScan().Validation: pre-commit passed. All 18
metadata_table_testcases pass locally, including 8 new ones covering selection order, unknown names, case sensitivity,*and no selection, an empty selection, map columns, time-travel scans (using a recording test table), andHistoryTable. The changed public headers also compile as a C++20 consumer.🤖 Generated with Claude Code