Uploaded image for project: 'Apache Avro'
  1. Apache Avro
  2. AVRO-3989

Schema conversion for logical types ignored when type is part of union

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Open
    • Major
    • Resolution: Unresolved
    • 1.11.3
    • None
    • java
    • None

    Description

      I have a schema with an optional timestamp which is to be rendered as millis and the generated Java class fails to get the conversions array and method added, which means that at runtime I get a ClassCastException when an "Instant" object is cast to a "Long" because there was no registered converter:

      The element with an optional timestamp in my schema:

      {{{}}
        "name": "DecisionStartTime",
        "type": [
          "null",
          {
            "type": "long",
            "logicalType": "timestamp-millis"
      {{    }}}
        ],
        "default": null
      }

      The problem seems to be here:

      https://github.com/apache/avro/blame/5a60b5c43fe9c3bb40f4a1303913fdb3598bd4a5/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic/record.vm#L198

      Which only renders the conversion part if any of the fields has logicalType set and the actual method:

      https://github.com/apache/avro/blob/5a60b5c43fe9c3bb40f4a1303913fdb3598bd4a5/lang/java/compiler/src/main/java/org/apache/avro/compiler/specific/SpecificCompiler.java#L976C3-L983C4

      Does not take into account that a field which is a union does not have a logical type, however one of the union types might.

      Proposed solution - replace method

        public boolean hasLogicalTypeField(Schema schema) {
          for (Schema.Field field : schema.getFields()) {
            if (field.schema().getLogicalType() != null) {
              return true;
      {{      }}}
      {{    }}}
          return false;
      {{  }}}

      With a method like this:

        public boolean hasLogicalTypeField(Schema schema) {
          for (Schema.Field field : schema.getFields()) {
            if (field.schema().getLogicalType() != null) {
              return true;
            } else if (field.schema().type == Type.UNION) {
              for (Schema type : field.schema().getTypes()) {
                if (type.getLogicalType() != null) {
                  return true;
      {{          }}}
      {{        }}}
      {{      }}}
      {{    }}}
          return false;
      {{  }}}

      The conversionInstance() method might also need tweaking in order to identify that the given type has a logical type as part of the union.

      Attachments

        1. image-2024-06-02-21-32-25-461.png
          72 kB
          Jacob Stampe Mikkelsen

        Issue Links

          Activity

            People

              Unassigned Unassigned
              jasmdk Jacob Stampe Mikkelsen
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated: