You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by el...@apache.org on 2016/03/07 19:28:22 UTC

[44/59] [partial] calcite git commit: [CALCITE-1078] Detach avatica from the core calcite Maven project

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java
new file mode 100644
index 0000000..478723f
--- /dev/null
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java
@@ -0,0 +1,1046 @@
+/*
+ * 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.ArrayImpl;
+import org.apache.calcite.avatica.util.Cursor;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.math.BigDecimal;
+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.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.RowId;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.SQLXML;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+
+/**
+ * Implementation of {@link java.sql.ResultSet}
+ * for the Avatica engine.
+ */
+public class AvaticaResultSet implements ResultSet, ArrayImpl.Factory {
+  protected final AvaticaStatement statement;
+  protected final QueryState state;
+  protected final Meta.Signature signature;
+  protected final Meta.Frame firstFrame;
+  protected final List<ColumnMetaData> columnMetaDataList;
+  protected final ResultSetMetaData resultSetMetaData;
+  protected final Calendar localCalendar;
+
+  protected Cursor cursor;
+  protected List<Cursor.Accessor> accessorList;
+  private int row;
+  private boolean afterLast;
+  private int fetchDirection;
+  private int fetchSize;
+  private int type;
+  private int concurrency;
+  private int holdability;
+  private boolean closed;
+  private long timeoutMillis;
+  private Cursor timeoutCursor;
+
+  public AvaticaResultSet(AvaticaStatement statement,
+      QueryState state,
+      Meta.Signature signature,
+      ResultSetMetaData resultSetMetaData,
+      TimeZone timeZone,
+      Meta.Frame firstFrame) {
+    this.statement = statement;
+    this.state = state;
+    this.signature = signature;
+    this.firstFrame = firstFrame;
+    this.columnMetaDataList = signature.columns;
+    this.type = statement.resultSetType;
+    this.concurrency = statement.resultSetConcurrency;
+    this.holdability = statement.resultSetHoldability;
+    this.fetchSize = statement.getFetchSize();
+    this.fetchDirection = statement.getFetchDirection();
+    this.resultSetMetaData = resultSetMetaData;
+    this.localCalendar = Calendar.getInstance(timeZone);
+  }
+
+  private int findColumn0(String columnLabel) throws SQLException {
+    for (ColumnMetaData columnMetaData : columnMetaDataList) {
+      // Per JDBC 3.0 specification, match is case-insensitive and if there is
+      // more than one column with a particular name, take the first.
+      if (columnMetaData.label.equalsIgnoreCase(columnLabel)) {
+        return columnMetaData.ordinal; // 0-based
+      }
+    }
+    throw new SQLException("column '" + columnLabel + "' not found");
+  }
+
+  /**
+   * Returns the accessor for column with a given index.
+   *
+   * @param columnIndex 1-based column index
+   * @return Accessor
+   * @throws SQLException if index is not valid
+   */
+  private Cursor.Accessor getAccessor(int columnIndex) throws SQLException {
+    try {
+      return accessorList.get(columnIndex - 1);
+    } catch (IndexOutOfBoundsException e) {
+      throw new SQLException("invalid column ordinal: " + columnIndex);
+    }
+  }
+
+  /**
+   * Returns the accessor for column with a given label.
+   *
+   * @param columnLabel Column label
+   * @return Accessor
+   * @throws SQLException if there is no column with that label
+   */
+  private Cursor.Accessor getAccessor(String columnLabel) throws SQLException {
+    return accessorList.get(findColumn0(columnLabel));
+  }
+
+  public void close() {
+    closed = true;
+    final Cursor cursor = this.cursor;
+    if (cursor != null) {
+      this.cursor = null;
+      cursor.close();
+    }
+    statement.onResultSetClose(this);
+    // TODO: for timeout, see IteratorResultSet.close
+/*
+        if (timeoutCursor != null) {
+            final long noTimeout = 0;
+            timeoutCursor.close(noTimeout);
+            timeoutCursor = null;
+        }
+*/
+  }
+
+  /**
+   * Sets the timeout that this result set will wait for a row from the
+   * underlying iterator.
+   *
+   * <p>Not a JDBC method.</p>
+   *
+   * @param timeoutMillis Timeout in milliseconds. Must be greater than zero.
+   */
+  void setTimeout(long timeoutMillis) {
+    assert timeoutMillis > 0;
+    assert this.timeoutMillis == 0;
+    this.timeoutMillis = timeoutMillis;
+    assert timeoutCursor == null;
+    timeoutCursor = cursor;
+
+    // TODO: for timeout, see IteratorResultSet.setTimeout
+/*
+        timeoutCursor = new TimeoutCursor(timeoutMillis);
+        timeoutCursor.start();
+*/
+  }
+
+  // not JDBC
+  protected void cancel() {
+    throw new UnsupportedOperationException(); // TODO:
+  }
+
+  /**
+   * Executes this result set. (Not a JDBC method.)
+   *
+   * <p>Note that execute cannot occur in the constructor, because the
+   * constructor occurs while the statement is locked, to make sure that
+   * execute/cancel don't happen at the same time.</p>
+   *
+   * @see org.apache.calcite.avatica.AvaticaConnection.Trojan#execute(AvaticaResultSet)
+   *
+   * @throws SQLException if execute fails for some reason.
+   */
+  protected AvaticaResultSet execute() throws SQLException {
+    final List<TypedValue> parameterValues = statement.getBoundParameterValues();
+    final Iterable<Object> iterable1 =
+        statement.connection.meta.createIterable(statement.handle, state, signature,
+            parameterValues, firstFrame);
+    this.cursor = MetaImpl.createCursor(signature.cursorFactory, iterable1);
+    this.accessorList =
+        cursor.createAccessors(columnMetaDataList, localCalendar, this);
+    this.row = -1;
+    this.afterLast = false;
+    return this;
+  }
+
+  protected AvaticaResultSet execute2(Cursor cursor,
+      List<ColumnMetaData> columnMetaDataList) {
+    this.cursor = cursor;
+    this.accessorList =
+        cursor.createAccessors(columnMetaDataList, localCalendar, this);
+    this.row = -1;
+    this.afterLast = false;
+    return this;
+  }
+
+  public ResultSet create(ColumnMetaData.AvaticaType elementType,
+      Iterable<Object> iterable) {
+    throw new UnsupportedOperationException();
+  }
+
+  public boolean next() throws SQLException {
+    // TODO: for timeout, see IteratorResultSet.next
+    if (isClosed()) {
+      throw new SQLException("next() called on closed cursor");
+    }
+    if (cursor.next()) {
+      ++row;
+      return true;
+    } else {
+      afterLast = true;
+      return false;
+    }
+  }
+
+  public int findColumn(String columnLabel) throws SQLException {
+    return findColumn0(columnLabel) + 1;
+  }
+
+  public boolean wasNull() throws SQLException {
+    return cursor.wasNull();
+  }
+
+  public String getString(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getString();
+  }
+
+  public boolean getBoolean(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getBoolean();
+  }
+
+  public byte getByte(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getByte();
+  }
+
+  public short getShort(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getShort();
+  }
+
+  public int getInt(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getInt();
+  }
+
+  public long getLong(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getLong();
+  }
+
+  public float getFloat(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getFloat();
+  }
+
+  public double getDouble(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getDouble();
+  }
+
+  public BigDecimal getBigDecimal(
+      int columnIndex, int scale) throws SQLException {
+    return getAccessor(columnIndex).getBigDecimal(scale);
+  }
+
+  public byte[] getBytes(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getBytes();
+  }
+
+  public Date getDate(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getDate(localCalendar);
+  }
+
+  public Time getTime(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getTime(localCalendar);
+  }
+
+  public Timestamp getTimestamp(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getTimestamp(localCalendar);
+  }
+
+  public InputStream getAsciiStream(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getAsciiStream();
+  }
+
+  public InputStream getUnicodeStream(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getUnicodeStream();
+  }
+
+  public InputStream getBinaryStream(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getBinaryStream();
+  }
+
+  public String getString(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getString();
+  }
+
+  public boolean getBoolean(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getBoolean();
+  }
+
+  public byte getByte(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getByte();
+  }
+
+  public short getShort(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getShort();
+  }
+
+  public int getInt(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getInt();
+  }
+
+  public long getLong(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getLong();
+  }
+
+  public float getFloat(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getFloat();
+  }
+
+  public double getDouble(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getDouble();
+  }
+
+  public BigDecimal getBigDecimal(
+      String columnLabel, int scale) throws SQLException {
+    return getAccessor(columnLabel).getBigDecimal(scale);
+  }
+
+  public byte[] getBytes(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getBytes();
+  }
+
+  public Date getDate(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getDate(localCalendar);
+  }
+
+  public Time getTime(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getTime(localCalendar);
+  }
+
+  public Timestamp getTimestamp(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getTimestamp(localCalendar);
+  }
+
+  public InputStream getAsciiStream(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getAsciiStream();
+  }
+
+  public InputStream getUnicodeStream(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getUnicodeStream();
+  }
+
+  public InputStream getBinaryStream(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getBinaryStream();
+  }
+
+  public SQLWarning getWarnings() throws SQLException {
+    return null; // no warnings, since warnings are not supported
+  }
+
+  public void clearWarnings() throws SQLException {
+    // no-op since warnings are not supported
+  }
+
+  public String getCursorName() throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public ResultSetMetaData getMetaData() throws SQLException {
+    return resultSetMetaData;
+  }
+
+  public Object getObject(int columnIndex) throws SQLException {
+    final Cursor.Accessor accessor = getAccessor(columnIndex);
+    final ColumnMetaData metaData = columnMetaDataList.get(columnIndex - 1);
+    return AvaticaSite.get(accessor, metaData.type.id, localCalendar);
+  }
+
+  public Object getObject(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getObject();
+  }
+
+  public Reader getCharacterStream(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getCharacterStream();
+  }
+
+  public Reader getCharacterStream(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getCharacterStream();
+  }
+
+  public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getBigDecimal();
+  }
+
+  public BigDecimal getBigDecimal(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getBigDecimal();
+  }
+
+  public boolean isBeforeFirst() throws SQLException {
+    return row < 0;
+  }
+
+  public boolean isAfterLast() throws SQLException {
+    return afterLast;
+  }
+
+  public boolean isFirst() throws SQLException {
+    return row == 0;
+  }
+
+  public boolean isLast() throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void beforeFirst() throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void afterLast() throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public boolean first() throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public boolean last() throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public int getRow() throws SQLException {
+    return row;
+  }
+
+  public boolean absolute(int row) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public boolean relative(int rows) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public boolean previous() throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void setFetchDirection(int direction) throws SQLException {
+    this.fetchDirection = direction;
+  }
+
+  public int getFetchDirection() throws SQLException {
+    return fetchDirection;
+  }
+
+  public void setFetchSize(int fetchSize) throws SQLException {
+    this.fetchSize = fetchSize;
+  }
+
+  public int getFetchSize() throws SQLException {
+    return fetchSize;
+  }
+
+  public int getType() throws SQLException {
+    return type;
+  }
+
+  public int getConcurrency() throws SQLException {
+    return concurrency;
+  }
+
+  public boolean rowUpdated() throws SQLException {
+    return false;
+  }
+
+  public boolean rowInserted() throws SQLException {
+    return false;
+  }
+
+  public boolean rowDeleted() throws SQLException {
+    return false;
+  }
+
+  public void updateNull(int columnIndex) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateBoolean(int columnIndex, boolean x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateByte(int columnIndex, byte x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateShort(int columnIndex, short x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateInt(int columnIndex, int x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateLong(int columnIndex, long x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateFloat(int columnIndex, float x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateDouble(int columnIndex, double x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateBigDecimal(
+      int columnIndex, BigDecimal x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateString(int columnIndex, String x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateBytes(int columnIndex, byte[] x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateDate(int columnIndex, Date x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateTime(int columnIndex, Time x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateTimestamp(
+      int columnIndex, Timestamp x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateAsciiStream(
+      int columnIndex, InputStream x, int length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateBinaryStream(
+      int columnIndex, InputStream x, int length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateCharacterStream(
+      int columnIndex, Reader x, int length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateObject(
+      int columnIndex, Object x, int scaleOrLength) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateObject(int columnIndex, Object x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateNull(String columnLabel) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateBoolean(
+      String columnLabel, boolean x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateByte(String columnLabel, byte x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateShort(String columnLabel, short x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateInt(String columnLabel, int x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateLong(String columnLabel, long x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateFloat(String columnLabel, float x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateDouble(String columnLabel, double x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateBigDecimal(
+      String columnLabel, BigDecimal x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateString(String columnLabel, String x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateBytes(String columnLabel, byte[] x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateDate(String columnLabel, Date x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateTime(String columnLabel, Time x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateTimestamp(
+      String columnLabel, Timestamp x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateAsciiStream(
+      String columnLabel, InputStream x, int length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateBinaryStream(
+      String columnLabel, InputStream x, int length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateCharacterStream(
+      String columnLabel, Reader reader, int length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateObject(
+      String columnLabel, Object x, int scaleOrLength) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateObject(String columnLabel, Object x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void insertRow() throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateRow() throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void deleteRow() throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void refreshRow() throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void cancelRowUpdates() throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void moveToInsertRow() throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void moveToCurrentRow() throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public AvaticaStatement getStatement() {
+    return statement;
+  }
+
+  public Object getObject(
+      int columnIndex, Map<String, Class<?>> map) throws SQLException {
+    return getAccessor(columnIndex).getObject(map);
+  }
+
+  public Ref getRef(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getRef();
+  }
+
+  public Blob getBlob(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getBlob();
+  }
+
+  public Clob getClob(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getClob();
+  }
+
+  public Array getArray(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getArray();
+  }
+
+  public Object getObject(
+      String columnLabel, Map<String, Class<?>> map) throws SQLException {
+    return getAccessor(columnLabel).getObject(map);
+  }
+
+  public Ref getRef(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getRef();
+  }
+
+  public Blob getBlob(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getBlob();
+  }
+
+  public Clob getClob(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getClob();
+  }
+
+  public Array getArray(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getArray();
+  }
+
+  public Date getDate(int columnIndex, Calendar cal) throws SQLException {
+    return getAccessor(columnIndex).getDate(cal);
+  }
+
+  public Date getDate(String columnLabel, Calendar cal) throws SQLException {
+    return getAccessor(columnLabel).getDate(cal);
+  }
+
+  public Time getTime(int columnIndex, Calendar cal) throws SQLException {
+    return getAccessor(columnIndex).getTime(cal);
+  }
+
+  public Time getTime(String columnLabel, Calendar cal) throws SQLException {
+    return getAccessor(columnLabel).getTime(cal);
+  }
+
+  public Timestamp getTimestamp(
+      int columnIndex, Calendar cal) throws SQLException {
+    return getAccessor(columnIndex).getTimestamp(cal);
+  }
+
+  public Timestamp getTimestamp(
+      String columnLabel, Calendar cal) throws SQLException {
+    return getAccessor(columnLabel).getTimestamp(cal);
+  }
+
+  public URL getURL(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getURL();
+  }
+
+  public URL getURL(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getURL();
+  }
+
+  public void updateRef(int columnIndex, Ref x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateRef(String columnLabel, Ref x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateBlob(int columnIndex, Blob x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateBlob(String columnLabel, Blob x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateClob(int columnIndex, Clob x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateClob(String columnLabel, Clob x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateArray(int columnIndex, Array x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateArray(String columnLabel, Array x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public RowId getRowId(int columnIndex) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public RowId getRowId(String columnLabel) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateRowId(int columnIndex, RowId x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateRowId(String columnLabel, RowId x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public int getHoldability() throws SQLException {
+    return holdability;
+  }
+
+  public boolean isClosed() throws SQLException {
+    return closed;
+  }
+
+  public void updateNString(
+      int columnIndex, String nString) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateNString(
+      String columnLabel, String nString) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateNClob(int columnIndex, NClob nClob) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateNClob(
+      String columnLabel, NClob nClob) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public NClob getNClob(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getNClob();
+  }
+
+  public NClob getNClob(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getNClob();
+  }
+
+  public SQLXML getSQLXML(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getSQLXML();
+  }
+
+  public SQLXML getSQLXML(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getSQLXML();
+  }
+
+  public void updateSQLXML(
+      int columnIndex, SQLXML xmlObject) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateSQLXML(
+      String columnLabel, SQLXML xmlObject) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public String getNString(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getNString();
+  }
+
+  public String getNString(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getNString();
+  }
+
+  public Reader getNCharacterStream(int columnIndex) throws SQLException {
+    return getAccessor(columnIndex).getNCharacterStream();
+  }
+
+  public Reader getNCharacterStream(String columnLabel) throws SQLException {
+    return getAccessor(columnLabel).getNCharacterStream();
+  }
+
+  public void updateNCharacterStream(
+      int columnIndex, Reader x, long length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateNCharacterStream(
+      String columnLabel, Reader reader, long length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateAsciiStream(
+      int columnIndex, InputStream x, long length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateBinaryStream(
+      int columnIndex, InputStream x, long length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateCharacterStream(
+      int columnIndex, Reader x, long length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateAsciiStream(
+      String columnLabel, InputStream x, long length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateBinaryStream(
+      String columnLabel, InputStream x, long length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateCharacterStream(
+      String columnLabel, Reader reader, long length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateBlob(
+      int columnIndex,
+      InputStream inputStream,
+      long length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateBlob(
+      String columnLabel,
+      InputStream inputStream,
+      long length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateClob(
+      int columnIndex, Reader reader, long length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateClob(
+      String columnLabel, Reader reader, long length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateNClob(
+      int columnIndex, Reader reader, long length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateNClob(
+      String columnLabel, Reader reader, long length) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateNCharacterStream(
+      int columnIndex, Reader x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateNCharacterStream(
+      String columnLabel, Reader reader) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateAsciiStream(
+      int columnIndex, InputStream x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateBinaryStream(
+      int columnIndex, InputStream x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateCharacterStream(
+      int columnIndex, Reader x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateAsciiStream(
+      String columnLabel, InputStream x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateBinaryStream(
+      String columnLabel, InputStream x) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateCharacterStream(
+      String columnLabel, Reader reader) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateBlob(
+      int columnIndex, InputStream inputStream) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateBlob(
+      String columnLabel, InputStream inputStream) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateClob(int columnIndex, Reader reader) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateClob(
+      String columnLabel, Reader reader) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateNClob(
+      int columnIndex, Reader reader) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public void updateNClob(
+      String columnLabel, Reader reader) throws SQLException {
+    throw new UnsupportedOperationException();
+  }
+
+  public <T> T getObject(int columnIndex, Class<T> type) throws SQLException {
+    return getAccessor(columnIndex).getObject(type);
+  }
+
+  public <T> T getObject(
+      String columnLabel, Class<T> type) throws SQLException {
+    return getAccessor(columnLabel).getObject(type);
+  }
+
+  public <T> T unwrap(Class<T> iface) throws SQLException {
+    if (iface.isInstance(this)) {
+      return iface.cast(this);
+    }
+    throw statement.connection.helper.createException(
+        "does not implement '" + iface + "'");
+  }
+
+  public boolean isWrapperFor(Class<?> iface) throws SQLException {
+    return iface.isInstance(this);
+  }
+}
+
+// End AvaticaResultSet.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSetMetaData.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSetMetaData.java b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSetMetaData.java
new file mode 100644
index 0000000..b4e8892
--- /dev/null
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSetMetaData.java
@@ -0,0 +1,145 @@
+/*
+ * 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 java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+
+/**
+ * Implementation of {@link ResultSetMetaData}
+ * for the Avatica framework.
+ */
+public class AvaticaResultSetMetaData implements ResultSetMetaData {
+  final AvaticaStatement statement;
+  final Object query; // reserved for future use
+  final Meta.Signature signature;
+
+  public AvaticaResultSetMetaData(
+      AvaticaStatement statement,
+      Object query,
+      Meta.Signature signature) {
+    this.statement = statement;
+    this.query = query;
+    this.signature = signature;
+  }
+
+  private ColumnMetaData getColumnMetaData(int column) {
+    return signature.columns.get(column - 1);
+  }
+
+  // implement ResultSetMetaData
+
+  public int getColumnCount() throws SQLException {
+    return signature.columns.size();
+  }
+
+  public boolean isAutoIncrement(int column) throws SQLException {
+    return getColumnMetaData(column).autoIncrement;
+  }
+
+  public boolean isCaseSensitive(int column) throws SQLException {
+    return getColumnMetaData(column).caseSensitive;
+  }
+
+  public boolean isSearchable(int column) throws SQLException {
+    return getColumnMetaData(column).searchable;
+  }
+
+  public boolean isCurrency(int column) throws SQLException {
+    return getColumnMetaData(column).currency;
+  }
+
+  public int isNullable(int column) throws SQLException {
+    return getColumnMetaData(column).nullable;
+  }
+
+  public boolean isSigned(int column) throws SQLException {
+    return getColumnMetaData(column).signed;
+  }
+
+  public int getColumnDisplaySize(int column) throws SQLException {
+    return getColumnMetaData(column).displaySize;
+  }
+
+  public String getColumnLabel(int column) throws SQLException {
+    return getColumnMetaData(column).label;
+  }
+
+  public String getColumnName(int column) throws SQLException {
+    return getColumnMetaData(column).columnName;
+  }
+
+  public String getSchemaName(int column) throws SQLException {
+    return getColumnMetaData(column).schemaName;
+  }
+
+  public int getPrecision(int column) throws SQLException {
+    return getColumnMetaData(column).precision;
+  }
+
+  public int getScale(int column) throws SQLException {
+    return getColumnMetaData(column).scale;
+  }
+
+  public String getTableName(int column) throws SQLException {
+    return getColumnMetaData(column).tableName;
+  }
+
+  public String getCatalogName(int column) throws SQLException {
+    return getColumnMetaData(column).catalogName;
+  }
+
+  public int getColumnType(int column) throws SQLException {
+    return getColumnMetaData(column).type.id;
+  }
+
+  public String getColumnTypeName(int column) throws SQLException {
+    return getColumnMetaData(column).type.name;
+  }
+
+  public boolean isReadOnly(int column) throws SQLException {
+    return getColumnMetaData(column).readOnly;
+  }
+
+  public boolean isWritable(int column) throws SQLException {
+    return getColumnMetaData(column).writable;
+  }
+
+  public boolean isDefinitelyWritable(int column) throws SQLException {
+    return getColumnMetaData(column).definitelyWritable;
+  }
+
+  public String getColumnClassName(int column) throws SQLException {
+    return getColumnMetaData(column).columnClassName;
+  }
+
+  // implement Wrapper
+
+  public <T> T unwrap(Class<T> iface) throws SQLException {
+    if (iface.isInstance(this)) {
+      return iface.cast(this);
+    }
+    throw statement.connection.helper.createException(
+        "does not implement '" + iface + "'");
+  }
+
+  public boolean isWrapperFor(Class<?> iface) throws SQLException {
+    return iface.isInstance(this);
+  }
+}
+
+// End AvaticaResultSetMetaData.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSeverity.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSeverity.java b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSeverity.java
new file mode 100644
index 0000000..675c8af
--- /dev/null
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSeverity.java
@@ -0,0 +1,90 @@
+/*
+ * 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.proto.Common;
+
+import java.util.Objects;
+
+/**
+ * An enumeration that denotes the severity of a given unexpected state.
+ */
+public enum AvaticaSeverity {
+  /**
+   * The severity of the outcome of some unexpected state is unknown.
+   */
+  UNKNOWN(0),
+
+  /**
+   * The system has been left in an unrecoverable state as a result of an operation.
+   */
+  FATAL(1),
+
+  /**
+   * The result of an action resulted in an error which the current operation cannot recover
+   * from. Clients can attempt to execute the operation again.
+   */
+  ERROR(2),
+
+  /**
+   * The operation completed successfully but a message was generated to warn the client about
+   * some unexpected state or action.
+   */
+  WARNING(3);
+
+  private final int value;
+
+  AvaticaSeverity(int value) {
+    this.value = value;
+  }
+
+  public int getValue() {
+    return value;
+  }
+
+  public Common.Severity toProto() {
+    switch (this) {
+    case UNKNOWN:
+      return Common.Severity.UNKNOWN_SEVERITY;
+    case FATAL:
+      return Common.Severity.FATAL_SEVERITY;
+    case ERROR:
+      return Common.Severity.ERROR_SEVERITY;
+    case WARNING:
+      return Common.Severity.WARNING_SEVERITY;
+    default:
+      throw new RuntimeException("Unhandled Severity level: " + this);
+    }
+  }
+
+  public static AvaticaSeverity fromProto(Common.Severity proto) {
+    switch (Objects.requireNonNull(proto)) {
+    case UNKNOWN_SEVERITY:
+      return AvaticaSeverity.UNKNOWN;
+    case FATAL_SEVERITY:
+      return AvaticaSeverity.FATAL;
+    case ERROR_SEVERITY:
+      return AvaticaSeverity.ERROR;
+    case WARNING_SEVERITY:
+      return AvaticaSeverity.WARNING;
+    default:
+      throw new RuntimeException("Unhandled protobuf Severity level: " + proto);
+    }
+  }
+}
+
+// End AvaticaSeverity.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSite.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSite.java b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSite.java
new file mode 100644
index 0000000..7fd6947
--- /dev/null
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSite.java
@@ -0,0 +1,587 @@
+/*
+ * 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;
+
+  /** Calendar is not thread-safe. But calendar is only used from within one
+   * thread, and we have to trust that clients are not modifying calendars
+   * that they pass to us in a method such as
+   * {@link java.sql.PreparedStatement#setTime(int, Time, Calendar)}, so we do
+   * not need to synchronize access. */
+  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) {
+    assert calendar != null;
+    assert parameter != null;
+    assert slots != null;
+    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) {
+    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/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSqlException.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSqlException.java b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSqlException.java
new file mode 100644
index 0000000..9408a7b
--- /dev/null
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSqlException.java
@@ -0,0 +1,138 @@
+/*
+ * 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 java.io.PrintStream;
+import java.io.PrintWriter;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A client-facing {@link SQLException} which encapsulates errors from the remote Avatica server.
+ */
+public class AvaticaSqlException extends SQLException {
+
+  private static final long serialVersionUID = 1L;
+
+  private final String errorMessage;
+  private final List<String> stackTraces;
+  private final String remoteServer;
+
+  /**
+   * Construct the Exception with information from the server.
+   *
+   * @param errorMessage A human-readable error message.
+   * @param errorCode An integer corresponding to a known error.
+   * @param stackTraces Server-side stacktrace.
+   * @param remoteServer The host:port where the Avatica server is located
+   */
+  public AvaticaSqlException(String errorMessage, String sqlState, int errorCode,
+      List<String> stackTraces, String remoteServer) {
+    super("Error " + errorCode + " (" + sqlState + ") : " + errorMessage, sqlState, errorCode);
+    this.errorMessage = errorMessage;
+    this.stackTraces = Objects.requireNonNull(stackTraces);
+    this.remoteServer = remoteServer;
+  }
+
+  public String getErrorMessage() {
+    return errorMessage;
+  }
+
+  /**
+   * @return The stacktraces for exceptions thrown on the Avatica server.
+   */
+  public List<String> getStackTraces() {
+    return stackTraces;
+  }
+
+  /**
+   * @return The host:port for the remote Avatica server. May be null.
+   */
+  public String getRemoteServer() {
+    return remoteServer;
+  }
+
+  // printStackTrace() will get redirected to printStackTrace(PrintStream), don't need to override.
+
+  @Override public void printStackTrace(PrintStream stream) {
+    super.printStackTrace(stream);
+    stream.flush();
+    printServerStackTrace(new PrintStreamOrWriter(stream));
+  }
+
+  @Override public void printStackTrace(PrintWriter writer) {
+    super.printStackTrace(writer);
+    writer.flush();
+    printServerStackTrace(new PrintStreamOrWriter(writer));
+  }
+
+  void printServerStackTrace(PrintStreamOrWriter streamOrWriter) {
+    for (String serverStackTrace : this.stackTraces) {
+      streamOrWriter.println(serverStackTrace);
+    }
+  }
+
+  /**
+   * A class that encapsulates either a PrintStream or a PrintWriter.
+   */
+  private static class PrintStreamOrWriter {
+    /**
+     * Enumeration to differentiate between a PrintStream and a PrintWriter.
+     */
+    private enum Type {
+      STREAM,
+      WRITER
+    }
+
+    private PrintStream stream;
+    private PrintWriter writer;
+    private final Type type;
+
+    public PrintStreamOrWriter(PrintStream stream) {
+      this.stream = stream;
+      type = Type.STREAM;
+    }
+
+    public PrintStreamOrWriter(PrintWriter writer) {
+      this.writer = writer;
+      type = Type.WRITER;
+    }
+
+    /**
+     * Prints the given string to the the provided stream or writer.
+     *
+     * @param string The string to print
+     */
+    public void println(String string) {
+      switch (type) {
+      case STREAM:
+        stream.println(string);
+        stream.flush();
+        return;
+      case WRITER:
+        writer.println(string);
+        writer.flush();
+        return;
+      default:
+        throw new IllegalStateException();
+      }
+    }
+  }
+}
+
+// End AvaticaSqlException.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java
new file mode 100644
index 0000000..cfd1d45
--- /dev/null
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java
@@ -0,0 +1,527 @@
+/*
+ * 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 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.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Implementation of {@link java.sql.Statement}
+ * for the Avatica engine.
+ */
+public abstract class AvaticaStatement
+    implements Statement {
+  /** The default value for {@link Statement#getFetchSize()}. */
+  public static final int DEFAULT_FETCH_SIZE = 100;
+
+  public final AvaticaConnection connection;
+  /** Statement id; unique within connection. */
+  public Meta.StatementHandle handle;
+  protected boolean closed;
+
+  /**
+   * Support for {@link #closeOnCompletion()} method.
+   */
+  protected boolean closeOnCompletion;
+
+  /**
+   * Current result set, or null if the statement is not executing anything.
+   * Any method which modifies this member must synchronize
+   * on the AvaticaStatement.
+   */
+  protected AvaticaResultSet openResultSet;
+
+  /** Current update count. Same lifecycle as {@link #openResultSet}. */
+  protected long updateCount;
+
+  private int queryTimeoutMillis;
+  final int resultSetType;
+  final int resultSetConcurrency;
+  final int resultSetHoldability;
+  private int fetchSize = DEFAULT_FETCH_SIZE;
+  private int fetchDirection;
+  protected long maxRowCount = 0;
+
+  private Meta.Signature signature;
+
+  protected void setSignature(Meta.Signature signature) {
+    this.signature = signature;
+  }
+
+  protected Meta.Signature getSignature() {
+    return signature;
+  }
+
+  public Meta.StatementType getStatementType() {
+    return signature.statementType;
+  }
+
+  /**
+   * Creates an AvaticaStatement.
+   *
+   * @param connection Connection
+   * @param h Statement handle
+   * @param resultSetType Result set type
+   * @param resultSetConcurrency Result set concurrency
+   * @param resultSetHoldability Result set holdability
+   */
+  protected AvaticaStatement(AvaticaConnection connection,
+      Meta.StatementHandle h, int resultSetType, int resultSetConcurrency,
+      int resultSetHoldability) {
+    this(connection, h, resultSetType, resultSetConcurrency, resultSetHoldability, null);
+  }
+
+  protected AvaticaStatement(AvaticaConnection connection,
+      Meta.StatementHandle h, int resultSetType, int resultSetConcurrency,
+      int resultSetHoldability, Meta.Signature signature) {
+    this.connection = Objects.requireNonNull(connection);
+    this.resultSetType = resultSetType;
+    this.resultSetConcurrency = resultSetConcurrency;
+    this.resultSetHoldability = resultSetHoldability;
+    this.signature = signature;
+    this.closed = false;
+    if (h == null) {
+      final Meta.ConnectionHandle ch = connection.handle;
+      h = connection.meta.createStatement(ch);
+    }
+    connection.statementMap.put(h.id, this);
+    this.handle = h;
+  }
+
+  /** Returns the identifier of the statement, unique within its connection. */
+  public int getId() {
+    return handle.id;
+  }
+
+  private void checkNotPreparedOrCallable(String s) throws SQLException {
+    if (this instanceof PreparedStatement
+        || this instanceof CallableStatement) {
+      throw connection.helper.createException("Cannot call " + s
+          + " on prepared or callable statement");
+    }
+  }
+
+  protected void executeInternal(String sql) throws SQLException {
+    // reset previous state before moving forward.
+    this.updateCount = -1;
+    try {
+      // In JDBC, maxRowCount = 0 means no limit; in prepare it means LIMIT 0
+      final long maxRowCount1 = maxRowCount <= 0 ? -1 : maxRowCount;
+      for (int i = 0; i < connection.maxRetriesPerExecute; i++) {
+        try {
+          Meta.ExecuteResult x =
+              connection.prepareAndExecuteInternal(this, sql, maxRowCount1);
+          return;
+        } catch (NoSuchStatementException e) {
+          resetStatement();
+        }
+      }
+    } catch (RuntimeException e) {
+      throw connection.helper.createException("Error while executing SQL \"" + sql + "\": "
+          + e.getMessage(), e);
+    }
+
+    throw new RuntimeException("Failed to successfully execute query after "
+        + connection.maxRetriesPerExecute + " attempts.");
+  }
+
+  protected void resetStatement() {
+    // Invalidate the old statement
+    connection.statementMap.remove(handle.id);
+    // Get a new one
+    final Meta.ConnectionHandle ch = new Meta.ConnectionHandle(connection.id);
+    Meta.StatementHandle h = connection.meta.createStatement(ch);
+    // Cache it in the connection
+    connection.statementMap.put(h.id, this);
+    // Update the local state and try again
+    this.handle = h;
+  }
+
+  /**
+   * Re-initialize the ResultSet on the server with the given state.
+   * @param state The ResultSet's state.
+   * @param offset Offset into the desired ResultSet
+   * @return True if the ResultSet has more results, false if there are no more results.
+   */
+  protected boolean syncResults(QueryState state, long offset) throws NoSuchStatementException {
+    return connection.meta.syncResults(handle, state, offset);
+  }
+
+  // implement Statement
+
+  public boolean execute(String sql) throws SQLException {
+    checkNotPreparedOrCallable("execute(String)");
+    executeInternal(sql);
+    // Result set is null for DML or DDL.
+    // Result set is closed if user cancelled the query.
+    return openResultSet != null && !openResultSet.isClosed();
+  }
+
+  public ResultSet executeQuery(String sql) throws SQLException {
+    checkNotPreparedOrCallable("executeQuery(String)");
+    try {
+      executeInternal(sql);
+      if (openResultSet == null) {
+        throw connection.helper.createException(
+            "Statement did not return a result set");
+      }
+      return openResultSet;
+    } catch (RuntimeException e) {
+      throw connection.helper.createException("Error while executing SQL \"" + sql + "\": "
+          + e.getMessage(), e);
+    }
+  }
+
+  public final int executeUpdate(String sql) throws SQLException {
+    return (int) executeLargeUpdate(sql);
+  }
+
+  public long executeLargeUpdate(String sql) throws SQLException {
+    checkNotPreparedOrCallable("executeUpdate(String)");
+    executeInternal(sql);
+    return updateCount;
+  }
+
+  public synchronized void close() throws SQLException {
+    try {
+      close_();
+    } catch (RuntimeException e) {
+      throw connection.helper.createException("While closing statement", e);
+    }
+  }
+
+  protected void close_() {
+    if (!closed) {
+      closed = true;
+      if (openResultSet != null) {
+        AvaticaResultSet c = openResultSet;
+        openResultSet = null;
+        c.close();
+      }
+      try {
+        // inform the server to close the resource
+        connection.meta.closeStatement(handle);
+      } finally {
+        // make sure we don't leak on our side
+        connection.statementMap.remove(handle.id);
+      }
+      // If onStatementClose throws, this method will throw an exception (later
+      // converted to SQLException), but this statement still gets closed.
+      connection.driver.handler.onStatementClose(this);
+    }
+  }
+
+  public int getMaxFieldSize() throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public void setMaxFieldSize(int max) throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public final int getMaxRows() {
+    return (int) getLargeMaxRows();
+  }
+
+  public long getLargeMaxRows() {
+    return maxRowCount;
+  }
+
+  public final void setMaxRows(int maxRowCount) throws SQLException {
+    setLargeMaxRows(maxRowCount);
+  }
+
+  public void setLargeMaxRows(long maxRowCount) throws SQLException {
+    if (maxRowCount < 0) {
+      throw connection.helper.createException(
+          "illegal maxRows value: " + maxRowCount);
+    }
+    this.maxRowCount = maxRowCount;
+  }
+
+  public void setEscapeProcessing(boolean enable) throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public int getQueryTimeout() throws SQLException {
+    long timeoutSeconds = getQueryTimeoutMillis() / 1000;
+    if (timeoutSeconds > Integer.MAX_VALUE) {
+      return Integer.MAX_VALUE;
+    }
+    if (timeoutSeconds == 0 && getQueryTimeoutMillis() > 0) {
+      // Don't return timeout=0 if e.g. timeoutMillis=500. 0 is special.
+      return 1;
+    }
+    return (int) timeoutSeconds;
+  }
+
+  int getQueryTimeoutMillis() {
+    return queryTimeoutMillis;
+  }
+
+  public void setQueryTimeout(int seconds) throws SQLException {
+    if (seconds < 0) {
+      throw connection.helper.createException(
+          "illegal timeout value " + seconds);
+    }
+    setQueryTimeoutMillis(seconds * 1000);
+  }
+
+  void setQueryTimeoutMillis(int millis) {
+    this.queryTimeoutMillis = millis;
+  }
+
+  public synchronized void cancel() throws SQLException {
+    if (openResultSet != null) {
+      openResultSet.cancel();
+    }
+  }
+
+  public SQLWarning getWarnings() throws SQLException {
+    return null; // no warnings, since warnings are not supported
+  }
+
+  public void clearWarnings() throws SQLException {
+    // no-op since warnings are not supported
+  }
+
+  public void setCursorName(String name) throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public ResultSet getResultSet() throws SQLException {
+    // NOTE: result set becomes visible in this member while
+    // executeQueryInternal is still in progress, and before it has
+    // finished executing. Its internal state may not be ready for API
+    // calls. JDBC never claims to be thread-safe! (Except for calls to the
+    // cancel method.) It is not possible to synchronize, because it would
+    // block 'cancel'.
+    return openResultSet;
+  }
+
+  public int getUpdateCount() throws SQLException {
+    return (int) updateCount;
+  }
+
+  public long getLargeUpdateCount() throws SQLException {
+    return updateCount;
+  }
+
+  public boolean getMoreResults() throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public void setFetchDirection(int direction) throws SQLException {
+    this.fetchDirection = direction;
+  }
+
+  public int getFetchDirection() {
+    return fetchDirection;
+  }
+
+  public void setFetchSize(int rows) throws SQLException {
+    this.fetchSize = rows;
+  }
+
+  public int getFetchSize() {
+    return fetchSize;
+  }
+
+  public int getResultSetConcurrency() throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public int getResultSetType() throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public void addBatch(String sql) throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public void clearBatch() throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public int[] executeBatch() throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public AvaticaConnection getConnection() {
+    return connection;
+  }
+
+  public boolean getMoreResults(int current) throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public ResultSet getGeneratedKeys() throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public int executeUpdate(
+      String sql, int autoGeneratedKeys) throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public int executeUpdate(
+      String sql, int[] columnIndexes) throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public int executeUpdate(
+      String sql, String[] columnNames) throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public boolean execute(
+      String sql, int autoGeneratedKeys) throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public boolean execute(
+      String sql, int[] columnIndexes) throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public boolean execute(
+      String sql, String[] columnNames) throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public int getResultSetHoldability() throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public boolean isClosed() throws SQLException {
+    return closed;
+  }
+
+  public void setPoolable(boolean poolable) throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  public boolean isPoolable() throws SQLException {
+    throw connection.helper.unsupported();
+  }
+
+  // implements java.sql.Statement.closeOnCompletion (added in JDK 1.7)
+  public void closeOnCompletion() throws SQLException {
+    closeOnCompletion = true;
+  }
+
+  // implements java.sql.Statement.isCloseOnCompletion (added in JDK 1.7)
+  public boolean isCloseOnCompletion() throws SQLException {
+    return closeOnCompletion;
+  }
+
+  // implement Wrapper
+
+  public <T> T unwrap(Class<T> iface) throws SQLException {
+    if (iface.isInstance(this)) {
+      return iface.cast(this);
+    }
+    throw connection.helper.createException(
+        "does not implement '" + iface + "'");
+  }
+
+  public boolean isWrapperFor(Class<?> iface) throws SQLException {
+    return iface.isInstance(this);
+  }
+
+  /**
+   * Executes a prepared statement.
+   *
+   * @param signature Parsed statement
+   * @param isUpdate if the execute is for an update
+   *
+   * @return as specified by {@link java.sql.Statement#execute(String)}
+   * @throws java.sql.SQLException if a database error occurs
+   */
+  protected boolean executeInternal(Meta.Signature signature, boolean isUpdate)
+      throws SQLException {
+    ResultSet resultSet = executeQueryInternal(signature, isUpdate);
+    // user may have cancelled the query
+    if (resultSet.isClosed()) {
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Executes a prepared query, closing any previously open result set.
+   *
+   * @param signature Parsed query
+   * @param isUpdate If the execute is for an update
+   * @return Result set
+   * @throws java.sql.SQLException if a database error occurs
+   */
+  protected ResultSet executeQueryInternal(Meta.Signature signature, boolean isUpdate)
+      throws SQLException {
+    return connection.executeQueryInternal(this, signature, null, null, isUpdate);
+  }
+
+  /**
+   * Called by each child result set when it is closed.
+   *
+   * @param resultSet Result set or cell set
+   */
+  void onResultSetClose(ResultSet resultSet) {
+    if (closeOnCompletion) {
+      close_();
+    }
+  }
+
+  /** Returns the list of values of this statement's parameters.
+   *
+   * <p>Called at execute time. Not a public API.</p>
+   *
+   * <p>The default implementation returns the empty list, because non-prepared
+   * statements have no parameters.</p>
+   *
+   * @see org.apache.calcite.avatica.AvaticaConnection.Trojan#getParameterValues(AvaticaStatement)
+   */
+  protected List<TypedValue> getParameterValues() {
+    return Collections.emptyList();
+  }
+
+  /** Returns a list of bound parameter values.
+   *
+   * <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<TypedValue> getBoundParameterValues() throws SQLException {
+    final List<TypedValue> parameterValues = getParameterValues();
+    for (Object parameterValue : parameterValues) {
+      if (parameterValue == null) {
+        throw new SQLException("unbound parameter");
+      }
+    }
+    return parameterValues;
+  }
+}
+
+// End AvaticaStatement.java