fix: table UUID partitions not working - #2916
Conversation
|
A few notes ... Avro has an 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 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 |
f92395e to
de83553
Compare
blackmwk
left a comment
There was a problem hiding this comment.
Thanks @JosephLenton for this fix!
5f0221f to
6ca9fa2
Compare
|
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
left a comment
There was a problem hiding this comment.
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) => { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn test_insert_into_partitioned_by_uuid() -> Result<()> { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
This I have added on my local branch and will push up.
6ca9fa2 to
a9fbd60
Compare
Which issue does this PR close?
Table Partitions work on writing with UUID values.
What changes are included in this PR?
PrimitiveLiteral::UInt128will now convert to aRawLiteralEnum::String, when the type is UUID.RawLiteralEnumfor UUID deserialisation.Are these changes tested?
I added: