xia0c created AVRO-2602:
---------------------------

             Summary: Updating breaks backward compatibility by throwing 
AvroTypeException in some cases
                 Key: AVRO-2602
                 URL: https://issues.apache.org/jira/browse/AVRO-2602
             Project: Apache Avro
          Issue Type: Bug
    Affects Versions: 1.9.1, 1.8.2, 1.9.0, 1.8.1, 1.8.0
            Reporter: xia0c


When I try to update Avro from 1.7.7 to the newer version. The following code:

{code:java}
        @Test
        public void Demo() throws IOException{
                
        Schema schema = Schema.parse("{\"type\": \"enum\", \"name\": 
\"MyEnum\", \"symbols\": [\"A\", \"B\", \"C\"]}");

        Map<String, String> expectedA = new java.util.HashMap();
        expectedA.put("", "A");
        assertEquals(expectedA, write(schema, "A"));
                
        }
        
    private Map<String, String> write(Schema schema, Object datum) throws 
IOException {
        DatumWriter<Object> writer = new GenericDatumWriter<Object>(schema);
        Map<String, String> out = new java.util.HashMap();
        KeyValueEncoder encoder = new KeyValueEncoder(schema, out);
        writer.write(datum, encoder);
        return out;
    }
    

}

class KeyValueEncoder extends ParsingEncoder implements Parser.ActionHandler {
          final Parser parser;
          protected BitSet isEmpty = new BitSet();
          private java.util.Map<String, String> out;
          
          KeyValueEncoder(Schema sc, java.util.Map<String, String> out) throws 
IOException {
            configure(out);
            this.parser =
              new Parser(new JsonGrammarGenerator().generate(sc), this);
          }
          


          public void flush() throws IOException {
            // Do nothing
          }

          public KeyValueEncoder configure(java.util.Map<String, String> 
newOut) throws IOException {
            this.out = newOut;
            return this;
          }
          
          
/////////////////////////////////////////////////////////////////////////////

          @Override
          public void writeNull() throws IOException {
            parser.advance(Symbol.NULL);
          }

          @Override
          public void writeBoolean(boolean b) throws IOException {
            parser.advance(Symbol.BOOLEAN);
            out.put(getKeyPathString(), Boolean.toString(b));
          }

          @Override
          public void writeInt(int n) throws IOException {
            parser.advance(Symbol.INT);
            out.put(getKeyPathString(), Integer.toString(n));
          }

          @Override
          public void writeLong(long n) throws IOException {
            parser.advance(Symbol.LONG);
            out.put(getKeyPathString(), Long.toString(n));
          }

          @Override
          public void writeFloat(float f) throws IOException {
            parser.advance(Symbol.FLOAT);
            out.put(getKeyPathString(), Float.toString(f));
          }

          @Override
          public void writeDouble(double d) throws IOException {
            parser.advance(Symbol.DOUBLE);
            out.put(getKeyPathString(), Double.toString(d));
          }

          @Override
          public void writeString(Utf8 utf8) throws IOException {
            writeString(utf8.toString());
          }
          
          @Override 
          public void writeString(String str) throws IOException {
            parser.advance(Symbol.STRING);
            trace("writeString(" + str + ")");
            if (parser.topSymbol() == Symbol.MAP_KEY_MARKER) {
              parser.advance(Symbol.MAP_KEY_MARKER);
              pushKeyPathComponent(str);
              // out.writeFieldName(str);
            } else {
              out.put(getKeyPathString(), str);
            }
          }

          @Override
          public void writeBytes(ByteBuffer bytes) throws IOException {
            if (bytes.hasArray()) {
              writeBytes(bytes.array(), bytes.position(), bytes.remaining());
            } else {
              byte[] b = new byte[bytes.remaining()];
              for (int i = 0; i < b.length; i++) {
                b[i] = bytes.get();
              }
              writeBytes(b);
            }
          }

          @Override
          public void writeBytes(byte[] bytes, int start, int len) throws 
IOException {
            parser.advance(Symbol.BYTES);
            writeByteArray(bytes, start, len);
          }

          private void writeByteArray(byte[] bytes, int start, int len) throws 
IOException {
            out.put(getKeyPathString(), new String(bytes, start, len, "UTF-8"));
          }

          @Override
          public void writeFixed(byte[] bytes, int start, int len) throws 
IOException {
            throw new IOException("Fixed encoding is not implemented");
          }

