Skip to content

Core: Implement source-ids to deal with multi arguments transforms#12897

Open
jbonofre wants to merge 8 commits into
apache:mainfrom
jbonofre:multi-arg-transforms
Open

Core: Implement source-ids to deal with multi arguments transforms#12897
jbonofre wants to merge 8 commits into
apache:mainfrom
jbonofre:multi-arg-transforms

Conversation

@jbonofre

@jbonofre jbonofre commented Apr 25, 2025

Copy link
Copy Markdown
Member

Following the update on the spec regarding source-id and source-ids (thanks again @Fokko 😄 ), here's the PR to introduce source-ids field in partition spec.

A few notes:

  1. Internal representation is now based on source-ids
  2. Serialization/deserialization supports source-id and source-ids elements in the json (exclusive), both populating source-ids internal representation (as List<Integer>)
  3. The TestPartitionSpecParser is still testing source-id, but also source-ids parsing, and neither source-id and source-ids presence (throwing IllegalArgumentException in that case)
  4. For backward compatibility (especially for the transforms), source id is still supported (using the first element in the internal representation) and some methods have been flagged as deprecated to encourage use of source ids.

@jbonofre

Copy link
Copy Markdown
Member Author

@rdblue @Fokko @RussellSpitzer ^^ Thanks !

