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

[3/9] incubator-calcite git commit: [CALCITE-618] Add Avatica support for getTables (Julian Hyde and Nick Dimiduk)

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/test/java/org/apache/calcite/avatica/test/RemoteDriverTest.java
----------------------------------------------------------------------
diff --git a/avatica/src/test/java/org/apache/calcite/avatica/test/RemoteDriverTest.java b/avatica/src/test/java/org/apache/calcite/avatica/test/RemoteDriverTest.java
index 5cf1ea5..c007f02 100644
--- a/avatica/src/test/java/org/apache/calcite/avatica/test/RemoteDriverTest.java
+++ b/avatica/src/test/java/org/apache/calcite/avatica/test/RemoteDriverTest.java
@@ -16,21 +16,34 @@
  */
 package org.apache.calcite.avatica.test;
 
+import org.apache.calcite.avatica.AvaticaConnection;
+import org.apache.calcite.avatica.jdbc.JdbcMeta;
 import org.apache.calcite.avatica.remote.LocalJsonService;
+import org.apache.calcite.avatica.remote.LocalService;
 import org.apache.calcite.avatica.remote.MockJsonService;
+import org.apache.calcite.avatica.remote.Service;
+
+import net.hydromatic.scott.data.hsqldb.ScottHsqldb;
 
 import org.junit.Ignore;
 import org.junit.Test;
 
 import java.sql.Connection;
 import java.sql.DriverManager;