          @Override
          public void writeEnum(int e) throws IOException {
            parser.advance(Symbol.ENUM);
            Symbol.EnumLabelsAction top = (Symbol.EnumLabelsAction) 
parser.popSymbol();
            trace("writeEnum(" + e + " : " + top.getLabel(e) + ")");
            if (e < 0 || e >= top.size) {
              throw new AvroTypeException(
                  "Enumeration out of range: max is " +
                  top.size + " but received " + e);
            }
            out.put(getKeyPathString(), top.getLabel(e));
          }

          @Override
          public void writeArrayStart() throws IOException {
            throw new IOException("Array encoding is not implemented");
          }

          @Override
          public void writeArrayEnd() throws IOException {
            if (! isEmpty.get(pos)) {
              parser.advance(Symbol.ITEM_END);
            }
            pop();
            parser.advance(Symbol.ARRAY_END);
            // out.writeEndArray();
          }

          @Override
          public void writeMapStart() throws IOException {
            push();
            isEmpty.set(depth());

            parser.advance(Symbol.MAP_START);
//           out.writeStartObject();
          }

          @Override
          public void writeMapEnd() throws IOException {
            if (! isEmpty.get(pos)) {
              parser.advance(Symbol.ITEM_END);
              popKeyPathComponent();
            }
            pop();

            parser.advance(Symbol.MAP_END);
            // out.writeEndObject();
          }

          @Override
          public void startItem() throws IOException {
            trace("startItem");
            if (! isEmpty.get(pos)) {
              parser.advance(Symbol.ITEM_END);
              popKeyPathComponent();
            }
            super.startItem();
            isEmpty.clear(depth());
          }

          @Override
          public void writeIndex(int unionIndex) throws IOException {
            parser.advance(Symbol.UNION);
            Symbol.Alternative top = (Symbol.Alternative) parser.popSymbol();
            Symbol symbol = top.getSymbol(unionIndex);
            if (symbol != Symbol.NULL) {
              pushKeyPathComponent(top.getLabel(unionIndex));
              parser.pushSymbol(Symbol.UNION_END);
            }
            parser.pushSymbol(symbol);
          }

          public Symbol doAction(Symbol input, Symbol top) throws IOException {
            if (top instanceof Symbol.FieldAdjustAction) {
              Symbol.FieldAdjustAction fa = (Symbol.FieldAdjustAction) top;
              pushKeyPathComponent(fa.fname);
            } else if (top == Symbol.RECORD_START) {
              // out.writeStartObject();
            } else if (top == Symbol.RECORD_END || top == Symbol.UNION_END) {
              // out.writeEndObject();
            } else if (top == Symbol.FIELD_END) {
              popKeyPathComponent();
            } else {
              throw new AvroTypeException("Unknown action symbol " + top);
            }
            return null;
          }

          
/////////////////////////////////////////////////////////////////////////////
          // Key path

          private java.util.ArrayList<String> keyPath = new 
java.util.ArrayList();

          private void pushKeyPathComponent(String component) {
            keyPath.add(component);
          }

          private void popKeyPathComponent() {
            keyPath.remove(keyPath.size() - 1);
          }

          private String getKeyPathString() {
            return StringUtils.join(keyPath, '|');
          }

          
/////////////////////////////////////////////////////////////////////////////

          private void trace(String s) {
            System.out.println(s + ":\t topSymbol=" + parser.topSymbol() + " 
keyPath=" + getKeyPathString());
          }
        
}
{code}

Throws an AvroTypeException error:

{code:java}
org.apache.avro.AvroTypeException: Not an enum: A for schema: 
{"type":"enum","name":"MyEnum","symbols":["A","B","C"]}
    at 
org.apache.avro.generic.GenericDatumWriter.writeEnum(GenericDatumWriter.java:218)
    at 
org.apache.avro.generic.GenericDatumWriter.writeWithoutConversion(GenericDatumWriter.java:133)
    at 
org.apache.avro.generic.GenericDatumWriter.write(GenericDatumWriter.java:82)
    at 
org.apache.avro.generic.GenericDatumWriter.write(GenericDatumWriter.java:72)
    at UTD.SeLab.BBI.BugDetection.TestAvro.write(TestAvro.java:42)
    at UTD.SeLab.BBI.BugDetection.TestAvro.Demo(TestAvro.java:34)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at 
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at 
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at 
org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at 
org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at 
org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at 
org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at 
org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at 
org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at 
org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at 
org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at 
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at 
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
    at 
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at 
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
{code}







--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to