You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2015/04/20 21:15:23 UTC

[1/2] incubator-calcite git commit: [CALCITE-677] RemoteDriverTest.testTypeHandling fails east of Greenwich

Repository: incubator-calcite
Updated Branches:
  refs/heads/master 9e272f291 -> 7f8ce1978


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/avatica/src/main/java/org/apache/calcite/avatica/remote/LocalService.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/LocalService.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/LocalService.java
index f35265c..bea1664 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/LocalService.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/LocalService.java
@@ -81,8 +81,9 @@ public class LocalService implements Service {
   private List<List<Object>> list2(Meta.MetaResultSet resultSet) {
     final Meta.StatementHandle h = new Meta.StatementHandle(
         resultSet.connectionId, resultSet.statementId, null);
+    final List<TypedValue> parameterValues = Collections.emptyList();
     final Iterable<Object> iterable = meta.createIterable(h,
-        resultSet.signature, Collections.emptyList(), resultSet.firstFrame);
+        resultSet.signature, parameterValues, resultSet.firstFrame);
     final List<List<Object>> list = new ArrayList<>();
     return MetaImpl.collect(resultSet.signature.cursorFactory, iterable, list);
   }
@@ -161,7 +162,7 @@ public class LocalService implements Service {
         request.connectionId, request.statementId, null);
     final Meta.Frame frame =
         meta.fetch(h,
-            TypedValue.values(request.parameterValues),
+            request.parameterValues,
             request.offset,
             request.fetchMaxRowCount);
     return new FetchResponse(frame);

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/avatica/src/main/java/org/apache/calcite/avatica/remote/RemoteMeta.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/RemoteMeta.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/RemoteMeta.java
index 8a83ff9..4267186 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/RemoteMeta.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/RemoteMeta.java
@@ -173,12 +173,11 @@ class RemoteMeta extends MetaImpl {
     }
   }
 
