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:24 UTC

[46/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-server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
----------------------------------------------------------------------
diff --git a/avatica-server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java b/avatica-server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
deleted file mode 100644
index ff95a45..0000000
--- a/avatica-server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
+++ /dev/null
@@ -1,626 +0,0 @@
-/*
- * 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.remote;
-
-import org.apache.calcite.avatica.AvaticaConnection;
-import org.apache.calcite.avatica.AvaticaSqlException;
-import org.apache.calcite.avatica.AvaticaStatement;
-import org.apache.calcite.avatica.AvaticaUtils;
-import org.apache.calcite.avatica.ConnectionPropertiesImpl;
-import org.apache.calcite.avatica.ConnectionSpec;
-import org.apache.calcite.avatica.Meta;
-import org.apache.calcite.avatica.jdbc.JdbcMeta;
-import org.apache.calcite.avatica.remote.Service.ErrorResponse;
-import org.apache.calcite.avatica.remote.Service.Response;
-import org.apache.calcite.avatica.server.AvaticaJsonHandler;
-import org.apache.calcite.avatica.server.AvaticaProtobufHandler;
-import org.apache.calcite.avatica.server.HttpServer;
-import org.apache.calcite.avatica.server.Main;
-import org.apache.calcite.avatica.server.Main.HandlerFactory;
-import org.apache.calcite.avatica.util.ArrayImpl;
-
-import com.google.common.base.Throwables;
-import com.google.common.cache.Cache;
-
-import org.junit.AfterClass;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.net.InetAddress;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.sql.Array;
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.UUID;
-
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-/** Tests covering {@link RemoteMeta}. */
-@RunWith(Parameterized.class)
-public class RemoteMetaTest {
-  private static final Random RANDOM = new Random();
-  private static final ConnectionSpec CONNECTION_SPEC = ConnectionSpec.HSQLDB;
-
-  // Keep a reference to the servers we start to clean them up after
-  private static final List<HttpServer> ACTIVE_SERVERS = new ArrayList<>();
-
-  private final HttpServer server;
-  private final String url;
-  private final int port;
-  private final Driver.Serialization serialization;
-
-  @Parameters
-  public static List<Object[]> parameters() throws Exception {
-    List<Object[]> params = new ArrayList<>();
-
-    final String[] mainArgs = { FullyRemoteJdbcMetaFactory.class.getName() };
-
-    // Bind to '0' to pluck an ephemeral port instead of expecting a certain one to be free
-
-    final HttpServer jsonServer = Main.start(mainArgs, 0, new HandlerFactory() {
-      @Override public AvaticaJsonHandler createHandler(Service service) {
-        return new AvaticaJsonHandler(service);
-      }
-    });
-    params.add(new Object[] {jsonServer, Driver.Serialization.JSON});
-    ACTIVE_SERVERS.add(jsonServer);
-
-    final HttpServer protobufServer = Main.start(mainArgs, 0, new HandlerFactory() {
-      @Override public AvaticaProtobufHandler createHandler(Service service) {
-        return new AvaticaProtobufHandler(service);
-      }
-    });
-    params.add(new Object[] {protobufServer, Driver.Serialization.PROTOBUF});
-
-    ACTIVE_SERVERS.add(protobufServer);
-
-    return params;
-  }
-
-  public RemoteMetaTest(HttpServer server, Driver.Serialization serialization) {
-    this.server = server;
-    this.port = this.server.getPort();
-    this.serialization = serialization;
-    url = "jdbc:avatica:remote:url=http://localhost:" + port + ";serialization="
-        + serialization.name();
-  }
-
-  @AfterClass public static void afterClass() throws Exception {
-    for (HttpServer server : ACTIVE_SERVERS) {
-      if (server != null) {
-        server.stop();
-      }
-    }
-  }
-
-  private static Meta getMeta(AvaticaConnection conn) throws Exception {
-    Field f = AvaticaConnection.class.getDeclaredField("meta");
-    f.setAccessible(true);
-    return (Meta) f.get(conn);
-  }
-
-  private static Meta.ExecuteResult prepareAndExecuteInternal(AvaticaConnection conn,
-    final AvaticaStatement statement, String sql, int maxRowCount) throws Exception {
-    Method m =
-        AvaticaConnection.class.getDeclaredMethod("prepareAndExecuteInternal",
-            AvaticaStatement.class, String.class, long.class);
-    m.setAccessible(true);
-    return (Meta.ExecuteResult) m.invoke(conn, statement, sql, maxRowCount);
-  }
-
-  private static Connection getConnection(JdbcMeta m, String id) throws Exception {
-    Field f = JdbcMeta.class.getDeclaredField("connectionCache");
-    f.setAccessible(true);
-    //noinspection unchecked
-    Cache<String, Connection> connectionCache = (Cache<String, Connection>) f.get(m);
-    return connectionCache.getIfPresent(id);
-  }
-
-  @Test public void testRemoteExecuteMaxRowCount() throws Exception {
-    ConnectionSpec.getDatabaseLock().lock();
-    try (AvaticaConnection conn = (AvaticaConnection) DriverManager.getConnection(url)) {
-      final AvaticaStatement statement = conn.createStatement();
-      prepareAndExecuteInternal(conn, statement,
-        "select * from (values ('a', 1), ('b', 2))", 0);
-      ResultSet rs = statement.getResultSet();
-      int count = 0;
-      while (rs.next()) {
-        count++;
-      }
-      assertEquals("Check maxRowCount=0 and ResultSets is 0 row", count, 0);
-      assertEquals("Check result set meta is still there",
-        rs.getMetaData().getColumnCount(), 2);
-      rs.close();
-      statement.close();
-      conn.close();
-    } finally {
-      ConnectionSpec.getDatabaseLock().unlock();
-    }
-  }
-
-  /** Test case for
-   * <a href="https://issues.apache.org/jira/browse/CALCITE-780">[CALCITE-780]
-   * HTTP error 413 when sending a long string to the Avatica server</a>. */
-  @Test public void testRemoteExecuteVeryLargeQuery() throws Exception {
-    ConnectionSpec.getDatabaseLock().lock();
-    try {
-      // Before the bug was fixed, a value over 7998 caused an HTTP 413.
-      // 16K bytes, I guess.
-      checkLargeQuery(8);
-      checkLargeQuery(240);
-      checkLargeQuery(8000);
-      checkLargeQuery(240000);
-    } finally {
-      ConnectionSpec.getDatabaseLock().unlock();
-    }
-  }
-
-  private void checkLargeQuery(int n) throws Exception {
-    try (AvaticaConnection conn = (AvaticaConnection) DriverManager.getConnection(url)) {
-      final AvaticaStatement statement = conn.createStatement();
-      final String frenchDisko = "It said human existence is pointless\n"
-          + "As acts of rebellious solidarity\n"
-          + "Can bring sense in this world\n"
-          + "La resistance!\n";
-      final String sql = "select '"
-          + longString(frenchDisko, n)
-          + "' as s from (values 'x')";
-      prepareAndExecuteInternal(conn, statement, sql, -1);
-      ResultSet rs = statement.getResultSet();
-      int count = 0;
-      while (rs.next()) {
-        count++;
-      }
-      assertThat(count, is(1));
-      rs.close();
-      statement.close();
-      conn.close();
-    }
-  }
-
-  /** Creates a string of exactly {@code length} characters by concatenating
-   * {@code fragment}. */
-  private static String longString(String fragment, int length) {
-    assert fragment.length() > 0;
-    final StringBuilder buf = new StringBuilder();
-    while (buf.length() < length) {
-      buf.append(fragment);
-    }
-    buf.setLength(length);
-    return buf.toString();
-  }
-
-  @Test public void testRemoteConnectionProperties() throws Exception {
-    ConnectionSpec.getDatabaseLock().lock();
-    try (AvaticaConnection conn = (AvaticaConnection) DriverManager.getConnection(url)) {
-      String id = conn.id;
-      final Map<String, ConnectionPropertiesImpl> m = ((RemoteMeta) getMeta(conn)).propsMap;
-      assertFalse("remote connection map should start ignorant", m.containsKey(id));
-      // force creating a connection object on the remote side.
-      try (final Statement stmt = conn.createStatement()) {
-        assertTrue("creating a statement starts a local object.", m.containsKey(id));
-        assertTrue(stmt.execute("select count(1) from EMP"));
-      }
-      Connection remoteConn = getConnection(FullyRemoteJdbcMetaFactory.getInstance(), id);
-      final boolean defaultRO = remoteConn.isReadOnly();
-      final boolean defaultAutoCommit = remoteConn.getAutoCommit();
-      final String defaultCatalog = remoteConn.getCatalog();
-      final String defaultSchema = remoteConn.getSchema();
-      conn.setReadOnly(!defaultRO);
-      assertTrue("local changes dirty local state", m.get(id).isDirty());
-      assertEquals("remote connection has not been touched", defaultRO, remoteConn.isReadOnly());
-      conn.setAutoCommit(!defaultAutoCommit);
-      assertEquals("remote connection has not been touched",
-          defaultAutoCommit, remoteConn.getAutoCommit());
-
-      // further interaction with the connection will force a sync
-      try (final Statement stmt = conn.createStatement()) {
-        assertEquals(!defaultAutoCommit, remoteConn.getAutoCommit());
-        assertFalse("local values should be clean", m.get(id).isDirty());
-      }
-    } finally {
-      ConnectionSpec.getDatabaseLock().unlock();
-    }
-  }
-
-  @Test public void testRemoteStatementInsert() throws Exception {
-    final String t = AvaticaUtils.unique("TEST_TABLE2");
-    AvaticaConnection conn = (AvaticaConnection) DriverManager.getConnection(url);
-    Statement statement = conn.createStatement();
-    final String create =
-        String.format("create table if not exists %s ("
-            + "  id int not null, msg varchar(255) not null)", t);
-    int status = statement.executeUpdate(create);
-    assertEquals(status, 0);
-
-    statement = conn.createStatement();
-    final String update = String.format("insert into %s values ('%d', '%s')",
-        t, RANDOM.nextInt(Integer.MAX_VALUE), UUID.randomUUID());
-    status = statement.executeUpdate(update);
-    assertEquals(status, 1);
-  }
-
-  @Test public void testBigints() throws Exception {
-    final String table = "TESTBIGINTS";
-    ConnectionSpec.getDatabaseLock().lock();
-    try (AvaticaConnection conn = (AvaticaConnection) DriverManager.getConnection(url);
-        Statement stmt = conn.createStatement()) {
-      assertFalse(stmt.execute("DROP TABLE IF EXISTS " + table));
-      assertFalse(stmt.execute("CREATE TABLE " + table + " (id BIGINT)"));
-      assertFalse(stmt.execute("INSERT INTO " + table + " values(10)"));
-      ResultSet results = conn.getMetaData().getColumns(null, null, table, null);
-      assertTrue(results.next());
-      assertEquals(table, results.getString(3));
-      // ordinal position
-      assertEquals(1L, results.getLong(17));
-    } finally {
-      ConnectionSpec.getDatabaseLock().unlock();
-    }
-  }
-
-  @Test public void testOpenConnectionWithProperties() throws Exception {
-    // This tests that username and password are used for creating a connection on the
-    // server. If this was not the case, it would succeed.
-    try {
-      DriverManager.getConnection(url, "john", "doe");
-      fail("expected exception");
-    } catch (RuntimeException e) {
-      assertEquals("Remote driver error: RuntimeException: "
-          + "java.sql.SQLInvalidAuthorizationSpecException: invalid authorization specification"
-          + " - not found: john"
-          + " -> SQLInvalidAuthorizationSpecException: invalid authorization specification - "
-          + "not found: john"
-          + " -> HsqlException: invalid authorization specification - not found: john",
-          e.getMessage());
-    }
-  }
-
-  @Test public void testRemoteConnectionsAreDifferent() throws SQLException {
-    Connection conn1 = DriverManager.getConnection(url);
-    Statement stmt = conn1.createStatement();
-    stmt.execute("DECLARE LOCAL TEMPORARY TABLE"
-        + " buffer (id INTEGER PRIMARY KEY, textdata VARCHAR(100))");
-    stmt.execute("insert into buffer(id, textdata) values(1, 'abc')");
-    stmt.executeQuery("select * from buffer");
-
-    // The local temporary table is local to the connection above, and should
-    // not be visible on another connection
-    Connection conn2 = DriverManager.getConnection(url);
-    Statement stmt2 = conn2.createStatement();
-    try {
-      stmt2.executeQuery("select * from buffer");
-      fail("expected exception");
-    } catch (Exception e) {
-      assertEquals("Error -1 (00000) : Error while executing SQL \"select * from buffer\": "
-          + "Remote driver error: RuntimeException: java.sql.SQLSyntaxErrorException: "
-          + "user lacks privilege or object not found: BUFFER -> "
-          + "SQLSyntaxErrorException: user lacks privilege or object not found: BUFFER -> "
-          + "HsqlException: user lacks privilege or object not found: BUFFER",
-          e.getMessage());
-    }
-  }
-
-  @Ignore("[CALCITE-942] AvaticaConnection should fail-fast when closed.")
-  @Test public void testRemoteConnectionClosing() throws Exception {
-    AvaticaConnection conn = (AvaticaConnection) DriverManager.getConnection(url);
-    // Verify connection is usable
-    conn.createStatement();
-    conn.close();
-
-    // After closing the connection, it should not be usable anymore
-    try {
-      conn.createStatement();
-      fail("expected exception");
-    } catch (SQLException e) {
-      assertThat(e.getMessage(),
-          containsString("Connection is closed"));
-    }
-  }
-
-  @Test public void testExceptionPropagation() throws Exception {
-    final String sql = "SELECT * from EMP LIMIT FOOBARBAZ";
-    ConnectionSpec.getDatabaseLock().lock();
-    try (final AvaticaConnection conn = (AvaticaConnection) DriverManager.getConnection(url);
-        final Statement stmt = conn.createStatement()) {
-      try {
-        // invalid SQL
-        stmt.execute(sql);
-        fail("Expected an AvaticaSqlException");
-      } catch (AvaticaSqlException e) {
-        assertEquals(ErrorResponse.UNKNOWN_ERROR_CODE, e.getErrorCode());
-        assertEquals(ErrorResponse.UNKNOWN_SQL_STATE, e.getSQLState());
-        assertTrue("Message should contain original SQL, was '" + e.getMessage() + "'",
-            e.getMessage().contains(sql));
-        assertEquals(1, e.getStackTraces().size());
-        final String stacktrace = e.getStackTraces().get(0);
-        final String substring = "unexpected token: FOOBARBAZ";
-        assertTrue("Message should contain '" + substring + "', was '" + e.getMessage() + ",",
-            stacktrace.contains(substring));
-      }
-    } finally {
-      ConnectionSpec.getDatabaseLock().unlock();
-    }
-  }
-
-  @Test public void testRemoteColumnsMeta() throws Exception {
-    // Verify all columns are retrieved, thus that frame-based fetching works correctly for columns
-    int rowCount = 0;
-    try (AvaticaConnection conn = (AvaticaConnection) DriverManager.getConnection(url)) {
-      ResultSet rs = conn.getMetaData().getColumns(null, null, null, null);
-      while (rs.next()) {
-        rowCount++;
-      }
-      rs.close();
-
-      // The implicitly created statement should have been closed
-      assertTrue(rs.getStatement().isClosed());
-    }
-    // default fetch size is 100, we are well beyond it
-    assertTrue(rowCount > 900);
-  }
-
-  @Test public void testArrays() throws SQLException {
-    ConnectionSpec.getDatabaseLock().lock();
-    try (AvaticaConnection conn = (AvaticaConnection) DriverManager.getConnection(url);
-         Statement stmt = conn.createStatement()) {
-      ResultSet resultSet =
-          stmt.executeQuery("select * from (values ('a', array['b', 'c']));");
-
-      assertTrue(resultSet.next());
-      assertEquals("a", resultSet.getString(1));
-      Array arr = resultSet.getArray(2);
-      assertTrue(arr instanceof ArrayImpl);
-      Object[] values = (Object[]) ((ArrayImpl) arr).getArray();
-      assertArrayEquals(new String[]{"b", "c"}, values);
-    } finally {
-      ConnectionSpec.getDatabaseLock().unlock();
-    }
-  }
-
-  @Test public void testBinaryAndStrings() throws Exception {
-    final String tableName = "testbinaryandstrs";
-    final byte[] data = "asdf".getBytes(StandardCharsets.UTF_8);
-    ConnectionSpec.getDatabaseLock().lock();
-    try (final Connection conn = DriverManager.getConnection(url);
-        final Statement stmt = conn.createStatement()) {
-      assertFalse(stmt.execute("DROP TABLE IF EXISTS " + tableName));
-      assertFalse(stmt.execute("CREATE TABLE " + tableName + "(id int, bin BINARY(4))"));
-      try (final PreparedStatement prepStmt = conn.prepareStatement(
-          "INSERT INTO " + tableName + " values(1, ?)")) {
-        prepStmt.setBytes(1, data);
-        assertFalse(prepStmt.execute());
-      }
-      try (ResultSet results = stmt.executeQuery("SELECT id, bin from " + tableName)) {
-        assertTrue(results.next());
-        assertEquals(1, results.getInt(1));
-        // byte comparison should work
-        assertArrayEquals("Bytes were " + Arrays.toString(results.getBytes(2)),
-            data, results.getBytes(2));
-        // as should string
-        assertEquals(new String(data, StandardCharsets.UTF_8), results.getString(2));
-        assertFalse(results.next());
-      }
-    } finally {
-      ConnectionSpec.getDatabaseLock().unlock();
-    }
-  }
-
-  @Test public void testLocalStackTraceHasServerStackTrace() {
-    ConnectionSpec.getDatabaseLock().lock();
-    try {
-      Statement statement = DriverManager.getConnection(url).createStatement();
-      statement.executeQuery("SELECT * FROM BOGUS_TABLE_DEF_DOESNT_EXIST");
-    } catch (SQLException e) {
-      // Verify that we got the expected exception
-      assertThat(e, instanceOf(AvaticaSqlException.class));
-
-      // Attempt to verify that we got a "server-side" class in the stack.
-      assertThat(Throwables.getStackTraceAsString(e),
-          containsString(JdbcMeta.class.getName()));
-    } finally {
-      ConnectionSpec.getDatabaseLock().unlock();
-    }
-  }
-
-  @Test public void testServerAddressInResponse() throws Exception {
-    ConnectionSpec.getDatabaseLock().lock();
-    try {
-      URL url = new URL("http://localhost:" + this.port);
-      AvaticaHttpClient httpClient = new AvaticaHttpClientImpl(url);
-      byte[] request;
-
-      Service.OpenConnectionRequest jsonReq = new Service.OpenConnectionRequest(
-          UUID.randomUUID().toString(), Collections.<String, String>emptyMap());
-      switch (this.serialization) {
-      case JSON:
-        request = JsonService.MAPPER.writeValueAsBytes(jsonReq);
-        break;
-      case PROTOBUF:
-        ProtobufTranslation pbTranslation = new ProtobufTranslationImpl();
-        request = pbTranslation.serializeRequest(jsonReq);
-        break;
-      default:
-        throw new IllegalStateException("Should not reach here");
-      }
-
-      byte[] response = httpClient.send(request);
-      Service.OpenConnectionResponse openCnxnResp;
-      switch (this.serialization) {
-      case JSON:
-        openCnxnResp = JsonService.MAPPER.readValue(response,
-            Service.OpenConnectionResponse.class);
-        break;
-      case PROTOBUF:
-        ProtobufTranslation pbTranslation = new ProtobufTranslationImpl();
-        Response genericResp = pbTranslation.parseResponse(response);
-        assertTrue("Expected an OpenConnnectionResponse, but got " + genericResp.getClass(),
-            genericResp instanceof Service.OpenConnectionResponse);
-        openCnxnResp = (Service.OpenConnectionResponse) genericResp;
-        break;
-      default:
-        throw new IllegalStateException("Should not reach here");
-      }
-
-      String hostname = InetAddress.getLocalHost().getHostName();
-
-      assertNotNull(openCnxnResp.rpcMetadata);
-      assertEquals(hostname + ":" + this.port, openCnxnResp.rpcMetadata.serverAddress);
-    } finally {
-      ConnectionSpec.getDatabaseLock().unlock();
-    }
-  }
-
-  @Test public void testCommitRollback() throws Exception {
-    final String productTable = "commitrollback_products";
-    final String salesTable = "commitrollback_sales";
-    ConnectionSpec.getDatabaseLock().lock();
-    try (final Connection conn = DriverManager.getConnection(url);
-        final Statement stmt = conn.createStatement()) {
-      assertFalse(stmt.execute("DROP TABLE IF EXISTS " + productTable));
-      assertFalse(
-          stmt.execute(
-              String.format("CREATE TABLE %s(id integer, stock integer)", productTable)));
-      assertFalse(stmt.execute("DROP TABLE IF EXISTS " + salesTable));
-      assertFalse(
-          stmt.execute(
-              String.format("CREATE TABLE %s(id integer, units_sold integer)", salesTable)));
-
-      final int productId = 1;
-      // No products and no sales
-      assertFalse(
-          stmt.execute(
-              String.format("INSERT INTO %s VALUES(%d, 0)", productTable, productId)));
-      assertFalse(
-          stmt.execute(
-              String.format("INSERT INTO %s VALUES(%d, 0)", salesTable, productId)));
-
-      conn.setAutoCommit(false);
-      PreparedStatement productStmt = conn.prepareStatement(
-          String.format("UPDATE %s SET stock = stock + ? WHERE id = ?", productTable));
-      PreparedStatement salesStmt = conn.prepareStatement(
-          String.format("UPDATE %s SET units_sold = units_sold + ? WHERE id = ?", salesTable));
-
-      // No stock
-      assertEquals(0, getInventory(conn, productTable, productId));
-
-      // Set a stock of 10 for product 1
-      productStmt.setInt(1, 10);
-      productStmt.setInt(2, productId);
-      productStmt.executeUpdate();
-
-      conn.commit();
-      assertEquals(10, getInventory(conn, productTable, productId));
-
-      // Sold 5 items (5 in stock, 5 sold)
-      productStmt.setInt(1, -5);
-      productStmt.setInt(2, productId);
-      productStmt.executeUpdate();
-      salesStmt.setInt(1, 5);
-      salesStmt.setInt(2, productId);
-      salesStmt.executeUpdate();
-
-      conn.commit();
-      // We will definitely see the updated values
-      assertEquals(5, getInventory(conn, productTable, productId));
-      assertEquals(5, getSales(conn, salesTable, productId));
-
-      // Update some "bad" values
-      productStmt.setInt(1, -10);
-      productStmt.setInt(2, productId);
-      productStmt.executeUpdate();
-      salesStmt.setInt(1, 10);
-      salesStmt.setInt(2, productId);
-      salesStmt.executeUpdate();
-
-      // We just went negative, nonsense. Better rollback.
-      conn.rollback();
-
-      // Should still have 5 and 5
-      assertEquals(5, getInventory(conn, productTable, productId));
-      assertEquals(5, getSales(conn, salesTable, productId));
-    } finally {
-      ConnectionSpec.getDatabaseLock().unlock();
-    }
-  }
-
-  private int getInventory(Connection conn, String productTable, int productId) throws Exception {
-    try (Statement stmt = conn.createStatement()) {
-      ResultSet results = stmt.executeQuery(
-          String.format("SELECT stock FROM %s WHERE id = %d", productTable, productId));
-      assertTrue(results.next());
-      return results.getInt(1);
-    }
-  }
-
-  private int getSales(Connection conn, String salesTable, int productId) throws Exception {
-    try (Statement stmt = conn.createStatement()) {
-      ResultSet results = stmt.executeQuery(
-          String.format("SELECT units_sold FROM %s WHERE id = %d", salesTable, productId));
-      assertTrue(results.next());
-      return results.getInt(1);
-    }
-  }
-
-  /** Factory that provides a {@link JdbcMeta}. */
-  public static class FullyRemoteJdbcMetaFactory implements Meta.Factory {
-
-    private static JdbcMeta instance = null;
-
-    private static JdbcMeta getInstance() {
-      if (instance == null) {
-        try {
-          instance = new JdbcMeta(CONNECTION_SPEC.url, CONNECTION_SPEC.username,
-              CONNECTION_SPEC.password);
-        } catch (SQLException e) {
-          throw new RuntimeException(e);
-        }
-      }
-      return instance;
-    }
-
-    @Override public Meta create(List<String> args) {
-      return getInstance();
-    }
-  }
-}
-
-// End RemoteMetaTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica-server/src/test/java/org/apache/calcite/avatica/server/HandlerFactoryTest.java
----------------------------------------------------------------------
diff --git a/avatica-server/src/test/java/org/apache/calcite/avatica/server/HandlerFactoryTest.java b/avatica-server/src/test/java/org/apache/calcite/avatica/server/HandlerFactoryTest.java
deleted file mode 100644
index 3504e02..0000000
--- a/avatica-server/src/test/java/org/apache/calcite/avatica/server/HandlerFactoryTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.server;
-
-import org.apache.calcite.avatica.remote.Driver.Serialization;
-import org.apache.calcite.avatica.remote.Service;
-
-import org.eclipse.jetty.server.Handler;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mockito;
-
-import static org.junit.Assert.assertTrue;
-
-/**
- * Tests the {@link HandlerFactory} implementation.
- */
-public class HandlerFactoryTest {
-
-  private HandlerFactory factory;
-  private Service service;
-
-  @Before
-  public void setup() {
-    this.factory = new HandlerFactory();
-    this.service = Mockito.mock(Service.class);
-  }
-
-  @Test
-  public void testJson() {
-    Handler handler = factory.getHandler(service, Serialization.JSON);
-    assertTrue("Expected an implementation of the AvaticaHandler, "
-        + "but got " + handler.getClass(), handler instanceof AvaticaJsonHandler);
-  }
-
-  @Test
-  public void testProtobuf() {
-    Handler handler = factory.getHandler(service, Serialization.PROTOBUF);
-    assertTrue("Expected an implementation of the AvaticaProtobufHandler, "
-        + "but got " + handler.getClass(), handler instanceof AvaticaProtobufHandler);
-  }
-}
-
-// End HandlerFactoryTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica-server/src/test/java/org/apache/calcite/avatica/test/AvaticaSuite.java
----------------------------------------------------------------------
diff --git a/avatica-server/src/test/java/org/apache/calcite/avatica/test/AvaticaSuite.java b/avatica-server/src/test/java/org/apache/calcite/avatica/test/AvaticaSuite.java
deleted file mode 100644
index 4a0c26c..0000000
--- a/avatica-server/src/test/java/org/apache/calcite/avatica/test/AvaticaSuite.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.test;
-
-import org.apache.calcite.avatica.RemoteDriverTest;
-
-import org.junit.runner.RunWith;
-
-import org.junit.runners.Suite;
-
-/**
- * Avatica test suite.
- */
-@RunWith(Suite.class)
-@Suite.SuiteClasses({
-    AvaticaUtilsTest.class,
-    ConnectStringParserTest.class,
-    RemoteDriverTest.class
-})
-public class AvaticaSuite {
-}
-
-// End AvaticaSuite.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica-server/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/avatica-server/src/test/resources/log4j.properties b/avatica-server/src/test/resources/log4j.properties
deleted file mode 100644
index 834e2db..0000000
--- a/avatica-server/src/test/resources/log4j.properties
+++ /dev/null
@@ -1,24 +0,0 @@
-# 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.
-
-# Root logger is configured at INFO and is sent to A1
-log4j.rootLogger=INFO, A1
-
-# A1 goes to the console
-log4j.appender.A1=org.apache.log4j.ConsoleAppender
-
-# Set the pattern for each log message
-log4j.appender.A1.layout=org.apache.log4j.PatternLayout
-log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p - %m%n

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/LICENSE
----------------------------------------------------------------------
diff --git a/avatica/LICENSE b/avatica/LICENSE
new file mode 100644
index 0000000..f7b9863
--- /dev/null
+++ b/avatica/LICENSE
@@ -0,0 +1,268 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
+
+
+
+
+
+-----------------------------------------------------------------------
+
+APACHE CALCITE SUBCOMPONENTS:
+
+The Apache Calcite project contains subcomponents with separate copyright
+notices and license terms. Your use of the source code for the these
+subcomponents is subject to the terms and conditions of the following
+licenses.
+
+-----------------------------------------------------------------------
+ The MIT License
+-----------------------------------------------------------------------
+
+The Apache Calcite project bundles the following files under the MIT License:
+
+- site
+    Parts of the web site generated by Jekyll (http://jekyllrb.com/)
+    Copyright (c) 2008-2015 Tom Preston-Werner
+- site/_sass/_font-awesome.scss
+    Font-awesome css files v4.1.0 (http://fortawesome.github.io/Font-Awesome/)
+    Copyright (c) 2013  Dave Gandy
+- site/_sass/_normalize.scss
+    normalize.css v3.0.2 | git.io/normalize
+    Copyright (c) Nicolas Gallagher and Jonathan Neal
+- site/_sass/_gridism.scss
+    Gridism: A simple, responsive, and handy CSS grid by @cobyism
+    https://github.com/cobyism/gridism
+    Copyright (c) 2013 Coby Chapple
+- site/js/html5shiv.min.js
+    HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem
+- site/js/respond.min.js
+    Respond.js v1.4.2: min/max-width media query polyfill
+    Copyright 2013 Scott Jehl
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+-----------------------------------------------------------------------
+ The Open Font License
+-----------------------------------------------------------------------
+
+The Apache Calcite project bundles the following fonts under the
+SIL Open Font License (OFL) - http://scripts.sil.org/OFL/
+
+- site/fonts/fontawesome-webfont.*
+   Font-awesome font files v4.0.3 (http://fortawesome.github.io/Font-Awesome/)

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/NOTICE
----------------------------------------------------------------------
diff --git a/avatica/NOTICE b/avatica/NOTICE
new file mode 100644
index 0000000..989c480
--- /dev/null
+++ b/avatica/NOTICE
@@ -0,0 +1,10 @@
+Apache Calcite
+Copyright 2012-2016 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+This product is based on source code originally developed
+by DynamoBI Corporation, LucidEra Inc., SQLstream Inc. and others
+under the auspices of the Eigenbase Foundation
+and released as the LucidDB project.

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/pom.xml
----------------------------------------------------------------------
diff --git a/avatica/core/pom.xml b/avatica/core/pom.xml
new file mode 100644
index 0000000..192d2dd
--- /dev/null
+++ b/avatica/core/pom.xml
@@ -0,0 +1,224 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.calcite.avatica</groupId>
+    <artifactId>calcite-avatica-parent</artifactId>
+    <version>1.7.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>calcite-avatica</artifactId>
+  <packaging>jar</packaging>
+  <name>Calcite Avatica</name>
+  <description>JDBC driver framework.</description>
+
+  <properties>
+    <top.dir>${project.basedir}/..</top.dir>
+  </properties>
+
+  <dependencies>
+    <!-- Make sure that there are no dependencies on other calcite modules,
+         or on libraries other than Jackson. -->
+    <dependency>
+      <groupId>org.apache.calcite</groupId>
+      <artifactId>calcite-avatica-metrics</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-annotations</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.google.protobuf</groupId>
+      <artifactId>protobuf-java</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpcore</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.hamcrest</groupId>
+      <artifactId>hamcrest-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-all</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.eclipse.m2e</groupId>
+          <artifactId>lifecycle-mapping</artifactId>
+          <version>1.0.0</version>
+          <configuration>
+            <lifecycleMappingMetadata>
+              <pluginExecutions>
+                <pluginExecution>
+                  <pluginExecutionFilter>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-checkstyle-plugin</artifactId>
+                    <versionRange>[2.12.1,)</versionRange>
+                    <goals>
+                      <goal>check</goal>
+                    </goals>
+                  </pluginExecutionFilter>
+                  <action>
+                    <ignore />
+                  </action>
+                </pluginExecution>
+              </pluginExecutions>
+            </lifecycleMappingMetadata>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+    <plugins>
+      <plugin>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>analyze</id>
+            <goals>
+              <goal>analyze-only</goal>
+            </goals>
+            <configuration>
+              <failOnWarning>true</failOnWarning>
+              <!-- ignore "unused but declared" warnings -->
+              <ignoredUnusedDeclaredDependencies>
+                <ignoredUnusedDeclaredDependency>org.slf4j:slf4j-log4j12</ignoredUnusedDeclaredDependency>
+              </ignoredUnusedDeclaredDependencies>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <!-- Parent module has the same plugin and does the work of
+           generating -sources.jar for each project. But without the
+           plugin declared here, IDEs don't know the sources are
+           available. -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar-no-fork</goal>
+              <goal>test-jar-no-fork</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <!-- Produce a tests jar so that avatica-server/pom.xml can reference for suite.
+           TODO: remove after moving over to annotation-based TestSuite definitions. -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-remote-resources-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>non-root-resources</id>
+            <goals>
+              <goal>process</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-shade-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>shade</goal>
+            </goals>
+            <configuration>
+              <relocations>
+                <relocation>
+                  <pattern>com.google.protobuf</pattern>
+                  <shadedPattern>org.apache.calcite.avatica.com.google.protobuf</shadedPattern>
+                </relocation>
+                <relocation>
+                  <pattern>org.apache.http</pattern>
+                  <shadedPattern>org.apache.calcite.avatica.org.apache.http</shadedPattern>
+                </relocation>
+                <relocation>
+                  <pattern>org.apache.commons</pattern>
+                  <shadedPattern>org.apache.calcite.avatica.org.apache.commons</shadedPattern>
+                </relocation>
+              </relocations>
+              <createDependencyReducedPom>false</createDependencyReducedPom>
+              <transformers>
+                <transformer implementation="org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer">
+                    <addHeader>false</addHeader>
+                </transformer>
+                <transformer implementation="org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer">
+                  <resources>
+                    <resource>LICENSE.txt</resource>
+                  </resources>
+                </transformer>
+              </transformers>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/com/google/protobuf/HBaseZeroCopyByteString.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/com/google/protobuf/HBaseZeroCopyByteString.java b/avatica/core/src/main/java/com/google/protobuf/HBaseZeroCopyByteString.java
new file mode 100644
index 0000000..62c4dd2
--- /dev/null
+++ b/avatica/core/src/main/java/com/google/protobuf/HBaseZeroCopyByteString.java
@@ -0,0 +1,77 @@
+/*
+ * 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 com.google.protobuf;
+
+/**
+ * Helper class to extract byte arrays from {@link ByteString} without copy.
+ *
+ * Without this protobufs would force us to copy every single byte array out
+ * of the objects de-serialized from the wire (which already do one copy, on
+ * top of the copies the JVM does to go from kernel buffer to C buffer and
+ * from C buffer to JVM buffer).
+ *
+ * Graciously copied from Apache HBase.
+ */
+public final class HBaseZeroCopyByteString extends LiteralByteString {
+  // Gotten from AsyncHBase code base with permission.
+  /** Private constructor so this class cannot be instantiated. */
+  private HBaseZeroCopyByteString() {
+    super(null);
+    throw new UnsupportedOperationException("Should never be here.");
+  }
+
+  /**
+   * Wraps a byte array in a {@link ByteString} without copying it.
+   *
+   * @param array The byte array to wrap
+   * @return a ByteString wrapping the <code>array</code>
+   */
+  public static ByteString wrap(final byte[] array) {
+    return new LiteralByteString(array);
+  }
+
+  /**
+   * Wraps a subset of a byte array in a {@link ByteString} without copying it.
+   *
+   * @param array The byte array to wrap
+   * @param offset the start of data in the array
+   * @param length The number of bytes of data at <code>offset</code>
+   * @return a ByteString wrapping the <code>array</code>
+   */
+  public static ByteString wrap(final byte[] array, int offset, int length) {
+    return new BoundedByteString(array, offset, length);
+  }
+
+
+  /**
+   * Extracts the byte array from the given {@link ByteString} without copy.
+   * @param buf A buffer from which to extract the array.  This buffer must be
+   * actually an instance of a {@code LiteralByteString}.
+   *
+   * @param buf <code>ByteString</code> to access
+   * @return The underlying byte array of the ByteString
+   */
+  public static byte[] zeroCopyGetBytes(final ByteString buf) {
+    if (buf instanceof LiteralByteString) {
+      return ((LiteralByteString) buf).bytes;
+    }
+    throw new UnsupportedOperationException("Need a LiteralByteString, got a "
+                                            + buf.getClass().getName());
+  }
+}
+
+// End HBaseZeroCopyByteString.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/com/google/protobuf/package-info.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/com/google/protobuf/package-info.java b/avatica/core/src/main/java/com/google/protobuf/package-info.java
new file mode 100644
index 0000000..92f110e
--- /dev/null
+++ b/avatica/core/src/main/java/com/google/protobuf/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+/**
+ * Avatica-custom classes to access protected classes in Google Protobuf.
+ */
+@PackageMarker
+package com.google.protobuf;
+
+import org.apache.calcite.avatica.util.PackageMarker;
+
+// End package-info.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaClientRuntimeException.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaClientRuntimeException.java b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaClientRuntimeException.java
new file mode 100644
index 0000000..df03b03
--- /dev/null
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaClientRuntimeException.java
@@ -0,0 +1,94 @@
+/*
+ * 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.AvaticaRuntimeException;
+import org.apache.calcite.avatica.remote.Service.ErrorResponse;
+import org.apache.calcite.avatica.remote.Service.RpcMetadataResponse;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The client-side representation of {@link AvaticaRuntimeException}. This exception is not intended
+ * for consumption by clients, {@link AvaticaSqlException} serves that purpose. This exception only
+ * exists to pass the original error attributes to a higher level of execution without modifying
+ * existing exception-handling logic.
+ */
+public class AvaticaClientRuntimeException extends RuntimeException {
+
+  private static final long serialVersionUID = 1L;
+
+  private final int errorCode;
+  private final String sqlState;
+  private final AvaticaSeverity severity;
+  private final List<String> serverExceptions;
+  private final RpcMetadataResponse metadata;
+
+  public AvaticaClientRuntimeException(String errorMessage, int errorCode, String sqlState,
+      AvaticaSeverity severity, List<String> serverExceptions, RpcMetadataResponse metadata) {
+    super(errorMessage);
+    this.errorCode = errorCode;
+    this.sqlState = sqlState;
+    this.severity = severity;
+    this.serverExceptions = serverExceptions;
+    this.metadata = metadata;
+  }
+
+  public AvaticaClientRuntimeException(String message, Throwable cause) {
+    super(message, cause);
+    errorCode = ErrorResponse.UNKNOWN_ERROR_CODE;
+    sqlState = ErrorResponse.UNKNOWN_SQL_STATE;
+    severity = AvaticaSeverity.UNKNOWN;
+    serverExceptions = Collections.singletonList("");
+    metadata = null;
+  }
+
+  public int getErrorCode() {
+    return errorCode;
+  }
+
+  public String getSqlState() {
+    return sqlState;
+  }
+
+  public AvaticaSeverity getSeverity() {
+    return severity;
+  }
+
+  public List<String> getServerExceptions() {
+    return serverExceptions;
+  }
+
+  public RpcMetadataResponse getRpcMetadata() {
+    return metadata;
+  }
+
+  @Override public String toString() {
+    StringBuilder sb = new StringBuilder(64);
+    sb.append(getClass().getSimpleName()).append(": ")
+    .append(getMessage()).append(". Error ").append(getErrorCode())
+    .append(" (").append(sqlState).append(") ").append(getSeverity()).append("\n\n");
+    for (String serverException : getServerExceptions()) {
+      sb.append(serverException).append("\n");
+    }
+    return sb.toString();
+  }
+
+}
+
+// End AvaticaClientRuntimeException.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
----------------------------------------------------------------------
diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
new file mode 100644
index 0000000..2d89f45
--- /dev/null
+++ b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
@@ -0,0 +1,696 @@
+/*
+ * 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.Meta.MetaResultSet;
+import org.apache.calcite.avatica.remote.Service.ErrorResponse;
+import org.apache.calcite.avatica.remote.Service.OpenConnectionRequest;
+import org.apache.calcite.avatica.remote.TypedValue;
+
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.CallableStatement;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.NClob;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLClientInfoException;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.SQLXML;
+import java.sql.Savepoint;
+import java.sql.Statement;
+import java.sql.Struct;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.TimeZone;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+
+/**
+ * Implementation of JDBC connection
+ * for the Avatica framework.
+ *
+ * <p>Abstract to allow newer versions of JDBC to add methods.
+ */
+public abstract class AvaticaConnection implements Connection {
+
+  /** The name of the sole column returned by DML statements, containing
+   * the number of rows modified. */
+  public static final String ROWCOUNT_COLUMN_NAME = "ROWCOUNT";
+
+  public static final String NUM_EXECUTE_RETRIES_KEY = "avatica.statement.retries";
+  public static final String NUM_EXECUTE_RETRIES_DEFAULT = "5";
+
+  /** The name of the sole column returned by an EXPLAIN statement.
+   *
+   * <p>Actually Avatica does not care what this column is called, but here is
+   * a useful place to define a suggested value. */
+  public static final String PLAN_COLUMN_NAME = "PLAN";
+
+  protected int statementCount;
+  private boolean closed;
+  private int holdability;
+  private int networkTimeout;
+
+  public final String id;
+  public final Meta.ConnectionHandle handle;
+  protected final UnregisteredDriver driver;
+  protected final AvaticaFactory factory;
+  final String url;
+  protected final Properties info;
+  protected final Meta meta;
+  protected final AvaticaDatabaseMetaData metaData;
+  public final Helper helper = Helper.INSTANCE;
+  public final Map<InternalProperty, Object> properties = new HashMap<>();
+  public final Map<Integer, AvaticaStatement> statementMap =
+      new ConcurrentHashMap<>();
+  protected final long maxRetriesPerExecute;
+
+  /**
+   * Creates an AvaticaConnection.
+   *
+   * <p>Not public; method is called only from the driver or a derived
+   * class.</p>
+   *
+   * @param driver Driver
+   * @param factory Factory for JDBC objects
+   * @param url Server URL
+   * @param info Other connection properties
+   */
+  protected AvaticaConnection(UnregisteredDriver driver,
+      AvaticaFactory factory,
+      String url,
+      Properties info) {
+    this.id = UUID.randomUUID().toString();
+    this.handle = new Meta.ConnectionHandle(this.id);
+    this.driver = driver;
+    this.factory = factory;
+    this.url = url;
+    this.info = info;
+    this.meta = driver.createMeta(this);
+    this.metaData = factory.newDatabaseMetaData(this);
+    this.holdability = metaData.getResultSetHoldability();
+    this.maxRetriesPerExecute = getNumStatementRetries(info);
+  }
+
+  /** Computes the number of retries
+   * {@link AvaticaStatement#executeInternal(Meta.Signature, boolean)}
+   * should retry before failing. */
+  long getNumStatementRetries(Properties props) {
+    return Long.valueOf(Objects.requireNonNull(props)
+        .getProperty(NUM_EXECUTE_RETRIES_KEY, NUM_EXECUTE_RETRIES_DEFAULT));
+  }
+
+  /** Returns a view onto this connection's configuration properties. Code
+   * in Avatica and derived projects should use this view rather than calling
+   * {@link java.util.Properties#getProperty(String)}. Derived projects will
+   * almost certainly subclass {@link ConnectionConfig} with their own
+   * properties. */
+  public ConnectionConfig config() {
+    return new ConnectionConfigImpl(info);
+  }
+
+  /**
+   * Opens the connection on the server.
+   */
+  public void openConnection() {
+    // Open the connection on the server
+    this.meta.openConnection(handle, OpenConnectionRequest.serializeProperties(info));
+  }
+
+  // Connection methods
+
+  public AvaticaStatement createStatement() throws SQLException {
+    //noinspection MagicConstant
+    return createStatement(ResultSet.TYPE_FORWARD_ONLY,
+        ResultSet.CONCUR_READ_ONLY,
+        holdability);
+  }
+
+  public PreparedStatement prepareStatement(String sql) throws SQLException {
+    //noinspection MagicConstant
+    return prepareStatement(
+        sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
+        holdability);
+  }
+
+  public CallableStatement prepareCall(String sql) throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public String nativeSQL(String sql) throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public void setAutoCommit(boolean autoCommit) throws SQLException {
+    meta.connectionSync(handle, new ConnectionPropertiesImpl().setAutoCommit(autoCommit));
+  }
+
+  public boolean getAutoCommit() throws SQLException {
+    return unbox(sync().isAutoCommit(), true);
+  }
+
+  public void commit() throws SQLException {
+    meta.commit(handle);
+  }
+
+  public void rollback() throws SQLException {
+    meta.rollback(handle);
+  }
+
+  public void close() throws SQLException {
+    if (!closed) {
+      closed = true;
+
+      // Per specification, if onConnectionClose throws, this method will throw
+      // a SQLException, but statement will still be closed.
+      try {
+        meta.closeConnection(handle);
+        driver.handler.onConnectionClose(this);
+      } catch (RuntimeException e) {
+        throw helper.createException("While closing connection", e);
+      }
+    }
+  }
+
+  public boolean isClosed() throws SQLException {
+    return closed;
+  }
+
+  public DatabaseMetaData getMetaData() throws SQLException {
+    return metaData;
+  }
+
+  public void setReadOnly(boolean readOnly) throws SQLException {
+    meta.connectionSync(handle, new ConnectionPropertiesImpl().setReadOnly(readOnly));
+  }
+
+  public boolean isReadOnly() throws SQLException {
+    return unbox(sync().isReadOnly(), true);
+  }
+
+  public void setCatalog(String catalog) throws SQLException {
+    meta.connectionSync(handle, new ConnectionPropertiesImpl().setCatalog(catalog));
+  }
+
+  public String getCatalog() {
+    return sync().getCatalog();
+  }
+
+  public void setTransactionIsolation(int level) throws SQLException {
+    meta.connectionSync(handle, new ConnectionPropertiesImpl().setTransactionIsolation(level));
+  }
+
+  public int getTransactionIsolation() throws SQLException {
+    //noinspection MagicConstant
+    return unbox(sync().getTransactionIsolation(), TRANSACTION_NONE);
+  }
+
+  public SQLWarning getWarnings() throws SQLException {
+    return null;
+  }
+
+  public void clearWarnings() throws SQLException {
+    // no-op since connection pooling often calls this.
+  }
+
+  public Statement createStatement(
+      int resultSetType, int resultSetConcurrency) throws SQLException {
+    //noinspection MagicConstant
+    return createStatement(resultSetType, resultSetConcurrency, holdability);
+  }
+
+  public PreparedStatement prepareStatement(
+      String sql,
+      int resultSetType,
+      int resultSetConcurrency) throws SQLException {
+    //noinspection MagicConstant
+    return prepareStatement(
+        sql, resultSetType, resultSetConcurrency, holdability);
+  }
+
+  public CallableStatement prepareCall(
+      String sql,
+      int resultSetType,
+      int resultSetConcurrency) throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public Map<String, Class<?>> getTypeMap() throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public void setHoldability(int holdability) throws SQLException {
+    if (!(holdability == ResultSet.CLOSE_CURSORS_AT_COMMIT
+        || holdability == ResultSet.HOLD_CURSORS_OVER_COMMIT)) {
+      throw new SQLException("invalid value");
+    }
+    this.holdability = holdability;
+  }
+
+  public int getHoldability() throws SQLException {
+    return holdability;
+  }
+
+  public Savepoint setSavepoint() throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public Savepoint setSavepoint(String name) throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public void rollback(Savepoint savepoint) throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public void releaseSavepoint(Savepoint savepoint) throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public AvaticaStatement createStatement(
+      int resultSetType,
+      int resultSetConcurrency,
+      int resultSetHoldability) throws SQLException {
+    return factory.newStatement(this, null, resultSetType, resultSetConcurrency,
+        resultSetHoldability);
+  }
+
+  public PreparedStatement prepareStatement(
+      String sql,
+      int resultSetType,
+      int resultSetConcurrency,
+      int resultSetHoldability) throws SQLException {
+    try {
+      final Meta.StatementHandle h = meta.prepare(handle, sql, -1);
+      return factory.newPreparedStatement(this, h, h.signature, resultSetType,
+          resultSetConcurrency, resultSetHoldability);
+    } catch (RuntimeException e) {
+      throw helper.createException("while preparing SQL: " + sql, e);
+    }
+  }
+
+  public CallableStatement prepareCall(
+      String sql,
+      int resultSetType,
+      int resultSetConcurrency,
+      int resultSetHoldability) throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public PreparedStatement prepareStatement(
+      String sql, int autoGeneratedKeys) throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public PreparedStatement prepareStatement(
+      String sql, int[] columnIndexes) throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public PreparedStatement prepareStatement(
+      String sql, String[] columnNames) throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public Clob createClob() throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public Blob createBlob() throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public NClob createNClob() throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public SQLXML createSQLXML() throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public boolean isValid(int timeout) throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public void setClientInfo(String name, String value)
+      throws SQLClientInfoException {
+    throw helper.clientInfo();
+  }
+
+  public void setClientInfo(Properties properties)
+      throws SQLClientInfoException {
+    throw helper.clientInfo();
+  }
+
+  public String getClientInfo(String name) throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public Properties getClientInfo() throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public Array createArrayOf(String typeName, Object[] elements)
+      throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public Struct createStruct(String typeName, Object[] attributes)
+      throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public void setSchema(String schema) throws SQLException {
+    meta.connectionSync(handle, new ConnectionPropertiesImpl().setSchema(schema));
+  }
+
+  public String getSchema() {
+    return sync().getSchema();
+  }
+
+  public void abort(Executor executor) throws SQLException {
+    throw helper.unsupported();
+  }
+
+  public void setNetworkTimeout(
+      Executor executor, int milliseconds) throws SQLException {
+    this.networkTimeout = milliseconds;
+  }
+
+  public int getNetworkTimeout() throws SQLException {
+    return networkTimeout;
+  }
+
+  public <T> T unwrap(Class<T> iface) throws SQLException {
+    if (iface.isInstance(this)) {
+      return iface.cast(this);
+    }
+    throw helper.createException(
+        "does not implement '" + iface + "'");
+  }
+
+  public boolean isWrapperFor(Class<?> iface) throws SQLException {
+    return iface.isInstance(this);
+  }
+
+  /** Returns the time zone of this connection. Determines the offset applied
+   * when converting datetime values from the database into
+   * {@link java.sql.Timestamp} values. */
+  public TimeZone getTimeZone() {
+    final String timeZoneName = config().timeZone();
+    return timeZoneName == null
+        ? TimeZone.getDefault()
+        : TimeZone.getTimeZone(timeZoneName);
+  }
+
+  /**
+   * Executes a prepared query, closing any previously open result set.
+   *
+   * @param statement     Statement
+   * @param signature     Prepared query
+   * @param firstFrame    First frame of rows, or null if we need to execute
+   * @param state         The state used to create the given result
+   * @param isUpdate      Was the caller context via {@link PreparedStatement#executeUpdate()}.
+   * @return Result set
+   * @throws java.sql.SQLException if a database error occurs
+   */
+  protected ResultSet executeQueryInternal(AvaticaStatement statement,
+      Meta.Signature signature, Meta.Frame firstFrame, QueryState state, boolean isUpdate)
+      throws SQLException {
+    // Close the previous open result set, if there is one.
+    Meta.Frame frame = firstFrame;
+    Meta.Signature signature2 = signature;
+
+    synchronized (statement) {
+      if (statement.openResultSet != null) {
+        final AvaticaResultSet rs = statement.openResultSet;
+        statement.openResultSet = null;
+        try {
+          rs.close();
+        } catch (Exception e) {
+          throw helper.createException(
+              "Error while closing previous result set", e);
+        }
+      }
+
+      try {
+        if (statement.isWrapperFor(AvaticaPreparedStatement.class)) {
+          final AvaticaPreparedStatement pstmt = (AvaticaPreparedStatement) statement;
+          Meta.StatementHandle handle = pstmt.handle;
+          if (isUpdate) {
+            // Make a copy of the StatementHandle, nulling out the Signature.
+            // CALCITE-1086 we don't need to send the Signature to the server
+            // when we're only performing an update. Saves on serialization.
+            handle = new Meta.StatementHandle(handle.connectionId, handle.id, null);
+          }
+          final Meta.ExecuteResult executeResult =
+              meta.execute(handle, pstmt.getParameterValues(),
+                  statement.getFetchSize());
+          final MetaResultSet metaResultSet = executeResult.resultSets.get(0);
+          frame = metaResultSet.firstFrame;
+          statement.updateCount = metaResultSet.updateCount;
+          signature2 = executeResult.resultSets.get(0).signature;
+        }
+      } catch (Exception e) {
+        e.printStackTrace();
+        throw helper.createException(e.getMessage(), e);
+      }
+
+      final TimeZone timeZone = getTimeZone();
+      if (frame == null && signature2 == null && statement.updateCount != -1) {
+        statement.openResultSet = null;
+      } else {
+        // Duplicative SQL, for support non-prepared statements
+        statement.openResultSet =
+            factory.newResultSet(statement, state, signature2, timeZone, frame);
+      }
+    }
+    // Release the monitor before executing, to give another thread the
+    // opportunity to call cancel.
+    try {
+      if (statement.openResultSet != null) {
+        statement.openResultSet.execute();
+        isUpdateCapable(statement);
+      }
+    } catch (Exception e) {
+      throw helper.createException(
+          "exception while executing query: " + e.getMessage(), e);
+    }
+    return statement.openResultSet;
+  }
+
+  /** Returns whether a a statement is capable of updates and if so,
+   * and the statement's {@code updateCount} is still -1, proceeds to
+   * get updateCount value from statement's resultSet.
+   *
+   * <p>Handles "ROWCOUNT" object as Number or List
+   *
+   * @param statement Statement
+   * @throws SQLException on error
+   */
+  private void isUpdateCapable(final AvaticaStatement statement)
+      throws SQLException {
+    Meta.Signature signature = statement.getSignature();
+    if (signature == null || signature.statementType == null) {
+      return;
+    }
+    if (signature.statementType.canUpdate() && statement.updateCount == -1) {
+      statement.openResultSet.next();
+      Object obj = statement.openResultSet.getObject(ROWCOUNT_COLUMN_NAME);
+      if (obj instanceof Number) {
+        statement.updateCount = ((Number) obj).intValue();
+      } else if (obj instanceof List) {
+        @SuppressWarnings("unchecked")
+        final List<Number> numbers = (List<Number>) obj;
+        statement.updateCount = numbers.get(0).intValue();
+      } else {
+        throw helper.createException("Not a valid return result.");
+      }
+      statement.openResultSet = null;
+    }
+  }
+
+  protected Meta.ExecuteResult prepareAndExecuteInternal(
+      final AvaticaStatement statement, final String sql, long maxRowCount)
+      throws SQLException, NoSuchStatementException {
+    final Meta.PrepareCallback callback =
+        new Meta.PrepareCallback() {
+          public Object getMonitor() {
+            return statement;
+          }
+
+          public void clear() throws SQLException {
+            if (statement.openResultSet != null) {
+              final AvaticaResultSet rs = statement.openResultSet;
+              statement.openResultSet = null;
+              try {
+                rs.close();
+              } catch (Exception e) {
+                throw helper.createException(
+                    "Error while closing previous result set", e);
+              }
+            }
+          }
+
+          public void assign(Meta.Signature signature, Meta.Frame firstFrame,
+              long updateCount) throws SQLException {
+            statement.setSignature(signature);
+
+            if (updateCount != -1) {
+              statement.updateCount = updateCount;
+            } else {
+              final TimeZone timeZone = getTimeZone();
+              statement.openResultSet = factory.newResultSet(statement, new QueryState(sql),
+                  signature, timeZone, firstFrame);
+            }
+          }
+
+          public void execute() throws SQLException {
+            if (statement.openResultSet != null) {
+              statement.openResultSet.execute();
+              isUpdateCapable(statement);
+            }
+          }
+        };
+    return meta.prepareAndExecute(statement.handle, sql, maxRowCount, callback);
+  }
+
+  protected ResultSet createResultSet(Meta.MetaResultSet metaResultSet, QueryState state)
+      throws SQLException {
+    final Meta.StatementHandle h = new Meta.StatementHandle(
+        metaResultSet.connectionId, metaResultSet.statementId, null);
+    final AvaticaStatement statement = lookupStatement(h);
+    // These are all the metadata operations, no updates
+    ResultSet resultSet = executeQueryInternal(statement, metaResultSet.signature.sanitize(),
+        metaResultSet.firstFrame, state, false);
+    if (metaResultSet.ownStatement) {
+      resultSet.getStatement().closeOnCompletion();
+    }
+    return resultSet;
+  }
+
+  /** Creates a statement wrapper around an existing handle. */
+  protected AvaticaStatement lookupStatement(Meta.StatementHandle h)
+      throws SQLException {
+    final AvaticaStatement statement = statementMap.get(h.id);
+    if (statement != null) {
+      return statement;
+    }
+    //noinspection MagicConstant
+    return factory.newStatement(this, Objects.requireNonNull(h),
+        ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, holdability);
+  }
+
+  // do not make public
+  protected static Trojan createTrojan() {
+    return new Trojan();
+  }
+
+  /** Converts a {@link Boolean} to a {@code boolean}, with a default value. */
+  private boolean unbox(Boolean b, boolean defaultValue) {
+    return b == null ? defaultValue : b;
+  }
+
+  /** Converts an {@link Integer} to an {@code int}, with a default value. */
+  private int unbox(Integer i, int defaultValue) {
+    return i == null ? defaultValue : i;
+  }
+
+  private Meta.ConnectionProperties sync() {
+    return meta.connectionSync(handle, new ConnectionPropertiesImpl());
+  }
+
+  /** A way to call package-protected methods. But only a sub-class of
+   * connection can create one. */
+  public static class Trojan {
+    // must be private
+    private Trojan() {
+    }
+
+    /** A means for anyone who has a trojan to call the protected method
+     * {@link org.apache.calcite.avatica.AvaticaResultSet#execute()}.
+     * @throws SQLException if execute fails for some reason. */
+    public ResultSet execute(AvaticaResultSet resultSet) throws SQLException {
+      return resultSet.execute();
+    }
+
+    /** A means for anyone who has a trojan to call the protected method
+     * {@link org.apache.calcite.avatica.AvaticaStatement#getParameterValues()}.
+     */
+    public List<TypedValue> getParameterValues(AvaticaStatement statement) {
+      return statement.getParameterValues();
+    }
+
+    /** A means for anyone who has a trojan to get the protected field
+     * {@link org.apache.calcite.avatica.AvaticaConnection#meta}. */
+    public Meta getMeta(AvaticaConnection connection) {
+      return connection.meta;
+    }
+  }
+
+  /**
+   * A Callable-like interface but without a "throws Exception".
+   *
+   * @param <T> The return type from {@code call}.
+   */
+  public interface CallableWithoutException<T> {
+    T call();
+  }
+
+  /**
+   * Invokes the given "callable", retrying the call when the server responds with an error
+   * denoting that the connection is missing on the server.
+   *
+   * @param callable The function to invoke.
+   * @return The value from the result of the callable.
+   */
+  public <T> T invokeWithRetries(CallableWithoutException<T> callable) {
+    RuntimeException lastException = null;
+    for (int i = 0; i < maxRetriesPerExecute; i++) {
+      try {
+        return callable.call();
+      } catch (AvaticaClientRuntimeException e) {
+        lastException = e;
+        if (ErrorResponse.MISSING_CONNECTION_ERROR_CODE == e.getErrorCode()) {
+          this.openConnection();
+          continue;
+        }
+        throw e;
+      }
+    }
+    if (null != lastException) {
+      throw lastException;
+    } else {
+      // Shouldn't ever happen.
+      throw new IllegalStateException();
+    }
+  }
+}
+
+// End AvaticaConnection.java