Skip to content

Commit 4502c1b

Browse files
RyanSkrabaopwvhk
andauthored
AVRO-4209: [java] Reflect correctly on recursive schemas (#3765)
* AVRO-4209: [java] Reflect correctly on recursive schemas * Fix CodeQL / Inconsistent equals and hashCode --------- Co-authored-by: Oscar Westra van Holthe - Kind <822992+opwvhk@users.noreply.github.com>
1 parent e1894b3 commit 4502c1b

2 files changed

Lines changed: 62 additions & 0 deletions

File tree

lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,7 @@ protected Schema createSchema(Type type, Map<String, Schema> names) {
739739
boolean error = Throwable.class.isAssignableFrom(c);
740740
schema = Schema.createRecord(name, doc, space, error);
741741
consumeAvroAliasAnnotation(c, schema);
742+
names.put(fullName, schema);
742743
for (Field field : getCachedFields(c))
743744
if ((field.getModifiers() & (Modifier.TRANSIENT | Modifier.STATIC)) == 0
744745
&& !field.isAnnotationPresent(AvroIgnore.class)) {
@@ -781,6 +782,9 @@ protected Schema createSchema(Type type, Map<String, Schema> names) {
781782
}
782783
schema.addProp(meta.key(), meta.value());
783784
}
785+
// This is added immediately back into the names to ensure that the discoverable
786+
// order is maintained if its a LinkedHashMap.
787+
names.remove(fullName, schema);
784788
}
785789
names.put(fullName, schema);
786790
}

lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflect.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.util.HashMap;
3434
import java.util.List;
3535
import java.util.Map;
36+
import java.util.Objects;
3637
import java.util.Optional;
3738
import java.util.Random;
3839
import org.apache.avro.AvroRuntimeException;
@@ -1416,4 +1417,61 @@ void avroDoc() {
14161417
+ "{\"name\":\"foo\",\"type\":\"int\",\"doc\":\"Some Documentation\"}" + "]}");
14171418
}
14181419

1420+
// test recursive record schema
1421+
public static class TreeNode {
1422+
public int value = 0;
1423+
@Nullable
1424+
public TreeNode left;
1425+
@Nullable
1426+
public TreeNode right;
1427+
1428+
public TreeNode() {
1429+
}
1430+
1431+
public TreeNode(int value) {
1432+
this.value = value;
1433+
}
1434+
1435+
@Override
1436+
public boolean equals(Object o) {
1437+
if (!(o instanceof TreeNode))
1438+
return false;
1439+
TreeNode that = (TreeNode) o;
1440+
if (value != that.value || !Objects.equals(left, that.left) || !Objects.equals(right, that.right))
1441+
return false;
1442+
return true;
1443+
}
1444+
1445+
@Override
1446+
public int hashCode() {
1447+
return Objects.hash(value, left, right);
1448+
}
1449+
1450+
}
1451+
1452+
@Test
1453+
void recursiveRecord() throws Exception {
1454+
Schema schema = ReflectData.get().getSchema(TreeNode.class);
1455+
assertEquals("TreeNode", schema.getName());
1456+
assertEquals(3, schema.getFields().size());
1457+
1458+
// Verify that the left tree node contains the parent schema
1459+
Schema leftSchema = schema.getField("left").schema();
1460+
assertEquals(Schema.Type.UNION, leftSchema.getType());
1461+
assertEquals(2, leftSchema.getTypes().size());
1462+
assertEquals(Schema.Type.NULL, leftSchema.getTypes().get(0).getType());
1463+
assertEquals(schema, leftSchema.getTypes().get(1));
1464+
1465+
// Verify that the right tree node is the same union
1466+
Schema rightSchema = schema.getField("right").schema();
1467+
assertEquals(leftSchema, rightSchema);
1468+
1469+
// Test serialization with actual recursive data
1470+
TreeNode root = new TreeNode(100);
1471+
root.left = new TreeNode(90);
1472+
root.right = new TreeNode(101);
1473+
root.left.left = new TreeNode(-100);
1474+
1475+
checkReadWrite(root);
1476+
}
14191477
}

0 commit comments

Comments
 (0)