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 2017/04/01 20:35:54 UTC

[02/51] [partial] calcite-avatica git commit: [CALCITE-1717] Remove Calcite code and lift avatica

http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/fc7b26c8/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java
deleted file mode 100644
index d432e4a..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTable.java
+++ /dev/null
@@ -1,222 +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.adapter.jdbc;
-
-import org.apache.calcite.DataContext;
-import org.apache.calcite.adapter.java.AbstractQueryableTable;
-import org.apache.calcite.adapter.java.JavaTypeFactory;
-import org.apache.calcite.avatica.ColumnMetaData;
-import org.apache.calcite.jdbc.CalciteConnection;
-import org.apache.calcite.linq4j.Enumerable;
-import org.apache.calcite.linq4j.Enumerator;
-import org.apache.calcite.linq4j.QueryProvider;
-import org.apache.calcite.linq4j.Queryable;
-import org.apache.calcite.plan.Convention;
-import org.apache.calcite.plan.RelOptCluster;
-import org.apache.calcite.plan.RelOptTable;
-import org.apache.calcite.prepare.Prepare.CatalogReader;
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.core.TableModify;
-import org.apache.calcite.rel.core.TableModify.Operation;
-import org.apache.calcite.rel.logical.LogicalTableModify;
-import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rel.type.RelDataTypeFactory;
-import org.apache.calcite.rel.type.RelDataTypeField;
-import org.apache.calcite.rel.type.RelProtoDataType;
-import org.apache.calcite.rex.RexNode;
-import org.apache.calcite.runtime.ResultSetEnumerable;
-import org.apache.calcite.schema.ModifiableTable;
-import org.apache.calcite.schema.ScannableTable;
-import org.apache.calcite.schema.Schema;
-import org.apache.calcite.schema.SchemaPlus;
-import org.apache.calcite.schema.TranslatableTable;
-import org.apache.calcite.schema.impl.AbstractTableQueryable;
-import org.apache.calcite.sql.SqlIdentifier;
-import org.apache.calcite.sql.SqlNodeList;
-import org.apache.calcite.sql.SqlSelect;
-import org.apache.calcite.sql.parser.SqlParserPos;
-import org.apache.calcite.sql.pretty.SqlPrettyWriter;
-import org.apache.calcite.sql.util.SqlString;
-import org.apache.calcite.util.Pair;
-import org.apache.calcite.util.Util;
-
-import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Queryable that gets its data from a table within a JDBC connection.
- *
- * <p>The idea is not to read the whole table, however. The idea is to use
- * this as a building block for a query, by applying Queryable operators
- * such as
- * {@link org.apache.calcite.linq4j.Queryable#where(org.apache.calcite.linq4j.function.Predicate2)}.
- * The resulting queryable can then be converted to a SQL query, which can be
- * executed efficiently on the JDBC server.</p>
- */
-class JdbcTable extends AbstractQueryableTable
-    implements TranslatableTable, ScannableTable, ModifiableTable {
-  private RelProtoDataType protoRowType;
-  private final JdbcSchema jdbcSchema;
-  private final String jdbcCatalogName;
-  private final String jdbcSchemaName;
-  private final String jdbcTableName;
-  private final Schema.TableType jdbcTableType;
-
-  public JdbcTable(JdbcSchema jdbcSchema, String jdbcCatalogName,
-      String jdbcSchemaName, String tableName, Schema.TableType jdbcTableType) {
-    super(Object[].class);
-    this.jdbcSchema = jdbcSchema;
-    this.jdbcCatalogName = jdbcCatalogName;
-    this.jdbcSchemaName = jdbcSchemaName;
-    this.jdbcTableName = tableName;
-    this.jdbcTableType = Preconditions.checkNotNull(jdbcTableType);
-  }
-
-  public String toString() {
-    return "JdbcTable {" + jdbcTableName + "}";
-  }
-
-  @Override public Schema.TableType getJdbcTableType() {
-    return jdbcTableType;
-  }
-
-  public RelDataType getRowType(RelDataTypeFactory typeFactory) {
-    if (protoRowType == null) {
-      try {
-        protoRowType =
-            jdbcSchema.getRelDataType(
-                jdbcCatalogName,
-                jdbcSchemaName,
-                jdbcTableName);
-      } catch (SQLException e) {
-        throw new RuntimeException(
-            "Exception while reading definition of table '" + jdbcTableName
-                + "'", e);
-      }
-    }
-    return protoRowType.apply(typeFactory);
-  }
-
-  private List<Pair<ColumnMetaData.Rep, Integer>> fieldClasses(
-      final JavaTypeFactory typeFactory) {
-    final RelDataType rowType = protoRowType.apply(typeFactory);
-    return Lists.transform(rowType.getFieldList(),
-        new Function<RelDataTypeField, Pair<ColumnMetaData.Rep, Integer>>() {
-          public Pair<ColumnMetaData.Rep, Integer>
-          apply(RelDataTypeField field) {
-            final RelDataType type = field.getType();
-            final Class clazz = (Class) typeFactory.getJavaClass(type);
-            final ColumnMetaData.Rep rep =
-                Util.first(ColumnMetaData.Rep.of(clazz),
-                    ColumnMetaData.Rep.OBJECT);
-            return Pair.of(rep, type.getSqlTypeName().getJdbcOrdinal());
-          }
-        });
-  }
-
-  SqlString generateSql() {
-    final SqlNodeList selectList =
-        new SqlNodeList(
-            Collections.singletonList(SqlIdentifier.star(SqlParserPos.ZERO)),
-            SqlParserPos.ZERO);
-    SqlSelect node =
-        new SqlSelect(SqlParserPos.ZERO, SqlNodeList.EMPTY, selectList,
-            tableName(), null, null, null, null, null, null, null);
-    final SqlPrettyWriter writer = new SqlPrettyWriter(jdbcSchema.dialect);
-    node.unparse(writer, 0, 0);
-    return writer.toSqlString();
-  }
-
-  SqlIdentifier tableName() {
-    final List<String> strings = new ArrayList<>();
-    if (jdbcSchema.catalog != null) {
-      strings.add(jdbcSchema.catalog);
-    }
-    if (jdbcSchema.schema != null) {
-      strings.add(jdbcSchema.schema);
-    }
-    strings.add(jdbcTableName);
-    return new SqlIdentifier(strings, SqlParserPos.ZERO);
-  }
-
-  public RelNode toRel(RelOptTable.ToRelContext context,
-      RelOptTable relOptTable) {
-    return new JdbcTableScan(context.getCluster(), relOptTable, this,
-        jdbcSchema.convention);
-  }
-
-  public <T> Queryable<T> asQueryable(QueryProvider queryProvider,
-      SchemaPlus schema, String tableName) {
-    return new JdbcTableQueryable<>(queryProvider, schema, tableName);
-  }
-
-  public Enumerable<Object[]> scan(DataContext root) {
-    final JavaTypeFactory typeFactory = root.getTypeFactory();
-    final SqlString sql = generateSql();
-    return ResultSetEnumerable.of(jdbcSchema.getDataSource(), sql.getSql(),
-        JdbcUtils.ObjectArrayRowBuilder.factory(fieldClasses(typeFactory)));
-  }
-
-  @Override public Collection getModifiableCollection() {
-    return null;
-  }
-
-  @Override public TableModify toModificationRel(RelOptCluster cluster,
-      RelOptTable table, CatalogReader catalogReader, RelNode input,
-      Operation operation, List<String> updateColumnList,
-      List<RexNode> sourceExpressionList, boolean flattened) {
-    jdbcSchema.convention.register(cluster.getPlanner());
-
-    return new LogicalTableModify(cluster, cluster.traitSetOf(Convention.NONE),
-        table, catalogReader, input, operation, updateColumnList,
-        sourceExpressionList, flattened);
-  }
-
-  /** Enumerable that returns the contents of a {@link JdbcTable} by connecting
-   * to the JDBC data source. */
-  private class JdbcTableQueryable<T> extends AbstractTableQueryable<T> {
-    public JdbcTableQueryable(QueryProvider queryProvider, SchemaPlus schema,
-        String tableName) {
-      super(queryProvider, schema, JdbcTable.this, tableName);
-    }
-
-    @Override public String toString() {
-      return "JdbcTableQueryable {table: " + tableName + "}";
-    }
-
-    public Enumerator<T> enumerator() {
-      final JavaTypeFactory typeFactory =
-          ((CalciteConnection) queryProvider).getTypeFactory();
-      final SqlString sql = generateSql();
-      //noinspection unchecked
-      final Enumerable<T> enumerable = (Enumerable<T>) ResultSetEnumerable.of(
-          jdbcSchema.getDataSource(),
-          sql.getSql(),
-          JdbcUtils.ObjectArrayRowBuilder.factory(fieldClasses(typeFactory)));
-      return enumerable.enumerator();
-    }
-  }
-}
-
-// End JdbcTable.java

http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/fc7b26c8/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTableScan.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTableScan.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTableScan.java
deleted file mode 100644
index 7ef8938..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcTableScan.java
+++ /dev/null
@@ -1,57 +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.adapter.jdbc;
-
-import org.apache.calcite.plan.RelOptCluster;
-import org.apache.calcite.plan.RelOptTable;
-import org.apache.calcite.plan.RelTraitSet;
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.core.TableScan;
-
-import com.google.common.collect.ImmutableList;
-
-import java.util.List;
-
-/**
- * Relational expression representing a scan of a table in a JDBC data source.
- */
-public class JdbcTableScan extends TableScan implements JdbcRel {
-  final JdbcTable jdbcTable;
-
-  protected JdbcTableScan(
-      RelOptCluster cluster,
-      RelOptTable table,
-      JdbcTable jdbcTable,
-      JdbcConvention jdbcConvention) {
-    super(cluster, cluster.traitSetOf(jdbcConvention), table);
-    this.jdbcTable = jdbcTable;
-    assert jdbcTable != null;
-  }
-
-  @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
-    assert inputs.isEmpty();
-    return new JdbcTableScan(
-        getCluster(), table, jdbcTable, (JdbcConvention) getConvention());
-  }
-
-  public JdbcImplementor.Result implement(JdbcImplementor implementor) {
-    return implementor.result(jdbcTable.tableName(),
-        ImmutableList.of(JdbcImplementor.Clause.FROM), this, null);
-  }
-}
-
-// End JdbcTableScan.java

http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/fc7b26c8/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcToEnumerableConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcToEnumerableConverter.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcToEnumerableConverter.java
deleted file mode 100644
index c74e741..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcToEnumerableConverter.java
+++ /dev/null
@@ -1,325 +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.adapter.jdbc;
-
-import org.apache.calcite.adapter.enumerable.EnumerableRel;
-import org.apache.calcite.adapter.enumerable.EnumerableRelImplementor;
-import org.apache.calcite.adapter.enumerable.JavaRowFormat;
-import org.apache.calcite.adapter.enumerable.PhysType;
-import org.apache.calcite.adapter.enumerable.PhysTypeImpl;
-import org.apache.calcite.adapter.java.JavaTypeFactory;
-import org.apache.calcite.linq4j.tree.BlockBuilder;
-import org.apache.calcite.linq4j.tree.Expression;
-import org.apache.calcite.linq4j.tree.Expressions;
-import org.apache.calcite.linq4j.tree.ParameterExpression;
-import org.apache.calcite.linq4j.tree.Primitive;
-import org.apache.calcite.linq4j.tree.UnaryExpression;
-import org.apache.calcite.plan.ConventionTraitDef;
-import org.apache.calcite.plan.RelOptCluster;
-import org.apache.calcite.plan.RelOptCost;
-import org.apache.calcite.plan.RelOptPlanner;
-import org.apache.calcite.plan.RelTraitSet;
-import org.apache.calcite.prepare.CalcitePrepareImpl;
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.convert.ConverterImpl;
-import org.apache.calcite.rel.metadata.RelMetadataQuery;
-import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.runtime.Hook;
-import org.apache.calcite.runtime.SqlFunctions;
-import org.apache.calcite.schema.Schemas;
-import org.apache.calcite.sql.SqlDialect;
-import org.apache.calcite.sql.type.SqlTypeName;
-import org.apache.calcite.util.BuiltInMethod;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.List;
-import java.util.TimeZone;
-
-/**
- * Relational expression representing a scan of a table in a JDBC data source.
- */
-public class JdbcToEnumerableConverter
-    extends ConverterImpl
-    implements EnumerableRel {
-  protected JdbcToEnumerableConverter(
-      RelOptCluster cluster,
-      RelTraitSet traits,
-      RelNode input) {
-    super(cluster, ConventionTraitDef.INSTANCE, traits, input);
-  }
-
-  @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
-    return new JdbcToEnumerableConverter(
-        getCluster(), traitSet, sole(inputs));
-  }
-
-  @Override public RelOptCost computeSelfCost(RelOptPlanner planner,
-      RelMetadataQuery mq) {
-    return super.computeSelfCost(planner, mq).multiplyBy(.1);
-  }
-
-  public Result implement(EnumerableRelImplementor implementor, Prefer pref) {
-    // Generate:
-    //   ResultSetEnumerable.of(schema.getDataSource(), "select ...")
-    final BlockBuilder builder0 = new BlockBuilder(false);
-    final JdbcRel child = (JdbcRel) getInput();
-    final PhysType physType =
-        PhysTypeImpl.of(
-            implementor.getTypeFactory(), getRowType(),
-            pref.prefer(JavaRowFormat.CUSTOM));
-    final JdbcConvention jdbcConvention =
-        (JdbcConvention) child.getConvention();
-    String sql = generateSql(jdbcConvention.dialect);
-    if (CalcitePrepareImpl.DEBUG) {
-      System.out.println("[" + sql + "]");
-    }
-    Hook.QUERY_PLAN.run(sql);
-    final Expression sql_ =
-        builder0.append("sql", Expressions.constant(sql));
-    final int fieldCount = getRowType().getFieldCount();
-    BlockBuilder builder = new BlockBuilder();
-    final ParameterExpression resultSet_ =
-        Expressions.parameter(Modifier.FINAL, ResultSet.class,
-            builder.newName("resultSet"));
-    CalendarPolicy calendarPolicy = CalendarPolicy.of(jdbcConvention.dialect);
-    final Expression calendar_;
-    switch (calendarPolicy) {
-    case LOCAL:
-      calendar_ =
-          builder0.append("calendar",
-              Expressions.call(Calendar.class, "getInstance",
-                  getTimeZoneExpression(implementor)));
-      break;
-    default:
-      calendar_ = null;
-    }
-    if (fieldCount == 1) {
-      final ParameterExpression value_ =
-          Expressions.parameter(Object.class, builder.newName("value"));
-      builder.add(Expressions.declare(Modifier.FINAL, value_, null));
-      generateGet(implementor, physType, builder, resultSet_, 0, value_,
-          calendar_, calendarPolicy);
-      builder.add(Expressions.return_(null, value_));
-    } else {
-      final Expression values_ =
-          builder.append("values",
-              Expressions.newArrayBounds(Object.class, 1,
-                  Expressions.constant(fieldCount)));
-      for (int i = 0; i < fieldCount; i++) {
-        generateGet(implementor, physType, builder, resultSet_, i,
-            Expressions.arrayIndex(values_, Expressions.constant(i)),
-            calendar_, calendarPolicy);
-      }
-      builder.add(
-          Expressions.return_(null, values_));
-    }
-    final ParameterExpression e_ =
-        Expressions.parameter(SQLException.class, builder.newName("e"));
-    final Expression rowBuilderFactory_ =
-        builder0.append("rowBuilderFactory",
-            Expressions.lambda(
-                Expressions.block(
-                    Expressions.return_(null,
-                        Expressions.lambda(
-                            Expressions.block(
-                                Expressions.tryCatch(
-                                    builder.toBlock(),
-                                    Expressions.catch_(
-                                        e_,
-                                        Expressions.throw_(
-                                            Expressions.new_(
-                                                RuntimeException.class,
-                                                e_)))))))),
-                resultSet_));
-    final Expression enumerable =
-        builder0.append(
-            "enumerable",
-            Expressions.call(
-                BuiltInMethod.RESULT_SET_ENUMERABLE_OF.method,
-                Expressions.call(
-                    Schemas.unwrap(jdbcConvention.expression,
-                        JdbcSchema.class),
-                    BuiltInMethod.JDBC_SCHEMA_DATA_SOURCE.method),
-                sql_,
-                rowBuilderFactory_));
-    builder0.add(
-        Expressions.return_(null, enumerable));
-    return implementor.result(physType, builder0.toBlock());
-  }
-
-  private UnaryExpression getTimeZoneExpression(
-      EnumerableRelImplementor implementor) {
-    return Expressions.convert_(
-        Expressions.call(
-            implementor.getRootExpression(),
-            "get",
-            Expressions.constant("timeZone")),
-        TimeZone.class);
-  }
-
-  private void generateGet(EnumerableRelImplementor implementor,
-      PhysType physType, BlockBuilder builder, ParameterExpression resultSet_,
-      int i, Expression target, Expression calendar_,
-      CalendarPolicy calendarPolicy) {
-    final Primitive primitive = Primitive.ofBoxOr(physType.fieldClass(i));
-    final RelDataType fieldType =
-        physType.getRowType().getFieldList().get(i).getType();
-    final List<Expression> dateTimeArgs = new ArrayList<Expression>();
-    dateTimeArgs.add(Expressions.constant(i + 1));
-    SqlTypeName sqlTypeName = fieldType.getSqlTypeName();
-    boolean offset = false;
-    switch (calendarPolicy) {
-    case LOCAL:
-      dateTimeArgs.add(calendar_);
-      break;
-    case NULL:
-      // We don't specify a calendar at all, so we don't add an argument and
-      // instead use the version of the getXXX that doesn't take a Calendar
-      break;
-    case DIRECT:
-      sqlTypeName = SqlTypeName.ANY;
-      break;
-    case SHIFT:
-      switch (sqlTypeName) {
-      case TIMESTAMP:
-      case DATE:
-        offset = true;
-      }
-      break;
-    }
-    final Expression source;
-    switch (sqlTypeName) {
-    case DATE:
-    case TIME:
-    case TIMESTAMP:
-      source = Expressions.call(
-          getMethod(sqlTypeName, fieldType.isNullable(), offset),
-          Expressions.<Expression>list()
-              .append(
-                  Expressions.call(resultSet_,
-                      getMethod2(sqlTypeName), dateTimeArgs))
-          .appendIf(offset, getTimeZoneExpression(implementor)));
-      break;
-    case ARRAY:
-      final Expression x = Expressions.convert_(
-          Expressions.call(resultSet_, jdbcGetMethod(primitive),
-              Expressions.constant(i + 1)),
-          java.sql.Array.class);
-      source = Expressions.call(BuiltInMethod.JDBC_ARRAY_TO_LIST.method, x);
-      break;
-    default:
-      source = Expressions.call(
-          resultSet_, jdbcGetMethod(primitive), Expressions.constant(i + 1));
-    }
-    builder.add(
-        Expressions.statement(
-            Expressions.assign(
-                target, source)));
-
-    // [CALCITE-596] If primitive type columns contain null value, returns null
-    // object
-    if (primitive != null) {
-      builder.add(
-          Expressions.ifThen(
-              Expressions.call(resultSet_, "wasNull"),
-              Expressions.statement(
-                  Expressions.assign(target,
-                      Expressions.constant(null)))));
-    }
-  }
-
-  private Method getMethod(SqlTypeName sqlTypeName, boolean nullable,
-      boolean offset) {
-    switch (sqlTypeName) {
-    case DATE:
-      return (nullable
-          ? BuiltInMethod.DATE_TO_INT_OPTIONAL
-          : BuiltInMethod.DATE_TO_INT).method;
-    case TIME:
-      return (nullable
-          ? BuiltInMethod.TIME_TO_INT_OPTIONAL
-          : BuiltInMethod.TIME_TO_INT).method;
-    case TIMESTAMP:
-      return (nullable
-          ? (offset
-          ? BuiltInMethod.TIMESTAMP_TO_LONG_OPTIONAL_OFFSET
-          : BuiltInMethod.TIMESTAMP_TO_LONG_OPTIONAL)
-          : (offset
-              ? BuiltInMethod.TIMESTAMP_TO_LONG_OFFSET
-              : BuiltInMethod.TIMESTAMP_TO_LONG)).method;
-    default:
-      throw new AssertionError(sqlTypeName + ":" + nullable);
-    }
-  }
-
-  private Method getMethod2(SqlTypeName sqlTypeName) {
-    switch (sqlTypeName) {
-    case DATE:
-      return BuiltInMethod.RESULT_SET_GET_DATE2.method;
-    case TIME:
-      return BuiltInMethod.RESULT_SET_GET_TIME2.method;
-    case TIMESTAMP:
-      return BuiltInMethod.RESULT_SET_GET_TIMESTAMP2.method;
-    default:
-      throw new AssertionError(sqlTypeName);
-    }
-  }
-
-  /** E,g, {@code jdbcGetMethod(int)} returns "getInt". */
-  private String jdbcGetMethod(Primitive primitive) {
-    return primitive == null
-        ? "getObject"
-        : "get" + SqlFunctions.initcap(primitive.primitiveName);
-  }
-
-  private String generateSql(SqlDialect dialect) {
-    final JdbcImplementor jdbcImplementor =
-        new JdbcImplementor(dialect,
-            (JavaTypeFactory) getCluster().getTypeFactory());
-    final JdbcImplementor.Result result =
-        jdbcImplementor.visitChild(0, getInput());
-    return result.asStatement().toSqlString(dialect).getSql();
-  }
-
-  /** Whether this JDBC driver needs you to pass a Calendar object to methods
-   * such as {@link ResultSet#getTimestamp(int, java.util.Calendar)}. */
-  private enum CalendarPolicy {
-    NONE,
-    NULL,
-    LOCAL,
-    DIRECT,
-    SHIFT;
-
-    static CalendarPolicy of(SqlDialect dialect) {
-      switch (dialect.getDatabaseProduct()) {
-      case MYSQL:
-        return SHIFT;
-      case HSQLDB:
-      default:
-        // NULL works for hsqldb-2.3; nothing worked for hsqldb-1.8.
-        return NULL;
-      }
-    }
-  }
-}
-
-// End JdbcToEnumerableConverter.java