-  @Override public Frame fetch(StatementHandle h, List<Object> parameterValues,
-      int offset, int fetchMaxRowCount) {
-    final List<TypedValue> typedValues = TypedValue.list(parameterValues);
+  @Override public Frame fetch(StatementHandle h,
+      List<TypedValue> parameterValues, int offset, int fetchMaxRowCount) {
     final Service.FetchResponse response =
         service.apply(
-            new Service.FetchRequest(h.connectionId, h.id, typedValues,
+            new Service.FetchRequest(h.connectionId, h.id, parameterValues,
                 offset, fetchMaxRowCount));
     return response.frame;
   }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/avatica/src/main/java/org/apache/calcite/avatica/remote/TypedValue.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/TypedValue.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/TypedValue.java
index 04c4199..d12fc40 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/TypedValue.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/TypedValue.java
@@ -23,11 +23,103 @@ import org.apache.calcite.avatica.util.DateTimeUtils;
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
 
+import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
 import java.util.List;
 
-/** Value and type. */
+/** Value and type.
+ *
+ * <p>There are 3 representations:
+ * <ul>
+ *   <li>JDBC - the representation used by JDBC get and set methods
+ *   <li>Serial - suitable for serializing using JSON
+ *   <li>Local - used by Calcite for efficient computation
+ * </ul>
+ *
+ * <p>The following table shows the Java type(s) that may represent each SQL
+ * type in each representation.
+ *
+ * <table>
+ *   <caption>SQL types and their representations</caption>
+ *   <tr>
+ *     <th>Type</th> <th>JDBC</th> <th>Serial</th> <th>Local</th>
+ *   </tr>
+ *   <tr>
+ *     <td>BOOLEAN</td> <td>boolean</td> <td>boolean</td> <td>boolean</td>
+ *   </tr>
+ *   <tr>
+ *     <td>BINARY, VARBINARY</td> <td>byte[]</td>
+ *                    <td>String (base64)</td> <td>{@link ByteString}</td>
+ *   </tr>
+ *   <tr>
+ *     <td>DATE</td> <td>{@link java.sql.Date}</td>
+ *                                   <td>int</td> <td>int</td>
+ *   </tr>
+ *   <tr>
+ *     <td>TIME</td> <td>{@link java.sql.Time}</td>
+ *                                   <td>int</td> <td>int</td>
+ *   </tr>
+ *   <tr>
+ *     <td>DATE</td> <td>{@link java.sql.Timestamp}</td>
+ *                                   <td>long</td> <td>long</td>
+ *   </tr>
+ *   <tr>
+ *     <td>CHAR, VARCHAR</td>
+ *                   <td>String</td> <td>String</td> <td>String</td>
+ *   </tr>
+ *   <tr>
+ *     <td>TINYINT</td> <td>byte</td> <td>Number</td> <td>byte</td>
+ *   </tr>
+ *   <tr>
+ *     <td>SMALLINT</td> <td>short</td> <td>Number</td> <td>short</td>
+ *   </tr>
+ *   <tr>
+ *     <td>INTEGER</td> <td>int</td> <td>Number</td> <td>int</td>
+ *   </tr>
+ *   <tr>
+ *     <td>BIGINT</td> <td>long</td> <td>Number</td> <td>long</td>
+ *   </tr>
+ *   <tr>
+ *     <td>REAL</td> <td>float</td> <td>Number</td> <td>float</td>
+ *   </tr>
+ *   <tr>
+ *     <td>FLOAT, DOUBLE</td>
+ *                   <td>double</td> <td>Number</td> <td>double</td>
+ *   </tr>
+ *   <tr>
+ *     <td>DECIMAL</td>
+ *                   <td>BigDecimal</td> <td>Number</td> <td>BigDecimal</td>
+ *   </tr>
+ * </table>
+ *
+ * Note:
+ * <ul>
+ *   <li>The various numeric types (TINYINT, SMALLINT, INTEGER, BIGINT, REAL,
+ *   FLOAT, DOUBLE) are represented by {@link Number} in serial format because
+ *   JSON numbers are not strongly typed. A {@code float} value {@code 3.0} is
+ *   transmitted as {@code 3}, and is therefore decoded as an {@code int}.
+ *
+ *   <li>The date-time types (DATE, TIME, TIMESTAMP) are represented in JDBC as
+ *   {@link java.sql.Date}, {@link java.sql.Time}, {@link java.sql.Timestamp},
+ *   all sub-classes of {@link java.util.Date}. When they are passed to and
+ *   from the server, they are interpreted in terms of a time zone, by default
+ *   the current connection's time zone. Their serial and local representations
+ *   as {@code int} (days since 1970-01-01 for DATE, milliseconds since
+ *   00:00:00.000 for TIME), and long (milliseconds since 1970-01-01
+ *   00:00:00.000 for TIMESTAMP) are easier to work with, because it is clear
+ *   that time zone is not involved.
+ *
+ *   <li>BINARY and VARBINARY values are represented as base64-encoded strings
+ *   for serialization over JSON.
+ * </ul>
+ */
 public class TypedValue {
+  public static final TypedValue NULL =
+      new TypedValue(ColumnMetaData.Rep.OBJECT, null);
+
   /** Type of the value. */
   public final ColumnMetaData.Rep type;
 
@@ -39,31 +131,83 @@ public class TypedValue {
 
   private TypedValue(ColumnMetaData.Rep rep, Object value) {
     this.type = rep;
-    this.value = serialize(rep, value);
+    this.value = value;
+    assert isSerial(rep, value) : "rep: " + rep + ", value: " + value;
+  }
+
+  private boolean isSerial(ColumnMetaData.Rep rep, Object value) {
+    if (value == null) {
+      return true;
+    }
+    switch (rep) {
+    case BYTE_STRING:
+      return value instanceof String;
+    case JAVA_SQL_DATE:
+    case JAVA_SQL_TIME:
+      return value instanceof Integer;
+    case JAVA_SQL_TIMESTAMP:
+    case JAVA_UTIL_DATE:
+      return value instanceof Long;
+    default:
+      return true;
+    }
   }
 
   @JsonCreator
   public static TypedValue create(@JsonProperty("type") String type,
       @JsonProperty("value") Object value) {
+    if (value == null) {
+      return NULL;
+    }
     ColumnMetaData.Rep rep = ColumnMetaData.Rep.valueOf(type);
-    return new TypedValue(rep, deserialize(rep, value));
+    return ofLocal(rep, serialToLocal(rep, value));
   }
 
-  /** Converts the value into the preferred representation.
+  /** Creates a TypedValue from a value in local representation. */
+  public static TypedValue ofLocal(ColumnMetaData.Rep rep, Object value) {
+    return new TypedValue(rep, localToSerial(rep, value));
+  }
+
+  /** Creates a TypedValue from a value in serial representation. */
+  public static TypedValue ofSerial(ColumnMetaData.Rep rep, Object value) {
+    return new TypedValue(rep, value);
+  }
+
+  /** Creates a TypedValue from a value in JDBC representation. */
+  public static TypedValue ofJdbc(ColumnMetaData.Rep rep, Object value,
+      Calendar calendar) {
+    if (value == null) {
+      return NULL;
+    }
+    return new TypedValue(rep, jdbcToSerial(rep, value, calendar));
+  }
+
+  /** Creates a TypedValue from a value in JDBC representation,
+   * deducing its type. */
+  public static TypedValue ofJdbc(Object value, Calendar calendar) {
+    if (value == null) {
+      return NULL;
+    }
+    final ColumnMetaData.Rep rep = ColumnMetaData.Rep.of(value.getClass());
+    return new TypedValue(rep, jdbcToSerial(rep, value, calendar));
+  }
+
+  /** Converts the value into the local representation.
    *
    * <p>For example, a byte string is represented as a {@link ByteString};
    * a long is represented as a {@link Long} (not just some {@link Number}).
    */
-  public Object deserialize() {
-    return deserialize(type, value);
+  public Object toLocal() {
+    if (value == null) {
+      return null;
+    }
+    return serialToLocal(type, value);
   }
 
   /** Converts a value to the exact type required for the given
    * representation. */
-  private static Object deserialize(ColumnMetaData.Rep rep, Object value) {
-    if (value == null) {
-      return null;
-    }
+  private static Object serialToLocal(ColumnMetaData.Rep rep, Object value) {
+    assert value != null;
     if (value.getClass() == rep.clazz) {
       return value;
     }
@@ -73,44 +217,105 @@ public class TypedValue {
     case SHORT:
       return ((Number) value).shortValue();
     case INTEGER:
+    case JAVA_SQL_DATE:
+    case JAVA_SQL_TIME:
       return ((Number) value).intValue();
     case LONG:
+    case JAVA_UTIL_DATE:
+    case JAVA_SQL_TIMESTAMP:
       return ((Number) value).longValue();
     case FLOAT:
       return ((Number) value).floatValue();
     case DOUBLE:
       return ((Number) value).doubleValue();
+    case NUMBER:
+      return value instanceof BigDecimal ? value
+          : value instanceof BigInteger ? new BigDecimal((BigInteger) value)
+          : value instanceof Double ? new BigDecimal((Double) value)
+          : value instanceof Float ? new BigDecimal((Float) value)
+          : new BigDecimal(((Number) value).longValue());
     case BYTE_STRING:
       return ByteString.ofBase64((String) value);
+    default:
+      throw new IllegalArgumentException("cannot convert " + value + " ("
+          + value.getClass() + ") to " + rep);
+    }
+  }
+
+  /** Converts the value into the JDBC representation.
+   *
+   * <p>For example, a byte string is represented as a {@link ByteString};
+   * a long is represented as a {@link Long} (not just some {@link Number}).
+   */
+  public Object toJdbc(Calendar calendar) {
+    if (value == null) {
+      return null;
+    }
+    return serialToJdbc(type, value, calendar);
+  }
+
+  private static Object serialToJdbc(ColumnMetaData.Rep type, Object value,
+      Calendar calendar) {
+    switch (type) {
+    case BYTE_STRING:
+      return ByteString.ofBase64((String) value).getBytes();
     case JAVA_UTIL_DATE:
-      return new java.util.Date((Long) value);
+      return new java.util.Date(adjust((Number) value, calendar));
     case JAVA_SQL_DATE:
-      return new java.sql.Date((long) (Integer) value
-          * DateTimeUtils.MILLIS_PER_DAY);
+      return new java.sql.Date(
+          adjust(((Number) value).longValue() * DateTimeUtils.MILLIS_PER_DAY,
+              calendar));
     case JAVA_SQL_TIME:
-      return new java.sql.Time((Integer) value);
+      return new java.sql.Time(adjust((Number) value, calendar));
     case JAVA_SQL_TIMESTAMP:
-      return new java.sql.Timestamp((Long) value);
+      return new java.sql.Timestamp(adjust((Number) value, calendar));
     default:
-      throw new IllegalArgumentException("cannot convert " + value + " ("
-          + value.getClass() + ") to " + rep);
+      return serialToLocal(type, value);
     }
   }
 
-  /** Converts a value to a type that can be serialized as JSON. */
-  private static Object serialize(ColumnMetaData.Rep rep, Object value) {
+  private static long adjust(Number number, Calendar calendar) {
+    long t = number.longValue();
+    if (calendar != null) {
+      t -= calendar.getTimeZone().getOffset(t);
+    }
+    return t;
+  }
+
+  /** Converts a value from JDBC format to a type that can be serialized as
+   * JSON. */
+  private static Object jdbcToSerial(ColumnMetaData.Rep rep, Object value,
+      Calendar calendar) {
     switch (rep) {
     case BYTE_STRING:
-      return ((ByteString) value).toBase64String();
+      return new ByteString((byte[]) value).toBase64String();
     case JAVA_UTIL_DATE:
     case JAVA_SQL_TIMESTAMP:
-      return ((java.util.Date) value).getTime();
     case JAVA_SQL_DATE:
-      return (int) DateTimeUtils.floorDiv(((java.sql.Date) value).getTime(),
-          DateTimeUtils.MILLIS_PER_DAY);
     case JAVA_SQL_TIME:
-      return (int) DateTimeUtils.floorMod(((java.sql.Time) value).getTime(),
-          DateTimeUtils.MILLIS_PER_DAY);
+      long t = ((Date) value).getTime();
+      if (calendar != null) {
+        t += calendar.getTimeZone().getOffset(t);
+      }
+      switch (rep) {
+      case JAVA_SQL_DATE:
+        return (int) DateTimeUtils.floorDiv(t, DateTimeUtils.MILLIS_PER_DAY);
+      case JAVA_SQL_TIME:
+        return (int) DateTimeUtils.floorMod(t, DateTimeUtils.MILLIS_PER_DAY);
+      default:
+        return t;
+      }
+    default:
+      return value;
+    }
+  }
+
+  /** Converts a value from internal format to a type that can be serialized
+   * as JSON. */
+  private static Object localToSerial(ColumnMetaData.Rep rep, Object value) {
+    switch (rep) {
+    case BYTE_STRING:
+      return ((ByteString) value).toBase64String();
     default:
       return value;
     }
@@ -120,27 +325,10 @@ public class TypedValue {
   public static List<Object> values(List<TypedValue> typedValues) {
     final List<Object> list = new ArrayList<>();
     for (TypedValue typedValue : typedValues) {
-      list.add(typedValue.deserialize());
+      list.add(typedValue.toLocal());
     }
     return list;
   }
-
-  /** Converts a list of values to a list of {@code TypedValue}. */
-  public static List<TypedValue> list(List<Object> values) {
-    final List<TypedValue> list = new ArrayList<>();
-    for (Object value : values) {
-      list.add(create(value));
-    }
-    return list;
-  }
-
-  private static TypedValue create(Object value) {
-    if (value == null) {
-      return new TypedValue(ColumnMetaData.Rep.OBJECT, null);
-    }
-    final ColumnMetaData.Rep rep = ColumnMetaData.Rep.of(value.getClass());
-    return new TypedValue(rep, value);
-  }
 }
 
 // End TypedValue.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/avatica/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java b/avatica/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java
index 8871ea3..17ccfde 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java
@@ -16,7 +16,7 @@
  */
 package org.apache.calcite.avatica.util;
 
-import org.apache.calcite.avatica.AvaticaParameter;
+import org.apache.calcite.avatica.AvaticaSite;
 import org.apache.calcite.avatica.AvaticaUtils;
 import org.apache.calcite.avatica.ColumnMetaData;
 
@@ -666,7 +666,7 @@ public abstract class AbstractCursor implements Cursor {
       if (n == null) {
         return null;
       }
-      return AvaticaParameter.toBigDecimal(n)
+      return AvaticaSite.toBigDecimal(n)
           .setScale(scale, BigDecimal.ROUND_UNNECESSARY);
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/avatica/src/main/java/org/apache/calcite/avatica/util/DateTimeUtils.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/util/DateTimeUtils.java b/avatica/src/main/java/org/apache/calcite/avatica/util/DateTimeUtils.java
index 92f50ef..a9b17a7 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/util/DateTimeUtils.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/util/DateTimeUtils.java
@@ -700,6 +700,17 @@ public class DateTimeUtils {
     }
   }
 
+  /** Resets to zero the "time" part of a timestamp. */
+  public static long resetTime(long timestamp) {
+    int date = (int) (timestamp / MILLIS_PER_DAY);
+    return (long) date * MILLIS_PER_DAY;
+  }
+
+  /** Resets to epoch (1970-01-01) the "date" part of a timestamp. */
+  public static long resetDate(long timestamp) {
+    return floorMod(timestamp, MILLIS_PER_DAY);
+  }
+
   public static long unixTimestampFloor(TimeUnitRange range, long timestamp) {
     int date = (int) (timestamp / MILLIS_PER_DAY);
     final int f = julianDateFloor(range, date + EPOCH_JULIAN, true);

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java
index 94b2bb7..f3819fe 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java
@@ -20,12 +20,13 @@ import org.apache.calcite.DataContext;
 import org.apache.calcite.adapter.java.JavaTypeFactory;
 import org.apache.calcite.avatica.AvaticaConnection;
 import org.apache.calcite.avatica.AvaticaFactory;
-import org.apache.calcite.avatica.AvaticaParameter;
+import org.apache.calcite.avatica.AvaticaSite;
 import org.apache.calcite.avatica.AvaticaStatement;
 import org.apache.calcite.avatica.Helper;
 import org.apache.calcite.avatica.InternalProperty;
 import org.apache.calcite.avatica.Meta;
 import org.apache.calcite.avatica.UnregisteredDriver;
+import org.apache.calcite.avatica.remote.TypedValue;
 import org.apache.calcite.config.CalciteConnectionConfig;
 import org.apache.calcite.config.CalciteConnectionConfigImpl;
 import org.apache.calcite.linq4j.BaseQueryable;
@@ -224,10 +225,10 @@ abstract class CalciteConnectionImpl
       CalcitePrepare.CalciteSignature<T> signature) throws SQLException {
     Map<String, Object> map = Maps.newLinkedHashMap();
     AvaticaStatement statement = lookupStatement(handle);
-    final List<Object> parameterValues =
+    final List<TypedValue> parameterValues =
         TROJAN.getParameterValues(statement);
-    for (Ord<Object> o : Ord.zip(parameterValues)) {
-      map.put("?" + o.i, o.e);
+    for (Ord<TypedValue> o : Ord.zip(parameterValues)) {
+      map.put("?" + o.i, o.e.toLocal());
     }
     map.putAll(signature.internalParameters);
     final DataContext dataContext = createDataContext(map);
@@ -329,7 +330,7 @@ abstract class CalciteConnectionImpl
       for (Map.Entry<String, Object> entry : parameters.entrySet()) {
         Object e = entry.getValue();
         if (e == null) {
-          e = AvaticaParameter.DUMMY_VALUE;
+          e = AvaticaSite.DUMMY_VALUE;
         }
         builder.put(entry.getKey(), e);
       }
@@ -338,7 +339,7 @@ abstract class CalciteConnectionImpl
 
     public synchronized Object get(String name) {
       Object o = map.get(name);
-      if (o == AvaticaParameter.DUMMY_VALUE) {
+      if (o == AvaticaSite.DUMMY_VALUE) {
         return null;
       }
       if (o == null && Variable.SQL_ADVISOR.camelName.equals(name)) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/core/src/main/java/org/apache/calcite/jdbc/CalciteJdbc41Factory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteJdbc41Factory.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteJdbc41Factory.java
index e5a567c..f44e871 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteJdbc41Factory.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteJdbc41Factory.java
@@ -138,121 +138,118 @@ public class CalciteJdbc41Factory extends CalciteFactory {
     public void setRowId(
         int parameterIndex,
         RowId x) throws SQLException {
-      getParameter(parameterIndex).setRowId(slots, parameterIndex, x);
+      getSite(parameterIndex).setRowId(x);
     }
 
     public void setNString(
         int parameterIndex, String value) throws SQLException {
-      getParameter(parameterIndex).setNString(slots, parameterIndex, value);
+      getSite(parameterIndex).setNString(value);
     }
 
     public void setNCharacterStream(
         int parameterIndex,
         Reader value,
         long length) throws SQLException {
-      getParameter(parameterIndex)
-          .setNCharacterStream(slots, parameterIndex, value, length);
+      getSite(parameterIndex)
+          .setNCharacterStream(value, length);
     }
 
     public void setNClob(
         int parameterIndex,
         NClob value) throws SQLException {
-      getParameter(parameterIndex).setNClob(slots, parameterIndex, value);
+      getSite(parameterIndex).setNClob(value);
     }
 
     public void setClob(
         int parameterIndex,
         Reader reader,
         long length) throws SQLException {
-      getParameter(parameterIndex)
-          .setClob(slots, parameterIndex, reader, length);
+      getSite(parameterIndex)
+          .setClob(reader, length);
     }
 
     public void setBlob(
         int parameterIndex,
         InputStream inputStream,
         long length) throws SQLException {
-      getParameter(parameterIndex)
-          .setBlob(slots, parameterIndex, inputStream, length);
+      getSite(parameterIndex)
+          .setBlob(inputStream, length);
     }
 
     public void setNClob(
         int parameterIndex,
         Reader reader,
         long length) throws SQLException {
-      getParameter(parameterIndex).setNClob(slots,
-          parameterIndex,
-          reader,
-          length);
+      getSite(parameterIndex).setNClob(reader, length);
     }
 
     public void setSQLXML(
         int parameterIndex, SQLXML xmlObject) throws SQLException {
-      getParameter(parameterIndex).setSQLXML(slots, parameterIndex, xmlObject);
+      getSite(parameterIndex).setSQLXML(xmlObject);
     }
 
     public void setAsciiStream(
         int parameterIndex,
         InputStream x,
         long length) throws SQLException {
-      getParameter(parameterIndex)
-          .setAsciiStream(slots, parameterIndex, x, length);
+      getSite(parameterIndex)
+          .setAsciiStream(x, length);
     }
 
     public void setBinaryStream(
         int parameterIndex,
         InputStream x,
         long length) throws SQLException {
-      getParameter(parameterIndex)
-          .setBinaryStream(slots, parameterIndex, x, length);
+      getSite(parameterIndex)
+          .setBinaryStream(x, length);
     }
 
     public void setCharacterStream(
         int parameterIndex,
         Reader reader,
         long length) throws SQLException {
-      getParameter(parameterIndex)
-          .setCharacterStream(slots, parameterIndex, reader, length);
+      getSite(parameterIndex)
+          .setCharacterStream(reader, length);
     }
 
     public void setAsciiStream(
         int parameterIndex, InputStream x) throws SQLException {
-      getParameter(parameterIndex).setAsciiStream(slots, parameterIndex, x);
+      getSite(parameterIndex).setAsciiStream(x);
     }
 
     public void setBinaryStream(
         int parameterIndex, InputStream x) throws SQLException {
-      getParameter(parameterIndex).setBinaryStream(slots, parameterIndex, x);
+      getSite(parameterIndex).setBinaryStream(x);
     }
 
     public void setCharacterStream(
         int parameterIndex, Reader reader) throws SQLException {
-      getParameter(parameterIndex)
-          .setCharacterStream(slots, parameterIndex, reader);
+      getSite(parameterIndex)
+          .setCharacterStream(reader);
     }
 
     public void setNCharacterStream(
         int parameterIndex, Reader value) throws SQLException {
-      getParameter(parameterIndex)
-          .setNCharacterStream(slots, parameterIndex, value);
+      getSite(parameterIndex)
+          .setNCharacterStream(value);
     }
 
     public void setClob(
         int parameterIndex,
         Reader reader) throws SQLException {
-      getParameter(parameterIndex).setClob(slots, parameterIndex, reader);
+      getSite(parameterIndex).setClob(reader);
     }
 
     public void setBlob(
         int parameterIndex, InputStream inputStream) throws SQLException {
-      getParameter(parameterIndex)
-          .setBlob(slots, parameterIndex, inputStream);
+      getSite(parameterIndex)
+          .setBlob(inputStream);
     }
 
     public void setNClob(
         int parameterIndex, Reader reader) throws SQLException {
-      getParameter(parameterIndex)
-          .setNClob(slots, parameterIndex, reader);
+      getSite(parameterIndex)
+          .setNClob(reader);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java
index 7054685..e08e6a2 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java
@@ -25,6 +25,7 @@ import org.apache.calcite.avatica.AvaticaUtils;
 import org.apache.calcite.avatica.ColumnMetaData;
 import org.apache.calcite.avatica.Meta;
 import org.apache.calcite.avatica.MetaImpl;
+import org.apache.calcite.avatica.remote.TypedValue;
 import org.apache.calcite.linq4j.Enumerable;
 import org.apache.calcite.linq4j.Enumerator;
 import org.apache.calcite.linq4j.Linq4j;
@@ -457,7 +458,7 @@ public class CalciteMetaImpl extends MetaImpl {
   }
 
   @Override public Iterable<Object> createIterable(StatementHandle handle,
-      Signature signature, List<Object> parameterValues, Frame firstFrame) {
+      Signature signature, List<TypedValue> parameterValues, Frame firstFrame) {
     try {
       //noinspection unchecked
       final CalcitePrepare.CalciteSignature<Object> calciteSignature =
@@ -504,7 +505,7 @@ public class CalciteMetaImpl extends MetaImpl {
     // TODO: share code with prepare and createIterable
   }
 
-  @Override public Frame fetch(StatementHandle h, List<Object> parameterValues,
+  @Override public Frame fetch(StatementHandle h, List<TypedValue> parameterValues,
       int offset, int fetchMaxRowCount) {
     final CalciteConnectionImpl calciteConnection = getConnection();
     CalciteServerStatement stmt = calciteConnection.server.getStatement(h);
@@ -512,7 +513,7 @@ public class CalciteMetaImpl extends MetaImpl {
     final Iterator<Object> iterator;
     if (parameterValues != null) {
       final Iterable<Object> iterable =
-          createIterable(h, signature, Collections.emptyList(), null);
+          createIterable(h, signature, parameterValues, null);
       iterator = iterable.iterator();
       stmt.setResultSet(iterator);
     } else {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java b/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java
index f9a32ac..f0b3b93 100644
--- a/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java
+++ b/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java
@@ -285,7 +285,7 @@ public class CalciteRemoteDriverTest {
       }
       int param = map.get(sqlType);
       Class clazz = entry.getKey();
-      for (Object sampleValue : values(sqlType.clazz)) {
+      for (Object sampleValue : values(sqlType.boxedClass())) {
         switch (sqlType) {
         case DATE:
         case TIME:


[2/2] incubator-calcite git commit: [CALCITE-677] RemoteDriverTest.testTypeHandling fails east of Greenwich

Posted by jh...@apache.org.
[CALCITE-677] RemoteDriverTest.testTypeHandling fails east of Greenwich

Move logic for setting parameters into new class AvaticaSite.


Project: http://git-wip-us.apache.org/repos/asf/incubator-calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-calcite/commit/7f8ce197
Tree: http://git-wip-us.apache.org/repos/asf/incubator-calcite/tree/7f8ce197
Diff: http://git-wip-us.apache.org/repos/asf/incubator-calcite/diff/7f8ce197

Branch: refs/heads/master
Commit: 7f8ce1978b1792848bea127094a2ee24d52c6604
Parents: 9e272f2
Author: Julian Hyde <jh...@apache.org>
Authored: Mon Apr 13 13:57:22 2015 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Mon Apr 20 01:37:25 2015 -0700

----------------------------------------------------------------------
 .../apache/calcite/avatica/jdbc/JdbcMeta.java   |  75 +--
 .../calcite/avatica/jdbc/JdbcResultSet.java     |  18 +-
 .../calcite/avatica/RemoteDriverTest.java       | 193 +++++--
 .../calcite/avatica/AvaticaConnection.java      |   5 +-
 .../calcite/avatica/AvaticaJdbc41Factory.java   |  54 +-
 .../calcite/avatica/AvaticaParameter.java       | 556 ------------------
 .../avatica/AvaticaPreparedStatement.java       | 130 +++--
 .../calcite/avatica/AvaticaResultSet.java       |   5 +-
 .../org/apache/calcite/avatica/AvaticaSite.java | 578 +++++++++++++++++++
 .../calcite/avatica/AvaticaStatement.java       |  17 +-
 .../apache/calcite/avatica/ColumnMetaData.java  |   2 +-
 .../java/org/apache/calcite/avatica/Meta.java   |   6 +-
 .../org/apache/calcite/avatica/MetaImpl.java    |  13 +-
 .../org/apache/calcite/avatica/SqlType.java     |  87 ++-
 .../calcite/avatica/remote/LocalService.java    |   5 +-
 .../calcite/avatica/remote/RemoteMeta.java      |   7 +-
 .../calcite/avatica/remote/TypedValue.java      | 274 +++++++--
 .../calcite/avatica/util/AbstractCursor.java    |   4 +-
 .../calcite/avatica/util/DateTimeUtils.java     |  11 +
 .../calcite/jdbc/CalciteConnectionImpl.java     |  13 +-
 .../calcite/jdbc/CalciteJdbc41Factory.java      |  59 +-
 .../apache/calcite/jdbc/CalciteMetaImpl.java    |   7 +-
 .../calcite/jdbc/CalciteRemoteDriverTest.java   |   2 +-
 23 files changed, 1239 insertions(+), 882 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java
----------------------------------------------------------------------
diff --git a/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java b/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java
index 72274ff..2beeadf 100644
--- a/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java
+++ b/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java
@@ -20,7 +20,8 @@ import org.apache.calcite.avatica.AvaticaParameter;
 import org.apache.calcite.avatica.ColumnMetaData;
 import org.apache.calcite.avatica.ConnectionPropertiesImpl;
 import org.apache.calcite.avatica.Meta;
-import org.apache.calcite.avatica.util.ByteString;
+import org.apache.calcite.avatica.SqlType;
+import org.apache.calcite.avatica.remote.TypedValue;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -30,9 +31,6 @@ import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.RemovalListener;
 import com.google.common.cache.RemovalNotification;
 
-import java.lang.reflect.Array;
-import java.lang.reflect.Type;
-import java.math.BigDecimal;
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.ParameterMetaData;
@@ -41,12 +39,10 @@ import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Statement;
-import java.sql.Types;
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Properties;
 import java.util.UUID;
@@ -54,43 +50,12 @@ import java.util.concurrent.TimeUnit;
 
 /** Implementation of {@link Meta} upon an existing JDBC data source. */
 public class JdbcMeta implements Meta {
-
   private static final Log LOG = LogFactory.getLog(JdbcMeta.class);
 
-  /**
-   * JDBC Types Mapped to Java Types
-   *
-   * @see <a href="https://docs.oracle.com/javase/1.5.0/docs/guide/jdbc/getstart/mapping.html#1051555">JDBC Types Mapped to Java Types</a>
-   */
-  protected static final Map<Integer, Type> SQL_TYPE_TO_JAVA_TYPE =
-      new HashMap<>();
-  static {
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.CHAR, String.class);
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.VARCHAR, String.class);
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.LONGNVARCHAR, String.class);
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.NUMERIC, BigDecimal.class);
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.DECIMAL, BigDecimal.class);
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.BIT, Boolean.TYPE);
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.TINYINT, Byte.TYPE);
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.SMALLINT, Short.TYPE);
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.INTEGER, Integer.TYPE);
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.BIGINT, Long.TYPE);
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.REAL, Float.TYPE);
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.FLOAT, Double.TYPE);
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.DOUBLE, Double.TYPE);
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.BINARY, byte[].class);
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.VARBINARY, byte[].class);
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.LONGVARBINARY, byte[].class);
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.DATE, java.sql.Date.class);
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.TIME, java.sql.Time.class);
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.TIMESTAMP, java.sql.Timestamp.class);
-    //put(Types.CLOB, Clob);
-    //put(Types.BLOB, Blob);
-    SQL_TYPE_TO_JAVA_TYPE.put(Types.ARRAY, Array.class);
-  }
-
   private static final String CONN_CACHE_KEY_BASE = "avatica.connectioncache";
 
+  final Calendar calendar = Calendar.getInstance();
+
   /** Configurable connection cache settings. */
   public enum ConnectionCacheSettings {
     /** JDBC connection property for setting connection cache concurrency level. */
@@ -190,20 +155,8 @@ public class JdbcMeta implements Meta {
     }
     final List<ColumnMetaData> columns = new ArrayList<>();
     for (int i = 1; i <= metaData.getColumnCount(); i++) {
-      final Type javaType =
-          SQL_TYPE_TO_JAVA_TYPE.get(metaData.getColumnType(i));
-      final ColumnMetaData.Rep rep;
-      switch (metaData.getColumnType(i)) {
-      case Types.DATE:
-      case Types.TIME:
-        rep = ColumnMetaData.Rep.INTEGER;
-        break;
-      case Types.TIMESTAMP:
-        rep = ColumnMetaData.Rep.LONG;
-        break;
-      default:
-        rep = ColumnMetaData.Rep.of(javaType);
-      }
+      final SqlType sqlType = SqlType.valueOf(metaData.getColumnType(i));
+      final ColumnMetaData.Rep rep = ColumnMetaData.Rep.of(sqlType.internal);
       ColumnMetaData.AvaticaType t =
           ColumnMetaData.scalar(metaData.getColumnType(i),
               metaData.getColumnTypeName(i), rep);
@@ -624,7 +577,7 @@ public class JdbcMeta implements Meta {
   }
 
   public Iterable<Object> createIterable(StatementHandle handle,
-      Signature signature, List<Object> parameterValues, Frame firstFrame) {
+      Signature signature, List<TypedValue> parameterValues, Frame firstFrame) {
     return null;
   }
 
@@ -788,7 +741,7 @@ public class JdbcMeta implements Meta {
     }
   }
 
-  public Frame fetch(StatementHandle h, List<Object> parameterValues,
+  public Frame fetch(StatementHandle h, List<TypedValue> parameterValues,
       int offset, int fetchMaxRowCount) {
     if (LOG.isTraceEnabled()) {
       LOG.trace("fetching " + h + " offset:" + offset + " fetchMaxRowCount:" + fetchMaxRowCount);
@@ -805,11 +758,8 @@ public class JdbcMeta implements Meta {
             (PreparedStatement) statementInfo.statement;
         if (parameterValues != null) {
           for (int i = 0; i < parameterValues.size(); i++) {
-            Object o = parameterValues.get(i);
-            if (o instanceof ByteString) {
-              o = ((ByteString) o).getBytes();
-            }
-            preparedStatement.setObject(i + 1, o);
+            TypedValue o = parameterValues.get(i);
+            preparedStatement.setObject(i + 1, o.toJdbc(calendar));
           }
         }
         if (preparedStatement.execute()) {
@@ -819,7 +769,8 @@ public class JdbcMeta implements Meta {
       if (statementInfo.resultSet == null) {
         return Frame.EMPTY;
       } else {
-        return JdbcResultSet.frame(statementInfo.resultSet, offset, fetchMaxRowCount);
+        return JdbcResultSet.frame(statementInfo.resultSet, offset,
+            fetchMaxRowCount, calendar);
       }
     } catch (SQLException e) {
       throw propagate(e);

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcResultSet.java
----------------------------------------------------------------------
diff --git a/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcResultSet.java b/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcResultSet.java
index bcc2745..b1a67ac 100644
--- a/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcResultSet.java
+++ b/avatica-server/src/main/java/org/apache/calcite/avatica/jdbc/JdbcResultSet.java
@@ -27,6 +27,7 @@ import java.sql.Time;
 import java.sql.Timestamp;
 import java.sql.Types;
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.List;
 
 /** Implementation of {@link org.apache.calcite.avatica.Meta.MetaResultSet}
@@ -44,7 +45,8 @@ class JdbcResultSet extends Meta.MetaResultSet {
       ResultSet resultSet) {
     try {
       Meta.Signature sig = JdbcMeta.signature(resultSet.getMetaData());
-      final Meta.Frame firstFrame = frame(resultSet, 0, -1);
+      final Calendar calendar = Calendar.getInstance(DateTimeUtils.GMT_ZONE);
+      final Meta.Frame firstFrame = frame(resultSet, 0, -1, calendar);
       resultSet.close();
       return new JdbcResultSet(connectionId, statementId, true, sig,
           firstFrame);
@@ -56,7 +58,7 @@ class JdbcResultSet extends Meta.MetaResultSet {
   /** Creates a frame containing a given number or unlimited number of rows
    * from a result set. */
   static Meta.Frame frame(ResultSet resultSet, int offset,
-      int fetchMaxRowCount) throws SQLException {
+      int fetchMaxRowCount, Calendar calendar) throws SQLException {
     final ResultSetMetaData metaData = resultSet.getMetaData();
     final int columnCount = metaData.getColumnCount();
     final int[] types = new int[columnCount];
@@ -72,15 +74,15 @@ class JdbcResultSet extends Meta.MetaResultSet {
       }
       Object[] columns = new Object[columnCount];
       for (int j = 0; j < columnCount; j++) {
-        columns[j] = getValue(resultSet, types[j], j);
+        columns[j] = getValue(resultSet, types[j], j, calendar);
       }
       rows.add(columns);
     }
     return new Meta.Frame(offset, done, rows);
   }
 
-  private static Object getValue(ResultSet resultSet, int type, int j)
-      throws SQLException {
+  private static Object getValue(ResultSet resultSet, int type, int j,
+      Calendar calendar) throws SQLException {
     switch (type) {
     case Types.BIGINT:
       final long aLong = resultSet.getLong(j + 1);
@@ -102,17 +104,17 @@ class JdbcResultSet extends Meta.MetaResultSet {
       final float aFloat = resultSet.getFloat(j + 1);
       return aFloat == 0D && resultSet.wasNull() ? null : aFloat;
     case Types.DATE:
-      final Date aDate = resultSet.getDate(j + 1);
+      final Date aDate = resultSet.getDate(j + 1, calendar);
       return aDate == null
           ? null
           : (int) (aDate.getTime() / DateTimeUtils.MILLIS_PER_DAY);
     case Types.TIME:
-      final Time aTime = resultSet.getTime(j + 1);
+      final Time aTime = resultSet.getTime(j + 1, calendar);
       return aTime == null
           ? null
           : (int) (aTime.getTime() % DateTimeUtils.MILLIS_PER_DAY);
     case Types.TIMESTAMP:
-      final Timestamp aTimestamp = resultSet.getTimestamp(j + 1);
+      final Timestamp aTimestamp = resultSet.getTimestamp(j + 1, calendar);
       return aTimestamp == null ? null : aTimestamp.getTime();
     default:
       return resultSet.getObject(j + 1);

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/avatica-server/src/test/java/org/apache/calcite/avatica/RemoteDriverTest.java
----------------------------------------------------------------------
diff --git a/avatica-server/src/test/java/org/apache/calcite/avatica/RemoteDriverTest.java b/avatica-server/src/test/java/org/apache/calcite/avatica/RemoteDriverTest.java
index b2aa2b9..204f0fa 100644
--- a/avatica-server/src/test/java/org/apache/calcite/avatica/RemoteDriverTest.java
+++ b/avatica-server/src/test/java/org/apache/calcite/avatica/RemoteDriverTest.java
@@ -40,6 +40,7 @@ import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Statement;
+import java.sql.Time;
 import java.sql.Timestamp;
 import java.util.Calendar;
 import java.util.Map;
@@ -352,27 +353,108 @@ public class RemoteDriverTest {
         Statement s2 = underTest.createStatement()) {
       assertTrue(s1.execute(query));
       assertTrue(s2.execute(query));
-      try (ResultSet rs1 = s1.getResultSet();
-          ResultSet rs2 = s2.getResultSet()) {
-        assertEquals(rs1.getMetaData().getColumnCount(), rs2.getMetaData().getColumnCount());
-        int colCount = rs1.getMetaData().getColumnCount();
-        while (rs1.next() && rs2.next()) {
-          for (int i = 0; i < colCount; i++) {
-            Object o1 = rs1.getObject(i + 1);
-            Object o2 = rs2.getObject(i + 1);
-            if (o1 instanceof Integer && o2 instanceof Short) {
-              // Hsqldb returns Integer for short columns; we prefer Short
-              o1 = ((Number) o1).shortValue();
-            }
-            if (o1 instanceof Integer && o2 instanceof Byte) {
-              // Hsqldb returns Integer for tinyint columns; we prefer Byte
-              o1 = ((Number) o1).byteValue();
-            }
-            assertEquals(o1, o2);
+      assertResultSetsEqual(s1, s2);
+    }
+  }
+
+  private void assertResultSetsEqual(Statement s1, Statement s2)
+      throws SQLException {
+    final TimeZone moscowTz = TimeZone.getTimeZone("Europe/Moscow");
+    final Calendar moscowCalendar = Calendar.getInstance(moscowTz);
+    final TimeZone alaskaTz = TimeZone.getTimeZone("America/Anchorage");
+    final Calendar alaskaCalendar = Calendar.getInstance(alaskaTz);
+    try (ResultSet rs1 = s1.getResultSet();
+        ResultSet rs2 = s2.getResultSet()) {
+      assertEquals(rs1.getMetaData().getColumnCount(),
+          rs2.getMetaData().getColumnCount());
+      int colCount = rs1.getMetaData().getColumnCount();
+      while (rs1.next() && rs2.next()) {
+        for (int i = 0; i < colCount; i++) {
+          Object o1 = rs1.getObject(i + 1);
+          Object o2 = rs2.getObject(i + 1);
+          if (o1 instanceof Integer && o2 instanceof Short) {
+            // Hsqldb returns Integer for short columns; we prefer Short
+            o1 = ((Number) o1).shortValue();
           }
+          if (o1 instanceof Integer && o2 instanceof Byte) {
+            // Hsqldb returns Integer for tinyint columns; we prefer Byte
+            o1 = ((Number) o1).byteValue();
+          }
+          if (o1 instanceof Date) {
+            Date d1 = rs1.getDate(i + 1, moscowCalendar);
+            Date d2 = rs2.getDate(i + 1, moscowCalendar);
+            assertEquals(d1, d2);
+            d1 = rs1.getDate(i + 1, alaskaCalendar);
+            d2 = rs2.getDate(i + 1, alaskaCalendar);
+            assertEquals(d1, d2);
+            d1 = rs1.getDate(i + 1, null);
+            d2 = rs2.getDate(i + 1, null);
+            assertEquals(d1, d2);
+            d1 = rs1.getDate(i + 1);
+            d2 = rs2.getDate(i + 1);
+            assertEquals(d1, d2);
+          }
+          if (o1 instanceof Timestamp) {
+            Timestamp d1 = rs1.getTimestamp(i + 1, moscowCalendar);
+            Timestamp d2 = rs2.getTimestamp(i + 1, moscowCalendar);
+            assertEquals(d1, d2);
+            d1 = rs1.getTimestamp(i + 1, alaskaCalendar);
+            d2 = rs2.getTimestamp(i + 1, alaskaCalendar);
+            assertEquals(d1, d2);
+            d1 = rs1.getTimestamp(i + 1, null);
+            d2 = rs2.getTimestamp(i + 1, null);
+            assertEquals(d1, d2);
+            d1 = rs1.getTimestamp(i + 1);
+            d2 = rs2.getTimestamp(i + 1);
+            assertEquals(d1, d2);
+          }
+          assertEquals(o1, o2);
         }
-        assertEquals(rs1.next(), rs2.next());
       }
+      assertEquals(rs1.next(), rs2.next());
+    }
+  }
+
+  /** Callback to set parameters on each prepared statement before
+   * each is executed and the result sets compared. */
+  interface PreparedStatementFunction {
+    void apply(PreparedStatement s1, PreparedStatement s2)
+        throws SQLException;
+  }
+
+  @Test public void testSetParameter() throws Exception {
+    checkSetParameter("select ? from (values 1)",
+        new PreparedStatementFunction() {
+          public void apply(PreparedStatement s1, PreparedStatement s2)
+              throws SQLException {
+            final Date d = new Date(1234567890);
+            s1.setDate(1, d);
+            s2.setDate(1, d);
+          }
+        });
+    checkSetParameter("select ? from (values 1)",
+        new PreparedStatementFunction() {
+          public void apply(PreparedStatement s1, PreparedStatement s2)
+              throws SQLException {
+            final Timestamp ts = new Timestamp(123456789012L);
+            s1.setTimestamp(1, ts);
+            s2.setTimestamp(1, ts);
+          }
+        });
+  }
+
+  void checkSetParameter(String query, PreparedStatementFunction fn)
+      throws SQLException {
+    try (Connection cannon =
+             DriverManager.getConnection(CONNECTION_SPEC.url,
+                 CONNECTION_SPEC.username, CONNECTION_SPEC.password);
+         Connection underTest = ljs();
+         PreparedStatement s1 = cannon.prepareStatement(query);
+         PreparedStatement s2 = underTest.prepareStatement(query)) {
+      fn.apply(s1, s2);
+      assertTrue(s1.execute());
+      assertTrue(s2.execute());
+      assertResultSetsEqual(s1, s2);
     }
   }
 
@@ -497,46 +579,75 @@ public class RemoteDriverTest {
   }
 
   @Test public void testPrepareBindExecuteFetchDate() throws Exception {
-    final Connection connection = ljs();
-    final String sql = "select ? + interval '2' day as c from (values (1, 'a'))";
-    final PreparedStatement ps =
-        connection.prepareStatement(sql);
-    final ParameterMetaData parameterMetaData = ps.getParameterMetaData();
-    assertThat(parameterMetaData.getParameterCount(), equalTo(1));
+    checkPrepareBindExecuteFetchDate(ljs());
+  }
+
+  @Test public void testPrepareBindExecuteFetchDate2() throws Exception {
+    try (Connection cannon =
+             DriverManager.getConnection(CONNECTION_SPEC.url,
+                 CONNECTION_SPEC.username, CONNECTION_SPEC.password)) {
+      checkPrepareBindExecuteFetchDate(cannon);
+    }
+  }
+
+  private void checkPrepareBindExecuteFetchDate(Connection connection) throws Exception {
+    final String sql0 =
+        "select cast(? as varchar(20)) as c\n"
+            + "from (values (1, 'a'))";
+    final String sql1 = "select ? + interval '2' day as c from (values (1, 'a'))";
 
-    Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
-    calendar.set(Calendar.YEAR, 2015);
-    calendar.set(Calendar.MONTH, 4);
-    calendar.set(Calendar.DAY_OF_MONTH, 8);
-    calendar.set(Calendar.HOUR_OF_DAY, 0);
-    calendar.set(Calendar.MINUTE, 0);
-    calendar.set(Calendar.SECOND, 0);
-    calendar.set(Calendar.MILLISECOND, 0);
-    final long time = calendar.getTime().getTime();
+    final Date date = Date.valueOf("2015-04-08");
+    final long time = date.getTime();
+
+    PreparedStatement ps;
+    ParameterMetaData parameterMetaData;
     ResultSet resultSet;
 
-    ps.setDate(1, new java.sql.Date(time), calendar);
+    ps = connection.prepareStatement(sql0);
+    parameterMetaData = ps.getParameterMetaData();
+    assertThat(parameterMetaData.getParameterCount(), equalTo(1));
+    ps.setDate(1, date);
+    resultSet = ps.executeQuery();
+    assertThat(resultSet.next(), is(true));
+    assertThat(resultSet.getString(1), is("2015-04-08"));
+
+    ps.setTimestamp(1, new Timestamp(time));
+    resultSet = ps.executeQuery();
+    assertThat(resultSet.next(), is(true));
+    assertThat(resultSet.getString(1), is("2015-04-08 00:00:00.0"));
+
+    ps.setTime(1, new Time(time));
+    resultSet = ps.executeQuery();
+    assertThat(resultSet.next(), is(true));
+    assertThat(resultSet.getString(1), is("00:00:00"));
+    ps.close();
+
+    ps = connection.prepareStatement(sql1);
+    parameterMetaData = ps.getParameterMetaData();
+    assertThat(parameterMetaData.getParameterCount(), equalTo(1));
+
+    ps.setDate(1, date);
     resultSet = ps.executeQuery();
     assertTrue(resultSet.next());
-    assertThat(resultSet.getDate(1, calendar),
+    assertThat(resultSet.getDate(1),
         equalTo(new Date(time + TimeUnit.DAYS.toMillis(2))));
-    assertThat(resultSet.getTimestamp(1, calendar),
+    assertThat(resultSet.getTimestamp(1),
         equalTo(new Timestamp(time + TimeUnit.DAYS.toMillis(2))));
 
-    ps.setTimestamp(1, new Timestamp(time), calendar);
+    ps.setTimestamp(1, new Timestamp(time));
     resultSet = ps.executeQuery();
     assertTrue(resultSet.next());
-    assertThat(resultSet.getTimestamp(1, calendar),
+    assertThat(resultSet.getTimestamp(1),
         equalTo(new Timestamp(time + TimeUnit.DAYS.toMillis(2))));
-    assertThat(resultSet.getTimestamp(1, calendar),
+    assertThat(resultSet.getTimestamp(1),
         equalTo(new Timestamp(time + TimeUnit.DAYS.toMillis(2))));
 
     ps.setObject(1, new java.util.Date(time));
     resultSet = ps.executeQuery();
     assertTrue(resultSet.next());
-    assertThat(resultSet.getDate(1, calendar),
+    assertThat(resultSet.getDate(1),
         equalTo(new Date(time + TimeUnit.DAYS.toMillis(2))));
-    assertThat(resultSet.getTimestamp(1, calendar),
+    assertThat(resultSet.getTimestamp(1),
         equalTo(new Timestamp(time + TimeUnit.DAYS.toMillis(2))));
 
     resultSet.close();

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/avatica/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
index 213b42a..1a6ea44 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
@@ -16,6 +16,8 @@
  */
 package org.apache.calcite.avatica;
 
+import org.apache.calcite.avatica.remote.TypedValue;
+
 import java.sql.Array;
 import java.sql.Blob;
 import java.sql.CallableStatement;
@@ -187,6 +189,7 @@ public abstract class AvaticaConnection implements Connection {
   }
 
   public int getTransactionIsolation() throws SQLException {
+    //noinspection MagicConstant
     return meta.connectionSync(handle, new ConnectionPropertiesImpl()).getTransactionIsolation();
   }
 
@@ -517,7 +520,7 @@ public abstract class AvaticaConnection implements Connection {
     /** A means for anyone who has a trojan to call the protected method
      * {@link org.apache.calcite.avatica.AvaticaStatement#getParameterValues()}.
      */
-    public List<Object> getParameterValues(AvaticaStatement statement) {
+    public List<TypedValue> getParameterValues(AvaticaStatement statement) {
       return statement.getParameterValues();
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/avatica/src/main/java/org/apache/calcite/avatica/AvaticaJdbc41Factory.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaJdbc41Factory.java b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaJdbc41Factory.java
index 6a70a26..b573eab 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaJdbc41Factory.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaJdbc41Factory.java
@@ -130,117 +130,117 @@ class AvaticaJdbc41Factory implements AvaticaFactory {
     public void setRowId(
         int parameterIndex,
         RowId x) throws SQLException {
-      getParameter(parameterIndex).setRowId(slots, parameterIndex, x);
+      getSite(parameterIndex).setRowId(x);
     }
 
     public void setNString(
         int parameterIndex, String value) throws SQLException {
-      getParameter(parameterIndex).setNString(slots, parameterIndex, value);
+      getSite(parameterIndex).setNString(value);
     }
 
     public void setNCharacterStream(
         int parameterIndex,
         Reader value,
         long length) throws SQLException {
-      getParameter(parameterIndex)
-          .setNCharacterStream(slots, parameterIndex, value, length);
+      getSite(parameterIndex)
+          .setNCharacterStream(value, length);
     }
 
     public void setNClob(
         int parameterIndex,
         NClob value) throws SQLException {
-      getParameter(parameterIndex).setNClob(slots, parameterIndex, value);
+      getSite(parameterIndex).setNClob(value);
     }
 
     public void setClob(
         int parameterIndex,
         Reader reader,
         long length) throws SQLException {
-      getParameter(parameterIndex)
-          .setClob(slots, parameterIndex, reader, length);
+      getSite(parameterIndex)
+          .setClob(reader, length);
     }
 
     public void setBlob(
         int parameterIndex,
         InputStream inputStream,
         long length) throws SQLException {
-      getParameter(parameterIndex)
-          .setBlob(slots, parameterIndex, inputStream, length);
+      getSite(parameterIndex)
+          .setBlob(inputStream, length);
     }
 
     public void setNClob(
         int parameterIndex,
         Reader reader,
         long length) throws SQLException {
-      getParameter(parameterIndex)
-          .setNClob(slots, parameterIndex, reader, length);
+      getSite(parameterIndex)
+          .setNClob(reader, length);
     }
 
     public void setSQLXML(
         int parameterIndex, SQLXML xmlObject) throws SQLException {
-      getParameter(parameterIndex).setSQLXML(slots, parameterIndex, xmlObject);
+      getSite(parameterIndex).setSQLXML(xmlObject);
     }
 
     public void setAsciiStream(
         int parameterIndex,
         InputStream x,
         long length) throws SQLException {
-      getParameter(parameterIndex)
-          .setAsciiStream(slots, parameterIndex, x, length);
+      getSite(parameterIndex)
+          .setAsciiStream(x, length);
     }
 
     public void setBinaryStream(
         int parameterIndex,
         InputStream x,
         long length) throws SQLException {
-      getParameter(parameterIndex)
-          .setBinaryStream(slots, parameterIndex, x, length);
+      getSite(parameterIndex)
+          .setBinaryStream(x, length);
     }
 
     public void setCharacterStream(
         int parameterIndex,
         Reader reader,
         long length) throws SQLException {
-      getParameter(parameterIndex)
-          .setCharacterStream(slots, parameterIndex, reader, length);
+      getSite(parameterIndex)
+          .setCharacterStream(reader, length);
     }
 
     public void setAsciiStream(
         int parameterIndex, InputStream x) throws SQLException {
-      getParameter(parameterIndex).setAsciiStream(slots, parameterIndex, x);
+      getSite(parameterIndex).setAsciiStream(x);
     }
 
     public void setBinaryStream(
         int parameterIndex, InputStream x) throws SQLException {
-      getParameter(parameterIndex).setBinaryStream(slots, parameterIndex, x);
+      getSite(parameterIndex).setBinaryStream(x);
     }
 
     public void setCharacterStream(
         int parameterIndex, Reader reader) throws SQLException {
-      getParameter(parameterIndex)
-          .setCharacterStream(slots, parameterIndex, reader);
+      getSite(parameterIndex)
+          .setCharacterStream(reader);
     }
 
     public void setNCharacterStream(
         int parameterIndex, Reader value) throws SQLException {
-      getParameter(parameterIndex)
-          .setNCharacterStream(slots, parameterIndex, value);
+      getSite(parameterIndex)
+          .setNCharacterStream(value);
     }
 
     public void setClob(
         int parameterIndex,
         Reader reader) throws SQLException {
-      getParameter(parameterIndex).setClob(slots, parameterIndex, reader);
+      getSite(parameterIndex).setClob(reader);
     }
 
     public void setBlob(
         int parameterIndex, InputStream inputStream) throws SQLException {
-      getParameter(parameterIndex).setBlob(slots, parameterIndex, inputStream);
+      getSite(parameterIndex).setBlob(inputStream);
     }
 
     public void setNClob(
         int parameterIndex, Reader reader) throws SQLException {
-      getParameter(parameterIndex).setNClob(slots, parameterIndex, reader);
+      getSite(parameterIndex).setNClob(reader);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/avatica/src/main/java/org/apache/calcite/avatica/AvaticaParameter.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaParameter.java b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaParameter.java
index 8ec961d..ac011ed 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaParameter.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaParameter.java
@@ -16,31 +16,9 @@
  */
 package org.apache.calcite.avatica;
 
-import org.apache.calcite.avatica.util.ByteString;
-import org.apache.calcite.avatica.util.Cursor;
-
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
 
-import java.io.InputStream;
-import java.io.Reader;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.net.URL;
-import java.sql.Array;
-import java.sql.Blob;
-import java.sql.Clob;
-import java.sql.Date;
-import java.sql.NClob;
-import java.sql.Ref;
-import java.sql.RowId;
-import java.sql.SQLException;
-import java.sql.SQLXML;
-import java.sql.Time;
-import java.sql.Timestamp;
-import java.sql.Types;
-import java.util.Calendar;
-
 /**
  * Metadata for a parameter.
  */
@@ -53,10 +31,6 @@ public class AvaticaParameter {
   public final String className;
   public final String name;
 
-  /** Value that means the parameter has been set to null.
-   * If value is null, parameter has not been set. */
-  public static final Object DUMMY_VALUE = Dummy.INSTANCE;
-
   @JsonCreator
   public AvaticaParameter(
       @JsonProperty("signed") boolean signed,
@@ -75,536 +49,6 @@ public class AvaticaParameter {
     this.name = name;
   }
 
-  public void setByte(Object[] slots, int index, byte o) {
-    slots[index] = o;
-  }
-
-  public void setChar(Object[] slots, int index, char o) {
-    slots[index] = o;
-  }
-
-  public void setShort(Object[] slots, int index, short o) {
-    slots[index] = o;
-  }
-
-  public void setInt(Object[] slots, int index, int o) {
-    slots[index] = o;
-  }
-
-  public void setLong(Object[] slots, int index, long o) {
-    slots[index] = o;
-  }
-
-  public void setBoolean(Object[] slots, int index, boolean o) {
-    slots[index] = o;
-  }
-
-  private static Object wrap(Object o) {
-    if (o == null) {
-      return DUMMY_VALUE;
-    }
-    return o;
-  }
-
-  public boolean isSet(Object[] slots, int index) {
-    return slots[index] != null;
-  }
-
-  public void setRowId(Object[] slots, int index, RowId x) {
-    slots[index] = wrap(x);
-  }
-
-  public void setNString(Object[] slots, int index, String o) {
-    slots[index] = wrap(o);
-  }
-
-  public void setNCharacterStream(Object[] slots, int index, Reader value,
-      long length) {
-  }
-
-  public void setNClob(Object[] slots, int index, NClob value) {
-    slots[index] = wrap(value);
-  }
-
-  public void setClob(Object[] slots, int index, Reader reader, long length) {
-  }
-
-  public void setBlob(Object[] slots, int index, InputStream inputStream,
-      long length) {
-  }
-
-  public void setNClob(Object[] slots, int index, Reader reader, long length) {
-  }
-
-  public void setSQLXML(Object[] slots, int index, SQLXML xmlObject) {
-    slots[index] = wrap(xmlObject);
-  }
-
-  public void setAsciiStream(Object[] slots, int index, InputStream x,
-      long length) {
-  }
-
-  public void setBinaryStream(Object[] slots, int index, InputStream x,
-      long length) {
-  }
-
-  public void setCharacterStream(Object[] slots, int index, Reader reader,
-      long length) {
-  }
-
-  public void setAsciiStream(Object[] slots, int index, InputStream x) {
-  }
-
-  public void setBinaryStream(Object[] slots, int index, InputStream x) {
-  }
-
-  public void setCharacterStream(Object[] slots, int index, Reader reader) {
-  }
-
-  public void setNCharacterStream(Object[] slots, int index, Reader value) {
-  }
-
-  public void setClob(Object[] slots, int index, Reader reader) {
-  }
-
-  public void setBlob(Object[] slots, int index, InputStream inputStream) {
-  }
-
-  public void setNClob(Object[] slots, int index, Reader reader) {
-  }
-
-  public void setUnicodeStream(Object[] slots, int index, InputStream x,
-      int length) {
-  }
-
-  public void setTimestamp(Object[] slots, int index, Timestamp x) {
-    slots[index] = wrap(x);
-  }
-
-  public void setTime(Object[] slots, int index, Time x) {
-    slots[index] = wrap(x);
-  }
-
-  public void setFloat(Object[] slots, int index, float x) {
-    slots[index] = wrap(x);
-  }
-
-  public void setDouble(Object[] slots, int index, double x) {
-    slots[index] = wrap(x);
-  }
-
-  public void setBigDecimal(Object[] slots, int index, BigDecimal x) {
-    slots[index] = wrap(x);
-  }
-
-  public void setString(Object[] slots, int index, String x) {
-    slots[index] = wrap(x);
-  }
-
-  public void setBytes(Object[] slots, int index, byte[] x) {
-    slots[index] = x == null ? DUMMY_VALUE : new ByteString(x);
-  }
-
-  public void setDate(Object[] slots, int index, Date x, Calendar cal) {
-    setDate(slots, index, x); // TODO: use calendar
-  }
-
-  public void setDate(Object[] slots, int index, Date x) {
-    slots[index] = wrap(x);
-  }
-
-  public void setObject(Object[] slots, int index, Object x,
-      int targetSqlType) {
-    if (x == null || Types.NULL == targetSqlType) {
-      setNull(slots, index, targetSqlType);
-      return;
-    }
-    switch (targetSqlType) {
-    case Types.CLOB:
-    case Types.DATALINK:
-    case Types.NCLOB:
-    case Types.OTHER:
-    case Types.REF:
-    case Types.SQLXML:
-    case Types.STRUCT:
-      throw notImplemented();
-    case Types.ARRAY:
-      setArray(slots, index, toArray(x));
-      break;
-    case Types.BIGINT:
-      setLong(slots, index, toLong(x));
-      break;
-    case Types.BINARY:
-    case Types.LONGVARBINARY:
-    case Types.VARBINARY:
-      setBytes(slots, index, toBytes(x));
-      break;
-    case Types.BIT:
-    case Types.BOOLEAN:
-      setBoolean(slots, index, toBoolean(x));
-      break;
-    case Types.BLOB:
-      if (x instanceof Blob) {
-        setBlob(slots, index, (Blob) x);
-        break;
-      } else if (x instanceof InputStream) {
-        setBlob(slots, index, (InputStream) x);
-      }
-      throw unsupportedCast(x.getClass(), Blob.class);
-    case Types.DATE:
-      setDate(slots, index, toDate(x));
-      break;
-    case Types.DECIMAL:
-    case Types.NUMERIC:
-      setBigDecimal(slots, index, toBigDecimal(x));
-      break;
-    case Types.DISTINCT:
-      throw notImplemented();
-    case Types.DOUBLE:
-    case Types.FLOAT: // yes really; SQL FLOAT is up to 8 bytes
-      setDouble(slots, index, toDouble(x));
-      break;
-    case Types.INTEGER:
-      setInt(slots, index, toInt(x));
-      break;
-    case Types.JAVA_OBJECT:
-      setObject(slots, index, x);
-      break;
-    case Types.LONGNVARCHAR:
-    case Types.LONGVARCHAR:
-    case Types.NVARCHAR:
-    case Types.VARCHAR:
-    case Types.CHAR:
-    case Types.NCHAR:
-      setString(slots, index, toString(x));
-      break;
-    case Types.REAL:
-      setFloat(slots, index, toFloat(x));
-      break;
-    case Types.ROWID:
-      if (x instanceof RowId) {
-        setRowId(slots, index, (RowId) x);
-        break;
-      }
-      throw unsupportedCast(x.getClass(), RowId.class);
-    case Types.SMALLINT:
-      setShort(slots, index, toShort(x));
-      break;
-    case Types.TIME:
-      setTime(slots, index, toTime(x));
-      break;
-    case Types.TIMESTAMP:
-      setTimestamp(slots, index, toTimestamp(x));
-      break;
-    case Types.TINYINT:
-      setByte(slots, index, toByte(x));
-      break;
-    default:
-      throw notImplemented();
-    }
-  }
-
-  /** Similar logic to {@link #setObject}. */
-  public static Object get(Cursor.Accessor accessor, int targetSqlType,
-      Calendar localCalendar) throws SQLException {
-    switch (targetSqlType) {
-    case Types.CLOB:
-    case Types.DATALINK:
-    case Types.NCLOB:
-    case Types.REF:
-    case Types.SQLXML:
-    case Types.STRUCT:
-      throw notImplemented();
-    case Types.ARRAY:
-      return accessor.getArray();
-    case Types.BIGINT:
-      final long aLong = accessor.getLong();
-      if (aLong == 0 && accessor.wasNull()) {
-        return null;
-      }
-      return aLong;
-    case Types.BINARY:
-    case Types.LONGVARBINARY:
-    case Types.VARBINARY:
-      return accessor.getBytes();
-    case Types.BIT:
-    case Types.BOOLEAN:
-      final boolean aBoolean = accessor.getBoolean();
-      if (!aBoolean && accessor.wasNull()) {
-        return null;
-      }
-      return aBoolean;
-    case Types.BLOB:
-      return accessor.getBlob();
-    case Types.DATE:
-      return accessor.getDate(localCalendar);
-    case Types.DECIMAL:
-    case Types.NUMERIC:
-      return accessor.getBigDecimal();
-    case Types.DISTINCT:
-      throw notImplemented();
-    case Types.DOUBLE:
-    case Types.FLOAT: // yes really; SQL FLOAT is up to 8 bytes
-      final double aDouble = accessor.getDouble();
-      if (aDouble == 0 && accessor.wasNull()) {
-        return null;
-      }
-      return aDouble;
-    case Types.INTEGER:
-      final int anInt = accessor.getInt();
-      if (anInt == 0 && accessor.wasNull()) {
-        return null;
-      }
-      return anInt;
-    case Types.JAVA_OBJECT:
-    case Types.OTHER:
-      return accessor.getObject();
-    case Types.LONGNVARCHAR:
-    case Types.LONGVARCHAR:
-    case Types.NVARCHAR:
-    case Types.VARCHAR:
-    case Types.CHAR:
-    case Types.NCHAR:
-      return accessor.getString();
-    case Types.REAL:
-      final float aFloat = accessor.getFloat();
-      if (aFloat == 0 && accessor.wasNull()) {
-        return null;
-      }
-      return aFloat;
-    case Types.ROWID:
-      throw notImplemented();
-    case Types.SMALLINT:
-      final short aShort = accessor.getShort();
-      if (aShort == 0 && accessor.wasNull()) {
-        return null;
-      }
-      return aShort;
-    case Types.TIME:
-      return accessor.getTime(localCalendar);
-    case Types.TIMESTAMP:
-      return accessor.getTimestamp(localCalendar);
-    case Types.TINYINT:
-      final byte aByte = accessor.getByte();
-      if (aByte == 0 && accessor.wasNull()) {
-        return null;
-      }
-      return aByte;
-    default:
-      throw notImplemented();
-    }
-  }
-
-  public void setObject(Object[] slots, int index, Object x) {
-    slots[index] = wrap(x);
-  }
-
-  public void setNull(Object[] slots, int index, int sqlType) {
-    slots[index] = DUMMY_VALUE;
-  }
-
-  public void setTime(Object[] slots, int index, Time x, Calendar cal) {
-  }
-
-  public void setRef(Object[] slots, int index, Ref x) {
-  }
-
-  public void setBlob(Object[] slots, int index, Blob x) {
-  }
-
-  public void setClob(Object[] slots, int index, Clob x) {
-  }
-
-  public void setArray(Object[] slots, int index, Array x) {
-  }
-
-  public void setTimestamp(Object[] slots, int index, Timestamp x,
-      Calendar cal) {
-  }
-
-  public void setNull(Object[] slots, int index, int sqlType, String typeName) {
-  }
-
-  public void setURL(Object[] slots, int index, URL x) {
-  }
-
-  public void setObject(Object[] slots, int index, Object x, int targetSqlType,
-      int scaleOrLength) {
-  }
-
-  private static RuntimeException unsupportedCast(Class<?> from, Class<?> to) {
-    return new UnsupportedOperationException("Cannot convert from "
-        + from.getCanonicalName() + " to " + to.getCanonicalName());
-  }
-
-  private static RuntimeException notImplemented() {
-    return new RuntimeException("not implemented");
-  }
-
-  private static Array toArray(Object x) {
-    if (x instanceof Array) {
-      return (Array) x;
-    }
-    throw unsupportedCast(x.getClass(), Array.class);
-  }
-
-  public static BigDecimal toBigDecimal(Object x) {
-    if (x instanceof BigDecimal) {
-      return (BigDecimal) x;
-    } else if (x instanceof BigInteger) {
-      return new BigDecimal((BigInteger) x);
-    } else if (x instanceof Number) {
-      if (x instanceof Double || x instanceof Float) {
-        return new BigDecimal(((Number) x).doubleValue());
-      } else {
-        return new BigDecimal(((Number) x).longValue());
-      }
-    } else if (x instanceof Boolean) {
-      return (Boolean) x ? BigDecimal.ONE : BigDecimal.ZERO;
-    } else if (x instanceof String) {
-      return new BigDecimal((String) x);
-    }
-    throw unsupportedCast(x.getClass(), BigDecimal.class);
-  }
-
-  private static boolean toBoolean(Object x) {
-    if (x instanceof Boolean) {
-      return (Boolean) x;
-    } else if (x instanceof Number) {
-      return ((Number) x).intValue() != 0;
-    } else if (x instanceof String) {
-      String s = (String) x;
-      if (s.equalsIgnoreCase("true") || s.equalsIgnoreCase("yes")) {
-        return true;
-      } else if (s.equalsIgnoreCase("false") || s.equalsIgnoreCase("no")) {
-        return false;
-      }
-    }
-    throw unsupportedCast(x.getClass(), Boolean.TYPE);
-  }
-
-  private static byte toByte(Object x) {
-    if (x instanceof Number) {
-      return ((Number) x).byteValue();
-    } else if (x instanceof Boolean) {
-      return (Boolean) x ? (byte) 1 : (byte) 0;
-    } else if (x instanceof String) {
-      return Byte.parseByte((String) x);
-    } else {
-      throw unsupportedCast(x.getClass(), Byte.TYPE);
-    }
-  }
-
-  private static byte[] toBytes(Object x) {
-    if (x instanceof byte[]) {
-      return (byte[]) x;
-    }
-    if (x instanceof String) {
-      return ((String) x).getBytes();
-    }
-    throw unsupportedCast(x.getClass(), byte[].class);
-  }
-
-  private static Date toDate(Object x) {
-    if (x instanceof String) {
-      return Date.valueOf((String) x);
-    }
-    return new Date(toLong(x));
-  }
-
-  private static Time toTime(Object x) {
-    if (x instanceof String) {
-      return Time.valueOf((String) x);
-    }
-    return new Time(toLong(x));
-  }
-
-  private static Timestamp toTimestamp(Object x) {
-    if (x instanceof String) {
-      return Timestamp.valueOf((String) x);
-    }
-    return new Timestamp(toLong(x));
-  }
-
-  private static double toDouble(Object x) {
-    if (x instanceof Number) {
-      return ((Number) x).doubleValue();
-    } else if (x instanceof Boolean) {
-      return (Boolean) x ? 1D : 0D;
-    } else if (x instanceof String) {
-      return Double.parseDouble((String) x);
-    } else {
-      throw unsupportedCast(x.getClass(), Double.TYPE);
-    }
-  }
-
-  private static float toFloat(Object x) {
-    if (x instanceof Number) {
-      return ((Number) x).floatValue();
-    } else if (x instanceof Boolean) {
-      return (Boolean) x ? 1F : 0F;
-    } else if (x instanceof String) {
-      return Float.parseFloat((String) x);
-    } else {
-      throw unsupportedCast(x.getClass(), Float.TYPE);
-    }
-  }
-
-  private static int toInt(Object x) {
-    if (x instanceof Number) {
-      return ((Number) x).intValue();
-    } else if (x instanceof Boolean) {
-      return (Boolean) x ? 1 : 0;
-    } else if (x instanceof String) {
-      return Integer.parseInt((String) x);
-    } else {
-      throw unsupportedCast(x.getClass(), Integer.TYPE);
-    }
-  }
-
-  private static long toLong(Object x) {
-    if (x instanceof Number) {
-      return ((Number) x).longValue();
-    } else if (x instanceof Boolean) {
-      return (Boolean) x ? 1L : 0L;
-    } else if (x instanceof String) {
-      return Long.parseLong((String) x);
-    } else {
-      throw unsupportedCast(x.getClass(), Long.TYPE);
-    }
-  }
-
-  private static short toShort(Object x) {
-    if (x instanceof Number) {
-      return ((Number) x).shortValue();
-    } else if (x instanceof Boolean) {
-      return (Boolean) x ? (short) 1 : (short) 0;
-    } else if (x instanceof String) {
-      return Short.parseShort((String) x);
-    } else {
-      throw unsupportedCast(x.getClass(), Short.TYPE);
-    }
-  }
-
-  private static String toString(Object x) {
-    if (x instanceof String) {
-      return (String) x;
-    } else if (x instanceof Character
-        || x instanceof Boolean) {
-      return x.toString();
-    }
-    throw unsupportedCast(x.getClass(), String.class);
-  }
-
-  /** Singleton value to denote parameters that have been set to null (as
-   * opposed to not set).
-   *
-   * <p>Not a valid value for a parameter.
-   *
-   * <p>As an enum, it is serializable by Jackson. */
-  private enum Dummy {
-    INSTANCE
-  }
 }
 
 // End AvaticaParameter.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/avatica/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java
index 51606c9..4656af8 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java
@@ -16,6 +16,8 @@
  */
 package org.apache.calcite.avatica;
 
+import org.apache.calcite.avatica.remote.TypedValue;
+
 import java.io.InputStream;
 import java.io.Reader;
 import java.math.BigDecimal;
@@ -48,7 +50,8 @@ public abstract class AvaticaPreparedStatement
     implements PreparedStatement, ParameterMetaData {
   private final Meta.Signature signature;
   private final ResultSetMetaData resultSetMetaData;
-  protected final Object[] slots;
+  private Calendar calendar;
+  protected final TypedValue[] slots;
 
   /**
    * Creates an AvaticaPreparedStatement.
@@ -70,15 +73,27 @@ public abstract class AvaticaPreparedStatement
     super(connection, h, resultSetType, resultSetConcurrency,
         resultSetHoldability);
     this.signature = signature;
-    this.slots = new Object[signature.parameters.size()];
+    this.slots = new TypedValue[signature.parameters.size()];
     this.resultSetMetaData =
         connection.factory.newResultSetMetaData(this, signature);
   }
 
-  @Override protected List<Object> getParameterValues() {
+  @Override protected List<TypedValue> getParameterValues() {
     return Arrays.asList(slots);
   }
 
+  /** Returns a calendar in the connection's time zone, creating one the first
+   * time this method is called.
+   *
+   * <p>Uses the calendar to offset date-time values when calling methods such
+   * as {@link #setDate(int, Date)}. */
+  protected synchronized Calendar getCalendar() {
+    if (calendar == null) {
+      calendar = Calendar.getInstance(connection.getTimeZone());
+    }
+    return calendar;
+  }
+
   // implement PreparedStatement
 
   public ResultSet executeQuery() throws SQLException {
@@ -95,79 +110,66 @@ public abstract class AvaticaPreparedStatement
   }
 
   public void setNull(int parameterIndex, int sqlType) throws SQLException {
-    getParameter(parameterIndex).setNull(slots, parameterIndex - 1, sqlType);
+    getSite(parameterIndex).setNull(sqlType);
   }
 
   public void setBoolean(int parameterIndex, boolean x) throws SQLException {
-    getParameter(parameterIndex).setBoolean(slots, parameterIndex - 1, x);
+    getSite(parameterIndex).setBoolean(x);
   }
 
   public void setByte(int parameterIndex, byte x) throws SQLException {
-    getParameter(parameterIndex).setByte(slots, parameterIndex - 1, x);
+    getSite(parameterIndex).setByte(x);
   }
 
   public void setShort(int parameterIndex, short x) throws SQLException {
-    getParameter(parameterIndex).setShort(slots, parameterIndex - 1, x);
+    getSite(parameterIndex).setShort(x);
   }
 
   public void setInt(int parameterIndex, int x) throws SQLException {
-    getParameter(parameterIndex).setInt(slots, parameterIndex - 1, x);
+    getSite(parameterIndex).setInt(x);
   }
 
   public void setLong(int parameterIndex, long x) throws SQLException {
-    getParameter(parameterIndex).setLong(slots, parameterIndex - 1, x);
+    getSite(parameterIndex).setLong(x);
   }
 
   public void setFloat(int parameterIndex, float x) throws SQLException {
-    getParameter(parameterIndex).setFloat(slots, parameterIndex - 1, x);
+    getSite(parameterIndex).setFloat(x);
   }
 
   public void setDouble(int parameterIndex, double x) throws SQLException {
-    getParameter(parameterIndex).setDouble(slots, parameterIndex - 1, x);
+    getSite(parameterIndex).setDouble(x);
   }
 
   public void setBigDecimal(int parameterIndex, BigDecimal x)
       throws SQLException {
-    getParameter(parameterIndex).setBigDecimal(slots, parameterIndex - 1, x);
+    getSite(parameterIndex).setBigDecimal(x);
   }
 
   public void setString(int parameterIndex, String x) throws SQLException {
-    getParameter(parameterIndex).setString(slots, parameterIndex - 1, x);
+    getSite(parameterIndex).setString(x);
   }
 
   public void setBytes(int parameterIndex, byte[] x) throws SQLException {
-    getParameter(parameterIndex).setBytes(slots, parameterIndex - 1, x);
-  }
-
-  public void setDate(int parameterIndex, Date x) throws SQLException {
-    getParameter(parameterIndex).setDate(slots, parameterIndex - 1, x);
-  }
-
-  public void setTime(int parameterIndex, Time x) throws SQLException {
-    getParameter(parameterIndex).setTime(slots, parameterIndex - 1, x);
-  }
-
-  public void setTimestamp(int parameterIndex, Timestamp x)
-      throws SQLException {
-    getParameter(parameterIndex).setTimestamp(slots, parameterIndex - 1, x);
+    getSite(parameterIndex).setBytes(x);
   }
 
   public void setAsciiStream(int parameterIndex, InputStream x, int length)
       throws SQLException {
-    getParameter(parameterIndex)
-        .setAsciiStream(slots, parameterIndex - 1, x, length);
+    getSite(parameterIndex)
+        .setAsciiStream(x, length);
   }
 
   public void setUnicodeStream(int parameterIndex, InputStream x, int length)
       throws SQLException {
-    getParameter(parameterIndex)
-        .setUnicodeStream(slots, parameterIndex - 1, x, length);
+    getSite(parameterIndex)
+        .setUnicodeStream(x, length);
   }
 
   public void setBinaryStream(int parameterIndex, InputStream x, int length)
       throws SQLException {
-    getParameter(parameterIndex)
-        .setBinaryStream(slots, parameterIndex - 1, x, length);
+    getSite(parameterIndex)
+        .setBinaryStream(x, length);
   }
 
   public void clearParameters() throws SQLException {
@@ -178,12 +180,12 @@ public abstract class AvaticaPreparedStatement
 
   public void setObject(int parameterIndex, Object x, int targetSqlType)
       throws SQLException {
-    getParameter(parameterIndex)
-        .setObject(slots, parameterIndex - 1, x, targetSqlType);
+    getSite(parameterIndex)
+        .setObject(x, targetSqlType, getCalendar());
   }
 
   public void setObject(int parameterIndex, Object x) throws SQLException {
-    getParameter(parameterIndex).setObject(slots, parameterIndex - 1, x);
+    getSite(parameterIndex).setObject(x);
   }
 
   public boolean execute() throws SQLException {
@@ -199,60 +201,75 @@ public abstract class AvaticaPreparedStatement
 
   public void setCharacterStream(int parameterIndex, Reader reader, int length)
       throws SQLException {
-    getParameter(parameterIndex)
-        .setCharacterStream(slots, parameterIndex - 1, reader, length);
+    getSite(parameterIndex)
+        .setCharacterStream(reader, length);
   }
 
   public void setRef(int parameterIndex, Ref x) throws SQLException {
-    getParameter(parameterIndex).setRef(slots, parameterIndex - 1, x);
+    getSite(parameterIndex).setRef(x);
   }
 
   public void setBlob(int parameterIndex, Blob x) throws SQLException {
-    getParameter(parameterIndex).setBlob(slots, parameterIndex - 1, x);
+    getSite(parameterIndex).setBlob(x);
   }
 
   public void setClob(int parameterIndex, Clob x) throws SQLException {
-    getParameter(parameterIndex).setClob(slots, parameterIndex - 1, x);
+    getSite(parameterIndex).setClob(x);
   }
 
   public void setArray(int parameterIndex, Array x) throws SQLException {
-    getParameter(parameterIndex).setArray(slots, parameterIndex - 1, x);
+    getSite(parameterIndex).setArray(x);
   }
 
   public ResultSetMetaData getMetaData() {
     return resultSetMetaData;
   }
 
-  public void setDate(int parameterIndex, Date x, Calendar cal)
+  public void setDate(int parameterIndex, Date x, Calendar calendar)
       throws SQLException {
-    getParameter(parameterIndex).setDate(slots, parameterIndex - 1, x, cal);
+    getSite(parameterIndex)
+        .setDate(x, calendar);
+  }
+
+  public void setDate(int parameterIndex, Date x) throws SQLException {
+    setDate(parameterIndex, x, getCalendar());
   }
 
-  public void setTime(int parameterIndex, Time x, Calendar cal)
+  public void setTime(int parameterIndex, Time x, Calendar calendar)
       throws SQLException {
-    getParameter(parameterIndex).setTime(slots, parameterIndex - 1, x, cal);
+    getSite(parameterIndex)
+        .setTime(x, calendar);
+  }
+
+  public void setTime(int parameterIndex, Time x) throws SQLException {
+    setTime(parameterIndex, x, getCalendar());
   }
 
-  public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal)
+  public void setTimestamp(int parameterIndex, Timestamp x, Calendar calendar)
       throws SQLException {
-    getParameter(parameterIndex)
-        .setTimestamp(slots, parameterIndex - 1, x, cal);
+    getSite(parameterIndex)
+        .setTimestamp(x, calendar);
+  }
+
+  public void setTimestamp(int parameterIndex, Timestamp x)
+      throws SQLException {
+    setTimestamp(parameterIndex, x, getCalendar());
   }
 
   public void setNull(int parameterIndex, int sqlType, String typeName)
       throws SQLException {
-    getParameter(parameterIndex)
-        .setNull(slots, parameterIndex - 1, sqlType, typeName);
+    getSite(parameterIndex)
+        .setNull(sqlType, typeName);
   }
 
   public void setURL(int parameterIndex, URL x) throws SQLException {
-    getParameter(parameterIndex).setURL(slots, parameterIndex - 1, x);
+    getSite(parameterIndex).setURL(x);
   }
 
   public void setObject(int parameterIndex, Object x, int targetSqlType,
       int scaleOrLength) throws SQLException {
-    getParameter(parameterIndex)
-        .setObject(slots, parameterIndex - 1, x, targetSqlType, scaleOrLength);
+    getSite(parameterIndex)
+        .setObject(x, targetSqlType, scaleOrLength);
   }
 
   // implement ParameterMetaData
@@ -268,6 +285,11 @@ public abstract class AvaticaPreparedStatement
     }
   }
 
+  protected AvaticaSite getSite(int param) throws SQLException {
+    final AvaticaParameter parameter = getParameter(param);
+    return new AvaticaSite(parameter, calendar, param - 1, slots);
+  }
+
   public int getParameterCount() {
     return signature.parameters.size();
   }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/avatica/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java
index 6f528b3..f7b7ccd 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.avatica;
 
+import org.apache.calcite.avatica.remote.TypedValue;
 import org.apache.calcite.avatica.util.ArrayImpl;
 import org.apache.calcite.avatica.util.Cursor;
 
@@ -179,7 +180,7 @@ public class AvaticaResultSet implements ResultSet, ArrayImpl.Factory {
    * @throws SQLException if execute fails for some reason.
    */
   protected AvaticaResultSet execute() throws SQLException {
-    final List<Object> parameterValues = statement.getBoundParameterValues();
+    final List<TypedValue> parameterValues = statement.getBoundParameterValues();
     final Iterable<Object> iterable1 =
         statement.connection.meta.createIterable(statement.handle, signature,
             parameterValues, firstFrame);
@@ -377,7 +378,7 @@ public class AvaticaResultSet implements ResultSet, ArrayImpl.Factory {
   public Object getObject(int columnIndex) throws SQLException {
     final Cursor.Accessor accessor = getAccessor(columnIndex);
     final ColumnMetaData metaData = columnMetaDataList.get(columnIndex - 1);
-    return AvaticaParameter.get(accessor, metaData.type.id, localCalendar);
+    return AvaticaSite.get(accessor, metaData.type.id, localCalendar);
   }
 
   public Object getObject(String columnLabel) throws SQLException {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/avatica/src/main/java/org/apache/calcite/avatica/AvaticaSite.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaSite.java b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaSite.java
new file mode 100644
index 0000000..047a8db
--- /dev/null
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaSite.java
@@ -0,0 +1,578 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.avatica;
+
+import org.apache.calcite.avatica.remote.TypedValue;
+import org.apache.calcite.avatica.util.Cursor;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Date;
+import java.sql.NClob;
+import java.sql.Ref;
+import java.sql.RowId;
+import java.sql.SQLException;
+import java.sql.SQLXML;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.util.Calendar;
+
+/**
+ * A location that a value can be written to or read from.
+ */
+public class AvaticaSite {
+  final AvaticaParameter parameter;
+  final Calendar calendar;
+  private final int index;
+  final TypedValue[] slots;
+
+  /** Value that means the parameter has been set to null.
+   * If value is null, parameter has not been set. */
+  public static final Object DUMMY_VALUE = Dummy.INSTANCE;
+
+  public AvaticaSite(AvaticaParameter parameter, Calendar calendar, int index,
+      TypedValue[] slots) {
+    this.parameter = parameter;
+    this.calendar = calendar;
+    this.index = index;
+    this.slots = slots;
+  }
+
+  private TypedValue wrap(ColumnMetaData.Rep rep, Object o,
+      Calendar calendar) {
+    return TypedValue.ofJdbc(rep, o, calendar);
+  }
+
+  private TypedValue wrap(ColumnMetaData.Rep rep, Object o) {
+    return TypedValue.ofJdbc(rep, o, calendar);
+  }
+
+  public boolean isSet(int index) {
+    return slots[index] != null;
+  }
+
+  public void setByte(byte o) {
+    slots[index] = wrap(ColumnMetaData.Rep.BYTE, o);
+  }
+
+  public void setChar(char o) {
+    slots[index] = wrap(ColumnMetaData.Rep.CHARACTER, o);
+  }
+
+  public void setShort(short o) {
+    slots[index] = wrap(ColumnMetaData.Rep.SHORT, o);
+  }
+
+  public void setInt(int o) {
+    slots[index] = wrap(ColumnMetaData.Rep.INTEGER, o);
+  }
+
+  public void setLong(long o) {
+    slots[index] = wrap(ColumnMetaData.Rep.LONG, o);
+  }
+
+  public void setBoolean(boolean o) {
+    slots[index] = wrap(ColumnMetaData.Rep.BOOLEAN, o);
+  }
+
+  public void setRowId(RowId x) {
+    slots[index] = wrap(ColumnMetaData.Rep.OBJECT, x);
+  }
+
+  public void setNString(String o) {
+    slots[index] = wrap(ColumnMetaData.Rep.STRING, o);
+  }
+
+  public void setNCharacterStream(Reader value, long length) {
+  }
+
+  public void setNClob(NClob value) {
+    slots[index] = wrap(ColumnMetaData.Rep.OBJECT, value);
+  }
+
+  public void setClob(Reader reader, long length) {
+  }
+
+  public void setBlob(InputStream inputStream, long length) {
+  }
+
+  public void setNClob(Reader reader, long length) {
+  }
+
+  public void setSQLXML(SQLXML xmlObject) {
+    slots[index] = wrap(ColumnMetaData.Rep.OBJECT, xmlObject);
+  }
+
+  public void setAsciiStream(InputStream x, long length) {
+  }
+
+  public void setBinaryStream(InputStream x, long length) {
+  }
+
+  public void setCharacterStream(Reader reader, long length) {
+  }
+
+  public void setAsciiStream(InputStream x) {
+  }
+
+  public void setBinaryStream(InputStream x) {
+  }
+
+  public void setCharacterStream(Reader reader) {
+  }
+
+  public void setNCharacterStream(Reader value) {
+  }
+
+  public void setClob(Reader reader) {
+  }
+
+  public void setBlob(InputStream inputStream) {
+  }
+
+  public void setNClob(Reader reader) {
+  }
+
+  public void setUnicodeStream(InputStream x, int length) {
+  }
+
+  public void setFloat(float x) {
+    slots[index] = wrap(ColumnMetaData.Rep.FLOAT, x);
+  }
+
+  public void setDouble(double x) {
+    slots[index] = wrap(ColumnMetaData.Rep.DOUBLE, x);
+  }
+
+  public void setBigDecimal(BigDecimal x) {
+    slots[index] = wrap(ColumnMetaData.Rep.NUMBER, x);
+  }
+
+  public void setString(String x) {
+    slots[index] = wrap(ColumnMetaData.Rep.STRING, x);
+  }
+
+  public void setBytes(byte[] x) {
+    slots[index] = wrap(ColumnMetaData.Rep.BYTE_STRING, x);
+  }
+
+  public void setTimestamp(Timestamp x, Calendar calendar) {
+    slots[index] = wrap(ColumnMetaData.Rep.JAVA_SQL_TIMESTAMP, x, calendar);
+  }
+
+  public void setTime(Time x, Calendar calendar) {
+    slots[index] = wrap(ColumnMetaData.Rep.JAVA_SQL_TIME, x, calendar);
+  }
+
+  public void setDate(Date x, Calendar cal) {
+    slots[index] = wrap(ColumnMetaData.Rep.JAVA_SQL_DATE, x, calendar);
+  }
+
+  public void setObject(Object x, int targetSqlType, Calendar calendar) {
+    if (x == null || Types.NULL == targetSqlType) {
+      setNull(targetSqlType);
+      return;
+    }
+    switch (targetSqlType) {
+    case Types.CLOB:
+    case Types.DATALINK:
+    case Types.NCLOB:
+    case Types.OTHER:
+    case Types.REF:
+    case Types.SQLXML:
+    case Types.STRUCT:
+      throw notImplemented();
+    case Types.ARRAY:
+      setArray(toArray(x));
+      break;
+    case Types.BIGINT:
+      setLong(toLong(x));
+      break;
+    case Types.BINARY:
+    case Types.LONGVARBINARY:
+    case Types.VARBINARY:
+      setBytes(toBytes(x));
+      break;
+    case Types.BIT:
+    case Types.BOOLEAN:
+      setBoolean(toBoolean(x));
+      break;
+    case Types.BLOB:
+      if (x instanceof Blob) {
+        setBlob((Blob) x);
+        break;
+      } else if (x instanceof InputStream) {
+        setBlob((InputStream) x);
+      }
+      throw unsupportedCast(x.getClass(), Blob.class);
+    case Types.DATE:
+      setDate(toDate(x), calendar);
+      break;
+    case Types.DECIMAL:
+    case Types.NUMERIC:
+      setBigDecimal(toBigDecimal(x));
+      break;
+    case Types.DISTINCT:
+      throw notImplemented();
+    case Types.DOUBLE:
+    case Types.FLOAT: // yes really; SQL FLOAT is up to 8 bytes
+      setDouble(toDouble(x));
+      break;
+    case Types.INTEGER:
+      setInt(toInt(x));
+      break;
+    case Types.JAVA_OBJECT:
+      setObject(x);
+      break;
+    case Types.LONGNVARCHAR:
+    case Types.LONGVARCHAR:
+    case Types.NVARCHAR:
+    case Types.VARCHAR:
+    case Types.CHAR:
+    case Types.NCHAR:
+      setString(toString(x));
+      break;
+    case Types.REAL:
+      setFloat(toFloat(x));
+      break;
+    case Types.ROWID:
+      if (x instanceof RowId) {
+        setRowId((RowId) x);
+        break;
+      }
+      throw unsupportedCast(x.getClass(), RowId.class);
+    case Types.SMALLINT:
+      setShort(toShort(x));
+      break;
+    case Types.TIME:
+      setTime(toTime(x), calendar);
+      break;
+    case Types.TIMESTAMP:
+      setTimestamp(toTimestamp(x), calendar);
+      break;
+    case Types.TINYINT:
+      setByte(toByte(x));
+      break;
+    default:
+      throw notImplemented();
+    }
+  }
+
+  /** Similar logic to {@link #setObject}. */
+  public static Object get(Cursor.Accessor accessor, int targetSqlType,
+      Calendar localCalendar) throws SQLException {
+    switch (targetSqlType) {
+    case Types.CLOB:
+    case Types.DATALINK:
+    case Types.NCLOB:
+    case Types.REF:
+    case Types.SQLXML:
+    case Types.STRUCT:
+      throw notImplemented();
+    case Types.ARRAY:
+      return accessor.getArray();
+    case Types.BIGINT:
+      final long aLong = accessor.getLong();
+      if (aLong == 0 && accessor.wasNull()) {
+        return null;
+      }
+      return aLong;
+    case Types.BINARY:
+    case Types.LONGVARBINARY:
+    case Types.VARBINARY:
+      return accessor.getBytes();
+    case Types.BIT:
+    case Types.BOOLEAN:
+      final boolean aBoolean = accessor.getBoolean();
+      if (!aBoolean && accessor.wasNull()) {
+        return null;
+      }
+      return aBoolean;
+    case Types.BLOB:
+      return accessor.getBlob();
+    case Types.DATE:
+      return accessor.getDate(localCalendar);
+    case Types.DECIMAL:
+    case Types.NUMERIC:
+      return accessor.getBigDecimal();
+    case Types.DISTINCT:
+      throw notImplemented();
+    case Types.DOUBLE:
+    case Types.FLOAT: // yes really; SQL FLOAT is up to 8 bytes
+      final double aDouble = accessor.getDouble();
+      if (aDouble == 0 && accessor.wasNull()) {
+        return null;
+      }
+      return aDouble;
+    case Types.INTEGER:
+      final int anInt = accessor.getInt();
+      if (anInt == 0 && accessor.wasNull()) {
+        return null;
+      }
+      return anInt;
+    case Types.JAVA_OBJECT:
+    case Types.OTHER:
+      return accessor.getObject();
+    case Types.LONGNVARCHAR:
+    case Types.LONGVARCHAR:
+    case Types.NVARCHAR:
+    case Types.VARCHAR:
+    case Types.CHAR:
+    case Types.NCHAR:
+      return accessor.getString();
+    case Types.REAL:
+      final float aFloat = accessor.getFloat();
+      if (aFloat == 0 && accessor.wasNull()) {
+        return null;
+      }
+      return aFloat;
+    case Types.ROWID:
+      throw notImplemented();
+    case Types.SMALLINT:
+      final short aShort = accessor.getShort();
+      if (aShort == 0 && accessor.wasNull()) {
+        return null;
+      }
+      return aShort;
+    case Types.TIME:
+      return accessor.getTime(localCalendar);
+    case Types.TIMESTAMP:
+      return accessor.getTimestamp(localCalendar);
+    case Types.TINYINT:
+      final byte aByte = accessor.getByte();
+      if (aByte == 0 && accessor.wasNull()) {
+        return null;
+      }
+      return aByte;
+    default:
+      throw notImplemented();
+    }
+  }
+
+  public void setObject(Object x) {
+    slots[index] = TypedValue.ofJdbc(x, calendar);
+  }
+
+  public void setNull(int sqlType) {
+    slots[index] = wrap(ColumnMetaData.Rep.OBJECT, null);
+  }
+
+  public void setRef(Ref x) {
+  }
+
+  public void setBlob(Blob x) {
+  }
+
+  public void setClob(Clob x) {
+  }
+
+  public void setArray(Array x) {
+  }
+
+  public void setNull(int sqlType, String typeName) {
+  }
+
+  public void setURL(URL x) {
+  }
+
+  public void setObject(Object x, int targetSqlType,
+      int scaleOrLength) {
+  }
+
+  private static RuntimeException unsupportedCast(Class<?> from, Class<?> to) {
+    return new UnsupportedOperationException("Cannot convert from "
+        + from.getCanonicalName() + " to " + to.getCanonicalName());
+  }
+
+  private static RuntimeException notImplemented() {
+    return new RuntimeException("not implemented");
+  }
+
+  private static Array toArray(Object x) {
+    if (x instanceof Array) {
+      return (Array) x;
+    }
+    throw unsupportedCast(x.getClass(), Array.class);
+  }
+
+  public static BigDecimal toBigDecimal(Object x) {
+    if (x instanceof BigDecimal) {
+      return (BigDecimal) x;
+    } else if (x instanceof BigInteger) {
+      return new BigDecimal((BigInteger) x);
+    } else if (x instanceof Number) {
+      if (x instanceof Double || x instanceof Float) {
+        return new BigDecimal(((Number) x).doubleValue());
+      } else {
+        return new BigDecimal(((Number) x).longValue());
+      }
+    } else if (x instanceof Boolean) {
+      return (Boolean) x ? BigDecimal.ONE : BigDecimal.ZERO;
+    } else if (x instanceof String) {
+      return new BigDecimal((String) x);
+    }
+    throw unsupportedCast(x.getClass(), BigDecimal.class);
+  }
+
+  private static boolean toBoolean(Object x) {
+    if (x instanceof Boolean) {
+      return (Boolean) x;
+    } else if (x instanceof Number) {
+      return ((Number) x).intValue() != 0;
+    } else if (x instanceof String) {
+      String s = (String) x;
+      if (s.equalsIgnoreCase("true") || s.equalsIgnoreCase("yes")) {
+        return true;
+      } else if (s.equalsIgnoreCase("false") || s.equalsIgnoreCase("no")) {
+        return false;
+      }
+    }
+    throw unsupportedCast(x.getClass(), Boolean.TYPE);
+  }
+
+  private static byte toByte(Object x) {
+    if (x instanceof Number) {
+      return ((Number) x).byteValue();
+    } else if (x instanceof Boolean) {
+      return (Boolean) x ? (byte) 1 : (byte) 0;
+    } else if (x instanceof String) {
+      return Byte.parseByte((String) x);
+    } else {
+      throw unsupportedCast(x.getClass(), Byte.TYPE);
+    }
+  }
+
+  private static byte[] toBytes(Object x) {
+    if (x instanceof byte[]) {
+      return (byte[]) x;
+    }
+    if (x instanceof String) {
+      return ((String) x).getBytes();
+    }
+    throw unsupportedCast(x.getClass(), byte[].class);
+  }
+
+  private static Date toDate(Object x) {
+    if (x instanceof String) {
+      return Date.valueOf((String) x);
+    }
+    return new Date(toLong(x));
+  }
+
+  private static Time toTime(Object x) {
+    if (x instanceof String) {
+      return Time.valueOf((String) x);
+    }
+    return new Time(toLong(x));
+  }
+
+  private static Timestamp toTimestamp(Object x) {
+    if (x instanceof String) {
+      return Timestamp.valueOf((String) x);
+    }
+    return new Timestamp(toLong(x));
+  }
+
+  private static double toDouble(Object x) {
+    if (x instanceof Number) {
+      return ((Number) x).doubleValue();
+    } else if (x instanceof Boolean) {
+      return (Boolean) x ? 1D : 0D;
+    } else if (x instanceof String) {
+      return Double.parseDouble((String) x);
+    } else {
+      throw unsupportedCast(x.getClass(), Double.TYPE);
+    }
+  }
+
+  private static float toFloat(Object x) {
+    if (x instanceof Number) {
+      return ((Number) x).floatValue();
+    } else if (x instanceof Boolean) {
+      return (Boolean) x ? 1F : 0F;
+    } else if (x instanceof String) {
+      return Float.parseFloat((String) x);
+    } else {
+      throw unsupportedCast(x.getClass(), Float.TYPE);
+    }
+  }
+
+  private static int toInt(Object x) {
+    if (x instanceof Number) {
+      return ((Number) x).intValue();
+    } else if (x instanceof Boolean) {
+      return (Boolean) x ? 1 : 0;
+    } else if (x instanceof String) {
+      return Integer.parseInt((String) x);
+    } else {
+      throw unsupportedCast(x.getClass(), Integer.TYPE);
+    }
+  }
+
+  private static long toLong(Object x) {
+    if (x instanceof Number) {
+      return ((Number) x).longValue();
+    } else if (x instanceof Boolean) {
+      return (Boolean) x ? 1L : 0L;
+    } else if (x instanceof String) {
+      return Long.parseLong((String) x);
+    } else {
+      throw unsupportedCast(x.getClass(), Long.TYPE);
+    }
+  }
+
+  private static short toShort(Object x) {
+    if (x instanceof Number) {
+      return ((Number) x).shortValue();
+    } else if (x instanceof Boolean) {
+      return (Boolean) x ? (short) 1 : (short) 0;
+    } else if (x instanceof String) {
+      return Short.parseShort((String) x);
+    } else {
+      throw unsupportedCast(x.getClass(), Short.TYPE);
+    }
+  }
+
+  private static String toString(Object x) {
+    if (x instanceof String) {
+      return (String) x;
+    } else if (x instanceof Character
+        || x instanceof Boolean) {
+      return x.toString();
+    }
+    throw unsupportedCast(x.getClass(), String.class);
+  }
+
+  /** Singleton value to denote parameters that have been set to null (as
+   * opposed to not set).
+   *
+   * <p>Not a valid value for a parameter.
+   *
+   * <p>As an enum, it is serializable by Jackson. */
+  private enum Dummy {
+    INSTANCE
+  }
+}
+
+// End AvaticaSite.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/avatica/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java
index 7049b40..7c6d3c2 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java
@@ -16,13 +16,14 @@
  */
 package org.apache.calcite.avatica;
 
+import org.apache.calcite.avatica.remote.TypedValue;
+
 import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.SQLWarning;
 import java.sql.Statement;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -429,7 +430,7 @@ public abstract class AvaticaStatement
    *
    * @see org.apache.calcite.avatica.AvaticaConnection.Trojan#getParameterValues(AvaticaStatement)
    */
-  protected List<Object> getParameterValues() {
+  protected List<TypedValue> getParameterValues() {
     return Collections.emptyList();
   }
 
@@ -438,18 +439,14 @@ public abstract class AvaticaStatement
    * <p>If any of the parameters have not been bound, throws.
    * If parameters have been bound to null, the value in the list is null.
    */
-  protected List<Object> getBoundParameterValues() throws SQLException {
-    final List<Object> list = new ArrayList<>();
-    for (Object parameterValue : getParameterValues()) {
+  protected List<TypedValue> getBoundParameterValues() throws SQLException {
+    final List<TypedValue> parameterValues = getParameterValues();
+    for (Object parameterValue : parameterValues) {
       if (parameterValue == null) {
         throw new SQLException("unbound parameter");
       }
-      if (parameterValue == AvaticaParameter.DUMMY_VALUE) {
-        parameterValue = null;
-      }
-      list.add(parameterValue);
     }
-    return list;
+    return parameterValues;
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/avatica/src/main/java/org/apache/calcite/avatica/ColumnMetaData.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/ColumnMetaData.java b/avatica/src/main/java/org/apache/calcite/avatica/ColumnMetaData.java
index 470e34f..e0a50b6 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/ColumnMetaData.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/ColumnMetaData.java
@@ -246,7 +246,7 @@ public class ColumnMetaData {
     }
 
     public String columnClassName() {
-      return SqlType.valueOf(id).clazz.getName();
+      return SqlType.valueOf(id).boxedClass().getName();
     }
 
     public AvaticaType setRep(Rep rep) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/avatica/src/main/java/org/apache/calcite/avatica/Meta.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/Meta.java b/avatica/src/main/java/org/apache/calcite/avatica/Meta.java
index cadcd17..c796d32 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/Meta.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/Meta.java
@@ -16,6 +16,8 @@
  */
 package org.apache.calcite.avatica;
 
+import org.apache.calcite.avatica.remote.TypedValue;
+
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -151,7 +153,7 @@ public interface Meta {
    * requires to be not null; derived classes may instead choose to execute the
    * relational expression in {@code signature}. */
   Iterable<Object> createIterable(StatementHandle handle, Signature signature,
-      List<Object> parameterValues, Frame firstFrame);
+      List<TypedValue> parameterValues, Frame firstFrame);
 
   /** Prepares a statement.
    *
@@ -191,7 +193,7 @@ public interface Meta {
    * no limit
    * @return Frame, or null if there are no more
    */
-  Frame fetch(StatementHandle h, List<Object> parameterValues, int offset,
+  Frame fetch(StatementHandle h, List<TypedValue> parameterValues, int offset,
       int fetchMaxRowCount);
 
   /** Called during the creation of a statement to allocate a new handle.

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/avatica/src/main/java/org/apache/calcite/avatica/MetaImpl.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/MetaImpl.java b/avatica/src/main/java/org/apache/calcite/avatica/MetaImpl.java
index f8f35a7..4371047 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/MetaImpl.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/MetaImpl.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.avatica;
 
+import org.apache.calcite.avatica.remote.TypedValue;
 import org.apache.calcite.avatica.util.ArrayIteratorCursor;
 import org.apache.calcite.avatica.util.Cursor;
 import org.apache.calcite.avatica.util.IteratorCursor;
@@ -685,14 +686,14 @@ public abstract class MetaImpl implements Meta {
   }
 
   public Iterable<Object> createIterable(StatementHandle handle,
-      Signature signature, List<Object> parameterValues, Frame firstFrame) {
+      Signature signature, List<TypedValue> parameterValues, Frame firstFrame) {
     if (firstFrame != null && firstFrame.done) {
       return firstFrame.rows;
     }
     return new FetchIterable(handle, firstFrame, parameterValues);
   }
 
-  public Frame fetch(StatementHandle h, List<Object> parameterValues,
+  public Frame fetch(StatementHandle h, List<TypedValue> parameterValues,
       int offset, int fetchMaxRowCount) {
     return null;
   }
@@ -760,10 +761,10 @@ public abstract class MetaImpl implements Meta {
   private class FetchIterable implements Iterable<Object> {
     private final StatementHandle handle;
     private final Frame firstFrame;
-    private final List<Object> parameterValues;
+    private final List<TypedValue> parameterValues;
 
     public FetchIterable(StatementHandle handle, Frame firstFrame,
-        List<Object> parameterValues) {
+        List<TypedValue> parameterValues) {
       this.handle = handle;
       this.firstFrame = firstFrame;
       this.parameterValues = parameterValues;
@@ -779,10 +780,10 @@ public abstract class MetaImpl implements Meta {
     private final StatementHandle handle;
     private Frame frame;
     private Iterator<Object> rows;
-    private List<Object> parameterValues;
+    private List<TypedValue> parameterValues;
 
     public FetchIterator(StatementHandle handle, Frame firstFrame,
-        List<Object> parameterValues) {
+        List<TypedValue> parameterValues) {
       this.handle = handle;
       this.parameterValues = parameterValues;
       if (firstFrame == null) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/7f8ce197/avatica/src/main/java/org/apache/calcite/avatica/SqlType.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/SqlType.java b/avatica/src/main/java/org/apache/calcite/avatica/SqlType.java
index 90e9cba..b224469 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/SqlType.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/SqlType.java
@@ -16,6 +16,8 @@
  */
 package org.apache.calcite.avatica;
 
+import org.apache.calcite.avatica.util.ByteString;
+
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.net.URL;
@@ -291,28 +293,29 @@ import java.util.Map;
  * -->
  */
 public enum SqlType {
-  BIT(Types.BIT, Boolean.class),
-  BOOLEAN(Types.BOOLEAN, Boolean.class),
-  TINYINT(Types.TINYINT, Byte.class),
-  SMALLINT(Types.SMALLINT, Short.class),
-  INTEGER(Types.INTEGER, Integer.class),
-  BIGINT(Types.BIGINT, Long.class),
-  NUMERIC(Types.NUMERIC, Object.class),
+  BIT(Types.BIT, boolean.class),
+  BOOLEAN(Types.BOOLEAN, boolean.class),
+  TINYINT(Types.TINYINT, byte.class),
+  SMALLINT(Types.SMALLINT, short.class),
+  INTEGER(Types.INTEGER, int.class),
+  BIGINT(Types.BIGINT, long.class),
+  NUMERIC(Types.NUMERIC, BigDecimal.class),
   DECIMAL(Types.DECIMAL, BigDecimal.class),
-  FLOAT(Types.FLOAT, Double.class),
-  REAL(Types.REAL, Float.class),
-  DOUBLE(Types.DOUBLE, Double.class),
-  DATE(Types.DATE, java.sql.Date.class),
-  TIME(Types.TIME, Time.class),
-  TIMESTAMP(Types.TIMESTAMP, Timestamp.class),
+  FLOAT(Types.FLOAT, double.class),
+  REAL(Types.REAL, float.class),
+  DOUBLE(Types.DOUBLE, double.class),
+  DATE(Types.DATE, java.sql.Date.class, int.class),
+  TIME(Types.TIME, Time.class, int.class),
+  TIMESTAMP(Types.TIMESTAMP, Timestamp.class, long.class),
   INTERVAL_YEAR_MONTH(Types.OTHER, Boolean.class),
   INTERVAL_DAY_TIME(Types.OTHER, Boolean.class),
   CHAR(Types.CHAR, String.class),
   VARCHAR(Types.VARCHAR, String.class),
   LONGVARCHAR(Types.LONGVARCHAR, String.class),
-  BINARY(Types.BINARY, byte[].class),
-  VARBINARY(Types.VARBINARY, byte[].class),
-  LONGVARBINARY(Types.LONGVARBINARY, byte[].class),
+  BINARY(Types.BINARY, byte[].class, ByteString.class, String.class),
+  VARBINARY(Types.VARBINARY, byte[].class, ByteString.class, String.class),
+  LONGVARBINARY(Types.LONGVARBINARY, byte[].class, ByteString.class,
+      String.class),
   NULL(Types.NULL, Void.class),
   ANY(Types.JAVA_OBJECT, Object.class),
   SYMBOL(Types.OTHER, Object.class),
@@ -323,7 +326,7 @@ public enum SqlType {
   SQLXML(Types.SQLXML, java.sql.SQLXML.class),
   MAP(Types.OTHER, Map.class),
   DISTINCT(Types.DISTINCT, Object.class),
-  STRUCT(Types.STRUCT, Object.class),
+  STRUCT(Types.STRUCT, Struct.class),
   REF(Types.REF, Ref.class),
   DATALINK(Types.DATALINK, URL.class),
   JAVA_OBJECT(Types.JAVA_OBJECT, Object.class),
@@ -340,8 +343,16 @@ public enum SqlType {
   /** Type id as appears in {@link java.sql.Types},
    * e.g. {@link java.sql.Types#INTEGER}. */
   public final int id;
+
+  /** Default Java type for this SQL type, as described in table B-1. */
   public final Class clazz;
 
+  /** Class used internally in Calcite to represent instances of this type. */
+  public final Class internal;
+
+  /** Class used to serialize values of this type as JSON. */
+  public final Class serial;
+
   private static final Map<Integer, SqlType> BY_ID = new HashMap<>();
   static {
     for (SqlType sqlType : values()) {
@@ -349,9 +360,19 @@ public enum SqlType {
     }
   }
 
-  SqlType(int id, Class clazz) {
+  SqlType(int id, Class clazz, Class internal, Class serial) {
     this.id = id;
     this.clazz = clazz;
+    this.internal = internal;
+    this.serial = serial;
+  }
+
+  SqlType(int id, Class clazz, Class internal) {
+    this(id, clazz, internal, internal);
+  }
+
+  SqlType(int id, Class clazz) {
+    this(id, clazz, clazz, clazz);
   }
 
   public static SqlType valueOf(int type) {
@@ -362,6 +383,20 @@ public enum SqlType {
     return sqlType;
   }
 
+  /** Returns the boxed type. */
+  public Class boxedClass() {
+    return box(clazz);
+  }
+
+  /** Returns the boxed class. For example, {@code box(int.class)}
+   * returns {@code java.lang.Integer}. */
+  private static Class box(Class clazz) {
+    if (clazz.isPrimitive()) {
+      return BOX.get(clazz);
+    }
+    return clazz;
+  }
+
   /** Returns the entries in JDBC table B-5. */
   public static Iterable<Map.Entry<Class, SqlType>> getSetConversions() {
     final ArrayList<Map.Entry<Class, SqlType>> list = new ArrayList<>();
@@ -375,10 +410,13 @@ public enum SqlType {
 
   public static final Map<Class, EnumSet<SqlType>> SET_LIST;
   public static final Map<Method, EnumSet<SqlType>> GET_LIST;
+  private static final Map<Class, Class> BOX;
 
   static {
     SET_LIST = new HashMap<>();
     GET_LIST = new HashMap<>();
+    BOX = new HashMap<>();
+
     EnumSet<SqlType> numericTypes = EnumSet.of(TINYINT, SMALLINT, INTEGER,
         BIGINT, REAL, FLOAT, DOUBLE, DECIMAL, NUMERIC, BIT, BOOLEAN);
     Class[] numericClasses = {
@@ -447,8 +485,8 @@ public enum SqlType {
         concat(charTypes, binaryTypes, ncharTypes,
             EnumSet.of(CLOB, NCLOB, SQLXML)));
     GET_LIST.put(Method.GET_N_CHARACTER_STREAM,
-        concat(charTypes, binaryTypes, ncharTypes,
-            EnumSet.of(CLOB, NCLOB, SQLXML)));
+        concat(
+            charTypes, binaryTypes, ncharTypes, EnumSet.of(CLOB, NCLOB, SQLXML)));
     GET_LIST.put(Method.GET_CLOB, EnumSet.of(CLOB, NCLOB));
     GET_LIST.put(Method.GET_N_CLOB, EnumSet.of(CLOB, NCLOB));
     GET_LIST.put(Method.GET_BLOB, EnumSet.of(BLOB));
@@ -459,6 +497,15 @@ public enum SqlType {
     GET_LIST.put(Method.GET_OBJECT, EnumSet.allOf(SqlType.class));
     GET_LIST.put(Method.GET_ROW_ID, EnumSet.of(ROWID));
     GET_LIST.put(Method.GET_SQLXML, EnumSet.of(SQLXML));
+
+    BOX.put(boolean.class, Boolean.class);
+    BOX.put(byte.class, Byte.class);
+    BOX.put(char.class, Character.class);
+    BOX.put(short.class, Short.class);
+    BOX.put(int.class, Integer.class);
+    BOX.put(long.class, Long.class);
+    BOX.put(float.class, Float.class);
+    BOX.put(double.class, Double.class);
   }
 
   @SafeVarargs