Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
Expand All @@ -69,17 +70,36 @@ public JavaTypeFactoryImpl(RelDataTypeSystem typeSystem) {
super(typeSystem);
}

/**
* Create a structured {@link RelDataType} from java type,
* throw exception if recursion is found.
*/
public RelDataType createStructType(Class type) {
Map<Type, Type> edges = new HashMap<>();
return createStructType(type, edges);
}

private RelDataType createStructType(Class type, Map<Type, Type> edges) {
Type checkType = edges.get(type);
while (checkType != null) {
if (checkType.equals(type)) {
throw new RuntimeException(
String.format(Locale.ROOT,
"Recursion in %s for creating struct type",
type.getTypeName()));
}
checkType = edges.get(checkType);
}
final List<RelDataTypeField> list = new ArrayList<>();
for (Field field : type.getFields()) {
if (!Modifier.isStatic(field.getModifiers())) {
// FIXME: watch out for recursion
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha, :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:)

final Type fieldType = fieldType(field);
edges.put(type, fieldType);
list.add(
new RelDataTypeFieldImpl(
field.getName(),
list.size(),
createType(fieldType)));
createType(fieldType, edges)));
}
}
return canonize(new JavaRecordType(list, type));
Expand Down Expand Up @@ -107,7 +127,16 @@ private Type fieldType(Field field) {
return klass;
}

/**
* Create a {@link RelDataType} from java type,
* throw exception if recursion is found.
*/
public RelDataType createType(Type type) {
Map<Type, Type> edges = new HashMap<>();
return createType(type, edges);
}

private RelDataType createType(Type type, Map<Type, Type> edges) {
if (type instanceof RelDataType) {
return (RelDataType) type;
}
Expand Down Expand Up @@ -155,7 +184,7 @@ public RelDataType createType(Type type) {
createTypeWithNullability(createSqlType(SqlTypeName.ANY), true),
createTypeWithNullability(createSqlType(SqlTypeName.ANY), true));
} else {
return createStructType(clazz);
return createStructType(clazz, edges);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
import org.junit.Test;

import java.lang.reflect.Type;
import java.util.Locale;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

/**
* Test for {@link org.apache.calcite.jdbc.JavaTypeFactoryImpl}.
Expand Down Expand Up @@ -89,6 +91,27 @@ public final class JavaTypeFactoryTest {
SqlTests.getTypeString(sqlStructType));
}

@Test public void testRecursion() {
try {
TYPE_FACTORY.createStructType(RecursionStruct.class);
fail();
} catch (RuntimeException e) {
assertEquals(e.getMessage(),
String.format(Locale.ROOT,
"Recursion in %s for creating struct type",
RecursionStruct.class.getTypeName()));
}
try {
TYPE_FACTORY.createStructType(RecursionStruct1.class);
fail();
} catch (RuntimeException e) {
assertEquals(e.getMessage(),
String.format(Locale.ROOT,
"Recursion in %s for creating struct type",
RecursionStruct1.class.getTypeName()));
}
}

private void assertRecordType(Type actual) {
String errorMessage =
"Type {" + actual.getTypeName() + "} is not a subtype of Types.RecordType";
Expand All @@ -105,6 +128,24 @@ private static class TwoFieldStruct {
public Integer intField;
public String strField;
}

/***/
private static class RecursionStruct {
public Integer intField;
public RecursionStruct next;
}

/***/
private static class RecursionStruct1 {
public Integer intField;
public RecursionStruct2 struct2;
}

/***/
private static class RecursionStruct2 {
public Integer intField;
public RecursionStruct1 struct1;
}
}

// End JavaTypeFactoryTest.java