Skip to content

feat: support catalog labels on tables loaded from REST catalogs - #960

Open
NihalJain wants to merge 1 commit into
apache:mainfrom
NihalJain:feat/catalog-labels
Open

NihalJain wants to merge 1 commit into
apache:mainfrom
NihalJain:feat/catalog-labels

Conversation

@NihalJain

@NihalJain NihalJain commented Sep 23, 2026 •

Copy link
Copy Markdown

Parse the optional labels field of the REST load-table response and expose it through Table::labels(), mirroring apache/iceberg#18045 and apache/iceberg#18046. Labels are catalog-provided rather than table state, so they live on the Table instance instead of TableMetadata.

Part of #938.

Unit tests cover the serde (all label shapes, null/absent handling, invalid input, strict int32 field-id) and the Table side (default empty, provided labels exposed, unchanged by Refresh()). The REST-response-to-Table wiring isn't reachable from unit tests, so I also checked it locally against the REST fixture behind a proxy that adds labels, covering create, load and register. Test code posted at https://gist.github.com/NihalJain/847d9137f090db63978a56c9fa84cd0b

Proper integration tests come in C3 once apache/iceberg#18047 ships in the fixture image.


AI Disclosure

  • Model: Opus 5.5
  • Platform/Tool: Claude Code
  • Human Oversight: Fully reviewed
  • Prompt Summary: Research how catalog labels were implemented in Iceberg Java, port the read path to iceberg-cpp for C1 of Catalog Labels: client-library read support for C++ #938 with matching semantics, verify it with unit tests and an end-to-end run against the REST fixture, and open a PR to apache/iceberg-cpp

Copilot AI lite review requested due to automatic review settings September 23, 2026 18:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The staged create-table path must preserve catalog-provided labels.

Review effort: Lite
Findings: None

What changed in this PR

Adds REST catalog label parsing and exposes catalog-provided labels through Table::labels().

Changes:

  • Adds label models, serde support, and strict field-ID parsing.
  • Wires labels into REST table responses and table instances.
  • Adds serde and table behavior tests.
File Summary
src/​iceberg/​util/​json_util_internal.h Adds strict int32 parsing.
src/​iceberg/​type_fwd.h Adds label forward declarations.
src/​iceberg/​test/​table_test.cc Tests table label behavior.
src/​iceberg/​test/​rest_json_serde_test.cc Tests label serialization and parsing.
src/​iceberg/​table.h Adds the labels API.
src/​iceberg/​table.cc Stores and exposes labels.
src/​iceberg/​labels.h Defines label models.
src/​iceberg/​catalog/​rest/​types.h Adds labels to load results; staged table construction still drops them (moderate issue).
src/​iceberg/​catalog/​rest/​types.cc Includes labels in equality checks.
src/​iceberg/​catalog/​rest/​rest_catalog.cc Wires labels into REST table handling.
src/​iceberg/​catalog/​rest/​json_serde.cc Implements label JSON serde.
src/​iceberg/​catalog/​rest/​json_serde_internal.h Declares label serde functions.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@wgtmac

wgtmac commented Oct 11, 2026

Copy link
Copy Markdown
Member

Thanks for adding this! Could you please rebase and resolve merge conflicts?

@NihalJain

Copy link
Copy Markdown
Author

Sure let me do that. Will update by EOD!

Copilot AI lite review requested due to automatic review settings October 12, 2026 14:50
@NihalJain
NihalJain force-pushed the feat/catalog-labels branch from b0b1a63 to 37376c2 Compare October 12, 2026 14:50
Parse the optional `labels` field of the REST load-table response and
expose it through Table::labels(), mirroring apache/iceberg#18045 and
apache/iceberg#18046. Labels are catalog-provided rather than table
state, so they live on the Table instance instead of TableMetadata.

Part of apache#938.
@NihalJain
NihalJain force-pushed the feat/catalog-labels branch from 37376c2 to dd1aee6 Compare October 12, 2026 14:55

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

1 open finding

🧠 Review effort: Lite

Comment on lines +516 to +526
// A non-object value is treated as absent labels rather than failing the load.
Result<Labels> LabelsFromJson(const nlohmann::json& json) {
Labels labels;
ICEBERG_ASSIGN_OR_RAISE(
labels.object_labels,
GetJsonValueOrDefault<decltype(labels.object_labels)>(json, kObjectLabels));
if (auto it = json.find(kFields); it != json.end() && !it->is_null()) {
if (!it->is_array()) {
return JsonParseError("Cannot parse '{}' from non-array: {}", kFields,
SafeDumpJson(*it));
}
Copilot AI lite review requested due to automatic review settings October 12, 2026 15:07
@NihalJain

Copy link
Copy Markdown
Author

Rebased onto main. field-id parsing now uses GetJsonInteger<int32_t> from #940, and labels.h is added to the C++20 public-header list from #936. No other functional changes. Unit tests, pre-commit, and an end-to-end check against the REST fixture all verified locally.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

3 open findings

🧠 Review effort: Lite

Comment on lines +516 to +533
// A non-object value is treated as absent labels rather than failing the load.
Result<Labels> LabelsFromJson(const nlohmann::json& json) {
Labels labels;
ICEBERG_ASSIGN_OR_RAISE(
labels.object_labels,
GetJsonValueOrDefault<decltype(labels.object_labels)>(json, kObjectLabels));
if (auto it = json.find(kFields); it != json.end() && !it->is_null()) {
if (!it->is_array()) {
return JsonParseError("Cannot parse '{}' from non-array: {}", kFields,
SafeDumpJson(*it));
}
for (const auto& entry : *it) {
ICEBERG_ASSIGN_OR_RAISE(auto field_label, FieldLabelFromJson(entry));
labels.fields.push_back(std::move(field_label));
}
}
return labels;
}
Comment thread src/iceberg/table.h
std::string metadata_location, std::shared_ptr<FileIO> io,
std::shared_ptr<Catalog> catalog, std::string full_name = "",
std::shared_ptr<MetricsReporter> reporter = nullptr);
std::shared_ptr<MetricsReporter> reporter = nullptr, Labels labels = {});

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants