| /* |
| * 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.util.ByteString; |
| |
| import com.fasterxml.jackson.annotation.JsonCreator; |
| import com.fasterxml.jackson.annotation.JsonProperty; |
| import com.fasterxml.jackson.annotation.JsonSubTypes; |
| import com.fasterxml.jackson.annotation.JsonTypeInfo; |
| import com.google.protobuf.Descriptors.FieldDescriptor; |
| |
| import java.lang.reflect.Type; |
| import java.math.BigDecimal; |
| import java.sql.Array; |
| import java.sql.DatabaseMetaData; |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| import java.sql.Struct; |
| import java.sql.Time; |
| import java.sql.Timestamp; |
| import java.sql.Types; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| |
| /** |
| * Metadata for a column. |
| * |
| * <p>(Compare with {@link java.sql.ResultSetMetaData}.) |
| */ |
| public class ColumnMetaData { |
| private static final FieldDescriptor CATALOG_NAME_DESCRIPTOR = Common.ColumnMetaData |
| .getDescriptor().findFieldByNumber(Common.ColumnMetaData.CATALOG_NAME_FIELD_NUMBER); |
| private static final FieldDescriptor SCHEMA_NAME_DESCRIPTOR = Common.ColumnMetaData |
| .getDescriptor().findFieldByNumber(Common.ColumnMetaData.SCHEMA_NAME_FIELD_NUMBER); |
| private static final FieldDescriptor LABEL_DESCRIPTOR = Common.ColumnMetaData |
| .getDescriptor().findFieldByNumber(Common.ColumnMetaData.LABEL_FIELD_NUMBER); |
| private static final FieldDescriptor COLUMN_NAME_DESCRIPTOR = Common.ColumnMetaData |
| .getDescriptor().findFieldByNumber(Common.ColumnMetaData.COLUMN_NAME_FIELD_NUMBER); |
| private static final FieldDescriptor TABLE_NAME_DESCRIPTOR = Common.ColumnMetaData |
| .getDescriptor().findFieldByNumber(Common.ColumnMetaData.TABLE_NAME_FIELD_NUMBER); |
| private static final FieldDescriptor COLUMN_CLASS_NAME_DESCRIPTOR = Common.ColumnMetaData |
| .getDescriptor().findFieldByNumber(Common.ColumnMetaData.COLUMN_CLASS_NAME_FIELD_NUMBER); |
| |
| public final int ordinal; // 0-based |
| public final boolean autoIncrement; |
| public final boolean caseSensitive; |
| public final boolean searchable; |
| public final boolean currency; |
| public final int nullable; |
| public final boolean signed; |
| public final int displaySize; |
| public final String label; |
| public final String columnName; |
| public final String schemaName; |
| public final int precision; |
| public final int scale; |
| public final String tableName; |
| public final String catalogName; |
| public final boolean readOnly; |
| public final boolean writable; |
| public final boolean definitelyWritable; |
| public final String columnClassName; |
| public final AvaticaType type; |
| |
| @JsonCreator |
| public ColumnMetaData( |
| @JsonProperty("ordinal") int ordinal, |
| @JsonProperty("autoIncrement") boolean autoIncrement, |
| @JsonProperty("caseSensitive") boolean caseSensitive, |
| @JsonProperty("searchable") boolean searchable, |
| @JsonProperty("currency") boolean currency, |
| @JsonProperty("nullable") int nullable, |
| @JsonProperty("signed") boolean signed, |
| @JsonProperty("displaySize") int displaySize, |
| @JsonProperty("label") String label, |
| @JsonProperty("columnName") String columnName, |
| @JsonProperty("schemaName") String schemaName, |
| @JsonProperty("precision") int precision, |
| @JsonProperty("scale") int scale, |
| @JsonProperty("tableName") String tableName, |
| @JsonProperty("catalogName") String catalogName, |
| @JsonProperty("type") AvaticaType type, |
| @JsonProperty("readOnly") boolean readOnly, |
| @JsonProperty("writable") boolean writable, |
| @JsonProperty("definitelyWritable") boolean definitelyWritable, |
| @JsonProperty("columnClassName") String columnClassName) { |
| this.ordinal = ordinal; |
| this.autoIncrement = autoIncrement; |
| this.caseSensitive = caseSensitive; |
| this.searchable = searchable; |
| this.currency = currency; |
| this.nullable = nullable; |
| this.signed = signed; |
| this.displaySize = displaySize; |
| this.label = label; |
| // Per the JDBC spec this should be just columnName. |
| // For example, the query |
| // select 1 as x, c as y from t |
| // should give columns |
| // (label=x, column=null, table=null) |
| // (label=y, column=c table=t) |
| // But DbUnit requires every column to have a name. Duh. |
| this.columnName = first(columnName, label); |
| this.schemaName = schemaName; |
| this.precision = precision; |
| this.scale = scale; |
| this.tableName = tableName; |
| this.catalogName = catalogName; |
| this.type = type; |
| this.readOnly = readOnly; |
| this.writable = writable; |
| this.definitelyWritable = definitelyWritable; |
| this.columnClassName = columnClassName; |
| } |
| |
| public Common.ColumnMetaData toProto() { |
| Common.ColumnMetaData.Builder builder = Common.ColumnMetaData.newBuilder(); |
| |
| // Primitive fields (can't be null) |
| builder.setOrdinal(ordinal) |
| .setAutoIncrement(autoIncrement) |
| .setCaseSensitive(caseSensitive) |
| .setSearchable(searchable) |
| .setCurrency(currency) |
| .setNullable(nullable) |
| .setSigned(signed) |
| .setDisplaySize(displaySize) |
| .setPrecision(precision) |
| .setScale(scale) |
| .setReadOnly(readOnly) |
| .setWritable(writable) |
| .setDefinitelyWritable(definitelyWritable); |
| |
| // Potentially null fields |
| if (null != label) { |
| builder.setLabel(label); |
| } |
| |
| if (null != columnName) { |
| builder.setColumnName(columnName); |
| } |
| |
| if (null != schemaName) { |
| builder.setSchemaName(schemaName); |
| } |
| |
| if (null != tableName) { |
| builder.setTableName(tableName); |
| } |
| |
| if (null != catalogName) { |
| builder.setCatalogName(catalogName); |
| } |
| |
| if (null != type) { |
| builder.setType(type.toProto()); |
| } |
| |
| if (null != columnClassName) { |
| builder.setColumnClassName(columnClassName); |
| } |
| |
| return builder.build(); |
| } |
| |
| public static ColumnMetaData fromProto(Common.ColumnMetaData proto) { |
| AvaticaType nestedType = AvaticaType.fromProto(proto.getType()); |
| |
| String catalogName = null; |
| if (proto.hasField(CATALOG_NAME_DESCRIPTOR)) { |
| catalogName = proto.getCatalogName(); |
| } |
| |
| String schemaName = null; |
| if (proto.hasField(SCHEMA_NAME_DESCRIPTOR)) { |
| schemaName = proto.getSchemaName(); |
| } |
| |
| String label = null; |
| if (proto.hasField(LABEL_DESCRIPTOR)) { |
| label = proto.getLabel(); |
| } |
| |
| String columnName = null; |
| if (proto.hasField(COLUMN_NAME_DESCRIPTOR)) { |
| columnName = proto.getColumnName(); |
| } |
| |
| String tableName = null; |
| if (proto.hasField(TABLE_NAME_DESCRIPTOR)) { |
| tableName = proto.getTableName(); |
| } |
| |
| String columnClassName = null; |
| if (proto.hasField(COLUMN_CLASS_NAME_DESCRIPTOR)) { |
| columnClassName = proto.getColumnClassName(); |
| } |
| |
| // Recreate the ColumnMetaData |
| return new ColumnMetaData(proto.getOrdinal(), proto.getAutoIncrement(), |
| proto.getCaseSensitive(), proto.getSearchable(), proto.getCurrency(), proto.getNullable(), |
| proto.getSigned(), proto.getDisplaySize(), label, columnName, |
| schemaName, proto.getPrecision(), proto.getScale(), tableName, |
| catalogName, nestedType, proto.getReadOnly(), proto.getWritable(), |
| proto.getDefinitelyWritable(), columnClassName); |
| } |
| |
| @Override public int hashCode() { |
| return Objects.hash(autoIncrement, caseSensitive, catalogName, |
| columnClassName, columnName, currency, definitelyWritable, displaySize, |
| label, nullable, ordinal, precision, readOnly, scale, schemaName, |
| searchable, signed, tableName, type, writable); |
| } |
| |
| @Override public boolean equals(Object o) { |
| return o == this |
| || o instanceof ColumnMetaData |
| && autoIncrement == ((ColumnMetaData) o).autoIncrement |
| && caseSensitive == ((ColumnMetaData) o).caseSensitive |
| && Objects.equals(catalogName, ((ColumnMetaData) o).catalogName) |
| && Objects.equals(columnClassName, ((ColumnMetaData) o).columnClassName) |
| && Objects.equals(columnName, ((ColumnMetaData) o).columnName) |
| && currency == ((ColumnMetaData) o).currency |
| && definitelyWritable == ((ColumnMetaData) o).definitelyWritable |
| && displaySize == ((ColumnMetaData) o).displaySize |
| && Objects.equals(label, ((ColumnMetaData) o).label) |
| && nullable == ((ColumnMetaData) o).nullable |
| && ordinal == ((ColumnMetaData) o).ordinal |
| && precision == ((ColumnMetaData) o).precision |
| && readOnly == ((ColumnMetaData) o).readOnly |
| && scale == ((ColumnMetaData) o).scale |
| && Objects.equals(schemaName, ((ColumnMetaData) o).schemaName) |
| && searchable == ((ColumnMetaData) o).searchable |
| && signed == ((ColumnMetaData) o).signed |
| && Objects.equals(tableName, ((ColumnMetaData) o).tableName) |
| && Objects.equals(type, ((ColumnMetaData) o).type) |
| && writable == ((ColumnMetaData) o).writable; |
| } |
| |
| private static <T> T first(T t0, T t1) { |
| return t0 != null ? t0 : t1; |
| } |
| |
| /** Creates a {@link ScalarType}. */ |
| public static ScalarType scalar(int type, String typeName, Rep rep) { |
| return new ScalarType(type, typeName, rep); |
| } |
| |
| /** Creates a {@link StructType}. */ |
| public static StructType struct(List<ColumnMetaData> columns) { |
| return new StructType(columns); |
| } |
| |
| /** Creates an {@link ArrayType}. */ |
| public static ArrayType array(AvaticaType componentType, String typeName, |
| Rep rep) { |
| return new ArrayType(Types.ARRAY, typeName, rep, componentType); |
| } |
| |
| /** Creates a ColumnMetaData for result sets that are not based on a struct |
| * but need to have a single 'field' for purposes of |
| * {@link java.sql.ResultSetMetaData}. */ |
| public static ColumnMetaData dummy(AvaticaType type, boolean nullable) { |
| return new ColumnMetaData( |
| 0, |
| false, |
| true, |
| false, |
| false, |
| nullable |
| ? DatabaseMetaData.columnNullable |
| : DatabaseMetaData.columnNoNulls, |
| true, |
| -1, |
| null, |
| null, |
| null, |
| -1, |
| -1, |
| null, |
| null, |
| type, |
| true, |
| false, |
| false, |
| type.columnClassName()); |
| } |
| |
| public ColumnMetaData setRep(Rep rep) { |
| return new ColumnMetaData(ordinal, autoIncrement, caseSensitive, searchable, |
| currency, nullable, signed, displaySize, label, columnName, schemaName, |
| precision, scale, tableName, catalogName, type.setRep(rep), readOnly, |
| writable, definitelyWritable, columnClassName); |
| } |
| |
| /** Description of the type used to internally represent a value. For example, |
| * a {@link java.sql.Date} might be represented as a {@link #PRIMITIVE_INT} |
| * if not nullable, or a {@link #JAVA_SQL_DATE}. */ |
| public enum Rep { |
| PRIMITIVE_BOOLEAN(boolean.class, Types.BOOLEAN), |
| PRIMITIVE_BYTE(byte.class, Types.TINYINT), |
| PRIMITIVE_CHAR(char.class, Types.CHAR), |
| PRIMITIVE_SHORT(short.class, Types.SMALLINT), |
| PRIMITIVE_INT(int.class, Types.INTEGER), |
| PRIMITIVE_LONG(long.class, Types.BIGINT), |
| PRIMITIVE_FLOAT(float.class, Types.FLOAT), |
| PRIMITIVE_DOUBLE(double.class, Types.DOUBLE), |
| BOOLEAN(Boolean.class, Types.BOOLEAN), |
| BYTE(Byte.class, Types.TINYINT), |
| CHARACTER(Character.class, Types.CHAR), |
| SHORT(Short.class, Types.SMALLINT), |
| INTEGER(Integer.class, Types.INTEGER), |
| LONG(Long.class, Types.BIGINT), |
| FLOAT(Float.class, Types.FLOAT), |
| DOUBLE(Double.class, Types.DOUBLE), |
| JAVA_SQL_TIME(Time.class, Types.TIME), |
| JAVA_SQL_TIMESTAMP(Timestamp.class, Types.TIMESTAMP), |
| JAVA_SQL_DATE(java.sql.Date.class, Types.DATE), |
| JAVA_UTIL_DATE(java.util.Date.class, Types.DATE), |
| BYTE_STRING(ByteString.class, Types.VARBINARY), |
| STRING(String.class, Types.VARCHAR), |
| |
| /** Values are represented as some sub-class of {@link Number}. |
| * The JSON encoding does this. */ |
| NUMBER(BigDecimal.class, Types.NUMERIC), |
| |
| ARRAY(Array.class, Types.ARRAY), |
| MULTISET(List.class, Types.JAVA_OBJECT), |
| STRUCT(Struct.class, Types.JAVA_OBJECT), |
| |
| OBJECT(Object.class, Types.JAVA_OBJECT); |
| |
| public final Class clazz; |
| public final int typeId; |
| |
| public static final Map<Class, Rep> VALUE_MAP; |
| |
| static { |
| Map<Class, Rep> builder = new HashMap<>(); |
| for (Rep rep : values()) { |
| builder.put(rep.clazz, rep); |
| } |
| builder.put(byte[].class, BYTE_STRING); |
| VALUE_MAP = Collections.unmodifiableMap(builder); |
| } |
| |
| Rep(Class clazz, int typeId) { |
| this.clazz = clazz; |
| this.typeId = typeId; |
| } |
| |
| public static Rep of(Type clazz) { |
| //noinspection SuspiciousMethodCalls |
| final Rep rep = VALUE_MAP.get(clazz); |
| return rep != null ? rep : OBJECT; |
| } |
| |
| /** Returns the value of a column of this type from a result set. */ |
| public Object jdbcGet(ResultSet resultSet, int i) throws SQLException { |
| switch (this) { |
| case PRIMITIVE_BOOLEAN: |
| return resultSet.getBoolean(i); |
| case PRIMITIVE_BYTE: |
| return resultSet.getByte(i); |
| case PRIMITIVE_SHORT: |
| return resultSet.getShort(i); |
| case PRIMITIVE_INT: |
| return resultSet.getInt(i); |
| case PRIMITIVE_LONG: |
| return resultSet.getLong(i); |
| case PRIMITIVE_FLOAT: |
| return resultSet.getFloat(i); |
| case PRIMITIVE_DOUBLE: |
| return resultSet.getDouble(i); |
| case BOOLEAN: |
| final boolean aBoolean = resultSet.getBoolean(i); |
| return resultSet.wasNull() ? null : aBoolean; |
| case BYTE: |
| final byte aByte = resultSet.getByte(i); |
| return resultSet.wasNull() ? null : aByte; |
| case SHORT: |
| final short aShort = resultSet.getShort(i); |
| return resultSet.wasNull() ? null : aShort; |
| case INTEGER: |
| final int anInt = resultSet.getInt(i); |
| return resultSet.wasNull() ? null : anInt; |
| case LONG: |
| final long aLong = resultSet.getLong(i); |
| return resultSet.wasNull() ? null : aLong; |
| case FLOAT: |
| final float aFloat = resultSet.getFloat(i); |
| return resultSet.wasNull() ? null : aFloat; |
| case DOUBLE: |
| final double aDouble = resultSet.getDouble(i); |
| return resultSet.wasNull() ? null : aDouble; |
| case JAVA_SQL_DATE: |
| return resultSet.getDate(i); |
| case JAVA_SQL_TIME: |
| return resultSet.getTime(i); |
| case JAVA_SQL_TIMESTAMP: |
| return resultSet.getTimestamp(i); |
| case ARRAY: |
| return resultSet.getArray(i); |
| case STRUCT: |
| return resultSet.getObject(i, Struct.class); |
| default: |
| return resultSet.getObject(i); |
| } |
| } |
| |
| public Common.Rep toProto() { |
| return Common.Rep.valueOf(name()); |
| } |
| |
| public static Rep fromProto(Common.Rep proto) { |
| if (Common.Rep.UNRECOGNIZED == proto) { |
| // Un-set in the message, treat it as null |
| return null; |
| } else if (Common.Rep.BIG_DECIMAL == proto) { |
| // BIG_DECIMAL has to come back as a NUMBER |
| return Rep.NUMBER; |
| } else if (Common.Rep.NULL == proto) { |
| return Rep.OBJECT; |
| } |
| return Rep.valueOf(proto.name()); |
| } |
| |
| /** |
| * Computes the given JDBC type for a primitive to the corresponding {@link Rep} for the |
| * equivalent Object type. If the provided type is not for a primitive, a {@link Rep} for the |
| * provided Object is returned. |
| * |
| * @param type The type of a value (based on {@link java.sql.Types}). |
| * @return The corresponding non-primitive {@link Rep} for the given {@code type}. |
| */ |
| public static ColumnMetaData.Rep nonPrimitiveRepOf(SqlType type) { |
| if (null == type) { |
| throw new NullPointerException(); |
| } |
| if (boolean.class == type.clazz) { |
| return ColumnMetaData.Rep.BOOLEAN; |
| } else if (byte.class == type.clazz) { |
| return ColumnMetaData.Rep.BYTE; |
| } else if (char.class == type.clazz) { |
| return ColumnMetaData.Rep.CHARACTER; |
| } else if (short.class == type.clazz) { |
| return ColumnMetaData.Rep.SHORT; |
| } else if (int.class == type.clazz) { |
| return ColumnMetaData.Rep.INTEGER; |
| } else if (long.class == type.clazz) { |
| return ColumnMetaData.Rep.LONG; |
| } else if (float.class == type.clazz) { |
| return ColumnMetaData.Rep.FLOAT; |
| } else if (double.class == type.clazz) { |
| return ColumnMetaData.Rep.DOUBLE; |
| } |
| return ColumnMetaData.Rep.of(type.clazz); |
| } |
| |
| /** |
| * Computes the given JDBC type into the {@link Rep} for the wire (serial) form of that type. |
| * |
| * @param type The type of a value (based on {@link java.sql.Types}). |
| * @return The corresponding {@link Rep} for the serial form of the {@code type}. |
| */ |
| public static ColumnMetaData.Rep serialRepOf(SqlType type) { |
| if (null == type) { |
| throw new NullPointerException(); |
| } |
| if (boolean.class == type.internal) { |
| return ColumnMetaData.Rep.BOOLEAN; |
| } else if (byte.class == type.internal) { |
| return ColumnMetaData.Rep.BYTE; |
| } else if (char.class == type.internal) { |
| return ColumnMetaData.Rep.CHARACTER; |
| } else if (short.class == type.internal) { |
| return ColumnMetaData.Rep.SHORT; |
| } else if (int.class == type.internal) { |
| return ColumnMetaData.Rep.INTEGER; |
| } else if (long.class == type.internal) { |
| return ColumnMetaData.Rep.LONG; |
| } else if (float.class == type.internal) { |
| return ColumnMetaData.Rep.FLOAT; |
| } else if (double.class == type.internal) { |
| return ColumnMetaData.Rep.DOUBLE; |
| } |
| return ColumnMetaData.Rep.of(type.internal); |
| } |
| } |
| |
| /** Base class for a column type. */ |
| @JsonTypeInfo( |
| use = JsonTypeInfo.Id.NAME, |
| property = "type", |
| defaultImpl = ScalarType.class) |
| @JsonSubTypes({ |
| @JsonSubTypes.Type(value = ScalarType.class, name = "scalar"), |
| @JsonSubTypes.Type(value = StructType.class, name = "struct"), |
| @JsonSubTypes.Type(value = ArrayType.class, name = "array") }) |
| public static class AvaticaType { |
| public final int id; |
| public final String name; |
| |
| /** The type of the field that holds the value. Not a JDBC property. */ |
| public final Rep rep; |
| |
| public AvaticaType(int id, String name, Rep rep) { |
| this.id = id; |
| this.name = Objects.requireNonNull(name); |
| this.rep = Objects.requireNonNull(rep); |
| } |
| |
| public String columnClassName() { |
| return SqlType.valueOf(id).boxedClass().getName(); |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| public AvaticaType setRep(Rep rep) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public Common.AvaticaType toProto() { |
| Common.AvaticaType.Builder builder = Common.AvaticaType.newBuilder(); |
| |
| builder.setName(name); |
| builder.setId(id); |
| builder.setRep(rep.toProto()); |
| |
| return builder.build(); |
| } |
| |
| public static AvaticaType fromProto(Common.AvaticaType proto) { |
| Common.Rep repProto = proto.getRep(); |
| Rep rep = Rep.valueOf(repProto.name()); |
| AvaticaType type; |
| |
| if (proto.hasComponent()) { |
| // ArrayType |
| // recurse on the type for the array elements |
| AvaticaType nestedType = AvaticaType.fromProto(proto.getComponent()); |
| type = ColumnMetaData.array(nestedType, proto.getName(), rep); |
| } else if (proto.getColumnsCount() > 0) { |
| // StructType |
| List<ColumnMetaData> columns = new ArrayList<>(proto.getColumnsCount()); |
| for (Common.ColumnMetaData protoColumn : proto.getColumnsList()) { |
| columns.add(ColumnMetaData.fromProto(protoColumn)); |
| } |
| type = ColumnMetaData.struct(columns); |
| } else { |
| // ScalarType |
| type = ColumnMetaData.scalar(proto.getId(), proto.getName(), rep); |
| } |
| |
| return type; |
| } |
| |
| @Override public int hashCode() { |
| return Objects.hash(id, name, rep); |
| } |
| |
| @Override public boolean equals(Object o) { |
| return o == this |
| || o instanceof AvaticaType |
| && id == ((AvaticaType) o).id |
| && Objects.equals(name, ((AvaticaType) o).name) |
| && rep == ((AvaticaType) o).rep; |
| } |
| } |
| |
| /** Scalar type. */ |
| public static class ScalarType extends AvaticaType { |
| @JsonCreator |
| public ScalarType(@JsonProperty("id") int id, |
| @JsonProperty("name") String name, |
| @JsonProperty("rep") Rep rep) { |
| super(id, name, rep); |
| } |
| |
| @Override public AvaticaType setRep(Rep rep) { |
| return new ScalarType(id, name, rep); |
| } |
| } |
| |
| /** Record type. */ |
| public static class StructType extends AvaticaType { |
| public final List<ColumnMetaData> columns; |
| |
| @JsonCreator |
| public StructType(List<ColumnMetaData> columns) { |
| super(Types.STRUCT, "STRUCT", ColumnMetaData.Rep.OBJECT); |
| this.columns = columns; |
| } |
| |
| @Override public Common.AvaticaType toProto() { |
| Common.AvaticaType.Builder builder = Common.AvaticaType.newBuilder(super.toProto()); |
| for (ColumnMetaData valueType : columns) { |
| builder.addColumns(valueType.toProto()); |
| } |
| return builder.build(); |
| } |
| |
| @Override public int hashCode() { |
| return Objects.hash(id, name, rep, columns); |
| } |
| |
| @Override public boolean equals(Object o) { |
| return o == this |
| || o instanceof StructType |
| && super.equals(o) |
| && Objects.equals(columns, ((StructType) o).columns); |
| } |
| } |
| |
| /** Array type. */ |
| public static class ArrayType extends AvaticaType { |
| private AvaticaType component; |
| |
| /** |
| * Not for public use. Use {@link ColumnMetaData#array(AvaticaType, String, Rep)}. |
| */ |
| @JsonCreator |
| public ArrayType(@JsonProperty("type") int type, @JsonProperty("name") String typeName, |
| @JsonProperty("rep") Rep representation, @JsonProperty("component") AvaticaType component) { |
| super(type, typeName, representation); |
| this.component = component; |
| } |
| |
| /** |
| * Updates the component of {@code this} to the given value. This is necessary to provide as |
| * accurate-as-possible of an {@code ArrayType} in the {@code Signature}. It cannot be done |
| * at initial construction of this object. |
| */ |
| public void updateComponentType(AvaticaType component) { |
| this.component = Objects.requireNonNull(component); |
| } |
| |
| public AvaticaType getComponent() { |
| return component; |
| } |
| |
| @Override public Common.AvaticaType toProto() { |
| Common.AvaticaType.Builder builder = Common.AvaticaType.newBuilder(super.toProto()); |
| |
| builder.setComponent(component.toProto()); |
| |
| return builder.build(); |
| } |
| |
| @Override public int hashCode() { |
| return Objects.hash(id, name, rep, component); |
| } |
| |
| @Override public boolean equals(Object o) { |
| return o == this |
| || o instanceof ArrayType |
| && super.equals(o) |
| && Objects.equals(component, ((ArrayType) o).component); |
| } |
| } |
| } |
| |
| // End ColumnMetaData.java |