This is an automated email from the ASF dual-hosted git repository. rubenql pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/calcite-avatica.git
The following commit(s) were added to refs/heads/master by this push: new ed44494 [CALCITE-4181] Avatica throws exception when select field is a List<Object> (Kent Nguyen) ed44494 is described below commit ed444948659a55309860952e1bbb41ee77ff4699 Author: Kent Nguyen <kngu...@fontis.vn> AuthorDate: Thu Aug 20 13:29:15 2020 +0700 [CALCITE-4181] Avatica throws exception when select field is a List<Object> (Kent Nguyen) --- .../calcite/avatica/util/AbstractCursor.java | 1 + .../apache/calcite/avatica/util/ArrayImplTest.java | 214 +++++++++++++++++++++ 2 files changed, 215 insertions(+) diff --git a/core/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java b/core/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java index 5559f60..ce81049 100644 --- a/core/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java +++ b/core/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java @@ -1341,6 +1341,7 @@ public abstract class AbstractCursor implements Cursor { case Types.TIMESTAMP: case Types.STRUCT: case Types.JAVA_OBJECT: + case Types.OTHER: return componentAccessor.getObject(); default: throw new IllegalStateException("Unhandled ARRAY component type: " + componentType.rep diff --git a/core/src/test/java/org/apache/calcite/avatica/util/ArrayImplTest.java b/core/src/test/java/org/apache/calcite/avatica/util/ArrayImplTest.java index 2ebd13b..9133270 100644 --- a/core/src/test/java/org/apache/calcite/avatica/util/ArrayImplTest.java +++ b/core/src/test/java/org/apache/calcite/avatica/util/ArrayImplTest.java @@ -33,6 +33,7 @@ import java.sql.Types; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Objects; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -164,6 +165,219 @@ public class ArrayImplTest { } } + /** + * The same test as arrayOfStructs(), except we use List instead of ArrayImpl. + */ + @Test public void listOfStructs() throws Exception { + ColumnMetaData intMetaData = MetaImpl.columnMetaData("MY_INT", 1, int.class, false); + ColumnMetaData stringMetaData = MetaImpl.columnMetaData("MY_STRING", 2, String.class, true); + StructType structType = ColumnMetaData.struct(Arrays.asList(intMetaData, stringMetaData)); + Struct struct1 = new StructImpl(Arrays.asList(1, "one")); + Struct struct2 = new StructImpl(Arrays.asList(2, "two")); + Struct struct3 = new StructImpl(Arrays.asList(3)); + Struct struct4 = new StructImpl(Arrays.asList(4, "four")); + ArrayType arrayType = ColumnMetaData.array(structType, "OBJECT", Rep.STRUCT); + ColumnMetaData arrayMetaData = MetaImpl.columnMetaData("MY_ARRAY", 1, arrayType, false); + ArrayImpl.Factory factory = new ArrayFactoryImpl(Unsafe.localCalendar().getTimeZone()); + + List<Object> list1 = Arrays.asList(struct1, struct2); + List<Object> list2 = Arrays.asList(struct3, struct4); + List<List<Object>> rows = Arrays.asList(Arrays.asList(list1), Arrays.asList(list2)); + + try (Cursor cursor = new ListIteratorCursor(rows.iterator())) { + List<Accessor> accessors = cursor.createAccessors(Arrays.asList(arrayMetaData), + Unsafe.localCalendar(), factory); + assertEquals(1, accessors.size()); + Accessor accessor = accessors.get(0); + + assertTrue(cursor.next()); + Array actualArray = accessor.getArray(); + + Object[] arrayData = (Object[]) actualArray.getArray(); + assertEquals(2, arrayData.length); + Struct actualStruct = (Struct) arrayData[0]; + Object[] o = actualStruct.getAttributes(); + assertEquals(2, o.length); + assertEquals(1, o[0]); + assertEquals("one", o[1]); + + actualStruct = (Struct) arrayData[1]; + o = actualStruct.getAttributes(); + assertEquals(2, o.length); + assertEquals(2, o[0]); + assertEquals("two", o[1]); + + assertTrue(cursor.next()); + actualArray = accessor.getArray(); + arrayData = (Object[]) actualArray.getArray(); + assertEquals(2, arrayData.length); + actualStruct = (Struct) arrayData[0]; + o = actualStruct.getAttributes(); + assertEquals(1, o.length); + assertEquals(3, o[0]); + + actualStruct = (Struct) arrayData[1]; + o = actualStruct.getAttributes(); + assertEquals(2, o.length); + assertEquals(4, o[0]); + assertEquals("four", o[1]); + } + } + + /** + * Plain Java object class for the two tests that follow. + */ + static class PlainJavaObject { + public int intProperty; + public String stringProperty; + + PlainJavaObject(int intProp, String stringProp) { + intProperty = intProp; + stringProperty = stringProp; + } + + @Override public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o == null) { + return false; + } + + if (getClass() != o.getClass()) { + return false; + } + + PlainJavaObject pjo = (PlainJavaObject) o; + + return Objects.equals(stringProperty, pjo.stringProperty) + && Objects.equals(intProperty, pjo.intProperty); + } + + @Override public int hashCode() { + return Objects.hash(stringProperty, intProperty); + } + } + + /** + * Test case for when a column is an array of plain Java objects. + * This is a common use case when data come from a dynamic schema source. + */ + @Test public void arraysOfJavaObjects() throws Exception { + final ColumnMetaData.Rep rep = ColumnMetaData.Rep.of(PlainJavaObject.class); + ColumnMetaData.AvaticaType objectAvaticaType = ColumnMetaData.scalar( + Types.OTHER, + "OTHER", + rep + ); + + String arrayTypeName = "JavaType(" + PlainJavaObject.class.toString() + ") ARRAY"; + ArrayType arrayType = ColumnMetaData.array(objectAvaticaType, arrayTypeName, rep); + + ColumnMetaData javaObjectArrayMetaData = MetaImpl.columnMetaData( + "PLAIN_JAVA_OBJECT_ARRAY", + 1, + arrayType, + true + ); + + PlainJavaObject pjo1 = new PlainJavaObject(1, "one"); + PlainJavaObject pjo2 = new PlainJavaObject(2, "two"); + PlainJavaObject pjo3 = new PlainJavaObject(3, "three"); + PlainJavaObject pjo4 = new PlainJavaObject(4, "four"); + + ArrayImpl.Factory factory = new ArrayFactoryImpl(Unsafe.localCalendar().getTimeZone()); + + Array array1 = factory.createArray(objectAvaticaType, Arrays.asList(pjo1, pjo2)); + Array array2 = factory.createArray(objectAvaticaType, Arrays.asList(pjo3, pjo4)); + List<List<Object>> rows = Arrays.asList(Arrays.asList(array1), Arrays.asList(array2)); + + try (Cursor cursor = new ListIteratorCursor(rows.iterator())) { + List<Accessor> accessors = cursor.createAccessors( + Arrays.asList(javaObjectArrayMetaData), + Unsafe.localCalendar(), + factory + ); + assertEquals(1, accessors.size()); + Accessor accessor = accessors.get(0); + + assertTrue(cursor.next()); + Array actualArray = accessor.getArray(); + + Object[] arrayData = (Object[]) actualArray.getArray(); + assertEquals(2, arrayData.length); + assertEquals(pjo1, arrayData[0]); + assertEquals(pjo2, arrayData[1]); + + assertTrue(cursor.next()); + actualArray = accessor.getArray(); + arrayData = (Object[]) actualArray.getArray(); + assertEquals(2, arrayData.length); + assertEquals(pjo3, arrayData[0]); + assertEquals(pjo4, arrayData[1]); + } + } + + /** + * Test case for when a column is a list of plain Java objects. + */ + @Test public void listOfJavaObjects() throws Exception { + final ColumnMetaData.Rep rep = ColumnMetaData.Rep.of(PlainJavaObject.class); + final ColumnMetaData.Rep rep2 = ColumnMetaData.Rep.of(PlainJavaObject.class); + ColumnMetaData.AvaticaType objectAvaticaType = ColumnMetaData.scalar( + Types.OTHER, + "OTHER", + rep + ); + + String arrayTypeName = "JavaType(" + PlainJavaObject.class.toString() + ") ARRAY"; + ArrayType arrayType = ColumnMetaData.array(objectAvaticaType, arrayTypeName, rep2); + + ColumnMetaData javaObjectArrayMetaData = MetaImpl.columnMetaData( + "PLAIN_JAVA_OBJECT_ARRAY", + 1, + arrayType, + true + ); + + PlainJavaObject pjo1 = new PlainJavaObject(1, "one"); + PlainJavaObject pjo2 = new PlainJavaObject(2, "two"); + PlainJavaObject pjo3 = new PlainJavaObject(3, "three"); + PlainJavaObject pjo4 = new PlainJavaObject(4, "four"); + + ArrayImpl.Factory factory = new ArrayFactoryImpl(Unsafe.localCalendar().getTimeZone()); + + List<Object> list1 = Arrays.asList(pjo1, pjo2); + List<Object> list2 = Arrays.asList(pjo3, pjo4); + List<List<Object>> rows = Arrays.asList(Arrays.asList(list1), Arrays.asList(list2)); + + try (Cursor cursor = new ListIteratorCursor(rows.iterator())) { + List<Accessor> accessors = cursor.createAccessors( + Arrays.asList(javaObjectArrayMetaData), + Unsafe.localCalendar(), + factory + ); + assertEquals(1, accessors.size()); + Accessor accessor = accessors.get(0); + + assertTrue(cursor.next()); + Array actualArray = accessor.getArray(); + + Object[] arrayData = (Object[]) actualArray.getArray(); + assertEquals(2, arrayData.length); + assertEquals(pjo1, arrayData[0]); + assertEquals(pjo2, arrayData[1]); + + assertTrue(cursor.next()); + actualArray = accessor.getArray(); + arrayData = (Object[]) actualArray.getArray(); + assertEquals(2, arrayData.length); + assertEquals(pjo3, arrayData[0]); + assertEquals(pjo4, arrayData[1]); + } + } + @Test public void testArrayWithOffsets() throws Exception { // Define the struct type we're creating ScalarType intType = ColumnMetaData.scalar(Types.INTEGER, "INTEGER", Rep.INTEGER);