You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by ni...@apache.org on 2016/10/04 14:25:03 UTC
avro git commit: AVRO-1923: Stop infinite recursion in
GenericData.toString
Repository: avro
Updated Branches:
refs/heads/master 2b1639ce5 -> e276aa5e4
AVRO-1923: Stop infinite recursion in GenericData.toString
Project: http://git-wip-us.apache.org/repos/asf/avro/repo
Commit: http://git-wip-us.apache.org/repos/asf/avro/commit/e276aa5e
Tree: http://git-wip-us.apache.org/repos/asf/avro/tree/e276aa5e
Diff: http://git-wip-us.apache.org/repos/asf/avro/diff/e276aa5e
Branch: refs/heads/master
Commit: e276aa5e40cf51e36b581750c135c9c3875cac84
Parents: 2b1639c
Author: Niels Basjes <nb...@bol.com>
Authored: Thu Sep 22 14:42:47 2016 +0200
Committer: Niels Basjes <nb...@bol.com>
Committed: Tue Oct 4 16:22:20 2016 +0200
----------------------------------------------------------------------
CHANGES.txt | 2 +
.../org/apache/avro/generic/GenericData.java | 21 ++++---
.../apache/avro/generic/TestGenericData.java | 60 ++++++++++++++++++++
3 files changed, 76 insertions(+), 7 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/avro/blob/e276aa5e/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 6bcafa3..f205783 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -73,6 +73,8 @@ Trunk (not yet released)
AVRO-1853: C++: Compiler::toBin crashes on empty string.
(Zoltan Ivanfi via tomwhite)
+ AVRO-1923: Stop infinite recursion in GenericData.toString. (Niels Basjes)
+
Avro 1.8.1 (14 May 2016)
INCOMPATIBLE CHANGES
http://git-wip-us.apache.org/repos/asf/avro/blob/e276aa5e/lang/java/avro/src/main/java/org/apache/avro/generic/GenericData.java
----------------------------------------------------------------------
diff --git a/lang/java/avro/src/main/java/org/apache/avro/generic/GenericData.java b/lang/java/avro/src/main/java/org/apache/avro/generic/GenericData.java
index 76eb625..0484c03 100644
--- a/lang/java/avro/src/main/java/org/apache/avro/generic/GenericData.java
+++ b/lang/java/avro/src/main/java/org/apache/avro/generic/GenericData.java
@@ -501,19 +501,24 @@ public class GenericData {
/** Renders a Java datum as <a href="http://www.json.org/">JSON</a>. */
public String toString(Object datum) {
StringBuilder buffer = new StringBuilder();
- toString(datum, buffer);
+ toString(datum, buffer, new IdentityHashMap<Object, Object>(128) );
return buffer.toString();
}
/** Renders a Java datum as <a href="http://www.json.org/">JSON</a>. */
- protected void toString(Object datum, StringBuilder buffer) {
+ protected void toString(Object datum, StringBuilder buffer, IdentityHashMap<Object, Object> seenObjects) {
+ if (seenObjects.containsKey(datum)) {
+ buffer.append(" \">>> CIRCULAR REFERENCE CANNOT BE PUT IN JSON STRING, ABORTING RECURSION<<<\" ");
+ return;
+ }
+ seenObjects.put(datum, datum);
if (isRecord(datum)) {
buffer.append("{");
int count = 0;
Schema schema = getRecordSchema(datum);
for (Field f : schema.getFields()) {
- toString(f.name(), buffer);
+ toString(f.name(), buffer, seenObjects);
buffer.append(": ");
- toString(getField(datum, f.name(), f.pos()), buffer);
+ toString(getField(datum, f.name(), f.pos()), buffer, seenObjects);
if (++count < schema.getFields().size())
buffer.append(", ");
}
@@ -524,7 +529,7 @@ public class GenericData {
long last = array.size()-1;
int i = 0;
for (Object element : array) {
- toString(element, buffer);
+ toString(element, buffer, seenObjects);
if (i++ < last)
buffer.append(", ");
}
@@ -535,9 +540,9 @@ public class GenericData {
@SuppressWarnings(value="unchecked")
Map<Object,Object> map = (Map<Object,Object>)datum;
for (Map.Entry<Object,Object> entry : map.entrySet()) {
- toString(entry.getKey(), buffer);
+ toString(entry.getKey(), buffer, seenObjects);
buffer.append(": ");
- toString(entry.getValue(), buffer);
+ toString(entry.getValue(), buffer, seenObjects);
if (++count < map.size())
buffer.append(", ");
}
@@ -558,6 +563,8 @@ public class GenericData {
buffer.append("\"");
buffer.append(datum);
buffer.append("\"");
+ } else if (datum instanceof GenericData) {
+ toString(datum, buffer, seenObjects);
} else {
buffer.append(datum);
}
http://git-wip-us.apache.org/repos/asf/avro/blob/e276aa5e/lang/java/avro/src/test/java/org/apache/avro/generic/TestGenericData.java
----------------------------------------------------------------------
diff --git a/lang/java/avro/src/test/java/org/apache/avro/generic/TestGenericData.java b/lang/java/avro/src/test/java/org/apache/avro/generic/TestGenericData.java
index 7b33971..09ed66a 100644
--- a/lang/java/avro/src/test/java/org/apache/avro/generic/TestGenericData.java
+++ b/lang/java/avro/src/test/java/org/apache/avro/generic/TestGenericData.java
@@ -25,6 +25,8 @@ import java.util.List;
import java.util.Collection;
import java.util.ArrayDeque;
+import static org.apache.avro.TestCircularReferences.Reference;
+import static org.apache.avro.TestCircularReferences.Referenceable;
import static org.junit.Assert.*;
import java.util.Arrays;
@@ -34,6 +36,7 @@ import org.apache.avro.Schema.Field;
import org.apache.avro.AvroRuntimeException;
import org.apache.avro.Schema.Type;
import org.apache.avro.SchemaBuilder;
+import org.apache.avro.TestCircularReferences.ReferenceManager;
import org.apache.avro.io.BinaryData;
import org.apache.avro.io.BinaryEncoder;
import org.apache.avro.io.EncoderFactory;
@@ -492,4 +495,61 @@ public class TestGenericData {
record.put("myString", "myValue");
assertTrue(GenericData.get().validate(unionSchema, record));
}
+
+ // Test copied from Apache Parquet: org.apache.parquet.avro.TestCircularReferences
+ @Test
+ public void testToStringRecursive() throws IOException {
+ ReferenceManager manager = new ReferenceManager();
+ GenericData model = new GenericData();
+ model.addLogicalTypeConversion(manager.getTracker());
+ model.addLogicalTypeConversion(manager.getHandler());
+
+ Schema parentSchema = Schema.createRecord("Parent", null, null, false);
+
+ Schema placeholderSchema = Schema.createRecord("Placeholder", null, null, false);
+ List<Schema.Field> placeholderFields = new ArrayList<Schema.Field>();
+ placeholderFields.add( // at least one field is needed to be a valid schema
+ new Schema.Field("id", Schema.create(Schema.Type.LONG), null, (Object)null));
+ placeholderSchema.setFields(placeholderFields);
+
+ Referenceable idRef = new Referenceable("id");
+
+ Schema parentRefSchema = Schema.createUnion(
+ Schema.create(Schema.Type.NULL),
+ Schema.create(Schema.Type.LONG),
+ idRef.addToSchema(placeholderSchema));
+
+ Reference parentRef = new Reference("parent");
+
+ List<Schema.Field> childFields = new ArrayList<Schema.Field>();
+ childFields.add(new Schema.Field("c", Schema.create(Schema.Type.STRING), null, (Object)null));
+ childFields.add(new Schema.Field("parent", parentRefSchema, null, (Object)null));
+ Schema childSchema = parentRef.addToSchema(
+ Schema.createRecord("Child", null, null, false, childFields));
+
+ List<Schema.Field> parentFields = new ArrayList<Schema.Field>();
+ parentFields.add(new Schema.Field("id", Schema.create(Schema.Type.LONG), null, (Object)null));
+ parentFields.add(new Schema.Field("p", Schema.create(Schema.Type.STRING), null, (Object)null));
+ parentFields.add(new Schema.Field("child", childSchema, null, (Object)null));
+ parentSchema.setFields(parentFields);
+
+ Schema schema = idRef.addToSchema(parentSchema);
+
+ Record parent = new Record(schema);
+ parent.put("id", 1L);
+ parent.put("p", "parent data!");
+
+ Record child = new Record(childSchema);
+ child.put("c", "child data!");
+ child.put("parent", parent);
+
+ parent.put("child", child);
+
+ try {
+ assertNotNull(parent.toString()); // This should not fail with an infinite recursion (StackOverflowError)
+ } catch (StackOverflowError e) {
+ fail("StackOverflowError occurred");
+ }
+ }
+
}