[CALCITE-4600] ClassCastException retrieving from an ARRAY that has DATE, TIME or TIMESTAMP elements
Support DATE/TIME/TIMESTAMP slot object's content for
Date/Time/Timestamp array accessors.
Rename method DateFromNumberAccessor#getDate to
DateFromNumberAccessor#getNumber.
Fix tests that were introduced in the following changes
(Alessandro Solimando):
* [CALCITE-4757] Allow columns of type Null in ResultSet
* [CALCITE-4536] Add support for BIT data type
Close apache/calcite-avatica#154
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 fa357d5..fb88ddd 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
@@ -919,6 +919,19 @@
}
return dateAsString(v.intValue(), null);
}
+
+ protected Number getNumber() throws SQLException {
+ final Object value = super.getObject();
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof Date) {
+ long time = ((Date) value).getTime();
+ time -= localCalendar.getTimeZone().getOffset(time);
+ return time / DateTimeUtils.MILLIS_PER_DAY;
+ }
+ return (Number) value;
+ }
}
/**
@@ -961,6 +974,17 @@
}
return timeAsString(v.intValue(), null);
}
+
+ protected Number getNumber() throws SQLException {
+ final Object v = super.getObject();
+ if (v == null) {
+ return null;
+ }
+ if (v instanceof Time) {
+ return ((Time) v).getTime();
+ }
+ return (Number) v;
+ }
}
/**
@@ -989,7 +1013,7 @@
}
@Override public Date getDate(Calendar calendar) throws SQLException {
- final Timestamp timestamp = getTimestamp(calendar);
+ final Timestamp timestamp = getTimestamp(calendar);
if (timestamp == null) {
return null;
}
@@ -997,7 +1021,7 @@
}
@Override public Time getTime(Calendar calendar) throws SQLException {
- final Timestamp timestamp = getTimestamp(calendar);
+ final Timestamp timestamp = getTimestamp(calendar);
if (timestamp == null) {
return null;
}
@@ -1013,6 +1037,17 @@
}
return timestampAsString(v.longValue(), null);
}
+
+ protected Number getNumber() throws SQLException {
+ final Object v = super.getObject();
+ if (v == null) {
+ return null;
+ }
+ if (v instanceof Timestamp) {
+ return ((Timestamp) v).getTime();
+ }
+ return (Number) v;
+ }
}
/**
diff --git a/core/src/main/java/org/apache/calcite/avatica/util/ArrayImpl.java b/core/src/main/java/org/apache/calcite/avatica/util/ArrayImpl.java
index 3cdc70a..e868d36 100644
--- a/core/src/main/java/org/apache/calcite/avatica/util/ArrayImpl.java
+++ b/core/src/main/java/org/apache/calcite/avatica/util/ArrayImpl.java
@@ -227,11 +227,21 @@
* Elements are compared using {@link Object#equals(Object)}, and null
* values are equal to each other. */
public static boolean equalContents(Array left, Array right) throws SQLException {
+ if (left.getBaseType() != right.getBaseType()) {
+ return false;
+ }
ResultSet leftResultSet = left.getResultSet();
ResultSet rightResultSet = right.getResultSet();
+ int leftColumnCount = leftResultSet.getMetaData().getColumnCount();
+ int rightColumnCount = rightResultSet.getMetaData().getColumnCount();
+ if (leftColumnCount != rightColumnCount) {
+ return false;
+ }
while (leftResultSet.next() && rightResultSet.next()) {
- if (!Objects.equals(leftResultSet.getObject(1), rightResultSet.getObject(1))) {
- return false;
+ for (int i = 1; i <= leftColumnCount; i++) {
+ if (!Objects.equals(leftResultSet.getObject(i), rightResultSet.getObject(i))) {
+ return false;
+ }
}
}
return !leftResultSet.next() && !rightResultSet.next();
diff --git a/core/src/test/java/org/apache/calcite/avatica/AvaticaResultSetConversionsTest.java b/core/src/test/java/org/apache/calcite/avatica/AvaticaResultSetConversionsTest.java
index c49e047..b61d9e5 100644
--- a/core/src/test/java/org/apache/calcite/avatica/AvaticaResultSetConversionsTest.java
+++ b/core/src/test/java/org/apache/calcite/avatica/AvaticaResultSetConversionsTest.java
@@ -57,9 +57,11 @@
import java.util.List;
import java.util.Map;
import java.util.Properties;
+import java.util.TimeZone;
import static org.hamcrest.CoreMatchers.isA;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -180,7 +182,28 @@
columnMetaData("null", 14,
ColumnMetaData.scalar(Types.NULL, "NULL",
ColumnMetaData.Rep.OBJECT),
- DatabaseMetaData.columnNullable));
+ DatabaseMetaData.columnNullable),
+ columnMetaData("date_array", 15,
+ ColumnMetaData.array(
+ ColumnMetaData.scalar(Types.DATE, "DATE",
+ ColumnMetaData.Rep.PRIMITIVE_INT),
+ "ARRAY",
+ ColumnMetaData.Rep.ARRAY),
+ DatabaseMetaData.columnNoNulls),
+ columnMetaData("timestamp_array", 16,
+ ColumnMetaData.array(
+ ColumnMetaData.scalar(Types.TIMESTAMP, "TIMESTAMP",
+ ColumnMetaData.Rep.PRIMITIVE_LONG),
+ "ARRAY",
+ ColumnMetaData.Rep.ARRAY),
+ DatabaseMetaData.columnNoNulls),
+ columnMetaData("time_array", 17,
+ ColumnMetaData.array(
+ ColumnMetaData.scalar(Types.TIME, "TIME",
+ ColumnMetaData.Rep.NUMBER),
+ "ARRAY",
+ ColumnMetaData.Rep.ARRAY),
+ DatabaseMetaData.columnNoNulls));
List<Object> row = Collections.<Object>singletonList(
new Object[] {
@@ -189,8 +212,11 @@
new Timestamp(1476130718123L),
Arrays.asList(1, 2, 3),
new StructImpl(Arrays.asList(42, false)),
- false,
+ true,
null,
+ Arrays.asList(123, 18234),
+ Arrays.asList(1476130718123L, 1479123123242L),
+ Arrays.asList(1476123L, 147912242L)
});
CursorFactory factory = CursorFactory.deduce(columns, null);
@@ -571,6 +597,60 @@
}
/**
+ * Accessor test helper for date array column.
+ */
+ private static final class DateArrayAccessorTestHelper extends AccessorTestHelper {
+ private DateArrayAccessorTestHelper(Getter g) {
+ super(g);
+ }
+
+ @Override public void testGetArray(ResultSet resultSet) throws SQLException {
+ ColumnMetaData.ScalarType intType =
+ ColumnMetaData.scalar(Types.DATE, "DATE", ColumnMetaData.Rep.PRIMITIVE_INT);
+ Array expectedArray =
+ new ArrayFactoryImpl(TimeZone.getTimeZone("UTC")).createArray(
+ intType, Arrays.asList(123, 18234));
+ assertTrue(ArrayImpl.equalContents(expectedArray, g.getArray(resultSet)));
+ }
+ }
+
+ /**
+ * Accessor test helper for time array column.
+ */
+ private static final class TimeArrayAccessorTestHelper extends AccessorTestHelper {
+ private TimeArrayAccessorTestHelper(Getter g) {
+ super(g);
+ }
+
+ @Override public void testGetArray(ResultSet resultSet) throws SQLException {
+ ColumnMetaData.ScalarType intType =
+ ColumnMetaData.scalar(Types.TIME, "TIME", ColumnMetaData.Rep.NUMBER);
+ Array expectedArray =
+ new ArrayFactoryImpl(TimeZone.getTimeZone("UTC")).createArray(
+ intType, Arrays.asList(1476123L, 147912242L));
+ assertTrue(ArrayImpl.equalContents(expectedArray, g.getArray(resultSet)));
+ }
+ }
+
+ /**
+ * Accessor test helper for timestamp array column.
+ */
+ private static final class TimestampArrayAccessorTestHelper extends AccessorTestHelper {
+ private TimestampArrayAccessorTestHelper(Getter g) {
+ super(g);
+ }
+
+ @Override public void testGetArray(ResultSet resultSet) throws SQLException {
+ ColumnMetaData.ScalarType intType =
+ ColumnMetaData.scalar(Types.TIMESTAMP, "TIMESTAMP", ColumnMetaData.Rep.PRIMITIVE_LONG);
+ Array expectedArray =
+ new ArrayFactoryImpl(TimeZone.getTimeZone("UTC")).createArray(
+ intType, Arrays.asList(1476130718123L, 1479123123242L));
+ assertTrue(ArrayImpl.equalContents(expectedArray, g.getArray(resultSet)));
+ }
+ }
+
+ /**
* Accessor test helper for row column.
*/
private static final class StructAccessorTestHelper extends AccessorTestHelper {
@@ -585,6 +665,23 @@
}
/**
+ * Accessor test helper for the (null) object column.
+ */
+ private static final class NullObjectAccessorTestHelper extends AccessorTestHelper {
+ private NullObjectAccessorTestHelper(Getter g) {
+ super(g);
+ }
+
+ @Override public void testGetString(ResultSet resultSet) throws SQLException {
+ assertNull(g.getString(resultSet));
+ }
+
+ @Override public void testGetObject(ResultSet resultSet) throws SQLException {
+ assertNull(g.getObject(resultSet));
+ }
+ }
+
+ /**
* Accessor test helper for the byte column.
*/
private static final class ByteAccessorTestHelper extends AccessorTestHelper {
@@ -1043,7 +1140,17 @@
new ArrayAccessorTestHelper(new OrdinalGetter(12)),
new ArrayAccessorTestHelper(new LabelGetter("array")),
new StructAccessorTestHelper(new OrdinalGetter(13)),
- new StructAccessorTestHelper(new LabelGetter("struct")));
+ new StructAccessorTestHelper(new LabelGetter("struct")),
+ new BooleanAccessorTestHelper(new OrdinalGetter(14)),
+ new BooleanAccessorTestHelper(new LabelGetter("bit")),
+ new NullObjectAccessorTestHelper(new OrdinalGetter(15)),
+ new NullObjectAccessorTestHelper(new LabelGetter("null")),
+ new DateArrayAccessorTestHelper(new OrdinalGetter(16)),
+ new DateArrayAccessorTestHelper(new LabelGetter("date_array")),
+ new TimestampArrayAccessorTestHelper(new OrdinalGetter(17)),
+ new TimestampArrayAccessorTestHelper(new LabelGetter("timestamp_array")),
+ new TimeArrayAccessorTestHelper(new OrdinalGetter(18)),
+ new TimeArrayAccessorTestHelper(new LabelGetter("time_array")));
}
private final AccessorTestHelper testHelper;