From ed2dbd4d871e9ab1992c39e38d9d56f33ce55127 Mon Sep 17 00:00:00 2001 From: Christophe Le Saec Date: Thu, 27 Jul 2023 14:38:11 +0200 Subject: [PATCH] AVRO-2764: explain union --- .../src/main/java/org/apache/avro/Schema.java | 8 ++ .../java/org/apache/avro/TestUnionError.java | 84 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 lang/java/avro/src/test/java/org/apache/avro/TestUnionError.java diff --git a/lang/java/avro/src/main/java/org/apache/avro/Schema.java b/lang/java/avro/src/main/java/org/apache/avro/Schema.java index 97513f52f12..14924c184ed 100644 --- a/lang/java/avro/src/main/java/org/apache/avro/Schema.java +++ b/lang/java/avro/src/main/java/org/apache/avro/Schema.java @@ -48,6 +48,8 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; + import org.apache.avro.util.internal.Accessor; import org.apache.avro.util.internal.Accessor.FieldAccessor; import org.apache.avro.util.internal.JacksonUtils; @@ -1280,6 +1282,12 @@ void toJson(Names names, JsonGenerator gen) throws IOException { type.toJson(names, gen); gen.writeEndArray(); } + + @Override + public String getName() { + return super.getName() + + this.getTypes().stream().map(Schema::getName).collect(Collectors.joining(", ", "[", "]")); + } } private static class FixedSchema extends NamedSchema { diff --git a/lang/java/avro/src/test/java/org/apache/avro/TestUnionError.java b/lang/java/avro/src/test/java/org/apache/avro/TestUnionError.java new file mode 100644 index 00000000000..7f5e48fb962 --- /dev/null +++ b/lang/java/avro/src/test/java/org/apache/avro/TestUnionError.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.avro; + +import org.apache.avro.generic.GenericData; +import org.apache.avro.generic.GenericDatumReader; +import org.apache.avro.generic.GenericDatumWriter; +import org.apache.avro.generic.GenericRecord; +import org.apache.avro.io.BinaryDecoder; +import org.apache.avro.io.BinaryEncoder; +import org.apache.avro.io.DecoderFactory; +import org.apache.avro.io.EncoderFactory; + +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class TestUnionError { + + @Test + void unionErrorMessage() throws IOException { + String writerSchemaJson = " {\n" + " \"type\" : \"record\",\n" + + " \"name\" : \"C\",\n" + " \"fields\" : [ {\n" + + " \"name\" : \"c\",\n" + " \"type\" : [ {\n" + + " \"type\" : \"record\",\n" + " \"name\" : \"A\",\n" + + " \"fields\" : [ {\n" + " \"name\" : \"amount\",\n" + + " \"type\" : \"int\"\n" + " } ]\n" + " }, {\n" + + " \"type\" : \"record\",\n" + " \"name\" : \"B\",\n" + + " \"fields\" : [ {\n" + " \"name\" : \"amount1\",\n" + + " \"type\" : \"int\"\n" + " } ]\n" + " } ]\n" + + " } ]\n" + " }"; + Schema writerSchema = new Schema.Parser().parse(writerSchemaJson); + + String readerSchemaJson = " {\n" + " \"type\" : \"record\",\n" + " \"name\" : \"C1\",\n" + + " \"fields\" : [ {\n" + " \"name\" : \"c\",\n" + + " \"type\" : [ {\n" + " \"type\" : \"record\",\n" + + " \"name\" : \"A\",\n" + " \"fields\" : [ {\n" + + " \"name\" : \"amount\",\n" + " \"type\" : \"int\"\n" + + " } ]\n" + " }, \"float\" ]\n" + " } ]\n" + " }"; + Schema readerSchema = new Schema.Parser().parse(readerSchemaJson); + + List unionSchemas = writerSchema.getField("c").schema().getTypes(); + + GenericRecord r = new GenericData.Record(writerSchema); + GenericRecord b = new GenericData.Record(unionSchemas.get(1)); + b.put("amount1", 12); + r.put("c", b); + + ByteArrayOutputStream outs = new ByteArrayOutputStream(); + GenericDatumWriter datumWriter = new GenericDatumWriter<>(writerSchema); + BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(outs, null); + datumWriter.write(r, encoder); + encoder.flush(); + + InputStream ins = new ByteArrayInputStream(outs.toByteArray()); + BinaryDecoder decoder = DecoderFactory.get().binaryDecoder(ins, null); + + GenericDatumReader datumReader = new GenericDatumReader<>(writerSchema, readerSchema); + AvroTypeException avroException = assertThrows(AvroTypeException.class, () -> datumReader.read(null, decoder)); + assertEquals("Found B, expecting union[A, float]", avroException.getMessage()); + } +}