Skip to content

Core: expose catalog labels on the loaded table via SupportsLabels - #18046

Merged
laskoviymishka merged 5 commits into
mainfrom
laskoviymishka/labels-table
Sep 21, 2026
Merged

laskoviymishka merged 5 commits into
mainfrom
laskoviymishka/labels-table

Conversation

@laskoviymishka

Copy link
Copy Markdown
Contributor

Part 2 of the catalog-labels stack (spec: #15750). Based on #18045 — review that first; this PR's diff is only the table-exposure layer.

Exposes catalog-provided labels on the loaded table:

  • SupportsLabels mixin interface and BaseTable.labels()
  • RESTSessionCatalog / RESTTable thread labels from the load response onto the table
  • labels are transient: not part of table state and not preserved across serialization
  • TestBaseTableLabels

Stacked on #18045.

Base automatically changed from laskoviymishka/labels-serde to main September 17, 2026 13:01
Builds on the labels read-path serde: make the catalog-provided labels from
the load response reachable by consumers.

- New SupportsLabels mixin interface (mirroring SupportsDistributedScanPlanning)
  with a single labels() accessor.
- BaseTable implements SupportsLabels via a new constructor that carries an
  optional Labels; existing constructors default to an empty instance. The field
  is transient and not copied into SerializableTable, so labels are ephemeral
  catalog enrichment and are not preserved across table serialization.
- RESTSessionCatalog populates labels from the load response for both the plain
  BaseTable and the server-side scan-planning RESTTable paths, across loadTable,
  registerTable, and createTable.
@laskoviymishka
laskoviymishka force-pushed the laskoviymishka/labels-table branch from 38a506b to 5a0ab8c Compare September 17, 2026 15:02
- Rename TestBaseTableLabels to TestLabelsOnBaseTable, drop a redundant comment,
  and assert the field-label contents (field id + labels), not just the count.
- Implement SupportsLabels on SerializableTable, returning Labels.EMPTY: labels
  are driver-side catalog enrichment and are not carried across serialization.

Co-authored-by: Isaac <no-reply@databricks.com>
@nastra
nastra requested a review from pvary September 18, 2026 09:30
* storage.
*/
public class SerializableTable implements Table, HasTableOperations, Serializable {
public class SerializableTable implements Table, HasTableOperations, Serializable, SupportsLabels {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why do we implement SupportsLabels if we don't support it?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ot we can just copy and serialize the labels - it is not that complicated

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

class TestLabelsOnBaseTable {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

do we want to test that it is working with RESTCatalog?

@pvary

pvary commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Do we want to do the View labels in a different PR?


@Override
public Labels labels() {
// labels is null only when this instance was deserialized bypassing writeReplace (e.g. Kryo).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

not just Kryo - normal Java deserialization from other package could cause this too.

Object hadoopConf) {
super(ops, name, reporter);
Object hadoopConf,
Labels labels) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is getting super ugly - 11 parameters 🤮
Maybe in another PR?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yeap, not the tiny thing, will file a separate PR to cleanup this.

BaseTable table =
new BaseTable(new TestTables.TestTableOperations(TABLE_NAME, temp), TABLE_NAME);

assertThat(table).isInstanceOf(SupportsLabels.class);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Are we checking that the BaseTable class implements SupportsLabels?

@pvary

pvary commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Do we have a clear plan how caching/refs fetching should work with labels and REST catalog?
Do we need to have a different ETag when the labels change? Are we ok if the Table fetch with Refs return different labels than a Table fetch with all snapshots, and we cache them both, and might alternately return different values?

Maybe we want to spec that we need to have different ETags for different labels if the labels carry mission critical info like PII settings, or we can ignore them, and highlight this in spec?

@laskoviymishka

Copy link
Copy Markdown
Contributor Author

this is all server-side, so i don't think labels need a spec change here.

etag/caching is only defined once in the spec, on the etag parameter: it's "a unique version of the table metadata", and servers "should produce unique tags for responses that return different metadata content." the refs vs all example is already there. we don't repeat caching rules for config, storage-credentials, remote-signing-config, etc., so adding something labels-specific would be odd.

on the client side, the etag is opaque. the client stores it and sends it back in If-None-Match; labels aren't involved in generating it. refs vs all already get different etags, so they naturally end up as separate cache entries with their own labels.

whether a label change changes the etag is up to the server. etag versions the metadata response, and labels aren't table metadata. the Labels text already says they may be cached following the response etag, which seems enough to me.

@laskoviymishka

Copy link
Copy Markdown
Contributor Author

Do we want to do the View labels in a different PR?

yes, i'll do it separately

laskoviymishka and others added 2 commits September 19, 2026 22:15
- Drop SupportsLabels from SerializableTable. Labels are transient catalog
  enrichment that is intentionally not shipped to executors, and the only
  consumer already guards with instanceof, so behavior is unchanged; a
  real label-carrying implementation stays additive if ever needed.
- Broaden the BaseTable#labels() null comment: the transient field is null
  after any deserialization that bypasses writeReplace(), not only Kryo.
- Make the TestLabelsOnBaseTable test methods package-private.

Co-authored-by: Isaac <no-reply@databricks.com>
Loads a table through RESTCatalog backed by an adapter that returns
object- and field-level labels on the load-table response, and asserts
they are surfaced through SupportsLabels#labels().

Co-authored-by: Isaac <no-reply@databricks.com>

@pvary pvary left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. Small question on javadoc.
Thanks!

Co-authored-by: pvary <peter.vary.apache@gmail.com>
@laskoviymishka
laskoviymishka merged commit e8d0f0f into main Sep 21, 2026
42 checks passed
@laskoviymishka
laskoviymishka deleted the laskoviymishka/labels-table branch September 21, 2026 20:37
NihalJain added a commit to NihalJain/iceberg-cpp that referenced this pull request Oct 12, 2026
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.
wgtmac pushed a commit to NihalJain/iceberg-cpp that referenced this pull request Oct 14, 2026
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.
wgtmac pushed a commit to apache/iceberg-cpp that referenced this pull request Oct 14, 2026
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.
yayaha added a commit to yayaha/iceberg-python that referenced this pull request Nov 9, 2026
Labels parsed from the load response were not reachable from the
loaded table. Add `Table.labels`. The REST catalog sets it on load,
create, and register. Other catalogs and staged tables
(`create_table_transaction`) get empty labels. Labels are not table
state: they are not part of equality, and refresh and commits keep
them. This matches the Java `SupportsLabels` behavior
(apache/iceberg#18046).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants