#18126·iceberg

Catalog Labels: client-library read support (Go / Rust / Python / C++)

Author: laskoviymishkaCreated Sep 15, 2026Updated Sep 17, 2026

Summary

Catalog Labels were added to the Iceberg REST spec in #15750 (merged) and implemented on the Java client/engine side as a stacked set of PRs (#18045–#18049). This is a tracking issue to bring the other REST client libraries — iceberg-go, iceberg-rust, iceberg-python (PyIceberg), and iceberg-cpp — to read-path parity.

Labels are catalog-provided enrichment, not table state: they are returned on the load response, are transient/runtime-only, and are never persisted into table metadata or serialized. A client that ignores labels remains spec-compliant.

Wire shape

LoadTableResult / LoadViewResult gain an optional labels object:

json
"labels": {
  "object-labels": { "<key>": "<value>" },
  "fields": [ { "field-id": 3, "labels": { "classification": "pii" } } ]
}
  • object-labels: flat catalog-object-level map. fields: per-field labels keyed by field-id.
  • A field-id may carry multiple keys and may not resolve to a current column (e.g. a dropped column) — clients should tolerate both.

Java reference implementation

  • Spec: #15750
  • serde: #18045 · table accessor (SupportsLabels): #18046 · REST server test fixture: #18047 · labels metadata table: #18048 · Spark DESCRIBE: #18049

Shared prerequisite

Client integration tests need a server that emits labels — that is the REST server test fixture (#18047); once merged it must ship in the iceberg-rest-fixture Docker image. This gates the integration-test PRs only; unit-test PRs (mocked responses) can proceed immediately.

  • #18047 merged and published to the iceberg-rest-fixture image

Per-repo proposed PRs

Each repo mirrors the Java layering (serde → table accessor → labels metadata table → integration tests), modeled on that client's existing storage-credentials / vended-credentials field. Read-path parity (metadata table) is included in all four. Rust and C++ are table-only (no LoadViewResult yet). Each per-repo group is a stack (each PR based on the previous); repos are independent.

iceberg-python (PyIceberg) — reconcile existing prototype to the spec

  • P1 serde + accessor: align internal keys "table"/"columns" → spec "object-labels"/"fields", add ViewResponse/View labels, unit tests
  • P2 table.inspect.labels() metadata table
  • P3 integration tests against the fixture (gated on prerequisite)

iceberg-go

  • G1 label types + serde on loadTableResponse/loadViewResponse + Table/View accessor + unit tests
  • G2 InspectTable.Labels() metadata table
  • G3 integration tests against the fixture (gated)

iceberg-cpp — table-only (no views yet)

  • C1 serde (FieldLabels/CatalogLabels + LoadTableResult.labels) + Table::catalog_labels() accessor + first REST serde unit test
  • C2 labels kind on inspect::MetadataTable
  • C3 integration tests against the fixture (gated)

iceberg-rust — table-only (no views yet)

  • R1 serde types (in the iceberg core crate) + labels on LoadTableResult + public-api.txt
  • R2 Table field + accessor + REST propagation
  • R3 inspect().labels() metadata table (+ DataFusion adapter)
  • R4 integration tests against the fixture (gated)

Notes

  • Suggested lead order: PyIceberg → Go → C++ → Rust (ascending ceremony).
  • Views: Go/Python cover table + view; Rust/C++ are table-only until LoadViewResult lands in those clients.