Skip to content

Avro: read named type references and write unique fixed type names - #4119

Open
ozenyunus35 wants to merge 3 commits into
apache:mainfrom
ozenyunus35:fix/avro-named-type-references
Open

ozenyunus35 wants to merge 3 commits into
apache:mainfrom
ozenyunus35:fix/avro-named-type-references

Conversation

@ozenyunus35

@ozenyunus35 ozenyunus35 commented Oct 16, 2026 •

Copy link
Copy Markdown

Closes #4116

Rationale for this change

As described in the issue, an Avro named type (decimal_P_S, fixed_L, uuid_fixed) may only be defined once per schema; later uses must refer to it by name. PyIceberg couldn't read the reference form (TypeError: Type not recognized: decimal_10_2), so it failed on manifests written by Java and iceberg-rust, and it wrote the same definition every time a type was reused, which strict readers reject.

  • Read: AvroSchemaConversion remembers each named type (record, enum, fixed) it converts, and resolves a later string type with that name to the same Iceberg type. A repeated full definition is still accepted, so existing manifests keep working. The map is reset at the start of avro_to_iceberg.
  • Write: following the review, ConvertSchemaToAvro adds the field-id of the field, list element or map key/value that uses a fixed, decimal or uuid type to its name (decimal_10_2_1, fixed_3_5, uuid_fixed_7), the same way records are named r<field-id>. Field-ids are unique, so every definition has its own name and no bookkeeping of defined names is needed.

Are these changes tested?

Yes, in the existing test files:

  • tests/utils/test_schema_conversion.py: reading the reference form (the repro from the issue, plus fixed_3 and uuid_fixed), reading the repeated form, an unknown name still raising TypeError, and writing a schema with repeated decimal, fixed (one inside a struct) and uuid types, including a list element and a map key and value: each gets the field-id in its name and the result reads back to the same schema.
  • tests/avro/test_file.py: test_repeated_named_types_round_trip writes a file with two decimal(10,2) and two uuid fields through AvroOutputFile and reads it back with PyIceberg and with fastavro. Without the change, fastavro refuses the file with a SchemaParseException for the redefined name.

The new tests fail without the change. tests/utils/test_schema_conversion.py, tests/avro, tests/test_avro_sanitization.py and tests/utils/test_manifest.py pass. Failures in my environment from optional extras I didn't install are the same before and after the change. ruff 0.14.3 and mypy 1.18.2 (the pre-commit versions) are clean.

Are there any user-facing changes?

Manifests and manifest lists that refer to a named type by name can now be read, and the fixed, decimal and uuid types PyIceberg writes now have the field-id in their name.

I used an AI assistant to help draft parts of the code and tests; I reviewed the changes and ran the tests myself.

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

I've suggested another approach in one of the comments, let me know what you think 👍

Comment thread pyiceberg/utils/schema_conversion.py Outdated


# Avro types that have a name and can be referred to by it later in the schema
AVRO_NAMED_TYPES = {"record", "enum", "fixed"}

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.

Let's use a frozenset here

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.

Done in 4f95b2e.

Comment thread pyiceberg/utils/schema_conversion.py Outdated
if name in self.defined_names:
return name
self.defined_names.add(name)
return named_type

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.

I think we're doing quite a bit of additional bookkeeping. I'm wondering if it wouldn't be easier to include the field-id to the name (which is unique by definition).

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.

Good idea, thanks. 4f95b2e drops the bookkeeping: a fixed, decimal or uuid type now gets the field-id of the field, list element or map key/value that uses it in its name (e.g. decimal_10_2_1), like records are named r<field-id>. Reading the reference form is unchanged.

Review feedback: a fixed, decimal or uuid type now gets the field-id of
the field, list element or map key/value that uses it in its name, the
same way records are named r<field-id>. Field-ids are unique, so every
definition has its own name and the writer no longer needs to track which
names it has defined. AVRO_NAMED_TYPES is a frozenset.
@ozenyunus35 ozenyunus35 changed the title Avro: define named types once and read references to them Avro: read named type references and write unique fixed type names Oct 18, 2026

This branch has not been deployed

No deployments
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.

Avro: can't read named type references, and writes repeated definitions (e.g. multiple decimal partition fields)

2 participants