http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/fc7b26c8/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcToEnumerableConverterRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcToEnumerableConverterRule.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcToEnumerableConverterRule.java
deleted file mode 100644
index a212c3b..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcToEnumerableConverterRule.java
+++ /dev/null
@@ -1,41 +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.adapter.jdbc;
-
-import org.apache.calcite.adapter.enumerable.EnumerableConvention;
-import org.apache.calcite.plan.RelTraitSet;
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.convert.ConverterRule;
-
-/**
- * Rule to convert a relational expression from
- * {@link JdbcConvention} to
- * {@link EnumerableConvention}.
- */
-public class JdbcToEnumerableConverterRule extends ConverterRule {
-  JdbcToEnumerableConverterRule(JdbcConvention out) {
-    super(RelNode.class, out, EnumerableConvention.INSTANCE,
-        "JdbcToEnumerableConverterRule:" + out);
-  }
-
-  @Override public RelNode convert(RelNode rel) {
-    RelTraitSet newTraitSet = rel.getTraitSet().replace(getOutTrait());
-    return new JdbcToEnumerableConverter(rel.getCluster(), newTraitSet, rel);
-  }
-}
-
-// End JdbcToEnumerableConverterRule.java

http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/fc7b26c8/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcUtils.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcUtils.java
deleted file mode 100644
index bb8e558..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcUtils.java
+++ /dev/null
@@ -1,229 +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.adapter.jdbc;
-
-import org.apache.calcite.avatica.ColumnMetaData;
-import org.apache.calcite.avatica.util.DateTimeUtils;
-import org.apache.calcite.linq4j.function.Function0;
-import org.apache.calcite.linq4j.function.Function1;
-import org.apache.calcite.sql.SqlDialect;
-import org.apache.calcite.util.ImmutableNullableList;
-import org.apache.calcite.util.Pair;
-
-import org.apache.commons.dbcp.BasicDataSource;
-
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import com.google.common.collect.ImmutableList;
-import com.google.common.primitives.Ints;
-
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.Date;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Time;
-import java.sql.Timestamp;
-import java.sql.Types;
-import java.util.HashMap;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TimeZone;
-import javax.annotation.Nonnull;
-import javax.sql.DataSource;
-
-/**
- * Utilities for the JDBC provider.
- */
-final class JdbcUtils {
-  private JdbcUtils() {
-    throw new AssertionError("no instances!");
-  }
-
-  /** Pool of dialects. */
-  static class DialectPool {
-    final Map<DataSource, SqlDialect> map0 = new IdentityHashMap<>();
-    final Map<List, SqlDialect> map = new HashMap<>();
-
-    public static final DialectPool INSTANCE = new DialectPool();
-
-    SqlDialect get(DataSource dataSource) {
-      final SqlDialect sqlDialect = map0.get(dataSource);
-      if (sqlDialect != null) {
-        return sqlDialect;
-      }
-      Connection connection = null;
-      try {
-        connection = dataSource.getConnection();
-        DatabaseMetaData metaData = connection.getMetaData();
-        String productName = metaData.getDatabaseProductName();
-        String productVersion = metaData.getDatabaseProductVersion();
-        List key = ImmutableList.of(productName, productVersion);
-        SqlDialect dialect = map.get(key);
-        if (dialect == null) {
-          dialect = SqlDialect.create(metaData);
-          map.put(key, dialect);
-          map0.put(dataSource, dialect);
-        }
-        connection.close();
-        connection = null;
-        return dialect;
-      } catch (SQLException e) {
-        throw new RuntimeException(e);
-      } finally {
-        if (connection != null) {
-          try {
-            connection.close();
-          } catch (SQLException e) {
-            // ignore
-          }
-        }
-      }
-    }
-  }
-
-  /** Builder that calls {@link ResultSet#getObject(int)} for every column,
-   * or {@code getXxx} if the result type is a primitive {@code xxx},
-   * and returns an array of objects for each row. */
-  static class ObjectArrayRowBuilder implements Function0<Object[]> {
-    private final ResultSet resultSet;
-    private final int columnCount;
-    private final ColumnMetaData.Rep[] reps;
-    private final int[] types;
-
-    ObjectArrayRowBuilder(ResultSet resultSet, ColumnMetaData.Rep[] reps,
-        int[] types)
-        throws SQLException {
-      this.resultSet = resultSet;
-      this.reps = reps;
-      this.types = types;
-      this.columnCount = resultSet.getMetaData().getColumnCount();
-    }
-
-    public static Function1<ResultSet, Function0<Object[]>> factory(
-        final List<Pair<ColumnMetaData.Rep, Integer>> list) {
-      return new Function1<ResultSet, Function0<Object[]>>() {
-        public Function0<Object[]> apply(ResultSet resultSet) {
-          try {
-            return new ObjectArrayRowBuilder(
-                resultSet,
-                Pair.left(list).toArray(new ColumnMetaData.Rep[list.size()]),
-                Ints.toArray(Pair.right(list)));
-          } catch (SQLException e) {
-            throw new RuntimeException(e);
-          }
-        }
-      };
-    }
-
-    public Object[] apply() {
-      try {
-        final Object[] values = new Object[columnCount];
-        for (int i = 0; i < columnCount; i++) {
-          values[i] = value(i);
-        }
-        return values;
-      } catch (SQLException e) {
-        throw new RuntimeException(e);
-      }
-    }
-
-    /**
-     * Gets a value from a given column in a JDBC result set.
-     *
-     * @param i Ordinal of column (1-based, per JDBC)
-     */
-    private Object value(int i) throws SQLException {
-      // MySQL returns timestamps shifted into local time. Using
-      // getTimestamp(int, Calendar) with a UTC calendar should prevent this,
-      // but does not. So we shift explicitly.
-      switch (types[i]) {
-      case Types.TIMESTAMP:
-        return shift(resultSet.getTimestamp(i + 1));
-      case Types.TIME:
-        return shift(resultSet.getTime(i + 1));
-      case Types.DATE:
-        return shift(resultSet.getDate(i + 1));
-      }
-      return reps[i].jdbcGet(resultSet, i + 1);
-    }
-
-    private static Timestamp shift(Timestamp v) {
-      if (v == null) {
-        return null;
-      }
-      long time = v.getTime();
-      int offset = TimeZone.getDefault().getOffset(time);
-      return new Timestamp(time + offset);
-    }
-
-    private static Time shift(Time v) {
-      if (v == null) {
-        return null;
-      }
-      long time = v.getTime();
-      int offset = TimeZone.getDefault().getOffset(time);
-      return new Time((time + offset) % DateTimeUtils.MILLIS_PER_DAY);
-    }
-
-    private static Date shift(Date v) {
-      if (v == null) {
-        return null;
-      }
-      long time = v.getTime();
-      int offset = TimeZone.getDefault().getOffset(time);
-      return new Date(time + offset);
-    }
-  }
-
-  /** Ensures that if two data sources have the same definition, they will use
-   * the same object.
-   *
-   * <p>This in turn makes it easier to cache
-   * {@link org.apache.calcite.sql.SqlDialect} objects. Otherwise, each time we
-   * see a new data source, we have to open a connection to find out what
-   * database product and version it is. */
-  static class DataSourcePool {
-    public static final DataSourcePool INSTANCE = new DataSourcePool();
-
-    private final LoadingCache<List<String>, BasicDataSource> cache =
-        CacheBuilder.newBuilder().softValues().build(
-            new CacheLoader<List<String>, BasicDataSource>() {
-              @Override public BasicDataSource load(@Nonnull List<String> key) {
-                BasicDataSource dataSource = new BasicDataSource();
-                dataSource.setUrl(key.get(0));
-                dataSource.setUsername(key.get(1));
-                dataSource.setPassword(key.get(2));
-                dataSource.setDriverClassName(key.get(3));
-                return dataSource;
-              }
-            });
-
-    public DataSource get(String url, String driverClassName,
-        String username, String password) {
-      // Get data source objects from a cache, so that we don't have to sniff
-      // out what kind of database they are quite as often.
-      final List<String> key =
-          ImmutableNullableList.of(url, username, password, driverClassName);
-      return cache.getUnchecked(key);
-    }
-  }
-}
-
-// End JdbcUtils.java

