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