[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;