http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/fc7b26c8/core/src/main/java/org/apache/calcite/adapter/jdbc/package-info.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/package-info.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/package-info.java
deleted file mode 100644
index ba2bdd8..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/jdbc/package-info.java
+++ /dev/null
@@ -1,26 +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.
- */
-
-/**
- * Query provider based on a JDBC data source.
- */
-@PackageMarker
-package org.apache.calcite.adapter.jdbc;
-
-import org.apache.calcite.avatica.util.PackageMarker;
-
-// End package-info.java

http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/fc7b26c8/core/src/main/java/org/apache/calcite/adapter/package-info.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/package-info.java b/core/src/main/java/org/apache/calcite/adapter/package-info.java
deleted file mode 100644
index 29084db..0000000
--- a/core/src/main/java/org/apache/calcite/adapter/package-info.java
+++ /dev/null
@@ -1,45 +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.
- */
-
-/**
- * Calcite adapters.
- *
- * <p>An adapter allows Calcite to access data in a particular data source as
- * if it were a collection of tables in a schema. Each adapter typically
- * contains an implementation of {@link org.apache.calcite.schema.SchemaFactory}
- * and some classes that implement other schema SPIs.
- *
- * <p>To use an adapter, include a custom schema in a JSON model file:
- *
- * <blockquote><pre>
- *    schemas: [
- *      {
- *        type: 'custom',
- *        name: 'My Custom Schema',
- *        factory: 'com.acme.MySchemaFactory',
- *        operand: {a: 'foo', b: [1, 3.5] }
- *      }
- *   ]
- * </pre>
- * </blockquote>
- */
-@PackageMarker
-package org.apache.calcite.adapter;
-
-import org.apache.calcite.avatica.util.PackageMarker;
-
-// End package-info.java

http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/fc7b26c8/core/src/main/java/org/apache/calcite/avatica/AvaticaClientRuntimeException.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/avatica/AvaticaClientRuntimeException.java b/core/src/main/java/org/apache/calcite/avatica/AvaticaClientRuntimeException.java
new file mode 100644
index 0000000..df03b03
--- /dev/null
+++ b/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-avatica/blob/fc7b26c8/core/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java b/core/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
new file mode 100644
index 0000000..51649c1
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
@@ -0,0 +1,769 @@
+/*
+ * 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.ExecuteBatchResult;
+import org.apache.calcite.avatica.Meta.MetaResultSet;
+import org.apache.calcite.avatica.remote.KerberosConnection;
+import org.apache.calcite.avatica.remote.Service;
+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;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * 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;
+  private KerberosConnection kerberosConnection;
+  private Service service;
+
+  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 AvaticaSpecificDatabaseMetaData metaData;
+  public final Helper helper = Helper.INSTANCE;
+  public final Map<InternalProperty, Object> properties = new HashMap<>();
+  public final Map<Integer, AvaticaStatement> statementMap =
+      new ConcurrentHashMap<>();
+  final Map<Integer, AtomicBoolean> flagMap = 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);
+    try {
+      this.holdability = metaData.getResultSetHoldability();
+    } catch (SQLException e) {
+      // We know the impl doesn't throw this.
+      throw new RuntimeException(e);
+    }
+    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);
+        if (null != kerberosConnection) {
+          kerberosConnection.stopRenewalThread();
+        }
+      } 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;
+  }
+
+  /** Executes a batch update using an {@link AvaticaPreparedStatement}.
+   *
+   * @param pstmt The prepared statement.
+   * @return An array of update counts containing one element for each command in the batch.
+   */
+  protected long[] executeBatchUpdateInternal(AvaticaPreparedStatement pstmt) throws SQLException {
+    try {
+      // Get the handle from the statement
+      Meta.StatementHandle handle = pstmt.handle;
+      // Execute it against meta
+      return meta.executeBatch(handle, pstmt.getParameterValueBatch()).updateCounts;
+    } catch (Exception e) {
+      throw helper.createException(e.getMessage(), e);
+    }
+  }
+
+  /** 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);
+            }
+          }
+        };
+    // The old semantics were that maxRowCount was also treated as the maximum number of
+    // elements in the first Frame of results. A value of -1 would also preserve this, but an
+    // explicit (positive) number is easier to follow, IMO.
+    return meta.prepareAndExecute(statement.handle, sql, maxRowCount,
+        AvaticaUtils.toSaturatedInt(maxRowCount), callback);
+  }
+
+  protected ExecuteBatchResult prepareAndUpdateBatch(final AvaticaStatement statement,
+      final List<String> queries) throws NoSuchStatementException, SQLException {
+    return meta.prepareAndExecuteBatch(statement.handle, queries);
+  }
+
+  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());
+  }
+
+  /** Returns or creates a slot whose state can be changed to cancel a
+   * statement. Statements will receive the same slot if and only if their id
+   * is the same. */
+  public AtomicBoolean getCancelFlag(Meta.StatementHandle h)
+      throws NoSuchStatementException {
+    AvaticaUtils.upgrade("after dropping JDK 1.7, use Map.computeIfAbsent");
+    synchronized (flagMap) {
+      AtomicBoolean b = flagMap.get(h.id);
+      if (b == null) {
+        b = new AtomicBoolean();
+        flagMap.put(h.id, b);
+      }
+      return b;
+    }
+  }
+
+  /** 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();
+    }
+  }
+
+  public void setKerberosConnection(KerberosConnection kerberosConnection) {
+    this.kerberosConnection = Objects.requireNonNull(kerberosConnection);
+  }
+
+  public KerberosConnection getKerberosConnection() {
+    return this.kerberosConnection;
+  }
+
+  public Service getService() {
+    assert null != service;
+    return service;
+  }
+
+  public void setService(Service service) {
+    this.service = Objects.requireNonNull(service);
+  }
+}
+
+// End AvaticaConnection.java