+import java.sql.ParameterMetaData;
+import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
 
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 /**
  * Unit test for Avatica Remote JDBC driver.
@@ -40,7 +53,20 @@ public class RemoteDriverTest {
       MockJsonService.Factory.class.getName();
 
   public static final String LJS =
-      LocalJsonService.Factory.class.getName();
+      LocalJdbcServiceFactory.class.getName();
+
+  public static final String QRJS =
+      QuasiRemoteJdbcServiceFactory.class.getName();
+
+  private static final ConnectionSpec CONNECTION_SPEC = ConnectionSpec.HSQLDB;
+
+  private Connection mjs() throws SQLException {
+    return DriverManager.getConnection("jdbc:avatica:remote:factory=" + MJS);
+  }
+
+  private Connection ljs() throws SQLException {
+    return DriverManager.getConnection("jdbc:avatica:remote:factory=" + QRJS);
+  }
 
   @Test public void testRegister() throws Exception {
     final Connection connection =
@@ -50,6 +76,33 @@ public class RemoteDriverTest {
     assertThat(connection.isClosed(), is(true));
   }
 
+  @Test public void testSchemas() throws Exception {
+    final Connection connection = mjs();
+    final ResultSet resultSet =
+        connection.getMetaData().getSchemas(null, null);
+    assertFalse(resultSet.next());
+    final ResultSetMetaData metaData = resultSet.getMetaData();
+    assertTrue(metaData.getColumnCount() >= 2);
+    assertEquals("TABLE_CATALOG", metaData.getColumnName(1));
+    assertEquals("TABLE_SCHEM", metaData.getColumnName(2));
+    resultSet.close();
+    connection.close();
+  }
+
+  @Test public void testTables() throws Exception {
+    final Connection connection = mjs();
+    final ResultSet resultSet =
+        connection.getMetaData().getTables(null, null, null, new String[0]);
+    assertFalse(resultSet.next());
+    final ResultSetMetaData metaData = resultSet.getMetaData();
+    assertTrue(metaData.getColumnCount() >= 3);
+    assertEquals("TABLE_CAT", metaData.getColumnName(1));
+    assertEquals("TABLE_SCHEM", metaData.getColumnName(2));
+    assertEquals("TABLE_NAME", metaData.getColumnName(3));
+    resultSet.close();
+    connection.close();
+  }
+
   @Ignore
   @Test public void testNoFactory() throws Exception {
     final Connection connection =
@@ -68,8 +121,7 @@ public class RemoteDriverTest {
 
   @Ignore
   @Test public void testCatalogsMock() throws Exception {
-    final Connection connection =
-        DriverManager.getConnection("jdbc:avatica:remote:factory=" + MJS);
+    final Connection connection = mjs();
     assertThat(connection.isClosed(), is(false));
     final ResultSet resultSet = connection.getMetaData().getSchemas();
     assertFalse(resultSet.next());
@@ -81,6 +133,164 @@ public class RemoteDriverTest {
     connection.close();
     assertThat(connection.isClosed(), is(true));
   }
+
+  @Test public void testStatementExecuteQueryLocal() throws Exception {
+    checkStatementExecuteQuery(ljs(), false);
+  }
+
+  @Ignore
+  @Test public void testStatementExecuteQueryMock() throws Exception {
+    checkStatementExecuteQuery(mjs(), false);
+  }
+
+  @Ignore
+  @Test public void testPrepareExecuteQueryLocal() throws Exception {
+    checkStatementExecuteQuery(ljs(), true);
+  }
+
+  @Ignore
+  @Test public void testPrepareExecuteQueryMock() throws Exception {
+    checkStatementExecuteQuery(mjs(), true);
+  }
+
+  private void checkStatementExecuteQuery(Connection connection,
+      boolean prepare) throws SQLException {
+    final String sql = "select * from (\n"
+        + "  values (1, 'a'), (null, 'b'), (3, 'c')) as t (c1, c2)";
+    final Statement statement;
+    final ResultSet resultSet;
+    final ParameterMetaData parameterMetaData;
+    if (prepare) {
+      final PreparedStatement ps = connection.prepareStatement(sql);
+      statement = ps;
+      parameterMetaData = ps.getParameterMetaData();
+      resultSet = ps.executeQuery();
+    } else {
+      statement = connection.createStatement();
+      parameterMetaData = null;
+      resultSet = statement.executeQuery(sql);
+    }
+    if (parameterMetaData != null) {
+      assertThat(parameterMetaData.getParameterCount(), equalTo(2));
+    }
+    final ResultSetMetaData metaData = resultSet.getMetaData();
+    assertEquals(2, metaData.getColumnCount());
+    assertEquals("C1", metaData.getColumnName(1));
+    assertEquals("C2", metaData.getColumnName(2));
+    assertTrue(resultSet.next());
+    assertTrue(resultSet.next());
+    assertTrue(resultSet.next());
+    assertFalse(resultSet.next());
+    resultSet.close();
+    statement.close();
+    connection.close();
+  }
+
+  @Test public void testPrepareBindExecuteFetch() throws Exception {
+    checkPrepareBindExecuteFetch(ljs());
+  }
+
+  private void checkPrepareBindExecuteFetch(Connection connection)
+      throws SQLException {
+    final String sql = "select cast(? as integer) * 3 as c, 'x' as x\n"
+        + "from (values (1, 'a'))";
+    final PreparedStatement ps =
+        connection.prepareStatement(sql);
+    final ResultSetMetaData metaData = ps.getMetaData();
+    assertEquals(2, metaData.getColumnCount());
+    assertEquals("C", metaData.getColumnName(1));
+    assertEquals("X", metaData.getColumnName(2));
+    try {
+      final ResultSet resultSet = ps.executeQuery();
+      fail("expected error, got " + resultSet);
+    } catch (SQLException e) {
+      assertThat(e.getMessage(),
+          equalTo("exception while executing query: unbound parameter"));
+    }
+
+    final ParameterMetaData parameterMetaData = ps.getParameterMetaData();
+    assertThat(parameterMetaData.getParameterCount(), equalTo(1));
+
+    ps.setInt(1, 10);
+    final ResultSet resultSet = ps.executeQuery();
+    assertTrue(resultSet.next());
+    assertThat(resultSet.getInt(1), equalTo(30));
+    assertFalse(resultSet.next());
+    resultSet.close();
+
+    ps.setInt(1, 20);
+    final ResultSet resultSet2 = ps.executeQuery();
+    assertFalse(resultSet2.isClosed());
+    assertTrue(resultSet2.next());
+    assertThat(resultSet2.getInt(1), equalTo(60));
+    assertThat(resultSet2.wasNull(), is(false));
+    assertFalse(resultSet2.next());
+    resultSet2.close();
+
+    ps.setObject(1, null);
+    final ResultSet resultSet3 = ps.executeQuery();
+    assertTrue(resultSet3.next());
+    assertThat(resultSet3.getInt(1), equalTo(0));
+    assertThat(resultSet3.wasNull(), is(true));
+    assertFalse(resultSet3.next());
+    resultSet3.close();
+
+    ps.close();
+    connection.close();
+  }
+
+  /** Factory that creates a service based on a local JDBC connection. */
+  public static class LocalJdbcServiceFactory implements Service.Factory {
+    @Override public Service create(AvaticaConnection connection) {
+      try {
+        Connection connection1 =
+            DriverManager.getConnection(CONNECTION_SPEC.url,
+                CONNECTION_SPEC.username, CONNECTION_SPEC.password);
+        return new LocalService(new JdbcMeta(connection1));
+      } catch (SQLException e) {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  /** Factory that creates a service based on a local JDBC connection. */
+  public static class QuasiRemoteJdbcServiceFactory implements Service.Factory {
+    @Override public Service create(AvaticaConnection connection) {
+      try {
+        Connection connection1 =
+            DriverManager.getConnection(CONNECTION_SPEC.url,
+                CONNECTION_SPEC.username, CONNECTION_SPEC.password);
+        final JdbcMeta jdbcMeta = new JdbcMeta(connection1);
+        final LocalService localService = new LocalService(jdbcMeta);
+        final LocalJsonService localJsonService =
+            new LocalJsonService(localService);
+        return localJsonService;
+      } catch (SQLException e) {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  /** Information necessary to create a JDBC connection. Specify one to run
+   * tests against a different database. (hsqldb is the default.) */
+  public static class ConnectionSpec {
+    public final String url;
+    public final String username;
+    public final String password;
+    public final String driver;
+
+    public ConnectionSpec(String url, String username, String password,
+        String driver) {
+      this.url = url;
+      this.username = username;
+      this.password = password;
+      this.driver = driver;
+    }
+
+    public static final ConnectionSpec HSQLDB =
+        new ConnectionSpec(ScottHsqldb.URI, ScottHsqldb.USER,
+            ScottHsqldb.PASSWORD, "org.hsqldb.jdbcDriver");
+  }
 }
 
 // End RemoteDriverTest.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/core/pom.xml
----------------------------------------------------------------------
diff --git a/core/pom.xml b/core/pom.xml
index 36a88f5..34059f7 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -89,6 +89,11 @@ limitations under the License.
       <scope>test</scope>
     </dependency>
     <dependency>
+      <groupId>net.hydromatic</groupId>
+      <artifactId>scott-data-hsqldb</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>org.codehaus.janino</groupId>
       <artifactId>janino</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/core/src/main/java/org/apache/calcite/jdbc/CalciteJdbc41Factory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteJdbc41Factory.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteJdbc41Factory.java
index 0b87f0f..e5a567c 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteJdbc41Factory.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteJdbc41Factory.java
@@ -91,13 +91,13 @@ public class CalciteJdbc41Factory extends CalciteFactory {
   }
 
   public CalciteResultSet newResultSet(AvaticaStatement statement,
-      Meta.Signature signature, TimeZone timeZone, Iterable<Object> iterable) {
+      Meta.Signature signature, TimeZone timeZone, Meta.Frame firstFrame) {
     final ResultSetMetaData metaData =
         newResultSetMetaData(statement, signature);
     final CalcitePrepare.CalciteSignature calciteSignature =
         (CalcitePrepare.CalciteSignature) signature;
     return new CalciteResultSet(statement, calciteSignature, metaData, timeZone,
-        iterable);
+        firstFrame);
   }
 
   public ResultSetMetaData newResultSetMetaData(AvaticaStatement statement,

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java
index 152e918..cf395f6 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java
@@ -157,7 +157,7 @@ public class CalciteMetaImpl extends MetaImpl {
     final Iterable<Object> iterable = (Iterable<Object>) (Iterable) enumerable;
     return createResultSet(Collections.<String, Object>emptyMap(),
         columns, CursorFactory.record(clazz, fields, fieldNames),
-        iterable);
+        new Frame(0, true, iterable));
   }
 
   @Override protected <E> MetaResultSet
@@ -165,12 +165,12 @@ public class CalciteMetaImpl extends MetaImpl {
     final List<ColumnMetaData> columns = fieldMetaData(clazz).columns;
     final CursorFactory cursorFactory = CursorFactory.deduce(columns, clazz);
     return createResultSet(Collections.<String, Object>emptyMap(), columns,
-        cursorFactory, Collections.emptyList());
+        cursorFactory, Frame.EMPTY);
   }
 
   protected MetaResultSet createResultSet(
       Map<String, Object> internalParameters, List<ColumnMetaData> columns,
-      CursorFactory cursorFactory, final Iterable<Object> iterable) {
+      CursorFactory cursorFactory, final Frame firstFrame) {
     try {
       final CalciteConnectionImpl connection = getConnection();
       final AvaticaStatement statement = connection.createStatement();
@@ -180,10 +180,10 @@ public class CalciteMetaImpl extends MetaImpl {
               columns, cursorFactory, -1, null) {
             @Override public Enumerable<Object> enumerable(
                 DataContext dataContext) {
-              return Linq4j.asEnumerable(iterable);
+              return Linq4j.asEnumerable(firstFrame.rows);
             }
           };
-      return new MetaResultSet(statement.getId(), true, signature, iterable);
+      return new MetaResultSet(statement.getId(), true, signature, firstFrame);
     } catch (SQLException e) {
       throw new RuntimeException(e);
     }
@@ -441,7 +441,7 @@ public class CalciteMetaImpl extends MetaImpl {
   }
 
   @Override public Iterable<Object> createIterable(StatementHandle handle,
-      Signature signature, Iterable<Object> iterable) {
+      Signature signature, List<Object> parameterValues, Frame firstFrame) {
     try {
       //noinspection unchecked
       final CalcitePrepare.CalciteSignature<Object> calciteSignature =

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/core/src/main/java/org/apache/calcite/jdbc/CalciteResultSet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteResultSet.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteResultSet.java
index 72f31ef..777524b 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteResultSet.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteResultSet.java
@@ -20,6 +20,7 @@ import org.apache.calcite.avatica.AvaticaResultSet;
 import org.apache.calcite.avatica.AvaticaStatement;
 import org.apache.calcite.avatica.ColumnMetaData;
 import org.apache.calcite.avatica.Handler;
+import org.apache.calcite.avatica.Meta;
 import org.apache.calcite.avatica.util.Cursor;
 import org.apache.calcite.linq4j.Enumerator;
 import org.apache.calcite.linq4j.Linq4j;
@@ -42,8 +43,8 @@ public class CalciteResultSet extends AvaticaResultSet {
   CalciteResultSet(AvaticaStatement statement,
       CalcitePrepare.CalciteSignature calciteSignature,
       ResultSetMetaData resultSetMetaData, TimeZone timeZone,
-      Iterable<Object> iterable) {
-    super(statement, calciteSignature, resultSetMetaData, timeZone, iterable);
+      Meta.Frame firstFrame) {
+    super(statement, calciteSignature, resultSetMetaData, timeZone, firstFrame);
   }
 
   @Override protected CalciteResultSet execute() throws SQLException {
@@ -68,7 +69,7 @@ public class CalciteResultSet extends AvaticaResultSet {
     final CalciteResultSet resultSet =
         new CalciteResultSet(statement,
             (CalcitePrepare.CalciteSignature) signature, resultSetMetaData,
-            localCalendar.getTimeZone(), iterable);
+            localCalendar.getTimeZone(), new Meta.Frame(0, true, iterable));
     final Cursor cursor = resultSet.createCursor(elementType, iterable);
     final List<ColumnMetaData> columnMetaDataList;
     if (elementType instanceof ColumnMetaData.StructType) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/core/src/main/java/org/apache/calcite/materialize/MaterializationService.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/materialize/MaterializationService.java b/core/src/main/java/org/apache/calcite/materialize/MaterializationService.java
index 0819dcf..2d4c3f2 100644
--- a/core/src/main/java/org/apache/calcite/materialize/MaterializationService.java
+++ b/core/src/main/java/org/apache/calcite/materialize/MaterializationService.java
@@ -126,7 +126,7 @@ public class MaterializationService {
               Functions.adapt(calciteSignature.columns,
                   new Function1<ColumnMetaData, ColumnMetaData.Rep>() {
                     public ColumnMetaData.Rep apply(ColumnMetaData column) {
-                      return column.type.representation;
+                      return column.type.rep;
                     }
                   }),
               new AbstractQueryable<Object>() {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
index 97eb68a..298f8d6 100644
--- a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
+++ b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
@@ -50,6 +50,7 @@ import com.google.common.collect.ImmutableMultiset;
 import com.google.common.collect.Lists;
 
 import net.hydromatic.foodmart.data.hsqldb.FoodmartHsqldb;
+import net.hydromatic.scott.data.hsqldb.ScottHsqldb;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -100,11 +101,11 @@ public class CalciteAssert {
    * MySQL manually with the foodmart data set, otherwise there will be test
    * failures.  To run against MySQL, specify '-Dcalcite.test.db=mysql' on the
    * java command line. */
-  public static final ConnectionSpec CONNECTION_SPEC =
+  public static final DatabaseInstance DB =
       Util.first(System.getProperty("calcite.test.db"), "hsqldb")
           .equals("mysql")
-          ? ConnectionSpec.MYSQL
-          : ConnectionSpec.HSQLDB;
+          ? DatabaseInstance.MYSQL
+          : DatabaseInstance.HSQLDB;
 
   /** Whether to enable slow tests. Default is false. */
   public static final boolean ENABLE_SLOW =
@@ -633,12 +634,9 @@ public class CalciteAssert {
       return rootSchema.add("foodmart",
           new ReflectiveSchema(new JdbcTest.FoodmartSchema()));
     case JDBC_FOODMART:
+      final ConnectionSpec cs = DB.foodmart;
       final DataSource dataSource =
-          JdbcSchema.dataSource(
-              CONNECTION_SPEC.url,
-              CONNECTION_SPEC.driver,
-              CONNECTION_SPEC.username,
-              CONNECTION_SPEC.password);
+          JdbcSchema.dataSource(cs.url, cs.driver, cs.username, cs.password);
       return rootSchema.add("foodmart",
           JdbcSchema.create(rootSchema, "foodmart", dataSource, null,
               "foodmart"));
@@ -1438,23 +1436,22 @@ public class CalciteAssert {
 
   /** Information necessary to create a JDBC connection. Specify one to run
    * tests against a different database. (hsqldb is the default.) */
-  public enum ConnectionSpec {
-    HSQLDB(FoodmartHsqldb.URI, "FOODMART", "FOODMART",
-        "org.hsqldb.jdbcDriver"),
-    MYSQL("jdbc:mysql://localhost/foodmart", "foodmart", "foodmart",
-        "com.mysql.jdbc.Driver");
-
-    public final String url;
-    public final String username;
-    public final String password;
-    public final String driver;
-
-    ConnectionSpec(String url, String username, String password,
-        String driver) {
-      this.url = url;
-      this.username = username;
-      this.password = password;
-      this.driver = driver;
+  public enum DatabaseInstance {
+    HSQLDB(
+        new ConnectionSpec(FoodmartHsqldb.URI, "FOODMART", "FOODMART",
+            "org.hsqldb.jdbcDriver"),
+        new ConnectionSpec(ScottHsqldb.URI, ScottHsqldb.USER,
+            ScottHsqldb.PASSWORD, "org.hsqldb.jdbcDriver")),
+    MYSQL(
+        new ConnectionSpec("jdbc:mysql://localhost/foodmart", "foodmart",
+            "foodmart", "com.mysql.jdbc.Driver"), null);
+
+    public final ConnectionSpec foodmart;
+    public final ConnectionSpec scott;
+
+    DatabaseInstance(ConnectionSpec foodmart, ConnectionSpec scott) {
+      this.foodmart = foodmart;
+      this.scott = scott;
     }
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/core/src/test/java/org/apache/calcite/test/ConnectionSpec.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/ConnectionSpec.java b/core/src/test/java/org/apache/calcite/test/ConnectionSpec.java
new file mode 100644
index 0000000..4142083
--- /dev/null
+++ b/core/src/test/java/org/apache/calcite/test/ConnectionSpec.java
@@ -0,0 +1,37 @@
+/*
+ * 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.test;
+
+/** Information necessary to create a JDBC connection.
+ *
+ * <p>Specify one to run tests against a different database. */
+public class ConnectionSpec {
+  public final String url;
+  public final String username;
+  public final String password;
+  public final String driver;
+
+  public ConnectionSpec(String url, String username, String password,
+      String driver) {
+    this.url = url;
+    this.username = username;
+    this.password = password;
+    this.driver = driver;
+  }
+}
+
+// End ConnectionSpec.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java b/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
index 08d2903..78c1249 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
@@ -32,7 +32,7 @@ public class JdbcAdapterTest {
             + "    JdbcTableScan(table=[[foodmart, sales_fact_1997]])\n"
             + "    JdbcTableScan(table=[[foodmart, sales_fact_1998]])")
         .runs()
-        .enable(CalciteAssert.CONNECTION_SPEC.url.startsWith("jdbc:hsqldb:"))
+        .enable(CalciteAssert.DB == CalciteAssert.DatabaseInstance.HSQLDB)
         .planHasSql("SELECT *\n"
             + "FROM \"foodmart\".\"sales_fact_1997\"\n"
             + "UNION ALL\n"
@@ -48,7 +48,7 @@ public class JdbcAdapterTest {
             + "  select * from \"sales_fact_1998\")\n"
             + "where \"product_id\" = 1")
         .runs()
-        .enable(CalciteAssert.CONNECTION_SPEC.url.startsWith("jdbc:hsqldb:"))
+        .enable(CalciteAssert.DB == CalciteAssert.DatabaseInstance.HSQLDB)
         .planHasSql("SELECT *\n"
             + "FROM \"foodmart\".\"sales_fact_1997\"\n"
             + "WHERE \"product_id\" = 1\n"
@@ -63,7 +63,7 @@ public class JdbcAdapterTest {
         .query("select \"store_id\", \"store_name\" from \"store\"\n"
             + "where \"store_name\" in ('Store 1', 'Store 10', 'Store 11', 'Store 15', 'Store 16', 'Store 24', 'Store 3', 'Store 7')")
         .runs()
-        .enable(CalciteAssert.CONNECTION_SPEC.url.startsWith("jdbc:hsqldb:"))
+        .enable(CalciteAssert.DB == CalciteAssert.DatabaseInstance.HSQLDB)
         .planHasSql(
             "SELECT \"store_id\", \"store_name\"\n"
             + "FROM \"foodmart\".\"store\"\n"

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index 0024bf1..989243f 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -177,10 +177,10 @@ public class JdbcTest {
   public static final String FOODMART_SCHEMA = "     {\n"
       + "       type: 'jdbc',\n"
       + "       name: 'foodmart',\n"
-      + "       jdbcDriver: '" + CalciteAssert.CONNECTION_SPEC.driver + "',\n"
-      + "       jdbcUser: '" + CalciteAssert.CONNECTION_SPEC.username + "',\n"
-      + "       jdbcPassword: '" + CalciteAssert.CONNECTION_SPEC.password + "',\n"
-      + "       jdbcUrl: '" + CalciteAssert.CONNECTION_SPEC.url + "',\n"
+      + "       jdbcDriver: '" + CalciteAssert.DB.foodmart.driver + "',\n"
+      + "       jdbcUser: '" + CalciteAssert.DB.foodmart.username + "',\n"
+      + "       jdbcPassword: '" + CalciteAssert.DB.foodmart.password + "',\n"
+      + "       jdbcUrl: '" + CalciteAssert.DB.foodmart.url + "',\n"
       + "       jdbcCatalog: null,\n"
       + "       jdbcSchema: 'foodmart'\n"
       + "     }\n";
@@ -4644,8 +4644,21 @@ public class JdbcTest {
                           + "from \"hr\".\"emps\"\n"
                           + "where \"deptno\" < ? and \"name\" like ?");
 
+                  // execute with vars unbound - gives error
+                  ResultSet resultSet;
+                  try {
+                    resultSet = preparedStatement.executeQuery();
+                    fail("expected error, got " + resultSet);
+                  } catch (SQLException e) {
+                    assertThat(e.getMessage(),
+                        equalTo(
+                            "exception while executing query: unbound parameter"));
+                  }
+
                   // execute with both vars null - no results
-                  ResultSet resultSet = preparedStatement.executeQuery();
+                  preparedStatement.setNull(1, java.sql.Types.INTEGER);
+                  preparedStatement.setNull(2, java.sql.Types.VARCHAR);
+                  resultSet = preparedStatement.executeQuery();
                   assertFalse(resultSet.next());
 
                   // execute with ?0=15, ?1='%' - 3 rows
@@ -5943,7 +5956,7 @@ public class JdbcTest {
               }
             })
         .returns("C=0\n");
-    switch (CalciteAssert.CONNECTION_SPEC) {
+    switch (CalciteAssert.DB) {
     case HSQLDB:
       assertThat(Util.toLinux(sqls[0]),
           equalTo("SELECT COUNT(*) AS \"C\"\n"

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/doc/avatica.md
----------------------------------------------------------------------
diff --git a/doc/avatica.md b/doc/avatica.md
new file mode 100644
index 0000000..32cc905
--- /dev/null
+++ b/doc/avatica.md
@@ -0,0 +1,81 @@
+# Avatica
+
+## Introduction
+
+Avatica is a framework for building JDBC and ODBC drivers for databases,
+and an RPC wire protocol.
+
+![Avatica Architecture]
+(https://raw.githubusercontent.com/julianhyde/share/master/slides/avatica-architecture.png)
+
+Avatica's Java binding has very few dependencies.
+Even though it is part of Apache Calcite it does not depend on other parts of
+Calcite. It depends only on JDK 1.7+ and Jackson.
+
+Avatica's wire protocol is JSON over HTTP.
+The Java implementation uses Jackson to convert request/response command
+objects to/from JSON.
+
+Avatica-Server is a Java implementation of Avatica RPC.
+It embeds the Jetty HTTP server.
+
+Core concepts:
+* Meta is a local API sufficient to implement any Avatica provider
+* Factory creates implementations of the JDBC classes (Driver, Connection,
+  Statement, ResultSet) on top of a Meta
+* Service is an interface that implements the functions of Meta in terms
+  of request and response command objects
+
+## JDBC
+
+Avatica implements JDBC by means of Factory.
+Factory creates implementations of the JDBC classes (Driver, Connection,
+Statement, PreparedStatement, ResultSet) on top of a Meta.
+
+## ODBC
+
+Work has not started on Avatica ODBC.
+
+Avatica ODBC would use the same wire protocol and could use the same server
+implementation in Java. The ODBC client would be written in C or C++.
+
+Since the Avatica protocol abstracts many of the differences between providers,
+the same ODBC client could be used for different databases.
+
+## Project structure
+
+We know that it is important that client libraries have minimal dependencies.
+
+Avatica is currently part of Apache Calcite.
+It does not depend upon any other part of Calcite.
+At some point Avatica could become a separate project.
+
+Packages:
+* [org.apache.calcite.avatica](http://www.hydromatic.net/calcite/apidocs/org/apache/calcite/avatica/package-summary.html) Core framework
+* [org.apache.calcite.avatica.remote](http://www.hydromatic.net/calcite/apidocs/org/apache/calcite/avatica/remote/package-summary.html) JDBC driver that uses remote procedure calls
+* [org.apache.calcite.avatica.server](http://www.hydromatic.net/calcite/apidocs/org/apache/calcite/avatica/server/package-summary.html) HTTP server
+* [org.apache.calcite.avatica.util](http://www.hydromatic.net/calcite/apidocs/org/apache/calcite/avatica/util/package-summary.html) Utilities
+
+## Status
+
+### Implemented
+* Create connection, create statement, metadata, prepare, bind, execute, fetch
+* RPC using JSON over HTTP
+* Local implementation
+* Implementation over an existing JDBC driver
+* Composite RPCs (combining several requests into one round trip)
+  * Execute-Fetch
+  * Metadata-Fetch (metadata calls such as getTables return all rows)
+
+### Not implemented
+* ODBC
+* RPCs
+  * CloseStatement
+  * CloseConnection
+* Composite RPCs
+  * CreateStatement-Prepare
+  * CloseStatement-CloseConnection
+  * Prepare-Execute-Fetch (Statement.executeQuery should fetch first N rows)
+* Remove statements from statement table
+* DML (INSERT, UPDATE, DELETE)
+* Statement.execute applied to SELECT statement

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 54fd593..683f173 100644
--- a/pom.xml
+++ b/pom.xml
@@ -131,6 +131,11 @@ limitations under the License.
         <version>11.0.2</version>
       </dependency>
       <dependency>
+        <groupId>junit</groupId>
+        <artifactId>junit</artifactId>
+        <version>4.11</version>
+      </dependency>
+      <dependency>
         <groupId>io.airlift.tpch</groupId>
         <artifactId>tpch</artifactId>
         <version>0.1</version>
@@ -159,7 +164,21 @@ limitations under the License.
         <groupId>net.hydromatic</groupId>
         <artifactId>quidem</artifactId>
         <version>0.3</version>
-        <scope>test</scope>
+      </dependency>
+      <dependency>
+        <groupId>net.hydromatic</groupId>
+        <artifactId>scott-data-hsqldb</artifactId>
+        <version>0.1</version>
+      </dependency>
+      <dependency>
+        <groupId>net.hydromatic</groupId>
+        <artifactId>tpcds</artifactId>
+        <version>0.3</version>
+      </dependency>
+      <dependency>
+        <groupId>net.sf.opencsv</groupId>
+        <artifactId>opencsv</artifactId>
+        <version>2.3</version>
       </dependency>
       <dependency>
         <groupId>org.apache.commons</groupId>
@@ -192,17 +211,6 @@ limitations under the License.
         <version>5.1.5-jhyde</version>
       </dependency>
       <dependency>
-        <groupId>net.hydromatic</groupId>
-        <artifactId>tpcds</artifactId>
-        <version>0.3</version>
-        <type>jar</type>
-      </dependency>
-      <dependency>
-        <groupId>net.sf.opencsv</groupId>
-        <artifactId>opencsv</artifactId>
-        <version>2.3</version>
-      </dependency>
-      <dependency>
         <groupId>org.apache.spark</groupId>
         <artifactId>spark-core_2.10</artifactId>
         <version>0.9.0-incubating</version>
@@ -228,11 +236,6 @@ limitations under the License.
         <version>0.7.1</version>
       </dependency>
       <dependency>
-        <groupId>junit</groupId>
-        <artifactId>junit</artifactId>
-        <version>4.11</version>
-      </dependency>
-      <dependency>
         <groupId>xerces</groupId>
         <artifactId>xercesImpl</artifactId>
         <version>2.9.1</version>