http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/InternalProperty.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/InternalProperty.java b/avatica/core/src/main/java/org/apache/calcite/avatica/InternalProperty.java new file mode 100644 index 0000000..a5a3852 --- /dev/null +++ b/avatica/core/src/main/java/org/apache/calcite/avatica/InternalProperty.java @@ -0,0 +1,109 @@ +/* + * 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.ConnectionProperty.Type; +import org.apache.calcite.avatica.util.Casing; +import org.apache.calcite.avatica.util.Quoting; + +import java.util.Map; + +/** + * Definitions of properties that drive the behavior of + * {@link org.apache.calcite.avatica.AvaticaDatabaseMetaData}. + */ +public enum InternalProperty { + /** Whether identifiers are matched case-sensitively. */ + CASE_SENSITIVE(Type.BOOLEAN, true), + + /** Character that quotes identifiers. */ + SQL_KEYWORDS(Type.STRING, null), + + /** How identifiers are quoted. */ + QUOTING(Quoting.class, Quoting.DOUBLE_QUOTE), + + /** How identifiers are stored if they are quoted. */ + QUOTED_CASING(Casing.class, Casing.UNCHANGED), + + /** How identifiers are stored if they are not quoted. */ + UNQUOTED_CASING(Casing.class, Casing.TO_UPPER), + + /** How identifiers are stored if they are not quoted. */ + NULL_SORTING(NullSorting.class, NullSorting.END); + + private final Type type; + private final Class enumClass; + private final Object defaultValue; + + /** Creates an InternalProperty based on an enum. */ + <E extends Enum> InternalProperty(Class<E> enumClass, E defaultValue) { + this(Type.ENUM, enumClass, defaultValue); + } + + /** Creates an InternalProperty based on a non-enum type. */ + InternalProperty(Type type, Object defaultValue) { + this(type, null, defaultValue); + } + + private InternalProperty(Type type, Class enumClass, Object defaultValue) { + this.type = type; + this.enumClass = enumClass; + this.defaultValue = defaultValue; + } + + private <T> T get_(Map<InternalProperty, Object> map, T defaultValue) { + final Object s = map.get(this); + if (s != null) { + return (T) s; + } + if (defaultValue != null) { + return (T) defaultValue; + } + throw new RuntimeException("Required property '" + name() + + "' not specified"); + } + + /** Returns the string value of this property, or null if not specified and + * no default. */ + public String getString(Map<InternalProperty, Object> map) { + assert type == Type.STRING; + return get_(map, (String) defaultValue); + } + + /** Returns the boolean value of this property. Throws if not set and no + * default. */ + public boolean getBoolean(Map<InternalProperty, Object> map) { + assert type == Type.BOOLEAN; + return get_(map, (Boolean) defaultValue); + } + + /** Returns the enum value of this property. Throws if not set and no + * default. */ + public <E extends Enum> E getEnum(Map<InternalProperty, Object> map, + Class<E> enumClass) { + assert type == Type.ENUM; + //noinspection unchecked + return get_(map, (E) defaultValue); + } + + /** Where nulls appear in a sorted relation. */ + enum NullSorting { + START, END, LOW, HIGH, + } +} + +// End InternalProperty.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/Meta.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/Meta.java b/avatica/core/src/main/java/org/apache/calcite/avatica/Meta.java new file mode 100644 index 0000000..4cc460c --- /dev/null +++ b/avatica/core/src/main/java/org/apache/calcite/avatica/Meta.java @@ -0,0 +1,1272 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.avatica; + +import org.apache.calcite.avatica.proto.Common; +import org.apache.calcite.avatica.remote.TypedValue; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.google.protobuf.ByteString; +import com.google.protobuf.Descriptors.FieldDescriptor; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; + +/** + * Command handler for getting various metadata. Should be implemented by each + * driver. + * + * <p>Also holds other abstract methods that are not related to metadata + * that each provider must implement. This is not ideal.</p> + */ +public interface Meta { + + /** + * Returns a map of static database properties. + * + * <p>The provider can omit properties whose value is the same as the + * default. + */ + Map<DatabaseProperty, Object> getDatabaseProperties(ConnectionHandle ch); + + /** Per {@link DatabaseMetaData#getTables(String, String, String, String[])}. */ + MetaResultSet getTables(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat tableNamePattern, + List<String> typeList); + + /** Per {@link DatabaseMetaData#getColumns(String, String, String, String)}. */ + MetaResultSet getColumns(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat tableNamePattern, + Pat columnNamePattern); + + MetaResultSet getSchemas(ConnectionHandle ch, String catalog, Pat schemaPattern); + + /** Per {@link DatabaseMetaData#getCatalogs()}. */ + MetaResultSet getCatalogs(ConnectionHandle ch); + + /** Per {@link DatabaseMetaData#getTableTypes()}. */ + MetaResultSet getTableTypes(ConnectionHandle ch); + + /** Per {@link DatabaseMetaData#getProcedures(String, String, String)}. */ + MetaResultSet getProcedures(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat procedureNamePattern); + + /** Per {@link DatabaseMetaData#getProcedureColumns(String, String, String, String)}. */ + MetaResultSet getProcedureColumns(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat procedureNamePattern, + Pat columnNamePattern); + + /** Per {@link DatabaseMetaData#getColumnPrivileges(String, String, String, String)}. */ + MetaResultSet getColumnPrivileges(ConnectionHandle ch, + String catalog, + String schema, + String table, + Pat columnNamePattern); + + /** Per {@link DatabaseMetaData#getTablePrivileges(String, String, String)}. */ + MetaResultSet getTablePrivileges(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat tableNamePattern); + + /** Per + * {@link DatabaseMetaData#getBestRowIdentifier(String, String, String, int, boolean)}. */ + MetaResultSet getBestRowIdentifier(ConnectionHandle ch, + String catalog, + String schema, + String table, + int scope, + boolean nullable); + + /** Per {@link DatabaseMetaData#getVersionColumns(String, String, String)}. */ + MetaResultSet getVersionColumns(ConnectionHandle ch, String catalog, String schema, String table); + + /** Per {@link DatabaseMetaData#getPrimaryKeys(String, String, String)}. */ + MetaResultSet getPrimaryKeys(ConnectionHandle ch, String catalog, String schema, String table); + + /** Per {@link DatabaseMetaData#getImportedKeys(String, String, String)}. */ + MetaResultSet getImportedKeys(ConnectionHandle ch, String catalog, String schema, String table); + + /** Per {@link DatabaseMetaData#getExportedKeys(String, String, String)}. */ + MetaResultSet getExportedKeys(ConnectionHandle ch, String catalog, String schema, String table); + + /** Per + * {@link DatabaseMetaData#getCrossReference(String, String, String, String, String, String)}. */ + MetaResultSet getCrossReference(ConnectionHandle ch, + String parentCatalog, + String parentSchema, + String parentTable, + String foreignCatalog, + String foreignSchema, + String foreignTable); + + /** Per {@link DatabaseMetaData#getTypeInfo()}. */ + MetaResultSet getTypeInfo(ConnectionHandle ch); + + /** Per {@link DatabaseMetaData#getIndexInfo(String, String, String, boolean, boolean)}. */ + MetaResultSet getIndexInfo(ConnectionHandle ch, String catalog, + String schema, + String table, + boolean unique, + boolean approximate); + + /** Per {@link DatabaseMetaData#getUDTs(String, String, String, int[])}. */ + MetaResultSet getUDTs(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat typeNamePattern, + int[] types); + + /** Per {@link DatabaseMetaData#getSuperTypes(String, String, String)}. */ + MetaResultSet getSuperTypes(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat typeNamePattern); + + /** Per {@link DatabaseMetaData#getSuperTables(String, String, String)}. */ + MetaResultSet getSuperTables(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat tableNamePattern); + + /** Per {@link DatabaseMetaData#getAttributes(String, String, String, String)}. */ + MetaResultSet getAttributes(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat typeNamePattern, + Pat attributeNamePattern); + + /** Per {@link DatabaseMetaData#getClientInfoProperties()}. */ + MetaResultSet getClientInfoProperties(ConnectionHandle ch); + + /** Per {@link DatabaseMetaData#getFunctions(String, String, String)}. */ + MetaResultSet getFunctions(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat functionNamePattern); + + /** Per {@link DatabaseMetaData#getFunctionColumns(String, String, String, String)}. */ + MetaResultSet getFunctionColumns(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat functionNamePattern, + Pat columnNamePattern); + + /** Per {@link DatabaseMetaData#getPseudoColumns(String, String, String, String)}. */ + MetaResultSet getPseudoColumns(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat tableNamePattern, + Pat columnNamePattern); + + /** Creates an iterable for a result set. + * + * <p>The default implementation just returns {@code iterable}, which it + * requires to be not null; derived classes may instead choose to execute the + * relational expression in {@code signature}. */ + Iterable<Object> createIterable(StatementHandle stmt, QueryState state, Signature signature, + List<TypedValue> parameterValues, Frame firstFrame); + + /** Prepares a statement. + * + * @param ch Connection handle + * @param sql SQL query + * @param maxRowCount Negative for no limit (different meaning than JDBC) + * @return Signature of prepared statement + */ + StatementHandle prepare(ConnectionHandle ch, String sql, long maxRowCount); + + /** Prepares and executes a statement. + * + * @param h Statement handle + * @param sql SQL query + * @param maxRowCount Negative for no limit (different meaning than JDBC) + * @param callback Callback to lock, clear and assign cursor + * + * @return Result containing statement ID, and if a query, a result set and + * first frame of data + */ + ExecuteResult prepareAndExecute(StatementHandle h, String sql, + long maxRowCount, PrepareCallback callback) throws NoSuchStatementException; + + /** Returns a frame of rows. + * + * <p>The frame describes whether there may be another frame. If there is not + * another frame, the current iteration is done when we have finished the + * rows in the this frame. + * + * <p>The default implementation always returns null. + * + * @param h Statement handle + * @param offset Zero-based offset of first row in the requested frame + * @param fetchMaxRowCount Maximum number of rows to return; negative means + * no limit + * @return Frame, or null if there are no more + */ + Frame fetch(StatementHandle h, long offset, int fetchMaxRowCount) throws + NoSuchStatementException, MissingResultsException; + + /** Executes a prepared statement. + * + * @param h Statement handle + * @param parameterValues A list of parameter values; may be empty, not null + * @param maxRowCount Maximum number of rows to return; negative means + * no limit + * @return Execute result + */ + ExecuteResult execute(StatementHandle h, List<TypedValue> parameterValues, + long maxRowCount) throws NoSuchStatementException; + + /** Called during the creation of a statement to allocate a new handle. + * + * @param ch Connection handle + */ + StatementHandle createStatement(ConnectionHandle ch); + + /** Closes a statement. */ + void closeStatement(StatementHandle h); + + /** + * Opens (creates) a connection. The client allocates its own connection ID which the server is + * then made aware of through the {@link ConnectionHandle}. The Map {@code info} argument is + * analogous to the {@link Properties} typically passed to a "normal" JDBC Driver. Avatica + * specific properties should not be included -- only properties for the underlying driver. + * + * @param ch A ConnectionHandle encapsulates information about the connection to be opened + * as provided by the client. + * @param info A Map corresponding to the Properties typically passed to a JDBC Driver. + */ + void openConnection(ConnectionHandle ch, Map<String, String> info); + + /** Closes a connection */ + void closeConnection(ConnectionHandle ch); + + /** + * Re-set the {@link ResultSet} on a Statement. Not a JDBC method. + * @return True if there are results to fetch after resetting to the given offset. False otherwise + */ + boolean syncResults(StatementHandle sh, QueryState state, long offset) + throws NoSuchStatementException; + + /** + * Makes all changes since the last commit/rollback permanent. Analogy to + * {@link Connection#commit()}. + * + * @param ch A reference to the real JDBC Connection. + */ + void commit(ConnectionHandle ch); + + /** + * Undoes all changes since the last commit/rollback. Analogy to + * {@link Connection#rollback()}; + * + * @param ch A reference to the real JDBC Connection. + */ + void rollback(ConnectionHandle ch); + + /** Sync client and server view of connection properties. + * + * <p>Note: this interface is considered "experimental" and may undergo further changes as this + * functionality is extended to other aspects of state management for + * {@link java.sql.Connection}, {@link java.sql.Statement}, and {@link java.sql.ResultSet}.</p> + */ + ConnectionProperties connectionSync(ConnectionHandle ch, ConnectionProperties connProps); + + /** Factory to create instances of {@link Meta}. */ + interface Factory { + Meta create(List<String> args); + } + + /** Wrapper to remind API calls that a parameter is a pattern (allows '%' and + * '_' wildcards, per the JDBC spec) rather than a string to be matched + * exactly. */ + class Pat { + public final String s; + + private Pat(String s) { + this.s = s; + } + + @Override public String toString() { + return "Pat[" + s + "]"; + } + + @JsonCreator + public static Pat of(@JsonProperty("s") String name) { + return new Pat(name); + } + } + + /** Database property. + * + * <p>Values exist for methods, such as + * {@link DatabaseMetaData#getSQLKeywords()}, which always return the same + * value at all times and across connections. + * + * @see #getDatabaseProperties(Meta.ConnectionHandle) + */ + enum DatabaseProperty { + /** Database property containing the value of + * {@link DatabaseMetaData#getNumericFunctions()}. */ + GET_NUMERIC_FUNCTIONS(""), + + /** Database property containing the value of + * {@link DatabaseMetaData#getStringFunctions()}. */ + GET_STRING_FUNCTIONS(""), + + /** Database property containing the value of + * {@link DatabaseMetaData#getSystemFunctions()}. */ + GET_SYSTEM_FUNCTIONS(""), + + /** Database property containing the value of + * {@link DatabaseMetaData#getTimeDateFunctions()}. */ + GET_TIME_DATE_FUNCTIONS(""), + + /** Database property containing the value of + * {@link DatabaseMetaData#getSQLKeywords()}. */ + GET_S_Q_L_KEYWORDS(""), + + /** Database property containing the value of + * {@link DatabaseMetaData#getDefaultTransactionIsolation()}. */ + GET_DEFAULT_TRANSACTION_ISOLATION(Connection.TRANSACTION_NONE); + + public final Class<?> type; + public final Object defaultValue; + public final Method method; + + <T> DatabaseProperty(T defaultValue) { + this.defaultValue = defaultValue; + final String methodName = AvaticaUtils.toCamelCase(name()); + try { + this.method = DatabaseMetaData.class.getMethod(methodName); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + this.type = AvaticaUtils.box(method.getReturnType()); + assert defaultValue == null || defaultValue.getClass() == type; + } + + /** Returns a value of this property, using the default value if the map + * does not contain an explicit value. */ + public <T> T getProp(Meta meta, ConnectionHandle ch, Class<T> aClass) { + return getProp(meta.getDatabaseProperties(ch), aClass); + } + + /** Returns a value of this property, using the default value if the map + * does not contain an explicit value. */ + public <T> T getProp(Map<DatabaseProperty, Object> map, Class<T> aClass) { + assert aClass == type; + Object v = map.get(this); + if (v == null) { + v = defaultValue; + } + return aClass.cast(v); + } + + public static DatabaseProperty fromProto(Common.DatabaseProperty proto) { + return DatabaseProperty.valueOf(proto.getName()); + } + + public Common.DatabaseProperty toProto() { + return Common.DatabaseProperty.newBuilder().setName(name()).build(); + } + } + + /** Response from execute. + * + * <p>Typically a query will have a result set and rowCount = -1; + * a DML statement will have a rowCount and no result sets. + */ + class ExecuteResult { + public final List<MetaResultSet> resultSets; + + public ExecuteResult(List<MetaResultSet> resultSets) { + this.resultSets = resultSets; + } + } + + /** Meta data from which a result set can be constructed. + * + * <p>If {@code updateCount} is not -1, the result is just a count. A result + * set cannot be constructed. */ + class MetaResultSet { + public final String connectionId; + public final int statementId; + public final boolean ownStatement; + public final Frame firstFrame; + public final Signature signature; + public final long updateCount; + + @Deprecated // to be removed before 2.0 + protected MetaResultSet(String connectionId, int statementId, + boolean ownStatement, Signature signature, Frame firstFrame, + int updateCount) { + this(connectionId, statementId, ownStatement, signature, firstFrame, + (long) updateCount); + } + + protected MetaResultSet(String connectionId, int statementId, + boolean ownStatement, Signature signature, Frame firstFrame, + long updateCount) { + this.signature = signature; + this.connectionId = connectionId; + this.statementId = statementId; + this.ownStatement = ownStatement; + this.firstFrame = firstFrame; // may be null even if signature is not null + this.updateCount = updateCount; + } + + public static MetaResultSet create(String connectionId, int statementId, + boolean ownStatement, Signature signature, Frame firstFrame) { + return new MetaResultSet(connectionId, statementId, ownStatement, + Objects.requireNonNull(signature), firstFrame, -1L); + } + + public static MetaResultSet count(String connectionId, int statementId, + long updateCount) { + assert updateCount >= 0 + : "Meta.count(" + connectionId + ", " + statementId + ", " + + updateCount + ")"; + return new MetaResultSet(connectionId, statementId, false, null, null, + updateCount); + } + } + + /** Information necessary to convert an {@link Iterable} into a + * {@link org.apache.calcite.avatica.util.Cursor}. */ + final class CursorFactory { + private static final FieldDescriptor CLASS_NAME_DESCRIPTOR = Common.CursorFactory. + getDescriptor().findFieldByNumber(Common.CursorFactory.CLASS_NAME_FIELD_NUMBER); + + public final Style style; + public final Class clazz; + @JsonIgnore + public final List<Field> fields; + public final List<String> fieldNames; + + private CursorFactory(Style style, Class clazz, List<Field> fields, + List<String> fieldNames) { + assert (fieldNames != null) + == (style == Style.RECORD_PROJECTION || style == Style.MAP); + assert (fields != null) == (style == Style.RECORD_PROJECTION); + this.style = Objects.requireNonNull(style); + this.clazz = clazz; + this.fields = fields; + this.fieldNames = fieldNames; + } + + @JsonCreator + public static CursorFactory create(@JsonProperty("style") Style style, + @JsonProperty("clazz") Class clazz, + @JsonProperty("fieldNames") List<String> fieldNames) { + switch (style) { + case OBJECT: + return OBJECT; + case ARRAY: + return ARRAY; + case LIST: + return LIST; + case RECORD: + return record(clazz); + case RECORD_PROJECTION: + return record(clazz, null, fieldNames); + case MAP: + return map(fieldNames); + default: + throw new AssertionError("unknown style: " + style); + } + } + + public static final CursorFactory OBJECT = + new CursorFactory(Style.OBJECT, null, null, null); + + public static final CursorFactory ARRAY = + new CursorFactory(Style.ARRAY, null, null, null); + + public static final CursorFactory LIST = + new CursorFactory(Style.LIST, null, null, null); + + public static CursorFactory record(Class resultClazz) { + return new CursorFactory(Style.RECORD, resultClazz, null, null); + } + + public static CursorFactory record(Class resultClass, List<Field> fields, + List<String> fieldNames) { + if (fields == null) { + fields = new ArrayList<>(); + for (String fieldName : fieldNames) { + try { + fields.add(resultClass.getField(fieldName)); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + } + return new CursorFactory(Style.RECORD_PROJECTION, resultClass, fields, + fieldNames); + } + + public static CursorFactory map(List<String> fieldNames) { + return new CursorFactory(Style.MAP, null, null, fieldNames); + } + + public static CursorFactory deduce(List<ColumnMetaData> columns, + Class resultClazz) { + if (columns.size() == 1) { + return OBJECT; + } + if (resultClazz == null) { + return ARRAY; + } + if (resultClazz.isArray()) { + return ARRAY; + } + if (List.class.isAssignableFrom(resultClazz)) { + return LIST; + } + return record(resultClazz); + } + + public Common.CursorFactory toProto() { + Common.CursorFactory.Builder builder = Common.CursorFactory.newBuilder(); + + if (null != clazz) { + builder.setClassName(clazz.getName()); + } + builder.setStyle(style.toProto()); + if (null != fieldNames) { + builder.addAllFieldNames(fieldNames); + } + + return builder.build(); + } + + public static CursorFactory fromProto(Common.CursorFactory proto) { + // Reconstruct CursorFactory + Class<?> clz = null; + + if (proto.hasField(CLASS_NAME_DESCRIPTOR)) { + try { + clz = Class.forName(proto.getClassName()); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + return CursorFactory.create(Style.fromProto(proto.getStyle()), clz, + proto.getFieldNamesList()); + } + + @Override public int hashCode() { + return Objects.hash(clazz, fieldNames, fields, style); + } + + @Override public boolean equals(Object o) { + return o == this + || o instanceof CursorFactory + && Objects.equals(clazz, ((CursorFactory) o).clazz) + && Objects.equals(fieldNames, ((CursorFactory) o).fieldNames) + && Objects.equals(fields, ((CursorFactory) o).fields) + && style == ((CursorFactory) o).style; + } + } + + /** How logical fields are represented in the objects returned by the + * iterator. */ + enum Style { + OBJECT, + RECORD, + RECORD_PROJECTION, + ARRAY, + LIST, + MAP; + + public Common.CursorFactory.Style toProto() { + return Common.CursorFactory.Style.valueOf(name()); + } + + public static Style fromProto(Common.CursorFactory.Style proto) { + return Style.valueOf(proto.name()); + } + } + + /** Result of preparing a statement. */ + public class Signature { + private static final FieldDescriptor SQL_DESCRIPTOR = Common.Signature + .getDescriptor().findFieldByNumber(Common.Signature.SQL_FIELD_NUMBER); + private static final FieldDescriptor CURSOR_FACTORY_DESCRIPTOR = Common.Signature + .getDescriptor().findFieldByNumber(Common.Signature.CURSOR_FACTORY_FIELD_NUMBER); + + public final List<ColumnMetaData> columns; + public final String sql; + public final List<AvaticaParameter> parameters; + public final transient Map<String, Object> internalParameters; + public final CursorFactory cursorFactory; + + public final Meta.StatementType statementType; + + /** Creates a Signature. */ + public Signature(List<ColumnMetaData> columns, + String sql, + List<AvaticaParameter> parameters, + Map<String, Object> internalParameters, + CursorFactory cursorFactory, + Meta.StatementType statementType) { + this.columns = columns; + this.sql = sql; + this.parameters = parameters; + this.internalParameters = internalParameters; + this.cursorFactory = cursorFactory; + this.statementType = statementType; + } + + /** Used by Jackson to create a Signature by de-serializing JSON. */ + @JsonCreator + public static Signature create( + @JsonProperty("columns") List<ColumnMetaData> columns, + @JsonProperty("sql") String sql, + @JsonProperty("parameters") List<AvaticaParameter> parameters, + @JsonProperty("cursorFactory") CursorFactory cursorFactory, + @JsonProperty("statementType") Meta.StatementType statementType) { + return new Signature(columns, sql, parameters, + Collections.<String, Object>emptyMap(), cursorFactory, statementType); + } + + /** Returns a copy of this Signature, substituting given CursorFactory. */ + public Signature setCursorFactory(CursorFactory cursorFactory) { + return new Signature(columns, sql, parameters, internalParameters, + cursorFactory, statementType); + } + + /** Creates a copy of this Signature with null lists and maps converted to + * empty. */ + public Signature sanitize() { + if (columns == null || parameters == null || internalParameters == null + || statementType == null) { + return new Signature(sanitize(columns), sql, sanitize(parameters), + sanitize(internalParameters), cursorFactory, + Meta.StatementType.SELECT); + } + return this; + } + + private <E> List<E> sanitize(List<E> list) { + return list == null ? Collections.<E>emptyList() : list; + } + + private <K, V> Map<K, V> sanitize(Map<K, V> map) { + return map == null ? Collections.<K, V>emptyMap() : map; + } + + public Common.Signature toProto() { + Common.Signature.Builder builder = Common.Signature.newBuilder(); + + if (null != sql) { + builder.setSql(sql); + } + + if (null != cursorFactory) { + builder.setCursorFactory(cursorFactory.toProto()); + } + + if (null != columns) { + for (ColumnMetaData column : columns) { + builder.addColumns(column.toProto()); + } + } + + if (null != parameters) { + for (AvaticaParameter parameter : parameters) { + builder.addParameters(parameter.toProto()); + } + } + + return builder.build(); + } + + public static Signature fromProto(Common.Signature protoSignature) { + List<ColumnMetaData> metadata = new ArrayList<>(protoSignature.getColumnsCount()); + for (Common.ColumnMetaData protoMetadata : protoSignature.getColumnsList()) { + metadata.add(ColumnMetaData.fromProto(protoMetadata)); + } + + List<AvaticaParameter> parameters = new ArrayList<>(protoSignature.getParametersCount()); + for (Common.AvaticaParameter protoParam : protoSignature.getParametersList()) { + parameters.add(AvaticaParameter.fromProto(protoParam)); + } + + String sql = null; + if (protoSignature.hasField(SQL_DESCRIPTOR)) { + sql = protoSignature.getSql(); + } + + CursorFactory cursorFactory = null; + if (protoSignature.hasField(CURSOR_FACTORY_DESCRIPTOR)) { + cursorFactory = CursorFactory.fromProto(protoSignature.getCursorFactory()); + } + final Meta.StatementType statementType = + Meta.StatementType.fromProto(protoSignature.getStatementType()); + + return Signature.create(metadata, sql, parameters, cursorFactory, statementType); + } + + @Override public int hashCode() { + return Objects.hash(columns, cursorFactory, parameters, sql); + } + + @Override public boolean equals(Object o) { + return o == this + || o instanceof Signature + && Objects.equals(columns, ((Signature) o).columns) + && Objects.equals(cursorFactory, ((Signature) o).cursorFactory) + && Objects.equals(parameters, ((Signature) o).parameters) + && Objects.equals(sql, ((Signature) o).sql); + } + } + + /** A collection of rows. */ + class Frame { + private static final FieldDescriptor HAS_ARRAY_VALUE_DESCRIPTOR = Common.ColumnValue + .getDescriptor().findFieldByNumber(Common.ColumnValue.HAS_ARRAY_VALUE_FIELD_NUMBER); + private static final FieldDescriptor SCALAR_VALUE_DESCRIPTOR = Common.ColumnValue + .getDescriptor().findFieldByNumber(Common.ColumnValue.SCALAR_VALUE_FIELD_NUMBER); + /** Frame that has zero rows and is the last frame. */ + public static final Frame EMPTY = + new Frame(0, true, Collections.emptyList()); + + /** Frame that has zero rows but may have another frame. */ + public static final Frame MORE = + new Frame(0, false, Collections.emptyList()); + + /** Zero-based offset of first row. */ + public final long offset; + /** Whether this is definitely the last frame of rows. + * If true, there are no more rows. + * If false, there may or may not be more rows. */ + public final boolean done; + /** The rows. */ + public final Iterable<Object> rows; + + public Frame(long offset, boolean done, Iterable<Object> rows) { + this.offset = offset; + this.done = done; + this.rows = rows; + } + + @JsonCreator + public static Frame create(@JsonProperty("offset") int offset, + @JsonProperty("done") boolean done, + @JsonProperty("rows") List<Object> rows) { + if (offset == 0 && done && rows.isEmpty()) { + return EMPTY; + } + return new Frame(offset, done, rows); + } + + public Common.Frame toProto() { + Common.Frame.Builder builder = Common.Frame.newBuilder(); + + builder.setDone(done).setOffset(offset); + + for (Object row : this.rows) { + if (null == row) { + // Does this need to be persisted for some reason? + continue; + } + + if (row instanceof Object[]) { + final Common.Row.Builder rowBuilder = Common.Row.newBuilder(); + + for (Object element : (Object[]) row) { + final Common.ColumnValue.Builder columnBuilder = Common.ColumnValue.newBuilder(); + + if (element instanceof List) { + columnBuilder.setHasArrayValue(true); + List<?> list = (List<?>) element; + // Add each element in the list/array to the column's value + for (Object listItem : list) { + columnBuilder.addArrayValue(serializeScalar(listItem)); + } + } else { + // The default value, but still explicit. + columnBuilder.setHasArrayValue(false); + // Only one value for this column, a scalar. + columnBuilder.setScalarValue(serializeScalar(element)); + } + + // Add value to row + rowBuilder.addValue(columnBuilder.build()); + } + + // Collect all rows + builder.addRows(rowBuilder.build()); + } else { + // Can a "row" be a primitive? A struct? Only an Array? + throw new RuntimeException("Only arrays are supported"); + } + } + + return builder.build(); + } + + static Common.TypedValue serializeScalar(Object element) { + final Common.TypedValue.Builder valueBuilder = Common.TypedValue.newBuilder(); + + // Numbers + if (element instanceof Byte) { + valueBuilder.setType(Common.Rep.BYTE).setNumberValue(((Byte) element).longValue()); + } else if (element instanceof Short) { + valueBuilder.setType(Common.Rep.SHORT).setNumberValue(((Short) element).longValue()); + } else if (element instanceof Integer) { + valueBuilder.setType(Common.Rep.INTEGER) + .setNumberValue(((Integer) element).longValue()); + } else if (element instanceof Long) { + valueBuilder.setType(Common.Rep.LONG).setNumberValue((Long) element); + } else if (element instanceof Double) { + valueBuilder.setType(Common.Rep.DOUBLE).setDoubleValue((Double) element); + } else if (element instanceof Float) { + valueBuilder.setType(Common.Rep.FLOAT).setNumberValue(((Float) element).longValue()); + } else if (element instanceof BigDecimal) { + valueBuilder.setType(Common.Rep.NUMBER) + .setDoubleValue(((BigDecimal) element).doubleValue()); + // Strings + } else if (element instanceof String) { + valueBuilder.setType(Common.Rep.STRING) + .setStringValue((String) element); + } else if (element instanceof Character) { + valueBuilder.setType(Common.Rep.CHARACTER) + .setStringValue(element.toString()); + // Bytes + } else if (element instanceof byte[]) { + valueBuilder.setType(Common.Rep.BYTE_STRING) + .setBytesValues(ByteString.copyFrom((byte[]) element)); + // Boolean + } else if (element instanceof Boolean) { + valueBuilder.setType(Common.Rep.BOOLEAN).setBoolValue((boolean) element); + } else if (null == element) { + valueBuilder.setType(Common.Rep.NULL); + // Unhandled + } else { + throw new RuntimeException("Unhandled type in Frame: " + element.getClass()); + } + + return valueBuilder.build(); + } + + public static Frame fromProto(Common.Frame proto) { + List<Object> parsedRows = new ArrayList<>(proto.getRowsCount()); + for (Common.Row protoRow : proto.getRowsList()) { + ArrayList<Object> row = new ArrayList<>(protoRow.getValueCount()); + for (Common.ColumnValue protoColumn : protoRow.getValueList()) { + final Object value; + if (!isNewStyleColumn(protoColumn)) { + // Backward compatibility + value = parseOldStyleColumn(protoColumn); + } else { + // Current style parsing (separate scalar and array values) + value = parseColumn(protoColumn); + } + + row.add(value); + } + + parsedRows.add(row); + } + + return new Frame(proto.getOffset(), proto.getDone(), parsedRows); + } + + /** + * Determines whether this message contains the new attributes in the + * message. We can't directly test for the negative because our + * {@code hasField} trick does not work on repeated fields. + * + * @param column The protobuf column object + * @return True if the message is the new style, false otherwise. + */ + static boolean isNewStyleColumn(Common.ColumnValue column) { + return column.hasField(HAS_ARRAY_VALUE_DESCRIPTOR) + || column.hasField(SCALAR_VALUE_DESCRIPTOR); + } + + /** + * For Calcite 1.5, we made the mistake of using array length to determine when the value for a + * column is a scalar or an array. This method performs the old parsing for backwards + * compatibility. + * + * @param column The protobuf ColumnValue object + * @return The parsed value for this column + */ + static Object parseOldStyleColumn(Common.ColumnValue column) { + if (column.getValueCount() > 1) { + List<Object> array = new ArrayList<>(column.getValueCount()); + for (Common.TypedValue columnValue : column.getValueList()) { + array.add(getScalarValue(columnValue)); + } + return array; + } else { + return getScalarValue(column.getValue(0)); + } + } + + /** + * Parses the value for a ColumnValue using the separated array and scalar attributes. + * + * @param column The protobuf ColumnValue object + * @return The parse value for this column + */ + static Object parseColumn(Common.ColumnValue column) { + // Verify that we have one or the other (scalar or array) + validateColumnValue(column); + + if (!column.hasField(SCALAR_VALUE_DESCRIPTOR)) { + // Array + List<Object> array = new ArrayList<>(column.getArrayValueCount()); + for (Common.TypedValue arrayValue : column.getArrayValueList()) { + array.add(getScalarValue(arrayValue)); + } + return array; + } else { + // Scalar + return getScalarValue(column.getScalarValue()); + } + } + + /** + * Verifies that a ColumnValue has only a scalar or array value, not both and not neither. + * + * @param column The protobuf ColumnValue object + * @throws IllegalArgumentException When the above condition is not met + */ + static void validateColumnValue(Common.ColumnValue column) { + final boolean hasScalar = column.hasField(SCALAR_VALUE_DESCRIPTOR); + final boolean hasArrayValue = column.getHasArrayValue(); + + // These should always be different + if (hasScalar == hasArrayValue) { + throw new IllegalArgumentException("A column must have a scalar or array value, not " + + (hasScalar ? "both" : "neither")); + } + } + + static Object getScalarValue(Common.TypedValue protoElement) { + // TODO Should these be primitives or Objects? + switch (protoElement.getType()) { + case BYTE: + return Long.valueOf(protoElement.getNumberValue()).byteValue(); + case SHORT: + return Long.valueOf(protoElement.getNumberValue()).shortValue(); + case INTEGER: + return Long.valueOf(protoElement.getNumberValue()).intValue(); + case LONG: + return protoElement.getNumberValue(); + case FLOAT: + return Long.valueOf(protoElement.getNumberValue()).floatValue(); + case DOUBLE: + return protoElement.getDoubleValue(); + case NUMBER: + // TODO more cases here to expand on? BigInteger? + return BigDecimal.valueOf(protoElement.getDoubleValue()); + case STRING: + return protoElement.getStringValue(); + case CHARACTER: + // A single character in the string + return protoElement.getStringValue().charAt(0); + case BYTE_STRING: + return protoElement.getBytesValues().toByteArray(); + case BOOLEAN: + return protoElement.getBoolValue(); + case NULL: + return null; + default: + throw new RuntimeException("Unhandled type: " + protoElement.getType()); + } + } + + @Override public int hashCode() { + return Objects.hash(done, offset, rows); + } + + @Override public boolean equals(Object o) { + return o == this + || o instanceof Frame + && equalRows(rows, ((Frame) o).rows) + && offset == ((Frame) o).offset + && done == ((Frame) o).done; + } + + private static boolean equalRows(Iterable<Object> rows, Iterable<Object> otherRows) { + if (null == rows) { + if (null != otherRows) { + return false; + } + } else { + Iterator<Object> iter1 = rows.iterator(); + Iterator<Object> iter2 = otherRows.iterator(); + while (iter1.hasNext() && iter2.hasNext()) { + Object obj1 = iter1.next(); + Object obj2 = iter2.next(); + + // Can't just call equals on an array + if (obj1 instanceof Object[]) { + if (obj2 instanceof Object[]) { + // Compare array and array + if (!Arrays.equals((Object[]) obj1, (Object[]) obj2)) { + return false; + } + } else if (obj2 instanceof List) { + // compare array and list + @SuppressWarnings("unchecked") + List<Object> obj2List = (List<Object>) obj2; + if (!Arrays.equals((Object[]) obj1, obj2List.toArray())) { + return false; + } + } else { + // compare array and something that isn't an array will always fail + return false; + } + } else if (obj1 instanceof List) { + if (obj2 instanceof Object[]) { + // Compare list and array + @SuppressWarnings("unchecked") + List<Object> obj1List = (List<Object>) obj1; + if (!Arrays.equals(obj1List.toArray(), (Object[]) obj2)) { + return false; + } + } else if (!obj1.equals(obj2)) { + // compare list and something else, let it fall to equals() + return false; + } + } else if (!obj1.equals(obj2)) { + // Not an array, leave it to equals() + return false; + } + } + + // More elements in one of the iterables + if (iter1.hasNext() || iter2.hasNext()) { + return false; + } + } + + return true; + } + } + + /** Connection handle. */ + class ConnectionHandle { + public final String id; + + @Override public String toString() { + return id; + } + + @JsonCreator + public ConnectionHandle(@JsonProperty("id") String id) { + this.id = id; + } + } + + /** Statement handle. */ + class StatementHandle { + private static final FieldDescriptor SIGNATURE_DESCRIPTOR = Common.StatementHandle + .getDescriptor().findFieldByNumber(Common.StatementHandle.SIGNATURE_FIELD_NUMBER); + public final String connectionId; + public final int id; + + // not final because LocalService#apply(PrepareRequest) + /** Only present for PreparedStatement handles, null otherwise. */ + public Signature signature; + + @Override public String toString() { + return connectionId + "::" + Integer.toString(id); + } + + @JsonCreator + public StatementHandle( + @JsonProperty("connectionId") String connectionId, + @JsonProperty("id") int id, + @JsonProperty("signature") Signature signature) { + this.connectionId = connectionId; + this.id = id; + this.signature = signature; + } + + public Common.StatementHandle toProto() { + Common.StatementHandle.Builder builder = Common.StatementHandle.newBuilder() + .setConnectionId(connectionId).setId(id); + if (null != signature) { + builder.setSignature(signature.toProto()); + } + return builder.build(); + } + + public static StatementHandle fromProto(Common.StatementHandle protoHandle) { + // Signature is optional in the update path for executes. + Signature signature = null; + if (protoHandle.hasField(SIGNATURE_DESCRIPTOR)) { + signature = Signature.fromProto(protoHandle.getSignature()); + } + return new StatementHandle(protoHandle.getConnectionId(), protoHandle.getId(), signature); + } + + @Override public int hashCode() { + return Objects.hash(connectionId, id, signature); + } + + @Override public boolean equals(Object o) { + return o == this + || o instanceof StatementHandle + && Objects.equals(connectionId, ((StatementHandle) o).connectionId) + && Objects.equals(signature, ((StatementHandle) o).signature) + && id == ((StatementHandle) o).id; + } + } + + /** A pojo containing various client-settable {@link java.sql.Connection} properties. + * + * <p>{@code java.lang} types are used here so that {@code null} can be used to indicate + * a value has no been set.</p> + * + * <p>Note: this interface is considered "experimental" and may undergo further changes as this + * functionality is extended to other aspects of state management for + * {@link java.sql.Connection}, {@link java.sql.Statement}, and {@link java.sql.ResultSet}.</p> + */ + @JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + property = "connProps", + defaultImpl = ConnectionPropertiesImpl.class) + @JsonSubTypes({ + @JsonSubTypes.Type(value = ConnectionPropertiesImpl.class, name = "connPropsImpl") + }) + interface ConnectionProperties { + + /** Overwrite fields in {@code this} with any non-null fields in {@code that} + * + * @return {@code this} + */ + ConnectionProperties merge(ConnectionProperties that); + + /** @return {@code true} when no properies have been set, {@code false} otherwise. */ + @JsonIgnore + boolean isEmpty(); + + /** Set {@code autoCommit} status. + * + * @return {@code this} + */ + ConnectionProperties setAutoCommit(boolean val); + + Boolean isAutoCommit(); + + /** Set {@code readOnly} status. + * + * @return {@code this} + */ + ConnectionProperties setReadOnly(boolean val); + + Boolean isReadOnly(); + + /** Set {@code transactionIsolation} status. + * + * @return {@code this} + */ + ConnectionProperties setTransactionIsolation(int val); + + Integer getTransactionIsolation(); + + /** Set {@code catalog}. + * + * @return {@code this} + */ + ConnectionProperties setCatalog(String val); + + String getCatalog(); + + /** Set {@code schema}. + * + * @return {@code this} + */ + ConnectionProperties setSchema(String val); + + String getSchema(); + + Common.ConnectionProperties toProto(); + } + + /** API to put a result set into a statement, being careful to enforce + * thread-safety and not to overwrite existing open result sets. */ + interface PrepareCallback { + Object getMonitor(); + void clear() throws SQLException; + void assign(Signature signature, Frame firstFrame, long updateCount) + throws SQLException; + void execute() throws SQLException; + } + + /** Type of statement. */ + enum StatementType { + SELECT, INSERT, UPDATE, DELETE, UPSERT, MERGE, OTHER_DML, IS_DML, + CREATE, DROP, ALTER, OTHER_DDL, CALL; + + public boolean canUpdate() { + switch(this) { + case INSERT: + return true; + case IS_DML: + return true; + default: + return false; + } + } + + public Common.StatementType toProto() { + return Common.StatementType.valueOf(name()); + } + + public static StatementType fromProto(Common.StatementType proto) { + return StatementType.valueOf(proto.name()); + } + } +} + +// End Meta.java http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/MetaImpl.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/MetaImpl.java b/avatica/core/src/main/java/org/apache/calcite/avatica/MetaImpl.java new file mode 100644 index 0000000..7bb99fc --- /dev/null +++ b/avatica/core/src/main/java/org/apache/calcite/avatica/MetaImpl.java @@ -0,0 +1,961 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.avatica; + +import org.apache.calcite.avatica.remote.TypedValue; +import org.apache.calcite.avatica.util.ArrayIteratorCursor; +import org.apache.calcite.avatica.util.Cursor; +import org.apache.calcite.avatica.util.IteratorCursor; +import org.apache.calcite.avatica.util.ListIteratorCursor; +import org.apache.calcite.avatica.util.MapIteratorCursor; +import org.apache.calcite.avatica.util.RecordIteratorCursor; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; + +/** + * Basic implementation of {@link Meta}. + * + * <p>Each sub-class must implement the two remaining abstract methods, + * {@link #prepare} and + * {@link #prepareAndExecute}. + * It should also override metadata methods such as {@link #getCatalogs(Meta.ConnectionHandle)} and + * {@link #getTables} for the element types for which it has instances; the + * default metadata methods return empty collections. + */ +public abstract class MetaImpl implements Meta { + /** The {@link AvaticaConnection} backing {@code this}. */ + protected final AvaticaConnection connection; + /** Represents the various states specific to {@link #connection}. + * + * <p>Note: this instance is used recursively with {@link #connection}'s getter and setter + * methods.</p> + */ + protected final ConnectionPropertiesImpl connProps; + + public MetaImpl(AvaticaConnection connection) { + this.connection = connection; + this.connProps = new ConnectionPropertiesImpl(); + } + + /** Uses a {@link org.apache.calcite.avatica.Meta.CursorFactory} to convert + * an {@link Iterable} into a + * {@link org.apache.calcite.avatica.util.Cursor}. */ + public static Cursor createCursor(CursorFactory cursorFactory, + Iterable<Object> iterable) { + switch (cursorFactory.style) { + case OBJECT: + return new IteratorCursor<Object>(iterable.iterator()) { + protected Getter createGetter(int ordinal) { + return new ObjectGetter(ordinal); + } + }; + case ARRAY: + @SuppressWarnings("unchecked") final Iterable<Object[]> iterable1 = + (Iterable<Object[]>) (Iterable) iterable; + return new ArrayIteratorCursor(iterable1.iterator()); + case RECORD: + @SuppressWarnings("unchecked") final Class<Object> clazz = + cursorFactory.clazz; + return new RecordIteratorCursor<Object>(iterable.iterator(), clazz); + case RECORD_PROJECTION: + @SuppressWarnings("unchecked") final Class<Object> clazz2 = + cursorFactory.clazz; + return new RecordIteratorCursor<Object>(iterable.iterator(), clazz2, + cursorFactory.fields); + case LIST: + @SuppressWarnings("unchecked") final Iterable<List<Object>> iterable2 = + (Iterable<List<Object>>) (Iterable) iterable; + return new ListIteratorCursor(iterable2.iterator()); + case MAP: + @SuppressWarnings("unchecked") final Iterable<Map<String, Object>> + iterable3 = + (Iterable<Map<String, Object>>) (Iterable) iterable; + return new MapIteratorCursor(iterable3.iterator(), + cursorFactory.fieldNames); + default: + throw new AssertionError("unknown style: " + cursorFactory.style); + } + } + + public static List<List<Object>> collect(CursorFactory cursorFactory, + final Iterator<Object> iterator, List<List<Object>> list) { + final Iterable<Object> iterable = new Iterable<Object>() { + public Iterator<Object> iterator() { + return iterator; + } + }; + return collect(cursorFactory, iterable, list); + } + + public static List<List<Object>> collect(CursorFactory cursorFactory, + Iterable<Object> iterable, final List<List<Object>> list) { + switch (cursorFactory.style) { + case OBJECT: + for (Object o : iterable) { + list.add(Collections.singletonList(o)); + } + return list; + case ARRAY: + @SuppressWarnings("unchecked") final Iterable<Object[]> iterable1 = + (Iterable<Object[]>) (Iterable) iterable; + for (Object[] objects : iterable1) { + list.add(Arrays.asList(objects)); + } + return list; + case RECORD: + case RECORD_PROJECTION: + final Field[] fields; + switch (cursorFactory.style) { + case RECORD: + fields = cursorFactory.clazz.getFields(); + break; + default: + fields = cursorFactory.fields.toArray( + new Field[cursorFactory.fields.size()]); + } + for (Object o : iterable) { + final Object[] objects = new Object[fields.length]; + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + try { + objects[i] = field.get(o); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + list.add(Arrays.asList(objects)); + } + return list; + case LIST: + @SuppressWarnings("unchecked") final Iterable<List<Object>> iterable2 = + (Iterable<List<Object>>) (Iterable) iterable; + for (List<Object> objects : iterable2) { + list.add(objects); + } + return list; + case MAP: + @SuppressWarnings("unchecked") final Iterable<Map<String, Object>> + iterable3 = + (Iterable<Map<String, Object>>) (Iterable) iterable; + for (Map<String, Object> map : iterable3) { + final List<Object> objects = new ArrayList<Object>(); + for (String fieldName : cursorFactory.fieldNames) { + objects.add(map.get(fieldName)); + } + list.add(objects); + } + return list; + default: + throw new AssertionError("unknown style: " + cursorFactory.style); + } + } + + @Override public void openConnection(ConnectionHandle ch, Map<String, String> info) { + // dummy implementation, connection is already created at this point + } + + @Override public void closeConnection(ConnectionHandle ch) { + // TODO: implement + // + // lots of Calcite tests break with this simple implementation, + // requires investigation + +// try { +// connection.close(); +// } catch (SQLException e) { +// throw new RuntimeException(e); +// } + } + + @Override public ConnectionProperties connectionSync(ConnectionHandle ch, + ConnectionProperties connProps) { + this.connProps.merge(connProps); + this.connProps.setDirty(false); + return this.connProps; + } + + public StatementHandle createStatement(ConnectionHandle ch) { + return new StatementHandle(ch.id, connection.statementCount++, null); + } + + /** Creates an empty result set. Useful for JDBC metadata methods that are + * not implemented or which query entities that are not supported (e.g. + * triggers in Lingual). */ + protected <E> MetaResultSet createEmptyResultSet(final Class<E> clazz) { + return createResultSet(Collections.<String, Object>emptyMap(), + fieldMetaData(clazz).columns, + CursorFactory.deduce(fieldMetaData(clazz).columns, null), + Frame.EMPTY); + } + + public static ColumnMetaData columnMetaData(String name, int index, + Class<?> type) { + TypeInfo pair = TypeInfo.m.get(type); + ColumnMetaData.Rep rep = + ColumnMetaData.Rep.VALUE_MAP.get(type); + ColumnMetaData.AvaticaType scalarType = + ColumnMetaData.scalar(pair.sqlType, pair.sqlTypeName, rep); + return new ColumnMetaData( + index, false, true, false, false, + pair.primitive + ? DatabaseMetaData.columnNullable + : DatabaseMetaData.columnNoNulls, + true, -1, name, name, null, + 0, 0, null, null, scalarType, true, false, false, + scalarType.columnClassName()); + } + + protected static ColumnMetaData.StructType fieldMetaData(Class clazz) { + final List<ColumnMetaData> list = new ArrayList<ColumnMetaData>(); + for (Field field : clazz.getFields()) { + if (Modifier.isPublic(field.getModifiers()) + && !Modifier.isStatic(field.getModifiers())) { + list.add( + columnMetaData( + AvaticaUtils.camelToUpper(field.getName()), + list.size() + 1, field.getType())); + } + } + return ColumnMetaData.struct(list); + } + + protected MetaResultSet createResultSet( + Map<String, Object> internalParameters, List<ColumnMetaData> columns, + CursorFactory cursorFactory, Frame firstFrame) { + try { + final AvaticaStatement statement = connection.createStatement(); + final Signature signature = + new Signature(columns, "", Collections.<AvaticaParameter>emptyList(), + internalParameters, cursorFactory, Meta.StatementType.SELECT); + return MetaResultSet.create(connection.id, statement.getId(), true, + signature, firstFrame); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + /** An object that has a name. */ + public interface Named { + @JsonIgnore String getName(); + } + + /** Metadata describing a column. */ + public static class MetaColumn implements Named { + public final String tableCat; + public final String tableSchem; + public final String tableName; + public final String columnName; + public final int dataType; + public final String typeName; + public final int columnSize; + public final String bufferLength = null; + public final Integer decimalDigits; + public final int numPrecRadix; + public final int nullable; + public final String remarks = null; + public final String columnDef = null; + public final String sqlDataType = null; + public final String sqlDatetimeSub = null; + public final int charOctetLength; + public final int ordinalPosition; + public final String isNullable; + public final String scopeCatalog = null; + public final String scopeSchema = null; + public final String scopeTable = null; + public final String sourceDataType = null; + public final String isAutoincrement = null; + public final String isGeneratedcolumn = null; + + public MetaColumn( + String tableCat, + String tableSchem, + String tableName, + String columnName, + int dataType, + String typeName, + int columnSize, + Integer decimalDigits, + int numPrecRadix, + int nullable, + int charOctetLength, + int ordinalPosition, + String isNullable) { + this.tableCat = tableCat; + this.tableSchem = tableSchem; + this.tableName = tableName; + this.columnName = columnName; + this.dataType = dataType; + this.typeName = typeName; + this.columnSize = columnSize; + this.decimalDigits = decimalDigits; + this.numPrecRadix = numPrecRadix; + this.nullable = nullable; + this.charOctetLength = charOctetLength; + this.ordinalPosition = ordinalPosition; + this.isNullable = isNullable; + } + + public String getName() { + return columnName; + } + } + + /** Metadata describing a TypeInfo. */ + + /** Metadata describing a table. */ + public static class MetaTable implements Named { + public final String tableCat; + public final String tableSchem; + public final String tableName; + public final String tableType; + public final String remarks = null; + public final String typeCat = null; + public final String typeSchem = null; + public final String typeName = null; + public final String selfReferencingColName = null; + public final String refGeneration = null; + + public MetaTable(String tableCat, + String tableSchem, + String tableName, + String tableType) { + this.tableCat = tableCat; + this.tableSchem = tableSchem; + this.tableName = tableName; + this.tableType = tableType; + } + + public String getName() { + return tableName; + } + } + + /** Metadata describing a schema. */ + public static class MetaSchema implements Named { + public final String tableCatalog; + public final String tableSchem; + + public MetaSchema( + String tableCatalog, + String tableSchem) { + this.tableCatalog = tableCatalog; + this.tableSchem = tableSchem; + } + + public String getName() { + return tableSchem; + } + } + + /** Metadata describing a catalog. */ + public static class MetaCatalog implements Named { + public final String tableCat; + + public MetaCatalog( + String tableCatalog) { + this.tableCat = tableCatalog; + } + + public String getName() { + return tableCat; + } + } + + /** Metadata describing a table type. */ + public static class MetaTableType { + public final String tableType; + + public MetaTableType(String tableType) { + this.tableType = tableType; + } + } + + /** Metadata describing a procedure. */ + public static class MetaProcedure { + } + + /** Metadata describing a procedure column. */ + public static class MetaProcedureColumn { + } + + /** Metadata describing a column privilege. */ + public static class MetaColumnPrivilege { + } + + /** Metadata describing a table privilege. */ + public static class MetaTablePrivilege { + } + + /** Metadata describing the best identifier for a row. */ + public static class MetaBestRowIdentifier { + } + + /** Metadata describing a version column. */ + public static class MetaVersionColumn { + public final short scope; + public final String columnName; + public final int dataType; + public final String typeName; + public final int columnSize; + public final int bufferLength; + public final short decimalDigits; + public final short pseudoColumn; + + MetaVersionColumn(short scope, String columnName, int dataType, + String typeName, int columnSize, int bufferLength, short decimalDigits, + short pseudoColumn) { + this.scope = scope; + this.columnName = columnName; + this.dataType = dataType; + this.typeName = typeName; + this.columnSize = columnSize; + this.bufferLength = bufferLength; + this.decimalDigits = decimalDigits; + this.pseudoColumn = pseudoColumn; + } + } + + /** Metadata describing a primary key. */ + public static class MetaPrimaryKey { + public final String tableCat; + public final String tableSchem; + public final String tableName; + public final String columnName; + public final short keySeq; + public final String pkName; + + MetaPrimaryKey(String tableCat, String tableSchem, String tableName, + String columnName, short keySeq, String pkName) { + this.tableCat = tableCat; + this.tableSchem = tableSchem; + this.tableName = tableName; + this.columnName = columnName; + this.keySeq = keySeq; + this.pkName = pkName; + } + } + + /** Metadata describing an imported key. */ + public static class MetaImportedKey { + } + + /** Metadata describing an exported key. */ + public static class MetaExportedKey { + } + + /** Metadata describing a cross reference. */ + public static class MetaCrossReference { + } + + /** Metadata describing type info. */ + public static class MetaTypeInfo implements Named { + public final String typeName; + public final int dataType; + public final int precision; + public final String literalPrefix; + public final String literalSuffix; + //TODO: Add create parameter for type on DDL + public final String createParams = null; + public final int nullable; + public final boolean caseSensitive; + public final int searchable; + public final boolean unsignedAttribute; + public final boolean fixedPrecScale; + public final boolean autoIncrement; + public final String localTypeName; + public final int minimumScale; + public final int maximumScale; + public final int sqlDataType = 0; + public final int sqlDatetimeSub = 0; + public final Integer numPrecRadix; //nullable int + + public MetaTypeInfo( + String typeName, + int dataType, + int precision, + String literalPrefix, + String literalSuffix, + int nullable, + boolean caseSensitive, + int searchable, + boolean unsignedAttribute, + boolean fixedPrecScale, + boolean autoIncrement, + int minimumScale, + int maximumScale, + int numPrecRadix) { + this.typeName = typeName; + this.dataType = dataType; + this.precision = precision; + this.literalPrefix = literalPrefix; + this.literalSuffix = literalSuffix; + this.nullable = nullable; + this.caseSensitive = caseSensitive; + this.searchable = searchable; + this.unsignedAttribute = unsignedAttribute; + this.fixedPrecScale = fixedPrecScale; + this.autoIncrement = autoIncrement; + this.localTypeName = typeName; + // Make min to be 0 instead of -1 + this.minimumScale = minimumScale == -1 ? 0 : minimumScale; + this.maximumScale = maximumScale == -1 ? 0 : maximumScale; + this.numPrecRadix = numPrecRadix == 0 ? null : numPrecRadix; + } + + public String getName() { + return typeName; + } + } + + /** Metadata describing index info. */ + public static class MetaIndexInfo { + } + + /** Metadata describing a user-defined type. */ + public static class MetaUdt { + } + + /** Metadata describing a super-type. */ + public static class MetaSuperType { + } + + /** Metadata describing an attribute. */ + public static class MetaAttribute { + } + + /** Metadata describing a client info property. */ + public static class MetaClientInfoProperty { + } + + /** Metadata describing a function. */ + public static class MetaFunction { + } + + /** Metadata describing a function column. */ + public static class MetaFunctionColumn { + } + + /** Metadata describing a pseudo column. */ + public static class MetaPseudoColumn { + } + + /** Metadata describing a super-table. */ + public static class MetaSuperTable { + } + + public Map<DatabaseProperty, Object> getDatabaseProperties(ConnectionHandle ch) { + return Collections.emptyMap(); + } + + public MetaResultSet getTables(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat tableNamePattern, + List<String> typeList) { + return createEmptyResultSet(MetaTable.class); + } + + public MetaResultSet getColumns(ConnectionHandle ch, String catalog, + Pat schemaPattern, + Pat tableNamePattern, + Pat columnNamePattern) { + return createEmptyResultSet(MetaColumn.class); + } + + public MetaResultSet getSchemas(ConnectionHandle ch, String catalog, Pat schemaPattern) { + return createEmptyResultSet(MetaSchema.class); + } + + public MetaResultSet getCatalogs(ConnectionHandle ch) { + return createEmptyResultSet(MetaCatalog.class); + } + + public MetaResultSet getTableTypes(ConnectionHandle ch) { + return createEmptyResultSet(MetaTableType.class); + } + + public MetaResultSet getProcedures(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat procedureNamePattern) { + return createEmptyResultSet(MetaProcedure.class); + } + + public MetaResultSet getProcedureColumns(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat procedureNamePattern, + Pat columnNamePattern) { + return createEmptyResultSet(MetaProcedureColumn.class); + } + + public MetaResultSet getColumnPrivileges(ConnectionHandle ch, + String catalog, + String schema, + String table, + Pat columnNamePattern) { + return createEmptyResultSet(MetaColumnPrivilege.class); + } + + public MetaResultSet getTablePrivileges(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat tableNamePattern) { + return createEmptyResultSet(MetaTablePrivilege.class); + } + + public MetaResultSet getBestRowIdentifier(ConnectionHandle ch, + String catalog, + String schema, + String table, + int scope, + boolean nullable) { + return createEmptyResultSet(MetaBestRowIdentifier.class); + } + + public MetaResultSet getVersionColumns(ConnectionHandle ch, + String catalog, + String schema, + String table) { + return createEmptyResultSet(MetaVersionColumn.class); + } + + public MetaResultSet getPrimaryKeys(ConnectionHandle ch, + String catalog, + String schema, + String table) { + return createEmptyResultSet(MetaPrimaryKey.class); + } + + public MetaResultSet getImportedKeys(ConnectionHandle ch, + String catalog, + String schema, + String table) { + return createEmptyResultSet(MetaImportedKey.class); + } + + public MetaResultSet getExportedKeys(ConnectionHandle ch, + String catalog, + String schema, + String table) { + return createEmptyResultSet(MetaExportedKey.class); + } + + public MetaResultSet getCrossReference(ConnectionHandle ch, + String parentCatalog, + String parentSchema, + String parentTable, + String foreignCatalog, + String foreignSchema, + String foreignTable) { + return createEmptyResultSet(MetaCrossReference.class); + } + + public MetaResultSet getTypeInfo(ConnectionHandle ch) { + return createEmptyResultSet(MetaTypeInfo.class); + } + + public MetaResultSet getIndexInfo(ConnectionHandle ch, + String catalog, + String schema, + String table, + boolean unique, + boolean approximate) { + return createEmptyResultSet(MetaIndexInfo.class); + } + + public MetaResultSet getUDTs(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat typeNamePattern, + int[] types) { + return createEmptyResultSet(MetaUdt.class); + } + + public MetaResultSet getSuperTypes(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat typeNamePattern) { + return createEmptyResultSet(MetaSuperType.class); + } + + public MetaResultSet getSuperTables(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat tableNamePattern) { + return createEmptyResultSet(MetaSuperTable.class); + } + + public MetaResultSet getAttributes(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat typeNamePattern, + Pat attributeNamePattern) { + return createEmptyResultSet(MetaAttribute.class); + } + + public MetaResultSet getClientInfoProperties(ConnectionHandle ch) { + return createEmptyResultSet(MetaClientInfoProperty.class); + } + + public MetaResultSet getFunctions(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat functionNamePattern) { + return createEmptyResultSet(MetaFunction.class); + } + + public MetaResultSet getFunctionColumns(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat functionNamePattern, + Pat columnNamePattern) { + return createEmptyResultSet(MetaFunctionColumn.class); + } + + public MetaResultSet getPseudoColumns(ConnectionHandle ch, + String catalog, + Pat schemaPattern, + Pat tableNamePattern, + Pat columnNamePattern) { + return createEmptyResultSet(MetaPseudoColumn.class); + } + + @Override public Iterable<Object> createIterable(StatementHandle handle, QueryState state, + Signature signature, List<TypedValue> parameterValues, Frame firstFrame) { + if (firstFrame != null && firstFrame.done) { + return firstFrame.rows; + } + AvaticaStatement stmt; + try { + stmt = connection.lookupStatement(handle); + } catch (SQLException e) { + throw new RuntimeException(e); + } + return new FetchIterable(stmt, state, + firstFrame, parameterValues); + } + + public Frame fetch(AvaticaStatement stmt, List<TypedValue> parameterValues, + long offset, int fetchMaxRowCount) throws NoSuchStatementException, MissingResultsException { + return null; + } + + /** Information about a type. */ + private static class TypeInfo { + private static Map<Class<?>, TypeInfo> m = + new HashMap<Class<?>, TypeInfo>(); + static { + put(boolean.class, true, Types.BOOLEAN, "BOOLEAN"); + put(Boolean.class, false, Types.BOOLEAN, "BOOLEAN"); + put(byte.class, true, Types.TINYINT, "TINYINT"); + put(Byte.class, false, Types.TINYINT, "TINYINT"); + put(short.class, true, Types.SMALLINT, "SMALLINT"); + put(Short.class, false, Types.SMALLINT, "SMALLINT"); + put(int.class, true, Types.INTEGER, "INTEGER"); + put(Integer.class, false, Types.INTEGER, "INTEGER"); + put(long.class, true, Types.BIGINT, "BIGINT"); + put(Long.class, false, Types.BIGINT, "BIGINT"); + put(float.class, true, Types.FLOAT, "FLOAT"); + put(Float.class, false, Types.FLOAT, "FLOAT"); + put(double.class, true, Types.DOUBLE, "DOUBLE"); + put(Double.class, false, Types.DOUBLE, "DOUBLE"); + put(String.class, false, Types.VARCHAR, "VARCHAR"); + put(java.sql.Date.class, false, Types.DATE, "DATE"); + put(Time.class, false, Types.TIME, "TIME"); + put(Timestamp.class, false, Types.TIMESTAMP, "TIMESTAMP"); + } + + private final boolean primitive; + private final int sqlType; + private final String sqlTypeName; + + public TypeInfo(boolean primitive, int sqlType, String sqlTypeName) { + this.primitive = primitive; + this.sqlType = sqlType; + this.sqlTypeName = sqlTypeName; + } + + static void put(Class clazz, boolean primitive, int sqlType, + String sqlTypeName) { + m.put(clazz, new TypeInfo(primitive, sqlType, sqlTypeName)); + } + } + + /** Iterator that never returns any elements. */ + private static class EmptyIterator implements Iterator<Object> { + public static final Iterator<Object> INSTANCE = new EmptyIterator(); + + public void remove() { + throw new UnsupportedOperationException(); + } + + public boolean hasNext() { + return false; + } + + public Object next() { + throw new NoSuchElementException(); + } + } + + /** Iterable that yields an iterator over rows coming from a sequence of + * {@link Meta.Frame}s. */ + private class FetchIterable implements Iterable<Object> { + private final AvaticaStatement stmt; + private final QueryState state; + private final Frame firstFrame; + private final List<TypedValue> parameterValues; + + public FetchIterable(AvaticaStatement stmt, QueryState state, Frame firstFrame, + List<TypedValue> parameterValues) { + this.stmt = stmt; + this.state = state; + this.firstFrame = firstFrame; + this.parameterValues = parameterValues; + } + + public Iterator<Object> iterator() { + return new FetchIterator(stmt, state, firstFrame, parameterValues); + } + } + + /** Iterator over rows coming from a sequence of {@link Meta.Frame}s. */ + private class FetchIterator implements Iterator<Object> { + private final AvaticaStatement stmt; + private final QueryState state; + private Frame frame; + private Iterator<Object> rows; + private List<TypedValue> parameterValues; + private List<TypedValue> originalParameterValues; + private long currentOffset = 0; + + public FetchIterator(AvaticaStatement stmt, QueryState state, Frame firstFrame, + List<TypedValue> parameterValues) { + this.stmt = stmt; + this.state = state; + this.parameterValues = parameterValues; + this.originalParameterValues = parameterValues; + if (firstFrame == null) { + frame = Frame.MORE; + rows = EmptyIterator.INSTANCE; + } else { + frame = firstFrame; + rows = firstFrame.rows.iterator(); + } + moveNext(); + } + + public void remove() { + throw new UnsupportedOperationException("remove"); + } + + public boolean hasNext() { + return rows != null; + } + + public Object next() { + if (rows == null) { + throw new NoSuchElementException(); + } + final Object o = rows.next(); + currentOffset++; + moveNext(); + return o; + } + + private void moveNext() { + for (;;) { + if (rows.hasNext()) { + break; + } + if (frame.done) { + rows = null; + break; + } + try { + // currentOffset updated after element is read from `rows` iterator + frame = fetch(stmt.handle, currentOffset, AvaticaStatement.DEFAULT_FETCH_SIZE); + } catch (NoSuchStatementException e) { + resetStatement(); + // re-fetch the batch where we left off + continue; + } catch (MissingResultsException e) { + try { + // We saw the statement, but it didnt' have a resultset initialized. So, reset it. + if (!stmt.syncResults(state, currentOffset)) { + // This returned false, so there aren't actually any more results to iterate over + frame = null; + rows = null; + break; + } + // syncResults returning true means we need to fetch those results + } catch (NoSuchStatementException e1) { + // Tried to reset the result set, but lost the statement, save a loop before retrying. + resetStatement(); + // Will just loop back around to a MissingResultsException, but w/e recursion + } + // Kick back to the top to try to fetch again (in both branches) + continue; + } + parameterValues = null; // don't execute next time + if (frame == null) { + rows = null; + break; + } + // It is valid for rows to be empty, so we go around the loop again to + // check + rows = frame.rows.iterator(); + } + } + + private void resetStatement() { + // If we have to reset the statement, we need to reset the parameterValues too + parameterValues = originalParameterValues; + // Defer to the statement to reset itself + stmt.resetStatement(); + } + } + + /** Returns whether a list of parameter values has any null elements. */ + public static boolean checkParameterValueHasNull(List<TypedValue> parameterValues) { + for (TypedValue x : parameterValues) { + if (x == null) { + return true; + } + } + return false; + } +} + +// End MetaImpl.java http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/MissingResultsException.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/MissingResultsException.java b/avatica/core/src/main/java/org/apache/calcite/avatica/MissingResultsException.java new file mode 100644 index 0000000..7746769 --- /dev/null +++ b/avatica/core/src/main/java/org/apache/calcite/avatica/MissingResultsException.java @@ -0,0 +1,41 @@ +/* + * 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.StatementHandle; + +import java.sql.ResultSet; + +/** + * An Exception which denotes that a cached Statement is present but has no {@link ResultSet}. + */ +public class MissingResultsException extends Exception { + + private static final long serialVersionUID = 1L; + + private final StatementHandle handle; + + public MissingResultsException(StatementHandle handle) { + this.handle = handle; + } + + public StatementHandle getHandle() { + return handle; + } +} + +// End MissingResultsException.java http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/NoSuchConnectionException.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/NoSuchConnectionException.java b/avatica/core/src/main/java/org/apache/calcite/avatica/NoSuchConnectionException.java new file mode 100644 index 0000000..b5a940d --- /dev/null +++ b/avatica/core/src/main/java/org/apache/calcite/avatica/NoSuchConnectionException.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.avatica; + +/** + * An Exception that denotes that the given Connection is not cached. + */ +public class NoSuchConnectionException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + private final String connectionId; + + public NoSuchConnectionException(String connectionId) { + this.connectionId = connectionId; + } + + public String getConnectionId() { + return connectionId; + } +} + +// End NoSuchConnectionException.java http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/NoSuchStatementException.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/NoSuchStatementException.java b/avatica/core/src/main/java/org/apache/calcite/avatica/NoSuchStatementException.java new file mode 100644 index 0000000..321011b --- /dev/null +++ b/avatica/core/src/main/java/org/apache/calcite/avatica/NoSuchStatementException.java @@ -0,0 +1,39 @@ +/* + * 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.StatementHandle; + +/** + * An Exception that denotes that the given Statement is not cached. + */ +public class NoSuchStatementException extends Exception { + + private static final long serialVersionUID = 1L; + + private final StatementHandle stmtHandle; + + public NoSuchStatementException(StatementHandle stmtHandle) { + this.stmtHandle = stmtHandle; + } + + public StatementHandle getStatementHandle() { + return stmtHandle; + } +} + +// End NoSuchStatementException.java
