You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@flink.apache.org by "ASF GitHub Bot (Jira)" <ji...@apache.org> on 2026/07/30 08:26:00 UTC

[jira] [Updated] (FLINK-40262) Support matching Avro record fields by name in the Avro RowData converters

     [ https://issues.apache.org/jira/browse/FLINK-40262?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

ASF GitHub Bot updated FLINK-40262:
-----------------------------------
    Labels: pull-request-available  (was: )

> Support matching Avro record fields by name in the Avro RowData converters
> --------------------------------------------------------------------------
>
>                 Key: FLINK-40262
>                 URL: https://issues.apache.org/jira/browse/FLINK-40262
>             Project: Flink
>          Issue Type: Improvement
>          Components: Formats (JSON, Avro, Parquet, ORC, SequenceFile)
>            Reporter: Pritam Kumar
>            Priority: Major
>              Labels: pull-request-available
>
> h2. Problem
>   {{RowDataToAvroConverters}} and {{AvroToRowDataConverters}} pair a {{RowType}} field with an Avro record field by *ordinal position*:
>   {code:java}
>   for (int i = 0; i < length; ++i) {
>       final Schema.Field schemaField = fields.get(i);
>       record.put(i, fieldConverters[i].convert(
>               schemaField.schema(), fieldGetters[i].getFieldOrNull(row)));
>   }
>   {code}
>   * a schema registry subject;
>   * the {{avro-confluent.schema}} option;
>   * the {{AvroRowDataSerializationSchema(rowType, nestedSchema, converter)}} and {{AvroRowDataDeserializationSchema(nestedSchema, converter, typeInfo)}} constructors, which downstream connectors use directly.
>   In those cases column {{i}} and Avro field {{i}} need not be the same field. Writing puts each value into whichever field happens to sit at that position, and reading does the mirror image. Where the types at a position differ the job fails
>   with a {{ClassCastException}}; where they happen to agree, the data is silently corrupted.
>   h3. Example
>   Table {{a STRING, b STRING}} against a registry schema that declares {{b}} before {{a}}. Both are strings, so nothing fails: {{a}}'s value is written into {{b}} and vice versa.
>   h2. Proposal
>   Add an explicit strategy and thread it through both converters:
>   {code:java}
>   public enum FieldMatching { INDEX, NAME }
>   {code}
>   * {{INDEX}} — today's behaviour, and the default. Unchanged.
>   * {{NAME}} — pair fields by name. Names are compared exactly first, then against Avro field {{aliases}}, and finally ignoring case ({{Locale.ROOT}}). Each stage runs to completion before the next, so a fuzzy match can never claim an Avro field
>   that another column matches exactly.
>   Every existing overload is kept and delegates with {{INDEX}}, so the change is binary compatible (japicmp clean).
>   h3. What NAME refuses
>   Name matching should not paper over a schema that does not line up. Resolution fails, naming the offending field, when:
>   * a column matches more than one Avro field;
>   * two columns match the same Avro field;
>   * a column has no Avro counterpart and we are writing — the column would be dropped;
>   * a {{NOT NULL}} column has no Avro counterpart and we are reading — it could only be read as {{NULL}};
>   * an Avro field that no column maps to is neither nullable nor declares a default — the record cannot be encoded at all, and Avro surfaces that as a bare {{NullPointerException}}.
>   Tolerated:
>   * an Avro field that no column maps to but that declares a default is written with that default;
>   * an Avro field that no column reads is ignored, which is ordinary projection.
>   h3. Cost
>   Resolution runs once per (row type, schema) pair and is memoized, so {{NAME}} costs one array lookup per field per record: no hashing, no lowercasing, no allocation on the hot path. The memo is {{transient}} and rebuilt after the converter is
>   shipped to a task.
>   h2. Scope
>   This issue covers the converters only, which makes the capability reachable from Java but not from SQL.
>   Exposing the strategy as an {{avro-confluent.field-matching}} option is deliberately left to a follow-up so that the two can be reviewed independently. That part also has to relax the order sensitive schema check in
>   {{RegistryAvroFormatFactory#getAvroSchema}}, which compares the converted {{DataType}} with {{equals()}} and therefore rejects a reordered schema during planning with _"Schema provided for 'avro-confluent' format does not match the table
>   schema"_ — so today such a schema never reaches the converters at all. Happy to fold that into this issue instead if reviewers would rather have it in one go.
>   The plain {{avro}} format needs no option: it always derives its schema from the table, so field order agrees by construction.
>   h2. Compatibility
>   {{INDEX}} is the default everywhere, no existing signature or default value changes, and japicmp reports no incompatibility.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)