Skip to content

fix: table UUID partitions not working - #2916

Open
JosephLenton wants to merge 6 commits into
apache:mainfrom
JosephLenton:fix-uuid-partitions-fail
Open

fix: table UUID partitions not working#2916
JosephLenton wants to merge 6 commits into
apache:mainfrom
JosephLenton:fix-uuid-partitions-fail

Conversation

@JosephLenton

Copy link
Copy Markdown

Which issue does this PR close?

Table Partitions work on writing with UUID values.

What changes are included in this PR?

  • PrimitiveLiteral::UInt128 will now convert to a RawLiteralEnum::String, when the type is UUID.
  • A similar case is added to RawLiteralEnum for UUID deserialisation.

Are these changes tested?

I added:

  • an integration test to confirm the UUID partition works.
  • a second test to confirm writing all of the primitive types work.

@JosephLenton JosephLenton changed the title Fix UUID partitions fail fix: table UUID partitions not working Jul 27, 2026
@JosephLenton

Copy link
Copy Markdown
Author

A few notes ... Avro has an avro::Value::Uuid type so I did look into adding a RawLiteralEnum::Uuid type. Then the types internally could all match up. However this doesn't play well with Serde + apache_avro::to_value. Uuid will only serialise to bytes or a string.

The other thing I found is with the integration tests. I found when I had multiple tests in the same file it would fail with a Reqwest + Tokio error. I believe in opendal-core there is a global Reqwest client, and this will keep sessions alive across multiple tests within the same program (i.e. a single integration test). It doesn't do this if the endpoint changes (this is relevant later).

As a workaround I put my second test into a second file.

I'd be happy to try to solve this in a followup ticket if there is interest, and any other work to make test simpler. The solution is technically straight forward, but would be a chunk of code. We are talking a custom S3 storage layer, changing OpenDal core, or moving to use test-containers (as the endpoint will be different across tests). So I've left that out.

@JosephLenton
JosephLenton force-pushed the fix-uuid-partitions-fail branch from f92395e to de83553 Compare July 27, 2026 19:44

@blackmwk blackmwk 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.

Thanks @JosephLenton for this fix!

Comment thread crates/integration_tests/src/lib.rs
Comment thread crates/iceberg/src/spec/values/serde.rs Outdated
@JosephLenton
JosephLenton force-pushed the fix-uuid-partitions-fail branch 2 times, most recently from 5f0221f to 6ca9fa2 Compare July 29, 2026 11:01
@JosephLenton

JosephLenton commented Jul 29, 2026

Copy link
Copy Markdown
Author

Hey @blackmwk those changes are done. Please take another look when you have time!

Btw whilst working on this I found a few items which I suspect to be bugs. I can add them as tickets later when I have time:

@mbutrovich
mbutrovich self-requested a review August 6, 2026 14:34

@mbutrovich mbutrovich left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

First pass, thanks for working on this @JosephLenton!

PrimitiveLiteral::Float(v) => RawLiteralEnum::Float(v.0),
PrimitiveLiteral::Double(v) => RawLiteralEnum::Double(v.0),
PrimitiveLiteral::String(v) => RawLiteralEnum::String(v),
PrimitiveLiteral::UInt128(v) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The crash in #2913 traces back to apache_avro's Value::validate_internal, which has match arms for (Value::String, Schema::Uuid) and (Value::Uuid, Schema::Uuid) but none for (Value::Bytes, Schema::Uuid) (apache-avro 0.21.0, types.rs around line 444-451). That diagnosis looks right.

But Schema::Uuid in apache_avro always serializes to {"type": "string", "logicalType": "uuid"} (schema.rs:2201-2206), and a Value::Uuid gets encoded as a length-prefixed string for that schema, not raw fixed bytes (encode.rs:135-142). The spec's Avro mapping for uuid is {"type": "fixed", "size": 16, "logicalType": "uuid"} (format/spec.md:936), and iceberg-java's TypeToSchema.UUID_SCHEMA builds exactly that, with UUIDWriter/UUIDReader calling encoder.writeFixed/reading a fixed 16-byte buffer.

avro/schema.rs:237 maps PrimitiveType::Uuid to AvroSchema::Uuid for the manifest schema, so doesn't this mean a manifest with a UUID partition column written after this fix ends up with that column typed as Avro string on disk, not fixed(16)? Avro string and fixed aren't resolution-compatible, so would a Java reader (or anything with a fixed(16) reader schema) fail to read it?

Would keeping this arm as Bytes (unchanged) and instead building a raw Fixed(16) schema with a logicalType: "uuid" attribute in avro/schema.rs (same pattern avro_decimal_schema already uses, wrapping Fixed for decimals) avoid the crash without changing the wire format? Value::Bytes already resolves fine against Schema::Fixed, and apache_avro's own schema parser already folds fixed(16)+logicalType=uuid back into Schema::Uuid on read, so the existing RawLiteralEnum::Bytes deserialize branch for Uuid a few lines down wouldn't need to change either.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I have made that change (to apply the work avro/schema.rs instead of values/serde.rs), and it makes a lot more sense.

However I am finding if I use {"type": "fixed", "size": 16} works, but {"type": "fixed", "size": 16, "logicalType": "uuid"} causes Avro to panic. The latter I am pretty certain I see Java using here: https://github.com/apache/iceberg/blob/0d60b2bb8780d781f4ceb69032418d222b649ae5/core/src/main/java/org/apache/iceberg/avro/TypeToSchema.java#L54-L55

I am wondering if a change is needed in Avro on this. I am going to have to go away and investigate further and come back.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Avro 0.21 seems to have a bug in it's Schema reading code handling {"type": "fixed", "size": 16, "logicalType": "uuid"}, specifically the logicalType: uuid part. It reads the type as Schema::UUID and transforms that to Schema::Bytes, which is wrong and mismatches in other parts causing a panic.

I'm pretty certain this is fixed in version 0.22, however that is not yet released. The release process is ongoing now.

Options are:

  • to go with {"type": "fixed", "size": 16} for now, which passes the DataFusion integration tests and seems to work fine. This is affecting a project where I work so this might be something I will use there.
  • wait until Avro 0.22 and update.
  • add a patch to work around the schema reading right now.

Comment thread crates/iceberg/src/spec/values/serde.rs Outdated
Comment thread crates/iceberg/src/spec/values/tests.rs Outdated
}

#[tokio::test]
async fn test_insert_into_partitioned_by_uuid() -> Result<()> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This round-trips entirely through iceberg-rust's own reader, so it wouldn't catch a mismatch between the Avro type written for the UUID column and what the spec (or another implementation) expects, since iceberg-rust just reads back what it wrote. Would it be worth asserting on the raw manifest's Avro schema for the partition field here, given that's the part actually in question?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This I have added on my local branch and will push up.

@JosephLenton
JosephLenton force-pushed the fix-uuid-partitions-fail branch from 6ca9fa2 to a9fbd60 Compare August 7, 2026 07:05
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.

Partitions with type UUID fail to create transactions

3 participants