@jbonofre jbonofre changed the title Implement source-ids to deal with multi arguments transforms Core: Implement source-ids to deal with multi arguments transforms Apr 25, 2025
Comment thread api/src/main/java/org/apache/iceberg/PartitionField.java Outdated
Comment thread .palantir/revapi.yml Outdated
fields.add(new PartitionField(sourceId, fieldId, name, transform));
Builder add(List<Integer> sourceIds, int fieldId, String name, Transform<?, ?> transform) {
// we use the first entry in the source-ids list here
checkAndAddPartitionName(name, sourceIds.get(0));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How does this work for multi arg partition field?
Do we need a logic that accepts the sourceIds and resolve the name of multi arg transform?

@jbonofre jbonofre Apr 25, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The sourceId here is to resolve conflict (no impact on name):

        if (sourceColumnId != null) {
          // for identity transform case we allow conflicts between partition and schema field name
          // as
          //   long as they are sourced from the same schema field
          Preconditions.checkArgument(
              schemaField == null || schemaField.fieldId() == sourceColumnId,
              "Cannot create identity partition sourced from different field in schema: %s",
              name);

Let me check if we should compare fieldId with each column id.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The current logic looks like

if (check conflicts). {
  if identity {
    make sure we aren't using a different field name for this identity transform
  } else {
   make sure we aren't matching any other column name
  }
}
Make sure it's not empty
Make sure we haven't already used this name for another partition
Add partition

I have no idea why those last 2 checks aren't part of the "if check conflicts" branch

Anyyyyyyway. I think this whole validation probably should be rewritten. The first branch we are checking basically based a lot of implicit assumptions when we should just be passing in the transform. But we don't have to do any of that now.

For now I think we should pass in sourceIds and just have the first branch include a "if sourceIds.length == 1"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

OK, let me refactore this part.

Comment thread core/src/main/java/org/apache/iceberg/PartitionSpecParser.java
@jbonofre
jbonofre force-pushed the multi-arg-transforms branch 3 times, most recently from a5b96f2 to 549c197 Compare April 25, 2025 13:29
Comment thread api/src/main/java/org/apache/iceberg/PartitionField.java Outdated
Comment thread api/src/main/java/org/apache/iceberg/PartitionField.java Outdated

for (UnboundPartitionField field : fields) {
Type fieldType = schema.findType(field.sourceId);
Type fieldType = schema.findType(field.sourceIds.get(0));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I know we don't have a multi-arg transform to resolve here yet but this doesn't seem like the right thing to do. I think Transforms.fromString needs to be modified to accept fromString(list[types], transformName)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, agree. Let me update that.

Comment thread api/src/main/java/org/apache/iceberg/UnboundPartitionSpec.java Outdated
Comment thread core/src/main/java/org/apache/iceberg/PartitionSpecParser.java Outdated
.isInstanceOf(IllegalArgumentException.class)
.hasMessage(
"Cannot parse partition field, either source-id or source-ids has to be present");
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should have some tests for "toJson" as well?

We also need a check that the validation for identity transforms still holds true. Ie you cannot make a multi-arg transform with the same name as any of the columns

@jbonofre jbonofre Apr 26, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I added a test in TestPartitionSpecParser testing when neither source-id and source-ids are provided: testFromJsonWithoutSourceIdAndSourceIds().
I will add additional tests for toJson path too.

@RussellSpitzer RussellSpitzer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Overall this looks pretty good to me. I think we are missing some testing but the approach looks correct. There are a few utility methods we need to fix up as well to accept sourceIds

@jbonofre

Copy link
Copy Markdown
Member Author

Thanks @RussellSpitzer ! Let me fix the util methods and add tests. Thanks again !

@jbonofre
jbonofre force-pushed the multi-arg-transforms branch from 549c197 to 1552f6c Compare April 26, 2025 04:28
Comment thread api/src/main/java/org/apache/iceberg/PartitionSpec.java Outdated
@jbonofre

Copy link
Copy Markdown
Member Author

Hey guys. I was traveling this week. I'm now back on this pr, updating according to the comments.

@jbonofre

Copy link
Copy Markdown
Member Author

I did a first update to introduce multi-args in Transform. I will check/update the tests too.

@jbonofre
jbonofre force-pushed the multi-arg-transforms branch 2 times, most recently from ccc3e48 to b1b407f Compare May 17, 2025 06:12
Comment thread api/src/main/java/org/apache/iceberg/PartitionSpec.java Outdated
Comment thread api/src/main/java/org/apache/iceberg/PartitionSpec.java
Comment thread api/src/main/java/org/apache/iceberg/UnboundPartitionSpec.java
@jbonofre

Copy link
Copy Markdown
Member Author

Finally back from several trips, so resuming work on this PR.

@jbonofre jbonofre added this to the Iceberg 1.10.0 milestone May 30, 2025
@github-actions github-actions Bot closed this Oct 30, 2025
@jbonofre

jbonofre commented Nov 3, 2025

Copy link
Copy Markdown
Member Author

Here we go again 😄

@jbonofre jbonofre reopened this Nov 3, 2025
@singhpk234 singhpk234 added not-stale and removed stale labels Nov 3, 2025
@jbonofre

jbonofre commented Dec 1, 2025

Copy link
Copy Markdown
Member Author

Back again :)

@stevenzwu stevenzwu removed this from the Iceberg 1.11.0 milestone Feb 2, 2026
@jbonofre

jbonofre commented Feb 24, 2026

Copy link
Copy Markdown
Member Author

I'm back (again 😄 ) on Iceberg, resuming my different PRs. Resuming the work.

@jbonofre
jbonofre force-pushed the multi-arg-transforms branch 5 times, most recently from eb304c2 to a84596f Compare March 7, 2026 14:09
Comment thread api/src/main/java/org/apache/iceberg/PartitionField.java
Comment thread api/src/main/java/org/apache/iceberg/PartitionField.java Outdated
return fieldList;
}

private ListMultimap<Integer, PartitionField> lazyFieldsBySourceId() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is broken now

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since it indexes by the first sourceId i'm not sure what will actually happen, we probably need to do something here

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I used a simple approach now, iterating on all source ids:

  for (PartitionField field : fields) {
      for (int sourceId : field.sourceIds()) {
          multiMap.put(sourceId, field);
      }
  }

I think it makes sense in this context.

Thoughts ?

if (element.has(FIELD_ID)) {
builder.addField(transform, sourceId, JsonUtil.getInt(FIELD_ID, element), name);
fieldIdCount++;
if (element.has(SOURCE_IDS)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we should at least warn here if source id is also present and has a different value. I could also see throwing an exception but we generally don't fail unless we absolutely have to when parsing. As long as the writer would override the bad source-id I think we are ok

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm hesitating here. To limit the impact, finally, I preferred to log (warn) when both source-ids and source-id are present (instead of failing).

Thoughts ?

@jbonofre
jbonofre force-pushed the multi-arg-transforms branch 2 times, most recently from 5c4b042 to ecde76e Compare March 13, 2026 19:41
@jbonofre
jbonofre force-pushed the multi-arg-transforms branch from 444ee1c to 410934d Compare March 23, 2026 10:09
Add a precondition guard on PartitionField.sourceId() to prevent misuse
with multi-arg transforms, fix toString() to use Joiner instead of
List.toString(), migrate all sourceId() call sites in PartitionSpec to
use sourceIds(), and add a consistency check in PartitionSpecParser when
both source-id and source-ids are present.
…source-ids

Follow reviewer guidance to warn rather than throw when parsing, since
Iceberg generally avoids hard failures during deserialization.
The testFromJsonFieldsWithMultipleSourceIds test was calling
sourceId() on a field with multiple source IDs, which now throws
after the hardening in 7a4cc92. Use sourceIds() instead.
@jbonofre
jbonofre force-pushed the multi-arg-transforms branch from 410934d to 80e8700 Compare April 20, 2026 06:07
@moomindani

Copy link
Copy Markdown
Contributor

Hi @jbonofre, are you still planning to pursue this PR? I'm asking because I recently implemented the v3 read-tolerance side of this in PyIceberg (apache/iceberg-python#3630: fields with multiple source-ids parse and are treated as unknown transforms, with spec-compliant source-id/source-ids round-trip), and I'd like to help bring the Java implementation to that baseline — today, metadata containing a source-ids field cannot be read at all. Happy to help here if that's useful.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants