44import java .math .BigDecimal ;
55import java .util .*;
66
7+ import org .apache .avro .LogicalTypes ;
78import org .apache .avro .Schema ;
89import org .apache .avro .Schema .Type ;
910import org .apache .avro .UnresolvedUnionException ;
@@ -164,6 +165,7 @@ protected GenericRecord _createRecord(Schema schema, Object currValue)
164165 // couldn't find an exact match
165166 schema = _recordOrMapFromUnion (schema );
166167 }
168+ type = schema .getType ();
167169 }
168170 if (type == Schema .Type .MAP ) {
169171 throw new IllegalStateException ("_createRecord should never be called for elements of type MAP" );
@@ -182,6 +184,7 @@ protected GenericRecord _createRecord(Schema schema)
182184 Type type = schema .getType ();
183185 if (type == Schema .Type .UNION ) {
184186 schema = _recordOrMapFromUnion (schema );
187+ type = schema .getType ();
185188 }
186189 if (type == Schema .Type .MAP ) {
187190 throw new IllegalStateException ("_createRecord should never be called for elements of type MAP" );
@@ -432,25 +435,41 @@ private static int _findNotNullIndex(List<Schema> types)
432435
433436 private static int _resolveBigDecimalIndex (Schema unionSchema , List <Schema > types ,
434437 BigDecimal value ) {
435- int match = -1 ;
438+ // Branches are considered in order of how well they retain the value,
439+ // regardless of declaration order: "decimal" first, then String, then Double
440+ int stringMatch = -1 ;
441+ int doubleMatch = -1 ;
436442
437443 for (int i = 0 , size = types .size (); i < size ; ++i ) {
438444 Schema schema = types .get (i );
439445 Schema .Type t = schema .getType ();
440446
441- if (t == Type .DOUBLE ) {
442- return i ;
443- }
444- // BigDecimals can be shoved into a double, but optimally would be a String or byte[] with logical type information
445- if (t == Type .DOUBLE ) {
446- match = i ;
447- continue ;
447+ if (t == Type .BYTES || t == Type .FIXED ) {
448+ // Best match: retains both scale and type.
449+ // NOTE: plain `bytes`/`fixed` must NOT be chosen, since conversion
450+ // requires the "decimal" logical type to exist
451+ if (schema .getLogicalType () instanceof LogicalTypes .Decimal ) {
452+ return i ;
453+ }
454+ } else if (t == Type .STRING ) {
455+ // Second best: retains all digits, but reads back as String
456+ if (stringMatch < 0 ) {
457+ stringMatch = i ;
458+ }
459+ } else if (t == Type .DOUBLE ) {
460+ // Last resort: lossy
461+ if (doubleMatch < 0 ) {
462+ doubleMatch = i ;
463+ }
448464 }
449465 }
450- if (match < 0 ) {
451- match = ReflectData . get (). resolveUnion ( unionSchema , value ) ;
466+ if (stringMatch >= 0 ) {
467+ return stringMatch ;
452468 }
453- return match ;
469+ if (doubleMatch >= 0 ) {
470+ return doubleMatch ;
471+ }
472+ return ReflectData .get ().resolveUnion (unionSchema , value );
454473 }
455474
456475 private static int _resolveMapIndex (Schema unionSchema , List <Schema > types ,
0 commit comments