feat(avro): apply column default values when reading missing fields - #800
feat(avro): apply column default values when reading missing fields#800huan233usc wants to merge 7 commits into
Conversation
75bd63f to
950f4dc
Compare
…3/4) When a column is present in the read schema but missing from an Avro data file (written before the column existed), fill it with the column's v3 initial-default instead of null. Reuses the shared arrow/literal_util materializer (merged in apache#792) and adds AppendDefaultToBuilder for the row-by-row Avro decode paths, plus a kDefault projection branch in the Avro schema/data projection. Part 3 of the v3 column-default-values work (POC apache#731), built on the schema support in apache#746 and the Parquet read path in apache#792.
Row-oriented Avro decode needs per-builder appends, while Parquet uses batch MakeDefaultArray. Share ToArrowScalar only; don't put the Avro shape into the shared arrow literal util.
0961b90 to
9e1605f
Compare
There was a problem hiding this comment.
Pull request overview
Adds Avro read-path support for Iceberg v3 initial-default column defaults, ensuring that when projected (table) schema fields are missing from older Avro files, readers materialize the specified defaults instead of null.
Changes:
- Avro schema projection: project missing fields with
initial-defaultasFieldProjection::Kind::kDefault. - Avro decode paths (generic + direct): materialize
kDefaultby appending the literal default into Arrow builders via a new Avro-localAppendDefaultToBuilder. - Tests: add unit coverage for default appends and an end-to-end Avro read test exercising schema evolution with defaults.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/iceberg/avro/avro_schema_util.cc | Projects missing fields with initial-default as kDefault. |
| src/iceberg/avro/avro_data_util.cc | Implements and uses AppendDefaultToBuilder to materialize kDefault during generic Avro decoding. |
| src/iceberg/avro/avro_direct_decoder.cc | Materializes kDefault during direct decoding into Arrow builders. |
| src/iceberg/avro/avro_data_util_internal.h | Exposes AppendDefaultToBuilder for reuse across Avro decode implementations. |
| src/iceberg/test/avro_data_test.cc | Adds unit tests covering default materialization behavior. |
| src/iceberg/test/avro_test.cc | Adds end-to-end test for reading missing fields with defaults using an evolved schema. |
Precompute cast scalars in FieldProjection attributes via PrepareDefaultScalars at reader init so per-row default fills only call AppendScalar.
| /// | ||
| /// Materialized once (see `PrepareDefaultScalars`) so row-by-row Avro decode only | ||
| /// needs `AppendScalar` instead of repeating `ToArrowScalar` / `CastTo` per row. | ||
| struct AvroDefaultAttributes : FieldProjection::ExtraAttributes { |
There was a problem hiding this comment.
Could this follow the ParquetExtraAttributes pattern and become AvroExtraAttributes with a default_scalar member? FieldProjection has a single attributes slot, so separate subclasses for future Avro attributes would not compose. A single Avro-specific container would also let the append path use checked_cast instead of a per-row dynamic_cast.
There was a problem hiding this comment.
Done — renamed to AvroExtraAttributes with a default_scalar member, mirroring ParquetExtraAttributes. Since Avro now has a single attributes container, the append path uses checked_cast instead of a per-row dynamic_cast, and future Avro attributes can be added to the same struct rather than as competing subclasses.
What
Part 3 of 4 of Iceberg v3 column default-value support (POC #731), built on the
schema layer (#746) and the Parquet read path (#792).
When a column is present in the read (table) schema but absent from an Avro data
file — because the column was added after those rows were written — fill it with
the column's v3
initial-defaultinstead ofnull.Changes
avro_schema_util.cc): when a field is missing from thefile and carries an
initial-default, project it asFieldProjection::Kind::kDefault, mirroring the generic / Parquet paths.avro_data_util.cc,avro_direct_decoder.cc): materializethe
kDefaultbranch through an Avro-localAppendDefaultToBuilderhelper.It reuses the shared
ToArrowScalarconversion, while keeping Avro'srow-by-row
ArrayBuilderappend behavior out of the shared Arrow utility.Tests
avro_data_test:AppendDefaultToBuilderappends a value and casts to thebuilder type;
AppendDatumToBuilderfills missing required and optionaldefault fields.
avro_test: end-to-end — write an Avro file with an old schema, then read itthrough
ReaderFactoryRegistrywith an evolved schema carrying defaults(
ReadMissingFieldsWithDefaults).Stack
addColumn/updateColumnDefault(feat: support v3 column default values in UpdateSchema (3/4) #793)Draft while the earlier PRs in the stack settle.